# include "config.h"
#endif
-#include "libcollectdclient/client.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
#include <assert.h>
-
#include <errno.h>
-#include <getopt.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <unistd.h>
+#if NAN_STATIC_DEFAULT
+# include <math.h>
+/* #endif NAN_STATIC_DEFAULT*/
+#elif NAN_STATIC_ISOC
+# ifndef __USE_ISOC99
+# define DISABLE_ISOC99 1
+# define __USE_ISOC99 1
+# endif /* !defined(__USE_ISOC99) */
+# include <math.h>
+# if DISABLE_ISOC99
+# undef DISABLE_ISOC99
+# undef __USE_ISOC99
+# endif /* DISABLE_ISOC99 */
+/* #endif NAN_STATIC_ISOC */
+#elif NAN_ZERO_ZERO
+# include <math.h>
+# ifdef NAN
+# undef NAN
+# endif
+# define NAN (0.0 / 0.0)
+# ifndef isnan
+# define isnan(f) ((f) != (f))
+# endif /* !defined(isnan) */
+# ifndef isfinite
+# define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+# define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
+#endif /* NAN_ZERO_ZERO */
+
+#include "libcollectdclient/collectd/client.h"
#define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
" * getval <identifier>\n"
" * flush [timeout=<seconds>] [plugin=<name>] [identifier=<id>]\n"
" * listval\n"
+ " * putval <identifier> [interval=<seconds>] <value-list(s)>\n"
"\nIdentifiers:\n\n"
"Hostname defaults to the local hostname if omitted (e.g., uptime/uptime).\n"
"No error is returned if the specified identifier does not exist.\n"
- "\nExample:\n\n"
-
- " collectdctl flush plugin=rrdtool identifie=somehost/cpu-0/cpu-wait\n\n"
-
- "Flushes all CPU wait RRD values of the first CPU of the local host.\n"
- "I.e., writes all pending RRD updates of that data-source to disk.\n"
-
"\n"PACKAGE" "VERSION", http://collectd.org/\n"
"by Florian octo Forster <octo@verplant.org>\n"
"for contributions see `AUTHORS'\n"
return count;
} /* count_chars */
+static int array_grow (void **array, int *array_len, size_t elem_size)
+{
+ void *tmp;
+
+ assert ((array != NULL) && (array_len != NULL));
+
+ tmp = realloc (*array, (*array_len + 1) * elem_size);
+ if (tmp == NULL) {
+ fprintf (stderr, "ERROR: Failed to allocate memory.\n");
+ return (-1);
+ }
+
+ *array = tmp;
+ ++(*array_len);
+ return (0);
+} /* array_grow */
+
static int parse_identifier (lcc_connection_t *c,
const char *value, lcc_identifier_t *ident)
{
for (i = 0; i < ret_values_num; ++i)
printf ("%s=%e\n", ret_values_names[i], ret_values[i]);
BAIL_OUT (0);
+#undef BAIL_OUT
} /* getval */
static int flush (lcc_connection_t *c, int argc, char **argv)
{
- lcc_identifier_t ident;
- lcc_identifier_t *identp = NULL;
+ int timeout = -1;
- char *plugin = NULL;
- int timeout = -1;
+ lcc_identifier_t *identifiers = NULL;
+ int identifiers_num = 0;
+
+ char **plugins = NULL;
+ int plugins_num = 0;
int status;
int i;
assert (strcasecmp (argv[0], "flush") == 0);
+#define BAIL_OUT(s) \
+ do { \
+ if (identifiers != NULL) \
+ free (identifiers); \
+ identifiers_num = 0; \
+ if (plugins != NULL) \
+ free (plugins); \
+ plugins_num = 0; \
+ return (s); \
+ } while (0)
+
for (i = 1; i < argc; ++i) {
char *key, *value;
if (! value) {
fprintf (stderr, "ERROR: flush: Invalid option ``%s''.\n", argv[i]);
- return (-1);
+ BAIL_OUT (-1);
}
*value = '\0';
if (strcasecmp (key, "timeout") == 0) {
char *endptr = NULL;
- timeout = strtol (value, &endptr, 0);
+ timeout = (int) strtol (value, &endptr, 0);
if (endptr == value) {
fprintf (stderr, "ERROR: Failed to parse timeout as number: %s.\n",
value);
- return (-1);
+ BAIL_OUT (-1);
}
else if ((endptr != NULL) && (*endptr != '\0')) {
fprintf (stderr, "WARNING: Ignoring trailing garbage after timeout: "
}
}
else if (strcasecmp (key, "plugin") == 0) {
- plugin = value;
+ status = array_grow ((void *)&plugins, &plugins_num,
+ sizeof (*plugins));
+ if (status != 0)
+ BAIL_OUT (status);
+
+ plugins[plugins_num - 1] = value;
}
else if (strcasecmp (key, "identifier") == 0) {
- int status;
+ status = array_grow ((void *)&identifiers, &identifiers_num,
+ sizeof (*identifiers));
+ if (status != 0)
+ BAIL_OUT (status);
- memset (&ident, 0, sizeof (ident));
- status = parse_identifier (c, value, &ident);
+ memset (identifiers + (identifiers_num - 1), 0, sizeof (*identifiers));
+ status = parse_identifier (c, value,
+ identifiers + (identifiers_num - 1));
if (status != 0)
- return (status);
- identp = &ident;
+ BAIL_OUT (status);
+ }
+ else {
+ fprintf (stderr, "ERROR: flush: Unknown option `%s'.\n", key);
+ BAIL_OUT (-1);
}
}
- status = lcc_flush (c, plugin, identp, timeout);
- if (status != 0) {
- fprintf (stderr, "ERROR: Flushing failed: %s.\n",
- lcc_strerror (c));
- return (-1);
+ if (plugins_num == 0) {
+ status = array_grow ((void *)&plugins, &plugins_num, sizeof (*plugins));
+ if (status != 0)
+ BAIL_OUT (status);
+
+ assert (plugins_num == 1);
+ plugins[0] = NULL;
}
- return 0;
+ for (i = 0; i < plugins_num; ++i) {
+ if (identifiers_num == 0) {
+ status = lcc_flush (c, plugins[i], NULL, timeout);
+ if (status != 0)
+ fprintf (stderr, "ERROR: Failed to flush plugin `%s': %s.\n",
+ (plugins[i] == NULL) ? "(all)" : plugins[i], lcc_strerror (c));
+ }
+ else {
+ int j;
+
+ for (j = 0; j < identifiers_num; ++j) {
+ status = lcc_flush (c, plugins[i], identifiers + j, timeout);
+ if (status != 0) {
+ char id[1024];
+
+ lcc_identifier_to_string (c, id, sizeof (id), identifiers + j);
+ fprintf (stderr, "ERROR: Failed to flush plugin `%s', "
+ "identifier `%s': %s.\n",
+ (plugins[i] == NULL) ? "(all)" : plugins[i],
+ id, lcc_strerror (c));
+ }
+ }
+ }
+ }
+
+ BAIL_OUT (0);
#undef BAIL_OUT
} /* flush */
#undef BAIL_OUT
} /* listval */
+static int putval (lcc_connection_t *c, int argc, char **argv)
+{
+ lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+
+ /* 64 ought to be enough for anybody ;-) */
+ value_t values[64];
+ int values_types[64];
+ size_t values_len = 0;
+
+ int status;
+ int i;
+
+ assert (strcasecmp (argv[0], "putval") == 0);
+
+ if (argc < 3) {
+ fprintf (stderr, "ERROR: putval: Missing identifier "
+ "and/or value list.\n");
+ return (-1);
+ }
+
+ vl.values = values;
+ vl.values_types = values_types;
+
+ status = parse_identifier (c, argv[1], &vl.identifier);
+ if (status != 0)
+ return (status);
+
+ for (i = 2; i < argc; ++i) {
+ char *tmp;
+
+ tmp = strchr (argv[i], (int)'=');
+
+ if (tmp != NULL) { /* option */
+ char *key = argv[i];
+ char *value = tmp;
+
+ *value = '\0';
+ ++value;
+
+ if (strcasecmp (key, "interval") == 0) {
+ char *endptr;
+
+ vl.interval = strtol (value, &endptr, 0);
+
+ if (endptr == value) {
+ fprintf (stderr, "ERROR: Failed to parse interval as number: %s.\n",
+ value);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "WARNING: Ignoring trailing garbage after "
+ "interval: %s.\n", endptr);
+ }
+ }
+ else {
+ fprintf (stderr, "ERROR: putval: Unknown option `%s'.\n", key);
+ return (-1);
+ }
+ }
+ else { /* value list */
+ char *value;
+
+ tmp = strchr (argv[i], (int)':');
+
+ if (tmp == NULL) {
+ fprintf (stderr, "ERROR: putval: Invalid value list: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+
+ *tmp = '\0';
+ ++tmp;
+
+ if (strcasecmp (argv[i], "N") == 0) {
+ vl.time = 0;
+ }
+ else {
+ char *endptr;
+
+ vl.time = strtol (argv[i], &endptr, 0);
+
+ if (endptr == argv[i]) {
+ fprintf (stderr, "ERROR: Failed to parse time as number: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "ERROR: Garbage after time: %s.\n", endptr);
+ return (-1);
+ }
+ }
+
+ values_len = 0;
+ value = tmp;
+ while (value != 0) {
+ char *dot, *endptr;
+
+ tmp = strchr (value, (int)':');
+
+ if (tmp != NULL) {
+ *tmp = '\0';
+ ++tmp;
+ }
+
+ /* This is a bit of a hack, but parsing types.db just does not make
+ * much sense imho -- the server might have different types defined
+ * anyway. Also, lcc uses the type information for formatting the
+ * number only, so the real meaning does not matter. -tokkee */
+ dot = strchr (value, (int)'.');
+ endptr = NULL;
+ if (strcasecmp (value, "U") == 0) {
+ values[values_len].gauge = NAN;
+ values_types[values_len] = LCC_TYPE_GAUGE;
+ }
+ else if (dot) { /* floating point value */
+ values[values_len].gauge = strtod (value, &endptr);
+ values_types[values_len] = LCC_TYPE_GAUGE;
+ }
+ else { /* integer */
+ values[values_len].counter = strtol (value, &endptr, 0);
+ values_types[values_len] = LCC_TYPE_COUNTER;
+ }
+ ++values_len;
+
+ if (endptr == value) {
+ fprintf (stderr, "ERROR: Failed to parse value as number: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "ERROR: Garbage after value: %s.\n", endptr);
+ return (-1);
+ }
+
+ value = tmp;
+ }
+
+ assert (values_len >= 1);
+ vl.values_len = values_len;
+
+ status = lcc_putval (c, &vl);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+ return (-1);
+ }
+ }
+ }
+
+ if (values_len == 0) {
+ fprintf (stderr, "ERROR: putval: Missing value list(s).\n");
+ return (-1);
+ }
+ return (0);
+} /* putval */
+
int main (int argc, char **argv) {
char address[1024] = "unix:"DEFAULT_SOCK;
status = flush (c, argc - optind, argv + optind);
else if (strcasecmp (argv[optind], "listval") == 0)
status = listval (c, argc - optind, argv + optind);
+ else if (strcasecmp (argv[optind], "putval") == 0)
+ status = putval (c, argc - optind, argv + optind);
else {
fprintf (stderr, "%s: invalid command: %s\n", argv[0], argv[optind]);
return (1);