From: Florian Forster Date: Tue, 21 Jul 2020 14:43:42 +0000 (+0200) Subject: src/daemon/metric.[ch]: Implement metric_parse_identity(). X-Git-Tag: 6.0.0-rc0~145^2~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=80b4014d1ccd2d983d4901ece8cfe2da787a685f;p=thirdparty%2Fcollectd.git src/daemon/metric.[ch]: Implement metric_parse_identity(). --- diff --git a/src/daemon/metric.c b/src/daemon/metric.c index 8f76242ec..b0a37fd10 100644 --- a/src/daemon/metric.c +++ b/src/daemon/metric.c @@ -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; +} diff --git a/src/daemon/metric.h b/src/daemon/metric.h index 4c318ea5a..b3b5f674d 100644 --- a/src/daemon/metric.h +++ b/src/daemon/metric.h @@ -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;