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