0b603fa876dcd9b823584fa355bd66087ab5a94b
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 **/
25 #include "common.h"
26 #include "utils_debug.h"
28 extern int operating_mode;
30 #ifdef HAVE_LIBKSTAT
31 extern kstat_ctl_t *kc;
32 #endif
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 */
51 void sstrncpy (char *d, const char *s, int len)
52 {
53 strncpy (d, s, len);
54 d[len - 1] = '\0';
55 }
57 char *sstrdup (const char *s)
58 {
59 char *r;
61 if (s == NULL)
62 return (NULL);
64 if((r = strdup (s)) == NULL)
65 {
66 DBG ("Not enough memory.");
67 exit(3);
68 }
70 return (r);
71 }
73 void *smalloc (size_t size)
74 {
75 void *r;
77 if ((r = malloc (size)) == NULL)
78 {
79 DBG("Not enough memory.");
80 exit(3);
81 }
83 return r;
84 }
86 #if 0
87 void sfree (void **ptr)
88 {
89 if (ptr == NULL)
90 return;
92 if (*ptr != NULL)
93 free (*ptr);
95 *ptr = NULL;
96 }
97 #endif
99 int strsplit (char *string, char **fields, size_t size)
100 {
101 size_t i;
102 char *ptr;
104 i = 0;
105 ptr = string;
106 while ((fields[i] = strtok (ptr, " \t")) != NULL)
107 {
108 ptr = NULL;
109 i++;
111 if (i >= size)
112 break;
113 }
115 return (i);
116 }
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;
126 memset (dst, '\0', dst_len);
128 if (fields_num <= 0)
129 return (-1);
131 sep_len = 0;
132 if (sep != NULL)
133 sep_len = strlen (sep);
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);
142 strncat (dst, sep, dst_len);
143 dst_len -= sep_len;
144 }
146 field_len = strlen (fields[i]);
148 if (dst_len <= field_len)
149 return (-1);
151 strncat (dst, fields[i], dst_len);
152 dst_len -= field_len;
153 }
155 return (strlen (dst));
156 }
158 int escape_slashes (char *buf, int buf_len)
159 {
160 int i;
162 if (strcmp (buf, "/") == 0)
163 {
164 if (buf_len < 5)
165 return (-1);
167 strncpy (buf, "root", buf_len);
168 return (0);
169 }
171 /* Move one to the left */
172 memmove (buf, buf + 1, buf_len - 1);
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';
183 return (0);
184 }
186 static int check_create_dir (const char *file_orig)
187 {
188 struct stat statbuf;
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;
200 /*
201 * Sanity checks first
202 */
203 if (file_orig == NULL)
204 return (-1);
206 if ((len = strlen (file_orig)) < 1)
207 return (-1);
208 else if (len >= 512)
209 return (-1);
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;
218 /*
219 * Create a copy for `strtok' to destroy
220 */
221 strncpy (file_copy, file_orig, 512);
222 file_copy[511] = '\0';
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++;
235 if (fields_num >= 16)
236 break;
237 }
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 }
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 }
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 }
287 return (0);
288 }
290 static int log_create_file (char *filename, char **ds_def, int ds_num)
291 {
292 FILE *log;
293 int i;
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 }
303 for (i = 0; i < ds_num; i++)
304 {
305 char *name;
306 char *tmp;
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 }
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 }
329 if (i != 0)
330 fprintf (log, ":");
331 fprintf(log, "%.*s", (tmp - name), name);
332 }
333 fprintf(log, "\n");
334 fclose(log);
336 return 0;
337 }
339 static 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];
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 }
359 strncpy (full_file, file, 1024);
361 tmp = full_file + strlen (full_file) - 4;
362 assert (tmp > 0);
364 /* Change the filename for logfiles. */
365 if (strncmp (tmp, ".rrd", 4) == 0)
366 {
367 time_t now;
368 struct tm *tm;
370 now = time (NULL);
371 tm = localtime (&now);
373 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
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);
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 }
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);
411 return (0);
412 } /* int log_update_file */
414 #if HAVE_LIBRRD
415 static int rrd_create_file (char *filename, char **ds_def, int ds_num)
416 {
417 char **argv;
418 int argc;
419 int i, j;
420 int status = 0;
422 if (check_create_dir (filename))
423 return (-1);
425 argc = ds_num + rra_num + 4;
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 }
433 argv[0] = "create";
434 argv[1] = filename;
435 argv[2] = "-s";
436 argv[3] = "10";
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;
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 }
453 free (argv);
455 return (status);
456 }
457 #endif /* HAVE_LIBRRD */
459 int rrd_update_file (char *host, char *file, char *values,
460 char **ds_def, int ds_num)
461 {
462 #if HAVE_LIBRRD
463 struct stat statbuf;
464 char full_file[1024];
465 char *argv[4] = { "update", full_file, values, NULL };
466 #endif /* HAVE_LIBRRD */
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));
474 #if HAVE_LIBRRD
475 /* host == NULL => local mode */
476 if (host != NULL)
477 {
478 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
479 return (-1);
480 }
481 else
482 {
483 if (snprintf (full_file, 1024, "%s", file) >= 1024)
484 return (-1);
485 }
487 if (stat (full_file, &statbuf) == -1)
488 {
489 if (errno == ENOENT)
490 {
491 if (rrd_create_file (full_file, ds_def, ds_num))
492 return (-1);
493 }
494 else
495 {
496 syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
497 return (-1);
498 }
499 }
500 else if (!S_ISREG (statbuf.st_mode))
501 {
502 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
503 return (-1);
504 }
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 return (0);
514 /* #endif HAVE_LIBRRD */
516 #else
517 syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
518 return (-1);
519 #endif
520 }
522 #ifdef HAVE_LIBKSTAT
523 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
524 {
525 char ident[128];
527 if (kc == NULL)
528 return (-1);
530 snprintf (ident, 128, "%s,%i,%s", module, instance, name);
531 ident[127] = '\0';
533 if (*ksp_ptr == NULL)
534 {
535 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
536 {
537 syslog (LOG_ERR, "Cound not find kstat %s", ident);
538 return (-1);
539 }
541 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
542 {
543 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
544 *ksp_ptr = NULL;
545 return (-1);
546 }
547 }
549 #ifdef assert
550 assert (*ksp_ptr != NULL);
551 assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
552 #endif
554 if (kstat_read (kc, *ksp_ptr, NULL) == -1)
555 {
556 syslog (LOG_WARNING, "kstat %s could not be read", ident);
557 return (-1);
558 }
560 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
561 {
562 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
563 return (-1);
564 }
566 return (0);
567 }
569 long long get_kstat_value (kstat_t *ksp, char *name)
570 {
571 kstat_named_t *kn;
572 long long retval = -1LL;
574 #ifdef assert
575 assert (ksp != NULL);
576 assert (ksp->ks_type == KSTAT_TYPE_NAMED);
577 #else
578 if (ksp == NULL)
579 {
580 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
581 return (-1LL);
582 }
583 else if (ksp->ks_type != KSTAT_TYPE_NAMED)
584 {
585 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
586 return (-1LL);
587 }
588 #endif
590 if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
591 return (retval);
593 if (kn->data_type == KSTAT_DATA_INT32)
594 retval = (long long) kn->value.i32;
595 else if (kn->data_type == KSTAT_DATA_UINT32)
596 retval = (long long) kn->value.ui32;
597 else if (kn->data_type == KSTAT_DATA_INT64)
598 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
599 else if (kn->data_type == KSTAT_DATA_UINT64)
600 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
601 else
602 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
604 return (retval);
605 }
606 #endif /* HAVE_LIBKSTAT */