6da5e33cbfaa1e24130365de6d274703be49d9bc
1 /**
2 * collectd - src/common.c
3 * Copyright (C) 2005,2006 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 #ifdef HAVE_MATH_H
29 # include <math.h>
30 #endif
32 extern int operating_mode;
34 #ifdef HAVE_LIBKSTAT
35 extern kstat_ctl_t *kc;
36 #endif
38 void sstrncpy (char *d, const char *s, int len)
39 {
40 strncpy (d, s, len);
41 d[len - 1] = '\0';
42 }
44 char *sstrdup (const char *s)
45 {
46 char *r;
48 if (s == NULL)
49 return (NULL);
51 if((r = strdup (s)) == NULL)
52 {
53 DBG ("Not enough memory.");
54 exit(3);
55 }
57 return (r);
58 }
60 void *smalloc (size_t size)
61 {
62 void *r;
64 if ((r = malloc (size)) == NULL)
65 {
66 DBG("Not enough memory.");
67 exit(3);
68 }
70 return r;
71 }
73 #if 0
74 void sfree (void **ptr)
75 {
76 if (ptr == NULL)
77 return;
79 if (*ptr != NULL)
80 free (*ptr);
82 *ptr = NULL;
83 }
84 #endif
86 ssize_t sread (int fd, void *buf, size_t count)
87 {
88 char *ptr;
89 size_t nleft;
90 ssize_t status;
92 ptr = (char *) buf;
93 nleft = count;
95 while (nleft > 0)
96 {
97 status = read (fd, (void *) ptr, nleft);
99 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
100 continue;
102 if (status < 0)
103 return (status);
105 if (status == 0)
106 {
107 DBG ("Received EOF from fd %i. "
108 "Closing fd and returning error.",
109 fd);
110 close (fd);
111 return (-1);
112 }
114 assert (nleft >= status);
116 nleft = nleft - status;
117 ptr = ptr + status;
118 }
120 return (0);
121 }
124 ssize_t swrite (int fd, const void *buf, size_t count)
125 {
126 const char *ptr;
127 size_t nleft;
128 ssize_t status;
130 ptr = (const char *) buf;
131 nleft = count;
133 while (nleft > 0)
134 {
135 status = write (fd, (const void *) ptr, nleft);
137 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
138 continue;
140 if (status < 0)
141 return (status);
143 nleft = nleft - status;
144 ptr = ptr + status;
145 }
147 return (0);
148 }
150 int strsplit (char *string, char **fields, size_t size)
151 {
152 size_t i;
153 char *ptr;
155 i = 0;
156 ptr = string;
157 while ((fields[i] = strtok (ptr, " \t")) != NULL)
158 {
159 ptr = NULL;
160 i++;
162 if (i >= size)
163 break;
164 }
166 return (i);
167 }
169 int strjoin (char *dst, size_t dst_len,
170 char **fields, size_t fields_num,
171 const char *sep)
172 {
173 int field_len;
174 int sep_len;
175 int i;
177 memset (dst, '\0', dst_len);
179 if (fields_num <= 0)
180 return (-1);
182 sep_len = 0;
183 if (sep != NULL)
184 sep_len = strlen (sep);
186 for (i = 0; i < fields_num; i++)
187 {
188 if ((i > 0) && (sep_len > 0))
189 {
190 if (dst_len <= sep_len)
191 return (-1);
193 strncat (dst, sep, dst_len);
194 dst_len -= sep_len;
195 }
197 field_len = strlen (fields[i]);
199 if (dst_len <= field_len)
200 return (-1);
202 strncat (dst, fields[i], dst_len);
203 dst_len -= field_len;
204 }
206 return (strlen (dst));
207 }
209 int strsubstitute (char *str, char c_from, char c_to)
210 {
211 int ret;
213 if (str == NULL)
214 return (-1);
216 ret = 0;
217 while (*str != '\0')
218 {
219 if (*str == c_from)
220 {
221 *str = c_to;
222 ret++;
223 }
224 str++;
225 }
227 return (ret);
228 }
230 int escape_slashes (char *buf, int buf_len)
231 {
232 int i;
234 if (strcmp (buf, "/") == 0)
235 {
236 if (buf_len < 5)
237 return (-1);
239 strncpy (buf, "root", buf_len);
240 return (0);
241 }
243 /* Move one to the left */
244 memmove (buf, buf + 1, buf_len - 1);
246 for (i = 0; i < buf_len - 1; i++)
247 {
248 if (buf[i] == '\0')
249 break;
250 else if (buf[i] == '/')
251 buf[i] = '_';
252 }
253 buf[i] = '\0';
255 return (0);
256 }
258 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
259 {
260 if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
261 return (-2);
263 if ((tv0->tv_sec < tv1->tv_sec)
264 || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
265 return (-1);
267 ret->tv_sec = tv0->tv_sec - tv1->tv_sec;
268 ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
270 if (ret->tv_nsec < 0)
271 {
272 assert (ret->tv_sec > 0);
274 ret->tv_nsec += 1000000000;
275 ret->tv_sec -= 1;
276 }
278 return (0);
279 }
281 int check_create_dir (const char *file_orig)
282 {
283 struct stat statbuf;
285 char file_copy[512];
286 char dir[512];
287 int dir_len = 512;
288 char *fields[16];
289 int fields_num;
290 char *ptr;
291 int last_is_file = 1;
292 int len;
293 int i;
295 /*
296 * Sanity checks first
297 */
298 if (file_orig == NULL)
299 return (-1);
301 if ((len = strlen (file_orig)) < 1)
302 return (-1);
303 else if (len >= 512)
304 return (-1);
306 /*
307 * If `file_orig' ends in a slash the last component is a directory,
308 * otherwise it's a file. Act accordingly..
309 */
310 if (file_orig[len - 1] == '/')
311 last_is_file = 0;
313 /*
314 * Create a copy for `strtok' to destroy
315 */
316 strncpy (file_copy, file_orig, 512);
317 file_copy[511] = '\0';
319 /*
320 * Break into components. This will eat up several slashes in a row and
321 * remove leading and trailing slashes..
322 */
323 ptr = file_copy;
324 fields_num = 0;
325 while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
326 {
327 ptr = NULL;
328 fields_num++;
330 if (fields_num >= 16)
331 break;
332 }
334 /*
335 * For each component, do..
336 */
337 for (i = 0; i < (fields_num - last_is_file); i++)
338 {
339 /*
340 * Do not create directories that start with a dot. This
341 * prevents `../../' attacks and other likely malicious
342 * behavior.
343 */
344 if (fields[i][0] == '.')
345 {
346 syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
347 return (-2);
348 }
350 /*
351 * Join the components together again
352 */
353 if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
354 {
355 syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
356 return (-1);
357 }
359 if (stat (dir, &statbuf) == -1)
360 {
361 if (errno == ENOENT)
362 {
363 if (mkdir (dir, 0755) == -1)
364 {
365 syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
366 return (-1);
367 }
368 }
369 else
370 {
371 syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
372 return (-1);
373 }
374 }
375 else if (!S_ISDIR (statbuf.st_mode))
376 {
377 syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
378 return (-1);
379 }
380 }
382 return (0);
383 }
385 static int log_create_file (char *filename, char **ds_def, int ds_num)
386 {
387 FILE *log;
388 int i;
390 if (check_create_dir (filename))
391 return (-1);
393 log = fopen (filename, "w");
394 if (log == NULL)
395 {
396 syslog (LOG_WARNING, "Failed to create %s: %s", filename,
397 strerror(errno));
398 return (-1);
399 }
401 fprintf (log, "epoch");
402 for (i = 0; i < ds_num; i++)
403 {
404 char *name;
405 char *tmp;
407 name = strchr (ds_def[i], ':');
408 if (name == NULL)
409 {
410 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
411 ds_def[i], filename);
412 fclose(log);
413 remove(filename);
414 return (-1);
415 }
417 name += 1;
418 tmp = strchr (name, ':');
419 if (tmp == NULL)
420 {
421 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
422 ds_def[i], filename);
423 fclose(log);
424 remove(filename);
425 return (-1);
426 }
428 /* The `%.*s' is needed because there is no null-byte behind
429 * the name. */
430 fprintf(log, ",%.*s", (int) (tmp - name), name);
431 }
432 fprintf(log, "\n");
433 fclose(log);
435 return 0;
436 }
438 int log_update_file (char *host, char *file, char *values,
439 char **ds_def, int ds_num)
440 {
441 char *tmp;
442 FILE *fp;
443 struct stat statbuf;
444 char full_file[1024];
446 /* Cook the values a bit: Substitute colons with commas */
447 strsubstitute (values, ':', ',');
449 /* host == NULL => local mode */
450 if (host != NULL)
451 {
452 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
453 return (-1);
454 }
455 else
456 {
457 if (snprintf (full_file, 1024, "%s", file) >= 1024)
458 return (-1);
459 }
461 strncpy (full_file, file, 1024);
463 tmp = full_file + strlen (full_file) - 4;
464 assert ((tmp != NULL) && (tmp > full_file));
466 /* Change the filename for logfiles. */
467 if (strncmp (tmp, ".rrd", 4) == 0)
468 {
469 time_t now;
470 struct tm *tm;
472 /* TODO: Find a way to minimize the calls to `localtime', since
473 * they are pretty expensive.. */
474 now = time (NULL);
475 tm = localtime (&now);
477 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
479 /* `localtime(3)' returns a pointer to static data,
480 * therefore the pointer may not be free'd. */
481 }
482 else
483 DBG ("The filename ends with `%s' which is unexpected.", tmp);
485 if (stat (full_file, &statbuf) == -1)
486 {
487 if (errno == ENOENT)
488 {
489 if (log_create_file (full_file, ds_def, ds_num))
490 return (-1);
491 }
492 else
493 {
494 syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
495 return (-1);
496 }
497 }
498 else if (!S_ISREG (statbuf.st_mode))
499 {
500 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
501 return (-1);
502 }
505 fp = fopen (full_file, "a");
506 if (fp == NULL)
507 {
508 syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
509 strerror(errno));
510 return (-1);
511 }
512 fprintf(fp, "%s\n", values);
513 fclose(fp);
515 return (0);
516 } /* int log_update_file */
519 #ifdef HAVE_LIBKSTAT
520 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
521 {
522 char ident[128];
524 if (kc == NULL)
525 return (-1);
527 snprintf (ident, 128, "%s,%i,%s", module, instance, name);
528 ident[127] = '\0';
530 if (*ksp_ptr == NULL)
531 {
532 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
533 {
534 syslog (LOG_ERR, "Cound not find kstat %s", ident);
535 return (-1);
536 }
538 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
539 {
540 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
541 *ksp_ptr = NULL;
542 return (-1);
543 }
544 }
546 #ifdef assert
547 assert (*ksp_ptr != NULL);
548 assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
549 #endif
551 if (kstat_read (kc, *ksp_ptr, NULL) == -1)
552 {
553 syslog (LOG_WARNING, "kstat %s could not be read", ident);
554 return (-1);
555 }
557 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
558 {
559 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
560 return (-1);
561 }
563 return (0);
564 }
566 long long get_kstat_value (kstat_t *ksp, char *name)
567 {
568 kstat_named_t *kn;
569 long long retval = -1LL;
571 #ifdef assert
572 assert (ksp != NULL);
573 assert (ksp->ks_type == KSTAT_TYPE_NAMED);
574 #else
575 if (ksp == NULL)
576 {
577 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
578 return (-1LL);
579 }
580 else if (ksp->ks_type != KSTAT_TYPE_NAMED)
581 {
582 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
583 return (-1LL);
584 }
585 #endif
587 if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
588 return (retval);
590 if (kn->data_type == KSTAT_DATA_INT32)
591 retval = (long long) kn->value.i32;
592 else if (kn->data_type == KSTAT_DATA_UINT32)
593 retval = (long long) kn->value.ui32;
594 else if (kn->data_type == KSTAT_DATA_INT64)
595 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
596 else if (kn->data_type == KSTAT_DATA_UINT64)
597 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
598 else
599 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
601 return (retval);
602 }
603 #endif /* HAVE_LIBKSTAT */