test_utils_latency_SOURCES = utils_latency_test.c testing.h
test_utils_latency_LDADD = liblatency.la daemon/libplugin_mock.la -lm
+noinst_LTLIBRARIES += libcmds.la
+libcmds_la_SOURCES = utils_cmds.c utils_cmds.h \
+ utils_cmd_flush.c utils_cmd_flush.h \
+ utils_cmd_getval.c utils_cmd_getval.h \
+ utils_cmd_listval.c utils_cmd_listval.h \
+ utils_cmd_putval.c utils_cmd_putval.h \
+ utils_parse_option.c
+libcmds_la_LIBADD = daemon/libcommon.la daemon/libmetadata.la \
+ daemon/libplugin_mock.la -lm
+check_PROGRAMS += test_utils_cmds
+TESTS += test_utils_cmds
+test_utils_cmds_SOURCES = utils_cmds_test.c testing.h
+test_utils_cmds_LDADD = libcmds.la \
+ daemon/libplugin_mock.la daemon/libmetadata.la
+
noinst_LTLIBRARIES += liblookup.la
liblookup_la_SOURCES = utils_vl_lookup.c utils_vl_lookup.h
liblookup_la_LIBADD = daemon/libavltree.la
if BUILD_PLUGIN_AMQP
pkglib_LTLIBRARIES += amqp.la
amqp_la_SOURCES = amqp.c \
+ utils_cmds.c utils_cmds.h \
utils_cmd_putval.c utils_cmd_putval.h \
- utils_parse_option.c utils_parse_option.h \
+ utils_parse_option.c utils_parse_option.h \
utils_format_graphite.c utils_format_graphite.h
amqp_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
if BUILD_PLUGIN_EXEC
pkglib_LTLIBRARIES += exec.la
exec_la_SOURCES = exec.c \
+ utils_cmds.c utils_cmds.h \
utils_cmd_putnotif.c utils_cmd_putnotif.h \
utils_cmd_putval.c utils_cmd_putval.h \
utils_parse_option.h utils_parse_option.c
if BUILD_PLUGIN_UNIXSOCK
pkglib_LTLIBRARIES += unixsock.la
unixsock_la_SOURCES = unixsock.c \
+ utils_cmds.c utils_cmds.h \
utils_cmd_flush.h utils_cmd_flush.c \
utils_cmd_getval.h utils_cmd_getval.c \
utils_cmd_getthreshold.h utils_cmd_getthreshold.c \
pkglib_LTLIBRARIES += write_kafka.la
write_kafka_la_SOURCES = write_kafka.c \
utils_format_graphite.c utils_format_graphite.h \
+ utils_cmds.c utils_cmds.h \
utils_cmd_putval.c utils_cmd_putval.h \
utils_crc32.c utils_crc32.h
write_kafka_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRDKAFKA_CPPFLAGS)
struct aggregation_s /* {{{ */
{
- identifier_t ident;
+ lookup_identifier_t ident;
unsigned int group_by;
unsigned int regex_fields;
struct agg_instance_s /* {{{ */
{
pthread_mutex_t lock;
- identifier_t ident;
+ lookup_identifier_t ident;
int ds_type;
if (strcasecmp ("text/collectd", content_type) == 0)
{
- status = handle_putval (stderr, body);
+ status = cmd_handle_putval (stderr, body);
if (status != 0)
- ERROR ("amqp plugin: handle_putval failed with status %i.",
+ ERROR ("amqp plugin: cmd_handle_putval failed with status %i.",
status);
return (status);
}
if (conf->format == CAMQP_FORMAT_COMMAND)
{
- status = create_putval (buffer, sizeof (buffer), ds, vl);
+ status = cmd_create_putval (buffer, sizeof (buffer), ds, vl);
if (status != 0)
{
- ERROR ("amqp plugin: create_putval failed with status %i.",
+ ERROR ("amqp plugin: cmd_create_putval failed with status %i.",
status);
return (status);
}
int parse_identifier (char *str, char **ret_host,
char **ret_plugin, char **ret_plugin_instance,
- char **ret_type, char **ret_type_instance)
+ char **ret_type, char **ret_type_instance,
+ char *default_host)
{
char *hostname = NULL;
char *plugin = NULL;
type = strchr (plugin, '/');
if (type == NULL)
- return (-1);
- *type = '\0'; type++;
+ {
+ if (default_host == NULL)
+ return (-1);
+ /* else: no host specified; use default */
+ type = plugin;
+ plugin = hostname;
+ hostname = default_host;
+ }
+ else
+ {
+ *type = '\0';
+ type++;
+ }
plugin_instance = strchr (plugin, '-');
if (plugin_instance != NULL)
status = parse_identifier (str_copy, &host,
&plugin, &plugin_instance,
- &type, &type_instance);
+ &type, &type_instance,
+ /* default_host = */ NULL);
if (status != 0)
return (status);
int parse_identifier (char *str, char **ret_host,
char **ret_plugin, char **ret_plugin_instance,
- char **ret_type, char **ret_type_instance);
+ char **ret_type, char **ret_type_instance,
+ char *default_host);
int parse_identifier_vl (const char *str, value_list_t *vl);
int parse_value (const char *value, value_t *ret_value, int ds_type);
int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds);
/*
* Public data types
*/
+struct identifier_s
+{
+ char *host;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+};
+typedef struct identifier_s identifier_t;
+
typedef unsigned long long counter_t;
typedef double gauge_t;
typedef int64_t derive_t;
return ENOTSUP;
}
+int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier)
+{
+ return ENOTSUP;
+}
+
+static data_source_t magic_ds[] = {{ "value", DS_TYPE_DERIVE, 0.0, NAN }};
+static data_set_t magic = { "MAGIC", 1, magic_ds };
+const data_set_t *plugin_get_ds (const char *name)
+{
+ if (strcmp (name, "MAGIC"))
+ return NULL;
+
+ return &magic;
+}
+
void plugin_log (int level, char const *format, ...)
{
char buffer[1024];
*/
#include "utils_cache.h"
+#include <errno.h>
gauge_t *uc_get_rate (__attribute__((unused)) data_set_t const *ds,
__attribute__((unused)) value_list_t const *vl)
{
+ errno = ENOTSUP;
return (NULL);
}
+
+int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num)
+{
+ return (ENOTSUP);
+}
+
+int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number)
+{
+ return (ENOTSUP);
+}
static int parse_line (char *buffer) /* {{{ */
{
if (strncasecmp ("PUTVAL", buffer, strlen ("PUTVAL")) == 0)
- return (handle_putval (stdout, buffer));
+ return (cmd_handle_putval (stdout, buffer));
else if (strncasecmp ("PUTNOTIF", buffer, strlen ("PUTNOTIF")) == 0)
return (handle_putnotif (stdout, buffer));
else
#define END_TEST exit ((fail_count__ == 0) ? 0 : 1);
+#define LOG(result, text) \
+ printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text)
+
#define OK1(cond, text) do { \
_Bool result = (cond); \
- printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \
+ LOG (result, text); \
if (!result) { return -1; } \
} while (0)
#define OK(cond) OK1(cond, #cond)
if (strcasecmp (fields[0], "getval") == 0)
{
- handle_getval (fhout, buffer);
+ cmd_handle_getval (fhout, buffer);
}
else if (strcasecmp (fields[0], "getthreshold") == 0)
{
}
else if (strcasecmp (fields[0], "putval") == 0)
{
- handle_putval (fhout, buffer);
+ cmd_handle_putval (fhout, buffer);
}
else if (strcasecmp (fields[0], "listval") == 0)
{
- handle_listval (fhout, buffer);
+ cmd_handle_listval (fhout, buffer);
}
else if (strcasecmp (fields[0], "putnotif") == 0)
{
}
else if (strcasecmp (fields[0], "flush") == 0)
{
- handle_flush (fhout, buffer);
+ cmd_handle_flush (fhout, buffer);
}
else
{
/**
* collectd - src/utils_cmd_flush.c
- * Copyright (C) 2008 Sebastian Harl
+ * Copyright (C) 2008, 2016 Sebastian Harl
* Copyright (C) 2008 Florian Forster
*
* Permission is hereby granted, free of charge, to any person obtaining a
#include "utils_parse_option.h"
#include "utils_cmd_flush.h"
-int handle_flush (FILE *fh, char *buffer)
+cmd_status_t cmd_parse_flush (size_t argc, char **argv,
+ cmd_flush_t *ret_flush, const cmd_options_t *opts,
+ cmd_error_handler_t *err)
{
- int success = 0;
- int error = 0;
-
- double timeout = 0.0;
- char **plugins = NULL;
- size_t plugins_num = 0;
- char **identifiers = NULL;
- size_t identifiers_num = 0;
-
-#define PRINT_TO_SOCK(fh, ...) \
- do { \
- if (fprintf (fh, __VA_ARGS__) < 0) { \
- char errbuf[1024]; \
- WARNING ("handle_flush: failed to write to socket #%i: %s", \
- fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
- strarray_free (plugins, plugins_num); \
- strarray_free (identifiers, identifiers_num); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
-
- 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)
+ if ((ret_flush == NULL) || (opts == NULL))
{
- PRINT_TO_SOCK (fh, "-1 Cannot parse command.\n");
- return (-1);
+ errno = EINVAL;
+ cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse_flush.");
+ return (CMD_ERROR);
}
- buffer += strlen ("FLUSH");
- while (*buffer != 0)
+ for (size_t i = 0; i < argc; i++)
{
char *opt_key;
char *opt_value;
opt_key = NULL;
opt_value = NULL;
- status = parse_option (&buffer, &opt_key, &opt_value);
+ status = cmd_parse_option (argv[i], &opt_key, &opt_value, err);
if (status != 0)
{
- PRINT_TO_SOCK (fh, "-1 Parsing options failed.\n");
- strarray_free (plugins, plugins_num);
- strarray_free (identifiers, identifiers_num);
- return (-1);
+ if (status == CMD_NO_OPTION)
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Invalid option string `%s'.", argv[i]);
+ cmd_destroy_flush (ret_flush);
+ return (CMD_PARSE_ERROR);
}
if (strcasecmp ("plugin", opt_key) == 0)
- strarray_add (&plugins, &plugins_num, opt_value);
+ {
+ strarray_add (&ret_flush->plugins, &ret_flush->plugins_num,
+ opt_value);
+ }
else if (strcasecmp ("identifier", opt_key) == 0)
- strarray_add (&identifiers, &identifiers_num, opt_value);
+ {
+ identifier_t *id = realloc (ret_flush->identifiers,
+ (ret_flush->identifiers_num + 1) * sizeof (*id));
+ if (id == NULL)
+ {
+ cmd_error (CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_flush (ret_flush);
+ return (CMD_ERROR);
+ }
+
+ ret_flush->identifiers = id;
+ id = ret_flush->identifiers + ret_flush->identifiers_num;
+ ret_flush->identifiers_num++;
+ if (parse_identifier (opt_value,
+ &id->host, &id->plugin, &id->plugin_instance,
+ &id->type, &id->type_instance,
+ opts->identifier_default_host) != 0)
+ {
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Invalid identifier `%s'.", opt_value);
+ cmd_destroy_flush (ret_flush);
+ return (CMD_PARSE_ERROR);
+ }
+ }
else if (strcasecmp ("timeout", opt_key) == 0)
{
char *endptr;
errno = 0;
endptr = NULL;
- timeout = strtod (opt_value, &endptr);
+ ret_flush->timeout = strtod (opt_value, &endptr);
- if ((endptr == opt_value) || (errno != 0) || (!isfinite (timeout)))
+ if ((endptr == opt_value) || (errno != 0)
+ || (!isfinite (ret_flush->timeout)))
{
- PRINT_TO_SOCK (fh, "-1 Invalid value for option `timeout': "
- "%s\n", opt_value);
- strarray_free (plugins, plugins_num);
- strarray_free (identifiers, identifiers_num);
- return (-1);
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Invalid value for option `timeout': %s",
+ opt_value);
+ cmd_destroy_flush (ret_flush);
+ return (CMD_PARSE_ERROR);
}
- else if (timeout < 0.0)
+ else if (ret_flush->timeout < 0.0)
{
- timeout = 0.0;
+ ret_flush->timeout = 0.0;
}
}
else
{
- PRINT_TO_SOCK (fh, "-1 Cannot parse option %s\n", opt_key);
- strarray_free (plugins, plugins_num);
- strarray_free (identifiers, identifiers_num);
- return (-1);
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Cannot parse option `%s'.", opt_key);
+ cmd_destroy_flush (ret_flush);
+ return (CMD_PARSE_ERROR);
}
- } /* while (*buffer != 0) */
+ }
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_flush */
- for (size_t i = 0; (i == 0) || (i < plugins_num); i++)
+cmd_status_t cmd_handle_flush (FILE *fh, char *buffer)
+{
+ cmd_error_handler_t err = { cmd_error_fh, fh };
+ cmd_t cmd;
+
+ int success = 0;
+ int error = 0;
+ int status;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return (-1);
+
+ DEBUG ("utils_cmd_flush: cmd_handle_flush (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_FLUSH)
+ {
+ cmd_error (CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING (cmd.type));
+ cmd_destroy (&cmd);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ for (size_t i = 0; (i == 0) || (i < cmd.cmd.flush.plugins_num); i++)
{
char *plugin = NULL;
- if (plugins_num != 0)
- plugin = plugins[i];
+ if (cmd.cmd.flush.plugins_num != 0)
+ plugin = cmd.cmd.flush.plugins[i];
- for (size_t j = 0; (j == 0) || (j < identifiers_num); j++)
+ for (size_t j = 0; (j == 0) || (j < cmd.cmd.flush.identifiers_num); j++)
{
char *identifier = NULL;
+ char buffer[1024];
int status;
- if (identifiers_num != 0)
- identifier = identifiers[j];
+ if (cmd.cmd.flush.identifiers_num != 0)
+ {
+ identifier_t *id = cmd.cmd.flush.identifiers + j;
+ if (format_name (buffer, sizeof (buffer),
+ id->host, id->plugin, id->plugin_instance,
+ id->type, id->type_instance) != 0)
+ {
+ error++;
+ continue;
+ }
+ identifier = buffer;
+ }
status = plugin_flush (plugin,
- DOUBLE_TO_CDTIME_T (timeout),
+ DOUBLE_TO_CDTIME_T (cmd.cmd.flush.timeout),
identifier);
if (status == 0)
success++;
}
}
- PRINT_TO_SOCK (fh, "0 Done: %i successful, %i errors\n",
+ cmd_error (CMD_OK, &err, "Done: %i successful, %i errors",
success, error);
- strarray_free (plugins, plugins_num);
- strarray_free (identifiers, identifiers_num);
+ cmd_destroy (&cmd);
return (0);
#undef PRINT_TO_SOCK
-} /* int handle_flush */
+} /* cmd_status_t cmd_handle_flush */
+
+void cmd_destroy_flush (cmd_flush_t *flush)
+{
+ if (flush == NULL)
+ return;
+
+ strarray_free (flush->plugins, flush->plugins_num);
+ flush->plugins = NULL;
+ flush->plugins_num = 0;
+
+ sfree (flush->identifiers);
+ flush->identifiers_num = 0;
+} /* void cmd_destroy_flush */
/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
/**
* collectd - src/utils_cmd_flush.h
- * Copyright (C) 2008 Sebastian Harl
+ * Copyright (C) 2008, 2016 Sebastian Harl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
#include <stdio.h>
-int handle_flush (FILE *fh, char *buffer);
+#include "utils_cmds.h"
+
+cmd_status_t cmd_parse_flush (size_t argc, char **argv,
+ 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);
+
+void cmd_destroy_flush (cmd_flush_t *flush);
#endif /* UTILS_CMD_FLUSH_H */
status = parse_identifier (identifier_copy, &host,
&plugin, &plugin_instance,
- &type, &type_instance);
+ &type, &type_instance,
+ /* default_host = */ NULL);
if (status != 0)
{
DEBUG ("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
#include "utils_parse_option.h"
#include "utils_cmd_getval.h"
+cmd_status_t cmd_parse_getval (size_t argc, char **argv,
+ 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)
+ cmd_error (CMD_PARSE_ERROR, err, "Missing identifier.");
+ else
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Garbage after identifier: `%s'.", argv[1]);
+ return (CMD_PARSE_ERROR);
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup (argv[0]);
+
+ 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,
+ opts->identifier_default_host);
+ if (status != 0)
+ {
+ DEBUG ("cmd_parse_getval: Cannot parse identifier `%s'.", identifier_copy);
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Cannot parse identifier `%s'.", identifier_copy);
+ sfree (identifier_copy);
+ return (CMD_PARSE_ERROR);
+ }
+
+ ret_getval->raw_identifier = identifier_copy;
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_getval */
+
#define print_to_socket(fh, ...) \
do { \
if (fprintf (fh, __VA_ARGS__) < 0) { \
char errbuf[1024]; \
- WARNING ("handle_getval: failed to write to socket #%i: %s", \
+ WARNING ("cmd_handle_getval: failed to write to socket #%i: %s", \
fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
return -1; \
} \
fflush(fh); \
} while (0)
-int handle_getval (FILE *fh, char *buffer)
+cmd_status_t cmd_handle_getval (FILE *fh, char *buffer)
{
- char *command;
- char *identifier;
- char *identifier_copy;
+ cmd_error_handler_t err = { cmd_error_fh, fh };
+ cmd_status_t status;
+ cmd_t cmd;
- char *hostname;
- char *plugin;
- char *plugin_instance;
- char *type;
- char *type_instance;
gauge_t *values;
size_t values_num;
const data_set_t *ds;
- int status;
if ((fh == NULL) || (buffer == NULL))
return (-1);
- DEBUG ("utils_cmd_getval: handle_getval (fh = %p, buffer = %s);",
+ DEBUG ("utils_cmd_getval: cmd_handle_getval (fh = %p, buffer = %s);",
(void *) fh, buffer);
- command = NULL;
- status = parse_string (&buffer, &command);
- if (status != 0)
- {
- print_to_socket (fh, "-1 Cannot parse command.\n");
- return (-1);
- }
- assert (command != NULL);
-
- if (strcasecmp ("GETVAL", command) != 0)
- {
- print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
- return (-1);
- }
-
- identifier = NULL;
- status = parse_string (&buffer, &identifier);
- if (status != 0)
- {
- print_to_socket (fh, "-1 Cannot parse identifier.\n");
- return (-1);
- }
- assert (identifier != NULL);
-
- if (*buffer != 0)
- {
- print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
- return (-1);
- }
-
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
- identifier_copy = sstrdup (identifier);
-
- status = parse_identifier (identifier_copy, &hostname,
- &plugin, &plugin_instance,
- &type, &type_instance);
- if (status != 0)
+ if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_GETVAL)
{
- DEBUG ("handle_getval: Cannot parse identifier `%s'.", identifier);
- print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n", identifier);
- sfree (identifier_copy);
- return (-1);
+ cmd_error (CMD_UNKNOWN_COMMAND, &err,
+ "Unexpected command: `%s'.", CMD_TO_STRING (cmd.type));
+ cmd_destroy (&cmd);
+ return (CMD_UNKNOWN_COMMAND);
}
- ds = plugin_get_ds (type);
+ ds = plugin_get_ds (cmd.cmd.getval.identifier.type);
if (ds == NULL)
{
- DEBUG ("handle_getval: plugin_get_ds (%s) == NULL;", type);
- print_to_socket (fh, "-1 Type `%s' is unknown.\n", type);
- sfree (identifier_copy);
+ DEBUG ("cmd_handle_getval: plugin_get_ds (%s) == NULL;",
+ cmd.cmd.getval.identifier.type);
+ cmd_error (CMD_ERROR, &err, "Type `%s' is unknown.\n",
+ cmd.cmd.getval.identifier.type);
+ cmd_destroy (&cmd);
return (-1);
}
values = NULL;
values_num = 0;
- status = uc_get_rate_by_name (identifier, &values, &values_num);
+ status = uc_get_rate_by_name (cmd.cmd.getval.raw_identifier, &values, &values_num);
if (status != 0)
{
- print_to_socket (fh, "-1 No such value\n");
- sfree (identifier_copy);
- return (-1);
+ cmd_error (CMD_ERROR, &err, "No such value.");
+ cmd_destroy (&cmd);
+ return (CMD_ERROR);
}
if (ds->ds_num != values_num)
ERROR ("ds[%s]->ds_num = %zu, "
"but uc_get_rate_by_name returned %zu values.",
ds->type, ds->ds_num, values_num);
- print_to_socket (fh, "-1 Error reading value from cache.\n");
+ cmd_error (CMD_ERROR, &err, "Error reading value from cache.");
sfree (values);
- sfree (identifier_copy);
- return (-1);
+ cmd_destroy (&cmd);
+ return (CMD_ERROR);
}
print_to_socket (fh, "%zu Value%s found\n", values_num,
}
sfree (values);
- sfree (identifier_copy);
+ cmd_destroy (&cmd);
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_handle_getval */
+
+void cmd_destroy_getval (cmd_getval_t *getval)
+{
+ if (getval == NULL)
+ return;
- return (0);
-} /* int handle_getval */
+ sfree (getval->raw_identifier);
+} /* void cmd_destroy_getval */
/* vim: set sw=2 sts=2 ts=8 : */
#include <stdio.h>
-int handle_getval (FILE *fh, char *buffer);
+#include "utils_cmds.h"
+
+cmd_status_t cmd_parse_getval (size_t argc, char **argv,
+ 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);
+
+void cmd_destroy_getval (cmd_getval_t *getval);
#endif /* UTILS_CMD_GETVAL_H */
#include "utils_cache.h"
#include "utils_parse_option.h"
+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)
+ {
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Garbage after end of command: `%s'.", argv[0]);
+ return (CMD_PARSE_ERROR);
+ }
+
+ return (CMD_OK);
+} /* cmd_status_t cmd_parse_listval */
+
#define free_everything_and_return(status) do { \
for (size_t j = 0; j < number; j++) { \
sfree(names[j]); \
char errbuf[1024]; \
WARNING ("handle_listval: failed to write to socket #%i: %s", \
fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
- free_everything_and_return (-1); \
+ free_everything_and_return (CMD_ERROR); \
} \
fflush(fh); \
} while (0)
-int handle_listval (FILE *fh, char *buffer)
+cmd_status_t cmd_handle_listval (FILE *fh, char *buffer)
{
- char *command;
+ cmd_error_handler_t err = { cmd_error_fh, fh };
+ cmd_status_t status;
+ cmd_t cmd;
+
char **names = NULL;
cdtime_t *times = NULL;
size_t number = 0;
- int status;
DEBUG ("utils_cmd_listval: handle_listval (fh = %p, buffer = %s);",
(void *) fh, buffer);
- command = NULL;
- status = parse_string (&buffer, &command);
- if (status != 0)
+ if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_LISTVAL)
{
- print_to_socket (fh, "-1 Cannot parse command.\n");
- free_everything_and_return (-1);
- }
- assert (command != NULL);
-
- if (strcasecmp ("LISTVAL", command) != 0)
- {
- print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
- free_everything_and_return (-1);
- }
-
- if (*buffer != 0)
- {
- print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
- free_everything_and_return (-1);
+ cmd_error (CMD_UNKNOWN_COMMAND, &err,
+ "Unexpected command: `%s'.", CMD_TO_STRING (cmd.type));
+ free_everything_and_return (CMD_UNKNOWN_COMMAND);
}
status = uc_get_names (&names, ×, &number);
if (status != 0)
{
DEBUG ("command listval: uc_get_names failed with status %i", status);
- print_to_socket (fh, "-1 uc_get_names failed.\n");
- free_everything_and_return (-1);
+ cmd_error (CMD_ERROR, &err, "uc_get_names failed.");
+ free_everything_and_return (CMD_ERROR);
}
print_to_socket (fh, "%i Value%s found\n",
print_to_socket (fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE (times[i]),
names[i]);
- free_everything_and_return (0);
-} /* int handle_listval */
+ free_everything_and_return (CMD_OK);
+} /* cmd_status_t cmd_handle_listval */
+
+void cmd_destroy_listval (cmd_listval_t *listval __attribute__((unused)))
+{
+ /* nothing to do */
+} /* void cmd_destroy_listval */
/* vim: set sw=2 sts=2 ts=8 : */
#include <stdio.h>
-int handle_listval (FILE *fh, char *buffer);
+#include "utils_cmds.h"
+
+cmd_status_t cmd_parse_listval (size_t argc, char **argv,
+ 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);
+
+void cmd_destroy_listval (cmd_listval_t *listval);
#endif /* UTILS_CMD_LISTVAL_H */
/**
* collectd - src/utils_cmd_putval.c
* Copyright (C) 2007-2009 Florian octo Forster
+ * Copyright (C) 2016 Sebastian tokkee Harl
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
*
* Authors:
* Florian octo Forster <octo at collectd.org>
+ * Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "utils_cmds.h"
+#include "utils_cmd_putval.h"
#include "utils_parse_option.h"
#include "utils_cmd_putval.h"
-#define print_to_socket(fh, ...) \
- do { \
- if (fprintf (fh, __VA_ARGS__) < 0) { \
- char errbuf[1024]; \
- WARNING ("handle_putval: failed to write to socket #%i: %s", \
- fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
- sfree (vl.values); \
- return -1; \
- } \
- fflush(fh); \
- } while (0)
+/*
+ * private helper functions
+ */
static int set_option (value_list_t *vl, const char *key, const char *value)
{
return (1);
return (0);
-} /* int parse_option */
+} /* int set_option */
+
+/*
+ * public API
+ */
-int handle_putval (FILE *fh, char *buffer)
+cmd_status_t cmd_parse_putval (size_t argc, char **argv,
+ cmd_putval_t *ret_putval, const cmd_options_t *opts,
+ cmd_error_handler_t *err)
{
- char *command;
+ cmd_status_t result;
+
char *identifier;
char *hostname;
char *plugin;
char *type;
char *type_instance;
int status;
- int values_submitted;
char *identifier_copy;
const data_set_t *ds;
value_list_t vl = VALUE_LIST_INIT;
- vl.values = NULL;
-
- DEBUG ("utils_cmd_putval: handle_putval (fh = %p, buffer = %s);",
- (void *) fh, buffer);
- command = NULL;
- status = parse_string (&buffer, &command);
- if (status != 0)
+ if ((ret_putval == NULL) || (opts == NULL))
{
- print_to_socket (fh, "-1 Cannot parse command.\n");
- return (-1);
+ errno = EINVAL;
+ cmd_error (CMD_ERROR, err, "Invalid arguments to cmd_parse_putval.");
+ return (CMD_ERROR);
}
- assert (command != NULL);
- if (strcasecmp ("PUTVAL", command) != 0)
+ if (argc < 2)
{
- print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
- return (-1);
+ cmd_error (CMD_PARSE_ERROR, err,
+ "Missing identifier and/or value-list.");
+ return (CMD_PARSE_ERROR);
}
- identifier = NULL;
- status = parse_string (&buffer, &identifier);
- if (status != 0)
- {
- print_to_socket (fh, "-1 Cannot parse identifier.\n");
- return (-1);
- }
- assert (identifier != NULL);
+ identifier = argv[0];
- /* parse_identifier() modifies its first argument,
- * returning pointers into it */
+ /* parse_identifier() modifies its first argument, returning pointers into
+ * it; retain the old value for later. */
identifier_copy = sstrdup (identifier);
- status = parse_identifier (identifier_copy, &hostname,
+ status = parse_identifier (identifier, &hostname,
&plugin, &plugin_instance,
- &type, &type_instance);
+ &type, &type_instance,
+ opts->identifier_default_host);
if (status != 0)
{
- DEBUG ("handle_putval: Cannot parse identifier `%s'.",
- identifier);
- print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n",
- identifier);
+ DEBUG ("cmd_handle_putval: Cannot parse identifier `%s'.",
+ identifier_copy);
+ cmd_error (CMD_PARSE_ERROR, err, "Cannot parse identifier `%s'.",
+ identifier_copy);
sfree (identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
if ((strlen (hostname) >= sizeof (vl.host))
|| ((type_instance != NULL)
&& (strlen (type_instance) >= sizeof (vl.type_instance))))
{
- print_to_socket (fh, "-1 Identifier too long.\n");
+ cmd_error (CMD_PARSE_ERROR, err, "Identifier too long.");
sfree (identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
sstrncpy (vl.host, hostname, sizeof (vl.host));
sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
ds = plugin_get_ds (type);
- if (ds == NULL) {
- print_to_socket (fh, "-1 Type `%s' isn't defined.\n", type);
+ if (ds == NULL)
+ {
+ cmd_error (CMD_PARSE_ERROR, err, "1 Type `%s' isn't defined.", type);
sfree (identifier_copy);
- return (-1);
+ return (CMD_PARSE_ERROR);
}
- /* Free identifier_copy */
hostname = NULL;
plugin = NULL; plugin_instance = NULL;
type = NULL; type_instance = NULL;
- sfree (identifier_copy);
vl.values_len = ds->ds_num;
vl.values = malloc (vl.values_len * sizeof (*vl.values));
if (vl.values == NULL)
{
- print_to_socket (fh, "-1 malloc failed.\n");
- return (-1);
+ cmd_error (CMD_ERROR, err, "malloc failed.");
+ sfree (identifier_copy);
+ return (CMD_ERROR);
}
- /* All the remaining fields are part of the optionlist. */
- values_submitted = 0;
- while (*buffer != 0)
+ ret_putval->raw_identifier = identifier_copy;
+ if (ret_putval->raw_identifier == NULL)
{
- char *string = NULL;
- char *value = NULL;
+ cmd_error (CMD_ERROR, err, "malloc failed.");
+ cmd_destroy_putval (ret_putval);
+ sfree (vl.values);
+ return (CMD_ERROR);
+ }
- status = parse_option (&buffer, &string, &value);
- if (status < 0)
- {
- /* parse_option failed, buffer has been modified.
- * => we need to abort */
- print_to_socket (fh, "-1 Misformatted option.\n");
- sfree (vl.values);
- return (-1);
- }
- else if (status == 0)
+ /* All the remaining fields are part of the option list. */
+ result = CMD_OK;
+ for (size_t i = 1; i < argc; ++i)
+ {
+ value_list_t *tmp;
+
+ char *key = NULL;
+ char *value = NULL;
+
+ status = cmd_parse_option (argv[i], &key, &value, err);
+ if (status == CMD_OK)
{
- assert (string != NULL);
+ assert (key != NULL);
assert (value != NULL);
- set_option (&vl, string, value);
+ set_option (&vl, key, value);
continue;
}
- /* else: parse_option but buffer has not been modified. This is
- * the default if no `=' is found.. */
+ else if (status != CMD_NO_OPTION)
+ {
+ /* parse_option failed, buffer has been modified.
+ * => we need to abort */
+ result = status;
+ break;
+ }
+ /* else: cmd_parse_option did not find an option; treat this as a
+ * value list. */
- status = parse_string (&buffer, &string);
+ status = parse_values (argv[i], &vl, ds);
if (status != 0)
{
- print_to_socket (fh, "-1 Misformatted value.\n");
- sfree (vl.values);
- return (-1);
+ cmd_error (CMD_PARSE_ERROR, err, "Parsing the values string failed.");
+ result = CMD_PARSE_ERROR;
+ break;
}
- assert (string != NULL);
- status = parse_values (string, &vl, ds);
- if (status != 0)
+ tmp = (value_list_t *) realloc (ret_putval->vl,
+ (ret_putval->vl_num + 1) * sizeof(*ret_putval->vl));
+ if (tmp == NULL)
{
- print_to_socket (fh, "-1 Parsing the values string failed.\n");
- sfree (vl.values);
- return (-1);
+ cmd_error (CMD_ERROR, err, "realloc failed.");
+ cmd_destroy_putval (ret_putval);
+ result = CMD_ERROR;
+ break;
}
- plugin_dispatch_values (&vl);
- values_submitted++;
+ ret_putval->vl = tmp;
+ ret_putval->vl_num++;
+ memcpy (&ret_putval->vl[ret_putval->vl_num - 1], &vl, sizeof (vl));
} /* while (*buffer != 0) */
/* Done parsing the options. */
- if (fh!=stdout)
- print_to_socket (fh, "0 Success: %i %s been dispatched.\n",
- values_submitted,
- (values_submitted == 1) ? "value has" : "values have");
+ if (result != CMD_OK)
+ {
+ if (ret_putval->vl_num == 0)
+ sfree (vl.values);
+ cmd_destroy_putval (ret_putval);
+ }
+
+ return (result);
+} /* cmd_status_t cmd_parse_putval */
- sfree (vl.values);
- return (0);
-} /* int handle_putval */
+void cmd_destroy_putval (cmd_putval_t *putval)
+{
+ if (putval == NULL)
+ return;
+
+ sfree (putval->raw_identifier);
+
+ for (size_t i = 0; i < putval->vl_num; ++i)
+ {
+ if (i == 0) /* values is shared between all entries */
+ sfree (putval->vl[i].values);
+ meta_data_destroy (putval->vl[i].meta);
+ putval->vl[i].meta = NULL;
+ }
+ sfree (putval->vl);
+ putval->vl = NULL;
+ putval->vl_num = 0;
+} /* void cmd_destroy_putval */
+
+cmd_status_t cmd_handle_putval (FILE *fh, char *buffer)
+{
+ cmd_error_handler_t err = { cmd_error_fh, fh };
+ cmd_t cmd;
+
+ int status;
+
+ DEBUG ("utils_cmd_putval: cmd_handle_putval (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ if ((status = cmd_parse (buffer, &cmd, NULL, &err)) != CMD_OK)
+ return (status);
+ if (cmd.type != CMD_PUTVAL)
+ {
+ cmd_error (CMD_UNKNOWN_COMMAND, &err, "Unexpected command: `%s'.",
+ CMD_TO_STRING (cmd.type));
+ cmd_destroy (&cmd);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ for (size_t i = 0; i < cmd.cmd.putval.vl_num; ++i)
+ plugin_dispatch_values (&cmd.cmd.putval.vl[i]);
+
+ if (fh != stdout)
+ cmd_error (CMD_OK, &err, "Success: %i %s been dispatched.",
+ (int)cmd.cmd.putval.vl_num,
+ (cmd.cmd.putval.vl_num == 1) ? "value has" : "values have");
+
+ cmd_destroy (&cmd);
+ return (CMD_OK);
+} /* int cmd_handle_putval */
-int create_putval (char *ret, size_t ret_len, /* {{{ */
+int cmd_create_putval (char *ret, size_t ret_len, /* {{{ */
const data_set_t *ds, const value_list_t *vl)
{
char buffer_ident[6 * DATA_MAX_NAME_LEN];
buffer_values);
return (0);
-} /* }}} int create_putval */
+} /* }}} int cmd_create_putval */
#include <stdio.h>
#include "plugin.h"
+#include "utils_cmds.h"
-int handle_putval (FILE *fh, char *buffer);
+cmd_status_t cmd_parse_putval (size_t argc, char **argv,
+ cmd_putval_t *ret_putval, const cmd_options_t *opts,
+ cmd_error_handler_t *err);
-int create_putval (char *ret, size_t ret_len,
+cmd_status_t cmd_handle_putval (FILE *fh, char *buffer);
+
+void cmd_destroy_putval (cmd_putval_t *putval);
+
+int cmd_create_putval (char *ret, size_t ret_len,
const data_set_t *ds, const value_list_t *vl);
#endif /* UTILS_CMD_PUTVAL_H */
--- /dev/null
+/**
+ * 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
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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>
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#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"
+
+#include <stdbool.h>
+#include <string.h>
+
+static cmd_options_t default_options = {
+ /* identifier_default_host = */ NULL,
+};
+
+/*
+ * private helper functions
+ */
+
+static cmd_status_t cmd_split (char *buffer,
+ size_t *ret_len, char ***ret_fields,
+ cmd_error_handler_t *err)
+{
+ char *field;
+ bool in_field, in_quotes;
+
+ size_t estimate, len;
+ char **fields;
+
+ estimate = 0;
+ in_field = false;
+ for (char *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 (char *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
+ */
+
+void cmd_error (cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...)
+{
+ va_list ap;
+
+ if ((err == NULL) || (err->cb == NULL))
+ return;
+
+ va_start (ap, format);
+ err->cb (err->ud, status, format, ap);
+ va_end (ap);
+} /* void cmd_error */
+
+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;
+
+ if ((argc < 1) || (argv == NULL) || (ret_cmd == NULL))
+ {
+ errno = EINVAL;
+ cmd_error (CMD_ERROR, err, "Missing command.");
+ 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, 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, 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, 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, opts, err);
+ }
+ else
+ {
+ ret_cmd->type = CMD_UNKNOWN;
+ cmd_error (CMD_UNKNOWN_COMMAND, err,
+ "Unknown command `%s'.", command);
+ return (CMD_UNKNOWN_COMMAND);
+ }
+
+ if (status != CMD_OK)
+ ret_cmd->type = CMD_UNKNOWN;
+ return (status);
+} /* cmd_status_t cmd_parsev */
+
+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;
+ 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, opts, err);
+ free (fields);
+ return (status);
+} /* cmd_status_t cmd_parse */
+
+void cmd_destroy (cmd_t *cmd)
+{
+ if (cmd == NULL)
+ return;
+
+ switch (cmd->type)
+ {
+ 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)
+{
+ FILE *fh = ud;
+ int code = -1;
+ char buf[1024];
+
+ if (status == CMD_OK)
+ code = 0;
+
+ vsnprintf (buf, sizeof(buf), format, ap);
+ buf[sizeof (buf) - 1] = '\0';
+ if (fprintf (fh, "%i %s\n", code, buf) < 0)
+ {
+ char errbuf[1024];
+ WARNING ("utils_cmds: failed to write to file-handle #%i: %s",
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf)));
+ return;
+ }
+
+ fflush (fh);
+} /* void cmd_error_fh */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
--- /dev/null
+/**
+ * collectd - src/utils_cmds.h
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#ifndef UTILS_CMDS_H
+#define UTILS_CMDS_H 1
+
+#include "plugin.h"
+
+#include <stdarg.h>
+
+typedef enum {
+ CMD_UNKNOWN = 0,
+ CMD_FLUSH = 1,
+ CMD_GETVAL = 2,
+ CMD_LISTVAL = 3,
+ CMD_PUTVAL = 4,
+} cmd_type_t;
+#define CMD_TO_STRING(type) \
+ ((type) == CMD_FLUSH) ? "FLUSH" \
+ : ((type) == CMD_GETVAL) ? "GETVAL" \
+ : ((type) == CMD_LISTVAL) ? "LISTVAL" \
+ : ((type) == CMD_PUTVAL) ? "PUTVAL" \
+ : "UNKNOWN"
+
+typedef struct {
+ double timeout;
+
+ char **plugins;
+ size_t plugins_num;
+ identifier_t *identifiers;
+ size_t identifiers_num;
+} cmd_flush_t;
+
+typedef struct {
+ char *raw_identifier;
+ identifier_t identifier;
+} cmd_getval_t;
+
+typedef struct {
+} cmd_listval_t;
+
+typedef struct {
+ /* The raw identifier as provided by the user. */
+ char *raw_identifier;
+
+ /* An array of the fully parsed identifier and all value lists, and their
+ * options as provided by the user. */
+ value_list_t *vl;
+ size_t vl_num;
+} cmd_putval_t;
+
+/*
+ * NAME
+ * cmd_t
+ *
+ * DESCRIPTION
+ * The representation of a fully parsed command.
+ */
+typedef struct {
+ cmd_type_t type;
+ union {
+ cmd_flush_t flush;
+ cmd_getval_t getval;
+ cmd_listval_t listval;
+ cmd_putval_t putval;
+ } cmd;
+} cmd_t;
+
+/*
+ * 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
+ * Status codes describing the parse result.
+ */
+typedef enum {
+ CMD_OK = 0,
+ CMD_ERROR = -1,
+ CMD_PARSE_ERROR = -2,
+ CMD_UNKNOWN_COMMAND = -3,
+
+ /* Not necessarily fatal errors. */
+ CMD_NO_OPTION = 1,
+} cmd_status_t;
+
+/*
+ * NAME
+ * cmd_error_handler_t
+ *
+ * DESCRIPTION
+ * An error handler describes a callback to be invoked when the parser
+ * encounters an error. The user data pointer will be passed to the callback
+ * as the first argument.
+ */
+typedef struct {
+ void (*cb) (void *, cmd_status_t, const char *, va_list);
+ void *ud;
+} cmd_error_handler_t;
+
+/*
+ * NAME:
+ * cmd_error
+ *
+ * DESCRIPTION
+ * Reports an error via the specified error handler (if set).
+ */
+void cmd_error (cmd_status_t status, cmd_error_handler_t *err,
+ const char *format, ...);
+
+/*
+ * NAME
+ * cmd_parse
+ *
+ * DESCRIPTION
+ * Parse a command string and populate a command object.
+ *
+ * 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,
+ const cmd_options_t *opts, 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);
+
+/*
+ * NAME
+ * cmd_parse_option
+ *
+ * DESCRIPTION
+ * Parses a command option which must be of the form:
+ * name=value with \ and spaces
+ *
+ * PARAMETERS
+ * `field' The parsed input field with any quotes removed and special
+ * characters unescaped.
+ * `ret_key' The parsed key will be stored at this location.
+ * `ret_value' The parsed value will be stored at this location.
+ *
+ * RETURN VALUE
+ * CMD_OK on success or an error code otherwise.
+ * CMD_NO_OPTION if `field' does not represent an option at all (missing
+ * equal sign).
+ */
+cmd_status_t cmd_parse_option (char *field,
+ char **ret_key, char **ret_value, cmd_error_handler_t *err);
+
+/*
+ * NAME
+ * cmd_error_fh
+ *
+ * DESCRIPTION
+ * An error callback writing the message to an open file handle using the
+ * format expected by the unixsock or exec plugins.
+ *
+ * PARAMETERS
+ * `ud' Error handler user-data pointer. This must be an open
+ * file-handle (FILE *).
+ * `status' The error status code.
+ * `format' Printf-style format string.
+ * `ap' Variable argument list providing the arguments for the format
+ * string.
+ */
+void cmd_error_fh (void *ud, cmd_status_t status,
+ const char *format, va_list ap);
+
+#endif /* UTILS_CMDS_H */
--- /dev/null
+/**
+ * collectd - src/tests/utils_cmds_test.c
+ * Copyright (C) 2016 Sebastian 'tokkee' Harl
+ *
+ * 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:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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:
+ * Sebastian 'tokkee' Harl <sh at tokkee.org>
+ **/
+
+#include "common.h"
+#include "testing.h"
+#include "utils_cmds.h"
+
+static void error_cb (void *ud, cmd_status_t status,
+ const char *format, va_list ap)
+{
+ if (status == CMD_OK)
+ return;
+
+ printf ("ERROR[%d]: ", status);
+ vprintf (format, ap);
+ printf ("\n");
+ fflush (stdout);
+} /* void error_cb */
+
+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,
+ },
+
+ /* 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,
+ },
+
+ /* Valid LISTVAL commands. */
+ {
+ "LISTVAL",
+ NULL,
+ CMD_OK,
+ CMD_LISTVAL,
+ },
+
+ /* 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,
+ },
+ /*
+ * As of collectd 5.x, PUTVAL accepts invalid options.
+ {
+ "PUTVAL myhost/magic/MAGIC invalid=2 1234:42",
+ NULL,
+ CMD_PARSE_ERROR,
+ CMD_UNKNOWN,
+ },
+ */
+
+ /* Invalid commands. */
+ {
+ "INVALID",
+ NULL,
+ CMD_UNKNOWN_COMMAND,
+ CMD_UNKNOWN,
+ },
+ {
+ "INVALID interval=2",
+ NULL,
+ CMD_UNKNOWN_COMMAND,
+ CMD_UNKNOWN,
+ },
+};
+
+DEF_TEST(parse)
+{
+ cmd_error_handler_t err = { error_cb, NULL };
+ int test_result = 0;
+
+ for (size_t i = 0; i < STATIC_ARRAY_SIZE (parse_data); i++) {
+ char *input = strdup (parse_data[i].input);
+
+ char description[1024];
+ cmd_status_t status;
+ cmd_t cmd;
+
+ _Bool result;
+
+ memset (&cmd, 0, sizeof (cmd));
+
+ status = cmd_parse (input, &cmd, parse_data[i].opts, &err);
+ snprintf (description, sizeof (description),
+ "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,
+ CMD_TO_STRING (parse_data[i].expected_type));
+ result = (status == parse_data[i].expected_status)
+ && (cmd.type == parse_data[i].expected_type);
+ LOG (result, description);
+
+ /* Run all tests before failing. */
+ if (! result)
+ test_result = -1;
+
+ cmd_destroy (&cmd);
+ free (input);
+ }
+
+ return (test_result);
+}
+
+int main (int argc, char **argv)
+{
+ RUN_TEST(parse);
+ END_TEST;
+}
struct user_obj_s
{
void *user_obj;
- identifier_t ident;
+ lookup_identifier_t ident;
user_obj_t *next;
};
} /* }}} int lu_copy_ident_to_match_part */
static int lu_copy_ident_to_match (identifier_match_t *match, /* {{{ */
- identifier_t const *ident, unsigned int group_by)
+ lookup_identifier_t const *ident, unsigned int group_by)
{
memset (match, 0, sizeof (*match));
} /* }}} void lookup_destroy */
int lookup_add (lookup_t *obj, /* {{{ */
- identifier_t const *ident, unsigned int group_by, void *user_class)
+ lookup_identifier_t const *ident, unsigned int group_by, void *user_class)
{
by_type_entry_t *by_type = NULL;
user_class_list_t *user_class_obj;
* freed. */
typedef void (*lookup_free_obj_callback_t) (void *user_obj);
-struct identifier_s
+struct lookup_identifier_s
{
char host[DATA_MAX_NAME_LEN];
char plugin[DATA_MAX_NAME_LEN];
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
};
-typedef struct identifier_s identifier_t;
+typedef struct lookup_identifier_s lookup_identifier_t;
#define LU_GROUP_BY_HOST 0x01
#define LU_GROUP_BY_PLUGIN 0x02
void lookup_destroy (lookup_t *obj);
int lookup_add (lookup_t *obj,
- identifier_t const *ident, unsigned int group_by, void *user_class);
+ lookup_identifier_t const *ident, unsigned int group_by, void *user_class);
/* TODO(octo): Pass lookup_obj_callback_t to lookup_search()? */
int lookup_search (lookup_t *obj,
static _Bool expect_new_obj = 0;
static _Bool have_new_obj = 0;
-static identifier_t last_class_ident;
-static identifier_t last_obj_ident;
+static lookup_identifier_t last_class_ident;
+static lookup_identifier_t last_obj_ident;
static data_source_t dsrc_test = { "value", DS_TYPE_DERIVE, 0.0, NAN };
static data_set_t const ds_test = { "test", 1, &dsrc_test };
value_list_t const *vl,
void *user_class, void *user_obj)
{
- identifier_t *class = user_class;
- identifier_t *obj = user_obj;
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj = user_obj;
OK1(expect_new_obj == have_new_obj,
(expect_new_obj ? "New obj is created." : "Updating existing obj."));
static void *lookup_class_callback (data_set_t const *ds,
value_list_t const *vl, void *user_class)
{
- identifier_t *class = user_class;
- identifier_t *obj;
+ lookup_identifier_t *class = user_class;
+ lookup_identifier_t *obj;
assert (expect_new_obj);
char const *type, char const *type_instance,
unsigned int group_by)
{
- identifier_t ident;
+ lookup_identifier_t ident;
void *user_class;
strncpy (ident.host, host, sizeof (ident.host));
Memcheck:Addr4
fun:parse_value
fun:parse_values
- fun:test_parse_values
+ ...
fun:main
}
switch (ctx->format) {
case KAFKA_FORMAT_COMMAND:
- status = create_putval(buffer, sizeof(buffer), ds, vl);
+ status = cmd_create_putval(buffer, sizeof(buffer), ds, vl);
if (status != 0) {
- ERROR("write_kafka plugin: create_putval failed with status %i.",
+ ERROR("write_kafka plugin: cmd_create_putval failed with status %i.",
status);
return status;
}