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