{GPL, other}: Relicense to MIT license.
[collectd.git] / src / common.c
index 0069a8b..93c1ca1 100644 (file)
@@ -1,19 +1,24 @@
 /**
  * collectd - src/common.c
- * Copyright (C) 2005-2010  Florian octo Forster
+ * Copyright (C) 2005-2014  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
- * Free Software Foundation; only version 2 of the License is applicable.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * Authors:
  *   Florian octo Forster <octo at collectd.org>
@@ -86,6 +91,47 @@ int ssnprintf (char *dest, size_t n, const char *format, ...)
        return (ret);
 } /* int ssnprintf */
 
+char *ssnprintf_alloc (char const *format, ...) /* {{{ */
+{
+       char static_buffer[1024] = "";
+       char *alloc_buffer;
+       size_t alloc_buffer_size;
+       int status;
+       va_list ap;
+
+       /* Try printing into the static buffer. In many cases it will be
+        * sufficiently large and we can simply return a strdup() of this
+        * buffer. */
+       va_start (ap, format);
+       status = vsnprintf (static_buffer, sizeof (static_buffer), format, ap);
+       va_end (ap);
+       if (status < 0)
+               return (NULL);
+
+       /* "status" does not include the null byte. */
+       alloc_buffer_size = (size_t) (status + 1);
+       if (alloc_buffer_size <= sizeof (static_buffer))
+               return (strdup (static_buffer));
+
+       /* Allocate a buffer large enough to hold the string. */
+       alloc_buffer = malloc (alloc_buffer_size);
+       if (alloc_buffer == NULL)
+               return (NULL);
+       memset (alloc_buffer, 0, alloc_buffer_size);
+
+       /* Print again into this new buffer. */
+       va_start (ap, format);
+       status = vsnprintf (alloc_buffer, alloc_buffer_size, format, ap);
+       va_end (ap);
+       if (status < 0)
+       {
+               sfree (alloc_buffer);
+               return (NULL);
+       }
+
+       return (alloc_buffer);
+} /* }}} char *ssnprintf_alloc */
+
 char *sstrdup (const char *s)
 {
        char *r;
@@ -336,8 +382,10 @@ int strunescape (char *buf, size_t buf_len)
                if (buf[i] != '\\')
                        continue;
 
-               if ((i >= buf_len) || (buf[i + 1] == '\0')) {
+               if (((i + 1) >= buf_len) || (buf[i + 1] == 0)) {
                        ERROR ("string unescape: backslash found at end of string.");
+                       /* Ensure null-byte at the end of the buffer. */
+                       buf[i] = 0;
                        return (-1);
                }
 
@@ -356,39 +404,60 @@ int strunescape (char *buf, size_t buf_len)
                                break;
                }
 
+               /* Move everything after the position one position to the left.
+                * Add a null-byte as last character in the buffer. */
                memmove (buf + i + 1, buf + i + 2, buf_len - i - 2);
+               buf[buf_len - 1] = 0;
        }
        return (0);
 } /* int strunescape */
 
-int escape_slashes (char *buf, int buf_len)
+size_t strstripnewline (char *buffer)
 {
-       int i;
+       size_t buffer_len = strlen (buffer);
 
-       if (strcmp (buf, "/") == 0)
+       while (buffer_len > 0)
        {
-               if (buf_len < 5)
-                       return (-1);
-
-               strncpy (buf, "root", buf_len);
-               return (0);
+               if ((buffer[buffer_len - 1] != '\n')
+                               && (buffer[buffer_len - 1] != '\r'))
+                       break;
+               buffer[buffer_len] = 0;
+               buffer_len--;
        }
 
-       if (buf_len <= 1)
+       return (buffer_len);
+} /* size_t strstripnewline */
+
+int escape_slashes (char *buffer, size_t buffer_size)
+{
+       int i;
+       size_t buffer_len;
+
+       buffer_len = strlen (buffer);
+
+       if (buffer_len <= 1)
+       {
+               if (strcmp ("/", buffer) == 0)
+               {
+                       if (buffer_size < 5)
+                               return (-1);
+                       sstrncpy (buffer, "root", buffer_size);
+               }
                return (0);
+       }
 
        /* Move one to the left */
-       if (buf[0] == '/')
-               memmove (buf, buf + 1, buf_len - 1);
+       if (buffer[0] == '/')
+       {
+               memmove (buffer, buffer + 1, buffer_len);
+               buffer_len--;
+       }
 
-       for (i = 0; i < buf_len - 1; i++)
+       for (i = 0; i < buffer_len - 1; i++)
        {
-               if (buf[i] == '\0')
-                       break;
-               else if (buf[i] == '/')
-                       buf[i] = '_';
+               if (buffer[i] == '/')
+                       buffer[i] = '_';
        }
-       buf[i] = '\0';
 
        return (0);
 } /* int escape_slashes */
@@ -548,7 +617,7 @@ int check_create_dir (const char *file_orig)
                        {
                                if (errno == ENOENT)
                                {
-                                       if (mkdir (dir, 0755) == 0)
+                                       if (mkdir (dir, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
                                                break;
 
                                        /* this might happen, if a different thread created
@@ -591,7 +660,7 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
        char ident[128];
 
        *ksp_ptr = NULL;
-       
+
        if (kc == NULL)
                return (-1);
 
@@ -636,24 +705,23 @@ long long get_kstat_value (kstat_t *ksp, char *name)
        kstat_named_t *kn;
        long long retval = -1LL;
 
-#ifdef assert
-       assert (ksp != NULL);
-       assert (ksp->ks_type == KSTAT_TYPE_NAMED);
-#else
        if (ksp == NULL)
        {
-               ERROR ("ERROR: %s:%i: ksp == NULL\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp is NULL.", name);
                return (-1LL);
        }
        else if (ksp->ks_type != KSTAT_TYPE_NAMED)
        {
-               ERROR ("ERROR: %s:%i: ksp->ks_type != KSTAT_TYPE_NAMED\n", __FILE__, __LINE__);
+               ERROR ("get_kstat_value (\"%s\"): ksp->ks_type (%#x) "
+                               "is not KSTAT_TYPE_NAMED (%#x).",
+                               name,
+                               (unsigned int) ksp->ks_type,
+                               (unsigned int) KSTAT_TYPE_NAMED);
                return (-1LL);
        }
-#endif
 
        if ((kn = (kstat_named_t *) kstat_data_lookup (ksp, name)) == NULL)
-               return (retval);
+               return (-1LL);
 
        if (kn->data_type == KSTAT_DATA_INT32)
                retval = (long long) kn->value.i32;
@@ -665,7 +733,7 @@ long long get_kstat_value (kstat_t *ksp, char *name)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
                WARNING ("get_kstat_value: Not a numeric value: %s", name);
-                
+
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
@@ -772,36 +840,43 @@ int format_name (char *ret, int ret_len,
                const char *plugin, const char *plugin_instance,
                const char *type, const char *type_instance)
 {
-       int  status;
+  char *buffer;
+  size_t buffer_size;
+
+  buffer = ret;
+  buffer_size = (size_t) ret_len;
+
+#define APPEND(str) do {                                               \
+  size_t l = strlen (str);                                             \
+  if (l >= buffer_size)                                                \
+    return (ENOBUFS);                                                  \
+  memcpy (buffer, (str), l);                                           \
+  buffer += l; buffer_size -= l;                                       \
+} while (0)
 
-       assert (plugin != NULL);
-       assert (type != NULL);
+  assert (plugin != NULL);
+  assert (type != NULL);
 
-       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s",
-                                       hostname, plugin, type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s/%s-%s",
-                                       hostname, plugin, type,
-                                       type_instance);
-       }
-       else
-       {
-               if ((type_instance == NULL) || (strlen (type_instance) == 0))
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s",
-                                       hostname, plugin, plugin_instance,
-                                       type);
-               else
-                       status = ssnprintf (ret, ret_len, "%s/%s-%s/%s-%s",
-                                       hostname, plugin, plugin_instance,
-                                       type, type_instance);
-       }
+  APPEND (hostname);
+  APPEND ("/");
+  APPEND (plugin);
+  if ((plugin_instance != NULL) && (plugin_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (plugin_instance);
+  }
+  APPEND ("/");
+  APPEND (type);
+  if ((type_instance != NULL) && (type_instance[0] != 0))
+  {
+    APPEND ("-");
+    APPEND (type_instance);
+  }
+  assert (buffer_size > 0);
+  buffer[0] = 0;
 
-       if ((status < 1) || (status >= ret_len))
-               return (-1);
-       return (0);
+#undef APPEND
+  return (0);
 } /* int format_name */
 
 int format_values (char *ret, size_t ret_len, /* {{{ */
@@ -953,9 +1028,25 @@ int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */
        return (0);
 } /* }}} int parse_identifier_vl */
 
-int parse_value (const char *value, value_t *ret_value, int ds_type)
+int parse_value (const char *value_orig, value_t *ret_value, int ds_type)
 {
+  char *value;
   char *endptr = NULL;
+  size_t value_len;
+
+  if (value_orig == NULL)
+    return (EINVAL);
+
+  value = strdup (value_orig);
+  if (value == NULL)
+    return (ENOMEM);
+  value_len = strlen (value);
+
+  while ((value_len > 0) && isspace ((int) value[value_len - 1]))
+  {
+    value[value_len - 1] = 0;
+    value_len--;
+  }
 
   switch (ds_type)
   {
@@ -976,11 +1067,13 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
       break;
 
     default:
+      sfree (value);
       ERROR ("parse_value: Invalid data source type: %i.", ds_type);
       return -1;
   }
 
   if (value == endptr) {
+    sfree (value);
     ERROR ("parse_value: Failed to parse string as %s: %s.",
         DS_TYPE_TO_STRING (ds_type), value);
     return -1;
@@ -988,8 +1081,9 @@ int parse_value (const char *value, value_t *ret_value, int ds_type)
   else if ((NULL != endptr) && ('\0' != *endptr))
     INFO ("parse_value: Ignoring trailing garbage \"%s\" after %s value. "
         "Input string was \"%s\".",
-        endptr, DS_TYPE_TO_STRING (ds_type), value);
+        endptr, DS_TYPE_TO_STRING (ds_type), value_orig);
 
+  sfree (value);
   return 0;
 } /* int parse_value */
 
@@ -1152,7 +1246,7 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        while ((ent = readdir (dh)) != NULL)
        {
                int status;
-               
+
                if (include_hidden)
                {
                        if ((strcmp (".", ent->d_name) == 0)
@@ -1179,18 +1273,25 @@ int walk_directory (const char *dir, dirwalk_callback_f callback,
        return (0);
 }
 
-int read_file_contents (const char *filename, char *buf, int bufsize)
+ssize_t read_file_contents (const char *filename, char *buf, size_t bufsize)
 {
        FILE *fh;
-       int n;
+       ssize_t ret;
 
-       if ((fh = fopen (filename, "r")) == NULL)
-               return -1;
+       fh = fopen (filename, "r");
+       if (fh == NULL)
+               return (-1);
 
-       n = fread(buf, 1, bufsize, fh);
-       fclose(fh);
+       ret = (ssize_t) fread (buf, 1, bufsize, fh);
+       if ((ret == 0) && (ferror (fh) != 0))
+       {
+               ERROR ("read_file_contents: Reading file \"%s\" failed.",
+                               filename);
+               ret = -1;
+       }
 
-       return n;
+       fclose(fh);
+       return (ret);
 }
 
 counter_t counter_diff (counter_t old_value, counter_t new_value)
@@ -1211,7 +1312,165 @@ counter_t counter_diff (counter_t old_value, counter_t new_value)
        }
 
        return (diff);
-} /* counter_t counter_to_gauge */
+} /* counter_t counter_diff */
+
+int rate_to_value (value_t *ret_value, gauge_t rate, /* {{{ */
+               rate_to_value_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       gauge_t delta_gauge;
+       cdtime_t delta_t;
+
+       if (ds_type == DS_TYPE_GAUGE)
+       {
+               state->last_value.gauge = rate;
+               state->last_time = t;
+
+               *ret_value = state->last_value;
+               return (0);
+       }
+
+       /* Counter and absolute can't handle negative rates. Reset "last time"
+        * to zero, so that the next valid rate will re-initialize the
+        * structure. */
+       if ((rate < 0.0)
+                       && ((ds_type == DS_TYPE_COUNTER)
+                               || (ds_type == DS_TYPE_ABSOLUTE)))
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       delta_t = t - state->last_time;
+       delta_gauge = (rate * CDTIME_T_TO_DOUBLE (delta_t)) + state->residual;
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = (derive_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.derive);
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.counter);
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) rate;
+                       state->residual = rate - ((gauge_t) state->last_value.absolute);
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               derive_t delta_derive = (derive_t) delta_gauge;
+
+               state->last_value.derive += delta_derive;
+               state->residual = delta_gauge - ((gauge_t) delta_derive);
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               counter_t delta_counter = (counter_t) delta_gauge;
+
+               state->last_value.counter += delta_counter;
+               state->residual = delta_gauge - ((gauge_t) delta_counter);
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               absolute_t delta_absolute = (absolute_t) delta_gauge;
+
+               state->last_value.absolute = delta_absolute;
+               state->residual = delta_gauge - ((gauge_t) delta_absolute);
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       *ret_value = state->last_value;
+       return (0);
+} /* }}} value_t rate_to_value */
+
+int value_to_rate (value_t *ret_rate, derive_t value, /* {{{ */
+               value_to_rate_state_t *state,
+               int ds_type, cdtime_t t)
+{
+       double interval;
+
+       /* Another invalid state: The time is not increasing. */
+       if (t <= state->last_time)
+       {
+               memset (state, 0, sizeof (*state));
+               return (EINVAL);
+       }
+
+       interval = CDTIME_T_TO_DOUBLE(t - state->last_time);
+
+       /* Previous value is invalid. */
+       if (state->last_time == 0) /* {{{ */
+       {
+               if (ds_type == DS_TYPE_DERIVE)
+               {
+                       state->last_value.derive = value;
+               }
+               else if (ds_type == DS_TYPE_COUNTER)
+               {
+                       state->last_value.counter = (counter_t) value;
+               }
+               else if (ds_type == DS_TYPE_ABSOLUTE)
+               {
+                       state->last_value.absolute = (absolute_t) value;
+               }
+               else
+               {
+                       assert (23 == 42);
+               }
+
+               state->last_time = t;
+               return (EAGAIN);
+       } /* }}} */
+
+       if (ds_type == DS_TYPE_DERIVE)
+       {
+               ret_rate->gauge = (value - state->last_value.derive) / interval;
+               state->last_value.derive = value;
+       }
+       else if (ds_type == DS_TYPE_COUNTER)
+       {
+               ret_rate->gauge = (((counter_t)value) - state->last_value.counter) / interval;
+               state->last_value.counter = (counter_t) value;
+       }
+       else if (ds_type == DS_TYPE_ABSOLUTE)
+       {
+               ret_rate->gauge = (((absolute_t)value) - state->last_value.absolute) / interval;
+               state->last_value.absolute = (absolute_t) value;
+       }
+       else
+       {
+               assert (23 == 42);
+       }
+
+        state->last_time = t;
+       return (0);
+} /* }}} value_t rate_to_value */
 
 int service_name_to_port_number (const char *service_name)
 {
@@ -1283,3 +1542,35 @@ int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
        *ret_value = tmp;
        return (0);
 } /* }}} int strtoderive */
+
+int strarray_add (char ***ret_array, size_t *ret_array_len, char const *str) /* {{{ */
+{
+       char **array;
+       size_t array_len = *ret_array_len;
+
+       if (str == NULL)
+               return (EINVAL);
+
+       array = realloc (*ret_array,
+            (array_len + 1) * sizeof (*array));
+       if (array == NULL)
+               return (ENOMEM);
+       *ret_array = array;
+
+       array[array_len] = strdup (str);
+       if (array[array_len] == NULL)
+               return (ENOMEM);
+
+       array_len++;
+        *ret_array_len = array_len;
+       return (0);
+} /* }}} int strarray_add */
+
+void strarray_free (char **array, size_t array_len) /* {{{ */
+{
+       size_t i;
+
+       for (i = 0; i < array_len; i++)
+               sfree (array[i]);
+       sfree (array);
+} /* }}} void strarray_free */