include netinet/in.h for sockaddr_in on FreeBSD
[collectd.git] / src / common.c
index 7cc1d9d..c6a651d 100644 (file)
@@ -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 <octo at verplant.org>
  *   Niki W. Waibel <niki.waibel@gmx.net>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Michał Mirosław <mirq-linux at rere.qmqm.pl>
 **/
 
 #if HAVE_CONFIG_H
 # include <arpa/inet.h>
 #endif
 
+/* for getaddrinfo */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
@@ -251,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,
@@ -315,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;
@@ -347,6 +392,19 @@ int escape_slashes (char *buf, int buf_len)
        return (0);
 } /* int escape_slashes */
 
+void replace_special (char *buffer, size_t buffer_size)
+{
+       size_t i;
+
+       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;
@@ -411,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;
 
        /*
@@ -467,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);
                }
 
@@ -482,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;
                }
        }
 
@@ -518,26 +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);
 
        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
@@ -547,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);
        }
 
@@ -777,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;
@@ -792,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)
                {
@@ -803,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++;
@@ -972,3 +1080,56 @@ counter_t counter_diff (counter_t old_value, counter_t new_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 */