From 0f77b589fdee440b959887673c3cd6ed244c215a Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Thu, 21 Aug 2008 09:26:21 +0200 Subject: [PATCH] unixsock plugin et alii: Allow passing of arbitrary identifiers to the FLUSH command. 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 | 6 ++ src/Makefile.am | 1 + src/unixsock.c | 15 ++-- src/utils_cmd_flush.c | 83 ++++++++++++------- src/utils_cmd_flush.h | 2 +- src/utils_parse_option.c | 123 +++++++++++++++++++++++++++++ src/utils_parse_option.h | 29 +++++++ 7 files changed, 223 insertions(+), 36 deletions(-) create mode 100644 src/utils_parse_option.c create mode 100644 src/utils_parse_option.h diff --git a/bindings/perl/Collectd/Unixsock.pm b/bindings/perl/Collectd/Unixsock.pm index af274a58..da144e6e 100644 --- a/bindings/perl/Collectd/Unixsock.pm +++ b/bindings/perl/Collectd/Unixsock.pm @@ -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"; } diff --git a/src/Makefile.am b/src/Makefile.am index f0ad3cb4..acc53054 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/unixsock.c b/src/unixsock.c index d34b91fa..c2e1f302 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -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 { diff --git a/src/utils_cmd_flush.c b/src/utils_cmd_flush.c index 7feaac28..74807b84 100644 --- a/src/utils_cmd_flush.c +++ b/src/utils_cmd_flush.c @@ -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 */ diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h index 334f0862..dccafd15 100644 --- a/src/utils_cmd_flush.h +++ b/src/utils_cmd_flush.h @@ -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 index 00000000..06e9f280 --- /dev/null +++ b/src/utils_parse_option.c @@ -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 + **/ + +#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 index 00000000..1cd91dee --- /dev/null +++ b/src/utils_parse_option.h @@ -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 + **/ + +#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 : */ -- 2.30.2