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