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