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