X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fcommon.c;h=c6a651dc56248050ccc0b7a50c7aca68e4f2c3f9;hb=902f7621661d97dac0c7bc83f39acbf76a3b947b;hp=8dbf976f11a9fc526a30350965e6d5d2bd54a58a;hpb=f5edd182195cb7a13fa32846ae869a8b8dcc1e3f;p=collectd.git diff --git a/src/common.c b/src/common.c index 8dbf976f..c6a651dc 100644 --- a/src/common.c +++ b/src/common.c @@ -1,6 +1,6 @@ /** * collectd - src/common.c - * Copyright (C) 2005-2008 Florian octo Forster + * Copyright (C) 2005-2009 Florian octo Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,6 +18,8 @@ * Authors: * Florian octo Forster * Niki W. Waibel + * Sebastian Harl + * Michał Mirosław **/ #if HAVE_CONFIG_H @@ -41,6 +43,15 @@ # include #endif +/* for getaddrinfo */ +#include +#include +#include + +#if HAVE_NETINET_IN_H +# include +#endif + #ifdef HAVE_LIBKSTAT extern kstat_ctl_t *kc; #endif @@ -61,18 +72,37 @@ char *sstrncpy (char *dest, const char *src, size_t n) return (dest); } /* char *sstrncpy */ +int ssnprintf (char *dest, size_t n, const char *format, ...) +{ + int ret = 0; + va_list ap; + + va_start (ap, format); + ret = vsnprintf (dest, n, format, ap); + dest[n - 1] = '\0'; + va_end (ap); + + return (ret); +} /* int ssnprintf */ + char *sstrdup (const char *s) { char *r; + size_t sz; if (s == NULL) return (NULL); - if((r = strdup (s)) == NULL) + /* Do not use `strdup' here, because it's not specified in POSIX. It's + * ``only'' an XSI extension. */ + sz = strlen (s) + 1; + r = (char *) malloc (sizeof (char) * sz); + if (r == NULL) { - ERROR ("Not enough memory."); + ERROR ("sstrdup: Out of memory."); exit (3); } + memcpy (r, s, sizeof (char) * sz); return (r); } /* char *sstrdup */ @@ -91,7 +121,7 @@ char *sstrerror (int errnum, char *buf, size_t buflen) pthread_mutex_lock (&strerror_r_lock); temp = strerror (errnum); - strncpy (buf, temp, buflen); + sstrncpy (buf, temp, buflen); pthread_mutex_unlock (&strerror_r_lock); } @@ -104,9 +134,9 @@ char *sstrerror (int errnum, char *buf, size_t buflen) if (buf[0] == '\0') { if ((temp != NULL) && (temp != buf) && (temp[0] != '\0')) - strncpy (buf, temp, buflen); + sstrncpy (buf, temp, buflen); else - strncpy (buf, "strerror_r did not return " + sstrncpy (buf, "strerror_r did not return " "an error message", buflen); } } @@ -115,13 +145,12 @@ char *sstrerror (int errnum, char *buf, size_t buflen) #else if (strerror_r (errnum, buf, buflen) != 0) { - snprintf (buf, buflen, "Error #%i; " + ssnprintf (buf, buflen, "Error #%i; " "Additionally, strerror_r failed.", errnum); } #endif /* STRERROR_R_CHAR_P */ - buf[buflen - 1] = '\0'; return (buf); } /* char *sstrerror */ @@ -233,7 +262,7 @@ int strsplit (char *string, char **fields, size_t size) break; } - return (i); + return ((int) i); } int strjoin (char *dst, size_t dst_len, @@ -297,6 +326,40 @@ int strsubstitute (char *str, char c_from, char c_to) return (ret); } /* int strsubstitute */ +int strunescape (char *buf, size_t buf_len) +{ + size_t i; + + for (i = 0; (i < buf_len) && (buf[i] != '\0'); ++i) + { + if (buf[i] != '\\') + continue; + + if ((i >= buf_len) || (buf[i + 1] == '\0')) { + ERROR ("string unescape: backslash found at end of string."); + return (-1); + } + + switch (buf[i + 1]) { + case 't': + buf[i] = '\t'; + break; + case 'n': + buf[i] = '\n'; + break; + case 'r': + buf[i] = '\r'; + break; + default: + buf[i] = buf[i + 1]; + break; + } + + memmove (buf + i + 1, buf + i + 2, buf_len - i - 2); + } + return (0); +} /* int strunescape */ + int escape_slashes (char *buf, int buf_len) { int i; @@ -329,28 +392,69 @@ int escape_slashes (char *buf, int buf_len) return (0); } /* int escape_slashes */ -int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret) +void replace_special (char *buffer, size_t buffer_size) { - if ((tv0 == NULL) || (tv1 == NULL) || (ret == NULL)) - return (-2); + size_t i; - if ((tv0->tv_sec < tv1->tv_sec) - || ((tv0->tv_sec == tv1->tv_sec) && (tv0->tv_usec < tv1->tv_usec))) - return (-1); + for (i = 0; i < buffer_size; i++) + { + if (buffer[i] == 0) + return; + if ((!isalnum ((int) buffer[i])) && (buffer[i] != '-')) + buffer[i] = '_'; + } +} /* void replace_special */ + +int timeval_cmp (struct timeval tv0, struct timeval tv1, struct timeval *delta) +{ + struct timeval *larger; + struct timeval *smaller; - ret->tv_sec = tv0->tv_sec - tv1->tv_sec; - ret->tv_nsec = 1000 * ((long) (tv0->tv_usec - tv1->tv_usec)); + int status; - if (ret->tv_nsec < 0) + NORMALIZE_TIMEVAL (tv0); + NORMALIZE_TIMEVAL (tv1); + + if ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec == tv1.tv_usec)) { - assert (ret->tv_sec > 0); + if (delta != NULL) { + delta->tv_sec = 0; + delta->tv_usec = 0; + } + return (0); + } - ret->tv_nsec += 1000000000; - ret->tv_sec -= 1; + if ((tv0.tv_sec < tv1.tv_sec) + || ((tv0.tv_sec == tv1.tv_sec) && (tv0.tv_usec < tv1.tv_usec))) + { + larger = &tv1; + smaller = &tv0; + status = -1; + } + else + { + larger = &tv0; + smaller = &tv1; + status = 1; } - return (0); -} + if (delta != NULL) { + delta->tv_sec = larger->tv_sec - smaller->tv_sec; + + if (smaller->tv_usec <= larger->tv_usec) + delta->tv_usec = larger->tv_usec - smaller->tv_usec; + else + { + --delta->tv_sec; + delta->tv_usec = 1000000 + larger->tv_usec - smaller->tv_usec; + } + } + + assert ((delta == NULL) + || ((0 <= delta->tv_usec) && (delta->tv_usec < 1000000))); + + return (status); +} /* int timeval_cmp */ int check_create_dir (const char *file_orig) { @@ -365,7 +469,7 @@ int check_create_dir (const char *file_orig) char *saveptr; int last_is_file = 1; int path_is_absolute = 0; - int len; + size_t len; int i; /* @@ -376,7 +480,7 @@ int check_create_dir (const char *file_orig) if ((len = strlen (file_orig)) < 1) return (-1); - else if (len >= 512) + else if (len >= sizeof (file_copy)) return (-1); /* @@ -391,8 +495,7 @@ int check_create_dir (const char *file_orig) /* * Create a copy for `strtok_r' to destroy */ - strncpy (file_copy, file_orig, 512); - file_copy[511] = '\0'; + sstrncpy (file_copy, file_orig, sizeof (file_copy)); /* * Break into components. This will eat up several slashes in a row and @@ -422,7 +525,8 @@ int check_create_dir (const char *file_orig) */ if (fields[i][0] == '.') { - ERROR ("Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig); + ERROR ("Cowardly refusing to create a directory that " + "begins with a `.' (dot): `%s'", file_orig); return (-2); } @@ -437,32 +541,42 @@ int check_create_dir (const char *file_orig) return (-1); } - if (stat (dir, &statbuf) == -1) - { - if (errno == ENOENT) + while (42) { + if (stat (dir, &statbuf) == -1) { - if (mkdir (dir, 0755) == -1) + if (errno == ENOENT) { + if (mkdir (dir, 0755) == 0) + break; + + /* this might happen, if a different thread created + * the directory in the meantime + * => call stat() again to check for S_ISDIR() */ + if (EEXIST == errno) + continue; + char errbuf[1024]; ERROR ("check_create_dir: mkdir (%s): %s", dir, sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } + else + { + char errbuf[1024]; + ERROR ("check_create_dir: stat (%s): %s", dir, + sstrerror (errno, errbuf, + sizeof (errbuf))); + return (-1); + } } - else + else if (!S_ISDIR (statbuf.st_mode)) { - char errbuf[1024]; - ERROR ("stat (%s): %s", dir, - sstrerror (errno, errbuf, - sizeof (errbuf))); + ERROR ("check_create_dir: `%s' exists but is not " + "a directory!", dir); return (-1); } - } - else if (!S_ISDIR (statbuf.st_mode)) - { - ERROR ("stat (%s): Not a directory!", dir); - return (-1); + break; } } @@ -473,27 +587,26 @@ int check_create_dir (const char *file_orig) int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name) { char ident[128]; + + *ksp_ptr = NULL; if (kc == NULL) return (-1); - snprintf (ident, 128, "%s,%i,%s", module, instance, name); - ident[127] = '\0'; + ssnprintf (ident, sizeof (ident), "%s,%i,%s", module, instance, name); + *ksp_ptr = kstat_lookup (kc, module, instance, name); if (*ksp_ptr == NULL) { - if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL) - { - ERROR ("Cound not find kstat %s", ident); - return (-1); - } + ERROR ("get_kstat: Cound not find kstat %s", ident); + return (-1); + } - if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) - { - WARNING ("kstat %s has wrong type", ident); - *ksp_ptr = NULL; - return (-1); - } + if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) + { + ERROR ("get_kstat: kstat %s has wrong type", ident); + *ksp_ptr = NULL; + return (-1); } #ifdef assert @@ -503,13 +616,13 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name) if (kstat_read (kc, *ksp_ptr, NULL) == -1) { - WARNING ("kstat %s could not be read", ident); + ERROR ("get_kstat: kstat %s could not be read", ident); return (-1); } if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED) { - WARNING ("kstat %s has wrong type", ident); + ERROR ("get_kstat: kstat %s has wrong type", ident); return (-1); } @@ -663,21 +776,21 @@ int format_name (char *ret, int ret_len, if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0)) { if ((type_instance == NULL) || (strlen (type_instance) == 0)) - status = snprintf (ret, ret_len, "%s/%s/%s", + status = ssnprintf (ret, ret_len, "%s/%s/%s", hostname, plugin, type); else - status = snprintf (ret, ret_len, "%s/%s/%s-%s", + status = ssnprintf (ret, ret_len, "%s/%s/%s-%s", hostname, plugin, type, type_instance); } else { if ((type_instance == NULL) || (strlen (type_instance) == 0)) - status = snprintf (ret, ret_len, "%s/%s-%s/%s", + status = ssnprintf (ret, ret_len, "%s/%s-%s/%s", hostname, plugin, plugin_instance, type); else - status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s", + status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s", hostname, plugin, plugin_instance, type, type_instance); } @@ -733,6 +846,43 @@ int parse_identifier (char *str, char **ret_host, return (0); } /* int parse_identifier */ +int parse_value (const char *value, value_t *ret_value, int ds_type) +{ + char *endptr = NULL; + + switch (ds_type) + { + case DS_TYPE_COUNTER: + ret_value->counter = (counter_t) strtoull (value, &endptr, 0); + break; + + case DS_TYPE_GAUGE: + ret_value->gauge = (gauge_t) strtod (value, &endptr); + break; + + case DS_TYPE_DERIVE: + ret_value->counter = (derive_t) strtoll (value, &endptr, 0); + break; + + case DS_TYPE_ABSOLUTE: + ret_value->counter = (absolute_t) strtoull (value, &endptr, 0); + break; + + default: + ERROR ("parse_value: Invalid data source type: %i.", ds_type); + return -1; + } + + if (value == endptr) { + ERROR ("parse_value: Failed to parse string as number: %s.", value); + return -1; + } + else if ((NULL != endptr) && ('\0' != *endptr)) + WARNING ("parse_value: Ignoring trailing garbage after number: %s.", + endptr); + return 0; +} /* int parse_value */ + int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) { int i; @@ -748,7 +898,11 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) dummy = NULL; if (i >= vl->values_len) + { + /* Make sure i is invalid. */ + i = vl->values_len + 1; break; + } if (i == -1) { @@ -759,12 +913,10 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) } else { - if (strcmp ("U", ptr) == 0) + if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE)) vl->values[i].gauge = NAN; - else if (ds->ds[i].type == DS_TYPE_COUNTER) - vl->values[i].counter = atoll (ptr); - else if (ds->ds[i].type == DS_TYPE_GAUGE) - vl->values[i].gauge = atof (ptr); + else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type)) + return -1; } i++; @@ -838,26 +990,146 @@ int notification_init (notification_t *n, int severity, const char *message, n->severity = severity; if (message != NULL) - strncpy (n->message, message, sizeof (n->message)); + sstrncpy (n->message, message, sizeof (n->message)); if (host != NULL) - strncpy (n->host, host, sizeof (n->host)); + sstrncpy (n->host, host, sizeof (n->host)); if (plugin != NULL) - strncpy (n->plugin, plugin, sizeof (n->plugin)); + sstrncpy (n->plugin, plugin, sizeof (n->plugin)); if (plugin_instance != NULL) - strncpy (n->plugin_instance, plugin_instance, + sstrncpy (n->plugin_instance, plugin_instance, sizeof (n->plugin_instance)); if (type != NULL) - strncpy (n->type, type, sizeof (n->type)); + sstrncpy (n->type, type, sizeof (n->type)); if (type_instance != NULL) - strncpy (n->type_instance, type_instance, + sstrncpy (n->type_instance, type_instance, sizeof (n->type_instance)); - n->message[sizeof (n->message) - 1] = '\0'; - n->host[sizeof (n->host) - 1] = '\0'; - n->plugin[sizeof (n->plugin) - 1] = '\0'; - n->plugin_instance[sizeof (n->plugin_instance) - 1] = '\0'; - n->type[sizeof (n->type) - 1] = '\0'; - n->type_instance[sizeof (n->type_instance) - 1] = '\0'; - return (0); } /* int notification_init */ + +int walk_directory (const char *dir, dirwalk_callback_f callback, + void *user_data) +{ + struct dirent *ent; + DIR *dh; + int success; + int failure; + + success = 0; + failure = 0; + + if ((dh = opendir (dir)) == NULL) + { + char errbuf[1024]; + ERROR ("walk_directory: Cannot open '%s': %s", dir, + sstrerror (errno, errbuf, sizeof (errbuf))); + return -1; + } + + while ((ent = readdir (dh)) != NULL) + { + int status; + + if (ent->d_name[0] == '.') + continue; + + status = (*callback) (dir, ent->d_name, user_data); + if (status != 0) + failure++; + else + success++; + } + + closedir (dh); + + if ((success == 0) && (failure > 0)) + return (-1); + return (0); +} + +int read_file_contents (const char *filename, char *buf, int bufsize) +{ + FILE *fh; + int n; + + if ((fh = fopen (filename, "r")) == NULL) + return -1; + + n = fread(buf, 1, bufsize, fh); + fclose(fh); + + return n; +} + +counter_t counter_diff (counter_t old_value, counter_t new_value) +{ + counter_t diff; + + if (old_value > new_value) + { + if (old_value <= 4294967295U) + diff = (4294967295U - old_value) + new_value; + else + diff = (18446744073709551615ULL - old_value) + + new_value; + } + else + { + diff = new_value - old_value; + } + + return (diff); +} /* counter_t counter_to_gauge */ + +int service_name_to_port_number (const char *service_name) +{ + struct addrinfo *ai_list; + struct addrinfo *ai_ptr; + struct addrinfo ai_hints; + int status; + int service_number; + + if (service_name == NULL) + return (-1); + + ai_list = NULL; + memset (&ai_hints, 0, sizeof (ai_hints)); + ai_hints.ai_family = AF_UNSPEC; + + status = getaddrinfo (/* node = */ NULL, service_name, + &ai_hints, &ai_list); + if (status != 0) + { + ERROR ("service_name_to_port_number: getaddrinfo failed: %s", + gai_strerror (status)); + return (-1); + } + + service_number = -1; + for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) + { + if (ai_ptr->ai_family == AF_INET) + { + struct sockaddr_in *sa; + + sa = (void *) ai_ptr->ai_addr; + service_number = (int) ntohs (sa->sin_port); + } + else if (ai_ptr->ai_family == AF_INET6) + { + struct sockaddr_in6 *sa; + + sa = (void *) ai_ptr->ai_addr; + service_number = (int) ntohs (sa->sin6_port); + } + + if ((service_number > 0) && (service_number <= 65535)) + break; + } + + freeaddrinfo (ai_list); + + if ((service_number > 0) && (service_number <= 65535)) + return (service_number); + return (-1); +} /* int service_name_to_port_number */