solaris-fixes branch: Applied the swap-patch by Christophe Kalt.
[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 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 #if HAVE_LIBRRD
367 static int rra_get (char ***ret)
368 {
369         static char **rra_def = NULL;
370         static int rra_num = 0;
371
372         int rra_max = rra_timespans_num * rra_types_num;
373
374         int step;
375         int rows;
376         int span;
377
378         int cdp_num;
379         int cdp_len;
380         int i, j;
381
382         char buffer[64];
383
384         if ((rra_num != 0) && (rra_def != NULL))
385         {
386                 *ret = rra_def;
387                 return (rra_num);
388         }
389
390         if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
391                 return (-1);
392         memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
393
394         step = atoi (COLLECTD_STEP);
395         rows = atoi (COLLECTD_ROWS);
396
397         if ((step <= 0) || (rows <= 0))
398         {
399                 *ret = NULL;
400                 return (-1);
401         }
402
403         cdp_len = 0;
404         for (i = 0; i < rra_timespans_num; i++)
405         {
406                 span = rra_timespans[i];
407
408                 if ((span / step) < rows)
409                         continue;
410
411                 if (cdp_len == 0)
412                         cdp_len = 1;
413                 else
414                         cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
415
416                 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
417
418                 for (j = 0; j < rra_types_num; j++)
419                 {
420                         if (rra_num >= rra_max)
421                                 break;
422
423                         if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
424                                                 rra_types[j], COLLECTD_XFF,
425                                                 cdp_len, cdp_num) >= sizeof (buffer))
426                         {
427                                 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
428                                 continue;
429                         }
430
431                         rra_def[rra_num++] = sstrdup (buffer);
432                 }
433         }
434
435 #if COLLECT_DEBUG
436         DBG ("rra_num = %i", rra_num);
437         for (i = 0; i < rra_num; i++)
438                 DBG ("  %s", rra_def[i]);
439 #endif
440
441         *ret = rra_def;
442         return (rra_num);
443 }
444 #endif /* HAVE_LIBRRD */
445
446 static int log_create_file (char *filename, char **ds_def, int ds_num)
447 {
448         FILE *log;
449         int i;
450
451         if (check_create_dir (filename))
452                 return (-1);
453
454         log = fopen (filename, "w");
455         if (log == NULL)
456         {
457                 syslog (LOG_WARNING, "Failed to create %s: %s", filename,
458                                 strerror(errno));
459                 return (-1);
460         }
461
462         fprintf (log, "epoch");
463         for (i = 0; i < ds_num; i++)
464         {
465                 char *name;
466                 char *tmp;
467
468                 name = strchr (ds_def[i], ':');
469                 if (name == NULL)
470                 {
471                         syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
472                                         ds_def[i], filename);
473                         fclose(log);
474                         remove(filename);
475                         return (-1);
476                 }
477
478                 name += 1;
479                 tmp = strchr (name, ':');
480                 if (tmp == NULL)
481                 {
482                         syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
483                                         ds_def[i], filename);
484                         fclose(log);
485                         remove(filename);
486                         return (-1);
487                 }
488
489                 /* The `%.*s' is needed because there is no null-byte behind
490                  * the name. */
491                 fprintf(log, ",%.*s", (int) (tmp - name), name);
492         }
493         fprintf(log, "\n");
494         fclose(log);
495
496         return 0;
497 }
498
499 static int log_update_file (char *host, char *file, char *values,
500                 char **ds_def, int ds_num)
501 {
502         char *tmp;
503         FILE *fp;
504         struct stat statbuf;
505         char full_file[1024];
506
507         /* Cook the values a bit: Substitute colons with commas */
508         strsubstitute (values, ':', ',');
509
510         /* host == NULL => local mode */
511         if (host != NULL)
512         {
513                 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
514                         return (-1);
515         }
516         else
517         {
518                 if (snprintf (full_file, 1024, "%s", file) >= 1024)
519                         return (-1);
520         }
521
522         strncpy (full_file, file, 1024);
523
524         tmp = full_file + strlen (full_file) - 4;
525         assert ((tmp != NULL) && (tmp > full_file));
526
527         /* Change the filename for logfiles. */
528         if (strncmp (tmp, ".rrd", 4) == 0)
529         {
530                 time_t now;
531                 struct tm *tm;
532
533                 /* TODO: Find a way to minimize the calls to `localtime', since
534                  * they are pretty expensive.. */
535                 now = time (NULL);
536                 tm = localtime (&now);
537
538                 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
539
540                 /* `localtime(3)' returns a pointer to static data,
541                  * therefore the pointer may not be free'd. */
542         }
543         else
544                 DBG ("The filename ends with `%s' which is unexpected.", tmp);
545
546         if (stat (full_file, &statbuf) == -1)
547         {
548                 if (errno == ENOENT)
549                 {
550                         if (log_create_file (full_file, ds_def, ds_num))
551                                 return (-1);
552                 }
553                 else
554                 {
555                         syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
556                         return (-1);
557                 }
558         }
559         else if (!S_ISREG (statbuf.st_mode))
560         {
561                 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
562                 return (-1);
563         }
564
565
566         fp = fopen (full_file, "a");
567         if (fp == NULL)
568         {
569                 syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
570                                 strerror(errno));
571                 return (-1);
572         }
573         fprintf(fp, "%s\n", values);
574         fclose(fp);
575
576         return (0);
577 } /* int log_update_file */
578
579 #if HAVE_LIBRRD
580 static int rrd_create_file (char *filename, char **ds_def, int ds_num)
581 {
582         char **argv;
583         int argc;
584         char **rra_def;
585         int rra_num;
586         int i, j;
587         int status = 0;
588
589         if (check_create_dir (filename))
590                 return (-1);
591
592         if ((rra_num = rra_get (&rra_def)) < 1)
593         {
594                 syslog (LOG_ERR, "rra_create failed: Could not calculate RRAs");
595                 return (-1);
596         }
597
598         argc = ds_num + rra_num + 4;
599
600         if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
601         {
602                 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
603                 return (-1);
604         }
605
606         argv[0] = "create";
607         argv[1] = filename;
608         argv[2] = "-s";
609         argv[3] = COLLECTD_STEP;
610
611         j = 4;
612         for (i = 0; i < ds_num; i++)
613                 argv[j++] = ds_def[i];
614         for (i = 0; i < rra_num; i++)
615                 argv[j++] = rra_def[i];
616         argv[j] = NULL;
617
618         optind = 0; /* bug in librrd? */
619         rrd_clear_error ();
620         if (rrd_create (argc, argv) == -1)
621         {
622                 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
623                 status = -1;
624         }
625
626         free (argv);
627
628         return (status);
629 }
630 #endif /* HAVE_LIBRRD */
631
632 int rrd_update_file (char *host, char *file, char *values,
633                 char **ds_def, int ds_num)
634 {
635 #if HAVE_LIBRRD
636         struct stat statbuf;
637         char full_file[1024];
638         char *argv[4] = { "update", full_file, values, NULL };
639 #endif /* HAVE_LIBRRD */
640
641         /* I'd rather have a function `common_update_file' to make this
642          * decission, but for that we'd need to touch all plugins.. */
643         if (operating_mode == MODE_LOG)
644                 return (log_update_file (host, file, values,
645                                         ds_def, ds_num));
646
647 #if HAVE_LIBRRD
648         /* host == NULL => local mode */
649         if (host != NULL)
650         {
651                 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
652                         return (-1);
653         }
654         else
655         {
656                 if (snprintf (full_file, 1024, "%s", file) >= 1024)
657                         return (-1);
658         }
659
660         if (stat (full_file, &statbuf) == -1)
661         {
662                 if (errno == ENOENT)
663                 {
664                         if (rrd_create_file (full_file, ds_def, ds_num))
665                                 return (-1);
666                 }
667                 else
668                 {
669                         syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
670                         return (-1);
671                 }
672         }
673         else if (!S_ISREG (statbuf.st_mode))
674         {
675                 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
676                 return (-1);
677         }
678
679         optind = 0; /* bug in librrd? */
680         rrd_clear_error ();
681         if (rrd_update (3, argv) == -1)
682         {
683                 syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
684                 return (-1);
685         }
686         return (0);
687 /* #endif HAVE_LIBRRD */
688
689 #else
690         syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
691         return (-1);
692 #endif
693 }
694
695 #ifdef HAVE_LIBKSTAT
696 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
697 {
698         char ident[128];
699         
700         if (kc == NULL)
701                 return (-1);
702
703         snprintf (ident, 128, "%s,%i,%s", module, instance, name);
704         ident[127] = '\0';
705
706         if (*ksp_ptr == NULL)
707         {
708                 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
709                 {
710                         syslog (LOG_ERR, "Cound not find kstat %s", ident);
711                         return (-1);
712                 }
713
714                 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
715                 {
716                         syslog (LOG_WARNING, "kstat %s has wrong type", ident);
717                         *ksp_ptr = NULL;
718                         return (-1);
719                 }
720         }
721
722 #ifdef assert
723         assert (*ksp_ptr != NULL);
724         assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
725 #endif
726
727         if (kstat_read (kc, *ksp_ptr, NULL) == -1)
728         {
729                 syslog (LOG_WARNING, "kstat %s could not be read", ident);
730                 return (-1);
731         }
732
733         if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
734         {
735                 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
736                 return (-1);
737         }
738
739         return (0);
740 }
741
742 long long get_kstat_value (kstat_t *ksp, char *name)
743 {
744         kstat_named_t *kn;
745         long long retval = -1LL;
746
747 #ifdef assert
748         assert (ksp != NULL);
749         assert (ksp->ks_type == KSTAT_TYPE_NAMED);
750 #else
751         if (ksp == NULL)
752         {
753                 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
754                 return (-1LL);
755         }
756         else if (ksp->ks_type != KSTAT_TYPE_NAMED)
757         {
758                 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
759                 return (-1LL);
760         }
761 #endif
762
763         if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
764                 return (retval);
765
766         if (kn->data_type == KSTAT_DATA_INT32)
767                 retval = (long long) kn->value.i32;
768         else if (kn->data_type == KSTAT_DATA_UINT32)
769                 retval = (long long) kn->value.ui32;
770         else if (kn->data_type == KSTAT_DATA_INT64)
771                 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
772         else if (kn->data_type == KSTAT_DATA_UINT64)
773                 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
774         else
775                 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
776                  
777         return (retval);
778 }
779 #endif /* HAVE_LIBKSTAT */