Merge branch 'collectd-5.7'
[collectd.git] / src / daemon / common.c
1 /**
2  * collectd - src/common.c
3  * Copyright (C) 2005-2014  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  *   Niki W. Waibel <niki.waibel@gmx.net>
26  *   Sebastian Harl <sh at tokkee.org>
27  *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
28 **/
29
30 #if HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include "collectd.h"
35
36 #include "common.h"
37 #include "plugin.h"
38 #include "utils_cache.h"
39
40 /* for getaddrinfo */
41 #include <netdb.h>
42 #include <sys/types.h>
43
44 #include <poll.h>
45
46 #if HAVE_NETINET_IN_H
47 #include <netinet/in.h>
48 #endif
49
50 #if HAVE_NETINET_TCP_H
51 #include <netinet/tcp.h>
52 #endif
53
54 /* for ntohl and htonl */
55 #if HAVE_ARPA_INET_H
56 #include <arpa/inet.h>
57 #endif
58
59 #if HAVE_CAPABILITY
60 #include <sys/capability.h>
61 #endif
62
63 #ifdef HAVE_LIBKSTAT
64 extern kstat_ctl_t *kc;
65 #endif
66
67 /* AIX doesn't have MSG_DONTWAIT */
68 #ifndef MSG_DONTWAIT
69 #define MSG_DONTWAIT MSG_NONBLOCK
70 #endif
71
72 #if !HAVE_GETPWNAM_R
73 static pthread_mutex_t getpwnam_r_lock = PTHREAD_MUTEX_INITIALIZER;
74 #endif
75
76 #if !HAVE_STRERROR_R
77 static pthread_mutex_t strerror_r_lock = PTHREAD_MUTEX_INITIALIZER;
78 #endif
79
80 char *sstrncpy(char *dest, const char *src, size_t n) {
81   strncpy(dest, src, n);
82   dest[n - 1] = '\0';
83
84   return (dest);
85 } /* char *sstrncpy */
86
87 int ssnprintf(char *dest, size_t n, const char *format, ...) {
88   int ret = 0;
89   va_list ap;
90
91   va_start(ap, format);
92   ret = vsnprintf(dest, n, format, ap);
93   dest[n - 1] = '\0';
94   va_end(ap);
95
96   return (ret);
97 } /* int ssnprintf */
98
99 char *ssnprintf_alloc(char const *format, ...) /* {{{ */
100 {
101   char static_buffer[1024] = "";
102   char *alloc_buffer;
103   size_t alloc_buffer_size;
104   int status;
105   va_list ap;
106
107   /* Try printing into the static buffer. In many cases it will be
108    * sufficiently large and we can simply return a strdup() of this
109    * buffer. */
110   va_start(ap, format);
111   status = vsnprintf(static_buffer, sizeof(static_buffer), format, ap);
112   va_end(ap);
113   if (status < 0)
114     return (NULL);
115
116   /* "status" does not include the null byte. */
117   alloc_buffer_size = (size_t)(status + 1);
118   if (alloc_buffer_size <= sizeof(static_buffer))
119     return (strdup(static_buffer));
120
121   /* Allocate a buffer large enough to hold the string. */
122   alloc_buffer = calloc(1, alloc_buffer_size);
123   if (alloc_buffer == NULL)
124     return (NULL);
125
126   /* Print again into this new buffer. */
127   va_start(ap, format);
128   status = vsnprintf(alloc_buffer, alloc_buffer_size, format, ap);
129   va_end(ap);
130   if (status < 0) {
131     sfree(alloc_buffer);
132     return (NULL);
133   }
134
135   return (alloc_buffer);
136 } /* }}} char *ssnprintf_alloc */
137
138 char *sstrdup(const char *s) {
139   char *r;
140   size_t sz;
141
142   if (s == NULL)
143     return (NULL);
144
145   /* Do not use `strdup' here, because it's not specified in POSIX. It's
146    * ``only'' an XSI extension. */
147   sz = strlen(s) + 1;
148   r = malloc(sz);
149   if (r == NULL) {
150     ERROR("sstrdup: Out of memory.");
151     exit(3);
152   }
153   memcpy(r, s, sizeof(char) * sz);
154
155   return (r);
156 } /* char *sstrdup */
157
158 /* Even though Posix requires "strerror_r" to return an "int",
159  * some systems (e.g. the GNU libc) return a "char *" _and_
160  * ignore the second argument ... -tokkee */
161 char *sstrerror(int errnum, char *buf, size_t buflen) {
162   buf[0] = '\0';
163
164 #if !HAVE_STRERROR_R
165   {
166     char *temp;
167
168     pthread_mutex_lock(&strerror_r_lock);
169
170     temp = strerror(errnum);
171     sstrncpy(buf, temp, buflen);
172
173     pthread_mutex_unlock(&strerror_r_lock);
174   }
175 /* #endif !HAVE_STRERROR_R */
176
177 #elif STRERROR_R_CHAR_P
178   {
179     char *temp;
180     temp = strerror_r(errnum, buf, buflen);
181     if (buf[0] == '\0') {
182       if ((temp != NULL) && (temp != buf) && (temp[0] != '\0'))
183         sstrncpy(buf, temp, buflen);
184       else
185         sstrncpy(buf, "strerror_r did not return "
186                       "an error message",
187                  buflen);
188     }
189   }
190 /* #endif STRERROR_R_CHAR_P */
191
192 #else
193   if (strerror_r(errnum, buf, buflen) != 0) {
194     ssnprintf(buf, buflen, "Error #%i; "
195                            "Additionally, strerror_r failed.",
196               errnum);
197   }
198 #endif /* STRERROR_R_CHAR_P */
199
200   return (buf);
201 } /* char *sstrerror */
202
203 void *smalloc(size_t size) {
204   void *r;
205
206   if ((r = malloc(size)) == NULL) {
207     ERROR("Not enough memory.");
208     exit(3);
209   }
210
211   return (r);
212 } /* void *smalloc */
213
214 #if 0
215 void sfree (void **ptr)
216 {
217         if (ptr == NULL)
218                 return;
219
220         if (*ptr != NULL)
221                 free (*ptr);
222
223         *ptr = NULL;
224 }
225 #endif
226
227 ssize_t sread(int fd, void *buf, size_t count) {
228   char *ptr;
229   size_t nleft;
230   ssize_t status;
231
232   ptr = (char *)buf;
233   nleft = count;
234
235   while (nleft > 0) {
236     status = read(fd, (void *)ptr, nleft);
237
238     if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
239       continue;
240
241     if (status < 0)
242       return (status);
243
244     if (status == 0) {
245       DEBUG("Received EOF from fd %i. "
246             "Closing fd and returning error.",
247             fd);
248       close(fd);
249       return (-1);
250     }
251
252     assert((0 > status) || (nleft >= (size_t)status));
253
254     nleft = nleft - ((size_t)status);
255     ptr = ptr + ((size_t)status);
256   }
257
258   return (0);
259 }
260
261 ssize_t swrite(int fd, const void *buf, size_t count) {
262   const char *ptr;
263   size_t nleft;
264   ssize_t status;
265   struct pollfd pfd;
266
267   ptr = (const char *)buf;
268   nleft = count;
269
270   if (fd < 0)
271     return (-1);
272
273   /* checking for closed peer connection */
274   pfd.fd = fd;
275   pfd.events = POLLIN | POLLHUP;
276   pfd.revents = 0;
277   if (poll(&pfd, 1, 0) > 0) {
278     char buffer[32];
279     if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
280       // if recv returns zero (even though poll() said there is data to be
281       // read),
282       // that means the connection has been closed
283       return -1;
284     }
285   }
286
287   while (nleft > 0) {
288     status = write(fd, (const void *)ptr, nleft);
289
290     if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
291       continue;
292
293     if (status < 0)
294       return (status);
295
296     nleft = nleft - ((size_t)status);
297     ptr = ptr + ((size_t)status);
298   }
299
300   return (0);
301 }
302
303 int strsplit(char *string, char **fields, size_t size) {
304   size_t i;
305   char *ptr;
306   char *saveptr;
307
308   i = 0;
309   ptr = string;
310   saveptr = NULL;
311   while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) {
312     ptr = NULL;
313     i++;
314
315     if (i >= size)
316       break;
317   }
318
319   return ((int)i);
320 }
321
322 int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num,
323             const char *sep) {
324   size_t avail = 0;
325   char *ptr = buffer;
326   size_t sep_len = 0;
327
328   size_t buffer_req = 0;
329
330   if (((fields_num != 0) && (fields == NULL)) ||
331       ((buffer_size != 0) && (buffer == NULL)))
332     return (-EINVAL);
333
334   if (buffer != NULL)
335     buffer[0] = 0;
336
337   if (buffer_size != 0)
338     avail = buffer_size - 1;
339
340   if (sep != NULL)
341     sep_len = strlen(sep);
342
343   for (size_t i = 0; i < fields_num; i++) {
344     size_t field_len = strlen(fields[i]);
345
346     if (i != 0)
347       buffer_req += sep_len;
348     buffer_req += field_len;
349
350     if ((i != 0) && (sep_len > 0)) {
351       if (sep_len >= avail) {
352         /* prevent subsequent iterations from writing to the
353          * buffer. */
354         avail = 0;
355         continue;
356       }
357
358       memcpy(ptr, sep, sep_len);
359
360       ptr += sep_len;
361       avail -= sep_len;
362     }
363
364     if (field_len > avail)
365       field_len = avail;
366
367     memcpy(ptr, fields[i], field_len);
368     ptr += field_len;
369
370     avail -= field_len;
371     if (ptr != NULL)
372       *ptr = 0;
373   }
374
375   return (int)buffer_req;
376 }
377
378 int escape_string(char *buffer, size_t buffer_size) {
379   char *temp;
380   size_t j;
381
382   /* Check if we need to escape at all first */
383   temp = strpbrk(buffer, " \t\"\\");
384   if (temp == NULL)
385     return (0);
386
387   if (buffer_size < 3)
388     return (EINVAL);
389
390   temp = calloc(1, buffer_size);
391   if (temp == NULL)
392     return (ENOMEM);
393
394   temp[0] = '"';
395   j = 1;
396
397   for (size_t i = 0; i < buffer_size; i++) {
398     if (buffer[i] == 0) {
399       break;
400     } else if ((buffer[i] == '"') || (buffer[i] == '\\')) {
401       if (j > (buffer_size - 4))
402         break;
403       temp[j] = '\\';
404       temp[j + 1] = buffer[i];
405       j += 2;
406     } else {
407       if (j > (buffer_size - 3))
408         break;
409       temp[j] = buffer[i];
410       j++;
411     }
412   }
413
414   assert((j + 1) < buffer_size);
415   temp[j] = '"';
416   temp[j + 1] = 0;
417
418   sstrncpy(buffer, temp, buffer_size);
419   sfree(temp);
420   return (0);
421 } /* int escape_string */
422
423 int strunescape(char *buf, size_t buf_len) {
424   for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) {
425     if (buf[i] != '\\')
426       continue;
427
428     if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
429       ERROR("string unescape: backslash found at end of string.");
430       /* Ensure null-byte at the end of the buffer. */
431       buf[i] = 0;
432       return (-1);
433     }
434
435     switch (buf[i + 1]) {
436     case 't':
437       buf[i] = '\t';
438       break;
439     case 'n':
440       buf[i] = '\n';
441       break;
442     case 'r':
443       buf[i] = '\r';
444       break;
445     default:
446       buf[i] = buf[i + 1];
447       break;
448     }
449
450     /* Move everything after the position one position to the left.
451      * Add a null-byte as last character in the buffer. */
452     memmove(buf + i + 1, buf + i + 2, buf_len - i - 2);
453     buf[buf_len - 1] = 0;
454   }
455   return (0);
456 } /* int strunescape */
457
458 size_t strstripnewline(char *buffer) {
459   size_t buffer_len = strlen(buffer);
460
461   while (buffer_len > 0) {
462     if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r'))
463       break;
464     buffer_len--;
465     buffer[buffer_len] = 0;
466   }
467
468   return (buffer_len);
469 } /* size_t strstripnewline */
470
471 int escape_slashes(char *buffer, size_t buffer_size) {
472   size_t buffer_len;
473
474   buffer_len = strlen(buffer);
475
476   if (buffer_len <= 1) {
477     if (strcmp("/", buffer) == 0) {
478       if (buffer_size < 5)
479         return (-1);
480       sstrncpy(buffer, "root", buffer_size);
481     }
482     return (0);
483   }
484
485   /* Move one to the left */
486   if (buffer[0] == '/') {
487     memmove(buffer, buffer + 1, buffer_len);
488     buffer_len--;
489   }
490
491   for (size_t i = 0; i < buffer_len; i++) {
492     if (buffer[i] == '/')
493       buffer[i] = '_';
494   }
495
496   return (0);
497 } /* int escape_slashes */
498
499 void replace_special(char *buffer, size_t buffer_size) {
500   for (size_t i = 0; i < buffer_size; i++) {
501     if (buffer[i] == 0)
502       return;
503     if ((!isalnum((int)buffer[i])) && (buffer[i] != '-'))
504       buffer[i] = '_';
505   }
506 } /* void replace_special */
507
508 int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) {
509   struct timeval *larger;
510   struct timeval *smaller;
511
512   int status;
513
514   NORMALIZE_TIMEVAL(tv0);
515   NORMALIZE_TIMEVAL(tv1);
516
517   if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) {
518     if (delta != NULL) {
519       delta->tv_sec = 0;
520       delta->tv_usec = 0;
521     }
522     return (0);
523   }
524
525   if ((tv0.tv_sec < tv1.tv_sec) ||
526       ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) {
527     larger = &tv1;
528     smaller = &tv0;
529     status = -1;
530   } else {
531     larger = &tv0;
532     smaller = &tv1;
533     status = 1;
534   }
535
536   if (delta != NULL) {
537     delta->tv_sec = larger->tv_sec - smaller->tv_sec;
538
539     if (smaller->tv_usec <= larger->tv_usec)
540       delta->tv_usec = larger->tv_usec - smaller->tv_usec;
541     else {
542       --delta->tv_sec;
543       delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
544     }
545   }
546
547   assert((delta == NULL) ||
548          ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
549
550   return (status);
551 } /* int timeval_cmp */
552
553 int check_create_dir(const char *file_orig) {
554   struct stat statbuf;
555
556   char file_copy[512];
557   char dir[512];
558   int dir_len = 512;
559   char *fields[16];
560   int fields_num;
561   char *ptr;
562   char *saveptr;
563   int last_is_file = 1;
564   int path_is_absolute = 0;
565   size_t len;
566
567   /*
568    * Sanity checks first
569    */
570   if (file_orig == NULL)
571     return (-1);
572
573   if ((len = strlen(file_orig)) < 1)
574     return (-1);
575   else if (len >= sizeof(file_copy))
576     return (-1);
577
578   /*
579    * If `file_orig' ends in a slash the last component is a directory,
580    * otherwise it's a file. Act accordingly..
581    */
582   if (file_orig[len - 1] == '/')
583     last_is_file = 0;
584   if (file_orig[0] == '/')
585     path_is_absolute = 1;
586
587   /*
588    * Create a copy for `strtok_r' to destroy
589    */
590   sstrncpy(file_copy, file_orig, sizeof(file_copy));
591
592   /*
593    * Break into components. This will eat up several slashes in a row and
594    * remove leading and trailing slashes..
595    */
596   ptr = file_copy;
597   saveptr = NULL;
598   fields_num = 0;
599   while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) {
600     ptr = NULL;
601     fields_num++;
602
603     if (fields_num >= 16)
604       break;
605   }
606
607   /*
608    * For each component, do..
609    */
610   for (int i = 0; i < (fields_num - last_is_file); i++) {
611     /*
612      * Do not create directories that start with a dot. This
613      * prevents `../../' attacks and other likely malicious
614      * behavior.
615      */
616     if (fields[i][0] == '.') {
617       ERROR("Cowardly refusing to create a directory that "
618             "begins with a `.' (dot): `%s'",
619             file_orig);
620       return (-2);
621     }
622
623     /*
624      * Join the components together again
625      */
626     dir[0] = '/';
627     if (strjoin(dir + path_is_absolute, (size_t)(dir_len - path_is_absolute),
628                 fields, (size_t)(i + 1), "/") < 0) {
629       ERROR("strjoin failed: `%s', component #%i", file_orig, i);
630       return (-1);
631     }
632
633     while (42) {
634       if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) {
635         if (errno == ENOENT) {
636           if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
637             break;
638
639           /* this might happen, if a different thread created
640            * the directory in the meantime
641            * => call stat() again to check for S_ISDIR() */
642           if (EEXIST == errno)
643             continue;
644
645           char errbuf[1024];
646           ERROR("check_create_dir: mkdir (%s): %s", dir,
647                 sstrerror(errno, errbuf, sizeof(errbuf)));
648           return (-1);
649         } else {
650           char errbuf[1024];
651           ERROR("check_create_dir: stat (%s): %s", dir,
652                 sstrerror(errno, errbuf, sizeof(errbuf)));
653           return (-1);
654         }
655       } else if (!S_ISDIR(statbuf.st_mode)) {
656         ERROR("check_create_dir: `%s' exists but is not "
657               "a directory!",
658               dir);
659         return (-1);
660       }
661       break;
662     }
663   }
664
665   return (0);
666 } /* check_create_dir */
667
668 #ifdef HAVE_LIBKSTAT
669 int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
670   char ident[128];
671
672   *ksp_ptr = NULL;
673
674   if (kc == NULL)
675     return (-1);
676
677   ssnprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name);
678
679   *ksp_ptr = kstat_lookup(kc, module, instance, name);
680   if (*ksp_ptr == NULL) {
681     ERROR("get_kstat: Cound not find kstat %s", ident);
682     return (-1);
683   }
684
685   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
686     ERROR("get_kstat: kstat %s has wrong type", ident);
687     *ksp_ptr = NULL;
688     return (-1);
689   }
690
691 #ifdef assert
692   assert(*ksp_ptr != NULL);
693   assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
694 #endif
695
696   if (kstat_read(kc, *ksp_ptr, NULL) == -1) {
697     ERROR("get_kstat: kstat %s could not be read", ident);
698     return (-1);
699   }
700
701   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
702     ERROR("get_kstat: kstat %s has wrong type", ident);
703     return (-1);
704   }
705
706   return (0);
707 }
708
709 long long get_kstat_value(kstat_t *ksp, char *name) {
710   kstat_named_t *kn;
711   long long retval = -1LL;
712
713   if (ksp == NULL) {
714     ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
715     return (-1LL);
716   } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
717     ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
718           "is not KSTAT_TYPE_NAMED (%#x).",
719           name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
720     return (-1LL);
721   }
722
723   if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL)
724     return (-1LL);
725
726   if (kn->data_type == KSTAT_DATA_INT32)
727     retval = (long long)kn->value.i32;
728   else if (kn->data_type == KSTAT_DATA_UINT32)
729     retval = (long long)kn->value.ui32;
730   else if (kn->data_type == KSTAT_DATA_INT64)
731     retval =
732         (long long)kn->value.i64; /* According to ANSI C99 `long long' must hold
733                                      at least 64 bits */
734   else if (kn->data_type == KSTAT_DATA_UINT64)
735     retval = (long long)kn->value.ui64; /* XXX: Might overflow! */
736   else
737     WARNING("get_kstat_value: Not a numeric value: %s", name);
738
739   return (retval);
740 }
741 #endif /* HAVE_LIBKSTAT */
742
743 #ifndef HAVE_HTONLL
744 unsigned long long ntohll(unsigned long long n) {
745 #if BYTE_ORDER == BIG_ENDIAN
746   return (n);
747 #else
748   return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32);
749 #endif
750 } /* unsigned long long ntohll */
751
752 unsigned long long htonll(unsigned long long n) {
753 #if BYTE_ORDER == BIG_ENDIAN
754   return (n);
755 #else
756   return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32);
757 #endif
758 } /* unsigned long long htonll */
759 #endif /* HAVE_HTONLL */
760
761 #if FP_LAYOUT_NEED_NOTHING
762 /* Well, we need nothing.. */
763 /* #endif FP_LAYOUT_NEED_NOTHING */
764
765 #elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
766 #if FP_LAYOUT_NEED_ENDIANFLIP
767 #define FP_CONVERT(A)                                                          \
768   ((((uint64_t)(A)&0xff00000000000000LL) >> 56) |                              \
769    (((uint64_t)(A)&0x00ff000000000000LL) >> 40) |                              \
770    (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) |                              \
771    (((uint64_t)(A)&0x000000ff00000000LL) >> 8) |                               \
772    (((uint64_t)(A)&0x00000000ff000000LL) << 8) |                               \
773    (((uint64_t)(A)&0x0000000000ff0000LL) << 24) |                              \
774    (((uint64_t)(A)&0x000000000000ff00LL) << 40) |                              \
775    (((uint64_t)(A)&0x00000000000000ffLL) << 56))
776 #else
777 #define FP_CONVERT(A)                                                          \
778   ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) |                              \
779    (((uint64_t)(A)&0x00000000ffffffffLL) << 32))
780 #endif
781
782 double ntohd(double d) {
783   union {
784     uint8_t byte[8];
785     uint64_t integer;
786     double floating;
787   } ret;
788
789   ret.floating = d;
790
791   /* NAN in x86 byte order */
792   if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) &&
793       (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) &&
794       (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) {
795     return (NAN);
796   } else {
797     uint64_t tmp;
798
799     tmp = ret.integer;
800     ret.integer = FP_CONVERT(tmp);
801     return (ret.floating);
802   }
803 } /* double ntohd */
804
805 double htond(double d) {
806   union {
807     uint8_t byte[8];
808     uint64_t integer;
809     double floating;
810   } ret;
811
812   if (isnan(d)) {
813     ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
814     ret.byte[4] = ret.byte[5] = 0x00;
815     ret.byte[6] = 0xf8;
816     ret.byte[7] = 0x7f;
817     return (ret.floating);
818   } else {
819     uint64_t tmp;
820
821     ret.floating = d;
822     tmp = FP_CONVERT(ret.integer);
823     ret.integer = tmp;
824     return (ret.floating);
825   }
826 } /* double htond */
827 #endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
828
829 int format_name(char *ret, int ret_len, const char *hostname,
830                 const char *plugin, const char *plugin_instance,
831                 const char *type, const char *type_instance) {
832   char *buffer;
833   size_t buffer_size;
834
835   buffer = ret;
836   buffer_size = (size_t)ret_len;
837
838 #define APPEND(str)                                                            \
839   do {                                                                         \
840     size_t l = strlen(str);                                                    \
841     if (l >= buffer_size)                                                      \
842       return (ENOBUFS);                                                        \
843     memcpy(buffer, (str), l);                                                  \
844     buffer += l;                                                               \
845     buffer_size -= l;                                                          \
846   } while (0)
847
848   assert(plugin != NULL);
849   assert(type != NULL);
850
851   APPEND(hostname);
852   APPEND("/");
853   APPEND(plugin);
854   if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) {
855     APPEND("-");
856     APPEND(plugin_instance);
857   }
858   APPEND("/");
859   APPEND(type);
860   if ((type_instance != NULL) && (type_instance[0] != 0)) {
861     APPEND("-");
862     APPEND(type_instance);
863   }
864   assert(buffer_size > 0);
865   buffer[0] = 0;
866
867 #undef APPEND
868   return (0);
869 } /* int format_name */
870
871 int format_values(char *ret, size_t ret_len, /* {{{ */
872                   const data_set_t *ds, const value_list_t *vl,
873                   _Bool store_rates) {
874   size_t offset = 0;
875   int status;
876   gauge_t *rates = NULL;
877
878   assert(0 == strcmp(ds->type, vl->type));
879
880   memset(ret, 0, ret_len);
881
882 #define BUFFER_ADD(...)                                                        \
883   do {                                                                         \
884     status = ssnprintf(ret + offset, ret_len - offset, __VA_ARGS__);           \
885     if (status < 1) {                                                          \
886       sfree(rates);                                                            \
887       return (-1);                                                             \
888     } else if (((size_t)status) >= (ret_len - offset)) {                       \
889       sfree(rates);                                                            \
890       return (-1);                                                             \
891     } else                                                                     \
892       offset += ((size_t)status);                                              \
893   } while (0)
894
895   BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time));
896
897   for (size_t i = 0; i < ds->ds_num; i++) {
898     if (ds->ds[i].type == DS_TYPE_GAUGE)
899       BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge);
900     else if (store_rates) {
901       if (rates == NULL)
902         rates = uc_get_rate(ds, vl);
903       if (rates == NULL) {
904         WARNING("format_values: uc_get_rate failed.");
905         return (-1);
906       }
907       BUFFER_ADD(":" GAUGE_FORMAT, rates[i]);
908     } else if (ds->ds[i].type == DS_TYPE_COUNTER)
909       BUFFER_ADD(":%llu", vl->values[i].counter);
910     else if (ds->ds[i].type == DS_TYPE_DERIVE)
911       BUFFER_ADD(":%" PRIi64, vl->values[i].derive);
912     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
913       BUFFER_ADD(":%" PRIu64, vl->values[i].absolute);
914     else {
915       ERROR("format_values: Unknown data source type: %i", ds->ds[i].type);
916       sfree(rates);
917       return (-1);
918     }
919   } /* for ds->ds_num */
920
921 #undef BUFFER_ADD
922
923   sfree(rates);
924   return (0);
925 } /* }}} int format_values */
926
927 int parse_identifier(char *str, char **ret_host, char **ret_plugin,
928                      char **ret_plugin_instance, char **ret_type,
929                      char **ret_type_instance, char *default_host) {
930   char *hostname = NULL;
931   char *plugin = NULL;
932   char *plugin_instance = NULL;
933   char *type = NULL;
934   char *type_instance = NULL;
935
936   hostname = str;
937   if (hostname == NULL)
938     return (-1);
939
940   plugin = strchr(hostname, '/');
941   if (plugin == NULL)
942     return (-1);
943   *plugin = '\0';
944   plugin++;
945
946   type = strchr(plugin, '/');
947   if (type == NULL) {
948     if (default_host == NULL)
949       return (-1);
950     /* else: no host specified; use default */
951     type = plugin;
952     plugin = hostname;
953     hostname = default_host;
954   } else {
955     *type = '\0';
956     type++;
957   }
958
959   plugin_instance = strchr(plugin, '-');
960   if (plugin_instance != NULL) {
961     *plugin_instance = '\0';
962     plugin_instance++;
963   }
964
965   type_instance = strchr(type, '-');
966   if (type_instance != NULL) {
967     *type_instance = '\0';
968     type_instance++;
969   }
970
971   *ret_host = hostname;
972   *ret_plugin = plugin;
973   *ret_plugin_instance = plugin_instance;
974   *ret_type = type;
975   *ret_type_instance = type_instance;
976   return (0);
977 } /* int parse_identifier */
978
979 int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */
980 {
981   char str_copy[6 * DATA_MAX_NAME_LEN];
982   char *host = NULL;
983   char *plugin = NULL;
984   char *plugin_instance = NULL;
985   char *type = NULL;
986   char *type_instance = NULL;
987   int status;
988
989   if ((str == NULL) || (vl == NULL))
990     return (EINVAL);
991
992   sstrncpy(str_copy, str, sizeof(str_copy));
993
994   status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type,
995                             &type_instance,
996                             /* default_host = */ NULL);
997   if (status != 0)
998     return (status);
999
1000   sstrncpy(vl->host, host, sizeof(vl->host));
1001   sstrncpy(vl->plugin, plugin, sizeof(vl->plugin));
1002   sstrncpy(vl->plugin_instance,
1003            (plugin_instance != NULL) ? plugin_instance : "",
1004            sizeof(vl->plugin_instance));
1005   sstrncpy(vl->type, type, sizeof(vl->type));
1006   sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "",
1007            sizeof(vl->type_instance));
1008
1009   return (0);
1010 } /* }}} int parse_identifier_vl */
1011
1012 int parse_value(const char *value_orig, value_t *ret_value, int ds_type) {
1013   char *value;
1014   char *endptr = NULL;
1015   size_t value_len;
1016
1017   if (value_orig == NULL)
1018     return (EINVAL);
1019
1020   value = strdup(value_orig);
1021   if (value == NULL)
1022     return (ENOMEM);
1023   value_len = strlen(value);
1024
1025   while ((value_len > 0) && isspace((int)value[value_len - 1])) {
1026     value[value_len - 1] = 0;
1027     value_len--;
1028   }
1029
1030   switch (ds_type) {
1031   case DS_TYPE_COUNTER:
1032     ret_value->counter = (counter_t)strtoull(value, &endptr, 0);
1033     break;
1034
1035   case DS_TYPE_GAUGE:
1036     ret_value->gauge = (gauge_t)strtod(value, &endptr);
1037     break;
1038
1039   case DS_TYPE_DERIVE:
1040     ret_value->derive = (derive_t)strtoll(value, &endptr, 0);
1041     break;
1042
1043   case DS_TYPE_ABSOLUTE:
1044     ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0);
1045     break;
1046
1047   default:
1048     sfree(value);
1049     ERROR("parse_value: Invalid data source type: %i.", ds_type);
1050     return -1;
1051   }
1052
1053   if (value == endptr) {
1054     ERROR("parse_value: Failed to parse string as %s: \"%s\".",
1055           DS_TYPE_TO_STRING(ds_type), value);
1056     sfree(value);
1057     return -1;
1058   } else if ((NULL != endptr) && ('\0' != *endptr))
1059     INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
1060          "Input string was \"%s\".",
1061          endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
1062
1063   sfree(value);
1064   return 0;
1065 } /* int parse_value */
1066
1067 int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) {
1068   size_t i;
1069   char *dummy;
1070   char *ptr;
1071   char *saveptr;
1072
1073   if ((buffer == NULL) || (vl == NULL) || (ds == NULL))
1074     return EINVAL;
1075
1076   i = 0;
1077   dummy = buffer;
1078   saveptr = NULL;
1079   vl->time = 0;
1080   while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) {
1081     dummy = NULL;
1082
1083     if (i >= vl->values_len) {
1084       /* Make sure i is invalid. */
1085       i = 0;
1086       break;
1087     }
1088
1089     if (vl->time == 0) {
1090       if (strcmp("N", ptr) == 0)
1091         vl->time = cdtime();
1092       else {
1093         char *endptr = NULL;
1094         double tmp;
1095
1096         errno = 0;
1097         tmp = strtod(ptr, &endptr);
1098         if ((errno != 0)        /* Overflow */
1099             || (endptr == ptr)  /* Invalid string */
1100             || (endptr == NULL) /* This should not happen */
1101             || (*endptr != 0))  /* Trailing chars */
1102           return (-1);
1103
1104         vl->time = DOUBLE_TO_CDTIME_T(tmp);
1105       }
1106
1107       continue;
1108     }
1109
1110     if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
1111       vl->values[i].gauge = NAN;
1112     else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type))
1113       return -1;
1114
1115     i++;
1116   } /* while (strtok_r) */
1117
1118   if ((ptr != NULL) || (i == 0))
1119     return (-1);
1120   return (0);
1121 } /* int parse_values */
1122
1123 int parse_value_file(char const *path, value_t *ret_value, int ds_type) {
1124   FILE *fh;
1125   char buffer[256];
1126
1127   fh = fopen(path, "r");
1128   if (fh == NULL)
1129     return (-1);
1130
1131   if (fgets(buffer, sizeof(buffer), fh) == NULL) {
1132     fclose(fh);
1133     return (-1);
1134   }
1135
1136   fclose(fh);
1137
1138   strstripnewline(buffer);
1139
1140   return parse_value(buffer, ret_value, ds_type);
1141 } /* int parse_value_file */
1142
1143 #if !HAVE_GETPWNAM_R
1144 int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
1145                struct passwd **pwbufp) {
1146   int status = 0;
1147   struct passwd *pw;
1148
1149   memset(pwbuf, '\0', sizeof(struct passwd));
1150
1151   pthread_mutex_lock(&getpwnam_r_lock);
1152
1153   do {
1154     pw = getpwnam(name);
1155     if (pw == NULL) {
1156       status = (errno != 0) ? errno : ENOENT;
1157       break;
1158     }
1159
1160 #define GETPWNAM_COPY_MEMBER(member)                                           \
1161   if (pw->member != NULL) {                                                    \
1162     int len = strlen(pw->member);                                              \
1163     if (len >= buflen) {                                                       \
1164       status = ENOMEM;                                                         \
1165       break;                                                                   \
1166     }                                                                          \
1167     sstrncpy(buf, pw->member, buflen);                                         \
1168     pwbuf->member = buf;                                                       \
1169     buf += (len + 1);                                                          \
1170     buflen -= (len + 1);                                                       \
1171   }
1172     GETPWNAM_COPY_MEMBER(pw_name);
1173     GETPWNAM_COPY_MEMBER(pw_passwd);
1174     GETPWNAM_COPY_MEMBER(pw_gecos);
1175     GETPWNAM_COPY_MEMBER(pw_dir);
1176     GETPWNAM_COPY_MEMBER(pw_shell);
1177
1178     pwbuf->pw_uid = pw->pw_uid;
1179     pwbuf->pw_gid = pw->pw_gid;
1180
1181     if (pwbufp != NULL)
1182       *pwbufp = pwbuf;
1183   } while (0);
1184
1185   pthread_mutex_unlock(&getpwnam_r_lock);
1186
1187   return (status);
1188 } /* int getpwnam_r */
1189 #endif /* !HAVE_GETPWNAM_R */
1190
1191 int notification_init(notification_t *n, int severity, const char *message,
1192                       const char *host, const char *plugin,
1193                       const char *plugin_instance, const char *type,
1194                       const char *type_instance) {
1195   memset(n, '\0', sizeof(notification_t));
1196
1197   n->severity = severity;
1198
1199   if (message != NULL)
1200     sstrncpy(n->message, message, sizeof(n->message));
1201   if (host != NULL)
1202     sstrncpy(n->host, host, sizeof(n->host));
1203   if (plugin != NULL)
1204     sstrncpy(n->plugin, plugin, sizeof(n->plugin));
1205   if (plugin_instance != NULL)
1206     sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance));
1207   if (type != NULL)
1208     sstrncpy(n->type, type, sizeof(n->type));
1209   if (type_instance != NULL)
1210     sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
1211
1212   return (0);
1213 } /* int notification_init */
1214
1215 int walk_directory(const char *dir, dirwalk_callback_f callback,
1216                    void *user_data, int include_hidden) {
1217   struct dirent *ent;
1218   DIR *dh;
1219   int success;
1220   int failure;
1221
1222   success = 0;
1223   failure = 0;
1224
1225   if ((dh = opendir(dir)) == NULL) {
1226     char errbuf[1024];
1227     ERROR("walk_directory: Cannot open '%s': %s", dir,
1228           sstrerror(errno, errbuf, sizeof(errbuf)));
1229     return -1;
1230   }
1231
1232   while ((ent = readdir(dh)) != NULL) {
1233     int status;
1234
1235     if (include_hidden) {
1236       if ((strcmp(".", ent->d_name) == 0) || (strcmp("..", ent->d_name) == 0))
1237         continue;
1238     } else /* if (!include_hidden) */
1239     {
1240       if (ent->d_name[0] == '.')
1241         continue;
1242     }
1243
1244     status = (*callback)(dir, ent->d_name, user_data);
1245     if (status != 0)
1246       failure++;
1247     else
1248       success++;
1249   }
1250
1251   closedir(dh);
1252
1253   if ((success == 0) && (failure > 0))
1254     return (-1);
1255   return (0);
1256 }
1257
1258 ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) {
1259   FILE *fh;
1260   ssize_t ret;
1261
1262   fh = fopen(filename, "r");
1263   if (fh == NULL)
1264     return (-1);
1265
1266   ret = (ssize_t)fread(buf, 1, bufsize, fh);
1267   if ((ret == 0) && (ferror(fh) != 0)) {
1268     ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
1269     ret = -1;
1270   }
1271
1272   fclose(fh);
1273   return (ret);
1274 }
1275
1276 counter_t counter_diff(counter_t old_value, counter_t new_value) {
1277   counter_t diff;
1278
1279   if (old_value > new_value) {
1280     if (old_value <= 4294967295U)
1281       diff = (4294967295U - old_value) + new_value + 1;
1282     else
1283       diff = (18446744073709551615ULL - old_value) + new_value + 1;
1284   } else {
1285     diff = new_value - old_value;
1286   }
1287
1288   return (diff);
1289 } /* counter_t counter_diff */
1290
1291 int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */
1292                   rate_to_value_state_t *state, int ds_type, cdtime_t t) {
1293   gauge_t delta_gauge;
1294   cdtime_t delta_t;
1295
1296   if (ds_type == DS_TYPE_GAUGE) {
1297     state->last_value.gauge = rate;
1298     state->last_time = t;
1299
1300     *ret_value = state->last_value;
1301     return (0);
1302   }
1303
1304   /* Counter and absolute can't handle negative rates. Reset "last time"
1305    * to zero, so that the next valid rate will re-initialize the
1306    * structure. */
1307   if ((rate < 0.0) &&
1308       ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) {
1309     memset(state, 0, sizeof(*state));
1310     return (EINVAL);
1311   }
1312
1313   /* Another invalid state: The time is not increasing. */
1314   if (t <= state->last_time) {
1315     memset(state, 0, sizeof(*state));
1316     return (EINVAL);
1317   }
1318
1319   delta_t = t - state->last_time;
1320   delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual;
1321
1322   /* Previous value is invalid. */
1323   if (state->last_time == 0) /* {{{ */
1324   {
1325     if (ds_type == DS_TYPE_DERIVE) {
1326       state->last_value.derive = (derive_t)rate;
1327       state->residual = rate - ((gauge_t)state->last_value.derive);
1328     } else if (ds_type == DS_TYPE_COUNTER) {
1329       state->last_value.counter = (counter_t)rate;
1330       state->residual = rate - ((gauge_t)state->last_value.counter);
1331     } else if (ds_type == DS_TYPE_ABSOLUTE) {
1332       state->last_value.absolute = (absolute_t)rate;
1333       state->residual = rate - ((gauge_t)state->last_value.absolute);
1334     } else {
1335       assert(23 == 42);
1336     }
1337
1338     state->last_time = t;
1339     return (EAGAIN);
1340   } /* }}} */
1341
1342   if (ds_type == DS_TYPE_DERIVE) {
1343     derive_t delta_derive = (derive_t)delta_gauge;
1344
1345     state->last_value.derive += delta_derive;
1346     state->residual = delta_gauge - ((gauge_t)delta_derive);
1347   } else if (ds_type == DS_TYPE_COUNTER) {
1348     counter_t delta_counter = (counter_t)delta_gauge;
1349
1350     state->last_value.counter += delta_counter;
1351     state->residual = delta_gauge - ((gauge_t)delta_counter);
1352   } else if (ds_type == DS_TYPE_ABSOLUTE) {
1353     absolute_t delta_absolute = (absolute_t)delta_gauge;
1354
1355     state->last_value.absolute = delta_absolute;
1356     state->residual = delta_gauge - ((gauge_t)delta_absolute);
1357   } else {
1358     assert(23 == 42);
1359   }
1360
1361   state->last_time = t;
1362   *ret_value = state->last_value;
1363   return (0);
1364 } /* }}} value_t rate_to_value */
1365
1366 int value_to_rate(gauge_t *ret_rate, /* {{{ */
1367                   value_t value, int ds_type, cdtime_t t,
1368                   value_to_rate_state_t *state) {
1369   gauge_t interval;
1370
1371   /* Another invalid state: The time is not increasing. */
1372   if (t <= state->last_time) {
1373     memset(state, 0, sizeof(*state));
1374     return (EINVAL);
1375   }
1376
1377   interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
1378
1379   /* Previous value is invalid. */
1380   if (state->last_time == 0) {
1381     state->last_value = value;
1382     state->last_time = t;
1383     return (EAGAIN);
1384   }
1385
1386   switch (ds_type) {
1387   case DS_TYPE_DERIVE: {
1388     derive_t diff = value.derive - state->last_value.derive;
1389     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1390     break;
1391   }
1392   case DS_TYPE_GAUGE: {
1393     *ret_rate = value.gauge;
1394     break;
1395   }
1396   case DS_TYPE_COUNTER: {
1397     counter_t diff = counter_diff(state->last_value.counter, value.counter);
1398     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1399     break;
1400   }
1401   case DS_TYPE_ABSOLUTE: {
1402     absolute_t diff = value.absolute;
1403     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1404     break;
1405   }
1406   default:
1407     return EINVAL;
1408   }
1409
1410   state->last_value = value;
1411   state->last_time = t;
1412   return (0);
1413 } /* }}} value_t rate_to_value */
1414
1415 int service_name_to_port_number(const char *service_name) {
1416   struct addrinfo *ai_list;
1417   int status;
1418   int service_number;
1419
1420   if (service_name == NULL)
1421     return (-1);
1422
1423   struct addrinfo ai_hints = {.ai_family = AF_UNSPEC};
1424
1425   status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list);
1426   if (status != 0) {
1427     ERROR("service_name_to_port_number: getaddrinfo failed: %s",
1428           gai_strerror(status));
1429     return (-1);
1430   }
1431
1432   service_number = -1;
1433   for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
1434        ai_ptr = ai_ptr->ai_next) {
1435     if (ai_ptr->ai_family == AF_INET) {
1436       struct sockaddr_in *sa;
1437
1438       sa = (void *)ai_ptr->ai_addr;
1439       service_number = (int)ntohs(sa->sin_port);
1440     } else if (ai_ptr->ai_family == AF_INET6) {
1441       struct sockaddr_in6 *sa;
1442
1443       sa = (void *)ai_ptr->ai_addr;
1444       service_number = (int)ntohs(sa->sin6_port);
1445     }
1446
1447     if ((service_number > 0) && (service_number <= 65535))
1448       break;
1449   }
1450
1451   freeaddrinfo(ai_list);
1452
1453   if ((service_number > 0) && (service_number <= 65535))
1454     return (service_number);
1455   return (-1);
1456 } /* int service_name_to_port_number */
1457
1458 void set_sock_opts(int sockfd) /* {{{ */
1459 {
1460   int status;
1461   int socktype;
1462
1463   status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
1464                       &(socklen_t){sizeof(socktype)});
1465   if (status != 0) {
1466     WARNING("set_sock_opts: failed to determine socket type");
1467     return;
1468   }
1469
1470   if (socktype == SOCK_STREAM) {
1471     status =
1472         setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
1473     if (status != 0)
1474       WARNING("set_sock_opts: failed to set socket keepalive flag");
1475
1476 #ifdef TCP_KEEPIDLE
1477     int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
1478     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle,
1479                         sizeof(tcp_keepidle));
1480     if (status != 0)
1481       WARNING("set_sock_opts: failed to set socket tcp keepalive time");
1482 #endif
1483
1484 #ifdef TCP_KEEPINTVL
1485     int tcp_keepintvl =
1486         ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
1487     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl,
1488                         sizeof(tcp_keepintvl));
1489     if (status != 0)
1490       WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
1491 #endif
1492   }
1493 } /* }}} void set_sock_opts */
1494
1495 int strtoderive(const char *string, derive_t *ret_value) /* {{{ */
1496 {
1497   derive_t tmp;
1498   char *endptr;
1499
1500   if ((string == NULL) || (ret_value == NULL))
1501     return (EINVAL);
1502
1503   errno = 0;
1504   endptr = NULL;
1505   tmp = (derive_t)strtoll(string, &endptr, /* base = */ 0);
1506   if ((endptr == string) || (errno != 0))
1507     return (-1);
1508
1509   *ret_value = tmp;
1510   return (0);
1511 } /* }}} int strtoderive */
1512
1513 int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */
1514 {
1515   gauge_t tmp;
1516   char *endptr = NULL;
1517
1518   if ((string == NULL) || (ret_value == NULL))
1519     return (EINVAL);
1520
1521   errno = 0;
1522   endptr = NULL;
1523   tmp = (gauge_t)strtod(string, &endptr);
1524   if (errno != 0)
1525     return (errno);
1526   else if ((endptr == NULL) || (*endptr != 0))
1527     return (EINVAL);
1528
1529   *ret_value = tmp;
1530   return (0);
1531 } /* }}} int strtogauge */
1532
1533 int strarray_add(char ***ret_array, size_t *ret_array_len,
1534                  char const *str) /* {{{ */
1535 {
1536   char **array;
1537   size_t array_len = *ret_array_len;
1538
1539   if (str == NULL)
1540     return (EINVAL);
1541
1542   array = realloc(*ret_array, (array_len + 1) * sizeof(*array));
1543   if (array == NULL)
1544     return (ENOMEM);
1545   *ret_array = array;
1546
1547   array[array_len] = strdup(str);
1548   if (array[array_len] == NULL)
1549     return (ENOMEM);
1550
1551   array_len++;
1552   *ret_array_len = array_len;
1553   return (0);
1554 } /* }}} int strarray_add */
1555
1556 void strarray_free(char **array, size_t array_len) /* {{{ */
1557 {
1558   for (size_t i = 0; i < array_len; i++)
1559     sfree(array[i]);
1560   sfree(array);
1561 } /* }}} void strarray_free */
1562
1563 #if HAVE_CAPABILITY
1564 int check_capability(int arg) /* {{{ */
1565 {
1566   cap_value_t cap = (cap_value_t)arg;
1567
1568   if (!CAP_IS_SUPPORTED(cap))
1569     return (-1);
1570
1571   int have_cap = cap_get_bound(cap);
1572   if (have_cap != 1)
1573     return (-1);
1574
1575   return (0);
1576 } /* }}} int check_capability */
1577 #else
1578 int check_capability(__attribute__((unused)) int arg) /* {{{ */
1579 {
1580   WARNING("check_capability: unsupported capability implementation. "
1581           "Some plugin(s) may require elevated privileges to work properly.");
1582   return (0);
1583 } /* }}} int check_capability */
1584 #endif /* HAVE_CAPABILITY */