2 * collectd - src/bind.c
3 * Copyright (C) 2009 Bruno Prémont
4 * Copyright (C) 2009 Florian Forster
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.
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.
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
20 * Bruno Prémont <bonbons at linux-vserver.org>
21 * Florian Forster <octo at verplant.org>
24 /* Set to C99 and POSIX code */
25 #ifndef _ISOC99_SOURCE
26 # define _ISOC99_SOURCE
29 # define _POSIX_SOURCE
31 #ifndef _POSIX_C_SOURCE
32 # define _POSIX_C_SOURCE 200112L
38 # define _XOPEN_SOURCE 600
47 #include "configfile.h"
49 #include <curl/curl.h>
50 #include <libxml/parser.h>
51 #include <libxml/xpath.h>
53 #ifndef BIND_DEFAULT_URL
54 # define BIND_DEFAULT_URL "http://localhost:8053/"
58 * Some types used for the callback functions. `translation_table_ptr_t' and
59 * `list_info_ptr_t' are passed to the callbacks in the `void *user_data'
62 typedef int (*list_callback_t) (const char *name, counter_t value,
63 time_t current_time, void *user_data);
65 struct translation_info_s
69 const char *type_instance;
70 const int *config_variable;
72 typedef struct translation_info_s translation_info_t;
74 struct translation_table_ptr_s
76 const translation_info_t *table;
78 const char *plugin_instance;
80 typedef struct translation_table_ptr_s translation_table_ptr_t;
82 struct list_info_ptr_s
84 const char *plugin_instance;
87 typedef struct list_info_ptr_s list_info_ptr_t;
89 static char *url = NULL;
90 static int use_requests = 1;
91 static int use_rejects = 1;
92 static int use_responses = 1;
93 static int use_queries = 1;
94 static int use_rcode = 1;
95 static int use_zonestats = 1;
96 static int use_opcode = 1;
97 static int use_resolver = 1;
98 static int use_dnssec = 1;
100 static int use_rrqueries_in = 1;
101 static int use_query_results = 1;
102 static int use_updates = 1;
103 static int use_zone_maint = 1;
105 static CURL *curl = NULL;
107 static char *bind_buffer = NULL;
108 static size_t bind_buffer_size = 0;
109 static size_t bind_buffer_fill = 0;
110 static char bind_curl_error[CURL_ERROR_SIZE];
112 static const char *config_keys[] =
130 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
132 /* Translation table for the `nsstats' values. */
133 static const translation_info_t nsstats_translation_table[] =
136 { "Requestv4", "dns_request", "IPv4", &use_requests },
137 { "Requestv6", "dns_request", "IPv6", &use_requests },
138 { "ReqEdns0", "dns_request", "EDNS0", &use_requests },
139 { "ReqBadEDNSVer", "dns_request", "BadEDNSVer", &use_requests },
140 { "ReqTSIG", "dns_request", "TSIG", &use_requests },
141 { "ReqSIG0", "dns_request", "SIG0", &use_requests },
142 { "ReqBadSIG", "dns_request", "BadSIG", &use_requests },
143 { "ReqTCP", "dns_request", "TCP", &use_requests },
145 { "AuthQryRej", "dns_reject", "authorative", &use_rejects },
146 { "RecQryRej", "dns_reject", "recursive", &use_rejects },
147 { "XfrRej", "dns_reject", "transer", &use_rejects },
148 { "UpdateRej", "dns_reject", "update", &use_rejects },
150 { "Response", "dns_response", "normal", &use_responses },
151 { "TruncatedResp", "dns_response", "truncated", &use_responses },
152 { "RespEDNS0", "dns_response", "EDNS0", &use_responses },
153 { "RespTSIG", "dns_response", "TSIG", &use_responses },
154 { "RespSIG0", "dns_response", "SIG0", &use_responses },
156 { "QryAuthAns", "dns_query", "authorative", &use_queries },
157 { "QryNoauthAns", "dns_query", "nonauth", &use_queries },
158 { "QryReferral", "dns_query", "referral", &use_queries },
159 { "QryRecursion", "dns_query", "recursion", &use_queries },
160 { "QryDuplicate", "dns_query", "dupliate", &use_queries },
161 { "QryDropped", "dns_query", "dropped", &use_queries },
162 { "QryFailure", "dns_query", "failure", &use_queries },
164 { "QrySuccess", "dns_rcode", "tx-NOERROR", &use_rcode },
165 { "QryNxrrset", "dns_rcode", "tx-NXRRSET", &use_rcode },
166 { "QrySERVFAIL", "dns_rcode", "tx-SERVFAIL", &use_rcode },
167 { "QryFORMERR", "dns_rcode", "tx-FORMERR", &use_rcode },
168 { "QryNXDOMAIN", "dns_rcode", "tx-NXDOMAIN", &use_rcode }
170 { "XfrReqDone", "type", "type_instance", &use_something },
171 { "UpdateReqFwd", "type", "type_instance", &use_something },
172 { "UpdateRespFwd", "type", "type_instance", &use_something },
173 { "UpdateFwdFail", "type", "type_instance", &use_something },
174 { "UpdateDone", "type", "type_instance", &use_something },
175 { "UpdateFail", "type", "type_instance", &use_something },
176 { "UpdateBadPrereq", "type", "type_instance", &use_something },
179 static int nsstats_translation_table_length =
180 STATIC_ARRAY_SIZE (nsstats_translation_table);
181 #define PARSE_NSSTATS (use_requests || use_rejects || use_responses \
182 || use_queries || use_rcode)
184 /* Translation table for the `zonestats' values. */
185 static const translation_info_t zonestats_translation_table[] =
188 { "NotifyOutv4", "dns_notify", "tx-IPv4", &use_zonestats },
189 { "NotifyOutv6", "dns_notify", "tx-IPv6", &use_zonestats },
190 { "NotifyInv4", "dns_notify", "rx-IPv4", &use_zonestats },
191 { "NotifyInv6", "dns_notify", "rx-IPv6", &use_zonestats },
192 { "NotifyRej", "dns_notify", "rejected", &use_zonestats },
193 /* SOA/AXFS/IXFS requests */
194 { "SOAOutv4", "dns_opcode", "SOA-IPv4", &use_opcode },
195 { "SOAOutv6", "dns_opcode", "SOA-IPv4", &use_opcode },
196 { "AXFRReqv4", "dns_opcode", "AXFR-IPv4", &use_opcode },
197 { "AXFRReqv6", "dns_opcode", "AXFR-IPv6", &use_opcode },
198 { "IXFRReqv4", "dns_opcode", "IXFR-IPv4", &use_opcode },
199 { "IXFRReqv6", "dns_opcode", "IXFR-IPv6", &use_opcode },
200 /* Domain transfers */
201 { "XfrSuccess", "dns_transfer", "success", &use_zonestats },
202 { "XfrFail", "dns_transfer", "failure", &use_zonestats }
204 static int zonestats_translation_table_length =
205 STATIC_ARRAY_SIZE (zonestats_translation_table);
206 #define PARSE_ZONESTATS (use_zonestats || use_opcode)
208 /* Translation table for the `resstats' values. */
209 static const translation_info_t resstats_translation_table[] =
211 /* Generic resolver information */
212 { "Queryv4", "dns_query", "IPv4", &use_resolver },
213 { "Queryv6", "dns_query", "IPv6", &use_resolver },
214 { "Responsev4", "dns_response", "IPv4", &use_resolver },
215 { "Responsev6", "dns_response", "IPv6", &use_resolver },
216 /* Received response codes */
217 { "NXDOMAIN", "dns_rcode", "rx-NXDOMAIN", &use_rcode },
218 { "SERVFAIL", "dns_rcode", "rx-SERVFAIL", &use_rcode },
219 { "FORMERR", "dns_rcode", "rx-FORMERR", &use_rcode },
220 { "OtherError", "dns_rcode", "rx-OTHER", &use_rcode },
221 { "EDNS0Fail", "dns_rcode", "rx-EDNS0Fail", &use_rcode },
222 /* Received responses */
223 { "Mismatch", "dns_response", "mismatch", &use_responses },
224 { "Truncated", "dns_response", "truncated", &use_responses },
225 { "Lame", "dns_response", "lame", &use_responses },
226 { "Retry", "dns_query", "retry", &use_responses },
228 { "GlueFetchv4", "type", "type_instance", &use_something },
229 { "GlueFetchv6", "type", "type_instance", &use_something },
230 { "GlueFetchv4Fail", "type", "type_instance", &use_something },
231 { "GlueFetchv6Fail", "type", "type_instance", &use_something },
233 /* DNSSEC information */
234 { "ValAttempt", "dns_resolver", "DNSSEC-attempt", &use_dnssec },
235 { "ValOk", "dns_resolver", "DNSSEC-okay", &use_dnssec },
236 { "ValNegOk", "dns_resolver", "DNSSEC-negokay", &use_dnssec },
237 { "ValFail", "dns_resolver", "DNSSEC-fail", &use_dnssec }
239 static int resstats_translation_table_length =
240 STATIC_ARRAY_SIZE (resstats_translation_table);
241 #define PARSE_RESSTATS (use_resolver || use_rcode || use_responses || use_dnssec)
243 static void remove_special (char *buffer, size_t buffer_size)
247 for (i = 0; i < buffer_size; i++)
251 if (!isalnum ((int) buffer[i]))
254 } /* void remove_special */
256 static void submit_counter(time_t ts, const char *plugin_instance, const char *type,
257 const char *type_instance, counter_t value)
260 value_list_t vl = VALUE_LIST_INIT;
262 values[0].counter = value;
267 sstrncpy(vl.host, hostname_g, sizeof(vl.host));
268 sstrncpy(vl.plugin, "bind", sizeof(vl.plugin));
269 if (plugin_instance) {
270 sstrncpy(vl.plugin_instance, plugin_instance,
271 sizeof(vl.plugin_instance));
272 remove_special (vl.plugin_instance, sizeof (vl.plugin_instance));
274 sstrncpy(vl.type, type, sizeof(vl.type));
276 sstrncpy(vl.type_instance, type_instance,
277 sizeof(vl.type_instance));
278 remove_special (vl.plugin_instance, sizeof (vl.plugin_instance));
280 plugin_dispatch_values(&vl);
281 } /* void submit_counter */
283 static size_t bind_curl_callback (void *buf, size_t size, size_t nmemb,
286 size_t len = size * nmemb;
291 if ((bind_buffer_fill + len) >= bind_buffer_size)
295 temp = realloc(bind_buffer, bind_buffer_fill + len + 1);
298 ERROR ("bind plugin: realloc failed.");
302 bind_buffer_size = bind_buffer_fill + len + 1;
305 memcpy (bind_buffer + bind_buffer_fill, (char *) buf, len);
306 bind_buffer_fill += len;
307 bind_buffer[bind_buffer_fill] = 0;
310 } /* size_t bind_curl_callback */
313 * Callback, that's called with a translation table.
314 * (Plugin instance is fixed, type and type instance come from lookup table.)
316 static int bind_xml_table_callback (const char *name, counter_t value,
317 time_t current_time, void *user_data)
319 translation_table_ptr_t *table = (translation_table_ptr_t *) user_data;
325 for (i = 0; i < table->table_length; i++)
327 if (strcmp (table->table[i].xml_name, name) != 0)
330 if (*table->table[i].config_variable == 0)
333 submit_counter (current_time,
334 table->plugin_instance,
335 table->table[i].type,
336 table->table[i].type_instance,
342 } /* int bind_xml_table_callback */
345 * Callback, that's used for lists.
346 * (Plugin instance and type are fixed, xml name is used as type instance.)
348 static int bind_xml_list_callback (const char *name, counter_t value,
349 time_t current_time, void *user_data)
351 list_info_ptr_t *list_info = (list_info_ptr_t *) user_data;
353 if (list_info == NULL)
356 submit_counter (current_time,
357 list_info->plugin_instance,
359 /* type instance = */ name,
363 } /* int bind_xml_list_callback */
365 static int bind_xml_read_counter (xmlDoc *doc, xmlNode *node,
366 counter_t *ret_value)
368 char *str_ptr, *end_ptr;
371 str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
374 ERROR ("bind plugin: bind_xml_read_counter: xmlNodeListGetString failed.");
379 value = strtoll (str_ptr, &end_ptr, 10);
381 if (str_ptr == end_ptr || errno)
383 if (errno && (value < 0))
384 ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with underflow.");
385 else if (errno && (value > 0))
386 ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with overflow.");
388 ERROR ("bind plugin: bind_xml_read_counter: strtoll failed.");
394 } /* int bind_xml_read_counter */
396 static int bind_xml_read_timestamp (const char *xpath_expression, xmlDoc *doc,
397 xmlXPathContext *xpathCtx, time_t *ret_value)
399 xmlXPathObject *xpathObj = NULL;
405 xpathObj = xmlXPathEvalExpression (BAD_CAST xpath_expression, xpathCtx);
406 if (xpathObj == NULL)
408 ERROR ("bind plugin: Unable to evaluate XPath expression `%s'.",
413 if ((xpathObj->nodesetval == NULL) || (xpathObj->nodesetval->nodeNr < 1))
415 xmlXPathFreeObject (xpathObj);
419 if (xpathObj->nodesetval->nodeNr != 1)
421 NOTICE ("bind plugin: Evaluating the XPath expression `%s' returned "
422 "%i nodes. Only handling the first one.",
423 xpath_expression, xpathObj->nodesetval->nodeNr);
426 node = xpathObj->nodesetval->nodeTab[0];
428 if (node->xmlChildrenNode == NULL)
430 ERROR ("bind plugin: bind_xml_read_timestamp: "
431 "node->xmlChildrenNode == NULL");
432 xmlXPathFreeObject (xpathObj);
436 str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
439 ERROR ("bind plugin: bind_xml_read_timestamp: xmlNodeListGetString failed.");
440 xmlXPathFreeObject (xpathObj);
444 memset (&tm, 0, sizeof(tm));
445 tmp = strptime (str_ptr, "%Y-%m-%dT%T", &tm);
449 ERROR ("bind plugin: bind_xml_read_timestamp: strptime failed.");
450 xmlXPathFreeObject (xpathObj);
454 *ret_value = timegm(&tm);
456 xmlXPathFreeObject (xpathObj);
458 } /* int bind_xml_read_timestamp */
461 * bind_parse_generic_name_value
463 * Reads statistics in the form:
469 static int bind_parse_generic_name_value (const char *xpath_expression,
470 list_callback_t list_callback,
472 xmlDoc *doc, xmlXPathContext *xpathCtx,
475 xmlXPathObject *xpathObj = NULL;
479 xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
480 if (xpathObj == NULL)
482 ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
488 /* Iterate over all matching nodes. */
489 for (i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++)
491 xmlNode *name_node = NULL;
492 xmlNode *counter = NULL;
495 /* Iterate over all child nodes. */
496 for (child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode;
500 if (child->type != XML_ELEMENT_NODE)
503 if (xmlStrcmp (BAD_CAST "name", child->name) == 0)
505 else if (xmlStrcmp (BAD_CAST "counter", child->name) == 0)
509 if ((name_node != NULL) && (counter != NULL))
511 char *name = (char *) xmlNodeListGetString (doc,
512 name_node->xmlChildrenNode, 1);
516 status = bind_xml_read_counter (doc, counter, &value);
520 status = (*list_callback) (name, value, current_time, user_data);
528 DEBUG ("bind plugin: Found %d %s for XPath expression `%s'",
529 num_entries, (num_entries == 1) ? "entry" : "entries",
532 xmlXPathFreeObject(xpathObj);
535 } /* int bind_parse_generic_name_value */
538 * bind_parse_generic_value_list
540 * Reads statistics in the form:
548 static int bind_parse_generic_value_list (const char *xpath_expression,
549 list_callback_t list_callback,
551 xmlDoc *doc, xmlXPathContext *xpathCtx,
554 xmlXPathObject *xpathObj = NULL;
558 xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx);
559 if (xpathObj == NULL)
561 ERROR("bind plugin: Unable to evaluate XPath expression `%s'.",
567 /* Iterate over all matching nodes. */
568 for (i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++)
572 /* Iterate over all child nodes. */
573 for (child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode;
581 if (child->type != XML_ELEMENT_NODE)
584 node_name = (char *) child->name;
585 status = bind_xml_read_counter (doc, child, &value);
589 status = (*list_callback) (node_name, value, current_time, user_data);
595 DEBUG ("bind plugin: Found %d %s for XPath expression `%s'",
596 num_entries, (num_entries == 1) ? "entry" : "entries",
599 xmlXPathFreeObject(xpathObj);
602 } /* int bind_parse_generic_value_list */
604 static int bind_xml_stats (int version, xmlDoc *doc,
605 xmlXPathContext *xpathCtx, xmlNode *statsnode)
607 time_t current_time = 0;
610 xpathCtx->node = statsnode;
612 /* TODO: Check `server/boot-time' to recognize server restarts. */
614 status = bind_xml_read_timestamp ("server/current-time",
615 doc, xpathCtx, ¤t_time);
618 ERROR ("bind plugin: Reading `server/current-time' failed.");
621 DEBUG ("bind plugin: Current server time is %i.", (int) current_time);
623 /* XPath: server/requests/opcode
624 * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ...
628 * <counter>1</counter>
634 list_info_ptr_t list_info =
636 /* plugin instance = */ "requests",
637 /* type = */ "dns_opcode"
640 bind_parse_generic_name_value (/* xpath = */ "server/requests/opcode",
641 /* callback = */ bind_xml_list_callback,
642 /* user_data = */ &list_info,
643 doc, xpathCtx, current_time);
646 /* XPath: server/queries-in/rdtype
647 * Variables: RESERVED0, A, NS, CNAME, SOA, MR, PTR, HINFO, MX, TXT, RP,
648 * X25, PX, AAAA, LOC, SRV, NAPTR, A6, DS, RRSIG, NSEC, DNSKEY,
649 * SPF, TKEY, IXFR, AXFR, ANY, ..., Others
653 * <counter>1</counter>
657 if (use_rrqueries_in)
659 list_info_ptr_t list_info =
661 /* plugin instance = */ "queries-in",
662 /* type = */ "dns_qtype"
665 bind_parse_generic_name_value (/* xpath = */ "server/queries-in/rdtype",
666 /* callback = */ bind_xml_list_callback,
667 /* user_data = */ &list_info,
668 doc, xpathCtx, current_time);
671 /* XPath: server/nsstats, server/nsstat
672 * Variables: Requestv4, Requestv6, ReqEdns0, ReqBadEDNSVer, ReqTSIG,
673 * ReqSIG0, ReqBadSIG, ReqTCP, AuthQryRej, RecQryRej, XfrRej,
674 * UpdateRej, Response, TruncatedResp, RespEDNS0, RespTSIG,
675 * RespSIG0, QrySuccess, QryAuthAns, QryNoauthAns, QryReferral,
676 * QryNxrrset, QrySERVFAIL, QryFORMERR, QryNXDOMAIN, QryRecursion,
677 * QryDuplicate, QryDropped, QryFailure, XfrReqDone, UpdateReqFwd,
678 * UpdateRespFwd, UpdateFwdFail, UpdateDone, UpdateFail,
682 * <Requestv4>1</Requestv4>
683 * <Requestv6>0</Requestv6>
688 * <name>Requestv4</name>
689 * <counter>1</counter>
692 * <name>Requestv6</name>
693 * <counter>0</counter>
699 translation_table_ptr_t table_ptr =
701 nsstats_translation_table,
702 nsstats_translation_table_length,
703 /* plugin_instance = */ "nsstats"
708 bind_parse_generic_value_list ("server/nsstats",
709 /* callback = */ bind_xml_table_callback,
710 /* user_data = */ &table_ptr,
711 doc, xpathCtx, current_time);
715 bind_parse_generic_name_value ("server/nsstat",
716 /* callback = */ bind_xml_table_callback,
717 /* user_data = */ &table_ptr,
718 doc, xpathCtx, current_time);
722 /* XPath: server/zonestats, server/zonestat
723 * Variables: NotifyOutv4, NotifyOutv6, NotifyInv4, NotifyInv6, NotifyRej,
724 * SOAOutv4, SOAOutv6, AXFRReqv4, AXFRReqv6, IXFRReqv4, IXFRReqv6,
725 * XfrSuccess, XfrFail
728 * <NotifyOutv4>0</NotifyOutv4>
729 * <NotifyOutv6>0</NotifyOutv6>
734 * <name>NotifyOutv4</name>
735 * <counter>0</counter>
738 * <name>NotifyOutv6</name>
739 * <counter>0</counter>
745 translation_table_ptr_t table_ptr =
747 zonestats_translation_table,
748 zonestats_translation_table_length,
749 /* plugin_instance = */ "zonestats"
754 bind_parse_generic_value_list ("server/zonestats",
755 /* callback = */ bind_xml_table_callback,
756 /* user_data = */ &table_ptr,
757 doc, xpathCtx, current_time);
761 bind_parse_generic_name_value ("server/zonestat",
762 /* callback = */ bind_xml_table_callback,
763 /* user_data = */ &table_ptr,
764 doc, xpathCtx, current_time);
768 /* XPath: server/resstats
769 * Variables: Queryv4, Queryv6, Responsev4, Responsev6, NXDOMAIN, SERVFAIL,
770 * FORMERR, OtherError, EDNS0Fail, Mismatch, Truncated, Lame,
771 * Retry, GlueFetchv4, GlueFetchv6, GlueFetchv4Fail,
772 * GlueFetchv6Fail, ValAttempt, ValOk, ValNegOk, ValFail
775 * <Queryv4>0</Queryv4>
776 * <Queryv6>0</Queryv6>
781 * <name>Queryv4</name>
782 * <counter>0</counter>
785 * <name>Queryv6</name>
786 * <counter>0</counter>
792 translation_table_ptr_t table_ptr =
794 resstats_translation_table,
795 resstats_translation_table_length,
796 /* plugin_instance = */ "resstats"
801 bind_parse_generic_value_list ("server/resstats",
802 /* callback = */ bind_xml_table_callback,
803 /* user_data = */ &table_ptr,
804 doc, xpathCtx, current_time);
808 bind_parse_generic_name_value ("server/resstat",
809 /* callback = */ bind_xml_table_callback,
810 /* user_data = */ &table_ptr,
811 doc, xpathCtx, current_time);
816 } /* int bind_xml_stats */
818 static int bind_xml (const char *data)
821 xmlXPathContext *xpathCtx = NULL;
822 xmlXPathObject *xpathObj = NULL;
826 doc = xmlParseMemory (data, strlen (data));
829 ERROR ("bind plugin: xmlParseMemory failed.");
833 xpathCtx = xmlXPathNewContext (doc);
834 if (xpathCtx == NULL)
836 ERROR ("bind plugin: xmlXPathNewContext failed.");
841 xpathObj = xmlXPathEvalExpression (BAD_CAST "/isc/bind/statistics", xpathCtx);
842 if (xpathObj == NULL)
844 ERROR ("bind plugin: Cannot find the <statistics> tag.");
845 xmlXPathFreeContext (xpathCtx);
849 else if (xpathObj->nodesetval == NULL)
851 ERROR ("bind plugin: xmlXPathEvalExpression failed.");
852 xmlXPathFreeObject (xpathObj);
853 xmlXPathFreeContext (xpathCtx);
858 for (i = 0; i < xpathObj->nodesetval->nodeNr; i++)
862 int parsed_version = 0;
864 node = xpathObj->nodesetval->nodeTab[i];
865 assert (node != NULL);
867 attr_version = (char *) xmlGetProp (node, BAD_CAST "version");
868 if (attr_version == NULL)
870 NOTICE ("bind plugin: Found <statistics> tag doesn't have a "
871 "`version' attribute.");
874 DEBUG ("bind plugin: Found: <statistics version=\"%s\">", attr_version);
876 /* At the time this plugin was written, version "1.0" was used by
877 * BIND 9.5.0, version "2.0" was used by BIND 9.5.1 and 9.6.0. We assume
878 * that "1.*" and "2.*" don't introduce structural changes, so we just
879 * check for the first two characters here. */
880 if (strncmp ("1.", attr_version, strlen ("1.")) == 0)
882 else if (strncmp ("2.", attr_version, strlen ("2.")) == 0)
886 /* TODO: Use the complaint mechanism here. */
887 NOTICE ("bind plugin: Found <statistics> tag with version `%s'. "
888 "Unfortunately I have no clue how to parse that. "
889 "Please open a bug report for this.", attr_version);
890 xmlFree (attr_version);
894 ret = bind_xml_stats (parsed_version,
895 doc, xpathCtx, node);
897 xmlFree (attr_version);
898 /* One <statistics> node ought to be enough. */
902 xmlXPathFreeObject (xpathObj);
903 xmlXPathFreeContext (xpathCtx);
909 static int config_set_str (char **var, const char *value)
917 if ((*var = strdup (value)) == NULL)
921 } /* int config_set_str */
923 static int config_set_bool (int *var, const char *value)
927 else if (IS_FALSE (value))
932 } /* int config_set_bool */
934 static int bind_config (const char *key, const char *value)
936 if (strcasecmp (key, "URL") == 0)
937 return (config_set_str (&url, value));
938 else if (strcasecmp (key, "Requests") == 0)
939 return (config_set_bool (&use_requests, value));
940 else if (strcasecmp (key, "Rejects") == 0)
941 return (config_set_bool (&use_rejects, value));
942 else if (strcasecmp (key, "Responses") == 0)
943 return (config_set_bool (&use_responses, value));
944 else if (strcasecmp (key, "Queries") == 0)
945 return (config_set_bool (&use_queries, value));
946 else if (strcasecmp (key, "RCode") == 0)
947 return (config_set_bool (&use_rcode, value));
948 else if (strcasecmp (key, "ZoneStats") == 0)
949 return (config_set_bool (&use_zonestats, value));
950 else if (strcasecmp (key, "OpCodes") == 0)
951 return (config_set_bool (&use_opcode, value));
952 else if (strcasecmp (key, "Resolver") == 0)
953 return (config_set_bool (&use_resolver, value));
954 else if (strcasecmp (key, "DNSSEC") == 0)
955 return (config_set_bool (&use_dnssec, value));
957 else if (strcasecmp (key, "RRQueriesIn") == 0)
958 return (config_set_bool (&use_rrqueries_in, value));
959 else if (strcasecmp (key, "QueryResults") == 0)
960 return (config_set_bool (&use_query_results, value));
961 else if (strcasecmp (key, "Updates") == 0)
962 return (config_set_bool (&use_updates, value));
963 else if (strcasecmp (key, "ZoneMaintenance") == 0)
964 return (config_set_bool (&use_zone_maint, value));
968 } /* int bind_config */
970 static int bind_init (void)
975 curl = curl_easy_init ();
978 ERROR ("bind plugin: bind_init: curl_easy_init failed.");
982 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback);
983 curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
984 curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error);
985 curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL);
988 } /* int bind_init */
990 static int bind_read (void)
996 ERROR ("bind plugin: I don't have a CURL object.");
1000 bind_buffer_fill = 0;
1001 if (curl_easy_perform (curl) != 0)
1003 ERROR ("bind plugin: curl_easy_perform failed: %s",
1008 status = bind_xml (bind_buffer);
1013 } /* int bind_read */
1015 static int bind_shutdown (void)
1019 curl_easy_cleanup (curl);
1024 } /* int bind_shutdown */
1026 void module_register (void)
1028 plugin_register_config ("bind", bind_config, config_keys, config_keys_num);
1029 plugin_register_init ("bind", bind_init);
1030 plugin_register_read ("bind", bind_read);
1031 plugin_register_shutdown ("bind", bind_shutdown);
1032 } /* void module_register */
1034 /* vim: set sw=2 sts=2 ts=8 et fdm=marker : */