diff --git a/src/curl_json.c b/src/curl_json.c
index a7997907b74d46bd94fa18fff6e436ca4987c006..de66862c7cd415916df9182d43ad77387efd516a 100644 (file)
--- a/src/curl_json.c
+++ b/src/curl_json.c
#include "utils_avltree.h"
#include "utils_complain.h"
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
#include <curl/curl.h>
+
#include <yajl/yajl_parse.h>
#if HAVE_YAJL_YAJL_VERSION_H
# include <yajl/yajl_version.h>
#define CJ_DEFAULT_HOST "localhost"
#define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
-#define CJ_IS_KEY(key) (key)->magic == CJ_KEY_MAGIC
+#define CJ_IS_KEY(key) ((key)->magic == CJ_KEY_MAGIC)
#define CJ_ANY "*"
#define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y))
char *instance;
char *host;
+ char *sock;
+
char *url;
char *user;
char *pass;
char *credentials;
- int verify_peer;
- int verify_host;
+ _Bool verify_peer;
+ _Bool verify_host;
char *cacert;
+ struct curl_slist *headers;
+ char *post_body;
CURL *curl;
char curl_errbuf[CURL_ERROR_SIZE];
#endif
static int cj_read (user_data_t *ud);
-static int cj_curl_perform (cj_t *db, CURL *curl);
static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
static size_t cj_curl_callback (void *buf, /* {{{ */
if (status != yajl_status_ok)
{
unsigned char *msg =
- yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
+ yajl_get_error(db->yajl, /* verbose = */ 1,
+ /* 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 */
cj_t *db = (cj_t *)ctx;
cj_key_t *key = db->state[db->depth].key;
- char *endptr;
value_t vt;
int type;
+ int status;
- if (key == NULL)
+ if ((key == NULL) || !CJ_IS_KEY (key))
return (CJ_CB_CONTINUE);
memcpy (buffer, number, number_len);
buffer[sizeof (buffer) - 1] = 0;
type = cj_get_type (key);
- if (type < 0)
- return (CJ_CB_CONTINUE);
-
- endptr = NULL;
- errno = 0;
-
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) strtoull (buffer, &endptr, /* base = */ 0);
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) strtod (buffer, &endptr);
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) strtoll (buffer, &endptr, /* base = */ 0);
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) strtoull (buffer, &endptr, /* base = */ 0);
- else
- {
- ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type);
- return (CJ_CB_ABORT);
- }
-
- if ((endptr == &buffer[0]) || (errno != 0))
+ status = parse_value (buffer, &vt, type);
+ if (status != 0)
{
- NOTICE ("curl_json plugin: Overflow while parsing number. "
- "Ignoring this value.");
+ NOTICE ("curl_json plugin: Unable to parse number: \"%s\"", buffer);
return (CJ_CB_CONTINUE);
}
yajl_len_t len)
{
cj_t *db = (cj_t *)ctx;
- c_avl_tree_t *tree;
- char *ptr;
-
- if (db->depth != 1) /* e.g. _all_dbs */
- return (CJ_CB_CONTINUE);
+ char str[len + 1];
- cj_cb_map_key (ctx, val, len); /* same logic */
+ /* Create a null-terminated version of the string. */
+ memcpy (str, val, len);
+ str[len] = 0;
- tree = db->state[db->depth].tree;
+ /* No configuration for this string -> simply return. */
+ if (db->state[db->depth].key == NULL)
+ return (CJ_CB_CONTINUE);
- if ((tree != NULL) && (ptr = rindex (db->url, '/')))
+ if (!CJ_IS_KEY (db->state[db->depth].key))
{
- char url[PATH_MAX];
- CURL *curl;
-
- /* url =~ s,[^/]+$,$name, */
- len = (ptr - db->url) + 1;
- ptr = url;
- sstrncpy (ptr, db->url, sizeof (url));
- sstrncpy (ptr + len, db->state[db->depth].name, sizeof (url) - len);
-
- curl = curl_easy_duphandle (db->curl);
- curl_easy_setopt (curl, CURLOPT_URL, url);
- cj_curl_perform (db, curl);
- curl_easy_cleanup (curl);
+ NOTICE ("curl_json plugin: Found string \"%s\", but the configuration "
+ "expects a map here.", str);
+ return (CJ_CB_CONTINUE);
}
- return (CJ_CB_CONTINUE);
-}
+
+ /* Handle the string as if it was a number. */
+ 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);
+ ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url ? db->url : db->sock);
return (CJ_CB_ABORT);
}
return (CJ_CB_CONTINUE);
sfree (db->pass);
sfree (db->credentials);
sfree (db->cacert);
+ sfree (db->post_body);
+ curl_slist_free_all (db->headers);
sfree (db);
} /* }}} void cj_free */
/* Configuration handling functions {{{ */
-static int cj_config_add_string (const char *name, char **dest, /* {{{ */
- oconfig_item_t *ci)
+static c_avl_tree_t *cj_avl_create(void)
+{
+ return c_avl_create ((int (*) (const void *, const void *)) strcmp);
+}
+
+static int cj_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
+ oconfig_item_t *ci)
{
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
{
return (-1);
}
- sfree (*dest);
- *dest = strdup (ci->values[0].value.string);
+ *dest = curl_slist_append(*dest, ci->values[0].value.string);
if (*dest == NULL)
return (-1);
return (0);
-} /* }}} int cj_config_add_string */
-
-static int cj_config_set_boolean (const char *name, int *dest, /* {{{ */
- oconfig_item_t *ci)
-{
- if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
- {
- WARNING ("curl_json plugin: `%s' needs exactly one boolean argument.", name);
- return (-1);
- }
-
- *dest = ci->values[0].value.boolean ? 1 : 0;
-
- return (0);
-} /* }}} int cj_config_set_boolean */
-
-static c_avl_tree_t *cj_avl_create(void)
-{
- return c_avl_create ((int (*) (const void *, const void *)) strcmp);
-}
+} /* }}} int cj_config_append_string */
static int cj_config_add_key (cj_t *db, /* {{{ */
oconfig_item_t *ci)
if (strcasecmp ("Key", ci->key) == 0)
{
- status = cj_config_add_string ("Key", &key->path, ci);
+ status = cf_util_get_string (ci, &key->path);
if (status != 0)
{
sfree (key);
oconfig_item_t *child = ci->children + i;
if (strcasecmp ("Type", child->key) == 0)
- status = cj_config_add_string ("Type", &key->type, child);
+ status = cf_util_get_string (child, &key->type);
else if (strcasecmp ("Instance", child->key) == 0)
- status = cj_config_add_string ("Instance", &key->instance, child);
+ status = cf_util_get_string (child, &key->instance);
else
{
WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
db->verify_host ? 2L : 0L);
if (db->cacert != NULL)
curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+ if (db->headers != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_HTTPHEADER, db->headers);
+ if (db->post_body != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
return (0);
} /* }}} int cj_init_curl */
memset (db, 0, sizeof (*db));
if (strcasecmp ("URL", ci->key) == 0)
- {
- status = cj_config_add_string ("URL", &db->url, ci);
- if (status != 0)
- {
- sfree (db);
- return (status);
- }
- }
+ status = cf_util_get_string (ci, &db->url);
+ else if (strcasecmp ("Sock", ci->key) == 0)
+ status = cf_util_get_string (ci, &db->sock);
else
{
ERROR ("curl_json plugin: cj_config: "
"Invalid key: %s", ci->key);
return (-1);
}
+ if (status != 0)
+ {
+ sfree (db);
+ return (status);
+ }
/* Fill the `cj_t' structure.. */
for (i = 0; i < ci->children_num; i++)
oconfig_item_t *child = ci->children + i;
if (strcasecmp ("Instance", child->key) == 0)
- status = cj_config_add_string ("Instance", &db->instance, child);
+ status = cf_util_get_string (child, &db->instance);
else if (strcasecmp ("Host", child->key) == 0)
- status = cj_config_add_string ("Host", &db->host, child);
- else if (strcasecmp ("User", child->key) == 0)
- status = cj_config_add_string ("User", &db->user, child);
- else if (strcasecmp ("Password", child->key) == 0)
- status = cj_config_add_string ("Password", &db->pass, child);
- else if (strcasecmp ("VerifyPeer", child->key) == 0)
- status = cj_config_set_boolean ("VerifyPeer", &db->verify_peer, child);
- else if (strcasecmp ("VerifyHost", child->key) == 0)
- status = cj_config_set_boolean ("VerifyHost", &db->verify_host, child);
- else if (strcasecmp ("CACert", child->key) == 0)
- status = cj_config_add_string ("CACert", &db->cacert, child);
+ status = cf_util_get_string (child, &db->host);
+ else if (db->url && strcasecmp ("User", child->key) == 0)
+ status = cf_util_get_string (child, &db->user);
+ else if (db->url && strcasecmp ("Password", child->key) == 0)
+ status = cf_util_get_string (child, &db->pass);
+ else if (db->url && strcasecmp ("VerifyPeer", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_peer);
+ else if (db->url && strcasecmp ("VerifyHost", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_host);
+ else if (db->url && strcasecmp ("CACert", child->key) == 0)
+ status = cf_util_get_string (child, &db->cacert);
+ else if (db->url && strcasecmp ("Header", child->key) == 0)
+ status = cj_config_append_string ("Header", &db->headers, child);
+ else if (db->url && strcasecmp ("Post", child->key) == 0)
+ status = cf_util_get_string (child, &db->post_body);
else if (strcasecmp ("Key", child->key) == 0)
status = cj_config_add_key (db, child);
else
if (db->tree == NULL)
{
WARNING ("curl_json plugin: No (valid) `Key' block "
- "within `URL' block `%s'.", db->url);
+ "within `%s' block `%s'.", db->url ? "URL" : "Sock", db->url ? db->url : db->sock);
status = -1;
}
- if (status == 0)
+ if (status == 0 && db->url)
status = cj_init_curl (db);
}
ud.free_func = cj_free;
ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
- db->instance, db->url);
+ db->instance, db->url ? db->url : db->sock);
plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
/* interval = */ NULL, &ud);
{
oconfig_item_t *child = ci->children + i;
- if (strcasecmp ("URL", child->key) == 0)
+ if (strcasecmp ("Sock", child->key) == 0 || strcasecmp ("URL", child->key) == 0)
{
status = cj_config_add_url (child);
if (status == 0)
plugin_dispatch_values (&vl);
} /* }}} int cj_submit */
-static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
+static int cj_sock_perform (cj_t *db) /* {{{ */
+{
+ struct sockaddr_un sa_unix = {};
+ 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);
+ if (fd < 0)
+ 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 : "<null>", strerror(errno));
+ close (fd);
+ return (-1);
+ }
+
+ ssize_t red;
+ do {
+ unsigned char buffer[4096];
+ red = read (fd, buffer, sizeof(buffer));
+ if (red < 0) {
+ ERROR ("curl_json plugin: read(%s) failed: %s",
+ (db->sock != NULL) ? db->sock : "<null>", strerror(errno));
+ close (fd);
+ return (-1);
+ }
+ if (!cj_curl_callback (buffer, red, 1, db))
+ break;
+ } while (red > 0);
+ close (fd);
+ return (0);
+} /* }}} int cj_sock_perform */
+
+
+static int cj_curl_perform(cj_t *db) /* {{{ */
{
int status;
long rc;
char *url;
+ url = NULL;
+ curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &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 != NULL) ? url : "<null>");
+ return (-1);
+ }
+
+ curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ /* The response code is zero if a non-HTTP transport was used. */
+ if ((rc != 0) && (rc != 200))
+ {
+ ERROR ("curl_json plugin: curl_easy_perform failed with "
+ "response code %ld (%s)", rc, url);
+ return (-1);
+ }
+ return (0);
+} /* }}} int cj_curl_perform */
+
+static int cj_perform (cj_t *db) /* {{{ */
+{
+ int status;
yajl_handle yprev = db->yajl;
db->yajl = yajl_alloc (&ycallbacks,
return (-1);
}
- status = curl_easy_perform (curl);
-
- curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
-
- /* The response code is zero if a non-HTTP transport was used. */
- if ((rc != 0) && (rc != 200))
- {
- ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
- rc, url);
- yajl_free (db->yajl);
- db->yajl = yprev;
- return (-1);
- }
-
- if (status != 0)
+ if (db->url ? cj_curl_perform (db) < 0 : cj_sock_perform (db) < 0)
{
- ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
- status, db->curl_errbuf, url);
yajl_free (db->yajl);
db->yajl = yprev;
return (-1);
}
#if HAVE_YAJL_V2
- status = yajl_complete_parse(db->yajl);
+ status = yajl_complete_parse(db->yajl);
#else
- status = yajl_parse_complete(db->yajl);
+ status = yajl_parse_complete(db->yajl);
#endif
if (status != yajl_status_ok)
{
- ERROR ("curl_json plugin: %s failed with status %i.",
-#if HAVE_YAJL_V2
- "yajl_complete_parse",
-#else
- "yajl_parse_complete",
-#endif
- status);
+ unsigned char *errmsg;
+
+ errmsg = yajl_get_error (db->yajl, /* verbose = */ 0,
+ /* jsonText = */ NULL, /* jsonTextLen = */ 0);
+ ERROR ("curl_json plugin: yajl_parse_complete failed: %s",
+ (char *) errmsg);
+ yajl_free_error (db->yajl, errmsg);
+ yajl_free (db->yajl);
+ db->yajl = yprev;
+ return (-1);
}
yajl_free (db->yajl);
db->yajl = yprev;
-
return (0);
-} /* }}} int cj_curl_perform */
+} /* }}} int cj_perform */
static int cj_read (user_data_t *ud) /* {{{ */
{
db->state[db->depth].tree = db->tree;
db->key = NULL;
- return cj_curl_perform (db, db->curl);
+ return cj_perform (db);
} /* }}} int cj_read */
void module_register (void)