curl_json plugin: warn when config expects a map and a number is provided
[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  **/
23
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"
30
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
36
37 #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
38 # define HAVE_YAJL_V2 1
39 #endif
40
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))
46
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 /* }}} */
57
58 struct cj_s /* {{{ */
59 {
60   char *instance;
61   char *host;
62
63   char *url;
64   char *user;
65   char *pass;
66   char *credentials;
67   _Bool verify_peer;
68   _Bool verify_host;
69   char *cacert;
70   struct curl_slist *headers;
71   char *post_body;
72
73   CURL *curl;
74   char curl_errbuf[CURL_ERROR_SIZE];
75
76   yajl_handle yajl;
77   c_avl_tree_t *tree;
78   cj_key_t *key;
79   int depth;
80   struct {
81     union {
82       c_avl_tree_t *tree;
83       cj_key_t *key;
84     };
85     _Bool in_array;
86     int index;
87     char name[DATA_MAX_NAME_LEN];
88   } state[YAJL_MAX_DEPTH];
89 };
90 typedef struct cj_s cj_t; /* }}} */
91
92 #if HAVE_YAJL_V2
93 typedef size_t yajl_len_t;
94 #else
95 typedef unsigned int yajl_len_t;
96 #endif
97
98 static int cj_read (user_data_t *ud);
99 static int cj_curl_perform (cj_t *db, CURL *curl);
100 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
101
102 static size_t cj_curl_callback (void *buf, /* {{{ */
103     size_t size, size_t nmemb, void *user_data)
104 {
105   cj_t *db;
106   size_t len;
107   yajl_status status;
108
109   len = size * nmemb;
110
111   if (len <= 0)
112     return (len);
113
114   db = user_data;
115   if (db == NULL)
116     return (0);
117
118   status = yajl_parse(db->yajl, (unsigned char *)buf, len);
119   if (status == yajl_status_ok)
120     return (len);
121 #if !HAVE_YAJL_V2
122   else if (status == yajl_status_insufficient_data)
123     return (len);
124 #endif
125
126   if (status != yajl_status_ok)
127   {
128     unsigned char *msg =
129       yajl_get_error(db->yajl, /* verbose = */ 1,
130           /* jsonText = */ (unsigned char *) buf, (unsigned int) len);
131     ERROR ("curl_json plugin: yajl_parse failed: %s", msg);
132     yajl_free_error(db->yajl, msg);
133     return (0); /* abort write callback */
134   }
135
136   return (len);
137 } /* }}} size_t cj_curl_callback */
138
139 static int cj_get_type (cj_key_t *key)
140 {
141   const data_set_t *ds;
142
143   ds = plugin_get_ds (key->type);
144   if (ds == NULL)
145   {
146     static char type[DATA_MAX_NAME_LEN] = "!!!invalid!!!";
147
148     assert (key->type != NULL);
149     if (strcmp (type, key->type) != 0)
150     {
151       ERROR ("curl_json plugin: Unable to look up DS type \"%s\".",
152           key->type);
153       sstrncpy (type, key->type, sizeof (type));
154     }
155
156     return -1;
157   }
158   else if (ds->ds_num > 1)
159   {
160     static c_complain_t complaint = C_COMPLAIN_INIT_STATIC;
161
162     c_complain_once (LOG_WARNING, &complaint,
163         "curl_json plugin: The type \"%s\" has more than one data source. "
164         "This is currently not supported. I will return the type of the "
165         "first data source, but this will likely lead to problems later on.",
166         key->type);
167   }
168
169   return ds->ds[0].type;
170 }
171
172 static int cj_cb_map_key (void *ctx, const unsigned char *val,
173     yajl_len_t len);
174
175 static void cj_cb_inc_array_index (void * ctx, _Bool ignore)
176 {
177   cj_t *db = (cj_t *)ctx;
178
179   if (db->state[db->depth].in_array) {
180     if (ignore)
181       db->state[db->depth].index++;
182     else {
183       char name[DATA_MAX_NAME_LEN];
184       cj_cb_map_key (ctx, (unsigned char *)name,
185                      ssnprintf (name, sizeof (name),
186                                 "%d", db->state[db->depth].index++));
187     }
188   }
189 }
190
191 /* yajl callbacks */
192 #define CJ_CB_ABORT    0
193 #define CJ_CB_CONTINUE 1
194
195 static int cj_cb_boolean (void * ctx, int boolVal)
196 {
197   cj_cb_inc_array_index (ctx, 1);
198   return (CJ_CB_CONTINUE);
199 }
200
201 static int cj_cb_null (void * ctx)
202 {
203   cj_cb_inc_array_index (ctx, 1);
204   return (CJ_CB_CONTINUE);
205 }
206
207 /* "number" may not be null terminated, so copy it into a buffer before
208  * parsing. */
209 static int cj_cb_number (void *ctx,
210     const char *number, yajl_len_t number_len)
211 {
212   char buffer[number_len + 1];
213
214   cj_t *db = (cj_t *)ctx;
215   cj_key_t *key = db->state[db->depth].key;
216   value_t vt;
217   int type;
218   int status;
219
220   if ((key == NULL) || !CJ_IS_KEY (key)) {
221     if (key != NULL)
222       NOTICE ("curl_json plugin: Found \"%.*s\", but the configuration expects"
223               " a map.", (int)number_len > number_len ? 0 : (int)number_len,
224               number);
225     cj_cb_inc_array_index (ctx, 1);
226     return (CJ_CB_CONTINUE);
227   } else
228     cj_cb_inc_array_index (ctx, 0);
229
230   memcpy (buffer, number, number_len);
231   buffer[sizeof (buffer) - 1] = 0;
232
233   type = cj_get_type (key);
234   status = parse_value (buffer, &vt, type);
235   if (status != 0)
236   {
237     NOTICE ("curl_json plugin: Unable to parse number: \"%s\"", buffer);
238     return (CJ_CB_CONTINUE);
239   }
240
241   cj_submit (db, key, &vt);
242   return (CJ_CB_CONTINUE);
243 } /* int cj_cb_number */
244
245 static int cj_cb_map_key (void *ctx, const unsigned char *val,
246     yajl_len_t len)
247 {
248   cj_t *db = (cj_t *)ctx;
249   c_avl_tree_t *tree;
250
251   tree = db->state[db->depth-1].tree;
252
253   if (tree != NULL)
254   {
255     cj_key_t *value;
256     char *name;
257
258     name = db->state[db->depth].name;
259     len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
260     sstrncpy (name, (char *)val, len+1);
261
262     if (c_avl_get (tree, name, (void *) &value) == 0)
263       db->state[db->depth].key = value;
264     else if (c_avl_get (tree, CJ_ANY, (void *) &value) == 0)
265       db->state[db->depth].key = value;
266     else
267       db->state[db->depth].key = NULL;
268   }
269
270   return (CJ_CB_CONTINUE);
271 }
272
273 static int cj_cb_string (void *ctx, const unsigned char *val,
274     yajl_len_t len)
275 {
276   /* Handle the string as if it was a number. */
277   return (cj_cb_number (ctx, (const char *) val, len));
278 } /* int cj_cb_string */
279
280 static int cj_cb_start (void *ctx)
281 {
282   cj_t *db = (cj_t *)ctx;
283   if (++db->depth >= YAJL_MAX_DEPTH)
284   {
285     ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
286     return (CJ_CB_ABORT);
287   }
288   return (CJ_CB_CONTINUE);
289 }
290
291 static int cj_cb_end (void *ctx)
292 {
293   cj_t *db = (cj_t *)ctx;
294   db->state[db->depth].tree = NULL;
295   --db->depth;
296   return (CJ_CB_CONTINUE);
297 }
298
299 static int cj_cb_start_map (void *ctx)
300 {
301   cj_cb_inc_array_index (ctx, 0);
302   return cj_cb_start (ctx);
303 }
304
305 static int cj_cb_end_map (void *ctx)
306 {
307   return cj_cb_end (ctx);
308 }
309
310 static int cj_cb_start_array (void * ctx)
311 {
312   cj_t *db = (cj_t *)ctx;
313   cj_cb_inc_array_index (ctx, 0);
314   if (db->depth+1 < YAJL_MAX_DEPTH) {
315     db->state[db->depth+1].in_array = 1;
316     db->state[db->depth+1].index = 0;
317   }
318   return cj_cb_start (ctx);
319 }
320
321 static int cj_cb_end_array (void * ctx)
322 {
323   cj_t *db = (cj_t *)ctx;
324   db->state[db->depth].in_array = 0;
325   return cj_cb_end (ctx);
326 }
327
328 static yajl_callbacks ycallbacks = {
329   cj_cb_null, /* null */
330   cj_cb_boolean, /* boolean */
331   NULL, /* integer */
332   NULL, /* double */
333   cj_cb_number,
334   cj_cb_string,
335   cj_cb_start_map,
336   cj_cb_map_key,
337   cj_cb_end_map,
338   cj_cb_start_array,
339   cj_cb_end_array
340 };
341
342 /* end yajl callbacks */
343
344 static void cj_key_free (cj_key_t *key) /* {{{ */
345 {
346   if (key == NULL)
347     return;
348
349   sfree (key->path);
350   sfree (key->type);
351   sfree (key->instance);
352
353   sfree (key);
354 } /* }}} void cj_key_free */
355
356 static void cj_tree_free (c_avl_tree_t *tree) /* {{{ */
357 {
358   char *name;
359   void *value;
360
361   while (c_avl_pick (tree, (void *) &name, (void *) &value) == 0)
362   {
363     cj_key_t *key = (cj_key_t *)value;
364
365     if (CJ_IS_KEY(key))
366       cj_key_free (key);
367     else
368       cj_tree_free ((c_avl_tree_t *)value);
369
370     sfree (name);
371   }
372
373   c_avl_destroy (tree);
374 } /* }}} void cj_tree_free */
375
376 static void cj_free (void *arg) /* {{{ */
377 {
378   cj_t *db;
379
380   DEBUG ("curl_json plugin: cj_free (arg = %p);", arg);
381
382   db = (cj_t *) arg;
383
384   if (db == NULL)
385     return;
386
387   if (db->curl != NULL)
388     curl_easy_cleanup (db->curl);
389   db->curl = NULL;
390
391   if (db->tree != NULL)
392     cj_tree_free (db->tree);
393   db->tree = NULL;
394
395   sfree (db->instance);
396   sfree (db->host);
397
398   sfree (db->url);
399   sfree (db->user);
400   sfree (db->pass);
401   sfree (db->credentials);
402   sfree (db->cacert);
403   sfree (db->post_body);
404   curl_slist_free_all (db->headers);
405
406   sfree (db);
407 } /* }}} void cj_free */
408
409 /* Configuration handling functions {{{ */
410
411 static c_avl_tree_t *cj_avl_create(void)
412 {
413   return c_avl_create ((int (*) (const void *, const void *)) strcmp);
414 }
415
416 static int cj_config_append_string (const char *name, struct curl_slist **dest, /* {{{ */
417     oconfig_item_t *ci)
418 {
419   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
420   {
421     WARNING ("curl_json plugin: `%s' needs exactly one string argument.", name);
422     return (-1);
423   }
424
425   *dest = curl_slist_append(*dest, ci->values[0].value.string);
426   if (*dest == NULL)
427     return (-1);
428
429   return (0);
430 } /* }}} int cj_config_append_string */
431
432 static int cj_config_add_key (cj_t *db, /* {{{ */
433                                    oconfig_item_t *ci)
434 {
435   cj_key_t *key;
436   int status;
437   int i;
438
439   if ((ci->values_num != 1)
440       || (ci->values[0].type != OCONFIG_TYPE_STRING))
441   {
442     WARNING ("curl_json plugin: The `Key' block "
443              "needs exactly one string argument.");
444     return (-1);
445   }
446
447   key = (cj_key_t *) malloc (sizeof (*key));
448   if (key == NULL)
449   {
450     ERROR ("curl_json plugin: malloc failed.");
451     return (-1);
452   }
453   memset (key, 0, sizeof (*key));
454   key->magic = CJ_KEY_MAGIC;
455
456   if (strcasecmp ("Key", ci->key) == 0)
457   {
458     status = cf_util_get_string (ci, &key->path);
459     if (status != 0)
460     {
461       sfree (key);
462       return (status);
463     }
464   }
465   else
466   {
467     ERROR ("curl_json plugin: cj_config: "
468            "Invalid key: %s", ci->key);
469     return (-1);
470   }
471
472   status = 0;
473   for (i = 0; i < ci->children_num; i++)
474   {
475     oconfig_item_t *child = ci->children + i;
476
477     if (strcasecmp ("Type", child->key) == 0)
478       status = cf_util_get_string (child, &key->type);
479     else if (strcasecmp ("Instance", child->key) == 0)
480       status = cf_util_get_string (child, &key->instance);
481     else
482     {
483       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
484       status = -1;
485     }
486
487     if (status != 0)
488       break;
489   } /* for (i = 0; i < ci->children_num; i++) */
490
491   while (status == 0)
492   {
493     if (key->type == NULL)
494     {
495       WARNING ("curl_json plugin: `Type' missing in `Key' block.");
496       status = -1;
497     }
498
499     break;
500   } /* while (status == 0) */
501
502   /* store path in a tree that will match the json map structure, example:
503    * "httpd/requests/count",
504    * "httpd/requests/current" ->
505    * { "httpd": { "requests": { "count": $key, "current": $key } } }
506    */
507   if (status == 0)
508   {
509     char *ptr;
510     char *name;
511     char ent[PATH_MAX];
512     c_avl_tree_t *tree;
513
514     if (db->tree == NULL)
515       db->tree = cj_avl_create();
516
517     tree = db->tree;
518     name = key->path;
519     ptr = key->path;
520     if (*ptr == '/')
521       ++ptr;
522
523     name = ptr;
524     while (*ptr)
525     {
526       if (*ptr == '/')
527       {
528         c_avl_tree_t *value;
529         int len;
530
531         len = ptr-name;
532         if (len == 0)
533           break;
534         sstrncpy (ent, name, len+1);
535
536         if (c_avl_get (tree, ent, (void *) &value) != 0)
537         {
538           value = cj_avl_create ();
539           c_avl_insert (tree, strdup (ent), value);
540         }
541
542         tree = value;
543         name = ptr+1;
544       }
545       ++ptr;
546     }
547     if (*name)
548       c_avl_insert (tree, strdup(name), key);
549     else
550     {
551       ERROR ("curl_json plugin: invalid key: %s", key->path);
552       status = -1;
553     }
554   }
555
556   return (status);
557 } /* }}} int cj_config_add_key */
558
559 static int cj_init_curl (cj_t *db) /* {{{ */
560 {
561   db->curl = curl_easy_init ();
562   if (db->curl == NULL)
563   {
564     ERROR ("curl_json plugin: curl_easy_init failed.");
565     return (-1);
566   }
567
568   curl_easy_setopt (db->curl, CURLOPT_NOSIGNAL, 1L);
569   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_curl_callback);
570   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
571   curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
572                     PACKAGE_NAME"/"PACKAGE_VERSION);
573   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
574   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
575
576   if (db->user != NULL)
577   {
578     size_t credentials_size;
579
580     credentials_size = strlen (db->user) + 2;
581     if (db->pass != NULL)
582       credentials_size += strlen (db->pass);
583
584     db->credentials = (char *) malloc (credentials_size);
585     if (db->credentials == NULL)
586     {
587       ERROR ("curl_json plugin: malloc failed.");
588       return (-1);
589     }
590
591     ssnprintf (db->credentials, credentials_size, "%s:%s",
592                db->user, (db->pass == NULL) ? "" : db->pass);
593     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
594   }
595
596   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (long) db->verify_peer);
597   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
598                     db->verify_host ? 2L : 0L);
599   if (db->cacert != NULL)
600     curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
601   if (db->headers != NULL)
602     curl_easy_setopt (db->curl, CURLOPT_HTTPHEADER, db->headers);
603   if (db->post_body != NULL)
604     curl_easy_setopt (db->curl, CURLOPT_POSTFIELDS, db->post_body);
605
606   return (0);
607 } /* }}} int cj_init_curl */
608
609 static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
610 {
611   cj_t *db;
612   int status = 0;
613   int i;
614
615   if ((ci->values_num != 1)
616       || (ci->values[0].type != OCONFIG_TYPE_STRING))
617   {
618     WARNING ("curl_json plugin: The `URL' block "
619              "needs exactly one string argument.");
620     return (-1);
621   }
622
623   db = (cj_t *) malloc (sizeof (*db));
624   if (db == NULL)
625   {
626     ERROR ("curl_json plugin: malloc failed.");
627     return (-1);
628   }
629   memset (db, 0, sizeof (*db));
630
631   if (strcasecmp ("URL", ci->key) == 0)
632   {
633     status = cf_util_get_string (ci, &db->url);
634     if (status != 0)
635     {
636       sfree (db);
637       return (status);
638     }
639   }
640   else
641   {
642     ERROR ("curl_json plugin: cj_config: "
643            "Invalid key: %s", ci->key);
644     return (-1);
645   }
646
647   /* Fill the `cj_t' structure.. */
648   for (i = 0; i < ci->children_num; i++)
649   {
650     oconfig_item_t *child = ci->children + i;
651
652     if (strcasecmp ("Instance", child->key) == 0)
653       status = cf_util_get_string (child, &db->instance);
654     else if (strcasecmp ("Host", child->key) == 0)
655       status = cf_util_get_string (child, &db->host);
656     else if (strcasecmp ("User", child->key) == 0)
657       status = cf_util_get_string (child, &db->user);
658     else if (strcasecmp ("Password", child->key) == 0)
659       status = cf_util_get_string (child, &db->pass);
660     else if (strcasecmp ("VerifyPeer", child->key) == 0)
661       status = cf_util_get_boolean (child, &db->verify_peer);
662     else if (strcasecmp ("VerifyHost", child->key) == 0)
663       status = cf_util_get_boolean (child, &db->verify_host);
664     else if (strcasecmp ("CACert", child->key) == 0)
665       status = cf_util_get_string (child, &db->cacert);
666     else if (strcasecmp ("Header", child->key) == 0)
667       status = cj_config_append_string ("Header", &db->headers, child);
668     else if (strcasecmp ("Post", child->key) == 0)
669       status = cf_util_get_string (child, &db->post_body);
670     else if (strcasecmp ("Key", child->key) == 0)
671       status = cj_config_add_key (db, child);
672     else
673     {
674       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
675       status = -1;
676     }
677
678     if (status != 0)
679       break;
680   }
681
682   if (status == 0)
683   {
684     if (db->tree == NULL)
685     {
686       WARNING ("curl_json plugin: No (valid) `Key' block "
687                "within `URL' block `%s'.", db->url);
688       status = -1;
689     }
690     if (status == 0)
691       status = cj_init_curl (db);
692   }
693
694   /* If all went well, register this database for reading */
695   if (status == 0)
696   {
697     user_data_t ud;
698     char cb_name[DATA_MAX_NAME_LEN];
699
700     if (db->instance == NULL)
701       db->instance = strdup("default");
702
703     DEBUG ("curl_json plugin: Registering new read callback: %s",
704            db->instance);
705
706     memset (&ud, 0, sizeof (ud));
707     ud.data = (void *) db;
708     ud.free_func = cj_free;
709
710     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
711                db->instance, db->url);
712
713     plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
714                                   /* interval = */ NULL, &ud);
715   }
716   else
717   {
718     cj_free (db);
719     return (-1);
720   }
721
722   return (0);
723 }
724  /* }}} int cj_config_add_database */
725
726 static int cj_config (oconfig_item_t *ci) /* {{{ */
727 {
728   int success;
729   int errors;
730   int status;
731   int i;
732
733   success = 0;
734   errors = 0;
735
736   for (i = 0; i < ci->children_num; i++)
737   {
738     oconfig_item_t *child = ci->children + i;
739
740     if (strcasecmp ("URL", child->key) == 0)
741     {
742       status = cj_config_add_url (child);
743       if (status == 0)
744         success++;
745       else
746         errors++;
747     }
748     else
749     {
750       WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
751       errors++;
752     }
753   }
754
755   if ((success == 0) && (errors > 0))
756   {
757     ERROR ("curl_json plugin: All statements failed.");
758     return (-1);
759   }
760
761   return (0);
762 } /* }}} int cj_config */
763
764 /* }}} End of configuration handling functions */
765
766 static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
767 {
768   value_list_t vl = VALUE_LIST_INIT;
769   char *host;
770
771   vl.values     = value;
772   vl.values_len = 1;
773
774   if ((db->host == NULL)
775       || (strcmp ("", db->host) == 0)
776       || (strcmp (CJ_DEFAULT_HOST, db->host) == 0))
777     host = hostname_g;
778   else
779     host = db->host;
780
781   if (key->instance == NULL)
782   {
783     if ((db->depth == 0) || (strcmp ("", db->state[db->depth-1].name) == 0))
784       sstrncpy (vl.type_instance, db->state[db->depth].name, sizeof (vl.type_instance));
785     else
786       ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
787           db->state[db->depth-1].name, db->state[db->depth].name);
788   }
789   else
790     sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
791
792   sstrncpy (vl.host, host, sizeof (vl.host));
793   sstrncpy (vl.plugin, "curl_json", sizeof (vl.plugin));
794   sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
795   sstrncpy (vl.type, key->type, sizeof (vl.type));
796
797   plugin_dispatch_values (&vl);
798 } /* }}} int cj_submit */
799
800 static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
801 {
802   int status;
803   long rc;
804   char *url;
805   yajl_handle yprev = db->yajl;
806
807   db->yajl = yajl_alloc (&ycallbacks,
808 #if HAVE_YAJL_V2
809       /* alloc funcs = */ NULL,
810 #else
811       /* alloc funcs = */ NULL, NULL,
812 #endif
813       /* context = */ (void *)db);
814   if (db->yajl == NULL)
815   {
816     ERROR ("curl_json plugin: yajl_alloc failed.");
817     db->yajl = yprev;
818     return (-1);
819   }
820
821   url = NULL;
822   curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
823
824   status = curl_easy_perform (curl);
825   if (status != CURLE_OK)
826   {
827     ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
828            status, db->curl_errbuf, (url != NULL) ? url : "<null>");
829     yajl_free (db->yajl);
830     db->yajl = yprev;
831     return (-1);
832   }
833
834   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
835
836   /* The response code is zero if a non-HTTP transport was used. */
837   if ((rc != 0) && (rc != 200))
838   {
839     ERROR ("curl_json plugin: curl_easy_perform failed with "
840         "response code %ld (%s)", rc, url);
841     yajl_free (db->yajl);
842     db->yajl = yprev;
843     return (-1);
844   }
845
846 #if HAVE_YAJL_V2
847     status = yajl_complete_parse(db->yajl);
848 #else
849     status = yajl_parse_complete(db->yajl);
850 #endif
851   if (status != yajl_status_ok)
852   {
853     unsigned char *errmsg;
854
855     errmsg = yajl_get_error (db->yajl, /* verbose = */ 0,
856         /* jsonText = */ NULL, /* jsonTextLen = */ 0);
857     ERROR ("curl_json plugin: yajl_parse_complete failed: %s",
858         (char *) errmsg);
859     yajl_free_error (db->yajl, errmsg);
860     yajl_free (db->yajl);
861     db->yajl = yprev;
862     return (-1);
863   }
864
865   yajl_free (db->yajl);
866   db->yajl = yprev;
867   return (0);
868 } /* }}} int cj_curl_perform */
869
870 static int cj_read (user_data_t *ud) /* {{{ */
871 {
872   cj_t *db;
873
874   if ((ud == NULL) || (ud->data == NULL))
875   {
876     ERROR ("curl_json plugin: cj_read: Invalid user data.");
877     return (-1);
878   }
879
880   db = (cj_t *) ud->data;
881
882   db->depth = 0;
883   memset (&db->state, 0, sizeof(db->state));
884   db->state[db->depth].tree = db->tree;
885   db->key = NULL;
886
887   return cj_curl_perform (db, db->curl);
888 } /* }}} int cj_read */
889
890 void module_register (void)
891 {
892   plugin_register_complex_config ("curl_json", cj_config);
893 } /* void module_register */
894
895 /* vim: set sw=2 sts=2 et fdm=marker : */