Code

curl_json plugin: Be more clever building the type instance.
[collectd.git] / src / curl_json.c
1 /**
2  * collectd - src/curl_json.c
3  * Copyright (C) 2009       Doug MacEachern
4  * Copyright (C) 2006-2011  Florian octo Forster
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Doug MacEachern <dougm at hyperic.com>
21  *   Florian octo Forster <octo at collectd.org>
22  **/
24 #include "collectd.h"
25 #include "common.h"
26 #include "plugin.h"
27 #include "configfile.h"
28 #include "utils_avltree.h"
29 #include "utils_complain.h"
31 #include <curl/curl.h>
32 #include <yajl/yajl_parse.h>
33 #if HAVE_YAJL_YAJL_VERSION_H
34 # include <yajl/yajl_version.h>
35 #endif
37 #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
38 # define HAVE_YAJL_V2 1
39 #endif
41 #define CJ_DEFAULT_HOST "localhost"
42 #define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
43 #define CJ_IS_KEY(key) (key)->magic == CJ_KEY_MAGIC
44 #define CJ_ANY "*"
45 #define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y))
47 struct cj_key_s;
48 typedef struct cj_key_s cj_key_t;
49 struct cj_key_s /* {{{ */
50 {
51   char *path;
52   char *type;
53   char *instance;
54   unsigned long magic;
55 };
56 /* }}} */
58 struct cj_s /* {{{ */
59 {
60   char *instance;
61   char *host;
63   char *url;
64   char *user;
65   char *pass;
66   char *credentials;
67   int   verify_peer;
68   int   verify_host;
69   char *cacert;
71   CURL *curl;
72   char curl_errbuf[CURL_ERROR_SIZE];
74   yajl_handle yajl;
75   c_avl_tree_t *tree;
76   cj_key_t *key;
77   int depth;
78   struct {
79     union {
80       c_avl_tree_t *tree;
81       cj_key_t *key;
82     };
83     char name[DATA_MAX_NAME_LEN];
84   } state[YAJL_MAX_DEPTH];
85 };
86 typedef struct cj_s cj_t; /* }}} */
88 #if HAVE_YAJL_V2
89 typedef size_t yajl_len_t;
90 #else
91 typedef unsigned int yajl_len_t;
92 #endif
94 static int cj_read (user_data_t *ud);
95 static int cj_curl_perform (cj_t *db, CURL *curl);
96 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
98 static size_t cj_curl_callback (void *buf, /* {{{ */
99     size_t size, size_t nmemb, void *user_data)
101   cj_t *db;
102   size_t len;
103   yajl_status status;
105   len = size * nmemb;
107   if (len <= 0)
108     return (len);
110   db = user_data;
111   if (db == NULL)
112     return (0);
114   status = yajl_parse(db->yajl, (unsigned char *)buf, len);
115   if (status == yajl_status_ok)
116   {
117 #if HAVE_YAJL_V2
118     status = yajl_complete_parse(db->yajl);
119 #else
120     status = yajl_parse_complete(db->yajl);
121 #endif
122     return (len);
123   }
124 #if !HAVE_YAJL_V2
125   else if (status == yajl_status_insufficient_data)
126     return (len);
127 #endif
129   if (status != yajl_status_ok)
130   {
131     unsigned char *msg =
132       yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
133     ERROR ("curl_json plugin: yajl_parse failed: %s", msg);
134     yajl_free_error(db->yajl, msg);
135     return (0); /* abort write callback */
136   }
138   return (len);
139 } /* }}} size_t cj_curl_callback */
141 static int cj_get_type (cj_key_t *key)
143   const data_set_t *ds;
145   ds = plugin_get_ds (key->type);
146   if (ds == NULL)
147   {
148     static char type[DATA_MAX_NAME_LEN] = "!!!invalid!!!";
150     assert (key->type != NULL);
151     if (strcmp (type, key->type) != 0)
152     {
153       ERROR ("curl_json plugin: Unable to look up DS type \"%s\".",
154           key->type);
155       sstrncpy (type, key->type, sizeof (type));
156     }
158     return -1;
159   }
160   else if (ds->ds_num > 1)
161   {
162     static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
164     c_complain_once (LOG_WARNING, &complaint,
165         "curl_json plugin: The type \"%s\" has more than one data source. "
166         "This is currently not supported. I will return the type of the "
167         "first data source, but this will likely lead to problems later on.",
168         key->type);
169   }
171   return ds->ds[0].type;
174 /* yajl callbacks */
175 #define CJ_CB_ABORT    0
176 #define CJ_CB_CONTINUE 1
178 /* "number" may not be null terminated, so copy it into a buffer before
179  * parsing. */
180 static int cj_cb_number (void *ctx,
181     const char *number, yajl_len_t number_len)
183   char buffer[number_len + 1];
185   cj_t *db = (cj_t *)ctx;
186   cj_key_t *key = db->state[db->depth].key;
187   char *endptr;
188   value_t vt;
189   int type;
191   if (key == NULL)
192     return (CJ_CB_CONTINUE);
194   memcpy (buffer, number, number_len);
195   buffer[sizeof (buffer) - 1] = 0;
197   type = cj_get_type (key);
198   if (type < 0)
199     return (CJ_CB_CONTINUE);
201   endptr = NULL;
202   errno = 0;
204   if (type == DS_TYPE_COUNTER)
205     vt.counter = (counter_t) strtoull (buffer, &endptr, /* base = */ 0);
206   else if (type == DS_TYPE_GAUGE)
207     vt.gauge = (gauge_t) strtod (buffer, &endptr);
208   else if (type == DS_TYPE_DERIVE)
209     vt.derive = (derive_t) strtoll (buffer, &endptr, /* base = */ 0);
210   else if (type == DS_TYPE_ABSOLUTE)
211     vt.absolute = (absolute_t) strtoull (buffer, &endptr, /* base = */ 0);
212   else
213   {
214     ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type);
215     return (CJ_CB_ABORT);
216   }
218   if ((endptr == &buffer[0]) || (errno != 0))
219   {
220     NOTICE ("curl_json plugin: Overflow while parsing number. "
221         "Ignoring this value.");
222     return (CJ_CB_CONTINUE);
223   }
225   cj_submit (db, key, &vt);
226   return (CJ_CB_CONTINUE);
227 } /* int cj_cb_number */
229 static int cj_cb_map_key (void *ctx, const unsigned char *val,
230     yajl_len_t len)
232   cj_t *db = (cj_t *)ctx;
233   c_avl_tree_t *tree;
235   tree = db->state[db->depth-1].tree;
237   if (tree != NULL)
238   {
239     cj_key_t *value;
240     char *name;
242     name = db->state[db->depth].name;
243     len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
244     sstrncpy (name, (char *)val, len+1);
246     if (c_avl_get (tree, name, (void *) &value) == 0)
247       db->state[db->depth].key = value;
248     else if (c_avl_get (tree, CJ_ANY, (void *) &value) == 0)
249       db->state[db->depth].key = value;
250     else
251       db->state[db->depth].key = NULL;
252   }
254   return (CJ_CB_CONTINUE);
257 static int cj_cb_string (void *ctx, const unsigned char *val,
258     yajl_len_t len)
260   cj_t *db = (cj_t *)ctx;
261   c_avl_tree_t *tree;
262   char *ptr;
264   if (db->depth != 1) /* e.g. _all_dbs */
265     return (CJ_CB_CONTINUE);
267   cj_cb_map_key (ctx, val, len); /* same logic */
269   tree = db->state[db->depth].tree;
271   if ((tree != NULL) && (ptr = rindex (db->url, '/')))
272   {
273     char url[PATH_MAX];
274     CURL *curl;
276     /* url =~ s,[^/]+$,$name, */
277     len = (ptr - db->url) + 1;
278     ptr = url;
279     sstrncpy (ptr, db->url, sizeof (url));
280     sstrncpy (ptr + len, db->state[db->depth].name, sizeof (url) - len);
282     curl = curl_easy_duphandle (db->curl);
283     curl_easy_setopt (curl, CURLOPT_URL, url);
284     cj_curl_perform (db, curl);
285     curl_easy_cleanup (curl);
286   }
287   return (CJ_CB_CONTINUE);
290 static int cj_cb_start (void *ctx)
292   cj_t *db = (cj_t *)ctx;
293   if (++db->depth >= YAJL_MAX_DEPTH)
294   {
295     ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
296     return (CJ_CB_ABORT);
297   }
298   return (CJ_CB_CONTINUE);
301 static int cj_cb_end (void *ctx)
303   cj_t *db = (cj_t *)ctx;
304   db->state[db->depth].tree = NULL;
305   --db->depth;
306   return (CJ_CB_CONTINUE);
309 static int cj_cb_start_map (void *ctx)
311   return cj_cb_start (ctx);
314 static int cj_cb_end_map (void *ctx)
316   return cj_cb_end (ctx);
319 static int cj_cb_start_array (void * ctx)
321   return cj_cb_start (ctx);
324 static int cj_cb_end_array (void * ctx)
326   return cj_cb_end (ctx);
329 static yajl_callbacks ycallbacks = {
330   NULL, /* null */
331   NULL, /* boolean */
332   NULL, /* integer */
333   NULL, /* double */
334   cj_cb_number,
335   cj_cb_string,
336   cj_cb_start_map,
337   cj_cb_map_key,
338   cj_cb_end_map,
339   cj_cb_start_array,
340   cj_cb_end_array
341 };
343 /* end yajl callbacks */
345 static void cj_key_free (cj_key_t *key) /* {{{ */
347   if (key == NULL)
348     return;
350   sfree (key->path);
351   sfree (key->type);
352   sfree (key->instance);
354   sfree (key);
355 } /* }}} void cj_key_free */
357 static void cj_tree_free (c_avl_tree_t *tree) /* {{{ */
359   char *name;
360   void *value;
362   while (c_avl_pick (tree, (void *) &name, (void *) &value) == 0)
363   {
364     cj_key_t *key = (cj_key_t *)value;
366     if (CJ_IS_KEY(key))
367       cj_key_free (key);
368     else
369       cj_tree_free ((c_avl_tree_t *)value);
371     sfree (name);
372   }
374   c_avl_destroy (tree);
375 } /* }}} void cj_tree_free */
377 static void cj_free (void *arg) /* {{{ */
379   cj_t *db;
381   DEBUG ("curl_json plugin: cj_free (arg = %p);", arg);
383   db = (cj_t *) arg;
385   if (db == NULL)
386     return;
388   if (db->curl != NULL)
389     curl_easy_cleanup (db->curl);
390   db->curl = NULL;
392   if (db->tree != NULL)
393     cj_tree_free (db->tree);
394   db->tree = NULL;
396   sfree (db->instance);
397   sfree (db->host);
399   sfree (db->url);
400   sfree (db->user);
401   sfree (db->pass);
402   sfree (db->credentials);
403   sfree (db->cacert);
405   sfree (db);
406 } /* }}} void cj_free */
408 /* Configuration handling functions {{{ */
410 static int cj_config_add_string (const char *name, char **dest, /* {{{ */
411                                       oconfig_item_t *ci)
413   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
414   {
415     WARNING ("curl_json plugin: `%s' needs exactly one string argument.", name);
416     return (-1);
417   }
419   sfree (*dest);
420   *dest = strdup (ci->values[0].value.string);
421   if (*dest == NULL)
422     return (-1);
424   return (0);
425 } /* }}} int cj_config_add_string */
427 static int cj_config_set_boolean (const char *name, int *dest, /* {{{ */
428                                        oconfig_item_t *ci)
430   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
431   {
432     WARNING ("curl_json plugin: `%s' needs exactly one boolean argument.", name);
433     return (-1);
434   }
436   *dest = ci->values[0].value.boolean ? 1 : 0;
438   return (0);
439 } /* }}} int cj_config_set_boolean */
441 static c_avl_tree_t *cj_avl_create(void)
443   return c_avl_create ((int (*) (const void *, const void *)) strcmp);
446 static int cj_config_add_key (cj_t *db, /* {{{ */
447                                    oconfig_item_t *ci)
449   cj_key_t *key;
450   int status;
451   int i;
453   if ((ci->values_num != 1)
454       || (ci->values[0].type != OCONFIG_TYPE_STRING))
455   {
456     WARNING ("curl_json plugin: The `Key' block "
457              "needs exactly one string argument.");
458     return (-1);
459   }
461   key = (cj_key_t *) malloc (sizeof (*key));
462   if (key == NULL)
463   {
464     ERROR ("curl_json plugin: malloc failed.");
465     return (-1);
466   }
467   memset (key, 0, sizeof (*key));
468   key->magic = CJ_KEY_MAGIC;
470   if (strcasecmp ("Key", ci->key) == 0)
471   {
472     status = cj_config_add_string ("Key", &key->path, ci);
473     if (status != 0)
474     {
475       sfree (key);
476       return (status);
477     }
478   }
479   else
480   {
481     ERROR ("curl_json plugin: cj_config: "
482            "Invalid key: %s", ci->key);
483     return (-1);
484   }
486   status = 0;
487   for (i = 0; i < ci->children_num; i++)
488   {
489     oconfig_item_t *child = ci->children + i;
491     if (strcasecmp ("Type", child->key) == 0)
492       status = cj_config_add_string ("Type", &key->type, child);
493     else if (strcasecmp ("Instance", child->key) == 0)
494       status = cj_config_add_string ("Instance", &key->instance, child);
495     else
496     {
497       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
498       status = -1;
499     }
501     if (status != 0)
502       break;
503   } /* for (i = 0; i < ci->children_num; i++) */
505   while (status == 0)
506   {
507     if (key->type == NULL)
508     {
509       WARNING ("curl_json plugin: `Type' missing in `Key' block.");
510       status = -1;
511     }
513     break;
514   } /* while (status == 0) */
516   /* store path in a tree that will match the json map structure, example:
517    * "httpd/requests/count",
518    * "httpd/requests/current" ->
519    * { "httpd": { "requests": { "count": $key, "current": $key } } }
520    */
521   if (status == 0)
522   {
523     char *ptr;
524     char *name;
525     char ent[PATH_MAX];
526     c_avl_tree_t *tree;
528     if (db->tree == NULL)
529       db->tree = cj_avl_create();
531     tree = db->tree;
532     name = key->path;
533     ptr = key->path;
534     if (*ptr == '/')
535       ++ptr;
537     name = ptr;
538     while (*ptr)
539     {
540       if (*ptr == '/')
541       {
542         c_avl_tree_t *value;
543         int len;
545         len = ptr-name;
546         if (len == 0)
547           break;
548         sstrncpy (ent, name, len+1);
550         if (c_avl_get (tree, ent, (void *) &value) != 0)
551         {
552           value = cj_avl_create ();
553           c_avl_insert (tree, strdup (ent), value);
554         }
556         tree = value;
557         name = ptr+1;
558       }
559       ++ptr;
560     }
561     if (*name)
562       c_avl_insert (tree, strdup(name), key);
563     else
564     {
565       ERROR ("curl_json plugin: invalid key: %s", key->path);
566       status = -1;
567     }
568   }
570   return (status);
571 } /* }}} int cj_config_add_key */
573 static int cj_init_curl (cj_t *db) /* {{{ */
575   db->curl = curl_easy_init ();
576   if (db->curl == NULL)
577   {
578     ERROR ("curl_json plugin: curl_easy_init failed.");
579     return (-1);
580   }
582   curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1);
583   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_curl_callback);
584   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
585   curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
586                     PACKAGE_NAME"/"PACKAGE_VERSION);
587   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
588   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
590   if (db->user != NULL)
591   {
592     size_t credentials_size;
594     credentials_size = strlen (db->user) + 2;
595     if (db->pass != NULL)
596       credentials_size += strlen (db->pass);
598     db->credentials = (char *) malloc (credentials_size);
599     if (db->credentials == NULL)
600     {
601       ERROR ("curl_json plugin: malloc failed.");
602       return (-1);
603     }
605     ssnprintf (db->credentials, credentials_size, "%s:%s",
606                db->user, (db->pass == NULL) ? "" : db->pass);
607     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
608   }
610   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer);
611   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
612                     db->verify_host ? 2 : 0);
613   if (db->cacert != NULL)
614     curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
616   return (0);
617 } /* }}} int cj_init_curl */
619 static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
621   cj_t *db;
622   int status = 0;
623   int i;
625   if ((ci->values_num != 1)
626       || (ci->values[0].type != OCONFIG_TYPE_STRING))
627   {
628     WARNING ("curl_json plugin: The `URL' block "
629              "needs exactly one string argument.");
630     return (-1);
631   }
633   db = (cj_t *) malloc (sizeof (*db));
634   if (db == NULL)
635   {
636     ERROR ("curl_json plugin: malloc failed.");
637     return (-1);
638   }
639   memset (db, 0, sizeof (*db));
641   if (strcasecmp ("URL", ci->key) == 0)
642   {
643     status = cj_config_add_string ("URL", &db->url, ci);
644     if (status != 0)
645     {
646       sfree (db);
647       return (status);
648     }
649   }
650   else
651   {
652     ERROR ("curl_json plugin: cj_config: "
653            "Invalid key: %s", ci->key);
654     return (-1);
655   }
657   /* Fill the `cj_t' structure.. */
658   for (i = 0; i < ci->children_num; i++)
659   {
660     oconfig_item_t *child = ci->children + i;
662     if (strcasecmp ("Instance", child->key) == 0)
663       status = cj_config_add_string ("Instance", &db->instance, child);
664     else if (strcasecmp ("Host", child->key) == 0)
665       status = cj_config_add_string ("Host", &db->host, child);
666     else if (strcasecmp ("User", child->key) == 0)
667       status = cj_config_add_string ("User", &db->user, child);
668     else if (strcasecmp ("Password", child->key) == 0)
669       status = cj_config_add_string ("Password", &db->pass, child);
670     else if (strcasecmp ("VerifyPeer", child->key) == 0)
671       status = cj_config_set_boolean ("VerifyPeer", &db->verify_peer, child);
672     else if (strcasecmp ("VerifyHost", child->key) == 0)
673       status = cj_config_set_boolean ("VerifyHost", &db->verify_host, child);
674     else if (strcasecmp ("CACert", child->key) == 0)
675       status = cj_config_add_string ("CACert", &db->cacert, child);
676     else if (strcasecmp ("Key", child->key) == 0)
677       status = cj_config_add_key (db, child);
678     else
679     {
680       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
681       status = -1;
682     }
684     if (status != 0)
685       break;
686   }
688   if (status == 0)
689   {
690     if (db->tree == NULL)
691     {
692       WARNING ("curl_json plugin: No (valid) `Key' block "
693                "within `URL' block `%s'.", db->url);
694       status = -1;
695     }
696     if (status == 0)
697       status = cj_init_curl (db);
698   }
700   /* If all went well, register this database for reading */
701   if (status == 0)
702   {
703     user_data_t ud;
704     char cb_name[DATA_MAX_NAME_LEN];
706     if (db->instance == NULL)
707       db->instance = strdup("default");
709     DEBUG ("curl_json plugin: Registering new read callback: %s",
710            db->instance);
712     memset (&ud, 0, sizeof (ud));
713     ud.data = (void *) db;
714     ud.free_func = cj_free;
716     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
717                db->instance, db->url);
719     plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
720                                   /* interval = */ NULL, &ud);
721   }
722   else
723   {
724     cj_free (db);
725     return (-1);
726   }
728   return (0);
730  /* }}} int cj_config_add_database */
732 static int cj_config (oconfig_item_t *ci) /* {{{ */
734   int success;
735   int errors;
736   int status;
737   int i;
739   success = 0;
740   errors = 0;
742   for (i = 0; i < ci->children_num; i++)
743   {
744     oconfig_item_t *child = ci->children + i;
746     if (strcasecmp ("URL", child->key) == 0)
747     {
748       status = cj_config_add_url (child);
749       if (status == 0)
750         success++;
751       else
752         errors++;
753     }
754     else
755     {
756       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
757       errors++;
758     }
759   }
761   if ((success == 0) && (errors > 0))
762   {
763     ERROR ("curl_json plugin: All statements failed.");
764     return (-1);
765   }
767   return (0);
768 } /* }}} int cj_config */
770 /* }}} End of configuration handling functions */
772 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
774   value_list_t vl = VALUE_LIST_INIT;
775   char *host;
777   vl.values     = value;
778   vl.values_len = 1;
780   if ((db->host == NULL)
781       || (strcmp ("", db->host) == 0)
782       || (strcmp (CJ_DEFAULT_HOST, db->host) == 0))
783     host = hostname_g;
784   else
785     host = db->host;
787   if (key->instance == NULL)
788   {
789     if ((db->depth == 0) || (strcmp ("", db->state[db->depth-1].name) == 0))
790       sstrncpy (vl.type_instance, db->state[db->depth].name, sizeof (vl.type_instance));
791     else
792       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
793           db->state[db->depth-1].name, db->state[db->depth].name);
794   }
795   else
796     sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
798   sstrncpy (vl.host, host, sizeof (vl.host));
799   sstrncpy (vl.plugin, "curl_json", sizeof (vl.plugin));
800   sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
801   sstrncpy (vl.type, key->type, sizeof (vl.type));
803   plugin_dispatch_values (&vl);
804 } /* }}} int cj_submit */
806 static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
808   int status;
809   long rc;
810   char *url;
811   yajl_handle yprev = db->yajl;
813   db->yajl = yajl_alloc (&ycallbacks,
814 #if HAVE_YAJL_V2
815       /* alloc funcs = */ NULL,
816 #else
817       /* alloc funcs = */ NULL, NULL,
818 #endif
819       /* context = */ (void *)db);
820   if (db->yajl == NULL)
821   {
822     ERROR ("curl_json plugin: yajl_alloc failed.");
823     db->yajl = yprev;
824     return (-1);
825   }
827   status = curl_easy_perform (curl);
829   yajl_free (db->yajl);
830   db->yajl = yprev;
832   curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
833   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
835   /* The response code is zero if a non-HTTP transport was used. */
836   if ((rc != 0) && (rc != 200))
837   {
838     ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
839            rc, url);
840     return (-1);
841   }
843   if (status != 0)
844   {
845     ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
846            status, db->curl_errbuf, url);
847     return (-1);
848   }
850   return (0);
851 } /* }}} int cj_curl_perform */
853 static int cj_read (user_data_t *ud) /* {{{ */
855   cj_t *db;
857   if ((ud == NULL) || (ud->data == NULL))
858   {
859     ERROR ("curl_json plugin: cj_read: Invalid user data.");
860     return (-1);
861   }
863   db = (cj_t *) ud->data;
865   db->depth = 0;
866   memset (&db->state, 0, sizeof(db->state));
867   db->state[db->depth].tree = db->tree;
868   db->key = NULL;
870   return cj_curl_perform (db, db->curl);
871 } /* }}} int cj_read */
873 void module_register (void)
875   plugin_register_complex_config ("curl_json", cj_config);
876 } /* void module_register */
878 /* vim: set sw=2 sts=2 et fdm=marker : */