From f5fdf24ccec9318baf11eec25d6dba881dc77332 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Thu, 9 Jun 2016 00:37:58 +0200 Subject: [PATCH] command parser: Add support for parser options. These can be used to tune the parser behavior. For now, there's an option to specify the default hostname in an identifier. --- src/utils_cmd_flush.c | 10 ++++--- src/utils_cmd_flush.h | 3 +- src/utils_cmd_getval.c | 14 +++++++-- src/utils_cmd_getval.h | 3 +- src/utils_cmd_listval.c | 3 +- src/utils_cmd_listval.h | 3 +- src/utils_cmd_putval.c | 14 +++++++-- src/utils_cmd_putval.h | 3 +- src/utils_cmds.c | 25 ++++++++++------ src/utils_cmds.h | 22 +++++++++++--- src/utils_cmds_test.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++--- 11 files changed, 145 insertions(+), 32 deletions(-) diff --git a/src/utils_cmd_flush.c b/src/utils_cmd_flush.c index 7e26be7b..320b0632 100644 --- a/src/utils_cmd_flush.c +++ b/src/utils_cmd_flush.c @@ -34,9 +34,11 @@ #include "utils_cmd_flush.h" cmd_status_t cmd_parse_flush (size_t argc, char **argv, - cmd_flush_t *ret_flush, cmd_error_handler_t *err) + cmd_flush_t *ret_flush, const cmd_options_t *opts, + cmd_error_handler_t *err) { - if (ret_flush == NULL) + + if ((ret_flush == NULL) || (opts == NULL)) { errno = EINVAL; cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse_flush."); @@ -83,7 +85,7 @@ cmd_status_t cmd_parse_flush (size_t argc, char **argv, if (parse_identifier (opt_value, &id->host, &id->plugin, &id->plugin_instance, &id->type, &id->type_instance, - NULL) != 0) + opts->identifier_default_host) != 0) { cmd_error (CMD_PARSE_ERROR, err, "Invalid identifier `%s'.", opt_value); @@ -142,7 +144,7 @@ cmd_status_t cmd_handle_flush (FILE *fh, char *buffer) DEBUG ("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);", (void *) fh, buffer); - if ((status = cmd_parse (buffer, &cmd, &err)) != CMD_OK) + if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK) return (status); if (cmd.type != CMD_FLUSH) { diff --git a/src/utils_cmd_flush.h b/src/utils_cmd_flush.h index 2afa3c71..9dbff20c 100644 --- a/src/utils_cmd_flush.h +++ b/src/utils_cmd_flush.h @@ -32,7 +32,8 @@ #include "utils_cmds.h" cmd_status_t cmd_parse_flush (size_t argc, char **argv, - cmd_flush_t *ret_flush, cmd_error_handler_t *err); + cmd_flush_t *ret_flush, const cmd_options_t *opts, + cmd_error_handler_t *err); cmd_status_t cmd_handle_flush (FILE *fh, char *buffer); diff --git a/src/utils_cmd_getval.c b/src/utils_cmd_getval.c index 3a8a2be8..f29680a1 100644 --- a/src/utils_cmd_getval.c +++ b/src/utils_cmd_getval.c @@ -34,11 +34,19 @@ #include "utils_cmd_getval.h" cmd_status_t cmd_parse_getval (size_t argc, char **argv, - cmd_getval_t *ret_getval, cmd_error_handler_t *err) + cmd_getval_t *ret_getval, const cmd_options_t *opts, + cmd_error_handler_t *err) { char *identifier_copy; int status; + if ((ret_getval == NULL) || (opts == NULL)) + { + errno = EINVAL; + cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse_getval."); + return (CMD_ERROR); + } + if (argc != 1) { if (argc == 0) @@ -56,7 +64,7 @@ cmd_status_t cmd_parse_getval (size_t argc, char **argv, status = parse_identifier (argv[0], &ret_getval->identifier.host, &ret_getval->identifier.plugin, &ret_getval->identifier.plugin_instance, &ret_getval->identifier.type, &ret_getval->identifier.type_instance, - NULL); + opts->identifier_default_host); if (status != 0) { DEBUG ("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy); @@ -99,7 +107,7 @@ cmd_status_t cmd_handle_getval (FILE *fh, char *buffer) DEBUG ("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);", (void *) fh, buffer); - if ((status = cmd_parse (buffer, &cmd, &err)) != CMD_OK) + if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK) return (status); if (cmd.type != CMD_GETVAL) { diff --git a/src/utils_cmd_getval.h b/src/utils_cmd_getval.h index 0efaed64..b0c64be1 100644 --- a/src/utils_cmd_getval.h +++ b/src/utils_cmd_getval.h @@ -32,7 +32,8 @@ #include "utils_cmds.h" cmd_status_t cmd_parse_getval (size_t argc, char **argv, - cmd_getval_t *ret_getval, cmd_error_handler_t *err); + cmd_getval_t *ret_getval, const cmd_options_t *opts, + cmd_error_handler_t *err); cmd_status_t cmd_handle_getval (FILE *fh, char *buffer); diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c index 7e256f01..27b88cbf 100644 --- a/src/utils_cmd_listval.c +++ b/src/utils_cmd_listval.c @@ -35,6 +35,7 @@ cmd_status_t cmd_parse_listval (size_t argc, char **argv, cmd_listval_t *ret_listval __attribute__((unused)), + const cmd_options_t *opts __attribute__((unused)), cmd_error_handler_t *err) { if (argc != 0) @@ -81,7 +82,7 @@ cmd_status_t cmd_handle_listval (FILE *fh, char *buffer) DEBUG ("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);", (void *) fh, buffer); - if ((status = cmd_parse (buffer, &cmd, &err)) != CMD_OK) + if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK) return (status); if (cmd.type != CMD_LISTVAL) { diff --git a/src/utils_cmd_listval.h b/src/utils_cmd_listval.h index 5d2866de..895878c0 100644 --- a/src/utils_cmd_listval.h +++ b/src/utils_cmd_listval.h @@ -32,7 +32,8 @@ #include "utils_cmds.h" cmd_status_t cmd_parse_listval (size_t argc, char **argv, - cmd_listval_t *ret_listval, cmd_error_handler_t *err); + cmd_listval_t *ret_listval, const cmd_options_t *opts, + cmd_error_handler_t *err); cmd_status_t cmd_handle_listval (FILE *fh, char *buffer); diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c index 0a0af8ae..b2537859 100644 --- a/src/utils_cmd_putval.c +++ b/src/utils_cmd_putval.c @@ -69,7 +69,8 @@ static int set_option (value_list_t *vl, const char *key, const char *value) */ cmd_status_t cmd_parse_putval (size_t argc, char **argv, - cmd_putval_t *ret_putval, cmd_error_handler_t *err) + cmd_putval_t *ret_putval, const cmd_options_t *opts, + cmd_error_handler_t *err) { cmd_status_t result; @@ -87,6 +88,13 @@ cmd_status_t cmd_parse_putval (size_t argc, char **argv, value_list_t vl = VALUE_LIST_INIT; size_t i; + if ((ret_putval == NULL) || (opts == NULL)) + { + errno = EINVAL; + cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse_putval."); + return (CMD_ERROR); + } + if (argc < 2) { cmd_error (CMD_PARSE_ERROR, err, @@ -103,7 +111,7 @@ cmd_status_t cmd_parse_putval (size_t argc, char **argv, status = parse_identifier (identifier, &hostname, &plugin, &plugin_instance, &type, &type_instance, - NULL); + opts->identifier_default_host); if (status != 0) { DEBUG ("cmd_handle_putval: Cannot parse identifier `%s'.", @@ -257,7 +265,7 @@ cmd_status_t cmd_handle_putval (FILE *fh, char *buffer) DEBUG ("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);", (void *) fh, buffer); - if ((status = cmd_parse (buffer, &cmd, &err)) != CMD_OK) + if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK) return (status); if (cmd.type != CMD_PUTVAL) { diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h index 363194db..bb0b227f 100644 --- a/src/utils_cmd_putval.h +++ b/src/utils_cmd_putval.h @@ -33,7 +33,8 @@ #include "utils_cmds.h" cmd_status_t cmd_parse_putval (size_t argc, char **argv, - cmd_putval_t *ret_putval, cmd_error_handler_t *err); + cmd_putval_t *ret_putval, const cmd_options_t *opts, + cmd_error_handler_t *err); cmd_status_t cmd_handle_putval (FILE *fh, char *buffer); diff --git a/src/utils_cmds.c b/src/utils_cmds.c index f574c6a1..f6675969 100644 --- a/src/utils_cmds.c +++ b/src/utils_cmds.c @@ -37,6 +37,10 @@ #include #include +static cmd_options_t default_options = { + /* identifier_default_host = */ NULL, +}; + /* * private helper functions */ @@ -195,8 +199,8 @@ void cmd_error (cmd_status_t status, cmd_error_handler_t *err, va_end (ap); } /* void cmd_error */ -cmd_status_t cmd_parsev (size_t argc, char **argv, - cmd_t *ret_cmd, cmd_error_handler_t *err) +cmd_status_t cmd_parsev (size_t argc, char **argv, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err) { char *command = NULL; cmd_status_t status; @@ -208,31 +212,34 @@ cmd_status_t cmd_parsev (size_t argc, char **argv, return CMD_ERROR; } + if (opts == NULL) + opts = &default_options; + memset (ret_cmd, 0, sizeof (*ret_cmd)); command = argv[0]; if (strcasecmp ("FLUSH", command) == 0) { ret_cmd->type = CMD_FLUSH; status = cmd_parse_flush (argc - 1, argv + 1, - &ret_cmd->cmd.flush, err); + &ret_cmd->cmd.flush, opts, err); } else if (strcasecmp ("GETVAL", command) == 0) { ret_cmd->type = CMD_GETVAL; status = cmd_parse_getval (argc - 1, argv + 1, - &ret_cmd->cmd.getval, err); + &ret_cmd->cmd.getval, opts, err); } else if (strcasecmp ("LISTVAL", command) == 0) { ret_cmd->type = CMD_LISTVAL; status = cmd_parse_listval (argc - 1, argv + 1, - &ret_cmd->cmd.listval, err); + &ret_cmd->cmd.listval, opts, err); } else if (strcasecmp ("PUTVAL", command) == 0) { ret_cmd->type = CMD_PUTVAL; status = cmd_parse_putval (argc - 1, argv + 1, - &ret_cmd->cmd.putval, err); + &ret_cmd->cmd.putval, opts, err); } else { @@ -247,8 +254,8 @@ cmd_status_t cmd_parsev (size_t argc, char **argv, return (status); } /* cmd_status_t cmd_parsev */ -cmd_status_t cmd_parse (char *buffer, - cmd_t *ret_cmd, cmd_error_handler_t *err) +cmd_status_t cmd_parse (char *buffer, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err) { char **fields = NULL; size_t fields_num = 0; @@ -257,7 +264,7 @@ cmd_status_t cmd_parse (char *buffer, if ((status = cmd_split (buffer, &fields_num, &fields, err)) != CMD_OK) return status; - status = cmd_parsev (fields_num, fields, ret_cmd, err); + status = cmd_parsev (fields_num, fields, ret_cmd, opts, err); free (fields); return (status); } /* cmd_status_t cmd_parse */ diff --git a/src/utils_cmds.h b/src/utils_cmds.h index 62cf8a92..205e89a0 100644 --- a/src/utils_cmds.h +++ b/src/utils_cmds.h @@ -91,6 +91,19 @@ typedef struct { /* * NAME + * cmd_options_t + * + * DESCRIPTIONS + * Optional settings for tuning the parser behavior. + */ +typedef struct { + /* identifier_default_host: If non-NULL, the hostname is optional and will + * default to the specified value. */ + char *identifier_default_host; +} cmd_options_t; + +/* + * NAME * cmd_status_t * * DESCRIPTION @@ -140,16 +153,17 @@ void cmd_error (cmd_status_t status, cmd_error_handler_t *err, * PARAMETERS * `buffer' The command string to be parsed. * `ret_cmd' The parse result will be stored at this location. + * `opts' Parser options. If NULL, defaults will be used. * `err' An optional error handler to invoke on error. * * RETURN VALUE * CMD_OK on success or the respective error code otherwise. */ -cmd_status_t cmd_parse (char *buffer, - cmd_t *ret_cmd, cmd_error_handler_t *err); +cmd_status_t cmd_parse (char *buffer, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err); -cmd_status_t cmd_parsev (size_t argc, char **argv, - cmd_t *ret_cmd, cmd_error_handler_t *err); +cmd_status_t cmd_parsev (size_t argc, char **argv, cmd_t *ret_cmd, + const cmd_options_t *opts, cmd_error_handler_t *err); void cmd_destroy (cmd_t *cmd); diff --git a/src/utils_cmds_test.c b/src/utils_cmds_test.c index 68b51403..f5743e99 100644 --- a/src/utils_cmds_test.c +++ b/src/utils_cmds_test.c @@ -40,49 +40,74 @@ static void error_cb (void *ud, cmd_status_t status, fflush (stdout); } /* void error_cb */ -struct { +static cmd_options_t default_host_opts = { + /* identifier_default_host = */ "dummy-host", +}; + +static struct { char *input; + cmd_options_t *opts; cmd_status_t expected_status; cmd_type_t expected_type; } parse_data[] = { /* Valid FLUSH commands. */ { "FLUSH", + NULL, CMD_OK, CMD_FLUSH, }, { "FLUSH identifier=myhost/magic/MAGIC", + NULL, + CMD_OK, + CMD_FLUSH, + }, + { + "FLUSH identifier=magic/MAGIC", + &default_host_opts, CMD_OK, CMD_FLUSH, }, { "FLUSH timeout=123 plugin=\"A\"", + NULL, CMD_OK, CMD_FLUSH, }, /* Invalid FLUSH commands. */ { + /* Missing hostname; no default. */ + "FLUSH identifier=magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { /* Missing 'identifier' key. */ "FLUSH myhost/magic/MAGIC", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { /* Invalid timeout. */ "FLUSH timeout=A", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { /* Invalid identifier. */ "FLUSH identifier=invalid", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { /* Invalid option. */ "FLUSH invalid=option", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, @@ -90,18 +115,33 @@ struct { /* Valid GETVAL commands. */ { "GETVAL myhost/magic/MAGIC", + NULL, + CMD_OK, + CMD_GETVAL, + }, + { + "GETVAL magic/MAGIC", + &default_host_opts, CMD_OK, CMD_GETVAL, }, /* Invalid GETVAL commands. */ { + "GETVAL magic/MAGIC", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { "GETVAL", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "GETVAL invalid", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, @@ -109,6 +149,7 @@ struct { /* Valid LISTVAL commands. */ { "LISTVAL", + NULL, CMD_OK, CMD_LISTVAL, }, @@ -116,70 +157,95 @@ struct { /* Invalid LISTVAL commands. */ { "LISTVAL invalid", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, /* Valid PUTVAL commands. */ { + "PUTVAL magic/MAGIC N:42", + &default_host_opts, + CMD_OK, + CMD_PUTVAL, + }, + { "PUTVAL myhost/magic/MAGIC N:42", + NULL, CMD_OK, CMD_PUTVAL, }, { "PUTVAL myhost/magic/MAGIC 1234:42", + NULL, CMD_OK, CMD_PUTVAL, }, { "PUTVAL myhost/magic/MAGIC 1234:42 2345:23", + NULL, CMD_OK, CMD_PUTVAL, }, { "PUTVAL myhost/magic/MAGIC interval=2 1234:42", + NULL, CMD_OK, CMD_PUTVAL, }, { "PUTVAL myhost/magic/MAGIC interval=2 1234:42 interval=5 2345:23", + NULL, CMD_OK, CMD_PUTVAL, }, /* Invalid PUTVAL commands. */ { + "PUTVAL magic/MAGIC N:42", + NULL, + CMD_PARSE_ERROR, + CMD_UNKNOWN, + }, + { "PUTVAL", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL invalid N:42", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL myhost/magic/MAGIC A:42", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL myhost/magic/MAGIC 1234:A", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL myhost/magic/MAGIC", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL 1234:A", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, { "PUTVAL myhost/magic/UNKNOWN 1234:42", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, @@ -187,6 +253,7 @@ struct { * As of collectd 5.x, PUTVAL accepts invalid options. { "PUTVAL myhost/magic/MAGIC invalid=2 1234:42", + NULL, CMD_PARSE_ERROR, CMD_UNKNOWN, }, @@ -195,11 +262,13 @@ struct { /* Invalid commands. */ { "INVALID", + NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN, }, { "INVALID interval=2", + NULL, CMD_UNKNOWN_COMMAND, CMD_UNKNOWN, }, @@ -222,10 +291,10 @@ DEF_TEST(parse) memset (&cmd, 0, sizeof (cmd)); - status = cmd_parse (input, &cmd, &err); + status = cmd_parse (input, &cmd, parse_data[i].opts, &err); snprintf (description, sizeof (description), - "cmd_parse (\"%s\") = %d (type=%d [%s]); want %d (type=%d [%s])", - parse_data[i].input, status, + "cmd_parse (\"%s\", opts=%p) = %d (type=%d [%s]); want %d (type=%d [%s])", + parse_data[i].input, parse_data[i].opts, status, cmd.type, CMD_TO_STRING (cmd.type), parse_data[i].expected_status, parse_data[i].expected_type, -- 2.11.0