curl_xml plugin: Move the setting of type_instance to a separate function.
[collectd.git] / src / curl_xml.c
1 /**
2  * collectd - src/curl_xml.c
3  * Copyright (C) 2009,2010       Amit Gupta
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Amit Gupta <amit.gupta221 at gmail.com>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26 #include "utils_avltree.h"
27
28 #include <libxml/parser.h>
29 #include <libxml/tree.h>
30 #include <libxml/xpath.h>
31
32 #include <curl/curl.h>
33
34 #define CX_DEFAULT_HOST "localhost"
35 #define CX_KEY_MAGIC 0x43484b59UL /* CHKY */
36 #define CX_IS_KEY(key) (key)->magic == CX_KEY_MAGIC
37
38 /*
39  * Private data structures
40  */
41 struct cx_values_s /* {{{ */
42 {
43   char path[DATA_MAX_NAME_LEN];
44   size_t path_len;
45 };
46 typedef struct cx_values_s cx_values_t;
47 /* }}} */
48
49 struct cx_xpath_s /* {{{ */
50 {
51   char *path;
52   char *type;
53   cx_values_t *values;
54   int values_len;
55   char *instance_prefix;
56   char *instance;
57   int is_table;
58   unsigned long magic;
59 };
60 typedef struct cx_xpath_s cx_xpath_t;
61 /* }}} */
62
63 struct cx_s /* {{{ */
64 {
65   char *instance;
66   char *host;
67
68   char *url;
69   char *user;
70   char *pass;
71   char *credentials;
72   _Bool verify_peer;
73   _Bool verify_host;
74   char *cacert;
75
76   CURL *curl;
77   char curl_errbuf[CURL_ERROR_SIZE];
78   char *buffer;
79   size_t buffer_size;
80   size_t buffer_fill;
81
82   c_avl_tree_t *tree; /* tree of xpath blocks */
83 };
84 typedef struct cx_s cx_t; /* }}} */
85
86 /*
87  * Private functions
88  */
89 static size_t cx_curl_callback (void *buf, /* {{{ */
90     size_t size, size_t nmemb, void *user_data)
91 {
92   size_t len = size * nmemb;
93   cx_t *db;
94
95   db = user_data;
96   if (db == NULL)
97   {
98     ERROR ("curl_xml plugin: cx_curl_callback: "
99            "user_data pointer is NULL.");
100     return (0);
101   }
102
103    if (len <= 0)
104     return (len);
105
106   if ((db->buffer_fill + len) >= db->buffer_size)
107   {
108     char *temp;
109
110     temp = (char *) realloc (db->buffer,
111                     db->buffer_fill + len + 1);
112     if (temp == NULL)
113     {
114       ERROR ("curl_xml plugin: realloc failed.");
115       return (0);
116     }
117     db->buffer = temp;
118     db->buffer_size = db->buffer_fill + len + 1;
119   }
120
121   memcpy (db->buffer + db->buffer_fill, (char *) buf, len);
122   db->buffer_fill += len;
123   db->buffer[db->buffer_fill] = 0;
124
125   return (len);
126 } /* }}} size_t cx_curl_callback */
127
128 static void cx_xpath_free (cx_xpath_t *xpath) /* {{{ */
129 {
130   if (xpath == NULL)
131     return;
132
133   sfree (xpath->path);
134   sfree (xpath->type);
135   sfree (xpath->instance_prefix);
136   sfree (xpath->instance);
137   sfree (xpath->values);
138   sfree (xpath);
139 } /* }}} void cx_xpath_free */
140
141 static void cx_tree_free (c_avl_tree_t *tree) /* {{{ */
142 {
143   char *name;
144   void *value;
145
146   while (c_avl_pick (tree, (void *) &name, (void *) &value) == 0)
147   {
148     cx_xpath_t *key = (cx_xpath_t *)value;
149
150     if (CX_IS_KEY(key))
151       cx_xpath_free (key);
152     else
153       cx_tree_free ((c_avl_tree_t *)value);
154
155     sfree (name);
156   }
157
158   c_avl_destroy (tree);
159 } /* }}} void cx_tree_free */
160
161 static void cx_free (void *arg) /* {{{ */
162 {
163   cx_t *db;
164
165   DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
166
167   db = (cx_t *) arg;
168
169   if (db == NULL)
170     return;
171
172   if (db->curl != NULL)
173     curl_easy_cleanup (db->curl);
174   db->curl = NULL;
175
176   if (db->tree != NULL)
177     cx_tree_free (db->tree);
178   db->tree = NULL;
179
180   sfree (db->buffer);
181   sfree (db->instance);
182   sfree (db->host);
183
184   sfree (db->url);
185   sfree (db->user);
186   sfree (db->pass);
187   sfree (db->credentials);
188   sfree (db->cacert);
189
190   sfree (db);
191 } /* }}} void cx_free */
192
193 static int cx_check_type (cx_xpath_t *xpath) /* {{{ */
194 {
195   const data_set_t *ds;
196   
197   ds = plugin_get_ds (xpath->type);
198   if (!ds)
199   {
200     WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
201     return (-1);
202   }
203
204   if (ds->ds_num != xpath->values_len)
205   {
206     WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
207         xpath->type, ds->ds_num, xpath->values_len);
208     return (-1);
209   }
210
211   return (0);
212 } /* }}} cx_check_type */
213
214 static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ 
215            xmlChar *expr)
216 {
217   xmlXPathObjectPtr xpath_obj;
218
219   /* XXX: When to free this? */
220   xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
221   if (xpath_obj == NULL)
222   {
223      WARNING ("curl_xml plugin: "
224                "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
225      return NULL;
226   }
227
228   return xpath_obj;
229 } /* }}} cx_evaluate_xpath */
230
231 static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
232 {
233   if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
234     return (0);
235
236   WARNING ("curl_xml plugin: "
237            "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
238   return -1;
239 } /* }}} cx_if_not_text_node */
240
241 static int cx_set_type_instance (xmlXPathContextPtr xpath_ctx, /* {{{ */
242     cx_xpath_t *xpath, value_list_t *vl,
243     _Bool is_table)
244 {
245   xmlXPathObjectPtr instance_node_obj = NULL;
246   xmlNodeSetPtr instance_node = NULL;
247
248   memset (vl->type_instance, 0, sizeof (vl->type_instance));
249
250   if (is_table && (vl->type_instance == NULL))
251   {
252     WARNING ("curl_xml plugin: "
253         "Base-XPath %s is a table, but no instance-XPath has been defined.",
254         xpath->path);
255     return (-1);
256   }
257
258   /* instance has to be an xpath expression */
259   if (xpath->instance != NULL)
260   {
261     int tmp_size;
262
263     instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
264     if (instance_node_obj == NULL)
265       return (-1); /* error is logged already */
266
267     instance_node = instance_node_obj->nodesetval;
268     tmp_size = (instance_node) ? instance_node->nodeNr : 0;
269
270     if ( (tmp_size == 0) && (is_table) )
271     {
272       WARNING ("curl_xml plugin: "
273           "relative xpath expression for 'Instance' \"%s\" doesn't match "
274           "any of the nodes. Skipping the node.", xpath->instance);
275       xmlXPathFreeObject (instance_node_obj);
276       return (-1);
277     }
278
279     if (tmp_size > 1)
280     {
281       WARNING ("curl_xml plugin: "
282           "relative xpath expression for 'Instance' \"%s\" is expected "
283           "to return only one text node. Skipping the node.", xpath->instance);
284       xmlXPathFreeObject (instance_node_obj);
285       return (-1);
286     }
287
288     /* ignoring the element if other than textnode/attribute */
289     if (cx_if_not_text_node(instance_node->nodeTab[0]))
290     {
291       WARNING ("curl_xml plugin: "
292           "relative xpath expression \"%s\" is expected to return only text node "
293           "which is not the case. Skipping the node.", xpath->instance);
294       xmlXPathFreeObject (instance_node_obj);
295       return (-1);
296     }
297   } /* if (xpath->instance != NULL) */
298
299   if (xpath->instance_prefix != NULL)
300   {
301     if (instance_node != NULL)
302       ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s-%s",
303           xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
304     else
305       sstrncpy (vl->type_instance, xpath->instance_prefix,
306           sizeof (vl->type_instance));
307   }
308   else
309   {
310     /* If instance_prefix and instance_node are NULL, then
311      * don't set the type_instance */
312     if (instance_node != NULL)
313       sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
314           sizeof (vl->type_instance));
315   }
316
317   /* Free `instance_node_obj' this late, because `instance_node' points to
318    * somewhere inside this structure. */
319   xmlXPathFreeObject (instance_node_obj);
320
321   return (0);
322 } /* }}} int cx_set_type_instance */
323
324 static int  cx_submit_xpath_values (char *plugin_instance, /* {{{ */
325     xmlXPathContextPtr xpath_ctx, 
326     char *base_xpath, cx_xpath_t *xpath)
327 {
328   int i;
329   int j;
330   int total_nodes;
331   int tmp_size;
332   int status=-1;
333   char *node_value;
334
335   xmlXPathObjectPtr base_node_obj = NULL;
336   xmlNodeSetPtr base_nodes = NULL;
337
338   value_list_t vl = VALUE_LIST_INIT;
339   const data_set_t *ds;
340
341   base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath); 
342   if (base_node_obj == NULL)
343     return -1; /* error is logged already */
344
345   base_nodes = base_node_obj->nodesetval;
346   total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
347
348   if (total_nodes == 0)
349   {
350      ERROR ("curl_xml plugin: "
351               "xpath expression \"%s\" doesn't match any of the node. Skipping...", base_xpath);
352      xmlXPathFreeObject (base_node_obj);
353      return -1;
354   }
355
356   /* If base_xpath returned multiple results, then */
357   /* Instance in the xpath block is required */ 
358   if (total_nodes > 1 && xpath->instance == NULL)
359   {
360     ERROR ("curl_xml plugin: "
361              "Instance is must in xpath block since the base xpath expression \"%s\" "
362              "returned multiple results. Skipping the xpath block...", base_xpath);
363     return -1;
364   }
365
366   /* set the values for the value_list */
367   ds = plugin_get_ds (xpath->type);
368   vl.values_len = ds->ds_num;
369   sstrncpy (vl.type, xpath->type, sizeof (vl.type));
370   sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
371   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
372   if (plugin_instance != NULL)
373     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); 
374
375   for (i = 0; i < total_nodes; i++)
376   {
377      xpath_ctx->node = base_nodes->nodeTab[i];
378
379      status = cx_set_type_instance (xpath_ctx, xpath, &vl,
380          /* is_table = */ (total_nodes > 1));
381      if (status != 0)
382        continue; /* An error has already been reported. */
383
384      for (j = 0; j < xpath->values_len; j++)
385      {
386        xmlXPathObjectPtr values_node_obj;
387        xmlNodeSetPtr values_node;
388
389        values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[j].path);
390        if (values_node_obj == NULL)
391          continue; /* Error already logged. */
392
393        values_node = values_node_obj->nodesetval;
394        tmp_size = (values_node) ? values_node->nodeNr : 0;
395
396        if (tmp_size == 0)
397        {
398          WARNING ("curl_xml plugin: "
399                 "relative xpath expression \"%s\" doesn't match any of the nodes. "
400                 "Skipping...", xpath->values[j].path);
401          xmlXPathFreeObject (values_node_obj);
402          continue;
403        }
404
405        if (tmp_size > 1)
406        {
407          WARNING ("curl_xml plugin: "
408                   "relative xpath expression \"%s\" is expected to return "
409                   "only one node. Skipping...", xpath->values[j].path);
410          xmlXPathFreeObject (values_node_obj);
411          continue;
412        }
413
414        /* ignoring the element if other than textnode/attribute*/
415        if (cx_if_not_text_node(values_node->nodeTab[0]))
416        {
417          WARNING ("curl_xml plugin: "
418                   "relative xpath expression \"%s\" is expected to return "
419                   "only text/attribute node which is not the case. Skipping...", 
420                   xpath->values[j].path);
421          xmlXPathFreeObject (values_node_obj);
422          continue;
423        }
424
425        vl.values = (value_t *) malloc (sizeof (value_t) * vl.values_len);
426        if (vl.values == NULL)
427        {
428          ERROR ("curl_xml plugin: malloc failed.");
429          xmlXPathFreeObject (base_node_obj);
430          xmlXPathFreeObject (values_node_obj);
431          return (-1);
432        } 
433
434        node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
435        switch (ds->ds[j].type)
436        {
437          case DS_TYPE_COUNTER:
438            vl.values[j].counter = atoi(node_value);
439            break;
440          case DS_TYPE_DERIVE:
441            vl.values[j].derive = atoi(node_value);
442            break;
443          case DS_TYPE_ABSOLUTE:
444            vl.values[j].absolute = atoi(node_value);
445            break;
446          case DS_TYPE_GAUGE: 
447            vl.values[j].absolute = atoi(node_value);
448        }
449       
450        /* free up object */
451        xmlXPathFreeObject (values_node_obj);
452
453        /* We have reached here which means that
454         * we have got something to work */
455        status = 0;
456      } /* for (j = 0; j < xpath->values_len; j++) */
457
458      /* submit the values */
459      if (vl.values)
460        plugin_dispatch_values (&vl);
461
462      sfree(vl.values);
463   } /* for (i = 0; i < total_nodes; i++) */
464
465   /* free up the allocated memory */
466   xmlXPathFreeObject (base_node_obj); 
467
468   return status; 
469 } /* }}} cx_submit_xpath_values */
470
471 static int cx_submit_statistics(xmlDocPtr doc, /* {{{ */ 
472                        xmlXPathContextPtr xpath_ctx, cx_t *db)
473 {
474   c_avl_iterator_t *iter;
475   char *key;
476   cx_xpath_t *value;
477   int status=-1;
478   
479   iter = c_avl_get_iterator (db->tree);
480   while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
481   {
482     if (cx_check_type(value) == -1)
483       continue;
484
485     if (cx_submit_xpath_values(db->instance, xpath_ctx, key, value) == 0)
486       status = 0; /* we got atleast one success */
487   } /* while (c_avl_iterator_next) */
488
489   return status;
490 } /* }}} cx_submit_statistics */
491
492 static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
493 {
494   int status;
495   xmlDocPtr doc;
496   xmlXPathContextPtr xpath_ctx;
497
498   /* Load the XML */
499   doc = xmlParseDoc(xml);
500   if (doc == NULL)
501   {
502     ERROR ("curl_xml plugin: Failed to parse the xml document  - %s", xml);
503     return (-1);
504   }
505
506   xpath_ctx = xmlXPathNewContext(doc);
507   if(xpath_ctx == NULL)
508   {
509     ERROR ("curl_xml plugin: Failed to create the xml context");
510     xmlFreeDoc(doc);
511     return (-1);
512   }
513
514   status = cx_submit_statistics (doc, xpath_ctx, db);
515   /* Cleanup */
516   xmlXPathFreeContext(xpath_ctx);
517   xmlFreeDoc(doc);
518   return status;
519 } /* }}} cx_parse_stats_xml */
520
521 static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
522 {
523   int status;
524   long rc;
525   char *ptr;
526   char *url;
527
528   db->buffer_fill = 0; 
529   status = curl_easy_perform (curl);
530
531   curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
532   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
533
534   if (rc != 200)
535   {
536     ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
537            rc, url);
538     return (-1);
539   }
540
541   if (status != 0)
542   {
543     ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
544            status, db->curl_errbuf, url);
545     return (-1);
546   }
547
548   ptr = db->buffer;
549
550   status = cx_parse_stats_xml(BAD_CAST ptr, db);
551   db->buffer_fill = 0;
552
553   return status;
554 } /* }}} int cx_curl_perform */
555
556 static int cx_read (user_data_t *ud) /* {{{ */
557 {
558   cx_t *db;
559
560   if ((ud == NULL) || (ud->data == NULL))
561   {
562     ERROR ("curl_xml plugin: cx_read: Invalid user data.");
563     return (-1);
564   }
565
566   db = (cx_t *) ud->data;
567
568   return cx_curl_perform (db, db->curl);
569 } /* }}} int cx_read */
570
571 /* Configuration handling functions {{{ */
572
573 static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */
574                                       oconfig_item_t *ci)
575 {
576   int i;
577
578   if (ci->values_num < 1)
579   {
580     WARNING ("curl_xml plugin: `Values' needs at least one argument.");
581     return (-1);
582   }
583
584   for (i = 0; i < ci->values_num; i++)
585     if (ci->values[i].type != OCONFIG_TYPE_STRING)
586     {
587       WARNING ("curl_xml plugin: `Values' needs only string argument.");
588       return (-1);
589     }
590
591   sfree (xpath->values);
592
593   xpath->values_len = 0;
594   xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num);
595   if (xpath->values == NULL)
596     return (-1);
597   xpath->values_len = ci->values_num;
598
599   /* populate cx_values_t structure */
600   for (i = 0; i < ci->values_num; i++)
601   {
602     xpath->values[i].path_len = sizeof (ci->values[i].value.string);
603     sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
604   }
605
606   return (0); 
607 } /* }}} cx_config_add_values */
608
609 static c_avl_tree_t *cx_avl_create(void) /* {{{ */
610 {
611   return c_avl_create ((int (*) (const void *, const void *)) strcmp);
612 } /* }}} cx_avl_create */
613
614 static int cx_config_add_xpath (cx_t *db, /* {{{ */
615                                    oconfig_item_t *ci)
616 {
617   cx_xpath_t *xpath;
618   int status;
619   int i;
620
621   if ((ci->values_num != 1)
622       || (ci->values[0].type != OCONFIG_TYPE_STRING))
623   {
624     WARNING ("curl_xml plugin: The `xpath' block "
625              "needs exactly one string argument.");
626     return (-1);
627   }
628
629   xpath = (cx_xpath_t *) malloc (sizeof (*xpath));
630   if (xpath == NULL)
631   {
632     ERROR ("curl_xml plugin: malloc failed.");
633     return (-1);
634   }
635   memset (xpath, 0, sizeof (*xpath));
636   xpath->magic = CX_KEY_MAGIC;
637
638   if (strcasecmp ("xpath", ci->key) == 0)
639   {
640     status = cf_util_get_string (ci, &xpath->path);
641     if (status != 0)
642     {
643       sfree (xpath);
644       return (status);
645     }
646   }
647   else
648   {
649     ERROR ("curl_xml plugin: cx_config: "
650            "Invalid key: %s", ci->key);
651     return (-1);
652   }
653
654   status = 0;
655   for (i = 0; i < ci->children_num; i++)
656   {
657     oconfig_item_t *child = ci->children + i;
658
659     if (strcasecmp ("Type", child->key) == 0)
660       status = cf_util_get_string (child, &xpath->type);
661     else if (strcasecmp ("InstancePrefix", child->key) == 0)
662       status = cf_util_get_string (child, &xpath->instance_prefix);
663     else if (strcasecmp ("Instance", child->key) == 0)
664       status = cf_util_get_string (child, &xpath->instance);
665     else if (strcasecmp ("Values", child->key) == 0)
666       status = cx_config_add_values ("Values", xpath, child);
667     else
668     {
669       WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
670       status = -1;
671     }
672
673     if (status != 0)
674       break;
675   } /* for (i = 0; i < ci->children_num; i++) */
676
677   while (status == 0)
678   {
679     if (xpath->type == NULL)
680     {
681       WARNING ("curl_xml plugin: `Type' missing in `xpath' block.");
682       status = -1;
683     }
684
685     break;
686   } /* while (status == 0) */
687
688   if (status == 0)
689   {
690     char *name;
691     c_avl_tree_t *tree;
692
693     if (db->tree == NULL)
694       db->tree = cx_avl_create();
695
696     tree = db->tree;
697     name = xpath->path;
698
699     if (*name)
700       c_avl_insert (tree, strdup(name), xpath);
701     else
702     {
703       ERROR ("curl_xml plugin: invalid key: %s", xpath->path);
704       status = -1;
705     }
706   }
707
708   return (status);
709 } /* }}} int cx_config_add_xpath */
710
711 /* Initialize db->curl */
712 static int cx_init_curl (cx_t *db) /* {{{ */
713 {
714   db->curl = curl_easy_init ();
715   if (db->curl == NULL)
716   {
717     ERROR ("curl_xml plugin: curl_easy_init failed.");
718     return (-1);
719   }
720
721   curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
722   curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
723   curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
724                     PACKAGE_NAME"/"PACKAGE_VERSION);
725   curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
726   curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
727
728   if (db->user != NULL)
729   {
730     size_t credentials_size;
731
732     credentials_size = strlen (db->user) + 2;
733     if (db->pass != NULL)
734       credentials_size += strlen (db->pass);
735
736     db->credentials = (char *) malloc (credentials_size);
737     if (db->credentials == NULL)
738     {
739       ERROR ("curl_xml plugin: malloc failed.");
740       return (-1);
741     }
742
743     ssnprintf (db->credentials, credentials_size, "%s:%s",
744                db->user, (db->pass == NULL) ? "" : db->pass);
745     curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
746   }
747
748   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer);
749   curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
750                     db->verify_host ? 2 : 0);
751   if (db->cacert != NULL)
752     curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
753
754   return (0);
755 } /* }}} int cx_init_curl */
756
757 static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
758 {
759   cx_t *db;
760   int status = 0;
761   int i;
762
763   if ((ci->values_num != 1)
764       || (ci->values[0].type != OCONFIG_TYPE_STRING))
765   {
766     WARNING ("curl_xml plugin: The `URL' block "
767              "needs exactly one string argument.");
768     return (-1);
769   }
770
771   db = (cx_t *) malloc (sizeof (*db));
772   if (db == NULL)
773   {
774     ERROR ("curl_xml plugin: malloc failed.");
775     return (-1);
776   }
777   memset (db, 0, sizeof (*db));
778
779   if (strcasecmp ("URL", ci->key) == 0)
780   {
781     status = cf_util_get_string (ci, &db->url);
782     if (status != 0)
783     {
784       sfree (db);
785       return (status);
786     }
787   }
788   else
789   {
790     ERROR ("curl_xml plugin: cx_config: "
791            "Invalid key: %s", ci->key);
792     return (-1);
793   }
794
795   /* Fill the `cx_t' structure.. */
796   for (i = 0; i < ci->children_num; i++)
797   {
798     oconfig_item_t *child = ci->children + i;
799
800     if (strcasecmp ("Instance", child->key) == 0)
801       status = cf_util_get_string (child, &db->instance);
802     else if (strcasecmp ("Host", child->key) == 0)
803       status = cf_util_get_string (child, &db->host);
804     else if (strcasecmp ("User", child->key) == 0)
805       status = cf_util_get_string (child, &db->user);
806     else if (strcasecmp ("Password", child->key) == 0)
807       status = cf_util_get_string (child, &db->pass);
808     else if (strcasecmp ("VerifyPeer", child->key) == 0)
809       status = cf_util_get_boolean (child, &db->verify_peer);
810     else if (strcasecmp ("VerifyHost", child->key) == 0)
811       status = cf_util_get_boolean (child, &db->verify_host);
812     else if (strcasecmp ("CACert", child->key) == 0)
813       status = cf_util_get_string (child, &db->cacert);
814     else if (strcasecmp ("xpath", child->key) == 0)
815       status = cx_config_add_xpath (db, child);
816     else
817     {
818       WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
819       status = -1;
820     }
821
822     if (status != 0)
823       break;
824   }
825
826   if (status == 0)
827   {
828     if (db->tree == NULL)
829     {
830       WARNING ("curl_xml plugin: No (valid) `Key' block "
831                "within `URL' block `%s'.", db->url);
832       status = -1;
833     }
834     if (status == 0)
835       status = cx_init_curl (db);
836   }
837
838   /* If all went well, register this database for reading */
839   if (status == 0)
840   {
841     user_data_t ud;
842     char cb_name[DATA_MAX_NAME_LEN];
843
844     if (db->instance == NULL)
845       db->instance = strdup("default");
846
847     DEBUG ("curl_xml plugin: Registering new read callback: %s",
848            db->instance);
849
850     memset (&ud, 0, sizeof (ud));
851     ud.data = (void *) db;
852     ud.free_func = cx_free;
853
854     ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
855                db->instance, db->url);
856
857     plugin_register_complex_read (cb_name, cx_read,
858                                   /* interval = */ NULL, &ud);
859   }
860   else
861   {
862     cx_free (db);
863     return (-1);
864   }
865
866   return (0);
867 } /* }}} int cx_config_add_url */
868
869 /* }}} End of configuration handling functions */
870
871 static int cx_config (oconfig_item_t *ci) /* {{{ */
872 {
873   int success;
874   int errors;
875   int status;
876   int i;
877
878   success = 0;
879   errors = 0;
880
881   for (i = 0; i < ci->children_num; i++)
882   {
883     oconfig_item_t *child = ci->children + i;
884
885     if (strcasecmp ("URL", child->key) == 0)
886     {
887       status = cx_config_add_url (child);
888       if (status == 0)
889         success++;
890       else
891         errors++;
892     }
893     else
894     {
895       WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
896       errors++;
897     }
898   }
899
900   if ((success == 0) && (errors > 0))
901   {
902     ERROR ("curl_xml plugin: All statements failed.");
903     return (-1);
904   }
905
906   return (0);
907 } /* }}} int cx_config */
908
909 void module_register (void)
910 {
911   plugin_register_complex_config ("curl_xml", cx_config);
912 } /* void module_register */
913
914 /* vim: set sw=2 sts=2 et fdm=marker : */