2 * collectd - src/powerdns.c
3 * Copyright (C) 2007-2008 C-Ware, Inc.
4 * Copyright (C) 2008 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 * Luke Heberling <lukeh at c-ware.com>
21 * Florian Forster <octo at collectd.org>
24 * Queries a PowerDNS control socket for statistics
30 #include "utils/common/common.h"
31 #include "utils_llist.h"
38 #include <sys/types.h>
43 #define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
45 #define FUNC_ERROR(func) \
47 ERROR("powerdns plugin: %s failed: %s", func, STRERRNO); \
49 #define SOCK_ERROR(func, sockpath) \
51 ERROR("powerdns plugin: Socket `%s` %s failed: %s", sockpath, func, \
55 #define SERVER_SOCKET LOCALSTATEDIR "/run/pdns.controlsocket"
56 #define SERVER_COMMAND "SHOW * \n"
58 #define RECURSOR_SOCKET LOCALSTATEDIR "/run/pdns_recursor.controlsocket"
59 #define RECURSOR_COMMAND \
60 "get noerror-answers nxdomain-answers " \
61 "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
62 "cache-misses questions \n"
65 typedef struct list_item_s list_item_t;
68 enum { SRV_AUTHORITATIVE, SRV_RECURSOR } server_type;
69 int (*func)(list_item_t *item);
76 struct sockaddr_un sockaddr;
80 struct statname_lookup_s {
83 const char *type_instance;
85 typedef struct statname_lookup_s statname_lookup_t;
87 /* Description of statistics returned by the recursor: {{{
88 all-outqueries counts the number of outgoing UDP queries since starting
89 answers-slow counts the number of queries answered after 1 second
90 answers0-1 counts the number of queries answered within 1 millisecond
91 answers1-10 counts the number of queries answered within 10
93 answers10-100 counts the number of queries answered within 100
95 answers100-1000 counts the number of queries answered within 1 second
96 cache-bytes size of the cache in bytes (since 3.3.1)
97 cache-entries shows the number of entries in the cache
98 cache-hits counts the number of cache hits since starting, this does
99 not include hits that got answered from the packet-cache
100 cache-misses counts the number of cache misses since starting
101 case-mismatches counts the number of mismatches in character case since
103 chain-resends number of queries chained to existing outstanding query
104 client-parse-errors counts number of client packets that could not be parsed
105 concurrent-queries shows the number of MThreads currently running
106 dlg-only-drops number of records dropped because of delegation only
108 dont-outqueries number of outgoing queries dropped because of 'dont-query'
110 edns-ping-matches number of servers that sent a valid EDNS PING respons
111 edns-ping-mismatches number of servers that sent an invalid EDNS PING response
112 failed-host-entries number of servers that failed to resolve
113 ipv6-outqueries number of outgoing queries over IPv6
114 ipv6-questions counts all End-user initiated queries with the RD bit set,
115 received over IPv6 UDP
116 malloc-bytes returns the number of bytes allocated by the process
117 (broken, always returns 0)
118 max-mthread-stack maximum amount of thread stack ever used
119 negcache-entries shows the number of entries in the Negative answer cache
120 no-packet-error number of errorneous received packets
121 noedns-outqueries number of queries sent out without EDNS
122 noerror-answers counts the number of times it answered NOERROR since
124 noping-outqueries number of queries sent out without ENDS PING
125 nsset-invalidations number of times an nsset was dropped because it no longer
127 nsspeeds-entries shows the number of entries in the NS speeds map
128 nxdomain-answers counts the number of times it answered NXDOMAIN since
130 outgoing-timeouts counts the number of timeouts on outgoing UDP queries
132 over-capacity-drops questions dropped because over maximum concurrent query
134 packetcache-bytes size of the packet cache in bytes (since 3.3.1)
135 packetcache-entries size of packet cache (since 3.2)
136 packetcache-hits packet cache hits (since 3.2)
137 packetcache-misses packet cache misses (since 3.2)
138 policy-drops packets dropped because of (Lua) policy decision
139 qa-latency shows the current latency average
140 questions counts all end-user initiated queries with the RD bit set
141 resource-limits counts number of queries that could not be performed
142 because of resource limits
143 security-status security status based on security polling
144 server-parse-errors counts number of server replied packets that could not be
146 servfail-answers counts the number of times it answered SERVFAIL since
148 spoof-prevents number of times PowerDNS considered itself spoofed, and
150 sys-msec number of CPU milliseconds spent in 'system' mode
151 tcp-client-overflow number of times an IP address was denied TCP access
152 because it already had too many connections
153 tcp-clients counts the number of currently active TCP/IP clients
154 tcp-outqueries counts the number of outgoing TCP queries since starting
155 tcp-questions counts all incoming TCP queries (since starting)
156 throttle-entries shows the number of entries in the throttle map
157 throttled-out counts the number of throttled outgoing UDP queries since
159 throttled-outqueries idem to throttled-out
160 unauthorized-tcp number of TCP questions denied because of allow-from
162 unauthorized-udp number of UDP questions denied because of allow-from
164 unexpected-packets number of answers from remote servers that were unexpected
165 (might point to spoofing)
166 unreachables number of times nameservers were unreachable since
168 uptime number of seconds process has been running (since 3.1.5)
169 user-msec number of CPU milliseconds spent in 'user' mode
172 static const char *const default_server_fields[] = /* {{{ */
174 "latency", "packetcache-hit", "packetcache-miss",
175 "packetcache-size", "query-cache-hit", "query-cache-miss",
176 "recursing-answers", "recursing-questions", "tcp-answers",
177 "tcp-queries", "udp-answers", "udp-queries",
179 static int default_server_fields_num = STATIC_ARRAY_SIZE(default_server_fields);
181 static statname_lookup_t lookup_table[] = /* {{{ */
183 /*********************
184 * Server statistics *
185 *********************/
187 {"recursing-questions", "dns_question", "recurse"},
188 {"tcp-queries", "dns_question", "tcp"},
189 {"udp-queries", "dns_question", "udp"},
190 {"rd-queries", "dns_question", "rd"},
193 {"recursing-answers", "dns_answer", "recurse"},
194 {"tcp-answers", "dns_answer", "tcp"},
195 {"udp-answers", "dns_answer", "udp"},
196 {"recursion-unanswered", "dns_answer", "recursion-unanswered"},
197 {"udp-answers-bytes", "total_bytes", "udp-answers-bytes"},
200 {"cache-bytes", "cache_size", "cache-bytes"},
201 {"packetcache-bytes", "cache_size", "packet-bytes"},
202 {"packetcache-entries", "cache_size", "packet-entries"},
203 {"packetcache-hit", "cache_result", "packet-hit"},
204 {"packetcache-hits", "cache_result", "packet-hit"},
205 {"packetcache-miss", "cache_result", "packet-miss"},
206 {"packetcache-misses", "cache_result", "packet-miss"},
207 {"packetcache-size", "cache_size", "packet"},
208 {"key-cache-size", "cache_size", "key"},
209 {"meta-cache-size", "cache_size", "meta"},
210 {"signature-cache-size", "cache_size", "signature"},
211 {"query-cache-hit", "cache_result", "query-hit"},
212 {"query-cache-miss", "cache_result", "query-miss"},
215 {"latency", "latency", NULL},
218 {"dnsupdate-answers", "dns_answer", "dnsupdate-answer"},
219 {"dnsupdate-changes", "dns_question", "dnsupdate-changes"},
220 {"dnsupdate-queries", "dns_question", "dnsupdate-queries"},
221 {"dnsupdate-refused", "dns_answer", "dnsupdate-refused"},
224 {"corrupt-packets", "ipt_packets", "corrupt"},
225 {"deferred-cache-inserts", "counter", "cache-deferred_insert"},
226 {"deferred-cache-lookup", "counter", "cache-deferred_lookup"},
227 {"dont-outqueries", "dns_question", "dont-outqueries"},
228 {"qsize-a", "cache_size", "answers"},
229 {"qsize-q", "cache_size", "questions"},
230 {"servfail-packets", "ipt_packets", "servfail"},
231 {"timedout-packets", "ipt_packets", "timeout"},
232 {"udp4-answers", "dns_answer", "udp4"},
233 {"udp4-queries", "dns_question", "queries-udp4"},
234 {"udp6-answers", "dns_answer", "udp6"},
235 {"udp6-queries", "dns_question", "queries-udp6"},
236 {"security-status", "dns_question", "security-status"},
237 {"udp-do-queries", "dns_question", "udp-do_queries"},
238 {"signatures", "counter", "signatures"},
240 /***********************
241 * Recursor statistics *
242 ***********************/
243 /* Answers by return code */
244 {"noerror-answers", "dns_rcode", "NOERROR"},
245 {"nxdomain-answers", "dns_rcode", "NXDOMAIN"},
246 {"servfail-answers", "dns_rcode", "SERVFAIL"},
248 /* CPU utilization */
249 {"sys-msec", "cpu", "system"},
250 {"user-msec", "cpu", "user"},
252 /* Question-to-answer latency */
253 {"qa-latency", "latency", NULL},
256 {"cache-entries", "cache_size", NULL},
257 {"cache-hits", "cache_result", "hit"},
258 {"cache-misses", "cache_result", "miss"},
260 /* Total number of questions.. */
261 {"questions", "dns_qtype", "total"},
263 /* All the other stuff.. */
264 {"all-outqueries", "dns_question", "outgoing"},
265 {"answers0-1", "dns_answer", "0_1"},
266 {"answers1-10", "dns_answer", "1_10"},
267 {"answers10-100", "dns_answer", "10_100"},
268 {"answers100-1000", "dns_answer", "100_1000"},
269 {"answers-slow", "dns_answer", "slow"},
270 {"case-mismatches", "counter", "case_mismatches"},
271 {"chain-resends", "dns_question", "chained"},
272 {"client-parse-errors", "counter", "drops-client_parse_error"},
273 {"concurrent-queries", "dns_question", "concurrent"},
274 {"dlg-only-drops", "counter", "drops-delegation_only"},
275 {"edns-ping-matches", "counter", "edns-ping_matches"},
276 {"edns-ping-mismatches", "counter", "edns-ping_mismatches"},
277 {"failed-host-entries", "counter", "entries-failed_host"},
278 {"ipv6-outqueries", "dns_question", "outgoing-ipv6"},
279 {"ipv6-questions", "dns_question", "incoming-ipv6"},
280 {"malloc-bytes", "gauge", "malloc_bytes"},
281 {"max-mthread-stack", "gauge", "max_mthread_stack"},
282 {"no-packet-error", "errors", "no_packet_error"},
283 {"noedns-outqueries", "dns_question", "outgoing-noedns"},
284 {"noping-outqueries", "dns_question", "outgoing-noping"},
285 {"over-capacity-drops", "dns_question", "incoming-over_capacity"},
286 {"negcache-entries", "cache_size", "negative"},
287 {"nsspeeds-entries", "gauge", "entries-ns_speeds"},
288 {"nsset-invalidations", "counter", "ns_set_invalidation"},
289 {"outgoing-timeouts", "counter", "drops-timeout_outgoing"},
290 {"policy-drops", "counter", "drops-policy"},
291 {"resource-limits", "counter", "drops-resource_limit"},
292 {"server-parse-errors", "counter", "drops-server_parse_error"},
293 {"spoof-prevents", "counter", "drops-spoofed"},
294 {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"},
295 {"tcp-clients", "gauge", "clients-tcp"},
296 {"tcp-outqueries", "dns_question", "outgoing-tcp"},
297 {"tcp-questions", "dns_question", "incoming-tcp"},
298 {"throttled-out", "dns_question", "outgoing-throttled"},
299 {"throttle-entries", "gauge", "entries-throttle"},
300 {"throttled-outqueries", "dns_question", "outgoing-throttle"},
301 {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"},
302 {"unauthorized-udp", "counter", "denied-unauthorized_udp"},
303 {"unexpected-packets", "dns_answer", "unexpected"},
304 {"unreachables", "counter", "unreachables"},
305 {"uptime", "uptime", NULL}}; /* }}} */
306 static int lookup_table_length = STATIC_ARRAY_SIZE(lookup_table);
308 static llist_t *list;
310 #define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR "/run/" PACKAGE_NAME "-powerdns"
311 static char *local_sockpath;
313 /* TODO: Do this before 4.4:
314 * - Update the collectd.conf(5) manpage.
319 /* <https://doc.powerdns.com/md/recursor/stats/> */
320 static void submit(const char *plugin_instance, /* {{{ */
321 const char *pdns_type, const char *value_str) {
322 value_list_t vl = VALUE_LIST_INIT;
325 const char *type = NULL;
326 const char *type_instance = NULL;
327 const data_set_t *ds;
331 for (i = 0; i < lookup_table_length; i++)
332 if (strcmp(lookup_table[i].name, pdns_type) == 0)
335 if (i >= lookup_table_length) {
336 INFO("powerdns plugin: submit: Not found in lookup table: %s = %s;",
337 pdns_type, value_str);
341 if (lookup_table[i].type == NULL)
344 type = lookup_table[i].type;
345 type_instance = lookup_table[i].type_instance;
347 ds = plugin_get_ds(type);
349 ERROR("powerdns plugin: The lookup table returned type `%s', "
350 "but I cannot find it via `plugin_get_ds'.",
355 if (ds->ds_num != 1) {
356 ERROR("powerdns plugin: type `%s' has %" PRIsz " data sources, "
357 "but I can only handle one.",
362 if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
368 sstrncpy(vl.plugin, "powerdns", sizeof(vl.plugin));
369 sstrncpy(vl.type, type, sizeof(vl.type));
370 if (type_instance != NULL)
371 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
372 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
374 plugin_dispatch_values(&vl);
375 } /* }}} static void submit */
377 static int powerdns_get_data_dgram(list_item_t *item, char **ret_buffer) {
384 size_t buffer_size = 0;
386 struct sockaddr_un sa_unix = {0};
388 cdtime_t cdt_timeout;
390 sd = socket(PF_UNIX, item->socktype, 0);
392 FUNC_ERROR("socket");
396 sa_unix.sun_family = AF_UNIX;
397 sstrncpy(sa_unix.sun_path,
398 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
399 sizeof(sa_unix.sun_path));
401 status = unlink(sa_unix.sun_path);
402 if ((status != 0) && (errno != ENOENT)) {
403 SOCK_ERROR("unlink", sa_unix.sun_path);
410 /* We need to bind to a specific path, because this is a datagram socket
411 * and otherwise the daemon cannot answer. */
412 status = bind(sd, (struct sockaddr *)&sa_unix, sizeof(sa_unix));
414 SOCK_ERROR("bind", sa_unix.sun_path);
418 /* Make the socket writeable by the daemon.. */
419 status = chmod(sa_unix.sun_path, 0666);
421 SOCK_ERROR("chmod", sa_unix.sun_path);
425 cdt_timeout = plugin_get_interval() * 3 / 4;
426 if (cdt_timeout < TIME_T_TO_CDTIME_T(2))
427 cdt_timeout = TIME_T_TO_CDTIME_T(2);
430 setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
431 &CDTIME_T_TO_TIMEVAL(cdt_timeout), sizeof(struct timeval));
433 SOCK_ERROR("setsockopt", sa_unix.sun_path);
438 connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
440 SOCK_ERROR("connect", sa_unix.sun_path);
444 status = send(sd, item->command, strlen(item->command), 0);
446 SOCK_ERROR("send", sa_unix.sun_path);
450 status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
452 SOCK_ERROR("recv", sa_unix.sun_path);
455 buffer_size = status + 1;
460 unlink(sa_unix.sun_path);
465 assert(buffer_size > 0);
466 buffer = malloc(buffer_size);
467 if (buffer == NULL) {
468 FUNC_ERROR("malloc");
472 memcpy(buffer, temp, buffer_size - 1);
473 buffer[buffer_size - 1] = '\0';
475 *ret_buffer = buffer;
477 } /* }}} int powerdns_get_data_dgram */
479 static int powerdns_get_data_stream(list_item_t *item, char **ret_buffer) {
486 size_t buffer_size = 0;
488 sd = socket(PF_UNIX, item->socktype, 0);
490 FUNC_ERROR("socket");
494 struct timeval timeout;
497 status = setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
499 FUNC_ERROR("setsockopt");
505 connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
507 SOCK_ERROR("connect", item->sockaddr.sun_path);
512 /* strlen + 1, because we need to send the terminating NULL byte, too. */
513 status = send(sd, item->command, strlen(item->command) + 1,
516 SOCK_ERROR("send", item->sockaddr.sun_path);
524 status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
526 SOCK_ERROR("recv", item->sockaddr.sun_path);
528 } else if (status == 0) {
532 buffer_new = realloc(buffer, buffer_size + status + 1);
533 if (buffer_new == NULL) {
534 FUNC_ERROR("realloc");
540 memcpy(buffer + buffer_size, temp, status);
541 buffer_size += status;
542 buffer[buffer_size] = 0;
551 *ret_buffer = buffer;
553 } /* }}} int powerdns_get_data_stream */
555 static int powerdns_get_data(list_item_t *item, char **ret_buffer) {
556 if (item->socktype == SOCK_DGRAM)
557 return powerdns_get_data_dgram(item, ret_buffer);
558 else if (item->socktype == SOCK_STREAM)
559 return powerdns_get_data_stream(item, ret_buffer);
561 ERROR("powerdns plugin: Unknown socket type: %i", (int)item->socktype);
564 } /* int powerdns_get_data */
566 static int powerdns_read_server(list_item_t *item) /* {{{ */
568 if (item->command == NULL)
569 item->command = strdup(SERVER_COMMAND);
570 if (item->command == NULL) {
571 ERROR("powerdns plugin: strdup failed.");
576 int status = powerdns_get_data(item, &buffer);
578 ERROR("powerdns plugin: powerdns_get_data failed.");
581 if (buffer == NULL) {
585 const char *const *fields = default_server_fields;
586 int fields_num = default_server_fields_num;
587 if (item->fields_num != 0) {
588 fields = (const char *const *)item->fields;
589 fields_num = item->fields_num;
592 assert(fields != NULL);
593 assert(fields_num > 0);
595 /* corrupt-packets=0,deferred-cache-inserts=0,deferred-cache-lookup=0,latency=0,packetcache-hit=0,packetcache-miss=0,packetcache-size=0,qsize-q=0,query-cache-hit=0,query-cache-miss=0,recursing-answers=0,recursing-questions=0,servfail-packets=0,tcp-answers=0,tcp-queries=0,timedout-packets=0,udp-answers=0,udp-queries=0,udp4-answers=0,udp4-queries=0,udp6-answers=0,udp6-queries=0,
597 char *dummy = buffer;
598 char *saveptr = NULL;
600 while ((key = strtok_r(dummy, ",", &saveptr)) != NULL) {
603 char *value = strchr(key, '=');
610 if (value[0] == '\0')
613 /* Check if this item was requested. */
615 for (i = 0; i < fields_num; i++)
616 if (strcasecmp(key, fields[i]) == 0)
621 submit(item->instance, key, value);
622 } /* while (strtok_r) */
627 } /* }}} int powerdns_read_server */
630 * powerdns_update_recursor_command
632 * Creates a string that holds the command to be sent to the recursor. This
633 * string is stores in the `command' member of the `list_item_t' passed to the
634 * function. This function is called by `powerdns_read_recursor'.
636 static int powerdns_update_recursor_command(list_item_t *li) /* {{{ */
644 if (li->fields_num < 1) {
645 sstrncpy(buffer, RECURSOR_COMMAND, sizeof(buffer));
647 sstrncpy(buffer, "get ", sizeof(buffer));
648 status = strjoin(&buffer[strlen("get ")], sizeof(buffer) - strlen("get "),
649 li->fields, li->fields_num,
650 /* seperator = */ " ");
652 ERROR("powerdns plugin: strjoin failed.");
655 buffer[sizeof(buffer) - 1] = 0;
656 size_t len = strlen(buffer);
657 if (len < sizeof(buffer) - 2) {
659 buffer[len++] = '\n';
660 buffer[len++] = '\0';
664 buffer[sizeof(buffer) - 1] = 0;
665 li->command = strdup(buffer);
666 if (li->command == NULL) {
667 ERROR("powerdns plugin: strdup failed.");
672 } /* }}} int powerdns_update_recursor_command */
674 static int powerdns_read_recursor(list_item_t *item) /* {{{ */
687 if (item->command == NULL) {
688 status = powerdns_update_recursor_command(item);
690 ERROR("powerdns plugin: powerdns_update_recursor_command failed.");
694 DEBUG("powerdns plugin: powerdns_read_recursor: item->command = %s;",
697 assert(item->command != NULL);
699 status = powerdns_get_data(item, &buffer);
701 ERROR("powerdns plugin: powerdns_get_data failed.");
705 keys_list = strdup(item->command);
706 if (keys_list == NULL) {
707 FUNC_ERROR("strdup");
713 value_saveptr = NULL;
715 /* Skip the `get' at the beginning */
716 strtok_r(keys_list, " \t", &key_saveptr);
719 while ((value = strtok_r(dummy, " \t\n\r", &value_saveptr)) != NULL) {
722 key = strtok_r(NULL, " \t", &key_saveptr);
726 submit(item->instance, key, value);
727 } /* while (strtok_r) */
733 } /* }}} int powerdns_read_recursor */
735 static int powerdns_config_add_collect(list_item_t *li, /* {{{ */
736 oconfig_item_t *ci) {
739 if (ci->values_num < 1) {
740 WARNING("powerdns plugin: The `Collect' option needs "
741 "at least one argument.");
745 for (int i = 0; i < ci->values_num; i++)
746 if (ci->values[i].type != OCONFIG_TYPE_STRING) {
747 WARNING("powerdns plugin: Only string arguments are allowed to "
748 "the `Collect' option.");
753 realloc(li->fields, sizeof(char *) * (li->fields_num + ci->values_num));
755 WARNING("powerdns plugin: realloc failed.");
760 for (int i = 0; i < ci->values_num; i++) {
761 li->fields[li->fields_num] = strdup(ci->values[i].value.string);
762 if (li->fields[li->fields_num] == NULL) {
763 WARNING("powerdns plugin: strdup failed.");
769 /* Invalidate a previously computed command */
773 } /* }}} int powerdns_config_add_collect */
775 static int powerdns_config_add_server(oconfig_item_t *ci) /* {{{ */
782 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
783 WARNING("powerdns plugin: `%s' needs exactly one string argument.",
788 item = calloc(1, sizeof(*item));
790 ERROR("powerdns plugin: calloc failed.");
794 item->instance = strdup(ci->values[0].value.string);
795 if (item->instance == NULL) {
796 ERROR("powerdns plugin: strdup failed.");
802 * Set default values for the members of list_item_t
804 if (strcasecmp("Server", ci->key) == 0) {
805 item->server_type = SRV_AUTHORITATIVE;
806 item->func = powerdns_read_server;
807 item->socktype = SOCK_STREAM;
808 socket_temp = strdup(SERVER_SOCKET);
809 } else if (strcasecmp("Recursor", ci->key) == 0) {
810 item->server_type = SRV_RECURSOR;
811 item->func = powerdns_read_recursor;
812 item->socktype = SOCK_DGRAM;
813 socket_temp = strdup(RECURSOR_SOCKET);
815 /* We must never get here.. */
821 for (int i = 0; i < ci->children_num; i++) {
822 oconfig_item_t *option = ci->children + i;
824 if (strcasecmp("Collect", option->key) == 0)
825 status = powerdns_config_add_collect(item, option);
826 else if (strcasecmp("Socket", option->key) == 0)
827 status = cf_util_get_string(option, &socket_temp);
829 ERROR("powerdns plugin: Option `%s' not allowed here.", option->key);
837 while (status == 0) {
840 if (socket_temp == NULL) {
841 ERROR("powerdns plugin: socket_temp == NULL.");
846 item->sockaddr.sun_family = AF_UNIX;
847 sstrncpy(item->sockaddr.sun_path, socket_temp,
848 sizeof(item->sockaddr.sun_path));
850 e = llentry_create(item->instance, item);
852 ERROR("powerdns plugin: llentry_create failed.");
856 llist_append(list, e);
867 DEBUG("powerdns plugin: Add server: instance = %s;", item->instance);
871 } /* }}} int powerdns_config_add_server */
873 static int powerdns_config(oconfig_item_t *ci) /* {{{ */
875 DEBUG("powerdns plugin: powerdns_config (ci = %p);", (void *)ci);
878 list = llist_create();
881 ERROR("powerdns plugin: `llist_create' failed.");
886 for (int i = 0; i < ci->children_num; i++) {
887 oconfig_item_t *option = ci->children + i;
889 if ((strcasecmp("Server", option->key) == 0) ||
890 (strcasecmp("Recursor", option->key) == 0))
891 powerdns_config_add_server(option);
892 else if (strcasecmp("LocalSocket", option->key) == 0) {
893 if ((option->values_num != 1) ||
894 (option->values[0].type != OCONFIG_TYPE_STRING)) {
895 WARNING("powerdns plugin: `%s' needs exactly one string argument.",
898 char *temp = strdup(option->values[0].value.string);
901 sfree(local_sockpath);
902 local_sockpath = temp;
905 ERROR("powerdns plugin: Option `%s' not allowed here.", option->key);
907 } /* for (i = 0; i < ci->children_num; i++) */
910 } /* }}} int powerdns_config */
912 static int powerdns_read(void) {
913 for (llentry_t *e = llist_head(list); e != NULL; e = e->next) {
914 list_item_t *item = e->value;
919 } /* static int powerdns_read */
921 static int powerdns_shutdown(void) {
925 for (llentry_t *e = llist_head(list); e != NULL; e = e->next) {
926 list_item_t *item = (list_item_t *)e->value;
929 sfree(item->instance);
930 sfree(item->command);
938 } /* static int powerdns_shutdown */
940 void module_register(void) {
941 plugin_register_complex_config("powerdns", powerdns_config);
942 plugin_register_read("powerdns", powerdns_read);
943 plugin_register_shutdown("powerdns", powerdns_shutdown);
944 } /* void module_register */