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