Tree-wide: remove last remnants of sizeof(char)
[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, 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     errno = EINVAL;
272     return errno;
273   }
274
275   /* checking for closed peer connection */
276   pfd.fd = fd;
277   pfd.events = POLLIN | POLLHUP;
278   pfd.revents = 0;
279   if (poll(&pfd, 1, 0) > 0) {
280     char buffer[32];
281     if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
282       /* if recv returns zero (even though poll() said there is data to be
283        * read), that means the connection has been closed */
284       return errno ? errno : -1;
285     }
286   }
287
288   while (nleft > 0) {
289     status = write(fd, (const void *)ptr, nleft);
290
291     if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
292       continue;
293
294     if (status < 0)
295       return errno ? errno : status;
296
297     nleft = nleft - ((size_t)status);
298     ptr = ptr + ((size_t)status);
299   }
300
301   return (0);
302 }
303
304 int strsplit(char *string, char **fields, size_t size) {
305   size_t i;
306   char *ptr;
307   char *saveptr;
308
309   i = 0;
310   ptr = string;
311   saveptr = NULL;
312   while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) {
313     ptr = NULL;
314     i++;
315
316     if (i >= size)
317       break;
318   }
319
320   return ((int)i);
321 }
322
323 int strjoin(char *buffer, size_t buffer_size, char **fields, size_t fields_num,
324             const char *sep) {
325   size_t avail = 0;
326   char *ptr = buffer;
327   size_t sep_len = 0;
328
329   size_t buffer_req = 0;
330
331   if (((fields_num != 0) && (fields == NULL)) ||
332       ((buffer_size != 0) && (buffer == NULL)))
333     return (-EINVAL);
334
335   if (buffer != NULL)
336     buffer[0] = 0;
337
338   if (buffer_size != 0)
339     avail = buffer_size - 1;
340
341   if (sep != NULL)
342     sep_len = strlen(sep);
343
344   for (size_t i = 0; i < fields_num; i++) {
345     size_t field_len = strlen(fields[i]);
346
347     if (i != 0)
348       buffer_req += sep_len;
349     buffer_req += field_len;
350
351     if ((i != 0) && (sep_len > 0)) {
352       if (sep_len >= avail) {
353         /* prevent subsequent iterations from writing to the
354          * buffer. */
355         avail = 0;
356         continue;
357       }
358
359       memcpy(ptr, sep, sep_len);
360
361       ptr += sep_len;
362       avail -= sep_len;
363     }
364
365     if (field_len > avail)
366       field_len = avail;
367
368     memcpy(ptr, fields[i], field_len);
369     ptr += field_len;
370
371     avail -= field_len;
372     if (ptr != NULL)
373       *ptr = 0;
374   }
375
376   return (int)buffer_req;
377 }
378
379 int escape_string(char *buffer, size_t buffer_size) {
380   char *temp;
381   size_t j;
382
383   /* Check if we need to escape at all first */
384   temp = strpbrk(buffer, " \t\"\\");
385   if (temp == NULL)
386     return (0);
387
388   if (buffer_size < 3)
389     return (EINVAL);
390
391   temp = calloc(1, buffer_size);
392   if (temp == NULL)
393     return (ENOMEM);
394
395   temp[0] = '"';
396   j = 1;
397
398   for (size_t i = 0; i < buffer_size; i++) {
399     if (buffer[i] == 0) {
400       break;
401     } else if ((buffer[i] == '"') || (buffer[i] == '\\')) {
402       if (j > (buffer_size - 4))
403         break;
404       temp[j] = '\\';
405       temp[j + 1] = buffer[i];
406       j += 2;
407     } else {
408       if (j > (buffer_size - 3))
409         break;
410       temp[j] = buffer[i];
411       j++;
412     }
413   }
414
415   assert((j + 1) < buffer_size);
416   temp[j] = '"';
417   temp[j + 1] = 0;
418
419   sstrncpy(buffer, temp, buffer_size);
420   sfree(temp);
421   return (0);
422 } /* int escape_string */
423
424 int strunescape(char *buf, size_t buf_len) {
425   for (size_t i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) {
426     if (buf[i] != '\\')
427       continue;
428
429     if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
430       ERROR("string unescape: backslash found at end of string.");
431       /* Ensure null-byte at the end of the buffer. */
432       buf[i] = 0;
433       return (-1);
434     }
435
436     switch (buf[i + 1]) {
437     case 't':
438       buf[i] = '\t';
439       break;
440     case 'n':
441       buf[i] = '\n';
442       break;
443     case 'r':
444       buf[i] = '\r';
445       break;
446     default:
447       buf[i] = buf[i + 1];
448       break;
449     }
450
451     /* Move everything after the position one position to the left.
452      * Add a null-byte as last character in the buffer. */
453     memmove(buf + i + 1, buf + i + 2, buf_len - i - 2);
454     buf[buf_len - 1] = 0;
455   }
456   return (0);
457 } /* int strunescape */
458
459 size_t strstripnewline(char *buffer) {
460   size_t buffer_len = strlen(buffer);
461
462   while (buffer_len > 0) {
463     if ((buffer[buffer_len - 1] != '\n') && (buffer[buffer_len - 1] != '\r'))
464       break;
465     buffer_len--;
466     buffer[buffer_len] = 0;
467   }
468
469   return (buffer_len);
470 } /* size_t strstripnewline */
471
472 int escape_slashes(char *buffer, size_t buffer_size) {
473   size_t buffer_len;
474
475   buffer_len = strlen(buffer);
476
477   if (buffer_len <= 1) {
478     if (strcmp("/", buffer) == 0) {
479       if (buffer_size < 5)
480         return (-1);
481       sstrncpy(buffer, "root", buffer_size);
482     }
483     return (0);
484   }
485
486   /* Move one to the left */
487   if (buffer[0] == '/') {
488     memmove(buffer, buffer + 1, buffer_len);
489     buffer_len--;
490   }
491
492   for (size_t i = 0; i < buffer_len; i++) {
493     if (buffer[i] == '/')
494       buffer[i] = '_';
495   }
496
497   return (0);
498 } /* int escape_slashes */
499
500 void replace_special(char *buffer, size_t buffer_size) {
501   for (size_t i = 0; i < buffer_size; i++) {
502     if (buffer[i] == 0)
503       return;
504     if ((!isalnum((int)buffer[i])) && (buffer[i] != '-'))
505       buffer[i] = '_';
506   }
507 } /* void replace_special */
508
509 int timeval_cmp(struct timeval tv0, struct timeval tv1, struct timeval *delta) {
510   struct timeval *larger;
511   struct timeval *smaller;
512
513   int status;
514
515   NORMALIZE_TIMEVAL(tv0);
516   NORMALIZE_TIMEVAL(tv1);
517
518   if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) {
519     if (delta != NULL) {
520       delta->tv_sec = 0;
521       delta->tv_usec = 0;
522     }
523     return (0);
524   }
525
526   if ((tv0.tv_sec < tv1.tv_sec) ||
527       ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) {
528     larger = &tv1;
529     smaller = &tv0;
530     status = -1;
531   } else {
532     larger = &tv0;
533     smaller = &tv1;
534     status = 1;
535   }
536
537   if (delta != NULL) {
538     delta->tv_sec = larger->tv_sec - smaller->tv_sec;
539
540     if (smaller->tv_usec <= larger->tv_usec)
541       delta->tv_usec = larger->tv_usec - smaller->tv_usec;
542     else {
543       --delta->tv_sec;
544       delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec;
545     }
546   }
547
548   assert((delta == NULL) ||
549          ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000)));
550
551   return (status);
552 } /* int timeval_cmp */
553
554 int check_create_dir(const char *file_orig) {
555   struct stat statbuf;
556
557   char file_copy[512];
558   char dir[512];
559   int dir_len = 512;
560   char *fields[16];
561   int fields_num;
562   char *ptr;
563   char *saveptr;
564   int last_is_file = 1;
565   int path_is_absolute = 0;
566   size_t len;
567
568   /*
569    * Sanity checks first
570    */
571   if (file_orig == NULL)
572     return (-1);
573
574   if ((len = strlen(file_orig)) < 1)
575     return (-1);
576   else if (len >= sizeof(file_copy))
577     return (-1);
578
579   /*
580    * If `file_orig' ends in a slash the last component is a directory,
581    * otherwise it's a file. Act accordingly..
582    */
583   if (file_orig[len - 1] == '/')
584     last_is_file = 0;
585   if (file_orig[0] == '/')
586     path_is_absolute = 1;
587
588   /*
589    * Create a copy for `strtok_r' to destroy
590    */
591   sstrncpy(file_copy, file_orig, sizeof(file_copy));
592
593   /*
594    * Break into components. This will eat up several slashes in a row and
595    * remove leading and trailing slashes..
596    */
597   ptr = file_copy;
598   saveptr = NULL;
599   fields_num = 0;
600   while ((fields[fields_num] = strtok_r(ptr, "/", &saveptr)) != NULL) {
601     ptr = NULL;
602     fields_num++;
603
604     if (fields_num >= 16)
605       break;
606   }
607
608   /*
609    * For each component, do..
610    */
611   for (int i = 0; i < (fields_num - last_is_file); i++) {
612     /*
613      * Do not create directories that start with a dot. This
614      * prevents `../../' attacks and other likely malicious
615      * behavior.
616      */
617     if (fields[i][0] == '.') {
618       ERROR("Cowardly refusing to create a directory that "
619             "begins with a `.' (dot): `%s'",
620             file_orig);
621       return (-2);
622     }
623
624     /*
625      * Join the components together again
626      */
627     dir[0] = '/';
628     if (strjoin(dir + path_is_absolute, (size_t)(dir_len - path_is_absolute),
629                 fields, (size_t)(i + 1), "/") < 0) {
630       ERROR("strjoin failed: `%s', component #%i", file_orig, i);
631       return (-1);
632     }
633
634     while (42) {
635       if ((stat(dir, &statbuf) == -1) && (lstat(dir, &statbuf) == -1)) {
636         if (errno == ENOENT) {
637           if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
638             break;
639
640           /* this might happen, if a different thread created
641            * the directory in the meantime
642            * => call stat() again to check for S_ISDIR() */
643           if (EEXIST == errno)
644             continue;
645
646           char errbuf[1024];
647           ERROR("check_create_dir: mkdir (%s): %s", dir,
648                 sstrerror(errno, errbuf, sizeof(errbuf)));
649           return (-1);
650         } else {
651           char errbuf[1024];
652           ERROR("check_create_dir: stat (%s): %s", dir,
653                 sstrerror(errno, errbuf, sizeof(errbuf)));
654           return (-1);
655         }
656       } else if (!S_ISDIR(statbuf.st_mode)) {
657         ERROR("check_create_dir: `%s' exists but is not "
658               "a directory!",
659               dir);
660         return (-1);
661       }
662       break;
663     }
664   }
665
666   return (0);
667 } /* check_create_dir */
668
669 #ifdef HAVE_LIBKSTAT
670 int get_kstat(kstat_t **ksp_ptr, char *module, int instance, char *name) {
671   char ident[128];
672
673   *ksp_ptr = NULL;
674
675   if (kc == NULL)
676     return (-1);
677
678   ssnprintf(ident, sizeof(ident), "%s,%i,%s", module, instance, name);
679
680   *ksp_ptr = kstat_lookup(kc, module, instance, name);
681   if (*ksp_ptr == NULL) {
682     ERROR("get_kstat: Cound not find kstat %s", ident);
683     return (-1);
684   }
685
686   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
687     ERROR("get_kstat: kstat %s has wrong type", ident);
688     *ksp_ptr = NULL;
689     return (-1);
690   }
691
692 #ifdef assert
693   assert(*ksp_ptr != NULL);
694   assert((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
695 #endif
696
697   if (kstat_read(kc, *ksp_ptr, NULL) == -1) {
698     ERROR("get_kstat: kstat %s could not be read", ident);
699     return (-1);
700   }
701
702   if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) {
703     ERROR("get_kstat: kstat %s has wrong type", ident);
704     return (-1);
705   }
706
707   return (0);
708 }
709
710 long long get_kstat_value(kstat_t *ksp, char *name) {
711   kstat_named_t *kn;
712   long long retval = -1LL;
713
714   if (ksp == NULL) {
715     ERROR("get_kstat_value (\"%s\"): ksp is NULL.", name);
716     return (-1LL);
717   } else if (ksp->ks_type != KSTAT_TYPE_NAMED) {
718     ERROR("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
719           "is not KSTAT_TYPE_NAMED (%#x).",
720           name, (unsigned int)ksp->ks_type, (unsigned int)KSTAT_TYPE_NAMED);
721     return (-1LL);
722   }
723
724   if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, name)) == NULL)
725     return (-1LL);
726
727   if (kn->data_type == KSTAT_DATA_INT32)
728     retval = (long long)kn->value.i32;
729   else if (kn->data_type == KSTAT_DATA_UINT32)
730     retval = (long long)kn->value.ui32;
731   else if (kn->data_type == KSTAT_DATA_INT64)
732     retval =
733         (long long)kn->value.i64; /* According to ANSI C99 `long long' must hold
734                                      at least 64 bits */
735   else if (kn->data_type == KSTAT_DATA_UINT64)
736     retval = (long long)kn->value.ui64; /* XXX: Might overflow! */
737   else
738     WARNING("get_kstat_value: Not a numeric value: %s", name);
739
740   return (retval);
741 }
742 #endif /* HAVE_LIBKSTAT */
743
744 #ifndef HAVE_HTONLL
745 unsigned long long ntohll(unsigned long long n) {
746 #if BYTE_ORDER == BIG_ENDIAN
747   return (n);
748 #else
749   return (((unsigned long long)ntohl(n)) << 32) + ntohl(n >> 32);
750 #endif
751 } /* unsigned long long ntohll */
752
753 unsigned long long htonll(unsigned long long n) {
754 #if BYTE_ORDER == BIG_ENDIAN
755   return (n);
756 #else
757   return (((unsigned long long)htonl(n)) << 32) + htonl(n >> 32);
758 #endif
759 } /* unsigned long long htonll */
760 #endif /* HAVE_HTONLL */
761
762 #if FP_LAYOUT_NEED_NOTHING
763 /* Well, we need nothing.. */
764 /* #endif FP_LAYOUT_NEED_NOTHING */
765
766 #elif FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP
767 #if FP_LAYOUT_NEED_ENDIANFLIP
768 #define FP_CONVERT(A)                                                          \
769   ((((uint64_t)(A)&0xff00000000000000LL) >> 56) |                              \
770    (((uint64_t)(A)&0x00ff000000000000LL) >> 40) |                              \
771    (((uint64_t)(A)&0x0000ff0000000000LL) >> 24) |                              \
772    (((uint64_t)(A)&0x000000ff00000000LL) >> 8) |                               \
773    (((uint64_t)(A)&0x00000000ff000000LL) << 8) |                               \
774    (((uint64_t)(A)&0x0000000000ff0000LL) << 24) |                              \
775    (((uint64_t)(A)&0x000000000000ff00LL) << 40) |                              \
776    (((uint64_t)(A)&0x00000000000000ffLL) << 56))
777 #else
778 #define FP_CONVERT(A)                                                          \
779   ((((uint64_t)(A)&0xffffffff00000000LL) >> 32) |                              \
780    (((uint64_t)(A)&0x00000000ffffffffLL) << 32))
781 #endif
782
783 double ntohd(double d) {
784   union {
785     uint8_t byte[8];
786     uint64_t integer;
787     double floating;
788   } ret;
789
790   ret.floating = d;
791
792   /* NAN in x86 byte order */
793   if ((ret.byte[0] == 0x00) && (ret.byte[1] == 0x00) && (ret.byte[2] == 0x00) &&
794       (ret.byte[3] == 0x00) && (ret.byte[4] == 0x00) && (ret.byte[5] == 0x00) &&
795       (ret.byte[6] == 0xf8) && (ret.byte[7] == 0x7f)) {
796     return (NAN);
797   } else {
798     uint64_t tmp;
799
800     tmp = ret.integer;
801     ret.integer = FP_CONVERT(tmp);
802     return (ret.floating);
803   }
804 } /* double ntohd */
805
806 double htond(double d) {
807   union {
808     uint8_t byte[8];
809     uint64_t integer;
810     double floating;
811   } ret;
812
813   if (isnan(d)) {
814     ret.byte[0] = ret.byte[1] = ret.byte[2] = ret.byte[3] = 0x00;
815     ret.byte[4] = ret.byte[5] = 0x00;
816     ret.byte[6] = 0xf8;
817     ret.byte[7] = 0x7f;
818     return (ret.floating);
819   } else {
820     uint64_t tmp;
821
822     ret.floating = d;
823     tmp = FP_CONVERT(ret.integer);
824     ret.integer = tmp;
825     return (ret.floating);
826   }
827 } /* double htond */
828 #endif /* FP_LAYOUT_NEED_ENDIANFLIP || FP_LAYOUT_NEED_INTSWAP */
829
830 int format_name(char *ret, int ret_len, const char *hostname,
831                 const char *plugin, const char *plugin_instance,
832                 const char *type, const char *type_instance) {
833   char *buffer;
834   size_t buffer_size;
835
836   buffer = ret;
837   buffer_size = (size_t)ret_len;
838
839 #define APPEND(str)                                                            \
840   do {                                                                         \
841     size_t l = strlen(str);                                                    \
842     if (l >= buffer_size)                                                      \
843       return (ENOBUFS);                                                        \
844     memcpy(buffer, (str), l);                                                  \
845     buffer += l;                                                               \
846     buffer_size -= l;                                                          \
847   } while (0)
848
849   assert(plugin != NULL);
850   assert(type != NULL);
851
852   APPEND(hostname);
853   APPEND("/");
854   APPEND(plugin);
855   if ((plugin_instance != NULL) && (plugin_instance[0] != 0)) {
856     APPEND("-");
857     APPEND(plugin_instance);
858   }
859   APPEND("/");
860   APPEND(type);
861   if ((type_instance != NULL) && (type_instance[0] != 0)) {
862     APPEND("-");
863     APPEND(type_instance);
864   }
865   assert(buffer_size > 0);
866   buffer[0] = 0;
867
868 #undef APPEND
869   return (0);
870 } /* int format_name */
871
872 int format_values(char *ret, size_t ret_len, /* {{{ */
873                   const data_set_t *ds, const value_list_t *vl,
874                   _Bool store_rates) {
875   size_t offset = 0;
876   int status;
877   gauge_t *rates = NULL;
878
879   assert(0 == strcmp(ds->type, vl->type));
880
881   memset(ret, 0, ret_len);
882
883 #define BUFFER_ADD(...)                                                        \
884   do {                                                                         \
885     status = ssnprintf(ret + offset, ret_len - offset, __VA_ARGS__);           \
886     if (status < 1) {                                                          \
887       sfree(rates);                                                            \
888       return (-1);                                                             \
889     } else if (((size_t)status) >= (ret_len - offset)) {                       \
890       sfree(rates);                                                            \
891       return (-1);                                                             \
892     } else                                                                     \
893       offset += ((size_t)status);                                              \
894   } while (0)
895
896   BUFFER_ADD("%.3f", CDTIME_T_TO_DOUBLE(vl->time));
897
898   for (size_t i = 0; i < ds->ds_num; i++) {
899     if (ds->ds[i].type == DS_TYPE_GAUGE)
900       BUFFER_ADD(":" GAUGE_FORMAT, vl->values[i].gauge);
901     else if (store_rates) {
902       if (rates == NULL)
903         rates = uc_get_rate(ds, vl);
904       if (rates == NULL) {
905         WARNING("format_values: uc_get_rate failed.");
906         return (-1);
907       }
908       BUFFER_ADD(":" GAUGE_FORMAT, rates[i]);
909     } else if (ds->ds[i].type == DS_TYPE_COUNTER)
910       BUFFER_ADD(":%llu", vl->values[i].counter);
911     else if (ds->ds[i].type == DS_TYPE_DERIVE)
912       BUFFER_ADD(":%" PRIi64, vl->values[i].derive);
913     else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
914       BUFFER_ADD(":%" PRIu64, vl->values[i].absolute);
915     else {
916       ERROR("format_values: Unknown data source type: %i", ds->ds[i].type);
917       sfree(rates);
918       return (-1);
919     }
920   } /* for ds->ds_num */
921
922 #undef BUFFER_ADD
923
924   sfree(rates);
925   return (0);
926 } /* }}} int format_values */
927
928 int parse_identifier(char *str, char **ret_host, char **ret_plugin,
929                      char **ret_plugin_instance, char **ret_type,
930                      char **ret_type_instance, char *default_host) {
931   char *hostname = NULL;
932   char *plugin = NULL;
933   char *plugin_instance = NULL;
934   char *type = NULL;
935   char *type_instance = NULL;
936
937   hostname = str;
938   if (hostname == NULL)
939     return (-1);
940
941   plugin = strchr(hostname, '/');
942   if (plugin == NULL)
943     return (-1);
944   *plugin = '\0';
945   plugin++;
946
947   type = strchr(plugin, '/');
948   if (type == NULL) {
949     if (default_host == NULL)
950       return (-1);
951     /* else: no host specified; use default */
952     type = plugin;
953     plugin = hostname;
954     hostname = default_host;
955   } else {
956     *type = '\0';
957     type++;
958   }
959
960   plugin_instance = strchr(plugin, '-');
961   if (plugin_instance != NULL) {
962     *plugin_instance = '\0';
963     plugin_instance++;
964   }
965
966   type_instance = strchr(type, '-');
967   if (type_instance != NULL) {
968     *type_instance = '\0';
969     type_instance++;
970   }
971
972   *ret_host = hostname;
973   *ret_plugin = plugin;
974   *ret_plugin_instance = plugin_instance;
975   *ret_type = type;
976   *ret_type_instance = type_instance;
977   return (0);
978 } /* int parse_identifier */
979
980 int parse_identifier_vl(const char *str, value_list_t *vl) /* {{{ */
981 {
982   char str_copy[6 * DATA_MAX_NAME_LEN];
983   char *host = NULL;
984   char *plugin = NULL;
985   char *plugin_instance = NULL;
986   char *type = NULL;
987   char *type_instance = NULL;
988   int status;
989
990   if ((str == NULL) || (vl == NULL))
991     return (EINVAL);
992
993   sstrncpy(str_copy, str, sizeof(str_copy));
994
995   status = parse_identifier(str_copy, &host, &plugin, &plugin_instance, &type,
996                             &type_instance,
997                             /* default_host = */ NULL);
998   if (status != 0)
999     return (status);
1000
1001   sstrncpy(vl->host, host, sizeof(vl->host));
1002   sstrncpy(vl->plugin, plugin, sizeof(vl->plugin));
1003   sstrncpy(vl->plugin_instance,
1004            (plugin_instance != NULL) ? plugin_instance : "",
1005            sizeof(vl->plugin_instance));
1006   sstrncpy(vl->type, type, sizeof(vl->type));
1007   sstrncpy(vl->type_instance, (type_instance != NULL) ? type_instance : "",
1008            sizeof(vl->type_instance));
1009
1010   return (0);
1011 } /* }}} int parse_identifier_vl */
1012
1013 int parse_value(const char *value_orig, value_t *ret_value, int ds_type) {
1014   char *value;
1015   char *endptr = NULL;
1016   size_t value_len;
1017
1018   if (value_orig == NULL)
1019     return (EINVAL);
1020
1021   value = strdup(value_orig);
1022   if (value == NULL)
1023     return (ENOMEM);
1024   value_len = strlen(value);
1025
1026   while ((value_len > 0) && isspace((int)value[value_len - 1])) {
1027     value[value_len - 1] = 0;
1028     value_len--;
1029   }
1030
1031   switch (ds_type) {
1032   case DS_TYPE_COUNTER:
1033     ret_value->counter = (counter_t)strtoull(value, &endptr, 0);
1034     break;
1035
1036   case DS_TYPE_GAUGE:
1037     ret_value->gauge = (gauge_t)strtod(value, &endptr);
1038     break;
1039
1040   case DS_TYPE_DERIVE:
1041     ret_value->derive = (derive_t)strtoll(value, &endptr, 0);
1042     break;
1043
1044   case DS_TYPE_ABSOLUTE:
1045     ret_value->absolute = (absolute_t)strtoull(value, &endptr, 0);
1046     break;
1047
1048   default:
1049     sfree(value);
1050     ERROR("parse_value: Invalid data source type: %i.", ds_type);
1051     return -1;
1052   }
1053
1054   if (value == endptr) {
1055     ERROR("parse_value: Failed to parse string as %s: \"%s\".",
1056           DS_TYPE_TO_STRING(ds_type), value);
1057     sfree(value);
1058     return -1;
1059   } else if ((NULL != endptr) && ('\0' != *endptr))
1060     INFO("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
1061          "Input string was \"%s\".",
1062          endptr, DS_TYPE_TO_STRING(ds_type), value_orig);
1063
1064   sfree(value);
1065   return 0;
1066 } /* int parse_value */
1067
1068 int parse_values(char *buffer, value_list_t *vl, const data_set_t *ds) {
1069   size_t i;
1070   char *dummy;
1071   char *ptr;
1072   char *saveptr;
1073
1074   if ((buffer == NULL) || (vl == NULL) || (ds == NULL))
1075     return EINVAL;
1076
1077   i = 0;
1078   dummy = buffer;
1079   saveptr = NULL;
1080   vl->time = 0;
1081   while ((ptr = strtok_r(dummy, ":", &saveptr)) != NULL) {
1082     dummy = NULL;
1083
1084     if (i >= vl->values_len) {
1085       /* Make sure i is invalid. */
1086       i = 0;
1087       break;
1088     }
1089
1090     if (vl->time == 0) {
1091       if (strcmp("N", ptr) == 0)
1092         vl->time = cdtime();
1093       else {
1094         char *endptr = NULL;
1095         double tmp;
1096
1097         errno = 0;
1098         tmp = strtod(ptr, &endptr);
1099         if ((errno != 0)        /* Overflow */
1100             || (endptr == ptr)  /* Invalid string */
1101             || (endptr == NULL) /* This should not happen */
1102             || (*endptr != 0))  /* Trailing chars */
1103           return (-1);
1104
1105         vl->time = DOUBLE_TO_CDTIME_T(tmp);
1106       }
1107
1108       continue;
1109     }
1110
1111     if ((strcmp("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
1112       vl->values[i].gauge = NAN;
1113     else if (0 != parse_value(ptr, &vl->values[i], ds->ds[i].type))
1114       return -1;
1115
1116     i++;
1117   } /* while (strtok_r) */
1118
1119   if ((ptr != NULL) || (i == 0))
1120     return (-1);
1121   return (0);
1122 } /* int parse_values */
1123
1124 int parse_value_file(char const *path, value_t *ret_value, int ds_type) {
1125   FILE *fh;
1126   char buffer[256];
1127
1128   fh = fopen(path, "r");
1129   if (fh == NULL)
1130     return (-1);
1131
1132   if (fgets(buffer, sizeof(buffer), fh) == NULL) {
1133     fclose(fh);
1134     return (-1);
1135   }
1136
1137   fclose(fh);
1138
1139   strstripnewline(buffer);
1140
1141   return parse_value(buffer, ret_value, ds_type);
1142 } /* int parse_value_file */
1143
1144 #if !HAVE_GETPWNAM_R
1145 int getpwnam_r(const char *name, struct passwd *pwbuf, char *buf, size_t buflen,
1146                struct passwd **pwbufp) {
1147   int status = 0;
1148   struct passwd *pw;
1149
1150   memset(pwbuf, '\0', sizeof(struct passwd));
1151
1152   pthread_mutex_lock(&getpwnam_r_lock);
1153
1154   do {
1155     pw = getpwnam(name);
1156     if (pw == NULL) {
1157       status = (errno != 0) ? errno : ENOENT;
1158       break;
1159     }
1160
1161 #define GETPWNAM_COPY_MEMBER(member)                                           \
1162   if (pw->member != NULL) {                                                    \
1163     int len = strlen(pw->member);                                              \
1164     if (len >= buflen) {                                                       \
1165       status = ENOMEM;                                                         \
1166       break;                                                                   \
1167     }                                                                          \
1168     sstrncpy(buf, pw->member, buflen);                                         \
1169     pwbuf->member = buf;                                                       \
1170     buf += (len + 1);                                                          \
1171     buflen -= (len + 1);                                                       \
1172   }
1173     GETPWNAM_COPY_MEMBER(pw_name);
1174     GETPWNAM_COPY_MEMBER(pw_passwd);
1175     GETPWNAM_COPY_MEMBER(pw_gecos);
1176     GETPWNAM_COPY_MEMBER(pw_dir);
1177     GETPWNAM_COPY_MEMBER(pw_shell);
1178
1179     pwbuf->pw_uid = pw->pw_uid;
1180     pwbuf->pw_gid = pw->pw_gid;
1181
1182     if (pwbufp != NULL)
1183       *pwbufp = pwbuf;
1184   } while (0);
1185
1186   pthread_mutex_unlock(&getpwnam_r_lock);
1187
1188   return (status);
1189 } /* int getpwnam_r */
1190 #endif /* !HAVE_GETPWNAM_R */
1191
1192 int notification_init(notification_t *n, int severity, const char *message,
1193                       const char *host, const char *plugin,
1194                       const char *plugin_instance, const char *type,
1195                       const char *type_instance) {
1196   memset(n, '\0', sizeof(notification_t));
1197
1198   n->severity = severity;
1199
1200   if (message != NULL)
1201     sstrncpy(n->message, message, sizeof(n->message));
1202   if (host != NULL)
1203     sstrncpy(n->host, host, sizeof(n->host));
1204   if (plugin != NULL)
1205     sstrncpy(n->plugin, plugin, sizeof(n->plugin));
1206   if (plugin_instance != NULL)
1207     sstrncpy(n->plugin_instance, plugin_instance, sizeof(n->plugin_instance));
1208   if (type != NULL)
1209     sstrncpy(n->type, type, sizeof(n->type));
1210   if (type_instance != NULL)
1211     sstrncpy(n->type_instance, type_instance, sizeof(n->type_instance));
1212
1213   return (0);
1214 } /* int notification_init */
1215
1216 int walk_directory(const char *dir, dirwalk_callback_f callback,
1217                    void *user_data, int include_hidden) {
1218   struct dirent *ent;
1219   DIR *dh;
1220   int success;
1221   int failure;
1222
1223   success = 0;
1224   failure = 0;
1225
1226   if ((dh = opendir(dir)) == NULL) {
1227     char errbuf[1024];
1228     ERROR("walk_directory: Cannot open '%s': %s", dir,
1229           sstrerror(errno, errbuf, sizeof(errbuf)));
1230     return -1;
1231   }
1232
1233   while ((ent = readdir(dh)) != NULL) {
1234     int status;
1235
1236     if (include_hidden) {
1237       if ((strcmp(".", ent->d_name) == 0) || (strcmp("..", ent->d_name) == 0))
1238         continue;
1239     } else /* if (!include_hidden) */
1240     {
1241       if (ent->d_name[0] == '.')
1242         continue;
1243     }
1244
1245     status = (*callback)(dir, ent->d_name, user_data);
1246     if (status != 0)
1247       failure++;
1248     else
1249       success++;
1250   }
1251
1252   closedir(dh);
1253
1254   if ((success == 0) && (failure > 0))
1255     return (-1);
1256   return (0);
1257 }
1258
1259 ssize_t read_file_contents(const char *filename, char *buf, size_t bufsize) {
1260   FILE *fh;
1261   ssize_t ret;
1262
1263   fh = fopen(filename, "r");
1264   if (fh == NULL)
1265     return (-1);
1266
1267   ret = (ssize_t)fread(buf, 1, bufsize, fh);
1268   if ((ret == 0) && (ferror(fh) != 0)) {
1269     ERROR("read_file_contents: Reading file \"%s\" failed.", filename);
1270     ret = -1;
1271   }
1272
1273   fclose(fh);
1274   return (ret);
1275 }
1276
1277 counter_t counter_diff(counter_t old_value, counter_t new_value) {
1278   counter_t diff;
1279
1280   if (old_value > new_value) {
1281     if (old_value <= 4294967295U)
1282       diff = (4294967295U - old_value) + new_value + 1;
1283     else
1284       diff = (18446744073709551615ULL - old_value) + new_value + 1;
1285   } else {
1286     diff = new_value - old_value;
1287   }
1288
1289   return (diff);
1290 } /* counter_t counter_diff */
1291
1292 int rate_to_value(value_t *ret_value, gauge_t rate, /* {{{ */
1293                   rate_to_value_state_t *state, int ds_type, cdtime_t t) {
1294   gauge_t delta_gauge;
1295   cdtime_t delta_t;
1296
1297   if (ds_type == DS_TYPE_GAUGE) {
1298     state->last_value.gauge = rate;
1299     state->last_time = t;
1300
1301     *ret_value = state->last_value;
1302     return (0);
1303   }
1304
1305   /* Counter and absolute can't handle negative rates. Reset "last time"
1306    * to zero, so that the next valid rate will re-initialize the
1307    * structure. */
1308   if ((rate < 0.0) &&
1309       ((ds_type == DS_TYPE_COUNTER) || (ds_type == DS_TYPE_ABSOLUTE))) {
1310     memset(state, 0, sizeof(*state));
1311     return (EINVAL);
1312   }
1313
1314   /* Another invalid state: The time is not increasing. */
1315   if (t <= state->last_time) {
1316     memset(state, 0, sizeof(*state));
1317     return (EINVAL);
1318   }
1319
1320   delta_t = t - state->last_time;
1321   delta_gauge = (rate * CDTIME_T_TO_DOUBLE(delta_t)) + state->residual;
1322
1323   /* Previous value is invalid. */
1324   if (state->last_time == 0) /* {{{ */
1325   {
1326     if (ds_type == DS_TYPE_DERIVE) {
1327       state->last_value.derive = (derive_t)rate;
1328       state->residual = rate - ((gauge_t)state->last_value.derive);
1329     } else if (ds_type == DS_TYPE_COUNTER) {
1330       state->last_value.counter = (counter_t)rate;
1331       state->residual = rate - ((gauge_t)state->last_value.counter);
1332     } else if (ds_type == DS_TYPE_ABSOLUTE) {
1333       state->last_value.absolute = (absolute_t)rate;
1334       state->residual = rate - ((gauge_t)state->last_value.absolute);
1335     } else {
1336       assert(23 == 42);
1337     }
1338
1339     state->last_time = t;
1340     return (EAGAIN);
1341   } /* }}} */
1342
1343   if (ds_type == DS_TYPE_DERIVE) {
1344     derive_t delta_derive = (derive_t)delta_gauge;
1345
1346     state->last_value.derive += delta_derive;
1347     state->residual = delta_gauge - ((gauge_t)delta_derive);
1348   } else if (ds_type == DS_TYPE_COUNTER) {
1349     counter_t delta_counter = (counter_t)delta_gauge;
1350
1351     state->last_value.counter += delta_counter;
1352     state->residual = delta_gauge - ((gauge_t)delta_counter);
1353   } else if (ds_type == DS_TYPE_ABSOLUTE) {
1354     absolute_t delta_absolute = (absolute_t)delta_gauge;
1355
1356     state->last_value.absolute = delta_absolute;
1357     state->residual = delta_gauge - ((gauge_t)delta_absolute);
1358   } else {
1359     assert(23 == 42);
1360   }
1361
1362   state->last_time = t;
1363   *ret_value = state->last_value;
1364   return (0);
1365 } /* }}} value_t rate_to_value */
1366
1367 int value_to_rate(gauge_t *ret_rate, /* {{{ */
1368                   value_t value, int ds_type, cdtime_t t,
1369                   value_to_rate_state_t *state) {
1370   gauge_t interval;
1371
1372   /* Another invalid state: The time is not increasing. */
1373   if (t <= state->last_time) {
1374     memset(state, 0, sizeof(*state));
1375     return (EINVAL);
1376   }
1377
1378   interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
1379
1380   /* Previous value is invalid. */
1381   if (state->last_time == 0) {
1382     state->last_value = value;
1383     state->last_time = t;
1384     return (EAGAIN);
1385   }
1386
1387   switch (ds_type) {
1388   case DS_TYPE_DERIVE: {
1389     derive_t diff = value.derive - state->last_value.derive;
1390     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1391     break;
1392   }
1393   case DS_TYPE_GAUGE: {
1394     *ret_rate = value.gauge;
1395     break;
1396   }
1397   case DS_TYPE_COUNTER: {
1398     counter_t diff = counter_diff(state->last_value.counter, value.counter);
1399     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1400     break;
1401   }
1402   case DS_TYPE_ABSOLUTE: {
1403     absolute_t diff = value.absolute;
1404     *ret_rate = ((gauge_t)diff) / ((gauge_t)interval);
1405     break;
1406   }
1407   default:
1408     return EINVAL;
1409   }
1410
1411   state->last_value = value;
1412   state->last_time = t;
1413   return (0);
1414 } /* }}} value_t rate_to_value */
1415
1416 int service_name_to_port_number(const char *service_name) {
1417   struct addrinfo *ai_list;
1418   int status;
1419   int service_number;
1420
1421   if (service_name == NULL)
1422     return (-1);
1423
1424   struct addrinfo ai_hints = {.ai_family = AF_UNSPEC};
1425
1426   status = getaddrinfo(/* node = */ NULL, service_name, &ai_hints, &ai_list);
1427   if (status != 0) {
1428     ERROR("service_name_to_port_number: getaddrinfo failed: %s",
1429           gai_strerror(status));
1430     return (-1);
1431   }
1432
1433   service_number = -1;
1434   for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
1435        ai_ptr = ai_ptr->ai_next) {
1436     if (ai_ptr->ai_family == AF_INET) {
1437       struct sockaddr_in *sa;
1438
1439       sa = (void *)ai_ptr->ai_addr;
1440       service_number = (int)ntohs(sa->sin_port);
1441     } else if (ai_ptr->ai_family == AF_INET6) {
1442       struct sockaddr_in6 *sa;
1443
1444       sa = (void *)ai_ptr->ai_addr;
1445       service_number = (int)ntohs(sa->sin6_port);
1446     }
1447
1448     if ((service_number > 0) && (service_number <= 65535))
1449       break;
1450   }
1451
1452   freeaddrinfo(ai_list);
1453
1454   if ((service_number > 0) && (service_number <= 65535))
1455     return (service_number);
1456   return (-1);
1457 } /* int service_name_to_port_number */
1458
1459 void set_sock_opts(int sockfd) /* {{{ */
1460 {
1461   int status;
1462   int socktype;
1463
1464   status = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, &socktype,
1465                       &(socklen_t){sizeof(socktype)});
1466   if (status != 0) {
1467     WARNING("set_sock_opts: failed to determine socket type");
1468     return;
1469   }
1470
1471   if (socktype == SOCK_STREAM) {
1472     status =
1473         setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &(int){1}, sizeof(int));
1474     if (status != 0)
1475       WARNING("set_sock_opts: failed to set socket keepalive flag");
1476
1477 #ifdef TCP_KEEPIDLE
1478     int tcp_keepidle = ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 100 + 1);
1479     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &tcp_keepidle,
1480                         sizeof(tcp_keepidle));
1481     if (status != 0)
1482       WARNING("set_sock_opts: failed to set socket tcp keepalive time");
1483 #endif
1484
1485 #ifdef TCP_KEEPINTVL
1486     int tcp_keepintvl =
1487         ((CDTIME_T_TO_MS(plugin_get_interval()) - 1) / 1000 + 1);
1488     status = setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &tcp_keepintvl,
1489                         sizeof(tcp_keepintvl));
1490     if (status != 0)
1491       WARNING("set_sock_opts: failed to set socket tcp keepalive interval");
1492 #endif
1493   }
1494 } /* }}} void set_sock_opts */
1495
1496 int strtoderive(const char *string, derive_t *ret_value) /* {{{ */
1497 {
1498   derive_t tmp;
1499   char *endptr;
1500
1501   if ((string == NULL) || (ret_value == NULL))
1502     return (EINVAL);
1503
1504   errno = 0;
1505   endptr = NULL;
1506   tmp = (derive_t)strtoll(string, &endptr, /* base = */ 0);
1507   if ((endptr == string) || (errno != 0))
1508     return (-1);
1509
1510   *ret_value = tmp;
1511   return (0);
1512 } /* }}} int strtoderive */
1513
1514 int strtogauge(const char *string, gauge_t *ret_value) /* {{{ */
1515 {
1516   gauge_t tmp;
1517   char *endptr = NULL;
1518
1519   if ((string == NULL) || (ret_value == NULL))
1520     return (EINVAL);
1521
1522   errno = 0;
1523   endptr = NULL;
1524   tmp = (gauge_t)strtod(string, &endptr);
1525   if (errno != 0)
1526     return (errno);
1527   else if ((endptr == NULL) || (*endptr != 0))
1528     return (EINVAL);
1529
1530   *ret_value = tmp;
1531   return (0);
1532 } /* }}} int strtogauge */
1533
1534 int strarray_add(char ***ret_array, size_t *ret_array_len,
1535                  char const *str) /* {{{ */
1536 {
1537   char **array;
1538   size_t array_len = *ret_array_len;
1539
1540   if (str == NULL)
1541     return (EINVAL);
1542
1543   array = realloc(*ret_array, (array_len + 1) * sizeof(*array));
1544   if (array == NULL)
1545     return (ENOMEM);
1546   *ret_array = array;
1547
1548   array[array_len] = strdup(str);
1549   if (array[array_len] == NULL)
1550     return (ENOMEM);
1551
1552   array_len++;
1553   *ret_array_len = array_len;
1554   return (0);
1555 } /* }}} int strarray_add */
1556
1557 void strarray_free(char **array, size_t array_len) /* {{{ */
1558 {
1559   for (size_t i = 0; i < array_len; i++)
1560     sfree(array[i]);
1561   sfree(array);
1562 } /* }}} void strarray_free */
1563
1564 #if HAVE_CAPABILITY
1565 int check_capability(int arg) /* {{{ */
1566 {
1567   cap_value_t cap_value = (cap_value_t)arg;
1568   cap_t cap;
1569   cap_flag_value_t cap_flag_value;
1570
1571   if (!CAP_IS_SUPPORTED(cap_value))
1572     return (-1);
1573
1574   if (!(cap = cap_get_proc())) {
1575     ERROR("check_capability: cap_get_proc failed.");
1576     return (-1);
1577   }
1578
1579   if (cap_get_flag(cap, cap_value, CAP_EFFECTIVE, &cap_flag_value) < 0) {
1580     ERROR("check_capability: cap_get_flag failed.");
1581     cap_free(cap);
1582     return (-1);
1583   }
1584   cap_free(cap);
1585
1586   return (cap_flag_value != CAP_SET);
1587 } /* }}} int check_capability */
1588 #else
1589 int check_capability(__attribute__((unused)) int arg) /* {{{ */
1590 {
1591   WARNING("check_capability: unsupported capability implementation. "
1592           "Some plugin(s) may require elevated privileges to work properly.");
1593   return (0);
1594 } /* }}} int check_capability */
1595 #endif /* HAVE_CAPABILITY */