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