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
31 #include "utils_llist.h"
39 #include <sys/types.h>
43 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
45 #define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0)
47 #define SERVER_SOCKET LOCALSTATEDIR"/run/pdns.controlsocket"
48 #define SERVER_COMMAND "SHOW * \n"
50 #define RECURSOR_SOCKET LOCALSTATEDIR"/run/pdns_recursor.controlsocket"
51 #define RECURSOR_COMMAND "get noerror-answers nxdomain-answers " \
52 "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
53 "cache-misses questions\n"
56 typedef struct list_item_s list_item_t;
65 int (*func) (list_item_t *item);
72 struct sockaddr_un sockaddr;
76 struct statname_lookup_s
80 const char *type_instance;
82 typedef struct statname_lookup_s statname_lookup_t;
84 /* Description of statistics returned by the recursor: {{{
85 all-outqueries counts the number of outgoing UDP queries since starting
86 answers-slow counts the number of queries answered after 1 second
87 answers0-1 counts the number of queries answered within 1 millisecond
88 answers1-10 counts the number of queries answered within 10 milliseconds
89 answers10-100 counts the number of queries answered within 100 milliseconds
90 answers100-1000 counts the number of queries answered within 1 second
91 cache-bytes size of the cache in bytes (since 3.3.1)
92 cache-entries shows the number of entries in the cache
93 cache-hits counts the number of cache hits since starting, this does not include hits that got answered from the packet-cache
94 cache-misses counts the number of cache misses since starting
95 case-mismatches counts the number of mismatches in character case since starting
96 chain-resends number of queries chained to existing outstanding query
97 client-parse-errors counts number of client packets that could not be parsed
98 concurrent-queries shows the number of MThreads currently running
99 dlg-only-drops number of records dropped because of delegation only setting
100 dont-outqueries number of outgoing queries dropped because of 'dont-query' setting (since 3.3)
101 edns-ping-matches number of servers that sent a valid EDNS PING respons
102 edns-ping-mismatches number of servers that sent an invalid EDNS PING response
103 failed-host-entries number of servers that failed to resolve
104 ipv6-outqueries number of outgoing queries over IPv6
105 ipv6-questions counts all End-user initiated queries with the RD bit set, received over IPv6 UDP
106 malloc-bytes returns the number of bytes allocated by the process (broken, always returns 0)
107 max-mthread-stack maximum amount of thread stack ever used
108 negcache-entries shows the number of entries in the Negative answer cache
109 no-packet-error number of errorneous received packets
110 noedns-outqueries number of queries sent out without EDNS
111 noerror-answers counts the number of times it answered NOERROR since starting
112 noping-outqueries number of queries sent out without ENDS PING
113 nsset-invalidations number of times an nsset was dropped because it no longer worked
114 nsspeeds-entries shows the number of entries in the NS speeds map
115 nxdomain-answers counts the number of times it answered NXDOMAIN since starting
116 outgoing-timeouts counts the number of timeouts on outgoing UDP queries since starting
117 over-capacity-drops questions dropped because over maximum concurrent query limit (since 3.2)
118 packetcache-bytes size of the packet cache in bytes (since 3.3.1)
119 packetcache-entries size of packet cache (since 3.2)
120 packetcache-hits packet cache hits (since 3.2)
121 packetcache-misses packet cache misses (since 3.2)
122 policy-drops packets dropped because of (Lua) policy decision
123 qa-latency shows the current latency average
124 questions counts all end-user initiated queries with the RD bit set
125 resource-limits counts number of queries that could not be performed because of resource limits
126 security-status security status based on security polling
127 server-parse-errors counts number of server replied packets that could not be parsed
128 servfail-answers counts the number of times it answered SERVFAIL since starting
129 spoof-prevents number of times PowerDNS considered itself spoofed, and dropped the data
130 sys-msec number of CPU milliseconds spent in 'system' mode
131 tcp-client-overflow number of times an IP address was denied TCP access because it already had too many connections
132 tcp-clients counts the number of currently active TCP/IP clients
133 tcp-outqueries counts the number of outgoing TCP queries since starting
134 tcp-questions counts all incoming TCP queries (since starting)
135 throttle-entries shows the number of entries in the throttle map
136 throttled-out counts the number of throttled outgoing UDP queries since starting
137 throttled-outqueries idem to throttled-out
138 unauthorized-tcp number of TCP questions denied because of allow-from restrictions
139 unauthorized-udp number of UDP questions denied because of allow-from restrictions
140 unexpected-packets number of answers from remote servers that were unexpected (might point to spoofing)
141 unreachables number of times nameservers were unreachable since starting
142 uptime number of seconds process has been running (since 3.1.5)
143 user-msec number of CPU milliseconds spent in 'user' mode
146 static const char* const default_server_fields[] = /* {{{ */
155 "recursing-questions",
161 static int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields);
163 static statname_lookup_t lookup_table[] = /* {{{ */
165 /*********************
166 * Server statistics *
167 *********************/
169 {"recursing-questions", "dns_question", "recurse"},
170 {"tcp-queries", "dns_question", "tcp"},
171 {"udp-queries", "dns_question", "udp"},
172 {"rd-queries", "dns_question", "rd"},
175 {"recursing-answers", "dns_answer", "recurse"},
176 {"tcp-answers", "dns_answer", "tcp"},
177 {"udp-answers", "dns_answer", "udp"},
178 {"recursion-unanswered", "dns_answer", "recursion-unanswered"},
179 {"udp-answers-bytes", "total_bytes", "udp-answers-bytes"},
182 {"cache-bytes", "cache_size", "cache-bytes"},
183 {"packetcache-bytes", "cache_size", "packet-bytes"},
184 {"packetcache-entries", "cache_size", "packet-entries"},
185 {"packetcache-hit", "cache_result", "packet-hit"},
186 {"packetcache-hits", "cache_result", "packet-hit"},
187 {"packetcache-miss", "cache_result", "packet-miss"},
188 {"packetcache-misses", "cache_result", "packet-miss"},
189 {"packetcache-size", "cache_size", "packet"},
190 {"key-cache-size", "cache_size", "key"},
191 {"meta-cache-size", "cache_size", "meta"},
192 {"signature-cache-size", "cache_size", "signature"},
193 {"query-cache-hit", "cache_result", "query-hit"},
194 {"query-cache-miss", "cache_result", "query-miss"},
197 {"latency", "latency", NULL},
200 {"dnsupdate-answers", "dns_answer", "dnsupdate-answer"},
201 {"dnsupdate-changes", "dns_question", "dnsupdate-changes"},
202 {"dnsupdate-queries", "dns_question", "dnsupdate-queries"},
203 {"dnsupdate-refused", "dns_answer", "dnsupdate-refused"},
206 {"corrupt-packets", "ipt_packets", "corrupt"},
207 {"deferred-cache-inserts", "counter", "cache-deferred_insert"},
208 {"deferred-cache-lookup", "counter", "cache-deferred_lookup"},
209 {"dont-outqueries", "dns_question", "dont-outqueries"},
210 {"qsize-a", "cache_size", "answers"},
211 {"qsize-q", "cache_size", "questions"},
212 {"servfail-packets", "ipt_packets", "servfail"},
213 {"timedout-packets", "ipt_packets", "timeout"},
214 {"udp4-answers", "dns_answer", "udp4"},
215 {"udp4-queries", "dns_question", "queries-udp4"},
216 {"udp6-answers", "dns_answer", "udp6"},
217 {"udp6-queries", "dns_question", "queries-udp6"},
218 {"security-status", "dns_question", "security-status"},
219 {"udp-do-queries", "dns_question", "udp-do_queries"},
220 {"signatures", "counter", "signatures"},
222 /***********************
223 * Recursor statistics *
224 ***********************/
225 /* Answers by return code */
226 {"noerror-answers", "dns_rcode", "NOERROR"},
227 {"nxdomain-answers", "dns_rcode", "NXDOMAIN"},
228 {"servfail-answers", "dns_rcode", "SERVFAIL"},
230 /* CPU utilization */
231 {"sys-msec", "cpu", "system"},
232 {"user-msec", "cpu", "user"},
234 /* Question-to-answer latency */
235 {"qa-latency", "latency", NULL},
238 {"cache-entries", "cache_size", NULL},
239 {"cache-hits", "cache_result", "hit"},
240 {"cache-misses", "cache_result", "miss"},
242 /* Total number of questions.. */
243 {"questions", "dns_qtype", "total"},
245 /* All the other stuff.. */
246 {"all-outqueries", "dns_question", "outgoing"},
247 {"answers0-1", "dns_answer", "0_1"},
248 {"answers1-10", "dns_answer", "1_10"},
249 {"answers10-100", "dns_answer", "10_100"},
250 {"answers100-1000", "dns_answer", "100_1000"},
251 {"answers-slow", "dns_answer", "slow"},
252 {"case-mismatches", "counter", "case_mismatches"},
253 {"chain-resends", "dns_question", "chained"},
254 {"client-parse-errors", "counter", "drops-client_parse_error"},
255 {"concurrent-queries", "dns_question", "concurrent"},
256 {"dlg-only-drops", "counter", "drops-delegation_only"},
257 {"edns-ping-matches", "counter", "edns-ping_matches"},
258 {"edns-ping-mismatches", "counter", "edns-ping_mismatches"},
259 {"failed-host-entries", "counter", "entries-failed_host"},
260 {"ipv6-outqueries", "dns_question", "outgoing-ipv6"},
261 {"ipv6-questions", "dns_question", "incoming-ipv6"},
262 {"malloc-bytes", "gauge", "malloc_bytes"},
263 {"max-mthread-stack", "gauge", "max_mthread_stack"},
264 {"no-packet-error", "gauge", "no_packet_error"},
265 {"noedns-outqueries", "dns_question", "outgoing-noedns"},
266 {"noping-outqueries", "dns_question", "outgoing-noping"},
267 {"over-capacity-drops", "dns_question", "incoming-over_capacity"},
268 {"negcache-entries", "cache_size", "negative"},
269 {"nsspeeds-entries", "gauge", "entries-ns_speeds"},
270 {"nsset-invalidations", "counter", "ns_set_invalidation"},
271 {"outgoing-timeouts", "counter", "drops-timeout_outgoing"},
272 {"policy-drops", "counter", "drops-policy"},
273 {"resource-limits", "counter", "drops-resource_limit"},
274 {"server-parse-errors", "counter", "drops-server_parse_error"},
275 {"spoof-prevents", "counter", "drops-spoofed"},
276 {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"},
277 {"tcp-clients", "gauge", "clients-tcp"},
278 {"tcp-outqueries", "dns_question", "outgoing-tcp"},
279 {"tcp-questions", "dns_question", "incoming-tcp"},
280 {"throttled-out", "dns_question", "outgoing-throttled"},
281 {"throttle-entries", "gauge", "entries-throttle"},
282 {"throttled-outqueries", "dns_question", "outgoing-throttle"},
283 {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"},
284 {"unauthorized-udp", "counter", "denied-unauthorized_udp"},
285 {"unexpected-packets", "dns_answer", "unexpected"},
286 {"uptime", "uptime", NULL}
288 static int lookup_table_length = STATIC_ARRAY_SIZE (lookup_table);
290 static llist_t *list = NULL;
292 #define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-powerdns"
293 static char *local_sockpath = NULL;
295 /* TODO: Do this before 4.4:
296 * - Update the collectd.conf(5) manpage.
301 /* <https://doc.powerdns.com/md/recursor/stats/> */
302 static void submit (const char *plugin_instance, /* {{{ */
303 const char *pdns_type, const char *value_str)
305 value_list_t vl = VALUE_LIST_INIT;
308 const char *type = NULL;
309 const char *type_instance = NULL;
310 const data_set_t *ds;
314 for (i = 0; i < lookup_table_length; i++)
315 if (strcmp (lookup_table[i].name, pdns_type) == 0)
318 if (i >= lookup_table_length)
320 INFO ("powerdns plugin: submit: Not found in lookup table: %s = %s;",
321 pdns_type, value_str);
325 if (lookup_table[i].type == NULL)
328 type = lookup_table[i].type;
329 type_instance = lookup_table[i].type_instance;
331 ds = plugin_get_ds (type);
334 ERROR ("powerdns plugin: The lookup table returned type `%s', "
335 "but I cannot find it via `plugin_get_ds'.",
342 ERROR ("powerdns plugin: type `%s' has %zu data sources, "
343 "but I can only handle one.",
348 if (0 != parse_value (value_str, &value, ds->ds[0].type))
350 ERROR ("powerdns plugin: Cannot convert `%s' "
351 "to a number.", value_str);
357 sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
358 sstrncpy (vl.type, type, sizeof (vl.type));
359 if (type_instance != NULL)
360 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
361 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
363 plugin_dispatch_values (&vl);
364 } /* }}} static void submit */
366 static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
368 size_t *ret_buffer_size)
375 size_t buffer_size = 0;
377 struct sockaddr_un sa_unix = { 0 };
379 struct timeval stv_timeout;
380 cdtime_t cdt_timeout;
382 sd = socket (PF_UNIX, item->socktype, 0);
385 FUNC_ERROR ("socket");
389 sa_unix.sun_family = AF_UNIX;
390 sstrncpy (sa_unix.sun_path,
391 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
392 sizeof (sa_unix.sun_path));
394 status = unlink (sa_unix.sun_path);
395 if ((status != 0) && (errno != ENOENT))
397 FUNC_ERROR ("unlink");
404 /* We need to bind to a specific path, because this is a datagram socket
405 * and otherwise the daemon cannot answer. */
406 status = bind (sd, (struct sockaddr *) &sa_unix, sizeof (sa_unix));
413 /* Make the socket writeable by the daemon.. */
414 status = chmod (sa_unix.sun_path, 0666);
417 FUNC_ERROR ("chmod");
421 cdt_timeout = plugin_get_interval () * 3 / 4;
422 if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
423 cdt_timeout = TIME_T_TO_CDTIME_T (2);
425 CDTIME_T_TO_TIMEVAL (cdt_timeout, &stv_timeout);
427 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout, sizeof (stv_timeout));
430 FUNC_ERROR ("setsockopt");
434 status = connect (sd, (struct sockaddr *) &item->sockaddr,
435 sizeof (item->sockaddr));
438 FUNC_ERROR ("connect");
442 status = send (sd, item->command, strlen (item->command), 0);
449 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
455 buffer_size = status + 1;
460 unlink (sa_unix.sun_path);
465 assert (buffer_size > 0);
466 buffer = malloc (buffer_size);
469 FUNC_ERROR ("malloc");
473 memcpy (buffer, temp, buffer_size - 1);
474 buffer[buffer_size - 1] = 0;
476 *ret_buffer = buffer;
477 *ret_buffer_size = buffer_size;
480 } /* }}} int powerdns_get_data_dgram */
482 static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
484 size_t *ret_buffer_size)
491 size_t buffer_size = 0;
493 sd = socket (PF_UNIX, item->socktype, 0);
496 FUNC_ERROR ("socket");
500 struct timeval timeout;
503 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
506 FUNC_ERROR ("setsockopt");
511 status = connect (sd, (struct sockaddr *) &item->sockaddr,
512 sizeof (item->sockaddr));
515 FUNC_ERROR ("connect");
520 /* strlen + 1, because we need to send the terminating NULL byte, too. */
521 status = send (sd, item->command, strlen (item->command) + 1,
534 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
540 else if (status == 0)
543 buffer_new = realloc (buffer, buffer_size + status + 1);
544 if (buffer_new == NULL)
546 FUNC_ERROR ("realloc");
552 memcpy (buffer + buffer_size, temp, status);
553 buffer_size += status;
554 buffer[buffer_size] = 0;
564 assert (status == 0);
565 *ret_buffer = buffer;
566 *ret_buffer_size = buffer_size;
570 } /* }}} int powerdns_get_data_stream */
572 static int powerdns_get_data (list_item_t *item, char **ret_buffer,
573 size_t *ret_buffer_size)
575 if (item->socktype == SOCK_DGRAM)
576 return (powerdns_get_data_dgram (item, ret_buffer, ret_buffer_size));
577 else if (item->socktype == SOCK_STREAM)
578 return (powerdns_get_data_stream (item, ret_buffer, ret_buffer_size));
581 ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
584 } /* int powerdns_get_data */
586 static int powerdns_read_server (list_item_t *item) /* {{{ */
589 size_t buffer_size = 0;
598 const char* const *fields;
601 if (item->command == NULL)
602 item->command = strdup (SERVER_COMMAND);
603 if (item->command == NULL)
605 ERROR ("powerdns plugin: strdup failed.");
609 status = powerdns_get_data (item, &buffer, &buffer_size);
613 if (item->fields_num != 0)
615 fields = (const char* const *) item->fields;
616 fields_num = item->fields_num;
620 fields = default_server_fields;
621 fields_num = default_server_fields_num;
624 assert (fields != NULL);
625 assert (fields_num > 0);
627 /* 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, */
630 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
634 value = strchr (key, '=');
641 if (value[0] == '\0')
644 /* Check if this item was requested. */
646 for (i = 0; i < fields_num; i++)
647 if (strcasecmp (key, fields[i]) == 0)
652 submit (item->instance, key, value);
653 } /* while (strtok_r) */
658 } /* }}} int powerdns_read_server */
661 * powerdns_update_recursor_command
663 * Creates a string that holds the command to be sent to the recursor. This
664 * string is stores in the `command' member of the `list_item_t' passed to the
665 * function. This function is called by `powerdns_read_recursor'.
667 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
675 if (li->fields_num < 1)
677 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
681 sstrncpy (buffer, "get ", sizeof (buffer));
682 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
683 li->fields, li->fields_num,
684 /* seperator = */ " ");
687 ERROR ("powerdns plugin: strjoin failed.");
690 buffer[sizeof (buffer) - 1] = 0;
691 size_t len = strlen (buffer);
692 if (len < sizeof (buffer) - 2)
695 buffer[len++] = '\n';
696 buffer[len++] = '\0';
700 buffer[sizeof (buffer) - 1] = 0;
701 li->command = strdup (buffer);
702 if (li->command == NULL)
704 ERROR ("powerdns plugin: strdup failed.");
709 } /* }}} int powerdns_update_recursor_command */
711 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
714 size_t buffer_size = 0;
725 if (item->command == NULL)
727 status = powerdns_update_recursor_command (item);
730 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
734 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
737 assert (item->command != NULL);
739 status = powerdns_get_data (item, &buffer, &buffer_size);
742 ERROR ("powerdns plugin: powerdns_get_data failed.");
746 keys_list = strdup (item->command);
747 if (keys_list == NULL)
749 FUNC_ERROR ("strdup");
755 value_saveptr = NULL;
757 /* Skip the `get' at the beginning */
758 strtok_r (keys_list, " \t", &key_saveptr);
761 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
765 key = strtok_r (NULL, " \t", &key_saveptr);
769 submit (item->instance, key, value);
770 } /* while (strtok_r) */
776 } /* }}} int powerdns_read_recursor */
778 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
783 if (ci->values_num < 1)
785 WARNING ("powerdns plugin: The `Collect' option needs "
786 "at least one argument.");
790 for (int i = 0; i < ci->values_num; i++)
791 if (ci->values[i].type != OCONFIG_TYPE_STRING)
793 WARNING ("powerdns plugin: Only string arguments are allowed to "
794 "the `Collect' option.");
798 temp = realloc (li->fields,
799 sizeof (char *) * (li->fields_num + ci->values_num));
802 WARNING ("powerdns plugin: realloc failed.");
807 for (int i = 0; i < ci->values_num; i++)
809 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
810 if (li->fields[li->fields_num] == NULL)
812 WARNING ("powerdns plugin: strdup failed.");
818 /* Invalidate a previously computed command */
822 } /* }}} int powerdns_config_add_collect */
824 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
831 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
833 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
838 item = calloc (1, sizeof (*item));
841 ERROR ("powerdns plugin: calloc failed.");
845 item->instance = strdup (ci->values[0].value.string);
846 if (item->instance == NULL)
848 ERROR ("powerdns plugin: strdup failed.");
854 * Set default values for the members of list_item_t
856 if (strcasecmp ("Server", ci->key) == 0)
858 item->server_type = SRV_AUTHORITATIVE;
859 item->func = powerdns_read_server;
860 item->socktype = SOCK_STREAM;
861 socket_temp = strdup (SERVER_SOCKET);
863 else if (strcasecmp ("Recursor", ci->key) == 0)
865 item->server_type = SRV_RECURSOR;
866 item->func = powerdns_read_recursor;
867 item->socktype = SOCK_DGRAM;
868 socket_temp = strdup (RECURSOR_SOCKET);
872 /* We must never get here.. */
878 for (int i = 0; i < ci->children_num; i++)
880 oconfig_item_t *option = ci->children + i;
882 if (strcasecmp ("Collect", option->key) == 0)
883 status = powerdns_config_add_collect (item, option);
884 else if (strcasecmp ("Socket", option->key) == 0)
885 status = cf_util_get_string (option, &socket_temp);
888 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
900 if (socket_temp == NULL)
902 ERROR ("powerdns plugin: socket_temp == NULL.");
907 item->sockaddr.sun_family = AF_UNIX;
908 sstrncpy (item->sockaddr.sun_path, socket_temp,
909 sizeof (item->sockaddr.sun_path));
911 e = llentry_create (item->instance, item);
914 ERROR ("powerdns plugin: llentry_create failed.");
918 llist_append (list, e);
930 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
934 } /* }}} int powerdns_config_add_server */
936 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
938 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
942 list = llist_create ();
946 ERROR ("powerdns plugin: `llist_create' failed.");
951 for (int i = 0; i < ci->children_num; i++)
953 oconfig_item_t *option = ci->children + i;
955 if ((strcasecmp ("Server", option->key) == 0)
956 || (strcasecmp ("Recursor", option->key) == 0))
957 powerdns_config_add_server (option);
958 else if (strcasecmp ("LocalSocket", option->key) == 0)
960 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
962 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
966 char *temp = strdup (option->values[0].value.string);
969 sfree (local_sockpath);
970 local_sockpath = temp;
975 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
977 } /* for (i = 0; i < ci->children_num; i++) */
980 } /* }}} int powerdns_config */
982 static int powerdns_read (void)
984 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
986 list_item_t *item = e->value;
991 } /* static int powerdns_read */
993 static int powerdns_shutdown (void)
998 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
1000 list_item_t *item = (list_item_t *) e->value;
1003 sfree (item->instance);
1004 sfree (item->command);
1008 llist_destroy (list);
1012 } /* static int powerdns_shutdown */
1014 void module_register (void)
1016 plugin_register_complex_config ("powerdns", powerdns_config);
1017 plugin_register_read ("powerdns", powerdns_read);
1018 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1019 } /* void module_register */
1021 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */