X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;f=src%2Fwrite_prometheus.c;h=991415ef08713ddb7939e63fd7d5d196a3d35981;hb=98e38add333bfcb893fcde408ecf3a0c43d99e31;hp=43a60fc980d4ef8f0332e239ae09e4737705870a;hpb=c53c496b6db4e11fd8aed8df9556dad481220196;p=collectd.git diff --git a/src/write_prometheus.c b/src/write_prometheus.c index 43a60fc9..991415ef 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -37,7 +37,7 @@ #include #ifndef PROMETHEUS_DEFAULT_STALENESS_DELTA -#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T(300) +#define PROMETHEUS_DEFAULT_STALENESS_DELTA TIME_T_TO_CDTIME_T_STATIC(300) #endif #define VARINT_UINT32_BYTES 5 @@ -98,6 +98,43 @@ static void format_protobuf(ProtobufCBuffer *buffer) { pthread_mutex_unlock(&metrics_lock); } +static char const *escape_label_value(char *buffer, size_t buffer_size, + char const *value) { + /* shortcut for values that don't need escaping. */ + if (strpbrk(value, "\n\"\\") == NULL) + return value; + + size_t value_len = strlen(value); + size_t buffer_len = 0; + + for (size_t i = 0; i < value_len; i++) { + switch (value[i]) { + case '\n': + case '"': + case '\\': + if ((buffer_size - buffer_len) < 3) { + break; + } + buffer[buffer_len] = '\\'; + buffer[buffer_len + 1] = (value[i] == '\n') ? 'n' : value[i]; + buffer_len += 2; + break; + + default: + if ((buffer_size - buffer_len) < 2) { + break; + } + buffer[buffer_len] = value[i]; + buffer_len++; + break; + } + } + + assert(buffer_len < buffer_size); + buffer[buffer_len] = 0; + return buffer; +} + /* format_labels formats a metric's labels in Prometheus-compatible format. This * format looks like this: * @@ -109,16 +146,22 @@ static char *format_labels(char *buffer, size_t buffer_size, assert(m->n_label >= 1); assert(m->n_label <= 3); -#define LABEL_BUFFER_SIZE (2 * DATA_MAX_NAME_LEN + 4) +#define LABEL_KEY_SIZE DATA_MAX_NAME_LEN +#define LABEL_VALUE_SIZE (2 * DATA_MAX_NAME_LEN - 1) +#define LABEL_BUFFER_SIZE (LABEL_KEY_SIZE + LABEL_VALUE_SIZE + 4) char *labels[3] = { (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, (char[LABEL_BUFFER_SIZE]){0}, }; - for (size_t i = 0; i < m->n_label; i++) + /* N.B.: the label *names* are hard-coded by this plugin and therefore we + * know that they are sane. */ + for (size_t i = 0; i < m->n_label; i++) { + char value[LABEL_VALUE_SIZE]; ssnprintf(labels[i], LABEL_BUFFER_SIZE, "%s=\"%s\"", m->label[i]->name, - m->label[i]->value); + escape_label_value(value, sizeof(value), m->label[i]->value)); + } strjoin(buffer, buffer_size, labels, m->n_label, ","); return buffer; @@ -210,12 +253,11 @@ static int http_handler(void *cls, struct MHD_Connection *connection, else format_text(buffer); - struct MHD_Response *res = #if defined(MHD_VERSION) && MHD_VERSION >= 0x00090500 - MHD_create_response_from_buffer( + struct MHD_Response *res = MHD_create_response_from_buffer( simple.len, simple.data, MHD_RESPMEM_MUST_COPY); #else - MHD_create_response_from_data( + struct MHD_Response *res = MHD_create_response_from_data( simple.len, simple.data, /* must_free = */ 0, /* must_copy = */ 1); #endif MHD_add_response_header(res, MHD_HTTP_HEADER_CONTENT_TYPE,