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