X-Git-Url: https://git.octo.it/?p=collectd.git;a=blobdiff_plain;f=src%2Fcurl_json.c;h=a2f287a81668b824986b55f4c122c510034b15e5;hp=a589cd658eb990adce119a378e9ef6c403c81353;hb=da11ce02eb202b3e01d3e2d1b40f248a84430973;hpb=a6f29de68fe19614b0aa90e920e99d72cb0f4468 diff --git a/src/curl_json.c b/src/curl_json.c index a589cd65..a2f287a8 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,16 +51,38 @@ 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; + char *plugin_name; char *host; char *sock; @@ -87,15 +107,7 @@ struct cj_s /* {{{ */ yajl_handle yajl; c_avl_tree_t *tree; 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; /* }}} */ @@ -121,18 +133,18 @@ static size_t cj_curl_callback(void *buf, /* {{{ */ len = size * nmemb; if (len == 0) - return (len); + return len; db = user_data; if (db == NULL) - return (0); + return 0; status = yajl_parse(db->yajl, (unsigned char *)buf, len); if (status == yajl_status_ok) - return (len); + return len; #if !HAVE_YAJL_V2 else if (status == yajl_status_insufficient_data) - return (len); + return len; #endif unsigned char *msg = @@ -140,16 +152,14 @@ static size_t cj_curl_callback(void *buf, /* {{{ */ /* jsonText = */ (unsigned char *)buf, (unsigned int)len); ERROR("curl_json plugin: yajl_parse failed: %s", msg); yajl_free_error(db->yajl, msg); - return (0); /* abort write callback */ + return 0; /* abort write callback */ } /* }}} 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!!!"; @@ -182,31 +192,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; @@ -219,7 +218,7 @@ static void cj_advance_array(cj_t *db) { db->state[db->depth].index++; char name[DATA_MAX_NAME_LEN]; - ssnprintf(name, sizeof(name), "%d", db->state[db->depth].index); + snprintf(name, sizeof(name), "%d", db->state[db->depth].index); cj_load_key(db, name); } @@ -227,53 +226,44 @@ static void cj_advance_array(cj_t *db) { #define CJ_CB_ABORT 0 #define CJ_CB_CONTINUE 1 -static int cj_cb_boolean(void *ctx, int boolVal) { - cj_advance_array(ctx); - return (CJ_CB_CONTINUE); -} - static int cj_cb_null(void *ctx) { cj_advance_array(ctx); - return (CJ_CB_CONTINUE); + return CJ_CB_CONTINUE; } 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); if (status != 0) { NOTICE("curl_json plugin: Unable to parse number: \"%s\"", buffer); cj_advance_array(ctx); - return (CJ_CB_CONTINUE); + return CJ_CB_CONTINUE; } cj_submit(db, key, &vt); cj_advance_array(ctx); - return (CJ_CB_CONTINUE); + return CJ_CB_CONTINUE; } /* int cj_cb_number */ /* Queries the key-tree of the parent context for "in_name" and, if found, @@ -294,15 +284,22 @@ static int cj_cb_map_key(void *ctx, unsigned char const *in_name, static int cj_cb_string(void *ctx, const unsigned char *val, yajl_len_t len) { /* Handle the string as if it was a number. */ - return (cj_cb_number(ctx, (const char *)val, len)); + return cj_cb_number(ctx, (const char *)val, len); } /* int cj_cb_string */ +static int cj_cb_boolean(void *ctx, int boolVal) { + if (boolVal) + return cj_cb_number(ctx, "1", 1); + else + return cj_cb_number(ctx, "0", 1); +} /* int cj_cb_boolean */ + 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); + return CJ_CB_CONTINUE; } static int cj_cb_start_map(void *ctx) { @@ -311,10 +308,10 @@ static int cj_cb_start_map(void *ctx) { if ((db->depth + 1) >= YAJL_MAX_DEPTH) { ERROR("curl_json plugin: %s depth exceeds max, aborting.", db->url ? db->url : db->sock); - return (CJ_CB_ABORT); + return CJ_CB_ABORT; } db->depth++; - return (CJ_CB_CONTINUE); + return CJ_CB_CONTINUE; } static int cj_cb_end_map(void *ctx) { return cj_cb_end(ctx); } @@ -367,17 +364,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); @@ -403,6 +399,7 @@ static void cj_free(void *arg) /* {{{ */ db->tree = NULL; sfree(db->instance); + sfree(db->plugin_name); sfree(db->host); sfree(db->sock); @@ -431,16 +428,16 @@ static int cj_config_append_string(const char *name, struct curl_slist *temp = NULL; if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING("curl_json plugin: `%s' needs exactly one string argument.", name); - return (-1); + return -1; } temp = curl_slist_append(*dest, ci->values[0].value.string); if (temp == NULL) - return (-1); + return -1; *dest = temp; - return (0); + return 0; } /* }}} int cj_config_append_string */ /* cj_append_key adds key to the configuration stored in db. @@ -471,13 +468,21 @@ static int cj_append_key(cj_t *db, cj_key_t *key) { /* {{{ */ len = COUCH_MIN(len, sizeof(name) - 1); sstrncpy(name, start, len + 1); - c_avl_tree_t *value; - if (c_avl_get(tree, name, (void *)&value) != 0) { - value = cj_avl_create(); - c_avl_insert(tree, strdup(name), value); + 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); } - tree = value; + if (e->type != TREE) + return EINVAL; + + tree = e->tree; start = end + 1; } @@ -486,7 +491,13 @@ static int cj_append_key(cj_t *db, cj_key_t *key) { /* {{{ */ return -1; } - c_avl_insert(tree, strdup(start), key); + 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 */ @@ -498,28 +509,27 @@ static int cj_config_add_key(cj_t *db, /* {{{ */ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING("curl_json plugin: The `Key' block " "needs exactly one string argument."); - return (-1); + return -1; } key = calloc(1, sizeof(*key)); if (key == NULL) { ERROR("curl_json plugin: calloc failed."); - return (-1); + return -1; } - key->magic = CJ_KEY_MAGIC; if (strcasecmp("Key", ci->key) == 0) { status = cf_util_get_string(ci, &key->path); if (status != 0) { sfree(key); - return (status); + return status; } } else { ERROR("curl_json plugin: cj_config: " "Invalid key: %s", ci->key); cj_key_free(key); - return (-1); + return -1; } status = 0; @@ -541,13 +551,13 @@ static int cj_config_add_key(cj_t *db, /* {{{ */ if (status != 0) { cj_key_free(key); - return (-1); + return -1; } if (key->type == NULL) { WARNING("curl_json plugin: `Type' missing in `Key' block."); cj_key_free(key); - return (-1); + return -1; } status = cj_append_key(db, key); @@ -564,7 +574,7 @@ static int cj_init_curl(cj_t *db) /* {{{ */ db->curl = curl_easy_init(); if (db->curl == NULL) { ERROR("curl_json plugin: curl_easy_init failed."); - return (-1); + return -1; } curl_easy_setopt(db->curl, CURLOPT_NOSIGNAL, 1L); @@ -572,7 +582,6 @@ static int cj_init_curl(cj_t *db) /* {{{ */ curl_easy_setopt(db->curl, CURLOPT_WRITEDATA, db); curl_easy_setopt(db->curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT); curl_easy_setopt(db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf); - curl_easy_setopt(db->curl, CURLOPT_URL, db->url); curl_easy_setopt(db->curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(db->curl, CURLOPT_MAXREDIRS, 50L); @@ -591,11 +600,11 @@ static int cj_init_curl(cj_t *db) /* {{{ */ db->credentials = malloc(credentials_size); if (db->credentials == NULL) { ERROR("curl_json plugin: malloc failed."); - return (-1); + return -1; } - ssnprintf(db->credentials, credentials_size, "%s:%s", db->user, - (db->pass == NULL) ? "" : db->pass); + snprintf(db->credentials, credentials_size, "%s:%s", db->user, + (db->pass == NULL) ? "" : db->pass); curl_easy_setopt(db->curl, CURLOPT_USERPWD, db->credentials); #endif @@ -623,7 +632,7 @@ static int cj_init_curl(cj_t *db) /* {{{ */ (long)CDTIME_T_TO_MS(plugin_get_interval())); #endif - return (0); + return 0; } /* }}} int cj_init_curl */ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ @@ -634,13 +643,13 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { WARNING("curl_json plugin: The `URL' block " "needs exactly one string argument."); - return (-1); + return -1; } db = calloc(1, sizeof(*db)); if (db == NULL) { ERROR("curl_json plugin: calloc failed."); - return (-1); + return -1; } db->timeout = -1; @@ -654,11 +663,11 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ "Invalid key: %s", ci->key); cj_free(db); - return (-1); + return -1; } if (status != 0) { sfree(db); - return (status); + return status; } /* Fill the `cj_t' structure.. */ @@ -667,6 +676,8 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ if (strcasecmp("Instance", child->key) == 0) status = cf_util_get_string(child, &db->instance); + else if (strcasecmp("Plugin", child->key) == 0) + status = cf_util_get_string(child, &db->plugin_name); else if (strcasecmp("Host", child->key) == 0) status = cf_util_get_string(child, &db->host); else if (db->url && strcasecmp("User", child->key) == 0) @@ -734,10 +745,10 @@ static int cj_config_add_url(oconfig_item_t *ci) /* {{{ */ sfree(cb_name); } else { cj_free(db); - return (-1); + return -1; } - return (0); + return 0; } /* }}} int cj_config_add_database */ @@ -768,10 +779,10 @@ static int cj_config(oconfig_item_t *ci) /* {{{ */ if ((success == 0) && (errors > 0)) { ERROR("curl_json plugin: All statements failed."); - return (-1); + return -1; } - return (0); + return 0; } /* }}} int cj_config */ /* }}} End of configuration handling functions */ @@ -794,13 +805,14 @@ static void cj_submit_impl(cj_t *db, cj_key_t *key, value_t *value) /* {{{ */ if (key->instance == NULL) { int len = 0; for (int i = 0; i < db->depth; i++) - len += ssnprintf(vl.type_instance + len, sizeof(vl.type_instance) - len, - i ? "-%s" : "%s", db->state[i + 1].name); + len += snprintf(vl.type_instance + len, sizeof(vl.type_instance) - len, + i ? "-%s" : "%s", db->state[i + 1].name); } else sstrncpy(vl.type_instance, key->instance, sizeof(vl.type_instance)); sstrncpy(vl.host, cj_host(db), sizeof(vl.host)); - sstrncpy(vl.plugin, "curl_json", sizeof(vl.plugin)); + sstrncpy(vl.plugin, (db->plugin_name != NULL) ? db->plugin_name : "curl_json", + sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, db->instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, key->type, sizeof(vl.type)); @@ -820,13 +832,13 @@ static int cj_sock_perform(cj_t *db) /* {{{ */ int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) - return (-1); + return -1; if (connect(fd, (struct sockaddr *)&sa_unix, sizeof(sa_unix)) < 0) { ERROR("curl_json plugin: connect(%s) failed: %s", (db->sock != NULL) ? db->sock : "", sstrerror(errno, errbuf, sizeof(errbuf))); close(fd); - return (-1); + return -1; } ssize_t red; @@ -838,13 +850,13 @@ static int cj_sock_perform(cj_t *db) /* {{{ */ (db->sock != NULL) ? db->sock : "", sstrerror(errno, errbuf, sizeof(errbuf))); close(fd); - return (-1); + return -1; } if (!cj_curl_callback(buffer, red, 1, db)) break; } while (red > 0); close(fd); - return (0); + return 0; } /* }}} int cj_sock_perform */ static int cj_curl_perform(cj_t *db) /* {{{ */ @@ -852,13 +864,14 @@ static int cj_curl_perform(cj_t *db) /* {{{ */ int status; long rc; char *url; - url = db->url; + + curl_easy_setopt(db->curl, CURLOPT_URL, db->url); status = curl_easy_perform(db->curl); if (status != CURLE_OK) { ERROR("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)", - status, db->curl_errbuf, url); - return (-1); + status, db->curl_errbuf, db->url); + return -1; } if (db->stats != NULL) curl_stats_dispatch(db->stats, db->curl, cj_host(db), "curl_json", @@ -872,9 +885,9 @@ static int cj_curl_perform(cj_t *db) /* {{{ */ ERROR("curl_json plugin: curl_easy_perform failed with " "response code %ld (%s)", rc, url); - return (-1); + return -1; } - return (0); + return 0; } /* }}} int cj_curl_perform */ static int cj_perform(cj_t *db) /* {{{ */ @@ -892,7 +905,7 @@ static int cj_perform(cj_t *db) /* {{{ */ if (db->yajl == NULL) { ERROR("curl_json plugin: yajl_alloc failed."); db->yajl = yprev; - return (-1); + return -1; } if (db->url) @@ -902,7 +915,7 @@ static int cj_perform(cj_t *db) /* {{{ */ if (status < 0) { yajl_free(db->yajl); db->yajl = yprev; - return (-1); + return -1; } #if HAVE_YAJL_V2 @@ -919,12 +932,12 @@ static int cj_perform(cj_t *db) /* {{{ */ yajl_free_error(db->yajl, errmsg); yajl_free(db->yajl); db->yajl = yprev; - return (-1); + return -1; } yajl_free(db->yajl); db->yajl = yprev; - return (0); + return 0; } /* }}} int cj_perform */ static int cj_read(user_data_t *ud) /* {{{ */ @@ -933,16 +946,26 @@ static int cj_read(user_data_t *ud) /* {{{ */ if ((ud == NULL) || (ud->data == NULL)) { ERROR("curl_json plugin: cj_read: Invalid user data."); - return (-1); + return -1; } db = (cj_t *)ud->data; db->depth = 0; memset(&db->state, 0, sizeof(db->state)); - db->state[db->depth].tree = db->tree; - 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) /* {{{ */ @@ -950,7 +973,7 @@ static int cj_init(void) /* {{{ */ /* Call this while collectd is still single-threaded to avoid * initialization issues in libgcrypt. */ curl_global_init(CURL_GLOBAL_SSL); - return (0); + return 0; } /* }}} int cj_init */ void module_register(void) {