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