X-Git-Url: https://git.octo.it/?p=collectd.git;a=blobdiff_plain;f=src%2Futils_format_json.c;h=6a0c6642d14562ff309994e3c6447fc1a7266793;hp=020d80beacb5f8401d26ec891a202cd2da226db1;hb=a9e50e9e30ecde17e167e271060c8183bfcbf407;hpb=f7b05ebf3311b4c6cb5289f1f7cd8ee02918ef92 diff --git a/src/utils_format_json.c b/src/utils_format_json.c index 020d80be..6a0c6642 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -1,6 +1,6 @@ /** * collectd - src/utils_format_json.c - * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2009-2015 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"), @@ -25,418 +25,393 @@ **/ #include "collectd.h" -#include "plugin.h" -#include "common.h" -#include "utils_cache.h" #include "utils_format_json.h" -static int json_escape_string (char *buffer, size_t buffer_size, /* {{{ */ - const char *string) -{ - size_t src_pos; +#include "common.h" +#include "plugin.h" +#include "utils_cache.h" + +#if HAVE_LIBYAJL +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#endif +#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1) +#define HAVE_YAJL_V2 1 +#endif +#endif + +static int json_escape_string(char *buffer, size_t buffer_size, /* {{{ */ + const char *string) { size_t dst_pos; if ((buffer == NULL) || (string == NULL)) - return (-EINVAL); + return -EINVAL; if (buffer_size < 3) - return (-ENOMEM); + return -ENOMEM; dst_pos = 0; -#define BUFFER_ADD(c) do { \ - if (dst_pos >= (buffer_size - 1)) { \ - buffer[buffer_size - 1] = 0; \ - return (-ENOMEM); \ - } \ - buffer[dst_pos] = (c); \ - dst_pos++; \ -} while (0) +#define BUFFER_ADD(c) \ + do { \ + if (dst_pos >= (buffer_size - 1)) { \ + buffer[buffer_size - 1] = 0; \ + return -ENOMEM; \ + } \ + buffer[dst_pos] = (c); \ + dst_pos++; \ + } while (0) /* Escape special characters */ - BUFFER_ADD ('"'); - for (src_pos = 0; string[src_pos] != 0; src_pos++) - { - if ((string[src_pos] == '"') - || (string[src_pos] == '\\')) - { - BUFFER_ADD ('\\'); - BUFFER_ADD (string[src_pos]); - } - else if (string[src_pos] <= 0x001F) - BUFFER_ADD ('?'); + BUFFER_ADD('"'); + for (size_t src_pos = 0; string[src_pos] != 0; src_pos++) { + if ((string[src_pos] == '"') || (string[src_pos] == '\\')) { + BUFFER_ADD('\\'); + BUFFER_ADD(string[src_pos]); + } else if (string[src_pos] <= 0x001F) + BUFFER_ADD('?'); else - BUFFER_ADD (string[src_pos]); + BUFFER_ADD(string[src_pos]); } /* for */ - BUFFER_ADD ('"'); + BUFFER_ADD('"'); buffer[dst_pos] = 0; #undef BUFFER_ADD - return (0); + return 0; } /* }}} int json_escape_string */ -static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, int store_rates) -{ +static int values_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates) { size_t offset = 0; - int i; gauge_t *rates = NULL; - memset (buffer, 0, buffer_size); - -#define BUFFER_ADD(...) do { \ - int status; \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - { \ - sfree(rates); \ - return (-1); \ - } \ - else if (((size_t) status) >= (buffer_size - offset)) \ - { \ - sfree(rates); \ - return (-ENOMEM); \ - } \ - else \ - offset += ((size_t) status); \ -} while (0) - - BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) - { + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) { \ + sfree(rates); \ + return -1; \ + } else if (((size_t)status) >= (buffer_size - offset)) { \ + sfree(rates); \ + return -ENOMEM; \ + } else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) - BUFFER_ADD (","); + BUFFER_ADD(","); - if (ds->ds[i].type == DS_TYPE_GAUGE) - { - if(isfinite (vl->values[i].gauge)) - BUFFER_ADD (JSON_GAUGE_FORMAT, vl->values[i].gauge); + if (ds->ds[i].type == DS_TYPE_GAUGE) { + if (isfinite(vl->values[i].gauge)) + BUFFER_ADD(JSON_GAUGE_FORMAT, vl->values[i].gauge); else - BUFFER_ADD ("null"); - } - else if (store_rates) - { - if (rates == NULL) - rates = uc_get_rate (ds, vl); + BUFFER_ADD("null"); + } else if (store_rates) { if (rates == NULL) - { - WARNING ("utils_format_json: uc_get_rate failed."); + rates = uc_get_rate(ds, vl); + if (rates == NULL) { + WARNING("utils_format_json: uc_get_rate failed."); sfree(rates); - return (-1); + return -1; } - if(isfinite (rates[i])) - BUFFER_ADD (JSON_GAUGE_FORMAT, rates[i]); + if (isfinite(rates[i])) + BUFFER_ADD(JSON_GAUGE_FORMAT, rates[i]); else - BUFFER_ADD ("null"); - } - else if (ds->ds[i].type == DS_TYPE_COUNTER) - BUFFER_ADD ("%llu", vl->values[i].counter); + BUFFER_ADD("null"); + } else if (ds->ds[i].type == DS_TYPE_COUNTER) + BUFFER_ADD("%" PRIu64, (uint64_t)vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_DERIVE) - BUFFER_ADD ("%"PRIi64, vl->values[i].derive); + BUFFER_ADD("%" PRIi64, vl->values[i].derive); else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) - BUFFER_ADD ("%"PRIu64, vl->values[i].absolute); - else - { - ERROR ("format_json: Unknown data source type: %i", - ds->ds[i].type); - sfree (rates); - return (-1); + BUFFER_ADD("%" PRIu64, vl->values[i].absolute); + else { + ERROR("format_json: Unknown data source type: %i", ds->ds[i].type); + sfree(rates); + return -1; } } /* for ds->ds_num */ - BUFFER_ADD ("]"); + BUFFER_ADD("]"); #undef BUFFER_ADD - DEBUG ("format_json: values_to_json: buffer = %s;", buffer); + DEBUG("format_json: values_to_json: buffer = %s;", buffer); sfree(rates); - return (0); + return 0; } /* }}} int values_to_json */ -static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds) -{ +static int dstypes_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds) { size_t offset = 0; - int i; - - memset (buffer, 0, buffer_size); - -#define BUFFER_ADD(...) do { \ - int status; \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - return (-1); \ - else if (((size_t) status) >= (buffer_size - offset)) \ - return (-ENOMEM); \ - else \ - offset += ((size_t) status); \ -} while (0) - - BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) - { + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) - BUFFER_ADD (","); + BUFFER_ADD(","); - BUFFER_ADD ("\"%s\"", DS_TYPE_TO_STRING (ds->ds[i].type)); + BUFFER_ADD("\"%s\"", DS_TYPE_TO_STRING(ds->ds[i].type)); } /* for ds->ds_num */ - BUFFER_ADD ("]"); + BUFFER_ADD("]"); #undef BUFFER_ADD - DEBUG ("format_json: dstypes_to_json: buffer = %s;", buffer); + DEBUG("format_json: dstypes_to_json: buffer = %s;", buffer); - return (0); + return 0; } /* }}} int dstypes_to_json */ -static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds) -{ +static int dsnames_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds) { size_t offset = 0; - int i; - - memset (buffer, 0, buffer_size); - -#define BUFFER_ADD(...) do { \ - int status; \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - return (-1); \ - else if (((size_t) status) >= (buffer_size - offset)) \ - return (-ENOMEM); \ - else \ - offset += ((size_t) status); \ -} while (0) - - BUFFER_ADD ("["); - for (i = 0; i < ds->ds_num; i++) - { + + memset(buffer, 0, buffer_size); + +#define BUFFER_ADD(...) \ + do { \ + int status; \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + BUFFER_ADD("["); + for (size_t i = 0; i < ds->ds_num; i++) { if (i > 0) - BUFFER_ADD (","); + BUFFER_ADD(","); - BUFFER_ADD ("\"%s\"", ds->ds[i].name); + BUFFER_ADD("\"%s\"", ds->ds[i].name); } /* for ds->ds_num */ - BUFFER_ADD ("]"); + BUFFER_ADD("]"); #undef BUFFER_ADD - DEBUG ("format_json: dsnames_to_json: buffer = %s;", buffer); + DEBUG("format_json: dsnames_to_json: buffer = %s;", buffer); - return (0); + return 0; } /* }}} int dsnames_to_json */ -static int meta_data_keys_to_json (char *buffer, size_t buffer_size, /* {{{ */ - meta_data_t *meta, char **keys, size_t keys_num) -{ +static int meta_data_keys_to_json(char *buffer, size_t buffer_size, /* {{{ */ + meta_data_t *meta, char **keys, + size_t keys_num) { size_t offset = 0; int status; - size_t i; buffer[0] = 0; -#define BUFFER_ADD(...) do { \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - return (-1); \ - else if (((size_t) status) >= (buffer_size - offset)) \ - return (-ENOMEM); \ - else \ - offset += ((size_t) status); \ -} while (0) - - for (i = 0; i < keys_num; ++i) - { +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) + + for (size_t i = 0; i < keys_num; ++i) { int type; char *key = keys[i]; - type = meta_data_type (meta, key); - if (type == MD_TYPE_STRING) - { + type = meta_data_type(meta, key); + if (type == MD_TYPE_STRING) { char *value = NULL; - if (meta_data_get_string (meta, key, &value) == 0) - { + if (meta_data_get_string(meta, key, &value) == 0) { char temp[512] = ""; - status = json_escape_string (temp, sizeof (temp), value); - sfree (value); + status = json_escape_string(temp, sizeof(temp), value); + sfree(value); if (status != 0) return status; - BUFFER_ADD (",\"%s\":%s", key, temp); + BUFFER_ADD(",\"%s\":%s", key, temp); } - } - else if (type == MD_TYPE_SIGNED_INT) - { + } else if (type == MD_TYPE_SIGNED_INT) { int64_t value = 0; - if (meta_data_get_signed_int (meta, key, &value) == 0) - BUFFER_ADD (",\"%s\":%"PRIi64, key, value); - } - else if (type == MD_TYPE_UNSIGNED_INT) - { + if (meta_data_get_signed_int(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%" PRIi64, key, value); + } else if (type == MD_TYPE_UNSIGNED_INT) { uint64_t value = 0; - if (meta_data_get_unsigned_int (meta, key, &value) == 0) - BUFFER_ADD (",\"%s\":%"PRIu64, key, value); - } - else if (type == MD_TYPE_DOUBLE) - { + if (meta_data_get_unsigned_int(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%" PRIu64, key, value); + } else if (type == MD_TYPE_DOUBLE) { double value = 0.0; - if (meta_data_get_double (meta, key, &value) == 0) - BUFFER_ADD (",\"%s\":%f", key, value); - } - else if (type == MD_TYPE_BOOLEAN) - { + if (meta_data_get_double(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%f", key, value); + } else if (type == MD_TYPE_BOOLEAN) { _Bool value = 0; - if (meta_data_get_boolean (meta, key, &value) == 0) - BUFFER_ADD (",\"%s\":%s", key, value ? "true" : "false"); + if (meta_data_get_boolean(meta, key, &value) == 0) + BUFFER_ADD(",\"%s\":%s", key, value ? "true" : "false"); } } /* for (keys) */ - if (offset <= 0) - return (ENOENT); + if (offset == 0) + return ENOENT; buffer[0] = '{'; /* replace leading ',' */ - BUFFER_ADD ("}"); + BUFFER_ADD("}"); #undef BUFFER_ADD - return (0); + return 0; } /* }}} int meta_data_keys_to_json */ -static int meta_data_to_json (char *buffer, size_t buffer_size, /* {{{ */ - meta_data_t *meta) -{ +static int meta_data_to_json(char *buffer, size_t buffer_size, /* {{{ */ + meta_data_t *meta) { char **keys = NULL; size_t keys_num; int status; - size_t i; if ((buffer == NULL) || (buffer_size == 0) || (meta == NULL)) - return (EINVAL); + return EINVAL; - status = meta_data_toc (meta, &keys); + status = meta_data_toc(meta, &keys); if (status <= 0) - return (status); - keys_num = (size_t) status; + return status; + keys_num = (size_t)status; - status = meta_data_keys_to_json (buffer, buffer_size, meta, keys, keys_num); + status = meta_data_keys_to_json(buffer, buffer_size, meta, keys, keys_num); - for (i = 0; i < keys_num; ++i) - sfree (keys[i]); - sfree (keys); + for (size_t i = 0; i < keys_num; ++i) + sfree(keys[i]); + sfree(keys); return status; } /* }}} int meta_data_to_json */ -static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */ - const data_set_t *ds, const value_list_t *vl, int store_rates) -{ +static int value_list_to_json(char *buffer, size_t buffer_size, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + int store_rates) { char temp[512]; size_t offset = 0; int status; - memset (buffer, 0, buffer_size); + memset(buffer, 0, buffer_size); -#define BUFFER_ADD(...) do { \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - return (-1); \ - else if (((size_t) status) >= (buffer_size - offset)) \ - return (-ENOMEM); \ - else \ - offset += ((size_t) status); \ -} while (0) +#define BUFFER_ADD(...) \ + do { \ + status = snprintf(buffer + offset, buffer_size - offset, __VA_ARGS__); \ + if (status < 1) \ + return -1; \ + else if (((size_t)status) >= (buffer_size - offset)) \ + return -ENOMEM; \ + else \ + offset += ((size_t)status); \ + } while (0) /* All value lists have a leading comma. The first one will be replaced with * a square bracket in `format_json_finalize'. */ - BUFFER_ADD (",{"); + BUFFER_ADD(",{"); - status = values_to_json (temp, sizeof (temp), ds, vl, store_rates); + status = values_to_json(temp, sizeof(temp), ds, vl, store_rates); if (status != 0) - return (status); - BUFFER_ADD ("\"values\":%s", temp); + return status; + BUFFER_ADD("\"values\":%s", temp); - status = dstypes_to_json (temp, sizeof (temp), ds); + status = dstypes_to_json(temp, sizeof(temp), ds); if (status != 0) - return (status); - BUFFER_ADD (",\"dstypes\":%s", temp); + return status; + BUFFER_ADD(",\"dstypes\":%s", temp); - status = dsnames_to_json (temp, sizeof (temp), ds); + status = dsnames_to_json(temp, sizeof(temp), ds); if (status != 0) - return (status); - BUFFER_ADD (",\"dsnames\":%s", temp); - - BUFFER_ADD (",\"time\":%.3f", CDTIME_T_TO_DOUBLE (vl->time)); - BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval)); - -#define BUFFER_ADD_KEYVAL(key, value) do { \ - status = json_escape_string (temp, sizeof (temp), (value)); \ - if (status != 0) \ - return (status); \ - BUFFER_ADD (",\"%s\":%s", (key), temp); \ -} while (0) - - BUFFER_ADD_KEYVAL ("host", vl->host); - BUFFER_ADD_KEYVAL ("plugin", vl->plugin); - BUFFER_ADD_KEYVAL ("plugin_instance", vl->plugin_instance); - BUFFER_ADD_KEYVAL ("type", vl->type); - BUFFER_ADD_KEYVAL ("type_instance", vl->type_instance); - - if (vl->meta != NULL) - { + return status; + BUFFER_ADD(",\"dsnames\":%s", temp); + + BUFFER_ADD(",\"time\":%.3f", CDTIME_T_TO_DOUBLE(vl->time)); + BUFFER_ADD(",\"interval\":%.3f", CDTIME_T_TO_DOUBLE(vl->interval)); + +#define BUFFER_ADD_KEYVAL(key, value) \ + do { \ + status = json_escape_string(temp, sizeof(temp), (value)); \ + if (status != 0) \ + return status; \ + BUFFER_ADD(",\"%s\":%s", (key), temp); \ + } while (0) + + BUFFER_ADD_KEYVAL("host", vl->host); + BUFFER_ADD_KEYVAL("plugin", vl->plugin); + BUFFER_ADD_KEYVAL("plugin_instance", vl->plugin_instance); + BUFFER_ADD_KEYVAL("type", vl->type); + BUFFER_ADD_KEYVAL("type_instance", vl->type_instance); + + if (vl->meta != NULL) { char meta_buffer[buffer_size]; - memset (meta_buffer, 0, sizeof (meta_buffer)); - status = meta_data_to_json (meta_buffer, sizeof (meta_buffer), vl->meta); + memset(meta_buffer, 0, sizeof(meta_buffer)); + status = meta_data_to_json(meta_buffer, sizeof(meta_buffer), vl->meta); if (status != 0) - return (status); + return status; - BUFFER_ADD (",\"meta\":%s", meta_buffer); + BUFFER_ADD(",\"meta\":%s", meta_buffer); } /* if (vl->meta != NULL) */ - BUFFER_ADD ("}"); + BUFFER_ADD("}"); #undef BUFFER_ADD_KEYVAL #undef BUFFER_ADD - DEBUG ("format_json: value_list_to_json: buffer = %s;", buffer); + DEBUG("format_json: value_list_to_json: buffer = %s;", buffer); - return (0); + return 0; } /* }}} int value_list_to_json */ -static int format_json_value_list_nocheck (char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free, - const data_set_t *ds, const value_list_t *vl, - int store_rates, size_t temp_size) -{ +static int format_json_value_list_nocheck(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, + size_t *ret_buffer_free, + const data_set_t *ds, + const value_list_t *vl, + int store_rates, size_t temp_size) { char temp[temp_size]; int status; - status = value_list_to_json (temp, sizeof (temp), ds, vl, store_rates); + status = value_list_to_json(temp, sizeof(temp), ds, vl, store_rates); if (status != 0) - return (status); - temp_size = strlen (temp); + return status; + temp_size = strlen(temp); - memcpy (buffer + (*ret_buffer_fill), temp, temp_size + 1); + memcpy(buffer + (*ret_buffer_fill), temp, temp_size + 1); (*ret_buffer_fill) += temp_size; (*ret_buffer_free) -= temp_size; - return (0); + return 0; } /* }}} int format_json_value_list_nocheck */ -int format_json_initialize (char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free) -{ +int format_json_initialize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free) { size_t buffer_fill; size_t buffer_free; - if ((buffer == NULL) || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)) - return (-EINVAL); + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; buffer_fill = *ret_buffer_fill; buffer_free = *ret_buffer_free; @@ -445,57 +420,284 @@ int format_json_initialize (char *buffer, /* {{{ */ buffer_fill = 0; if (buffer_free < 3) - return (-ENOMEM); + return -ENOMEM; - memset (buffer, 0, buffer_free); + memset(buffer, 0, buffer_free); *ret_buffer_fill = buffer_fill; *ret_buffer_free = buffer_free; - return (0); + return 0; } /* }}} int format_json_initialize */ -int format_json_finalize (char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free) -{ +int format_json_finalize(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free) { size_t pos; - if ((buffer == NULL) || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)) - return (-EINVAL); + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL)) + return -EINVAL; if (*ret_buffer_free < 2) - return (-ENOMEM); + return -ENOMEM; /* Replace the leading comma added in `value_list_to_json' with a square * bracket. */ if (buffer[0] != ',') - return (-EINVAL); + return -EINVAL; buffer[0] = '['; pos = *ret_buffer_fill; buffer[pos] = ']'; - buffer[pos+1] = 0; + buffer[pos + 1] = 0; (*ret_buffer_fill)++; (*ret_buffer_free)--; - return (0); + return 0; } /* }}} int format_json_finalize */ -int format_json_value_list (char *buffer, /* {{{ */ - size_t *ret_buffer_fill, size_t *ret_buffer_free, - const data_set_t *ds, const value_list_t *vl, int store_rates) -{ - if ((buffer == NULL) - || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL) - || (ds == NULL) || (vl == NULL)) - return (-EINVAL); +int format_json_value_list(char *buffer, /* {{{ */ + size_t *ret_buffer_fill, size_t *ret_buffer_free, + const data_set_t *ds, const value_list_t *vl, + int store_rates) { + if ((buffer == NULL) || (ret_buffer_fill == NULL) || + (ret_buffer_free == NULL) || (ds == NULL) || (vl == NULL)) + return -EINVAL; if (*ret_buffer_free < 3) - return (-ENOMEM); + return -ENOMEM; - return (format_json_value_list_nocheck (buffer, - ret_buffer_fill, ret_buffer_free, ds, vl, - store_rates, (*ret_buffer_free) - 2)); + return format_json_value_list_nocheck(buffer, ret_buffer_fill, + ret_buffer_free, ds, vl, store_rates, + (*ret_buffer_free) - 2); } /* }}} int format_json_value_list */ -/* vim: set sw=2 sts=2 et fdm=marker : */ +#if HAVE_LIBYAJL +static int json_add_string(yajl_gen g, char const *str) /* {{{ */ +{ + if (str == NULL) + return (int)yajl_gen_null(g); + + return (int)yajl_gen_string(g, (const unsigned char *)str, + (unsigned int)strlen(str)); +} /* }}} int json_add_string */ + +#define JSON_ADD(g, str) \ + do { \ + yajl_gen_status status = json_add_string(g, str); \ + if (status != yajl_gen_status_ok) { \ + return -1; \ + } \ + } while (0) + +#define JSON_ADDF(g, format, ...) \ + do { \ + char *str = ssnprintf_alloc(format, __VA_ARGS__); \ + yajl_gen_status status = json_add_string(g, str); \ + free(str); \ + if (status != yajl_gen_status_ok) { \ + return -1; \ + } \ + } while (0) + +#define CHECK_SUCCESS(cmd) \ + do { \ + yajl_gen_status s = (cmd); \ + if (s != yajl_gen_status_ok) { \ + return (int)s; \ + } \ + } while (0) + +static int format_json_meta(yajl_gen g, notification_meta_t *meta) /* {{{ */ +{ + if (meta == NULL) + return 0; + + JSON_ADD(g, meta->name); + switch (meta->type) { + case NM_TYPE_STRING: + JSON_ADD(g, meta->nm_value.nm_string); + break; + case NM_TYPE_SIGNED_INT: + JSON_ADDF(g, "%" PRIi64, meta->nm_value.nm_signed_int); + break; + case NM_TYPE_UNSIGNED_INT: + JSON_ADDF(g, "%" PRIu64, meta->nm_value.nm_unsigned_int); + break; + case NM_TYPE_DOUBLE: + JSON_ADDF(g, JSON_GAUGE_FORMAT, meta->nm_value.nm_double); + break; + case NM_TYPE_BOOLEAN: + JSON_ADD(g, meta->nm_value.nm_boolean ? "true" : "false"); + break; + default: + ERROR("format_json_meta: unknown meta data type %d (name \"%s\")", + meta->type, meta->name); + CHECK_SUCCESS(yajl_gen_null(g)); + } + + return format_json_meta(g, meta->next); +} /* }}} int format_json_meta */ + +static int format_time(yajl_gen g, cdtime_t t) /* {{{ */ +{ + char buffer[RFC3339NANO_SIZE] = ""; + + if (rfc3339nano(buffer, sizeof(buffer), t) != 0) + return -1; + + JSON_ADD(g, buffer); + return 0; +} /* }}} int format_time */ + +static int format_alert(yajl_gen g, notification_t const *n) /* {{{ */ +{ + CHECK_SUCCESS(yajl_gen_array_open(g)); /* BEGIN array */ + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN alert */ + + /* + * labels + */ + JSON_ADD(g, "labels"); + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN labels */ + + JSON_ADD(g, "alertname"); + if (strncmp(n->plugin, n->type, strlen(n->plugin)) == 0) + JSON_ADDF(g, "collectd_%s", n->type); + else + JSON_ADDF(g, "collectd_%s_%s", n->plugin, n->type); + + JSON_ADD(g, "instance"); + JSON_ADD(g, n->host); + + /* mangling of plugin instance and type instance into labels is copied from + * the Prometheus collectd exporter. */ + if (strlen(n->plugin_instance) > 0) { + JSON_ADD(g, n->plugin); + JSON_ADD(g, n->plugin_instance); + } + if (strlen(n->type_instance) > 0) { + if (strlen(n->plugin_instance) > 0) + JSON_ADD(g, "type"); + else + JSON_ADD(g, n->plugin); + JSON_ADD(g, n->type_instance); + } + + JSON_ADD(g, "severity"); + JSON_ADD(g, + (n->severity == NOTIF_FAILURE) + ? "FAILURE" + : (n->severity == NOTIF_WARNING) + ? "WARNING" + : (n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"); + + JSON_ADD(g, "service"); + JSON_ADD(g, "collectd"); + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END labels */ + + /* + * annotations + */ + JSON_ADD(g, "annotations"); + CHECK_SUCCESS(yajl_gen_map_open(g)); /* BEGIN annotations */ + + JSON_ADD(g, "summary"); + JSON_ADD(g, n->message); + + if (format_json_meta(g, n->meta) != 0) { + return -1; + } + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END annotations */ + + JSON_ADD(g, "startsAt"); + if (format_time(g, n->time) != 0) { + return -1; + } + + CHECK_SUCCESS(yajl_gen_map_close(g)); /* END alert */ + CHECK_SUCCESS(yajl_gen_array_close(g)); /* END array */ + + return 0; +} /* }}} format_alert */ + +/* + * Format (prometheus/alertmanager v1): + * + * [{ + * "labels": { + * "alertname": "collectd_cpu", + * "instance": "host.example.com", + * "severity": "FAILURE", + * "service": "collectd", + * "cpu": "0", + * "type": "wait" + * }, + * "annotations": { + * "summary": "...", + * // meta + * }, + * "startsAt": , + * "endsAt": , // not used + * }] + */ +int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ + notification_t const *n) { + yajl_gen g; + unsigned char const *out; +#if HAVE_YAJL_V2 + size_t unused_out_len; +#else + unsigned int unused_out_len; +#endif + + if ((buffer == NULL) || (n == NULL)) + return EINVAL; + +#if HAVE_YAJL_V2 + g = yajl_gen_alloc(NULL); + if (g == NULL) + return -1; +#if COLLECT_DEBUG + yajl_gen_config(g, yajl_gen_beautify, 1); + yajl_gen_config(g, yajl_gen_validate_utf8, 1); +#endif + +#else /* !HAVE_YAJL_V2 */ + yajl_gen_config conf = {0}; +#if COLLECT_DEBUG + conf.beautify = 1; + conf.indentString = " "; +#endif + g = yajl_gen_alloc(&conf, NULL); + if (g == NULL) + return -1; +#endif + + if (format_alert(g, n) != 0) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + + /* copy to output buffer */ + if (yajl_gen_get_buf(g, &out, &unused_out_len) != yajl_gen_status_ok) { + yajl_gen_clear(g); + yajl_gen_free(g); + return -1; + } + sstrncpy(buffer, (void *)out, buffer_size); + + yajl_gen_clear(g); + yajl_gen_free(g); + return 0; +} /* }}} format_json_notification */ +#else +int format_json_notification(char *buffer, size_t buffer_size, /* {{{ */ + notification_t const *n) { + ERROR("format_json_notification: Not available (requires libyajl)."); + return ENOTSUP; +} /* }}} int format_json_notification */ +#endif