/**
* collectd - src/curl_json.c
* Copyright (C) 2009 Doug MacEachern
- * Copyright (C) 2006-2009 Florian octo Forster
+ * Copyright (C) 2006-2011 Florian octo Forster
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
*
* Authors:
* Doug MacEachern <dougm at hyperic.com>
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
#include <curl/curl.h>
#include <yajl/yajl_parse.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+# include <yajl/yajl_version.h>
+#endif
+
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+# define HAVE_YAJL_V2 1
+#endif
#define CJ_DEFAULT_HOST "localhost"
#define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
};
typedef struct cj_s cj_t; /* }}} */
+#if HAVE_YAJL_V2
+typedef size_t yajl_len_t;
+#else
+typedef unsigned int yajl_len_t;
+#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);
status = yajl_parse(db->yajl, (unsigned char *)buf, len);
if (status == yajl_status_ok)
{
+#if HAVE_YAJL_V2
+ status = yajl_complete_parse(db->yajl);
+#else
status = yajl_parse_complete(db->yajl);
+#endif
return (len);
}
+#if !HAVE_YAJL_V2
else if (status == yajl_status_insufficient_data)
return (len);
+#endif
if (status != yajl_status_ok)
{
}
/* yajl callbacks */
-static int cj_cb_integer (void *ctx, long val)
+#define CJ_CB_ABORT 0
+#define CJ_CB_CONTINUE 1
+
+/* "number" may not be null terminated, so copy it into a buffer before
+ * parsing. */
+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;
+ char *endptr;
+ value_t vt;
+ int type;
- if (key != NULL)
- {
- value_t vt;
- int type;
+ if (key == NULL)
+ return (CJ_CB_CONTINUE);
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
+ memcpy (buffer, number, number_len);
+ buffer[sizeof (buffer) - 1] = 0;
- cj_submit (db, key, &vt);
- }
- return 1;
-}
+ type = cj_get_type (key);
-static int cj_cb_double (void *ctx, double val)
-{
- cj_t *db = (cj_t *)ctx;
- cj_key_t *key = db->state[db->depth].key;
+ endptr = NULL;
+ errno = 0;
- if (key != NULL)
+ 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
{
- value_t vt;
- int type;
-
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
+ ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type);
+ return (CJ_CB_ABORT);
+ }
- cj_submit (db, key, &vt);
+ if ((endptr == &buffer[0]) || (errno != 0))
+ {
+ NOTICE ("curl_json plugin: Overflow while parsing number. "
+ "Ignoring this value.");
+ return (CJ_CB_CONTINUE);
}
- return 1;
-}
+
+ cj_submit (db, key, &vt);
+ return (CJ_CB_CONTINUE);
+} /* int cj_cb_number */
static int cj_cb_map_key (void *ctx, const unsigned char *val,
- unsigned int len)
+ yajl_len_t len)
{
cj_t *db = (cj_t *)ctx;
c_avl_tree_t *tree;
db->state[db->depth].key = NULL;
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_string (void *ctx, const unsigned char *val,
- unsigned int len)
+ 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 1;
+ return (CJ_CB_CONTINUE);
cj_cb_map_key (ctx, val, len); /* same logic */
cj_curl_perform (db, curl);
curl_easy_cleanup (curl);
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_start (void *ctx)
if (++db->depth >= YAJL_MAX_DEPTH)
{
ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
- return 0;
+ return (CJ_CB_ABORT);
}
- return 1;
+ 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;
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_start_map (void *ctx)
static int cj_cb_end_array (void * ctx)
{
- return cj_cb_start (ctx);
+ return cj_cb_end (ctx);
}
static yajl_callbacks ycallbacks = {
NULL, /* null */
NULL, /* boolean */
- cj_cb_integer,
- cj_cb_double,
- NULL, /* number */
+ NULL, /* integer */
+ NULL, /* double */
+ cj_cb_number,
cj_cb_string,
cj_cb_start_map,
cj_cb_map_key,
return (-1);
}
+ curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_curl_callback);
curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
char *url;
yajl_handle yprev = db->yajl;
- db->yajl = yajl_alloc (&ycallbacks, NULL, NULL, (void *)db);
+ db->yajl = yajl_alloc (&ycallbacks,
+#if HAVE_YAJL_V2
+ /* alloc funcs = */ NULL,
+#else
+ /* alloc funcs = */ NULL, NULL,
+#endif
+ /* context = */ (void *)db);
if (db->yajl == NULL)
{
ERROR ("curl_json plugin: yajl_alloc failed.");
+ db->yajl = yprev;
return (-1);
}
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
- if (rc != 200)
+ /* 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);