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 /* for ntohl and htonl */
33 #if HAVE_ARPA_INET_H
34 # include <arpa/inet.h>
35 #endif
37 extern int operating_mode;
39 #ifdef HAVE_LIBKSTAT
40 extern kstat_ctl_t *kc;
41 #endif
43 void sstrncpy (char *d, const char *s, int len)
44 {
45 strncpy (d, s, len);
46 d[len - 1] = '\0';
47 }
49 char *sstrdup (const char *s)
50 {
51 char *r;
53 if (s == NULL)
54 return (NULL);
56 if((r = strdup (s)) == NULL)
57 {
58 DBG ("Not enough memory.");
59 exit(3);
60 }
62 return (r);
63 }
65 void *smalloc (size_t size)
66 {
67 void *r;
69 if ((r = malloc (size)) == NULL)
70 {
71 DBG("Not enough memory.");
72 exit(3);
73 }
75 return r;
76 }
78 #if 0
79 void sfree (void **ptr)
80 {
81 if (ptr == NULL)
82 return;
84 if (*ptr != NULL)
85 free (*ptr);
87 *ptr = NULL;
88 }
89 #endif
91 ssize_t sread (int fd, void *buf, size_t count)
92 {
93 char *ptr;
94 size_t nleft;
95 ssize_t status;
97 ptr = (char *) buf;
98 nleft = count;
100 while (nleft > 0)
101 {
102 status = read (fd, (void *) ptr, nleft);
104 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
105 continue;
107 if (status < 0)
108 return (status);
110 if (status == 0)
111 {
112 DBG ("Received EOF from fd %i. "
113 "Closing fd and returning error.",
114 fd);
115 close (fd);
116 return (-1);
117 }
119 assert (nleft >= status);
121 nleft = nleft - status;
122 ptr = ptr + status;
123 }
125 return (0);
126 }
129 ssize_t swrite (int fd, const void *buf, size_t count)
130 {
131 const char *ptr;
132 size_t nleft;
133 ssize_t status;
135 ptr = (const char *) buf;
136 nleft = count;
138 while (nleft > 0)
139 {
140 status = write (fd, (const void *) ptr, nleft);
142 if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
143 continue;
145 if (status < 0)
146 return (status);
148 nleft = nleft - status;
149 ptr = ptr + status;
150 }
152 return (0);
153 }
155 int strsplit (char *string, char **fields, size_t size)
156 {
157 size_t i;
158 char *ptr;
160 i = 0;
161 ptr = string;
162 while ((fields[i] = strtok (ptr, " \t")) != NULL)
163 {
164 ptr = NULL;
165 i++;
167 if (i >= size)
168 break;
169 }
171 return (i);
172 }
174 int strjoin (char *dst, size_t dst_len,
175 char **fields, size_t fields_num,
176 const char *sep)
177 {
178 int field_len;
179 int sep_len;
180 int i;
182 memset (dst, '\0', dst_len);
184 if (fields_num <= 0)
185 return (-1);
187 sep_len = 0;
188 if (sep != NULL)
189 sep_len = strlen (sep);
191 for (i = 0; i < fields_num; i++)
192 {
193 if ((i > 0) && (sep_len > 0))
194 {
195 if (dst_len <= sep_len)
196 return (-1);
198 strncat (dst, sep, dst_len);
199 dst_len -= sep_len;
200 }
202 field_len = strlen (fields[i]);
204 if (dst_len <= field_len)
205 return (-1);
207 strncat (dst, fields[i], dst_len);
208 dst_len -= field_len;
209 }
211 return (strlen (dst));
212 }
214 int strsubstitute (char *str, char c_from, char c_to)
215 {
216 int ret;
218 if (str == NULL)
219 return (-1);
221 ret = 0;
222 while (*str != '\0')
223 {
224 if (*str == c_from)
225 {
226 *str = c_to;
227 ret++;
228 }
229 str++;
230 }
232 return (ret);
233 }
235 int escape_slashes (char *buf, int buf_len)
236 {
237 int i;
239 if (strcmp (buf, "/") == 0)
240 {
241 if (buf_len < 5)
242 return (-1);
244 strncpy (buf, "root", buf_len);
245 return (0);
246 }
248 /* Move one to the left */
249 memmove (buf, buf + 1, buf_len - 1);
251 for (i = 0; i < buf_len - 1; i++)
252 {
253 if (buf[i] == '\0')
254 break;
255 else if (buf[i] == '/')
256 buf[i] = '_';
257 }
258 buf[i] = '\0';
260 return (0);
261 }
263 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
264 {
265 if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL))
266 return (-2);
268 if ((tv0->tv_sec < tv1->tv_sec)
269 || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec)))
270 return (-1);
272 ret->tv_sec = tv0->tv_sec - tv1->tv_sec;
273 ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec));
275 if (ret->tv_nsec < 0)
276 {
277 assert (ret->tv_sec > 0);
279 ret->tv_nsec += 1000000000;
280 ret->tv_sec -= 1;
281 }
283 return (0);
284 }
286 int check_create_dir (const char *file_orig)
287 {
288 struct stat statbuf;
290 char file_copy[512];
291 char dir[512];
292 int dir_len = 512;
293 char *fields[16];
294 int fields_num;
295 char *ptr;
296 int last_is_file = 1;
297 int len;
298 int i;
300 /*
301 * Sanity checks first
302 */
303 if (file_orig == NULL)
304 return (-1);
306 if ((len = strlen (file_orig)) < 1)
307 return (-1);
308 else if (len >= 512)
309 return (-1);
311 /*
312 * If `file_orig' ends in a slash the last component is a directory,
313 * otherwise it's a file. Act accordingly..
314 */
315 if (file_orig[len - 1] == '/')
316 last_is_file = 0;
318 /*
319 * Create a copy for `strtok' to destroy
320 */
321 strncpy (file_copy, file_orig, 512);
322 file_copy[511] = '\0';
324 /*
325 * Break into components. This will eat up several slashes in a row and
326 * remove leading and trailing slashes..
327 */
328 ptr = file_copy;
329 fields_num = 0;
330 while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
331 {
332 ptr = NULL;
333 fields_num++;
335 if (fields_num >= 16)
336 break;
337 }
339 /*
340 * For each component, do..
341 */
342 for (i = 0; i < (fields_num - last_is_file); i++)
343 {
344 /*
345 * Do not create directories that start with a dot. This
346 * prevents `../../' attacks and other likely malicious
347 * behavior.
348 */
349 if (fields[i][0] == '.')
350 {
351 syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
352 return (-2);
353 }
355 /*
356 * Join the components together again
357 */
358 if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
359 {
360 syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
361 return (-1);
362 }
364 if (stat (dir, &statbuf) == -1)
365 {
366 if (errno == ENOENT)
367 {
368 if (mkdir (dir, 0755) == -1)
369 {
370 syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
371 return (-1);
372 }
373 }
374 else
375 {
376 syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
377 return (-1);
378 }
379 }
380 else if (!S_ISDIR (statbuf.st_mode))
381 {
382 syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
383 return (-1);
384 }
385 }
387 return (0);
388 }
390 static int log_create_file (char *filename, char **ds_def, int ds_num)
391 {
392 FILE *log;
393 int i;
395 if (check_create_dir (filename))
396 return (-1);
398 log = fopen (filename, "w");
399 if (log == NULL)
400 {
401 syslog (LOG_WARNING, "Failed to create %s: %s", filename,
402 strerror(errno));
403 return (-1);
404 }
406 fprintf (log, "epoch");
407 for (i = 0; i < ds_num; i++)
408 {
409 char *name;
410 char *tmp;
412 name = strchr (ds_def[i], ':');
413 if (name == NULL)
414 {
415 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
416 ds_def[i], filename);
417 fclose(log);
418 remove(filename);
419 return (-1);
420 }
422 name += 1;
423 tmp = strchr (name, ':');
424 if (tmp == NULL)
425 {
426 syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
427 ds_def[i], filename);
428 fclose(log);
429 remove(filename);
430 return (-1);
431 }
433 /* The `%.*s' is needed because there is no null-byte behind
434 * the name. */
435 fprintf(log, ",%.*s", (int) (tmp - name), name);
436 }
437 fprintf(log, "\n");
438 fclose(log);
440 return 0;
441 }
443 int log_update_file (char *host, char *file, char *values,
444 char **ds_def, int ds_num)
445 {
446 char *tmp;
447 FILE *fp;
448 struct stat statbuf;
449 char full_file[1024];
451 /* Cook the values a bit: Substitute colons with commas */
452 strsubstitute (values, ':', ',');
454 /* host == NULL => local mode */
455 if (host != NULL)
456 {
457 if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
458 return (-1);
459 }
460 else
461 {
462 if (snprintf (full_file, 1024, "%s", file) >= 1024)
463 return (-1);
464 }
466 strncpy (full_file, file, 1024);
468 tmp = full_file + strlen (full_file) - 4;
469 assert ((tmp != NULL) && (tmp > full_file));
471 /* Change the filename for logfiles. */
472 if (strncmp (tmp, ".rrd", 4) == 0)
473 {
474 time_t now;
475 struct tm *tm;
477 /* TODO: Find a way to minimize the calls to `localtime', since
478 * they are pretty expensive.. */
479 now = time (NULL);
480 tm = localtime (&now);
482 strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
484 /* `localtime(3)' returns a pointer to static data,
485 * therefore the pointer may not be free'd. */
486 }
487 else
488 DBG ("The filename ends with `%s' which is unexpected.", tmp);
490 if (stat (full_file, &statbuf) == -1)
491 {
492 if (errno == ENOENT)
493 {
494 if (log_create_file (full_file, ds_def, ds_num))
495 return (-1);
496 }
497 else
498 {
499 syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
500 return (-1);
501 }
502 }
503 else if (!S_ISREG (statbuf.st_mode))
504 {
505 syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
506 return (-1);
507 }
510 fp = fopen (full_file, "a");
511 if (fp == NULL)
512 {
513 syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
514 strerror(errno));
515 return (-1);
516 }
517 fprintf(fp, "%s\n", values);
518 fclose(fp);
520 return (0);
521 } /* int log_update_file */
524 #ifdef HAVE_LIBKSTAT
525 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
526 {
527 char ident[128];
529 if (kc == NULL)
530 return (-1);
532 snprintf (ident, 128, "%s,%i,%s", module, instance, name);
533 ident[127] = '\0';
535 if (*ksp_ptr == NULL)
536 {
537 if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
538 {
539 syslog (LOG_ERR, "Cound not find kstat %s", ident);
540 return (-1);
541 }
543 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
544 {
545 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
546 *ksp_ptr = NULL;
547 return (-1);
548 }
549 }
551 #ifdef assert
552 assert (*ksp_ptr != NULL);
553 assert ((*ksp_ptr)->ks_type == KSTAT_TYPE_NAMED);
554 #endif
556 if (kstat_read (kc, *ksp_ptr, NULL) == -1)
557 {
558 syslog (LOG_WARNING, "kstat %s could not be read", ident);
559 return (-1);
560 }
562 if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
563 {
564 syslog (LOG_WARNING, "kstat %s has wrong type", ident);
565 return (-1);
566 }
568 return (0);
569 }
571 long long get_kstat_value (kstat_t *ksp, char *name)
572 {
573 kstat_named_t *kn;
574 long long retval = -1LL;
576 #ifdef assert
577 assert (ksp != NULL);
578 assert (ksp->ks_type == KSTAT_TYPE_NAMED);
579 #else
580 if (ksp == NULL)
581 {
582 fprintf (stderr, "ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
583 return (-1LL);
584 }
585 else if (ksp->ks_type != KSTAT_TYPE_NAMED)
586 {
587 fprintf (stderr, "ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
588 return (-1LL);
589 }
590 #endif
592 if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
593 return (retval);
595 if (kn->data_type == KSTAT_DATA_INT32)
596 retval = (long long) kn->value.i32;
597 else if (kn->data_type == KSTAT_DATA_UINT32)
598 retval = (long long) kn->value.ui32;
599 else if (kn->data_type == KSTAT_DATA_INT64)
600 retval = (long long) kn->value.i64; /* According to ANSI C99 `long long' must hold at least 64 bits */
601 else if (kn->data_type == KSTAT_DATA_UINT64)
602 retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
603 else
604 syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
606 return (retval);
607 }
608 #endif /* HAVE_LIBKSTAT */
610 unsigned long long ntohll (unsigned long long n)
611 {
612 #if __BYTE_ORDER == __BIG_ENDIAN
613 return (n);
614 #else
615 return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
616 #endif
617 }
619 unsigned long long htonll (unsigned long long n)
620 {
621 #if __BYTE_ORDER == __BIG_ENDIAN
622 return (n);
623 #else
624 return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
625 #endif
626 }