]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
src/daemon/metric.[ch]: Implement metric_parse_identity().
authorFlorian Forster <octo@google.com>
Tue, 21 Jul 2020 14:43:42 +0000 (16:43 +0200)
committerFlorian Forster <octo@google.com>
Tue, 21 Jul 2020 15:29:12 +0000 (17:29 +0200)
src/daemon/metric.c
src/daemon/metric.h

index 8f76242eccd40948afbf2a3cf66ccc01c7a8ec74..b0a37fd103765a03f32097bbb1ee57142ab88333 100644 (file)
@@ -323,3 +323,175 @@ metric_family_t *metric_family_clone(metric_family_t const *fam) {
 
   return ret;
 }
+
+/* parse_label_value reads a label value, unescapes it and prints it to buf. On
+ * success, inout is updated to point to the character just *after* the label
+ * value, i.e. the character *following* the ending quotes - either a comma or
+ * closing curlies. */
+static int parse_label_value(strbuf_t *buf, char const **inout) {
+  char const *ptr = *inout;
+
+  if (ptr[0] != '"') {
+    return EINVAL;
+  }
+  ptr++;
+
+  while (ptr[0] != '"') {
+    size_t valid_len = strcspn(ptr, "\\\"\n");
+    if (valid_len != 0) {
+      strbuf_printn(buf, ptr, valid_len);
+      ptr += valid_len;
+      continue;
+    }
+
+    if ((ptr[0] == 0) || (ptr[0] == '\n')) {
+      return EINVAL;
+    }
+
+    assert(ptr[0] == '\\');
+    if (ptr[1] == 0) {
+      return EINVAL;
+    }
+
+    char tmp[2] = {ptr[1], 0};
+    if (tmp[0] == 'n') {
+      tmp[0] = '\n';
+    } else if (tmp[0] == 'r') {
+      tmp[0] = '\r';
+    } else if (tmp[0] == 't') {
+      tmp[0] = '\t';
+    }
+
+    strbuf_print(buf, tmp);
+
+    ptr += 2;
+  }
+
+  assert(ptr[0] == '"');
+  ptr++;
+  *inout = ptr;
+  return 0;
+}
+
+/* metric_family_unmarshal_identity parses the metric identity and updates
+ * "inout" to point to the first character following the identity. With valid
+ * input, this means that "inout" will then point either to a '\0' (null byte)
+ * or a ' ' (space). */
+static int metric_family_unmarshal_identity(metric_family_t *fam,
+                                            char const **inout) {
+  if ((fam == NULL) || (inout == NULL) || (*inout == NULL)) {
+    return EINVAL;
+  }
+
+  char const *ptr = *inout;
+  size_t name_len = strspn(ptr, VALID_NAME_CHARS);
+  if (name_len == 0) {
+    return EINVAL;
+  }
+
+  char name[name_len + 1];
+  strncpy(name, ptr, name_len);
+  name[name_len] = 0;
+  ptr += name_len;
+
+  fam->name = strdup(name);
+  if (fam->name == NULL) {
+    return ENOMEM;
+  }
+
+  /* metric name without labels */
+  if ((ptr[0] == 0) || (ptr[0] == ' ')) {
+    *inout = ptr;
+    return 0;
+  }
+
+  if (ptr[0] != '{') {
+    return EINVAL;
+  }
+
+  metric_t *m = fam->metric.ptr;
+  int ret = 0;
+  while ((ptr[0] == '{') || (ptr[0] == ',')) {
+    ptr++;
+
+    size_t key_len = strspn(ptr, VALID_LABEL_CHARS);
+    if (key_len == 0) {
+      ret = EINVAL;
+      break;
+    }
+    char key[key_len + 1];
+    strncpy(key, ptr, key_len);
+    key[key_len] = 0;
+    ptr += key_len;
+
+    if (ptr[0] != '=') {
+      ret = EINVAL;
+      break;
+    }
+    ptr++;
+
+    strbuf_t value = STRBUF_CREATE;
+    int status = parse_label_value(&value, &ptr);
+    if (status != 0) {
+      ret = status;
+      STRBUF_DESTROY(value);
+      break;
+    }
+
+    /* one metric is added to the family by metric_family_unmarshal_text. */
+    assert(fam->metric.num >= 1);
+
+    status = label_set_add(&fam->metric.ptr[0].label, key, value.ptr);
+    STRBUF_DESTROY(value);
+    if (status != 0) {
+      ret = status;
+      break;
+    }
+  }
+
+  if (ret != 0) {
+    return ret;
+  }
+
+  if ((ptr[0] != '}') || ((ptr[1] != 0) && (ptr[1] != ' '))) {
+    return EINVAL;
+  }
+
+  *inout = &ptr[1];
+  return 0;
+}
+
+metric_t *metric_parse_identity(char const *buf) {
+  if (buf == NULL) {
+    errno = EINVAL;
+    return NULL;
+  }
+
+  metric_family_t *fam = calloc(1, sizeof(*fam));
+  if (fam == NULL) {
+    return NULL;
+  }
+  fam->type = METRIC_TYPE_UNTYPED;
+
+  int status = metric_list_add(&fam->metric, (metric_t){.family = fam});
+  if (status != 0) {
+    metric_family_free(fam);
+    errno = status;
+    return NULL;
+  }
+
+  status = metric_family_unmarshal_identity(fam, &buf);
+  if (status != 0) {
+    metric_family_free(fam);
+    errno = status;
+    return NULL;
+  }
+
+  if (buf[0] != 0) {
+    metric_family_free(fam);
+    errno = EINVAL;
+    return NULL;
+  }
+
+  return fam->metric.ptr;
+}
index 4c318ea5aebc5b4af413d67f5f5d3e4a688d0c3d..b3b5f674dd867df04e1efab99f19e90718c0607f 100644 (file)
@@ -120,6 +120,11 @@ typedef struct {
  */
 int metric_identity(strbuf_t *buf, metric_t const *m);
 
+/* metric_parse_identity parses "s" and returns a metric with only its identity
+ * set. On error, errno is set and NULL is returned. The returned memory must
+ * be freed by passing m->family to metric_family_free(). */
+metric_t *metric_parse_identity(char const *s);
+
 /* metric_list_t is an unordered list of metrics. */
 typedef struct {
   metric_t *ptr;