X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fcommon.c;h=cf2a63989f357600d3dc7aa011b551c90abf59f4;hb=fd909f8965b2b6714dc1da328c77f3681c04af5e;hp=1eec286a54d27c4b1731a80082912704e61dde10;hpb=dd7eb19ac13a5eb4e39894a0d613067a70f194a0;p=collectd.git diff --git a/src/common.c b/src/common.c index 1eec286a..cf2a6398 100644 --- a/src/common.c +++ b/src/common.c @@ -1,6 +1,6 @@ /** * collectd - src/common.c - * Copyright (C) 2005 Florian octo Forster + * Copyright (C) 2005,2006 Florian octo Forster * * This program is free software; you can redistribute it and/ * or modify it under the terms of the GNU General Public Li- @@ -25,56 +25,133 @@ #include "common.h" #include "utils_debug.h" +#ifdef HAVE_MATH_H +# include +#endif + +/* for ntohl and htonl */ +#if HAVE_ARPA_INET_H +# include +#endif + +extern int operating_mode; + #ifdef HAVE_LIBKSTAT extern kstat_ctl_t *kc; #endif -#ifdef HAVE_LIBRRD -static char *rra_def[] = -{ - "RRA:AVERAGE:0.2:6:1500", - "RRA:AVERAGE:0.1:180:1680", - "RRA:AVERAGE:0.1:2160:1520", - "RRA:MIN:0.2:6:1500", - "RRA:MIN:0.1:180:1680", - "RRA:MIN:0.1:2160:1520", - "RRA:MAX:0.2:6:1500", - "RRA:MAX:0.1:180:1680", - "RRA:MAX:0.1:2160:1520", - NULL -}; -static int rra_num = 9; -#endif /* HAVE_LIBRRD */ - -void -sstrncpy(char *d, const char *s, int len) +void sstrncpy (char *d, const char *s, int len) { - strncpy(d, s, len); - d[len - 1] = 0; + strncpy (d, s, len); + d[len - 1] = '\0'; } -char * -sstrdup(const char *s) +char *sstrdup (const char *s) { - char *r = strdup(s); - if(r == NULL) { - DBG("Not enough memory."); + char *r; + + if (s == NULL) + return (NULL); + + if((r = strdup (s)) == NULL) + { + DBG ("Not enough memory."); exit(3); } - return r; + + return (r); } -void * -smalloc(size_t size) +void *smalloc (size_t size) { - void *r = malloc(size); - if(r == NULL) { + void *r; + + if ((r = malloc (size)) == NULL) + { DBG("Not enough memory."); exit(3); } + return r; } +#if 0 +void sfree (void **ptr) +{ + if (ptr == NULL) + return; + + if (*ptr != NULL) + free (*ptr); + + *ptr = NULL; +} +#endif + +ssize_t sread (int fd, void *buf, size_t count) +{ + char *ptr; + size_t nleft; + ssize_t status; + + ptr = (char *) buf; + nleft = count; + + while (nleft > 0) + { + status = read (fd, (void *) ptr, nleft); + + if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + continue; + + if (status < 0) + return (status); + + if (status == 0) + { + DBG ("Received EOF from fd %i. " + "Closing fd and returning error.", + fd); + close (fd); + return (-1); + } + + assert (nleft >= status); + + nleft = nleft - status; + ptr = ptr + status; + } + + return (0); +} + + +ssize_t swrite (int fd, const void *buf, size_t count) +{ + const char *ptr; + size_t nleft; + ssize_t status; + + ptr = (const char *) buf; + nleft = count; + + while (nleft > 0) + { + status = write (fd, (const void *) ptr, nleft); + + if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR))) + continue; + + if (status < 0) + return (status); + + nleft = nleft - status; + ptr = ptr + status; + } + + return (0); +} + int strsplit (char *string, char **fields, size_t size) { size_t i; @@ -94,91 +171,289 @@ int strsplit (char *string, char **fields, size_t size) return (i); } -#ifdef HAVE_LIBRRD -int check_create_dir (char *dir) +int strjoin (char *dst, size_t dst_len, + char **fields, size_t fields_num, + const char *sep) +{ + int field_len; + int sep_len; + int i; + + memset (dst, '\0', dst_len); + + if (fields_num <= 0) + return (-1); + + sep_len = 0; + if (sep != NULL) + sep_len = strlen (sep); + + for (i = 0; i < fields_num; i++) + { + if ((i > 0) && (sep_len > 0)) + { + if (dst_len <= sep_len) + return (-1); + + strncat (dst, sep, dst_len); + dst_len -= sep_len; + } + + field_len = strlen (fields[i]); + + if (dst_len <= field_len) + return (-1); + + strncat (dst, fields[i], dst_len); + dst_len -= field_len; + } + + return (strlen (dst)); +} + +int strsubstitute (char *str, char c_from, char c_to) +{ + int ret; + + if (str == NULL) + return (-1); + + ret = 0; + while (*str != '\0') + { + if (*str == c_from) + { + *str = c_to; + ret++; + } + str++; + } + + return (ret); +} + +int escape_slashes (char *buf, int buf_len) +{ + int i; + + if (strcmp (buf, "/") == 0) + { + if (buf_len < 5) + return (-1); + + strncpy (buf, "root", buf_len); + return (0); + } + + /* Move one to the left */ + memmove (buf, buf + 1, buf_len - 1); + + for (i = 0; i < buf_len - 1; i++) + { + if (buf[i] == '\0') + break; + else if (buf[i] == '/') + buf[i] = '_'; + } + buf[i] = '\0'; + + return (0); +} + +int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret) +{ + if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL)) + return (-2); + + if ((tv0->tv_sec < tv1->tv_sec) + || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec))) + return (-1); + + ret->tv_sec = tv0->tv_sec - tv1->tv_sec; + ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec)); + + if (ret->tv_nsec < 0) + { + assert (ret->tv_sec > 0); + + ret->tv_nsec += 1000000000; + ret->tv_sec -= 1; + } + + return (0); +} + +int check_create_dir (const char *file_orig) { struct stat statbuf; - if (stat (dir, &statbuf) == -1) + char file_copy[512]; + char dir[512]; + int dir_len = 512; + char *fields[16]; + int fields_num; + char *ptr; + int last_is_file = 1; + int len; + int i; + + /* + * Sanity checks first + */ + if (file_orig == NULL) + return (-1); + + if ((len = strlen (file_orig)) < 1) + return (-1); + else if (len >= 512) + return (-1); + + /* + * If `file_orig' ends in a slash the last component is a directory, + * otherwise it's a file. Act accordingly.. + */ + if (file_orig[len - 1] == '/') + last_is_file = 0; + + /* + * Create a copy for `strtok' to destroy + */ + strncpy (file_copy, file_orig, 512); + file_copy[511] = '\0'; + + /* + * Break into components. This will eat up several slashes in a row and + * remove leading and trailing slashes.. + */ + ptr = file_copy; + fields_num = 0; + while ((fields[fields_num] = strtok (ptr, "/")) != NULL) { - if (errno == ENOENT) + ptr = NULL; + fields_num++; + + if (fields_num >= 16) + break; + } + + /* + * For each component, do.. + */ + for (i = 0; i < (fields_num - last_is_file); i++) + { + /* + * Do not create directories that start with a dot. This + * prevents `../../' attacks and other likely malicious + * behavior. + */ + if (fields[i][0] == '.') + { + syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig); + return (-2); + } + + /* + * Join the components together again + */ + if (strjoin (dir, dir_len, fields, i + 1, "/") < 0) { - if (mkdir (dir, 0755) == -1) + syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i); + return (-1); + } + + if (stat (dir, &statbuf) == -1) + { + if (errno == ENOENT) { - syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno)); + if (mkdir (dir, 0755) == -1) + { + syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno)); + return (-1); + } + } + else + { + syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno)); return (-1); } } - else + else if (!S_ISDIR (statbuf.st_mode)) { - syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno)); + syslog (LOG_ERR, "stat (%s): Not a directory!", dir); return (-1); } } - else if (!S_ISDIR (statbuf.st_mode)) - { - syslog (LOG_ERR, "stat %s: Not a directory!", dir); - return (-1); - } return (0); } -int rrd_create_file (char *filename, char **ds_def, int ds_num) +static int log_create_file (char *filename, char **ds_def, int ds_num) { - char **argv; - int argc; - int i, j; - int status = 0; + FILE *log; + int i; - argc = ds_num + rra_num + 4; + if (check_create_dir (filename)) + return (-1); - if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL) + log = fopen (filename, "w"); + if (log == NULL) { - syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno)); + syslog (LOG_WARNING, "Failed to create %s: %s", filename, + strerror(errno)); return (-1); } - argv[0] = "create"; - argv[1] = filename; - argv[2] = "-s"; - argv[3] = "10"; - - j = 4; + fprintf (log, "epoch"); for (i = 0; i < ds_num; i++) - argv[j++] = ds_def[i]; - for (i = 0; i < rra_num; i++) - argv[j++] = rra_def[i]; - argv[j] = NULL; - - optind = 0; /* bug in librrd? */ - rrd_clear_error (); - if (rrd_create (argc, argv) == -1) { - syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ()); - status = -1; + char *name; + char *tmp; + + name = strchr (ds_def[i], ':'); + if (name == NULL) + { + syslog (LOG_WARNING, "Invalid DS definition '%s' for %s", + ds_def[i], filename); + fclose(log); + remove(filename); + return (-1); + } + + name += 1; + tmp = strchr (name, ':'); + if (tmp == NULL) + { + syslog (LOG_WARNING, "Invalid DS definition '%s' for %s", + ds_def[i], filename); + fclose(log); + remove(filename); + return (-1); + } + + /* The `%.*s' is needed because there is no null-byte behind + * the name. */ + fprintf(log, ",%.*s", (int) (tmp - name), name); } + fprintf(log, "\n"); + fclose(log); - free (argv); - - return (status); + return 0; } -#endif /* HAVE_LIBRRD */ -int rrd_update_file (char *host, char *file, char *values, +int log_update_file (char *host, char *file, char *values, char **ds_def, int ds_num) { -#ifdef HAVE_LIBRRD + char *tmp; + FILE *fp; struct stat statbuf; char full_file[1024]; - char *argv[4] = { "update", full_file, values, NULL }; + + /* Cook the values a bit: Substitute colons with commas */ + strsubstitute (values, ':', ','); /* host == NULL => local mode */ if (host != NULL) { - if (check_create_dir (host)) - return (-1); - if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024) return (-1); } @@ -188,11 +463,35 @@ int rrd_update_file (char *host, char *file, char *values, return (-1); } + strncpy (full_file, file, 1024); + + tmp = full_file + strlen (full_file) - 4; + assert ((tmp != NULL) && (tmp > full_file)); + + /* Change the filename for logfiles. */ + if (strncmp (tmp, ".rrd", 4) == 0) + { + time_t now; + struct tm *tm; + + /* TODO: Find a way to minimize the calls to `localtime', since + * they are pretty expensive.. */ + now = time (NULL); + tm = localtime (&now); + + strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm); + + /* `localtime(3)' returns a pointer to static data, + * therefore the pointer may not be free'd. */ + } + else + DBG ("The filename ends with `%s' which is unexpected.", tmp); + if (stat (full_file, &statbuf) == -1) { if (errno == ENOENT) { - if (rrd_create_file (full_file, ds_def, ds_num)) + if (log_create_file (full_file, ds_def, ds_num)) return (-1); } else @@ -207,17 +506,20 @@ int rrd_update_file (char *host, char *file, char *values, return (-1); } - optind = 0; /* bug in librrd? */ - rrd_clear_error (); - if (rrd_update (3, argv) == -1) + + fp = fopen (full_file, "a"); + if (fp == NULL) { - syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ()); + syslog (LOG_WARNING, "Failed to append to %s: %s", full_file, + strerror(errno)); return (-1); } -#endif /* HAVE_LIBRRD */ + fprintf(fp, "%s\n", values); + fclose(fp); return (0); -} +} /* int log_update_file */ + #ifdef HAVE_LIBKSTAT int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name) @@ -304,3 +606,21 @@ long long get_kstat_value (kstat_t *ksp, char *name) return (retval); } #endif /* HAVE_LIBKSTAT */ + +unsigned long long ntohll (unsigned long long n) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + return (n); +#else + return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32); +#endif +} + +unsigned long long htonll (unsigned long long n) +{ +#if __BYTE_ORDER == __BIG_ENDIAN + return (n); +#else + return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32); +#endif +}