fd1fbd402194ee9c67fd886d60dc33d1a13a1b1d
1 /**
2 * collectd - src/common.c
3 * Copyright (C) 2005,2006 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/
6 * or modify it under the terms of the GNU General Public Li-
7 * cence as published by the Free Software Foundation; either
8 * version 2 of the Licence, or any later version.
9 *
10 * This program is distributed in the hope that it will be use-
11 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
12 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public Licence for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * Licence along with this program; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
18 * USA.
19 *
20 * Authors:
21 * Florian octo Forster <octo at verplant.org>
22 * Niki W. Waibel <niki.waibel@gmx.net>
23 **/
25 #if HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include "common.h"
30 #include "utils_debug.h"
32 #ifdef HAVE_MATH_H
33 # include <math.h>
34 #endif
36 extern int operating_mode;
38 #ifdef HAVE_LIBKSTAT
39 extern kstat_ctl_t *kc;
40 #endif
42 #ifdef HAVE_LIBRRD
43 #if 0
44 static char *rra_def[] =
45 {
46 "RRA:AVERAGE:0.0:1:1500",
47 "RRA:AVERAGE:0.2:6:1500",
48 "RRA:AVERAGE:0.1:180:1680",
49 "RRA:AVERAGE:0.1:2160:1520",
50 "RRA:MIN:0.0:1:1500",
51 "RRA:MIN:0.2:6:1500",
52 "RRA:MIN:0.1:180:1680",
53 "RRA:MIN:0.1:2160:1520",
54 "RRA:MAX:0.0:1:1500",
55 "RRA:MAX:0.2:6:1500",
56 "RRA:MAX:0.1:180:1680",
57 "RRA:MAX:0.1:2160:1520",
58 NULL
59 };
60 static int rra_num = 12;
61 #endif
63 static int rra_timespans[] =
64 {
65 3600,
66 86400,
67 604800,
68 2678400,
69 31622400,
70 0
71 };
72 static int rra_timespans_num = 5;
74 static char *rra_types[] =
75 {
76 "AVERAGE",
77 "MIN",
78 "MAX",
79 NULL
80 };
81 static int rra_types_num = 3;
82 #endif /* HAVE_LIBRRD */
84 void sstrncpy (char *d, const char *s, int len)
85 {
86 strncpy (d, s, len);
87 d[len - 1] = '\0';
88 }
90 char *sstrdup (const char *s)
91 {
92 char *r;
94 if (s == NULL)
95 return (NULL);
97 if((r = strdup (s)) == NULL)
98 {
99 DBG ("Not enough memory.");
100 exit(3);
101 }
103 return (r);
104 }
106 void *smalloc (size_t size)
107 {
108 void *r;
110 if ((r = malloc (size)) == NULL)
111 {
112 DBG("Not enough memory.");
113 exit(3);
114 }
116 return r;
117 }
119 #if 0
120 void sfree (void **ptr)
121 {
122 if (ptr == NULL)
123 return;
125 if (*ptr != NULL)
126 free (*ptr);
128 *ptr = NULL;
129 }
130 #endif
132 ssize_t sread (int fd, void *buf, size_t count)
133 {
134 char *ptr;
135 size_t nleft;
136 ssize_t status;
138 ptr = (char *) buf;
139 nleft = count;
141 while (nleft > 0)
142 {
143 status = read (fd, (void *) ptr, nleft);
145 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
146 continue;
148 if (status < 0)
149 return (status);
151 if (status == 0)
152 {
153 DBG ("Received EOF from fd %i. "
154 "Closing fd and returning error.",
155 fd);
156 close (fd);
157 return (-1);
158 }
160 assert (nleft >= status);
162 nleft = nleft - status;
163 ptr = ptr + status;
164 }
166 return (0);
167 }
170 ssize_t swrite (int fd, const void *buf, size_t count)
171 {
172 const char *ptr;
173 size_t nleft;
174 ssize_t status;
176 ptr = (const char *) buf;
177 nleft = count;
179 while (nleft > 0)
180 {
181 status = write (fd, (const void *) ptr, nleft);
183 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
184 continue;
186 if (status < 0)
187 return (status);
189 nleft = nleft - status;
190 ptr = ptr + status;
191 }
193 return (0);
194 }
196 int strsplit (char *string, char **fields, size_t size)
197 {
198 size_t i;
199 char *ptr;
201 i = 0;
202 ptr = string;
203 while ((fields[i] = strtok (ptr, " \t")) != NULL)
204 {
205 ptr = NULL;
206 i++;
208 if (i >= size)
209 break;
210 }
212 return (i);
213 }
215 int strjoin (char *dst, size_t dst_len,
216 char **fields, size_t fields_num,
217 const char *sep)
218 {
219 int field_len;
220 int sep_len;
221 int i;
223 memset (dst, '\0', dst_len);
225 if (fields_num <= 0)
226 return (-1);
228 sep_len = 0;
229 if (sep != NULL)
230 sep_len = strlen (sep);
232 for (i = 0; i < fields_num; i++)
233 {
234 if ((i > 0) && (sep_len > 0))
235 {
236 if (dst_len <= sep_len)
237 return (-1);
239 strncat (dst, sep, dst_len);
240 dst_len -= sep_len;
241 }
243 field_len = strlen (fields[i]);
245 if (dst_len <= field_len)
246 return (-1);
248 strncat (dst, fields[i], dst_len);
249 dst_len -= field_len;
250 }
252 return (strlen (dst));
253 }
255 int strsubstitute (char *str, char c_from, char c_to)
256 {
257 int ret;
259 if (str == NULL)
260 return (-1);
262 ret = 0;
263 while (*str != '\0')
264 {
265 if (*str == c_from)
266 {
267 *str = c_to;
268 ret++;
269 }
270 str++;
271 }
273 return (ret);
274 }
276 int escape_slashes (char *buf, int buf_len)
277 {
278 int i;
280 if (strcmp (buf, "/") == 0)
281 {
282 if (buf_len < 5)
283 return (-1);
285 strncpy (buf, "root", buf_len);
286 return (0);
287 }
289 /* Move one to the left */
290 memmove (buf, buf + 1, buf_len - 1);
292 for (i = 0; i < buf_len - 1; i++)
293 {
294 if (buf[i] == '\0')
295 break;
296 else if (buf[i] == '/')
297 buf[i] = '_';
298 }
299 buf[i] = '\0';
301 return (0);
302 }
304 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
305 {
306 if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
307 return (-2);
309 if ((tv0->tv_sec < tv1->tv_sec)
310 || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
311 return (-1);
313 ret->tv_sec = tv0->tv_sec - tv1->tv_sec;
314 ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
316 if (ret->tv_nsec < 0)
317 {
318 assert (ret->tv_sec > 0);
320 ret->tv_nsec += 1000000000;
321 ret->tv_sec -= 1;
322 }
324 return (0);
325 }
327 static int check_create_dir (const char *file_orig)
328 {
329 struct stat statbuf;
331 char file_copy[512];
332 char dir[512];
333 int dir_len = 512;
334 char *fields[16];
335 int fields_num;
336 char *ptr;
337 int last_is_file = 1;
338 int len;
339 int i;
341 /*
342 * Sanity checks first
343 */
344 if (file_orig == NULL)
345 return (-1);
347 if ((len = strlen (file_orig)) < 1)
348 return (-1);
349 else if (len >= 512)
350 return (-1);
352 /*
353 * If `file_orig' ends in a slash the last component is a directory,
354 * otherwise it's a file. Act accordingly..
355 */
356 if (file_orig[len - 1] == '/')
357 last_is_file = 0;
359 /*
360 * Create a copy for `strtok' to destroy
361 */
362 strncpy (file_copy, file_orig, 512);
363 file_copy[511] = '\0';
365 /*
366 * Break into components. This will eat up several slashes in a row and
367 * remove leading and trailing slashes..
368 */
369 ptr = file_copy;
370 fields_num = 0;
371 while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
372 {
373 ptr = NULL;
374 fields_num++;
376 if (fields_num >= 16)
377 break;
378 }
380 /*
381 * For each component, do..
382 */
383 for (i = 0; i < (fields_num - last_is_file); i++)
384 {
385 /*
386 * Do not create directories that start with a dot. This
387 * prevents `../../' attacks and other likely malicious
388 * behavior.
389 */
390 if (fields[i][0] == '.')
391 {
392 syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
393 return (-2);
394 }
396 /*
397 * Join the components together again
398 */
399 if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
400 {
401 syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
402 return (-1);
403 }
405 if (stat (dir, &statbuf) == -1)
406 {
407 if (errno == ENOENT)
408 {
409 if (mkdir (dir, 0755) == -1)
410 {
411 syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
412 return (-1);
413 }
414 }
415 else
416 {
417 syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
418 return (-1);
419 }
420 }
421 else if (!S_ISDIR (statbuf.st_mode))
422 {
423 syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
424 return (-1);
425 }
426 }
428 return (0);
429 }
431 /* * * * *
432 * Magic *
433 * * * * */
434 #if HAVE_LIBRRD
435 static int rra_get (char ***ret)
436 {
437 static char **rra_def = NULL;
438 static int rra_num = 0;
440 int rra_max = rra_timespans_num * rra_types_num;
442 int step;
443 int rows;
444 int span;
446 int cdp_num;
447 int cdp_len;
448 int i, j;
450 char buffer[64];
452 if ((rra_num != 0) && (rra_def != NULL))
453 {
454 *ret = rra_def;
455 return (rra_num);
456 }
458 if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
459 return (-1);
460 memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
462 step = atoi (COLLECTD_STEP);
463 rows = atoi (COLLECTD_ROWS);
465 if ((step <= 0) || (rows <= 0))
466 {
467 *ret = NULL;
468 return (-1);
469 }
471 cdp_len = 0;
472 for (i = 0; i < rra_timespans_num; i++)
473 {
474 span = rra_timespans[i];
476 if ((span / step) < rows)
477 continue;
479 if (cdp_len == 0)
480 cdp_len = 1;
481 else
482 cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
484 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
486 for (j = 0; j < rra_types_num; j++)
487 {
488 if (rra_num >= rra_max)
489 break;
491 if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
492 rra_types[j], COLLECTD_XFF,
493 cdp_len, cdp_num) >= sizeof (buffer))
494 {
495 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
496 continue;
497 }
499 rra_def[rra_num++] = sstrdup (buffer);
500 }
501 }
503 #if COLLECT_DEBUG
504 DBG ("rra_num = %i", rra_num);
505 for (i = 0; i < rra_num; i++)
506 DBG (" %s", rra_def[i]);
507 #endif
509 *ret = rra_def;
510 return (rra_num);
511 }
512 #endif /* HAVE_LIBRRD */
514 static int log_create_file (char *filename, char **ds_def, int ds_num)
515 {
516 FILE *log;
517 int i;
519 if (check_create_dir (filename))
520 return (-1);
522 log = fopen (filename, "w");
523 if (log == NULL)
524 {
525 syslog (LOG_WARNING, "Failed to create %s: %s", filename,
526 strerror(errno));
527 return (-1);
528 }
530 fprintf (log, "epoch");
531 for (i = 0; i < ds_num; i++)
532 {
533 char *name;
534 char *tmp;
536 name = strchr (ds_def[i], ':');
537 if (name == NULL)
538 {
539 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
540 ds_def[i], filename);
541 fclose(log);
542 remove(filename);
543 return (-1);
544 }
546 name += 1;
547 tmp = strchr (name, ':');
548 if (tmp == NULL)
549 {
550 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
551 ds_def[i], filename);
552 fclose(log);
553 remove(filename);
554 return (-1);
555 }
557 /* The `%.*s' is needed because there is no null-byte behind
558 * the name. */
559 fprintf(log, ",%.*s", (int) (tmp - name), name);
560 }
561 fprintf(log, "\n");
562 fclose(log);
564 return 0;
565 }
567 static int log_update_file (char *host, char *file, char *values,
568 char **ds_def, int ds_num)
569 {
570 char *tmp;
571 FILE *fp;
572 struct stat statbuf;
573 char full_file[1024];
575 /* Cook the values a bit: Substitute colons with commas */
576 strsubstitute (values, ':', ',');
578 /* host == NULL => local mode */
579 if (host != NULL)
580 {
581 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
582 return (-1);
583 }
584 else
585 {
586 if (snprintf (full_file, 1024, "%s", file) >= 1024)
587 return (-1);
588 }
590 strncpy (full_file, file, 1024);
592 tmp = full_file + strlen (full_file) - 4;
593 assert ((tmp != NULL) && (tmp > full_file));
595 /* Change the filename for logfiles. */
596 if (strncmp (tmp, ".rrd", 4) == 0)
597 {
598 time_t now;
599 struct tm *tm;
601 /* TODO: Find a way to minimize the calls to `localtime', since
602 * they are pretty expensive.. */
603 now = time (NULL);
604 tm = localtime (&now);
606 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
608 /* `localtime(3)' returns a pointer to static data,
609 * therefore the pointer may not be free'd. */
610 }
611 else
612 DBG ("The filename ends with `%s' which is unexpected.", tmp);
614 if (stat (full_file, &statbuf) == -1)
615 {
616 if (errno == ENOENT)
617 {
618 if (log_create_file (full_file, ds_def, ds_num))
619 return (-1);
620 }
621 else
622 {
623 syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
624 return (-1);
625 }
626 }
627 else if (!S_ISREG (statbuf.st_mode))
628 {
629 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
630 return (-1);
631 }
634 fp = fopen (full_file, "a");
635 if (fp == NULL)
636 {
637 syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
638 strerror(errno));
639 return (-1);
640 }
641 fprintf(fp, "%s\n", values);
642 fclose(fp);
644 return (0);
645 } /* int log_update_file */
647 #if HAVE_LIBRRD
648 static int rrd_create_file (char *filename, char **ds_def, int ds_num)
649 {
650 char **argv;
651 int argc;
652 char **rra_def;
653 int rra_num;
654 int i, j;
655 int status = 0;
657 if (check_create_dir (filename))
658 return (-1);
660 if ((rra_num = rra_get (&rra_def)) < 1)
661 {
662 syslog (LOG_ERR, "rra_create failed: Could not calculate RRAs");
663 return (-1);
664 }
666 argc = ds_num + rra_num + 4;
668 if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
669 {
670 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
671 return (-1);
672 }
674 argv[0] = "create";
675 argv[1] = filename;
676 argv[2] = "-s";
677 argv[3] = COLLECTD_STEP;
679 j = 4;
680 for (i = 0; i < ds_num; i++)
681 argv[j++] = ds_def[i];
682 for (i = 0; i < rra_num; i++)
683 argv[j++] = rra_def[i];
684 argv[j] = NULL;
686 optind = 0; /* bug in librrd? */
687 rrd_clear_error ();
688 if (rrd_create (argc, argv) == -1)
689 {
690 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
691 status = -1;
692 }
694 free (argv);
696 return (status);
697 }
698 #endif /* HAVE_LIBRRD */
700 int rrd_update_file (char *host, char *file, char *values,
701 char **ds_def, int ds_num)
702 {
703 #if HAVE_LIBRRD
704 struct stat statbuf;
705 char full_file[1024];
706 char *argv[4] = { "update", full_file, values, NULL };
707 #endif /* HAVE_LIBRRD */
709 /* I'd rather have a function `common_update_file' to make this
710 * decission, but for that we'd need to touch all plugins.. */
711 if (operating_mode == MODE_LOG)
712 return (log_update_file (host, file, values,
713 ds_def, ds_num));
715 #if HAVE_LIBRRD
716 /* host == NULL => local mode */
717 if (host != NULL)
718 {
719 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
720 return (-1);
721 }
722 else
723 {
724 if (snprintf (full_file, 1024, "%s", file) >= 1024)
725 return (-1);
726 }
728 if (stat (full_file, &statbuf) == -1)
729 {
730 if (errno == ENOENT)
731 {
732 if (rrd_create_file (full_file, ds_def, ds_num))
733 return (-1);
734 }
735 else
736 {
737 syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
738 return (-1);
739 }
740 }
741 else if (!S_ISREG (statbuf.st_mode))
742 {
743 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
744 return (-1);
745 }
747 optind = 0; /* bug in librrd? */
748 rrd_clear_error ();
749 if (rrd_update (3, argv) == -1)
750 {
751 syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
752 return (-1);
753 }
754 return (0);
755 /* #endif HAVE_LIBRRD */
757 #else
758 syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
759 return (-1);
760 #endif
761 }
763 #ifdef HAVE_LIBKSTAT
764 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
765 {
766 char ident[128];
768 if (kc == NULL)
769 return (-1);
771 snprintf (ident, 128, "%s,%i,%s", module, instance, name);
772 ident[127] = '\0';
774 if (*ksp_ptr == NULL)
775 {
776 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
777 {
778 syslog (LOG_ERR, "Cound not find kstat %s", ident);
779 return (-1);
780 }
782 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
783 {
784 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
785 *ksp_ptr = NULL;
786 return (-1);
787 }
788 }
790 #ifdef assert
791 assert (*ksp_ptr != NULL);
792 assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
793 #endif
795 if (kstat_read (kc, *ksp_ptr, NULL) == -1)
796 {
797 syslog (LOG_WARNING, "kstat %s could not be read", ident);
798 return (-1);
799 }
801 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
802 {
803 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
804 return (-1);
805 }
807 return (0);
808 }
810 long long get_kstat_value (kstat_t *ksp, char *name)
811 {
812 kstat_named_t *kn;
813 long long retval = -1LL;
815 #ifdef assert
816 assert (ksp != NULL);
817 assert (ksp->ks_type == KSTAT_TYPE_NAMED);
818 #else
819 if (ksp == NULL)
820 {
821 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
822 return (-1LL);
823 }
824 else if (ksp->ks_type != KSTAT_TYPE_NAMED)
825 {
826 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
827 return (-1LL);
828 }
829 #endif
831 if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
832 return (retval);
834 if (kn->data_type == KSTAT_DATA_INT32)
835 retval = (long long) kn->value.i32;
836 else if (kn->data_type == KSTAT_DATA_UINT32)
837 retval = (long long) kn->value.ui32;
838 else if (kn->data_type == KSTAT_DATA_INT64)
839 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
840 else if (kn->data_type == KSTAT_DATA_UINT64)
841 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
842 else
843 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
845 return (retval);
846 }
847 #endif /* HAVE_LIBKSTAT */