config-step/src/collectd.h:
[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 #ifdef HAVE_LIBKSTAT
33 extern kstat_ctl_t *kc;
34 #endif
35
36 #ifdef HAVE_LIBRRD
37 static char *rra_def[] =
38 {
39                 "RRA:AVERAGE:0.0:1:1500",
40                 "RRA:AVERAGE:0.2:6:1500",
41                 "RRA:AVERAGE:0.1:180:1680",
42                 "RRA:AVERAGE:0.1:2160:1520",
43                 "RRA:MIN:0.0:1:1500",
44                 "RRA:MIN:0.2:6:1500",
45                 "RRA:MIN:0.1:180:1680",
46                 "RRA:MIN:0.1:2160:1520",
47                 "RRA:MAX:0.0:1:1500",
48                 "RRA:MAX:0.2:6:1500",
49                 "RRA:MAX:0.1:180:1680",
50                 "RRA:MAX:0.1:2160:1520",
51                 NULL
52 };
53 static int rra_num = 12;
54
55 static int rra_timespans[] =
56 {
57         3600,
58         86400,
59         604800,
60         2678400,
61         31622400,
62         0
63 };
64 static int rra_timespans_num = 5;
65
66 static char *rra_types[] =
67 {
68         "AVERAGE",
69         "MIN",
70         "MAX",
71         NULL
72 };
73 static int rra_types_num = 3;
74 #endif /* HAVE_LIBRRD */
75
76 void sstrncpy (char *d, const char *s, int len)
77 {
78         strncpy (d, s, len);
79         d[len - 1] = '\0';
80 }
81
82 char *sstrdup (const char *s)
83 {
84         char *r;
85
86         if (s == NULL)
87                 return (NULL);
88
89         if((r = strdup (s)) == NULL)
90         {
91                 DBG ("Not enough memory.");
92                 exit(3);
93         }
94
95         return (r);
96 }
97
98 void *smalloc (size_t size)
99 {
100         void *r;
101
102         if ((r = malloc (size)) == NULL)
103         {
104                 DBG("Not enough memory.");
105                 exit(3);
106         }
107
108         return r;
109 }
110
111 #if 0
112 void sfree (void **ptr)
113 {
114         if (ptr == NULL)
115                 return;
116
117         if (*ptr != NULL)
118                 free (*ptr);
119
120         *ptr = NULL;
121 }
122 #endif
123
124 int strsplit (char *string, char **fields, size_t size)
125 {
126         size_t i;
127         char *ptr;
128
129         i = 0;
130         ptr = string;
131         while ((fields[i] = strtok (ptr, " \t")) != NULL)
132         {
133                 ptr = NULL;
134                 i++;
135
136                 if (i >= size)
137                         break;
138         }
139
140         return (i);
141 }
142
143 int strjoin (char *dst, size_t dst_len,
144                 char **fields, size_t fields_num,
145                 const char *sep)
146 {
147         int field_len;
148         int sep_len;
149         int i;
150
151         memset (dst, '\0', dst_len);
152
153         if (fields_num <= 0)
154                 return (-1);
155
156         sep_len = 0;
157         if (sep != NULL)
158                 sep_len = strlen (sep);
159
160         for (i = 0; i < fields_num; i++)
161         {
162                 if ((i > 0) && (sep_len > 0))
163                 {
164                         if (dst_len <= sep_len)
165                                 return (-1);
166
167                         strncat (dst, sep, dst_len);
168                         dst_len -= sep_len;
169                 }
170
171                 field_len = strlen (fields[i]);
172
173                 if (dst_len <= field_len)
174                         return (-1);
175
176                 strncat (dst, fields[i], dst_len);
177                 dst_len -= field_len;
178         }
179
180         return (strlen (dst));
181 }
182
183 int escape_slashes (char *buf, int buf_len)
184 {
185         int i;
186
187         if (strcmp (buf, "/") == 0)
188         {
189                 if (buf_len < 5)
190                         return (-1);
191
192                 strncpy (buf, "root", buf_len);
193                 return (0);
194         }
195
196         /* Move one to the left */
197         memmove (buf, buf + 1, buf_len - 1);
198
199         for (i = 0; i < buf_len - 1; i++)
200         {
201                 if (buf[i] == '\0')
202                         break;
203                 else if (buf[i] == '/')
204                         buf[i] = '_';
205         }
206         buf[i] = '\0';
207
208         return (0);
209 }
210
211 #ifdef HAVE_LIBRRD
212 int check_create_dir (const char *file_orig)
213 {
214         struct stat statbuf;
215
216         char  file_copy[512];
217         char  dir[512];
218         int   dir_len = 512;
219         char *fields[16];
220         int   fields_num;
221         char *ptr;
222         int   last_is_file = 1;
223         int   len;
224         int   i;
225
226         /*
227          * Sanity checks first
228          */
229         if (file_orig == NULL)
230                 return (-1);
231
232         if ((len = strlen (file_orig)) < 1)
233                 return (-1);
234         else if (len >= 512)
235                 return (-1);
236
237         /*
238          * If `file_orig' ends in a slash the last component is a directory,
239          * otherwise it's a file. Act accordingly..
240          */
241         if (file_orig[len - 1] == '/')
242                 last_is_file = 0;
243
244         /*
245          * Create a copy for `strtok' to destroy
246          */
247         strncpy (file_copy, file_orig, 512);
248         file_copy[511] = '\0';
249
250         /*
251          * Break into components. This will eat up several slashes in a row and
252          * remove leading and trailing slashes..
253          */
254         ptr = file_copy;
255         fields_num = 0;
256         while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
257         {
258                 ptr = NULL;
259                 fields_num++;
260
261                 if (fields_num >= 16)
262                         break;
263         }
264
265         /*
266          * For each component, do..
267          */
268         for (i = 0; i < (fields_num - last_is_file); i++)
269         {
270                 /*
271                  * Do not create directories that start with a dot. This
272                  * prevents `../../' attacks and other likely malicious
273                  * behavior.
274                  */
275                 if (fields[i][0] == '.')
276                 {
277                         syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
278                         return (-2);
279                 }
280
281                 /*
282                  * Join the components together again
283                  */
284                 if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
285                 {
286                         syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
287                         return (-1);
288                 }
289
290                 if (stat (dir, &statbuf) == -1)
291                 {
292                         if (errno == ENOENT)
293                         {
294                                 if (mkdir (dir, 0755) == -1)
295                                 {
296                                         syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
297                                         return (-1);
298                                 }
299                         }
300                         else
301                         {
302                                 syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
303                                 return (-1);
304                         }
305                 }
306                 else if (!S_ISDIR (statbuf.st_mode))
307                 {
308                         syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
309                         return (-1);
310                 }
311         }
312
313         return (0);
314 }
315
316 /* * * * *
317  * Magic *
318  * * * * */
319 int rra_get (char ***ret)
320 {
321         static char **rra_def = NULL;
322         static int rra_num = 0;
323
324         int rra_max = rra_timespans_num * rra_types_num;
325
326         int step;
327         int rows;
328         int span;
329
330         int cdp_num;
331         int cdp_len;
332         int i, j;
333
334         char buffer[64];
335
336         if ((rra_num != 0) && (rra_def != NULL))
337         {
338                 *ret = rra_def;
339                 return (rra_num);
340         }
341
342         if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
343                 return (-1);
344         memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
345
346         step = atoi (COLLECTD_STEP);
347         rows = atoi (COLLECTD_ROWS);
348
349         if ((step <= 0) || (rows <= 0))
350         {
351                 *ret = NULL;
352                 return (-1);
353         }
354
355         cdp_len = 0;
356         for (i = 0; i < rra_timespans_num; i++)
357         {
358                 span = rra_timespans[i];
359
360                 if ((span / step) < rows)
361                         continue;
362
363                 if (cdp_len == 0)
364                         cdp_len = 1;
365                 else
366                         cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
367
368                 cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
369
370                 for (j = 0; j < rra_types_num; j++)
371                 {
372                         if (rra_num >= rra_max)
373                                 break;
374
375                         if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
376                                                 rra_types[j], COLLECTD_XFF,
377                                                 cdp_len, cdp_num) >= sizeof (buffer))
378                         {
379                                 syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
380                                 continue;
381                         }
382
383                         rra_def[rra_num++] = sstrdup (buffer);
384                 }
385         }
386
387 #if COLLECT_DEBUG
388         DBG ("rra_num = %i", rra_num);
389         for (i = 0; i < rra_num; i++)
390                 DBG ("  %s", rra_def[i]);
391 #endif
392
393         *ret = rra_def;
394         return (rra_num);
395 }
396
397 int rrd_create_file (char *filename, char **ds_def, int ds_num)
398 {
399         char **argv;
400         int argc;
401         int i, j;
402         int status = 0;
403
404         if (check_create_dir (filename))
405                 return (-1);
406
407         rra_get (&argv); /* FIXME */
408
409         argc = ds_num + rra_num + 4;
410
411         if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
412         {
413                 syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
414                 return (-1);
415         }
416
417         argv[0] = "create";
418         argv[1] = filename;
419         argv[2] = "-s";
420         argv[3] = COLLECTD_STEP;
421
422         j = 4;
423         for (i = 0; i < ds_num; i++)
424                 argv[j++] = ds_def[i];
425         for (i = 0; i < rra_num; i++)
426                 argv[j++] = rra_def[i];
427         argv[j] = NULL;
428
429         optind = 0; /* bug in librrd? */
430         rrd_clear_error ();
431         if (rrd_create (argc, argv) == -1)
432         {
433                 syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
434                 status = -1;
435         }
436
437         free (argv);
438         
439         return (status);
440 }
441 #endif /* HAVE_LIBRRD */
442
443 int rrd_update_file (char *host, char *file, char *values,
444                 char **ds_def, int ds_num)
445 {
446 #ifdef HAVE_LIBRRD
447         struct stat statbuf;
448         char full_file[1024];
449         char *argv[4] = { "update", full_file, values, NULL };
450
451         /* host == NULL => local mode */
452         if (host != NULL)
453         {
454                 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
455                         return (-1);
456         }
457         else
458         {
459                 if (snprintf (full_file, 1024, "%s", file) >= 1024)
460                         return (-1);
461         }
462
463         if (stat (full_file, &statbuf) == -1)
464         {
465                 if (errno == ENOENT)
466                 {
467                         if (rrd_create_file (full_file, ds_def, ds_num))
468                                 return (-1);
469                 }
470                 else
471                 {
472                         syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
473                         return (-1);
474                 }
475         }
476         else if (!S_ISREG (statbuf.st_mode))
477         {
478                 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
479                 return (-1);
480         }
481
482         optind = 0; /* bug in librrd? */
483         rrd_clear_error ();
484         if (rrd_update (3, argv) == -1)
485         {
486                 syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
487                 return (-1);
488         }
489 #endif /* HAVE_LIBRRD */
490
491         return (0);
492 }
493
494 #ifdef HAVE_LIBKSTAT
495 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
496 {
497         char ident[128];
498         
499         if (kc == NULL)
500                 return (-1);
501
502         snprintf (ident, 128, "%s,%i,%s", module, instance, name);
503         ident[127] = '\0';
504
505         if (*ksp_ptr == NULL)
506         {
507                 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
508                 {
509                         syslog (LOG_ERR, "Cound not find kstat %s", ident);
510                         return (-1);
511                 }
512
513                 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
514                 {
515                         syslog (LOG_WARNING, "kstat %s has wrong type", ident);
516                         *ksp_ptr = NULL;
517                         return (-1);
518                 }
519         }
520
521 #ifdef assert
522         assert (*ksp_ptr != NULL);
523         assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
524 #endif
525
526         if (kstat_read (kc, *ksp_ptr, NULL) == -1)
527         {
528                 syslog (LOG_WARNING, "kstat %s could not be read", ident);
529                 return (-1);
530         }
531
532         if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
533         {
534                 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
535                 return (-1);
536         }
537
538         return (0);
539 }
540
541 long long get_kstat_value (kstat_t *ksp, char *name)
542 {
543         kstat_named_t *kn;
544         long long retval = -1LL;
545
546 #ifdef assert
547         assert (ksp != NULL);
548         assert (ksp->ks_type == KSTAT_TYPE_NAMED);
549 #else
550         if (ksp == NULL)
551         {
552                 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
553                 return (-1LL);
554         }
555         else if (ksp->ks_type != KSTAT_TYPE_NAMED)
556         {
557                 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
558                 return (-1LL);
559         }
560 #endif
561
562         if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
563                 return (retval);
564
565         if (kn->data_type == KSTAT_DATA_INT32)
566                 retval = (long long) kn->value.i32;
567         else if (kn->data_type == KSTAT_DATA_UINT32)
568                 retval = (long long) kn->value.ui32;
569         else if (kn->data_type == KSTAT_DATA_INT64)
570                 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
571         else if (kn->data_type == KSTAT_DATA_UINT64)
572                 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
573         else
574                 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
575                  
576         return (retval);
577 }
578 #endif /* HAVE_LIBKSTAT */