X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Futils_cmds.c;h=9ff9d4d6b1828e2ee2fc76d9cbfeb4a76ffdc114;hb=6e6c1eecf05ac8bc9e59b0967cb87e578253d5a0;hp=8468267e41cba673a92c205b7eda8fd167e98035;hpb=3090a8852788aaca0d8063fdf9ac4ba66f048cd8;p=collectd.git diff --git a/src/utils_cmds.c b/src/utils_cmds.c index 8468267e..9ff9d4d6 100644 --- a/src/utils_cmds.c +++ b/src/utils_cmds.c @@ -1,5 +1,6 @@ /** * collectd - src/utils_cmds.c + * Copyright (C) 2008 Florian Forster * Copyright (C) 2016 Sebastian 'tokkee' Harl * * Permission is hereby granted, free of charge, to any person obtaining a @@ -21,10 +22,14 @@ * DEALINGS IN THE SOFTWARE. * * Authors: + * Florian octo Forster * Sebastian 'tokkee' Harl **/ #include "utils_cmds.h" +#include "utils_cmd_flush.h" +#include "utils_cmd_getval.h" +#include "utils_cmd_listval.h" #include "utils_cmd_putval.h" #include "utils_parse_option.h" #include "daemon/common.h" @@ -33,6 +38,147 @@ #include /* + * private helper functions + */ + +static cmd_status_t cmd_split (char *buffer, + size_t *ret_len, char ***ret_fields, + cmd_error_handler_t *err) +{ + char *string, *field; + bool in_field, in_quotes; + + size_t estimate, len; + char **fields; + + estimate = 0; + in_field = false; + for (string = buffer; *string != '\0'; ++string) + { + /* Make a quick worst-case estimate of the number of fields by + * counting spaces and ignoring quotation marks. */ + if (!isspace ((int)*string)) + { + if (!in_field) + { + estimate++; + in_field = true; + } + } + else + { + in_field = false; + } + } + + /* fields will be NULL-terminated */ + fields = malloc ((estimate + 1) * sizeof (*fields)); + if (fields == NULL) { + cmd_error (CMD_ERROR, err, "malloc failed."); + return (CMD_ERROR); + } + +#define END_FIELD() \ + do { \ + *field = '\0'; \ + field = NULL; \ + in_field = false; \ + } while (0) +#define NEW_FIELD() \ + do { \ + field = string; \ + in_field = true; \ + assert (len < estimate); \ + fields[len] = field; \ + field++; \ + len++; \ + } while (0) + + len = 0; + field = NULL; + in_field = false; + in_quotes = false; + for (string = buffer; *string != '\0'; string++) + { + if (isspace ((int)string[0])) + { + if (! in_quotes) + { + if (in_field) + END_FIELD (); + + /* skip space */ + continue; + } + } + else if (string[0] == '"') + { + /* Note: Two consecutive quoted fields not separated by space are + * treated as different fields. This is the collectd 5.x behavior + * around splitting fields. */ + + if (in_quotes) + { + /* end of quoted field */ + if (! in_field) /* empty quoted string */ + NEW_FIELD (); + END_FIELD (); + in_quotes = false; + continue; + } + + in_quotes = true; + /* if (! in_field): add new field on next iteration + * else: quoted string following an unquoted string (one field) + * in either case: skip quotation mark */ + continue; + } + else if ((string[0] == '\\') && in_quotes) + { + /* Outside of quotes, a backslash is a regular character (mostly + * for backward compatibility). */ + + if (string[1] == '\0') + { + free (fields); + cmd_error (CMD_PARSE_ERROR, err, + "Backslash at end of string."); + return (CMD_PARSE_ERROR); + } + + /* un-escape the next character; skip backslash */ + string++; + } + + if (! in_field) + NEW_FIELD (); + else { + *field = string[0]; + field++; + } + } + + if (in_quotes) + { + free (fields); + cmd_error (CMD_PARSE_ERROR, err, "Unterminated quoted string."); + return (CMD_PARSE_ERROR); + } + +#undef NEW_FIELD +#undef END_FIELD + + fields[len] = NULL; + if (ret_len != NULL) + *ret_len = len; + if (ret_fields != NULL) + *ret_fields = fields; + else + free (fields); + return (CMD_OK); +} /* int cmd_split */ + +/* * public API */ @@ -49,32 +195,43 @@ void cmd_error (cmd_status_t status, cmd_error_handler_t *err, va_end (ap); } /* void cmd_error */ -cmd_status_t cmd_parse (char *buffer, +cmd_status_t cmd_parsev (size_t argc, char **argv, cmd_t *ret_cmd, cmd_error_handler_t *err) { char *command = NULL; - int status; - if ((buffer == NULL) || (ret_cmd == NULL)) + if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL)) { errno = EINVAL; - cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse."); + cmd_error (CMD_ERROR, err, "Missing command."); return CMD_ERROR; } - if ((status = parse_string (&buffer, &command)) != 0) + memset (ret_cmd, 0, sizeof (*ret_cmd)); + command = argv[0]; + if (strcasecmp ("FLUSH", command) == 0) { - cmd_error (CMD_PARSE_ERROR, err, - "Failed to extract command from `%s'.", buffer); - return (CMD_PARSE_ERROR); + ret_cmd->type = CMD_FLUSH; + return cmd_parse_flush (argc - 1, argv + 1, + &ret_cmd->cmd.flush, err); } - assert (command != NULL); - - memset (ret_cmd, 0, sizeof (*ret_cmd)); - if (strcasecmp ("PUTVAL", command) == 0) + else if (strcasecmp ("GETVAL", command) == 0) + { + ret_cmd->type = CMD_GETVAL; + return cmd_parse_getval (argc - 1, argv + 1, + &ret_cmd->cmd.getval, err); + } + else if (strcasecmp ("LISTVAL", command) == 0) + { + ret_cmd->type = CMD_LISTVAL; + return cmd_parse_listval (argc - 1, argv + 1, + &ret_cmd->cmd.listval, err); + } + else if (strcasecmp ("PUTVAL", command) == 0) { ret_cmd->type = CMD_PUTVAL; - return cmd_parse_putval (buffer, &ret_cmd->cmd.putval, err); + return cmd_parse_putval (argc - 1, argv + 1, + &ret_cmd->cmd.putval, err); } else { @@ -85,6 +242,21 @@ cmd_status_t cmd_parse (char *buffer, } return (CMD_OK); +} /* cmd_status_t cmd_parsev */ + +cmd_status_t cmd_parse (char *buffer, + cmd_t *ret_cmd, cmd_error_handler_t *err) +{ + char **fields = NULL; + size_t fields_num = 0; + cmd_status_t status; + + if ((status = cmd_split (buffer, &fields_num, &fields, err)) != CMD_OK) + return status; + + status = cmd_parsev (fields_num, fields, ret_cmd, err); + free (fields); + return (status); } /* cmd_status_t cmd_parse */ void cmd_destroy (cmd_t *cmd) @@ -97,12 +269,53 @@ void cmd_destroy (cmd_t *cmd) case CMD_UNKNOWN: /* nothing to do */ break; + case CMD_FLUSH: + cmd_destroy_flush (&cmd->cmd.flush); + break; + case CMD_GETVAL: + cmd_destroy_getval (&cmd->cmd.getval); + break; + case CMD_LISTVAL: + cmd_destroy_listval (&cmd->cmd.listval); + break; case CMD_PUTVAL: cmd_destroy_putval (&cmd->cmd.putval); break; } } /* void cmd_destroy */ +cmd_status_t cmd_parse_option (char *field, + char **ret_key, char **ret_value, cmd_error_handler_t *err) +{ + char *key, *value; + + if (field == NULL) + { + errno = EINVAL; + cmd_error (CMD_ERROR, err, "Invalid argument to cmd_parse_option."); + return (CMD_ERROR); + } + key = value = field; + + /* Look for the equal sign. */ + while (isalnum ((int)value[0]) || (value[0] == '_') || (value[0] == ':')) + value++; + if ((value[0] != '=') || (value == key)) + { + /* Whether this is a fatal error is up to the caller. */ + return (CMD_NO_OPTION); + } + *value = '\0'; + value++; + + if (ret_key != NULL) + *ret_key = key; + if (ret_value != NULL) + *ret_value = value; + + return (CMD_OK); +} /* cmd_status_t cmd_parse_option */ + void cmd_error_fh (void *ud, cmd_status_t status, const char *format, va_list ap) {