src/utils_cmd_flush.c: Fix parsing of the "timeout" option.
[collectd.git] / src / curl_json.c
index 53e8abd..fbac7ad 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/curl_json.c
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2006-2009  Florian octo Forster
+ * Copyright (C) 2006-2010  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
@@ -98,18 +98,12 @@ static size_t cj_curl_callback (void *buf, /* {{{ */
     return (0);
 
   status = yajl_parse(db->yajl, (unsigned char *)buf, len);
-  if (status == yajl_status_ok)
-  {
-    status = yajl_parse_complete(db->yajl);
-    return (len);
-  }
-  else if (status == yajl_status_insufficient_data)
-    return (len);
-
-  if (status != yajl_status_ok)
+  if ((status != yajl_status_ok)
+      && (status != yajl_status_insufficient_data))
   {
     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 */
@@ -130,59 +124,57 @@ static int cj_get_type (cj_key_t *key)
 }
 
 /* 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, unsigned int 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)
@@ -209,7 +201,7 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
       db->state[db->depth].key = NULL;
   }
 
-  return 1;
+  return (CJ_CB_CONTINUE);
 }
 
 static int cj_cb_string (void *ctx, const unsigned char *val,
@@ -220,7 +212,7 @@ static int cj_cb_string (void *ctx, const unsigned char *val,
   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 */
 
@@ -242,7 +234,7 @@ static int cj_cb_string (void *ctx, const unsigned char *val,
     cj_curl_perform (db, curl);
     curl_easy_cleanup (curl);
   }
-  return 1;
+  return (CJ_CB_CONTINUE);
 }
 
 static int cj_cb_start (void *ctx)
@@ -251,9 +243,9 @@ 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)
@@ -261,7 +253,7 @@ 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)
@@ -287,9 +279,9 @@ static int cj_cb_end_array (void * 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,
@@ -673,7 +665,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
                db->instance, db->url);
 
-    plugin_register_complex_read (cb_name, cj_read,
+    plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
   }
   else
@@ -770,27 +762,45 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
   }
 
   status = curl_easy_perform (curl);
-
-  yajl_free (db->yajl);
-  db->yajl = yprev;
+  if (status != 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);
+  }
 
   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);
+    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)
+  status = yajl_parse_complete (db->yajl);
+  if (status != yajl_status_ok)
   {
-    ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
-           status, db->curl_errbuf, url);
+    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 */