X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fcurl_json.c;h=181f18bbbf29f0310ace456b99c4b2920b689673;hb=17e1d8131433d5c737a568c9e85df735c6bcd7b0;hp=6f31145cc5b785a8339a1b4b1c5a2ff076bddb15;hpb=fbaf81a04c23d51947d94a5c7d9142290dff07bc;p=collectd.git diff --git a/src/curl_json.c b/src/curl_json.c index 6f31145c..181f18bb 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -44,8 +44,6 @@ #endif #define CJ_DEFAULT_HOST "localhost" -#define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */ -#define CJ_IS_KEY(key) ((key)->magic == CJ_KEY_MAGIC) #define CJ_ANY "*" #define COUCH_MIN(x, y) ((x) < (y) ? (x) : (y)) @@ -53,13 +51,34 @@ struct cj_key_s; typedef struct cj_key_s cj_key_t; struct cj_key_s /* {{{ */ { - unsigned long magic; char *path; char *type; char *instance; }; /* }}} */ +/* cj_tree_entry_t is a union of either a metric configuration ("key") or a tree + * mapping array indexes / map keys to a descendant cj_tree_entry_t*. */ +typedef struct { + enum { KEY, TREE } type; + union { + c_avl_tree_t *tree; + cj_key_t *key; + }; +} cj_tree_entry_t; + +/* cj_state_t is a stack providing the configuration relevant for the context + * that is currently being parsed. If entry->type == KEY, the parser should + * expect a metric (a numeric value). If entry->type == TREE, the parser should + * expect an array of map to descent into. If entry == NULL, no configuration + * exists for this part of the JSON structure. */ +typedef struct { + cj_tree_entry_t *entry; + _Bool in_array; + int index; + char name[DATA_MAX_NAME_LEN]; +} cj_state_t; + struct cj_s /* {{{ */ { char *instance; @@ -86,17 +105,8 @@ struct cj_s /* {{{ */ yajl_handle yajl; c_avl_tree_t *tree; - cj_key_t *key; int depth; - struct { - union { - c_avl_tree_t *tree; - cj_key_t *key; - }; - _Bool in_array; - int index; - char name[DATA_MAX_NAME_LEN]; - } state[YAJL_MAX_DEPTH]; + cj_state_t state[YAJL_MAX_DEPTH]; }; typedef struct cj_s cj_t; /* }}} */ @@ -107,7 +117,11 @@ typedef unsigned int yajl_len_t; #endif static int cj_read(user_data_t *ud); -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value); +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value); + +/* cj_submit is a function pointer to cj_submit_impl, allowing the unit-test to + * overwrite which function is called. */ +static void (*cj_submit)(cj_t *, cj_key_t *, value_t *) = cj_submit_impl; static size_t cj_curl_callback(void *buf, /* {{{ */ size_t size, size_t nmemb, void *user_data) { @@ -141,12 +155,10 @@ static size_t cj_curl_callback(void *buf, /* {{{ */ } /* }}} size_t cj_curl_callback */ static int cj_get_type(cj_key_t *key) { - const data_set_t *ds; - - if ((key == NULL) || !CJ_IS_KEY(key)) + if (key == NULL) return -EINVAL; - ds = plugin_get_ds(key->type); + const data_set_t *ds = plugin_get_ds(key->type); if (ds == NULL) { static char type[DATA_MAX_NAME_LEN] = "!!!invalid!!!"; @@ -179,31 +191,20 @@ static int cj_load_key(cj_t *db, char const *key) { sstrncpy(db->state[db->depth].name, key, sizeof(db->state[db->depth].name)); - c_avl_tree_t *tree = db->state[db->depth - 1].tree; - if (tree == NULL) { + if (db->state[db->depth - 1].entry == NULL || + db->state[db->depth - 1].entry->type != TREE) { return 0; } - /* the parent has a key, so the tree pointer is invalid. */ - if (CJ_IS_KEY(db->state[db->depth - 1].key)) { - return 0; - } + c_avl_tree_t *tree = db->state[db->depth - 1].entry->tree; + cj_tree_entry_t *e = NULL; - void *value = NULL; - if (c_avl_get(tree, key, (void *)&value) == 0) { - if (CJ_IS_KEY((cj_key_t *)value)) { - db->state[db->depth].key = value; - } else { - db->state[db->depth].tree = value; - } - } else if (c_avl_get(tree, CJ_ANY, (void *)&value) == 0) { - if (CJ_IS_KEY((cj_key_t *)value)) { - db->state[db->depth].key = value; - } else { - db->state[db->depth].tree = value; - } + if (c_avl_get(tree, key, (void *)&e) == 0) { + db->state[db->depth].entry = e; + } else if (c_avl_get(tree, CJ_ANY, (void *)&e) == 0) { + db->state[db->depth].entry = e; } else { - db->state[db->depth].key = NULL; + db->state[db->depth].entry = NULL; } return 0; @@ -235,30 +236,26 @@ static int cj_cb_null(void *ctx) { } static int cj_cb_number(void *ctx, const char *number, yajl_len_t number_len) { - char buffer[number_len + 1]; - cj_t *db = (cj_t *)ctx; - cj_key_t *key = db->state[db->depth].key; /* Create a null-terminated version of the string. */ + char buffer[number_len + 1]; memcpy(buffer, number, number_len); buffer[sizeof(buffer) - 1] = 0; - - - if (key == NULL) { - /* no config for this element. */ - cj_advance_array(ctx); - return CJ_CB_CONTINUE; - } else if (!CJ_IS_KEY(key)) { - /* the config expects a map or an array. */ - NOTICE( - "curl_json plugin: Found \"%s\", but the configuration expects a map.", - buffer); + if (db->state[db->depth].entry == NULL || + db->state[db->depth].entry->type != KEY) { + if (db->state[db->depth].entry != NULL) { + NOTICE("curl_json plugin: Found \"%s\", but the configuration expects a " + "map.", + buffer); + } cj_advance_array(ctx); return CJ_CB_CONTINUE; } + cj_key_t *key = db->state[db->depth].entry->key; + int type = cj_get_type(key); value_t vt; int status = parse_value(buffer, &vt, type); @@ -296,7 +293,7 @@ static int cj_cb_string(void *ctx, const unsigned char *val, yajl_len_t len) { static int cj_cb_end(void *ctx) { cj_t *db = (cj_t *)ctx; - db->state[db->depth].tree = NULL; + memset(&db->state[db->depth], 0, sizeof(db->state[db->depth])); db->depth--; cj_advance_array(ctx); return (CJ_CB_CONTINUE); @@ -364,17 +361,16 @@ static void cj_key_free(cj_key_t *key) /* {{{ */ static void cj_tree_free(c_avl_tree_t *tree) /* {{{ */ { char *name; - void *value; + cj_tree_entry_t *e; - while (c_avl_pick(tree, (void *)&name, (void *)&value) == 0) { - cj_key_t *key = (cj_key_t *)value; + while (c_avl_pick(tree, (void *)&name, (void *)&e) == 0) { + sfree(name); - if (CJ_IS_KEY(key)) - cj_key_free(key); + if (e->type == KEY) + cj_key_free(e->key); else - cj_tree_free((c_avl_tree_t *)value); - - sfree(name); + cj_tree_free(e->tree); + sfree(e); } c_avl_destroy(tree); @@ -440,6 +436,67 @@ static int cj_config_append_string(const char *name, return (0); } /* }}} int cj_config_append_string */ +/* cj_append_key adds key to the configuration stored in db. + * + * For example: + * "httpd/requests/count", + * "httpd/requests/current" -> + * { "httpd": { "requests": { "count": $key, "current": $key } } } + */ +static int cj_append_key(cj_t *db, cj_key_t *key) { /* {{{ */ + if (db->tree == NULL) + db->tree = cj_avl_create(); + + c_avl_tree_t *tree = db->tree; + + char const *start = key->path; + if (*start == '/') + ++start; + + char const *end; + while ((end = strchr(start, '/')) != NULL) { + char name[PATH_MAX]; + + size_t len = end - start; + if (len == 0) + break; + + len = COUCH_MIN(len, sizeof(name) - 1); + sstrncpy(name, start, len + 1); + + cj_tree_entry_t *e; + if (c_avl_get(tree, name, (void *)&e) != 0) { + e = calloc(1, sizeof(*e)); + if (e == NULL) + return ENOMEM; + e->type = TREE; + e->tree = cj_avl_create(); + + c_avl_insert(tree, strdup(name), e); + } + + if (e->type != TREE) + return EINVAL; + + tree = e->tree; + start = end + 1; + } + + if (strlen(start) == 0) { + ERROR("curl_json plugin: invalid key: %s", key->path); + return -1; + } + + cj_tree_entry_t *e = calloc(1, sizeof(*e)); + if (e == NULL) + return ENOMEM; + e->type = KEY; + e->key = key; + + c_avl_insert(tree, strdup(start), e); + return 0; +} /* }}} int cj_append_key */ + static int cj_config_add_key(cj_t *db, /* {{{ */ oconfig_item_t *ci) { cj_key_t *key; @@ -456,7 +513,6 @@ static int cj_config_add_key(cj_t *db, /* {{{ */ ERROR("curl_json plugin: calloc failed."); return (-1); } - key->magic = CJ_KEY_MAGIC; if (strcasecmp("Key", ci->key) == 0) { status = cf_util_get_string(ci, &key->path); @@ -500,53 +556,13 @@ static int cj_config_add_key(cj_t *db, /* {{{ */ return (-1); } - /* store path in a tree that will match the json map structure, example: - * "httpd/requests/count", - * "httpd/requests/current" -> - * { "httpd": { "requests": { "count": $key, "current": $key } } } - */ - char *ptr; - char *name; - c_avl_tree_t *tree; - - if (db->tree == NULL) - db->tree = cj_avl_create(); - - tree = db->tree; - ptr = key->path; - if (*ptr == '/') - ++ptr; - - name = ptr; - while ((ptr = strchr(name, '/')) != NULL) { - char ent[PATH_MAX]; - c_avl_tree_t *value; - size_t len; - - len = ptr - name; - if (len == 0) - break; - - len = COUCH_MIN(len, sizeof(ent) - 1); - sstrncpy(ent, name, len + 1); - - if (c_avl_get(tree, ent, (void *)&value) != 0) { - value = cj_avl_create(); - c_avl_insert(tree, strdup(ent), value); - } - - tree = value; - name = ptr + 1; - } - - if (strlen(name) == 0) { - ERROR("curl_json plugin: invalid key: %s", key->path); + status = cj_append_key(db, key); + if (status != 0) { cj_key_free(key); - return (-1); + return -1; } - c_avl_insert(tree, strdup(name), key); - return (status); + return 0; } /* }}} int cj_config_add_key */ static int cj_init_curl(cj_t *db) /* {{{ */ @@ -774,7 +790,7 @@ static const char *cj_host(cj_t *db) /* {{{ */ return db->host; } /* }}} cj_host */ -static void cj_submit(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ +static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ { value_list_t vl = VALUE_LIST_INIT; @@ -798,13 +814,14 @@ static void cj_submit(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ vl.interval = db->interval; plugin_dispatch_values(&vl); -} /* }}} int cj_submit */ +} /* }}} int cj_submit_impl */ static int cj_sock_perform(cj_t *db) /* {{{ */ { char errbuf[1024]; - struct sockaddr_un sa_unix = {0}; - sa_unix.sun_family = AF_UNIX; + struct sockaddr_un sa_unix = { + .sun_family = AF_UNIX, + }; sstrncpy(sa_unix.sun_path, db->sock, sizeof(sa_unix.sun_path)); int fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -929,10 +946,19 @@ static int cj_read(user_data_t *ud) /* {{{ */ db->depth = 0; memset(&db->state, 0, sizeof(db->state)); - db->state[db->depth].tree = db->tree; - db->key = NULL; - return cj_perform(db); + /* This is not a compound literal because EPEL6's GCC is not cool enough to + * handle anonymous unions within compound literals. */ + cj_tree_entry_t root = {0}; + root.type = TREE; + root.tree = db->tree; + db->state[0].entry = &root; + + int status = cj_perform(db); + + db->state[0].entry = NULL; + + return status; } /* }}} int cj_read */ static int cj_init(void) /* {{{ */ @@ -947,5 +973,3 @@ void module_register(void) { plugin_register_complex_config("curl_json", cj_config); plugin_register_init("curl_json", cj_init); } /* void module_register */ - -/* vim: set sw=2 sts=2 et fdm=marker : */