Code

curl_json: fix the array access implemented in f1e1e37e
[collectd.git] / src / curl_json.c
index 0b3e0e6782828866af8377913d7d07f5bb89a206..6dec89e518d2515b33c6ba4afc7aafaadbf2506a 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/curl_json.c
  * Copyright (C) 2009       Doug MacEachern
- * Copyright (C) 2006-2011  Florian octo Forster
+ * Copyright (C) 2006-2013  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
@@ -53,10 +53,10 @@ 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;
-  unsigned long magic;
 };
 /* }}} */
 
@@ -178,19 +178,22 @@ static int cj_get_type (cj_key_t *key)
 static int cj_cb_map_key (void *ctx, const unsigned char *val,
     yajl_len_t len);
 
-static void cj_cb_inc_array_index (void * ctx, _Bool ignore)
+static void cj_cb_inc_array_index (void *ctx, _Bool update_key)
 {
   cj_t *db = (cj_t *)ctx;
 
-  if (db->state[db->depth].in_array) {
-    if (ignore)
-      db->state[db->depth].index++;
-    else {
-      char name[DATA_MAX_NAME_LEN];
-      cj_cb_map_key (ctx, (unsigned char *)name,
-                     ssnprintf (name, sizeof (name),
-                                "%d", db->state[db->depth].index++));
-    }
+  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));
   }
 }
 
@@ -200,18 +203,16 @@ static void cj_cb_inc_array_index (void * ctx, _Bool ignore)
 
 static int cj_cb_boolean (void * ctx, int boolVal)
 {
-  cj_cb_inc_array_index (ctx, 1);
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
   return (CJ_CB_CONTINUE);
 }
 
 static int cj_cb_null (void * ctx)
 {
-  cj_cb_inc_array_index (ctx, 1);
+  cj_cb_inc_array_index (ctx, /* update_key = */ 0);
   return (CJ_CB_CONTINUE);
 }
 
-/* "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)
 {
@@ -223,19 +224,25 @@ static int cj_cb_number (void *ctx,
   int type;
   int status;
 
-  if ((key == NULL) || !CJ_IS_KEY (key)) {
-    if (key != NULL)
-      NOTICE ("curl_json plugin: Found \"%.*s\", but the configuration expects"
-              " a map.", (int)number_len > number_len ? 0 : (int)number_len,
-              number);
-    cj_cb_inc_array_index (ctx, 1);
-    return (CJ_CB_CONTINUE);
-  } else
-    cj_cb_inc_array_index (ctx, 0);
-
+  /* 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)
+      NOTICE ("curl_json plugin: Found \"%s\", but the configuration expects"
+              " a map.", buffer);
+    cj_cb_inc_array_index (ctx, /* update_key = */ 1);
+    key = db->state[db->depth].key;
+    if (key == NULL) {
+      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 (status != 0)
@@ -248,8 +255,11 @@ static int cj_cb_number (void *ctx,
   return (CJ_CB_CONTINUE);
 } /* int cj_cb_number */
 
-static int cj_cb_map_key (void *ctx, const unsigned char *val,
-    yajl_len_t len)
+/* Queries the key-tree of the parent context for "in_name" and, if found,
+ * updates the "key" field of the current context. Otherwise, "key" is set to
+ * 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;
@@ -258,17 +268,32 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val,
 
   if (tree != NULL)
   {
-    cj_key_t *value;
+    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;
-    len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
-    sstrncpy (name, (char *)val, len+1);
-
-    if (c_avl_get (tree, name, (void *) &value) == 0)
-      db->state[db->depth].key = value;
+    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)
-      db->state[db->depth].key = value;
+      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;
   }
@@ -305,7 +330,7 @@ static int cj_cb_end (void *ctx)
 
 static int cj_cb_start_map (void *ctx)
 {
-  cj_cb_inc_array_index (ctx, 0);
+  cj_cb_inc_array_index (ctx, /* update_key = */ 1);
   return cj_cb_start (ctx);
 }
 
@@ -317,7 +342,7 @@ static int cj_cb_end_map (void *ctx)
 static int cj_cb_start_array (void * ctx)
 {
   cj_t *db = (cj_t *)ctx;
-  cj_cb_inc_array_index (ctx, 0);
+  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;
@@ -540,6 +565,7 @@ static int cj_config_add_key (cj_t *db, /* {{{ */
         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)
@@ -704,7 +730,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
   if (status == 0)
   {
     user_data_t ud;
-    char cb_name[DATA_MAX_NAME_LEN];
+    char *cb_name;
 
     if (db->instance == NULL)
       db->instance = strdup("default");
@@ -716,11 +742,12 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ud.data = (void *) db;
     ud.free_func = cj_free;
 
-    ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
+    cb_name = ssnprintf_alloc ("curl_json-%s-%s",
                db->instance, db->url ? db->url : db->sock);
 
-    plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
+    plugin_register_complex_read (/* group = */ "curl_json", cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
+    sfree (cb_name);
   }
   else
   {
@@ -850,17 +877,17 @@ 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);
+  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 != NULL) ? url : "<null>");
+           status, db->curl_errbuf, url);
     return (-1);
   }
 
+  curl_easy_getinfo(db->curl, CURLINFO_EFFECTIVE_URL, &url);
   curl_easy_getinfo(db->curl, CURLINFO_RESPONSE_CODE, &rc);
 
   /* The response code is zero if a non-HTTP transport was used. */
@@ -947,9 +974,18 @@ static int cj_read (user_data_t *ud) /* {{{ */
   return cj_perform (db);
 } /* }}} int cj_read */
 
+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);
+} /* }}} int cj_init */
+
 void module_register (void)
 {
   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 : */