yajl_handle yajl;
c_avl_tree_t *tree;
- cj_key_t *key;
int depth;
struct {
union {
#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) {
return ds->ds[0].type;
}
-static int cj_cb_map_key(void *ctx, const unsigned char *val, yajl_len_t len);
+/* cj_load_key loads the configuration for "key" from the parent context and
+ * sets either .key or .tree in the current context. */
+static int cj_load_key(cj_t *db, char const *key) {
+ if (db == NULL || key == NULL || db->depth <= 0)
+ return EINVAL;
-static void cj_cb_inc_array_index(void *ctx, _Bool update_key) {
- cj_t *db = (cj_t *)ctx;
+ 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) {
+ 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;
+ }
+
+ 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;
+ }
+ } else {
+ db->state[db->depth].key = NULL;
+ }
+
+ return 0;
+}
+static void cj_advance_array(cj_t *db) {
if (!db->state[db->depth].in_array)
return;
db->state[db->depth].index++;
- if (update_key) {
- char name[DATA_MAX_NAME_LEN];
-
- ssnprintf(name, sizeof(name), "%d", db->state[db->depth].index - 1);
-
- cj_cb_map_key(ctx, (unsigned char *)name, (yajl_len_t)strlen(name));
- }
+ char name[DATA_MAX_NAME_LEN];
+ ssnprintf(name, sizeof(name), "%d", db->state[db->depth].index);
+ cj_load_key(db, name);
}
/* yajl callbacks */
#define CJ_CB_CONTINUE 1
static int cj_cb_boolean(void *ctx, int boolVal) {
- cj_cb_inc_array_index(ctx, /* update_key = */ 0);
+ cj_advance_array(ctx);
return (CJ_CB_CONTINUE);
}
static int cj_cb_null(void *ctx) {
- cj_cb_inc_array_index(ctx, /* update_key = */ 0);
+ cj_advance_array(ctx);
return (CJ_CB_CONTINUE);
}
cj_t *db = (cj_t *)ctx;
cj_key_t *key = db->state[db->depth].key;
- value_t vt;
- int type;
- int status;
/* Create a null-terminated version of the string. */
memcpy(buffer, number, number_len);
buffer[sizeof(buffer) - 1] = 0;
- if ((key == NULL) || !CJ_IS_KEY(key)) {
- if (key != NULL &&
- !db->state[db->depth].in_array /*can be inhomogeneous*/) {
- NOTICE("curl_json plugin: Found \"%s\", but the configuration expects"
- " a map.",
- buffer);
- return (CJ_CB_CONTINUE);
- }
- cj_cb_inc_array_index(ctx, /* update_key = */ 1);
- key = db->state[db->depth].key;
- if ((key == NULL) || !CJ_IS_KEY(key)) {
- return (CJ_CB_CONTINUE);
- }
- } else {
- cj_cb_inc_array_index(ctx, /* update_key = */ 1);
- }
- type = cj_get_type(key);
- status = parse_value(buffer, &vt, type);
+ 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);
+ cj_advance_array(ctx);
+ return CJ_CB_CONTINUE;
+ }
+
+ 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);
}
cj_submit(db, key, &vt);
+ cj_advance_array(ctx);
return (CJ_CB_CONTINUE);
} /* int cj_cb_number */
* NULL. */
static int cj_cb_map_key(void *ctx, unsigned char const *in_name,
yajl_len_t in_name_len) {
- cj_t *db = (cj_t *)ctx;
- c_avl_tree_t *tree;
+ char name[in_name_len + 1];
- tree = db->state[db->depth - 1].tree;
-
- if (tree != NULL) {
- cj_key_t *value = NULL;
- char *name;
- size_t name_len;
-
- /* Create a null-terminated version of the name. */
- name = db->state[db->depth].name;
- name_len =
- COUCH_MIN((size_t)in_name_len, sizeof(db->state[db->depth].name) - 1);
- memcpy(name, in_name, name_len);
- name[name_len] = 0;
-
- if (c_avl_get(tree, name, (void *)&value) == 0) {
- if (CJ_IS_KEY((cj_key_t *)value)) {
- db->state[db->depth].key = value;
- } else {
- db->state[db->depth].tree = (c_avl_tree_t *)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 = (c_avl_tree_t *)value;
- }
- else
- db->state[db->depth].key = NULL;
- }
+ memmove(name, in_name, in_name_len);
+ name[sizeof(name) - 1] = 0;
- return (CJ_CB_CONTINUE);
+ if (cj_load_key(ctx, name) != 0)
+ return CJ_CB_ABORT;
+
+ return CJ_CB_CONTINUE;
}
static int cj_cb_string(void *ctx, const unsigned char *val, yajl_len_t len) {
return (cj_cb_number(ctx, (const char *)val, len));
} /* int cj_cb_string */
-static int cj_cb_start(void *ctx) {
- cj_t *db = (cj_t *)ctx;
- if (++db->depth >= 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_CONTINUE);
-}
-
static int cj_cb_end(void *ctx) {
cj_t *db = (cj_t *)ctx;
db->state[db->depth].tree = NULL;
- --db->depth;
+ db->depth--;
+ cj_advance_array(ctx);
return (CJ_CB_CONTINUE);
}
static int cj_cb_start_map(void *ctx) {
- cj_cb_inc_array_index(ctx, /* update_key = */ 1);
- return cj_cb_start(ctx);
+ cj_t *db = (cj_t *)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);
+ }
+ db->depth++;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_end_map(void *ctx) { return cj_cb_end(ctx); }
static int cj_cb_start_array(void *ctx) {
cj_t *db = (cj_t *)ctx;
- cj_cb_inc_array_index(ctx, /* update_key = */ 1);
- if (db->depth + 1 < YAJL_MAX_DEPTH) {
- db->state[db->depth + 1].in_array = 1;
- db->state[db->depth + 1].index = 0;
+
+ 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_start(ctx);
+ db->depth++;
+ db->state[db->depth].in_array = 1;
+ db->state[db->depth].index = 0;
+
+ cj_load_key(db, "0");
+
+ return CJ_CB_CONTINUE;
}
static int cj_cb_end_array(void *ctx) {
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);
+
+ 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);
+ }
+
+ tree = value;
+ start = end + 1;
+ }
+
+ if (strlen(start) == 0) {
+ ERROR("curl_json plugin: invalid key: %s", key->path);
+ return -1;
+ }
+
+ c_avl_insert(tree, strdup(start), key);
+ return 0;
+} /* }}} int cj_append_key */
+
static int cj_config_add_key(cj_t *db, /* {{{ */
oconfig_item_t *ci) {
cj_key_t *key;
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) /* {{{ */
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;
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);
db->depth = 0;
memset(&db->state, 0, sizeof(db->state));
db->state[db->depth].tree = db->tree;
- db->key = NULL;
return cj_perform(db);
} /* }}} int cj_read */
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 : */