unixsock plugin et alii: Allow passing of arbitrary identifiers to the FLUSH command.
authorFlorian Forster <octo@noris.net>
Thu, 21 Aug 2008 07:26:21 +0000 (09:26 +0200)
committerFlorian Forster <octo@noris.net>
Thu, 21 Aug 2008 07:26:21 +0000 (09:26 +0200)
Due to a bug in the parsing of the FLUSH command, identifiers which
contained spaces would not be accepted. This patch introduces
src/utils_parse_option.[ch], which strips off an option in the form of
name="value" from the beginning of a buffer (removing leading and
trailing spaces) and returns the values found.

The Collectd::Unixsock module is updated to automatically quote
identifiers when necessary.

Many other commands may be effected by this bug, too. They'll be fixed
soon.

bindings/perl/Collectd/Unixsock.pm
src/Makefile.am
src/unixsock.c
src/utils_cmd_flush.c
src/utils_cmd_flush.h
src/utils_parse_option.c [new file with mode: 0644]
src/utils_parse_option.h [new file with mode: 0644]

index af274a5..da144e6 100644 (file)
@@ -471,6 +471,12 @@ sub flush
                        {
                                return;
                        }
+                       if ($ident_str =~ m/ /)
+                       {
+                               $ident_str =~ s#\\#\\\\#g;
+                               $ident_str =~ s#"#\\"#g;
+                               $ident_str = "\"$ident_str\"";
+                       }
 
                        $msg .= " identifier=$ident_str";
                }
index f0ad3cb..acc5305 100644 (file)
@@ -710,6 +710,7 @@ endif
 if BUILD_PLUGIN_UNIXSOCK
 pkglib_LTLIBRARIES += unixsock.la
 unixsock_la_SOURCES = unixsock.c \
+                     utils_parse_option.h utils_parse_option.c \
                      utils_cmd_flush.h utils_cmd_flush.c \
                      utils_cmd_getval.h utils_cmd_getval.c \
                      utils_cmd_listval.h utils_cmd_listval.c \
index d34b91f..c2e1f30 100644 (file)
@@ -159,9 +159,6 @@ static void *us_handle_client (void *arg)
 {
        int fd;
        FILE *fhin, *fhout;
-       char buffer[1024];
-       char *fields[128];
-       int   fields_num;
 
        fd = *((int *) arg);
        free (arg);
@@ -202,7 +199,11 @@ static void *us_handle_client (void *arg)
 
        while (42)
        {
-               int len;
+               char buffer[1024];
+               char buffer_copy[1024];
+               char *fields[128];
+               int   fields_num;
+               int   len;
 
                errno = 0;
                if (fgets (buffer, sizeof (buffer), fhin) == NULL)
@@ -225,9 +226,9 @@ static void *us_handle_client (void *arg)
                if (len == 0)
                        continue;
 
-               DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
+               sstrncpy (buffer_copy, buffer, sizeof (buffer_copy));
 
-               fields_num = strsplit (buffer, fields,
+               fields_num = strsplit (buffer_copy, fields,
                                sizeof (fields) / sizeof (fields[0]));
 
                if (fields_num < 1)
@@ -254,7 +255,7 @@ static void *us_handle_client (void *arg)
                }
                else if (strcasecmp (fields[0], "flush") == 0)
                {
-                       handle_flush (fhout, fields, fields_num);
+                       handle_flush (fhout, buffer);
                }
                else
                {
index 7feaac2..74807b8 100644 (file)
@@ -24,6 +24,7 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "utils_parse_option.h"
 
 #define print_to_socket(fh, ...) \
        if (fprintf (fh, __VA_ARGS__) < 0) { \
@@ -48,7 +49,7 @@ static int add_to_array (char ***array, int *array_num, char *value)
        return (0);
 } /* int add_to_array */
 
-int handle_flush (FILE *fh, char **fields, int fields_num)
+int handle_flush (FILE *fh, char *buffer)
 {
        int success = 0;
        int error   = 0;
@@ -61,47 +62,71 @@ int handle_flush (FILE *fh, char **fields, int fields_num)
 
        int i;
 
-       for (i = 1; i < fields_num; i++)
+       if ((fh == NULL) || (buffer == NULL))
+               return (-1);
+
+       DEBUG ("utils_cmd_flush: handle_flush (fh = %p, buffer = %s);",
+                       (void *) fh, buffer);
+
+       if (strncasecmp ("FLUSH", buffer, strlen ("FLUSH")) != 0)
        {
-               char *option = fields[i];
-               int   status = 0;
+               print_to_socket (fh, "-1 Cannot parse command.\n");
+               return (-1);
+       }
+       buffer += strlen ("FLUSH");
 
-               if (strncasecmp ("plugin=", option, strlen ("plugin=")) == 0)
+       while (*buffer != 0)
+       {
+               char *opt_key;
+               char *opt_value;
+               int status;
+
+               opt_key = NULL;
+               opt_value = NULL;
+               status = parse_option (&buffer, &opt_key, &opt_value);
+               if (status != 0)
                {
-                       char *plugin;
-                       
-                       plugin = option + strlen ("plugin=");
-                       add_to_array (&plugins, &plugins_num, plugin);
+                       print_to_socket (fh, "-1 Parsing options failed.\n");
+                       sfree (plugins);
+                       sfree (identifiers);
+                       return (-1);
                }
-               else if (strncasecmp ("identifier=", option, strlen ("identifier=")) == 0)
-               {
-                       char *identifier;
 
-                       identifier = option + strlen ("identifier=");
-                       add_to_array (&identifiers, &identifiers_num, identifier);
+               if (strcasecmp ("plugin", opt_key) == 0)
+               {
+                       add_to_array (&plugins, &plugins_num, opt_value);
                }
-               else if (strncasecmp ("timeout=", option, strlen ("timeout=")) == 0)
+               else if (strcasecmp ("identifier", opt_key) == 0)
                {
-                       char *endptr = NULL;
-                       char *value  = option + strlen ("timeout=");
-
+                       add_to_array (&identifiers, &identifiers_num, opt_value);
+               }
+               else if (strcasecmp ("timeout", opt_key) == 0)
+               {
+                       char *endptr;
+                       
                        errno = 0;
-                       timeout = strtol (value, &endptr, 0);
-
-                       if ((endptr == value) || (0 != errno))
-                               status = -1;
-                       else if (0 >= timeout)
+                       endptr = NULL;
+                       timeout = strtol (opt_value, &endptr, 0);
+
+                       if ((endptr == opt_value) || (errno != 0))
+                       {
+                               print_to_socket (fh, "-1 Invalid value for option `timeout': "
+                                               "%s\n", opt_value);
+                               sfree (plugins);
+                               sfree (identifiers);
+                               return (-1);
+                       }
+                       else if (timeout <= 0)
                                timeout = -1;
                }
                else
-                       status = -1;
-
-               if (status != 0)
                {
-                       print_to_socket (fh, "-1 Cannot parse option %s\n", option);
+                       print_to_socket (fh, "-1 Cannot parse option %s\n", opt_key);
+                       sfree (plugins);
+                       sfree (identifiers);
                        return (-1);
                }
-       }
+       } /* while (*buffer != 0) */
 
        /* Add NULL entries for `any plugin' and/or `any value' if nothing was
         * specified. */
@@ -143,6 +168,8 @@ int handle_flush (FILE *fh, char **fields, int fields_num)
                print_to_socket (fh, "0 Done\n");
        }
 
+       sfree (plugins);
+       sfree (identifiers);
        return (0);
 } /* int handle_flush */
 
index 334f086..dccafd1 100644 (file)
@@ -22,7 +22,7 @@
 #ifndef UTILS_CMD_FLUSH_H
 #define UTILS_CMD_FLUSH_H 1
 
-int handle_flush (FILE *fh, char **fields, int fields_num);
+int handle_flush (FILE *fh, char *buffer);
 
 #endif /* UTILS_CMD_FLUSH_H */
 
diff --git a/src/utils_parse_option.c b/src/utils_parse_option.c
new file mode 100644 (file)
index 0000000..06e9f28
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ * collectd - src/utils_parse_option.c
+ * Copyright (C) 2008  Florian 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Author:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_parse_option.h"
+
+/*
+ * parse_option
+ * ------------
+ *  Parses an ``option'' as used with the unixsock and exec commands. An
+ *  option is of the form:
+ *    name0="value"
+ *    name1="value with \"quotes\""
+ *    name2="value \\ backslash"
+ *  However, if the value does *not* contain a space character, you can skip
+ *  the quotes.
+ */
+int parse_option (char **ret_buffer, char **ret_key, char **ret_value)
+{
+  char *buffer;
+  char *key;
+  char *value;
+
+  buffer = *ret_buffer;
+
+  /* Eat up leading spaces */
+  key = buffer;
+  while (isspace ((int) *key))
+    key++;
+  if (*key == 0)
+    return (1);
+
+  /* Look for the equal sign */
+  value = key;
+  while (isalnum ((int) *value))
+    value++;
+  if ((*value != '=') || (value == key))
+    return (1);
+  *value = 0;
+  value++;
+  /* Empty values must be written as "" */
+  if (isspace ((int) *value) || (*value == 0))
+    return (-1);
+
+  /* A quoted value */
+  if (*value == '"')
+  {
+    char *dst;
+
+    value++;
+    if (*value == 0)
+      return (-1);
+
+    dst = value;
+    buffer = value;
+    while ((*buffer != '"') && (*buffer != 0))
+    {
+      /* Un-escape backslashes */
+      if (*buffer == '\\')
+      {
+        buffer++;
+        /* Catch a backslash at the end of buffer */
+        if (*buffer == 0)
+          return (-1);
+      }
+      *dst = *buffer;
+      buffer++;
+      dst++;
+    }
+    /* No quote sign has been found */
+    if (*buffer == 0)
+      return (-1);
+    *buffer = 0;
+    buffer++;
+
+    /* Check for trailing spaces. */
+    if ((*buffer != 0) && !isspace ((int) *buffer))
+      return (-1);
+  }
+  else /* an unquoted value */
+  {
+    buffer = value;
+    while (!isspace ((int) *buffer))
+      buffer++;
+    if (*buffer != 0)
+    {
+      *buffer = 0;
+      buffer++;
+    }
+  }
+
+  /* Eat up trailing spaces */
+  while (isspace ((int) *buffer))
+    buffer++;
+
+  *ret_buffer = buffer;
+  *ret_key = key;
+  *ret_value = value;
+
+  return (0);
+} /* int parse_option */
+
+/* vim: set sw=2 ts=8 tw=78 et : */
diff --git a/src/utils_parse_option.h b/src/utils_parse_option.h
new file mode 100644 (file)
index 0000000..1cd91de
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ * collectd - src/utils_parse_option.h
+ * Copyright (C) 2008  Florian 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.
+ *
+ * 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.
+ *
+ * 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
+ *
+ * Author:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_PARSE_OPTION
+#define UTILS_PARSE_OPTION 1
+
+int parse_option (char **ret_buffer, char **ret_key, char **ret_value);
+
+#endif /* UTILS_PARSE_OPTION */
+
+/* vim: set sw=2 ts=8 tw=78 et : */