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