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