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 "configfile.h"
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)
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;",
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, &values[0], ds->ds[0].type))
350 ERROR ("powerdns plugin: Cannot convert `%s' "
351 "to a number.", value);
357 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
358 sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
359 sstrncpy (vl.type, type, sizeof (vl.type));
360 if (type_instance != NULL)
361 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
362 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
364 plugin_dispatch_values (&vl);
365 } /* }}} static void submit */
367 static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
369 size_t *ret_buffer_size)
376 size_t buffer_size = 0;
378 struct sockaddr_un sa_unix;
380 struct timeval stv_timeout;
381 cdtime_t cdt_timeout;
383 sd = socket (PF_UNIX, item->socktype, 0);
386 FUNC_ERROR ("socket");
390 memset (&sa_unix, 0, sizeof (sa_unix));
391 sa_unix.sun_family = AF_UNIX;
392 sstrncpy (sa_unix.sun_path,
393 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
394 sizeof (sa_unix.sun_path));
396 status = unlink (sa_unix.sun_path);
397 if ((status != 0) && (errno != ENOENT))
399 FUNC_ERROR ("unlink");
406 /* We need to bind to a specific path, because this is a datagram socket
407 * and otherwise the daemon cannot answer. */
408 status = bind (sd, (struct sockaddr *) &sa_unix, sizeof (sa_unix));
415 /* Make the socket writeable by the daemon.. */
416 status = chmod (sa_unix.sun_path, 0666);
419 FUNC_ERROR ("chmod");
423 cdt_timeout = plugin_get_interval () * 3 / 4;
424 if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
425 cdt_timeout = TIME_T_TO_CDTIME_T (2);
427 CDTIME_T_TO_TIMEVAL (cdt_timeout, &stv_timeout);
429 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout, sizeof (stv_timeout));
432 FUNC_ERROR ("setsockopt");
436 status = connect (sd, (struct sockaddr *) &item->sockaddr,
437 sizeof (item->sockaddr));
440 FUNC_ERROR ("connect");
444 status = send (sd, item->command, strlen (item->command), 0);
451 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
457 buffer_size = status + 1;
462 unlink (sa_unix.sun_path);
467 assert (buffer_size > 0);
468 buffer = malloc (buffer_size);
471 FUNC_ERROR ("malloc");
475 memcpy (buffer, temp, buffer_size - 1);
476 buffer[buffer_size - 1] = 0;
478 *ret_buffer = buffer;
479 *ret_buffer_size = buffer_size;
482 } /* }}} int powerdns_get_data_dgram */
484 static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
486 size_t *ret_buffer_size)
493 size_t buffer_size = 0;
495 sd = socket (PF_UNIX, item->socktype, 0);
498 FUNC_ERROR ("socket");
502 struct timeval timeout;
505 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
508 FUNC_ERROR ("setsockopt");
513 status = connect (sd, (struct sockaddr *) &item->sockaddr,
514 sizeof (item->sockaddr));
517 FUNC_ERROR ("connect");
522 /* strlen + 1, because we need to send the terminating NULL byte, too. */
523 status = send (sd, item->command, strlen (item->command) + 1,
536 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
542 else if (status == 0)
545 buffer_new = (char *) realloc (buffer, buffer_size + status + 1);
546 if (buffer_new == NULL)
548 FUNC_ERROR ("realloc");
554 memcpy (buffer + buffer_size, temp, status);
555 buffer_size += status;
556 buffer[buffer_size] = 0;
566 assert (status == 0);
567 *ret_buffer = buffer;
568 *ret_buffer_size = buffer_size;
572 } /* }}} int powerdns_get_data_stream */
574 static int powerdns_get_data (list_item_t *item, char **ret_buffer,
575 size_t *ret_buffer_size)
577 if (item->socktype == SOCK_DGRAM)
578 return (powerdns_get_data_dgram (item, ret_buffer, ret_buffer_size));
579 else if (item->socktype == SOCK_STREAM)
580 return (powerdns_get_data_stream (item, ret_buffer, ret_buffer_size));
583 ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
586 } /* int powerdns_get_data */
588 static int powerdns_read_server (list_item_t *item) /* {{{ */
591 size_t buffer_size = 0;
600 const char* const *fields;
603 if (item->command == NULL)
604 item->command = strdup (SERVER_COMMAND);
605 if (item->command == NULL)
607 ERROR ("powerdns plugin: strdup failed.");
611 status = powerdns_get_data (item, &buffer, &buffer_size);
615 if (item->fields_num != 0)
617 fields = (const char* const *) item->fields;
618 fields_num = item->fields_num;
622 fields = default_server_fields;
623 fields_num = default_server_fields_num;
626 assert (fields != NULL);
627 assert (fields_num > 0);
629 /* 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, */
632 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
638 value = strchr (key, '=');
645 if (value[0] == '\0')
648 /* Check if this item was requested. */
649 for (i = 0; i < fields_num; i++)
650 if (strcasecmp (key, fields[i]) == 0)
655 submit (item->instance, key, value);
656 } /* while (strtok_r) */
661 } /* }}} int powerdns_read_server */
664 * powerdns_update_recursor_command
666 * Creates a string that holds the command to be sent to the recursor. This
667 * string is stores in the `command' member of the `list_item_t' passed to the
668 * function. This function is called by `powerdns_read_recursor'.
670 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
678 if (li->fields_num < 1)
680 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
684 sstrncpy (buffer, "get ", sizeof (buffer));
685 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
686 li->fields, li->fields_num,
687 /* seperator = */ " ");
690 ERROR ("powerdns plugin: strjoin failed.");
693 buffer[sizeof (buffer) - 1] = 0;
694 size_t len = strlen (buffer);
695 if (len < sizeof (buffer) - 2)
698 buffer[len++] = '\n';
699 buffer[len++] = '\0';
703 buffer[sizeof (buffer) - 1] = 0;
704 li->command = strdup (buffer);
705 if (li->command == NULL)
707 ERROR ("powerdns plugin: strdup failed.");
712 } /* }}} int powerdns_update_recursor_command */
714 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
717 size_t buffer_size = 0;
728 if (item->command == NULL)
730 status = powerdns_update_recursor_command (item);
733 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
737 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
740 assert (item->command != NULL);
742 status = powerdns_get_data (item, &buffer, &buffer_size);
745 ERROR ("powerdns plugin: powerdns_get_data failed.");
749 keys_list = strdup (item->command);
750 if (keys_list == NULL)
752 FUNC_ERROR ("strdup");
758 value_saveptr = NULL;
760 /* Skip the `get' at the beginning */
761 strtok_r (keys_list, " \t", &key_saveptr);
764 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
768 key = strtok_r (NULL, " \t", &key_saveptr);
772 submit (item->instance, key, value);
773 } /* while (strtok_r) */
779 } /* }}} int powerdns_read_recursor */
781 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
787 if (ci->values_num < 1)
789 WARNING ("powerdns plugin: The `Collect' option needs "
790 "at least one argument.");
794 for (i = 0; i < ci->values_num; i++)
795 if (ci->values[i].type != OCONFIG_TYPE_STRING)
797 WARNING ("powerdns plugin: Only string arguments are allowed to "
798 "the `Collect' option.");
802 temp = (char **) realloc (li->fields,
803 sizeof (char *) * (li->fields_num + ci->values_num));
806 WARNING ("powerdns plugin: realloc failed.");
811 for (i = 0; i < ci->values_num; i++)
813 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
814 if (li->fields[li->fields_num] == NULL)
816 WARNING ("powerdns plugin: strdup failed.");
822 /* Invalidate a previously computed command */
826 } /* }}} int powerdns_config_add_collect */
828 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
836 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
838 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
843 item = calloc (1, sizeof (*item));
846 ERROR ("powerdns plugin: calloc failed.");
850 item->instance = strdup (ci->values[0].value.string);
851 if (item->instance == NULL)
853 ERROR ("powerdns plugin: strdup failed.");
859 * Set default values for the members of list_item_t
861 if (strcasecmp ("Server", ci->key) == 0)
863 item->server_type = SRV_AUTHORITATIVE;
864 item->func = powerdns_read_server;
865 item->socktype = SOCK_STREAM;
866 socket_temp = strdup (SERVER_SOCKET);
868 else if (strcasecmp ("Recursor", ci->key) == 0)
870 item->server_type = SRV_RECURSOR;
871 item->func = powerdns_read_recursor;
872 item->socktype = SOCK_DGRAM;
873 socket_temp = strdup (RECURSOR_SOCKET);
877 /* We must never get here.. */
883 for (i = 0; i < ci->children_num; i++)
885 oconfig_item_t *option = ci->children + i;
887 if (strcasecmp ("Collect", option->key) == 0)
888 status = powerdns_config_add_collect (item, option);
889 else if (strcasecmp ("Socket", option->key) == 0)
890 status = cf_util_get_string (option, &socket_temp);
893 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
905 if (socket_temp == NULL)
907 ERROR ("powerdns plugin: socket_temp == NULL.");
912 item->sockaddr.sun_family = AF_UNIX;
913 sstrncpy (item->sockaddr.sun_path, socket_temp,
914 sizeof (item->sockaddr.sun_path));
916 e = llentry_create (item->instance, item);
919 ERROR ("powerdns plugin: llentry_create failed.");
923 llist_append (list, e);
935 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
939 } /* }}} int powerdns_config_add_server */
941 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
945 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
949 list = llist_create ();
953 ERROR ("powerdns plugin: `llist_create' failed.");
958 for (i = 0; i < ci->children_num; i++)
960 oconfig_item_t *option = ci->children + i;
962 if ((strcasecmp ("Server", option->key) == 0)
963 || (strcasecmp ("Recursor", option->key) == 0))
964 powerdns_config_add_server (option);
965 else if (strcasecmp ("LocalSocket", option->key) == 0)
967 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
969 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
973 char *temp = strdup (option->values[0].value.string);
976 sfree (local_sockpath);
977 local_sockpath = temp;
982 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
984 } /* for (i = 0; i < ci->children_num; i++) */
987 } /* }}} int powerdns_config */
989 static int powerdns_read (void)
993 for (e = llist_head (list); e != NULL; e = e->next)
995 list_item_t *item = e->value;
1000 } /* static int powerdns_read */
1002 static int powerdns_shutdown (void)
1009 for (e = llist_head (list); e != NULL; e = e->next)
1011 list_item_t *item = (list_item_t *) e->value;
1014 sfree (item->instance);
1015 sfree (item->command);
1019 llist_destroy (list);
1023 } /* static int powerdns_shutdown */
1025 void module_register (void)
1027 plugin_register_complex_config ("powerdns", powerdns_config);
1028 plugin_register_read ("powerdns", powerdns_read);
1029 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1030 } /* void module_register */
1032 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */