]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
cmds: Implement the "PUTMETRIC" command.
authorFlorian Forster <octo@google.com>
Wed, 15 Jul 2020 12:01:18 +0000 (14:01 +0200)
committerFlorian Forster <octo@google.com>
Wed, 29 Jul 2020 11:40:54 +0000 (13:40 +0200)
Makefile.am
src/daemon/plugin_mock.c
src/daemon/utils_cache_mock.c
src/utils/cmds/cmds.c
src/utils/cmds/cmds.h
src/utils/cmds/cmds_test.c
src/utils/cmds/putmetric.c [new file with mode: 0644]
src/utils/cmds/putmetric.h [new file with mode: 0644]
src/utils/cmds/putval.c

index e55b03223b99fbe29d3302ebc2424e17b131d0ca..1e906133b5bccdc5f70d7333fa0a4c0faf2b0991 100644 (file)
@@ -552,6 +552,8 @@ libcmds_la_SOURCES = \
        src/utils/cmds/getval.h \
        src/utils/cmds/listval.c \
        src/utils/cmds/listval.h \
+       src/utils/cmds/putmetric.c \
+       src/utils/cmds/putmetric.h \
        src/utils/cmds/putnotif.c \
        src/utils/cmds/putnotif.h \
        src/utils/cmds/putval.c \
@@ -565,6 +567,7 @@ test_utils_cmds_SOURCES = \
        src/testing.h
 test_utils_cmds_LDADD = \
        libcmds.la \
+       libmetric.la \
        libplugin_mock.la
 
 liblookup_la_SOURCES = \
index 788503fc63c33a1c469452dca65b7ab55af3a31d..d8a1b67bff505f0b9a09b904cc1de1f17c2dfa26 100644 (file)
@@ -121,6 +121,8 @@ DECLARE_UNREGISTER(notification)
 
 int plugin_dispatch_values(value_list_t const *vl) { return ENOTSUP; }
 
+int plugin_dispatch_metric_family(metric_family_t const *fam) { return ENOTSUP; }
+
 int plugin_dispatch_notification(__attribute__((unused))
                                  const notification_t *notif) {
   return ENOTSUP;
index d984c7e9ba4d40fd1f712ddd6c91761b40adaf00..e73ac2847af78ab6cd50bc722152f95260133966 100644 (file)
@@ -40,6 +40,10 @@ int uc_get_rate(__attribute__((unused)) metric_t const *m,
   return ENOTSUP;
 }
 
+int uc_get_rate_by_name(const char *name, gauge_t *ret_value) {
+  return ENOTSUP;
+}
+
 int uc_get_rate_by_name_vl(const char *name, gauge_t **ret_values,
                            size_t *ret_values_num) {
   return ENOTSUP;
index 036cefa17de89633d92d5e5677f6c96b88409afa..6994cf2fadd9f4cdbb2f9739048ed9ecb2f4b25b 100644 (file)
@@ -32,6 +32,7 @@
 #include "utils/cmds/listval.h"
 #include "utils/cmds/parse_option.h"
 #include "utils/cmds/putval.h"
+#include "utils/cmds/putmetric.h"
 #include "utils/common/common.h"
 
 #include <stdbool.h>
@@ -211,6 +212,10 @@ cmd_status_t cmd_parsev(size_t argc, char **argv, cmd_t *ret_cmd,
     ret_cmd->type = CMD_PUTVAL;
     status =
         cmd_parse_putval(argc - 1, argv + 1, &ret_cmd->cmd.putval, opts, err);
+  } else if (strcasecmp("PUTMETRIC", command) == 0) {
+    ret_cmd->type = CMD_PUTMETRIC;
+    status =
+        cmd_parse_putmetric(argc - 1, argv + 1, &ret_cmd->cmd.putmetric, opts, err);
   } else {
     ret_cmd->type = CMD_UNKNOWN;
     cmd_error(CMD_UNKNOWN_COMMAND, err, "Unknown command `%s'.", command);
@@ -255,6 +260,9 @@ void cmd_destroy(cmd_t *cmd) {
   case CMD_PUTVAL:
     cmd_destroy_putval(&cmd->cmd.putval);
     break;
+  case CMD_PUTMETRIC:
+    cmd_destroy_putmetric(&cmd->cmd.putmetric);
+    break;
   }
 } /* void cmd_destroy */
 
index 1e0e6fccf33fd2901eb14556c19273a03331fe80..b6c9a54ece303343b1f924bfa0ccd10db8f5ad52 100644 (file)
@@ -37,6 +37,7 @@ typedef enum {
   CMD_GETVAL = 2,
   CMD_LISTVAL = 3,
   CMD_PUTVAL = 4,
+  CMD_PUTMETRIC = 5,
 } cmd_type_t;
 #define CMD_TO_STRING(type)                                                    \
   ((type) == CMD_FLUSH)                                                        \
@@ -45,7 +46,8 @@ typedef enum {
             ? "GETVAL"                                                         \
             : ((type) == CMD_LISTVAL)                                          \
                   ? "LISTVAL"                                                  \
-                  : ((type) == CMD_PUTVAL) ? "PUTVAL" : "UNKNOWN"
+                  : ((type) == CMD_PUTVAL) ? "PUTVAL" \
+                 : ((type) == CMD_PUTMETRIC) ? "PUTMETRIC" : "UNKNOWN"
 
 typedef struct {
   double timeout;
@@ -77,6 +79,19 @@ typedef struct {
   metric_family_t *family;
 } cmd_putval_t;
 
+typedef struct {
+  /* Depending on the function, this is an input or output field:
+   *
+   * cmd_parse_putmetric:
+   *   OUTPUT  Receives parsed metric information. The metric family will
+   *           contain a single metric.
+   * cmd_create_putmetric:
+   *   INPUT   Holds the metrics for which to format the PUTVAL command. The
+   *           metric family may contain multiple metrics.
+   */
+  metric_family_t *family;
+} cmd_putmetric_t;
+
 /*
  * NAME
  *   cmd_t
@@ -90,6 +105,7 @@ typedef struct {
     cmd_flush_t flush;
     cmd_getval_t getval;
     cmd_putval_t putval;
+    cmd_putmetric_t putmetric;
   } cmd;
 } cmd_t;
 
index 9e7882031f509fd62dd946992e793ae225458da3..d95fd59ab0e45292a8c6228834d2f03292f37517 100644 (file)
  * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
  * See Github Issue #3193 for details
  */
+#include "utils/cmds/cmds.h"
+
 #include "utils/common/common.h"
+#include "utils/strbuf/strbuf.h"
 #include "testing.h"
-#include "utils/cmds/cmds.h"
+#include "utils/cmds/putmetric.h"
 // clang-format on
 
 static void error_cb(void *ud, cmd_status_t status, const char *format,
@@ -39,10 +42,20 @@ static void error_cb(void *ud, cmd_status_t status, const char *format,
   if (status == CMD_OK)
     return;
 
-  printf("ERROR[%d]: ", status);
-  vprintf(format, ap);
-  printf("\n");
-  fflush(stdout);
+  strbuf_t *buf = ud;
+
+  strbuf_printf(buf, "ERROR[%d]: ", status);
+
+  va_list ap_copy;
+  va_copy(ap_copy, ap);
+
+  int size = vsnprintf(NULL, 0, format, ap_copy);
+  assert(size > 0);
+
+  char buffer[size];
+  vsnprintf(buffer, sizeof(buffer), format, ap);
+
+  strbuf_print(buf, buffer);
 } /* void error_cb */
 
 static cmd_options_t default_host_opts = {
@@ -168,6 +181,12 @@ static struct {
     },
 
     /* Valid PUTVAL commands. */
+    {
+        "PUTVAL unit_test N:42",
+        &default_host_opts,
+        CMD_OK,
+        CMD_PUTVAL,
+    },
     {
         "PUTVAL magic/MAGIC N:42",
         &default_host_opts,
@@ -276,6 +295,68 @@ static struct {
     },
     */
 
+    /* Valid PUTMETRIC commands. */
+    {
+        "PUTMETRIC unit_test 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC gauge type=GAUGE 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC counter type=Counter 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC untyped type=untyped 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC quoted_gauge type=\"GAUGE\" 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC with_interval interval=10.0 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC with_time time=1594806526 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC with_label label:unquoted=bare 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC with_label label:quoted=\"with space\" 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+    {
+        "PUTMETRIC multiple_label label:foo=1 label:bar=2 42",
+        NULL,
+        CMD_OK,
+        CMD_PUTMETRIC,
+    },
+
     /* Invalid commands. */
     {
         "INVALID",
@@ -292,21 +373,18 @@ static struct {
 };
 
 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;
+    strbuf_t errbuf = STRBUF_CREATE;
+    cmd_error_handler_t err = {error_cb, &errbuf};
 
-    memset(&cmd, 0, sizeof(cmd));
+    cmd_t cmd = {0};
+    cmd_status_t status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
 
-    status = cmd_parse(input, &cmd, parse_data[i].opts, &err);
+    char description[1024];
     ssnprintf(description, sizeof(description),
               "cmd_parse (\"%s\", opts=%p) = "
               "%d (type=%d [%s]); want %d "
@@ -315,8 +393,14 @@ DEF_TEST(parse) {
               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);
+
+    bool result = (status == parse_data[i].expected_status) &&
+                  (cmd.type == parse_data[i].expected_type);
+
+    if (errbuf.ptr != NULL) {
+      printf("error buffer = \"%s\"\n", errbuf.ptr);
+    }
+
     LOG(result, description);
 
     /* Run all tests before failing. */
@@ -330,7 +414,113 @@ DEF_TEST(parse) {
   return test_result;
 }
 
+DEF_TEST(format_putmetric) {
+  struct {
+    metric_t m;
+    char *want;
+    int want_err;
+  } cases[] = {
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_UNTYPED,
+                      },
+                  .value.gauge = 42,
+              },
+          .want = "PUTMETRIC test 42",
+      },
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_GAUGE,
+                      },
+                  .value.gauge = 42,
+              },
+          .want = "PUTMETRIC test type=GAUGE 42",
+      },
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_COUNTER,
+                      },
+                  .value.counter = 42,
+              },
+          .want = "PUTMETRIC test type=COUNTER 42",
+      },
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_UNTYPED,
+                      },
+                  .value.gauge = 42,
+                  .time = TIME_T_TO_CDTIME_T(1594809888),
+              },
+          .want = "PUTMETRIC test time=1594809888.000 42",
+      },
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_UNTYPED,
+                      },
+                  .value.gauge = 42,
+                  .interval = TIME_T_TO_CDTIME_T(10),
+              },
+          .want = "PUTMETRIC test interval=10.000 42",
+      },
+      {
+          .m =
+              {
+                  .family =
+                      &(metric_family_t){
+                          .name = "test",
+                          .type = METRIC_TYPE_UNTYPED,
+                      },
+                  .value.gauge = 42,
+                  .label.ptr =
+                      &(label_pair_t){
+                          .name = "foo",
+                          .value = "with \"quotes\"",
+                      },
+                  .label.num = 1,
+              },
+          .want = "PUTMETRIC test label:foo=\"with \\\"quotes\\\"\" 42",
+      },
+  };
+
+  for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) {
+    strbuf_t buf = STRBUF_CREATE;
+
+    EXPECT_EQ_INT(cases[i].want_err, cmd_format_putmetric(&buf, &cases[i].m));
+    if (cases[i].want_err) {
+      STRBUF_DESTROY(buf);
+      continue;
+    }
+
+    EXPECT_EQ_STR(cases[i].want, buf.ptr);
+
+    STRBUF_DESTROY(buf);
+  }
+
+  return 0;
+}
+
 int main(int argc, char **argv) {
   RUN_TEST(parse);
+  RUN_TEST(format_putmetric);
   END_TEST;
 }
diff --git a/src/utils/cmds/putmetric.c b/src/utils/cmds/putmetric.c
new file mode 100644 (file)
index 0000000..d572729
--- /dev/null
@@ -0,0 +1,278 @@
+/**
+ * collectd - src/utils_cmd_putmetric.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"),
+ * 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 "collectd.h"
+
+#include "utils/cmds/putmetric.h"
+#include "utils/common/common.h"
+
+/*
+ * private helper functions
+ */
+
+/* TODO(octo): add an option to set metric->value_type */
+static int set_option(metric_t *m, char const *key, char const *value,
+                      cmd_error_handler_t *err) {
+  if ((m == NULL) || (key == NULL) || (value == NULL))
+    return -1;
+
+  printf("set_option(\"%s\", \"%s\")\n", key, value);
+
+  if (strcasecmp("type", key) == 0) {
+    if (strcasecmp("GAUGE", value) == 0) {
+      m->family->type = METRIC_TYPE_GAUGE;
+    } else if (strcasecmp("COUNTER", value) == 0) {
+      m->family->type = METRIC_TYPE_COUNTER;
+    } else if (strcasecmp("UNTYPED", value) == 0) {
+      m->family->type = METRIC_TYPE_UNTYPED;
+    } else {
+      return CMD_ERROR;
+    }
+  } else if (strcasecmp("interval", key) == 0) {
+    errno = 0;
+    char *endptr = NULL;
+    double d = strtod(value, &endptr);
+
+    if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (d < 0)) {
+      return CMD_ERROR;
+    }
+    m->interval = DOUBLE_TO_CDTIME_T(d);
+  } else if (strcasecmp("time", key) == 0) {
+    errno = 0;
+    char *endptr = NULL;
+    double d = strtod(value, &endptr);
+
+    if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (d < 0)) {
+      return CMD_ERROR;
+    }
+    m->time = DOUBLE_TO_CDTIME_T(d);
+  } else if (strncasecmp("label:", key, 5) == 0) {
+    char const *name = key + strlen("label:");
+    return metric_label_set(m, name, value) ? CMD_ERROR : CMD_OK;
+  } else {
+    return CMD_ERROR;
+  }
+  return CMD_OK;
+} /* int set_option */
+
+/*
+ * public API
+ */
+
+cmd_status_t cmd_parse_putmetric(size_t argc, char **argv,
+                                 cmd_putmetric_t *ret_putmetric,
+                                 __attribute__((unused))
+                                 cmd_options_t const *opts,
+                                 cmd_error_handler_t *errhndl) {
+  if ((argc < 2) || (argv == NULL) || (ret_putmetric == NULL)) {
+    errno = EINVAL;
+    cmd_error(CMD_ERROR, errhndl, "Invalid arguments to cmd_parse_putmetric.");
+    return CMD_ERROR;
+  }
+
+  if (argc < 2) {
+    cmd_error(CMD_PARSE_ERROR, errhndl,
+              "Missing identifier and/or value-list.");
+    return CMD_PARSE_ERROR;
+  }
+
+  metric_family_t *fam = calloc(1, sizeof(*fam));
+  if (fam == NULL) {
+    cmd_error(CMD_ERROR, errhndl, "calloc failed");
+    return CMD_ERROR;
+  }
+  fam->type = METRIC_TYPE_UNTYPED;
+
+  int status = metric_family_metric_append(fam, (metric_t){0});
+  if (status != 0) {
+    return CMD_ERROR;
+  }
+  metric_t *m = fam->metric.ptr;
+
+  int next_pos = 0;
+  cmd_status_t result = CMD_OK;
+  for (size_t i = 0; i < argc; ++i) {
+    char *key = NULL;
+    char *value = NULL;
+
+    int status = cmd_parse_option(argv[i], &key, &value, errhndl);
+    if (status == CMD_OK) {
+      assert(key != NULL);
+      assert(value != NULL);
+
+      result = set_option(m, key, value, errhndl);
+      if (result != CMD_OK) {
+        break;
+      }
+      continue;
+    } else if (status == CMD_NO_OPTION) {
+      /* Positional argument */
+      if (next_pos == 0) {
+        fam->name = strdup(argv[i]);
+        if (fam->name == NULL) {
+          cmd_error(CMD_ERROR, errhndl, "calloc failed");
+          result = CMD_ERROR;
+          break;
+        }
+        next_pos++;
+        continue;
+      } else if (next_pos == 1) {
+        int status = parse_value(argv[i], &m->value, fam->type);
+        if (status != 0) {
+          cmd_error(CMD_ERROR, errhndl, "parse_value failed");
+          result = CMD_ERROR;
+          break;
+        }
+        next_pos++;
+        continue;
+      } else {
+        /* error is handled after the loop */
+        next_pos++;
+        continue;
+      }
+    } else {
+      /* parse_option failed, buffer has been modified.
+       * => we need to abort */
+      result = status;
+      break;
+    }
+  }
+
+  if ((result == CMD_OK) && (next_pos != 2)) {
+    char errmsg[256];
+    snprintf(errmsg, sizeof(errmsg),
+             "Found %d positional argument(s), expected 2.", next_pos);
+    cmd_error(CMD_PARSE_ERROR, errhndl, errmsg);
+    result = CMD_ERROR;
+  }
+
+  if (result != CMD_OK) {
+    metric_family_free(fam);
+    return result;
+  }
+
+  *ret_putmetric = (cmd_putmetric_t){
+      .family = fam,
+  };
+  return CMD_OK;
+} /* cmd_status_t cmd_parse_putmetric */
+
+void cmd_destroy_putmetric(cmd_putmetric_t *putmetric) {
+  if (putmetric == NULL)
+    return;
+
+  metric_family_free(putmetric->family);
+
+  (*putmetric) = (cmd_putmetric_t){0};
+} /* void cmd_destroy_putmetric */
+
+cmd_status_t cmd_handle_putmetric(FILE *fh, char *buffer) {
+  cmd_error_handler_t err = {cmd_error_fh, fh};
+
+  DEBUG("utils_cmd_putmetric: cmd_handle_putmetric (fh = %p, buffer = %s);",
+        (void *)fh, buffer);
+
+  cmd_t cmd = {0};
+  int status;
+  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;
+  }
+
+  status = plugin_dispatch_metric_family(cmd.cmd.putmetric.family);
+  if (status != 0) {
+    cmd_error(CMD_ERROR, &err,
+              "plugin_dispatch_metric_list failed with status %d.", status);
+    cmd_destroy(&cmd);
+    return CMD_ERROR;
+  }
+
+  if (fh != stdout) {
+    cmd_putmetric_t *putmetric = &cmd.cmd.putmetric;
+    size_t n = putmetric->family->metric.num;
+    cmd_error(CMD_OK, &err, "Success: %zu %s been dispatched.", n,
+              (n == 1) ? "metric has" : "metrics have");
+  }
+
+  cmd_destroy(&cmd);
+  return CMD_OK;
+} /* int cmd_handle_putmetric */
+
+/* TODO(octo): Improve the readability of the command.
+ *
+ * Currently, this assumes lines similar to:
+ *
+ *   PUTVAL "metric_name{key=\"value\"}" interval=10.000 42
+ *
+ * Encoding the labels in this way generates a lot of escaped quotes, which is
+ * not ideal. An alternative representation would be:
+ *
+ *   PUTVAL metric_name label:key="value" interval=10.000 42
+ */
+int cmd_format_putmetric(strbuf_t *buf, metric_t const *m) { /* {{{ */
+  if ((buf == NULL) || (m == NULL)) {
+    return EINVAL;
+  }
+
+  strbuf_print(buf, "PUTMETRIC ");
+  strbuf_print(buf, m->family->name);
+  switch (m->family->type) {
+  case METRIC_TYPE_UNTYPED:
+    /* no op */
+    break;
+  case METRIC_TYPE_COUNTER:
+    strbuf_print(buf, " type=COUNTER");
+    break;
+  case METRIC_TYPE_GAUGE:
+    strbuf_print(buf, " type=GAUGE");
+    break;
+  default:
+    return EINVAL;
+  }
+
+  if (m->time != 0) {
+    strbuf_printf(buf, " time=%.3f", CDTIME_T_TO_DOUBLE(m->time));
+  }
+  if (m->interval != 0) {
+    strbuf_printf(buf, " interval=%.3f", CDTIME_T_TO_DOUBLE(m->interval));
+  }
+
+  for (size_t i = 0; i < m->label.num; i++) {
+    label_pair_t *l = m->label.ptr + i;
+    strbuf_printf(buf, " label:%s=\"", l->name);
+    strbuf_print_escaped(buf, l->value, "\\\"\n\r\t", '\\');
+    strbuf_print(buf, "\"");
+  }
+
+  strbuf_print(buf, " ");
+  return value_marshal_text(buf, m->value, m->family->type);
+} /* }}} int cmd_format_putmetric */
diff --git a/src/utils/cmds/putmetric.h b/src/utils/cmds/putmetric.h
new file mode 100644 (file)
index 0000000..150a0ae
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * collectd - src/utils_cmd_putval.h
+ * Copyright (C) 2007–2020  Florian octo Forster
+ *
+ * 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>
+ **/
+
+#ifndef UTILS_CMD_PUTMETRIC_H
+#define UTILS_CMD_PUTMETRIC_H 1
+
+#include "plugin.h"
+#include "utils/cmds/cmds.h"
+
+#include <stdio.h>
+
+cmd_status_t cmd_parse_putmetric(size_t argc, char **argv,
+                                 cmd_putmetric_t *ret_putmetric,
+                                 const cmd_options_t *opts,
+                                 cmd_error_handler_t *err);
+
+cmd_status_t cmd_handle_putmetric(FILE *fh, char *buffer);
+
+void cmd_destroy_putmetric(cmd_putmetric_t *putmetric);
+
+int cmd_format_putmetric(strbuf_t *buf, metric_t const *m);
+
+#endif /* UTILS_CMD_PUTMETRIC_H */
index a27e6aec4379853f38b642418ffcd13e45170b80..61dc8fda584815912a4dfec41f15a046aa35a968 100644 (file)
@@ -114,7 +114,7 @@ cmd_status_t cmd_parse_putval(size_t argc, char **argv,
   }
 
   char *identifier = strdup(argv[0]);
-  if (ret_putval->raw_identifier == NULL) {
+  if (identifier == NULL) {
     cmd_error(CMD_ERROR, errhndl, "malloc failed.");
     return CMD_ERROR;
   }