X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fbind.c;h=d7b8c08f7b7c4be06ae8a4fcb96895fcd43f2ff8;hb=0e363577f4d0a94bef1929f9d9829fb94765ec1e;hp=b640a59669a363fe7a0b4e73ccdcb419877a8b47;hpb=26fbc23e518dcc74502ae3b2495112adc3840879;p=collectd.git diff --git a/src/bind.c b/src/bind.c index b640a596..d7b8c08f 100644 --- a/src/bind.c +++ b/src/bind.c @@ -98,6 +98,10 @@ struct list_info_ptr_s }; typedef struct list_info_ptr_s list_info_ptr_t; +/* FIXME: Enabled by default for backwards compatibility. */ +/* TODO: Remove time parsing code. */ +static _Bool config_parse_time = 1; + static char *url = NULL; static int global_opcodes = 1; static int global_qtypes = 1; @@ -249,7 +253,8 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */ vl.values = values; vl.values_len = 1; - vl.time = TIME_T_TO_CDTIME_T (ts); + if (config_parse_time) + vl.time = TIME_T_TO_CDTIME_T (ts); sstrncpy(vl.host, hostname_g, sizeof(vl.host)); sstrncpy(vl.plugin, "bind", sizeof(vl.plugin)); if (plugin_instance) { @@ -622,6 +627,84 @@ static int bind_parse_generic_value_list (const char *xpath_expression, /* {{{ * return (0); } /* }}} int bind_parse_generic_value_list */ +/* + * bind_parse_generic_name_attr_value_list + * + * Reads statistics in the form: + * + * 123 + * 234 + * 345 + * : + * + */ +static int bind_parse_generic_name_attr_value_list (const char *xpath_expression, /* {{{ */ + list_callback_t list_callback, + void *user_data, + xmlDoc *doc, xmlXPathContext *xpathCtx, + time_t current_time, int ds_type) +{ + xmlXPathObject *xpathObj = NULL; + int num_entries; + int i; + + xpathObj = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpathCtx); + if (xpathObj == NULL) + { + ERROR("bind plugin: Unable to evaluate XPath expression `%s'.", + xpath_expression); + return (-1); + } + + num_entries = 0; + /* Iterate over all matching nodes. */ + for (i = 0; xpathObj->nodesetval && (i < xpathObj->nodesetval->nodeNr); i++) + { + xmlNode *child; + + /* Iterate over all child nodes. */ + for (child = xpathObj->nodesetval->nodeTab[i]->xmlChildrenNode; + child != NULL; + child = child->next) + { + if (child->type != XML_ELEMENT_NODE) + continue; + + if (strncmp ("counter", (char *) child->name, strlen ("counter")) != 0) + continue; + + char *attr_name; + value_t value; + int status; + + attr_name = (char *) xmlGetProp (child, BAD_CAST "name"); + if (attr_name == NULL) + { + DEBUG ("bind plugin: found without name."); + continue; + } + if (ds_type == DS_TYPE_GAUGE) + status = bind_xml_read_gauge (doc, child, &value.gauge); + else + status = bind_xml_read_derive (doc, child, &value.derive); + if (status != 0) + continue; + + status = (*list_callback) (attr_name, value, current_time, user_data); + if (status == 0) + num_entries++; + } + } + + DEBUG ("bind plugin: Found %d %s for XPath expression `%s'", + num_entries, (num_entries == 1) ? "entry" : "entries", + xpath_expression); + + xmlXPathFreeObject(xpathObj); + + return (0); +} /* }}} int bind_parse_generic_name_attr_value_list */ + static int bind_xml_stats_handle_zone (int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *path_ctx, xmlNode *node, cb_view_t *view, time_t current_time) @@ -740,45 +823,68 @@ static int bind_xml_stats_search_zones (int version, xmlDoc *doc, /* {{{ */ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */ xmlXPathContext *path_ctx, xmlNode *node, time_t current_time) { - xmlXPathObject *path_obj; char *view_name = NULL; cb_view_t *view; int i; size_t j; - path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx); - if (path_obj == NULL) + if (version == 3) { - ERROR ("bind plugin: xmlXPathEvalExpression failed."); - return (-1); - } + view_name = (char*) xmlGetProp(node, BAD_CAST "name"); - for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++) - { - view_name = (char *) xmlNodeListGetString (doc, - path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1); - if (view_name != NULL) - break; - } + if (view_name == NULL) + { + ERROR ("bind plugin: Could not determine view name."); + return (-1); + } + + for (j = 0; j < views_num; j++) + { + if (strcasecmp (view_name, views[j].name) == 0) + break; + } - if (view_name == NULL) + xmlFree (view_name); + view_name = NULL; + } + else { - ERROR ("bind plugin: Could not determine view name."); + xmlXPathObject *path_obj; + path_obj = xmlXPathEvalExpression (BAD_CAST "name", path_ctx); + if (path_obj == NULL) + { + ERROR ("bind plugin: xmlXPathEvalExpression failed."); + return (-1); + } + + for (i = 0; path_obj->nodesetval && (i < path_obj->nodesetval->nodeNr); i++) + { + view_name = (char *) xmlNodeListGetString (doc, + path_obj->nodesetval->nodeTab[i]->xmlChildrenNode, 1); + if (view_name != NULL) + break; + } + + if (view_name == NULL) + { + ERROR ("bind plugin: Could not determine view name."); + xmlXPathFreeObject (path_obj); + return (-1); + } + + for (j = 0; j < views_num; j++) + { + if (strcasecmp (view_name, views[j].name) == 0) + break; + } + + xmlFree (view_name); xmlXPathFreeObject (path_obj); - return (-1); - } - for (j = 0; j < views_num; j++) - { - if (strcasecmp (view_name, views[j].name) == 0) - break; + view_name = NULL; + path_obj = NULL; } - xmlFree (view_name); - xmlXPathFreeObject (path_obj); - - view_name = NULL; - path_obj = NULL; if (j >= views_num) return (0); @@ -794,16 +900,25 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */ list_info_ptr_t list_info = { plugin_instance, - /* type = */ "dns_qtype_gauge" + /* type = */ "dns_qtype" }; ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-qtypes", view->name); - - bind_parse_generic_name_value (/* xpath = */ "rdtype", + if (version == 3) + { + bind_parse_generic_name_attr_value_list (/* xpath = */ "counters[@type='resqtype']", + /* callback = */ bind_xml_list_callback, + /* user_data = */ &list_info, + doc, path_ctx, current_time, DS_TYPE_COUNTER); + } + else + { + bind_parse_generic_name_value (/* xpath = */ "rdtype", /* callback = */ bind_xml_list_callback, /* user_data = */ &list_info, doc, path_ctx, current_time, DS_TYPE_COUNTER); + } } /* }}} */ if (view->resolver_stats != 0) /* {{{ */ @@ -818,20 +933,30 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */ ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-resolver_stats", view->name); - - bind_parse_generic_name_value ("resstat", - /* callback = */ bind_xml_table_callback, - /* user_data = */ &table_ptr, - doc, path_ctx, current_time, DS_TYPE_COUNTER); + if (version == 3) + { + bind_parse_generic_name_attr_value_list ("counters[@type='resstats']", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, path_ctx, current_time, DS_TYPE_COUNTER); + } + else + { + bind_parse_generic_name_value ("resstat", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, path_ctx, current_time, DS_TYPE_COUNTER); + } } /* }}} */ + /* Record types in the cache */ if (view->cacherrsets != 0) /* {{{ */ { char plugin_instance[DATA_MAX_NAME_LEN]; list_info_ptr_t list_info = { plugin_instance, - /* type = */ "dns_qtype_gauge" + /* type = */ "dns_qtype_cached" }; ssnprintf (plugin_instance, sizeof (plugin_instance), "%s-cache_rr_sets", @@ -843,7 +968,8 @@ static int bind_xml_stats_handle_view (int version, xmlDoc *doc, /* {{{ */ doc, path_ctx, current_time, DS_TYPE_GAUGE); } /* }}} */ - if (view->zones_num > 0) + // v3 does not provide per-zone stats any more + if (version < 3 && view->zones_num > 0) bind_xml_stats_search_zones (version, doc, path_ctx, node, view, current_time); @@ -890,28 +1016,145 @@ static int bind_xml_stats_search_views (int version, xmlDoc *doc, /* {{{ */ return (0); } /* }}} int bind_xml_stats_search_views */ -static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ - xmlXPathContext *xpathCtx, xmlNode *statsnode) +static void bind_xml_stats_v3 (xmlDoc *doc, /* {{{ */ + xmlXPathContext *xpathCtx, xmlNode *statsnode, time_t current_time) { - time_t current_time = 0; - int status; + /* XPath: server/counters[@type='opcode'] + * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ... + * Layout v3: + * + * 1 + * : + * + */ + if (global_opcodes != 0) + { + list_info_ptr_t list_info = + { + /* plugin instance = */ "global-opcodes", + /* type = */ "dns_opcode" + }; + bind_parse_generic_name_attr_value_list (/* xpath = */ "server/counters[@type='opcode']", + /* callback = */ bind_xml_list_callback, + /* user_data = */ &list_info, + doc, xpathCtx, current_time, DS_TYPE_COUNTER); + } - xpathCtx->node = statsnode; + /* XPath: server/counters[@type='qtype'] + * Variables: RESERVED0, A, NS, CNAME, SOA, MR, PTR, HINFO, MX, TXT, RP, + * X25, PX, AAAA, LOC, SRV, NAPTR, A6, DS, RRSIG, NSEC, DNSKEY, + * SPF, TKEY, IXFR, AXFR, ANY, ..., Others + * Layout v3: + * + * 1 + * : + * + */ + if (global_qtypes != 0) + { + list_info_ptr_t list_info = + { + /* plugin instance = */ "global-qtypes", + /* type = */ "dns_qtype" + }; - /* TODO: Check `server/boot-time' to recognize server restarts. */ + bind_parse_generic_name_attr_value_list (/* xpath = */ "server/counters[@type='qtype']", + /* callback = */ bind_xml_list_callback, + /* user_data = */ &list_info, + doc, xpathCtx, current_time, DS_TYPE_COUNTER); + } - status = bind_xml_read_timestamp ("server/current-time", - doc, xpathCtx, ¤t_time); - if (status != 0) + /* XPath: server/counters[@type='nsstat'] + * Variables: Requestv4, Requestv6, ReqEdns0, ReqBadEDNSVer, ReqTSIG, + * ReqSIG0, ReqBadSIG, ReqTCP, AuthQryRej, RecQryRej, XfrRej, + * UpdateRej, Response, TruncatedResp, RespEDNS0, RespTSIG, + * RespSIG0, QrySuccess, QryAuthAns, QryNoauthAns, QryReferral, + * QryNxrrset, QrySERVFAIL, QryFORMERR, QryNXDOMAIN, QryRecursion, + * QryDuplicate, QryDropped, QryFailure, XfrReqDone, UpdateReqFwd, + * UpdateRespFwd, UpdateFwdFail, UpdateDone, UpdateFail, + * UpdateBadPrereq + * Layout v3: + * 1 + * 0 + * : + * + */ + if (global_server_stats) { - ERROR ("bind plugin: Reading `server/current-time' failed."); - return (-1); + translation_table_ptr_t table_ptr = + { + nsstats_translation_table, + nsstats_translation_table_length, + /* plugin_instance = */ "global-server_stats" + }; + + bind_parse_generic_name_attr_value_list ("server/counters[@type='nsstat']", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, xpathCtx, current_time, DS_TYPE_COUNTER); } - DEBUG ("bind plugin: Current server time is %i.", (int) current_time); - /* XPath: server/requests/opcode + /* XPath: server/zonestats, server/zonestat, server/counters[@type='zonestat'] + * Variables: NotifyOutv4, NotifyOutv6, NotifyInv4, NotifyInv6, NotifyRej, + * SOAOutv4, SOAOutv6, AXFRReqv4, AXFRReqv6, IXFRReqv4, IXFRReqv6, + * XfrSuccess, XfrFail + * Layout v3: + * 0 + * 0 + * : + * + */ + if (global_zone_maint_stats) + { + translation_table_ptr_t table_ptr = + { + zonestats_translation_table, + zonestats_translation_table_length, + /* plugin_instance = */ "global-zone_maint_stats" + }; + + bind_parse_generic_name_attr_value_list ("server/counters[@type='zonestat']", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, xpathCtx, current_time, DS_TYPE_COUNTER); + } + + /* XPath: server/resstats, server/counters[@type='resstat'] + * Variables: Queryv4, Queryv6, Responsev4, Responsev6, NXDOMAIN, SERVFAIL, + * FORMERR, OtherError, EDNS0Fail, Mismatch, Truncated, Lame, + * Retry, GlueFetchv4, GlueFetchv6, GlueFetchv4Fail, + * GlueFetchv6Fail, ValAttempt, ValOk, ValNegOk, ValFail + * Layout v3: + * 0 + * 0 + * : + * + */ + if (global_resolver_stats != 0) + { + translation_table_ptr_t table_ptr = + { + resstats_translation_table, + resstats_translation_table_length, + /* plugin_instance = */ "global-resolver_stats" + }; + + bind_parse_generic_name_attr_value_list ("server/counters[@type='resstat']", + /* callback = */ bind_xml_table_callback, + /* user_data = */ &table_ptr, + doc, xpathCtx, current_time, DS_TYPE_COUNTER); + } +} /* }}} bind_xml_stats_v3 */ + +static void bind_xml_stats_v1_v2 (int version, xmlDoc *doc, /* {{{ */ + xmlXPathContext *xpathCtx, xmlNode *statsnode, time_t current_time) +{ + /* XPath: server/requests/opcode, server/counters[@type='opcode'] * Variables: QUERY, IQUERY, NOTIFY, UPDATE, ... - * Layout: + * Layout V1 and V2: * * A * 1 @@ -932,11 +1175,11 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ doc, xpathCtx, current_time, DS_TYPE_COUNTER); } - /* XPath: server/queries-in/rdtype + /* XPath: server/queries-in/rdtype, server/counters[@type='qtype'] * Variables: RESERVED0, A, NS, CNAME, SOA, MR, PTR, HINFO, MX, TXT, RP, * X25, PX, AAAA, LOC, SRV, NAPTR, A6, DS, RRSIG, NSEC, DNSKEY, * SPF, TKEY, IXFR, AXFR, ANY, ..., Others - * Layout: + * Layout v1 or v2: * * A * 1 @@ -956,8 +1199,8 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ /* user_data = */ &list_info, doc, xpathCtx, current_time, DS_TYPE_COUNTER); } - - /* XPath: server/nsstats, server/nsstat + + /* XPath: server/nsstats, server/nsstat, server/counters[@type='nsstat'] * Variables: Requestv4, Requestv6, ReqEdns0, ReqBadEDNSVer, ReqTSIG, * ReqSIG0, ReqBadSIG, ReqTCP, AuthQryRej, RecQryRej, XfrRej, * UpdateRej, Response, TruncatedResp, RespEDNS0, RespTSIG, @@ -986,7 +1229,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ if (global_server_stats) { translation_table_ptr_t table_ptr = - { + { nsstats_translation_table, nsstats_translation_table_length, /* plugin_instance = */ "global-server_stats" @@ -1008,7 +1251,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ } } - /* XPath: server/zonestats, server/zonestat + /* XPath: server/zonestats, server/zonestat, server/counters[@type='zonestat'] * Variables: NotifyOutv4, NotifyOutv6, NotifyInv4, NotifyInv6, NotifyRej, * SOAOutv4, SOAOutv6, AXFRReqv4, AXFRReqv6, IXFRReqv4, IXFRReqv6, * XfrSuccess, XfrFail @@ -1032,7 +1275,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ if (global_zone_maint_stats) { translation_table_ptr_t table_ptr = - { + { zonestats_translation_table, zonestats_translation_table_length, /* plugin_instance = */ "global-zone_maint_stats" @@ -1054,7 +1297,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ } } - /* XPath: server/resstats + /* XPath: server/resstats, server/counters[@type='resstat'] * Variables: Queryv4, Queryv6, Responsev4, Responsev6, NXDOMAIN, SERVFAIL, * FORMERR, OtherError, EDNS0Fail, Mismatch, Truncated, Lame, * Retry, GlueFetchv4, GlueFetchv6, GlueFetchv4Fail, @@ -1079,7 +1322,7 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ if (global_resolver_stats != 0) { translation_table_ptr_t table_ptr = - { + { resstats_translation_table, resstats_translation_table_length, /* plugin_instance = */ "global-resolver_stats" @@ -1100,10 +1343,39 @@ static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ doc, xpathCtx, current_time, DS_TYPE_COUNTER); } } +} /* }}} bind_xml_stats_v1_v2 */ + +static int bind_xml_stats (int version, xmlDoc *doc, /* {{{ */ + xmlXPathContext *xpathCtx, xmlNode *statsnode) +{ + time_t current_time = 0; + int status; + + xpathCtx->node = statsnode; + + /* TODO: Check `server/boot-time' to recognize server restarts. */ + + status = bind_xml_read_timestamp ("server/current-time", + doc, xpathCtx, ¤t_time); + if (status != 0) + { + ERROR ("bind plugin: Reading `server/current-time' failed."); + return (-1); + } + DEBUG ("bind plugin: Current server time is %i.", (int) current_time); + + if (version == 3) + { + bind_xml_stats_v3(doc, xpathCtx, statsnode, current_time); + } + else + { + bind_xml_stats_v1_v2(version, doc, xpathCtx, statsnode, current_time); + } /* XPath: memory/summary * Variables: TotalUse, InUse, BlockSize, ContextSize, Lost - * Layout: v2: + * Layout: v2 and v3: * * 6587096 * 1345424 @@ -1157,6 +1429,64 @@ static int bind_xml (const char *data) /* {{{ */ return (-1); } + // + // version 3.* of statistics XML (since BIND9.9) + // + + xpathObj = xmlXPathEvalExpression (BAD_CAST "/statistics", xpathCtx); + if (xpathObj == NULL || xpathObj->nodesetval == NULL || xpathObj->nodesetval->nodeNr == 0) + { + DEBUG ("bind plugin: Statistics appears not to be v3"); + // we will fallback to v1 or v2 detection + if (xpathObj != NULL) { xmlXPathFreeObject (xpathObj); } + } + else + { + for (i = 0; i < xpathObj->nodesetval->nodeNr; i++) + { + xmlNode *node; + char *attr_version; + + node = xpathObj->nodesetval->nodeTab[i]; + assert (node != NULL); + + attr_version = (char *) xmlGetProp (node, BAD_CAST "version"); + if (attr_version == NULL) + { + NOTICE ("bind plugin: Found tag doesn't have a " + "`version' attribute."); + continue; + } + DEBUG ("bind plugin: Found: ", attr_version); + + if (strncmp ("3.", attr_version, strlen ("3.")) != 0) + { + /* TODO: Use the complaint mechanism here. */ + NOTICE ("bind plugin: Found tag with version `%s'. " + "Unfortunately I have no clue how to parse that. " + "Please open a bug report for this.", attr_version); + xmlFree (attr_version); + continue; + } + ret = bind_xml_stats (3, doc, xpathCtx, node); + + xmlFree (attr_version); + /* One node ought to be enough. */ + break; + } + + // we are finished, early-return + xmlXPathFreeObject (xpathObj); + xmlXPathFreeContext (xpathCtx); + xmlFreeDoc (doc); + + return (ret); + } + + // + // versions 1.* or 2.* of statistics XML + // + xpathObj = xmlXPathEvalExpression (BAD_CAST "/isc/bind/statistics", xpathCtx); if (xpathObj == NULL) { @@ -1363,6 +1693,8 @@ static int bind_config (oconfig_item_t *ci) /* {{{ */ bind_config_set_bool ("MemoryStats", &global_memory_stats, child); else if (strcasecmp ("View", child->key) == 0) bind_config_add_view (child); + else if (strcasecmp ("ParseTime", child->key) == 0) + cf_util_get_boolean (child, &config_parse_time); else { WARNING ("bind plugin: Unknown configuration option " @@ -1385,12 +1717,13 @@ static int bind_init (void) /* {{{ */ return (-1); } - curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, bind_curl_callback); - curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION); + curl_easy_setopt (curl, CURLOPT_USERAGENT, COLLECTD_USERAGENT); curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, bind_curl_error); curl_easy_setopt (curl, CURLOPT_URL, (url != NULL) ? url : BIND_DEFAULT_URL); - curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt (curl, CURLOPT_MAXREDIRS, 50L); return (0); } /* }}} int bind_init */ @@ -1406,7 +1739,7 @@ static int bind_read (void) /* {{{ */ } bind_buffer_fill = 0; - if (curl_easy_perform (curl) != 0) + if (curl_easy_perform (curl) != CURLE_OK) { ERROR ("bind plugin: curl_easy_perform failed: %s", bind_curl_error);