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