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"
38 #include <sys/types.h>
43 #define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)0)->sun_path)
45 #define FUNC_ERROR(func) \
48 ERROR("powerdns plugin: %s failed: %s", func, \
49 sstrerror(errno, errbuf, sizeof(errbuf))); \
51 #define SOCK_ERROR(func, sockpath) \
54 ERROR("powerdns plugin: Socket `%s` %s failed: %s", sockpath, func, \
55 sstrerror(errno, errbuf, sizeof(errbuf))); \
58 #define SERVER_SOCKET LOCALSTATEDIR "/run/pdns.controlsocket"
59 #define SERVER_COMMAND "SHOW * \n"
61 #define RECURSOR_SOCKET LOCALSTATEDIR "/run/pdns_recursor.controlsocket"
62 #define RECURSOR_COMMAND \
63 "get noerror-answers nxdomain-answers " \
64 "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
65 "cache-misses questions \n"
68 typedef struct list_item_s list_item_t;
71 enum { SRV_AUTHORITATIVE, SRV_RECURSOR } server_type;
72 int (*func)(list_item_t *item);
79 struct sockaddr_un sockaddr;
83 struct statname_lookup_s {
86 const char *type_instance;
88 typedef struct statname_lookup_s statname_lookup_t;
90 /* Description of statistics returned by the recursor: {{{
91 all-outqueries counts the number of outgoing UDP queries since starting
92 answers-slow counts the number of queries answered after 1 second
93 answers0-1 counts the number of queries answered within 1 millisecond
94 answers1-10 counts the number of queries answered within 10
96 answers10-100 counts the number of queries answered within 100
98 answers100-1000 counts the number of queries answered within 1 second
99 cache-bytes size of the cache in bytes (since 3.3.1)
100 cache-entries shows the number of entries in the cache
101 cache-hits counts the number of cache hits since starting, this does
102 not include hits that got answered from the packet-cache
103 cache-misses counts the number of cache misses since starting
104 case-mismatches counts the number of mismatches in character case since
106 chain-resends number of queries chained to existing outstanding query
107 client-parse-errors counts number of client packets that could not be parsed
108 concurrent-queries shows the number of MThreads currently running
109 dlg-only-drops number of records dropped because of delegation only
111 dont-outqueries number of outgoing queries dropped because of 'dont-query'
113 edns-ping-matches number of servers that sent a valid EDNS PING respons
114 edns-ping-mismatches number of servers that sent an invalid EDNS PING response
115 failed-host-entries number of servers that failed to resolve
116 ipv6-outqueries number of outgoing queries over IPv6
117 ipv6-questions counts all End-user initiated queries with the RD bit set,
118 received over IPv6 UDP
119 malloc-bytes returns the number of bytes allocated by the process
120 (broken, always returns 0)
121 max-mthread-stack maximum amount of thread stack ever used
122 negcache-entries shows the number of entries in the Negative answer cache
123 no-packet-error number of errorneous received packets
124 noedns-outqueries number of queries sent out without EDNS
125 noerror-answers counts the number of times it answered NOERROR since
127 noping-outqueries number of queries sent out without ENDS PING
128 nsset-invalidations number of times an nsset was dropped because it no longer
130 nsspeeds-entries shows the number of entries in the NS speeds map
131 nxdomain-answers counts the number of times it answered NXDOMAIN since
133 outgoing-timeouts counts the number of timeouts on outgoing UDP queries
135 over-capacity-drops questions dropped because over maximum concurrent query
137 packetcache-bytes size of the packet cache in bytes (since 3.3.1)
138 packetcache-entries size of packet cache (since 3.2)
139 packetcache-hits packet cache hits (since 3.2)
140 packetcache-misses packet cache misses (since 3.2)
141 policy-drops packets dropped because of (Lua) policy decision
142 qa-latency shows the current latency average
143 questions counts all end-user initiated queries with the RD bit set
144 resource-limits counts number of queries that could not be performed
145 because of resource limits
146 security-status security status based on security polling
147 server-parse-errors counts number of server replied packets that could not be
149 servfail-answers counts the number of times it answered SERVFAIL since
151 spoof-prevents number of times PowerDNS considered itself spoofed, and
153 sys-msec number of CPU milliseconds spent in 'system' mode
154 tcp-client-overflow number of times an IP address was denied TCP access
155 because it already had too many connections
156 tcp-clients counts the number of currently active TCP/IP clients
157 tcp-outqueries counts the number of outgoing TCP queries since starting
158 tcp-questions counts all incoming TCP queries (since starting)
159 throttle-entries shows the number of entries in the throttle map
160 throttled-out counts the number of throttled outgoing UDP queries since
162 throttled-outqueries idem to throttled-out
163 unauthorized-tcp number of TCP questions denied because of allow-from
165 unauthorized-udp number of UDP questions denied because of allow-from
167 unexpected-packets number of answers from remote servers that were unexpected
168 (might point to spoofing)
169 unreachables number of times nameservers were unreachable since
171 uptime number of seconds process has been running (since 3.1.5)
172 user-msec number of CPU milliseconds spent in 'user' mode
175 static const char *const default_server_fields[] = /* {{{ */
177 "latency", "packetcache-hit", "packetcache-miss",
178 "packetcache-size", "query-cache-hit", "query-cache-miss",
179 "recursing-answers", "recursing-questions", "tcp-answers",
180 "tcp-queries", "udp-answers", "udp-queries",
182 static int default_server_fields_num = STATIC_ARRAY_SIZE(default_server_fields);
184 static statname_lookup_t lookup_table[] = /* {{{ */
186 /*********************
187 * Server statistics *
188 *********************/
190 {"recursing-questions", "dns_question", "recurse"},
191 {"tcp-queries", "dns_question", "tcp"},
192 {"udp-queries", "dns_question", "udp"},
193 {"rd-queries", "dns_question", "rd"},
196 {"recursing-answers", "dns_answer", "recurse"},
197 {"tcp-answers", "dns_answer", "tcp"},
198 {"udp-answers", "dns_answer", "udp"},
199 {"recursion-unanswered", "dns_answer", "recursion-unanswered"},
200 {"udp-answers-bytes", "total_bytes", "udp-answers-bytes"},
203 {"cache-bytes", "cache_size", "cache-bytes"},
204 {"packetcache-bytes", "cache_size", "packet-bytes"},
205 {"packetcache-entries", "cache_size", "packet-entries"},
206 {"packetcache-hit", "cache_result", "packet-hit"},
207 {"packetcache-hits", "cache_result", "packet-hit"},
208 {"packetcache-miss", "cache_result", "packet-miss"},
209 {"packetcache-misses", "cache_result", "packet-miss"},
210 {"packetcache-size", "cache_size", "packet"},
211 {"key-cache-size", "cache_size", "key"},
212 {"meta-cache-size", "cache_size", "meta"},
213 {"signature-cache-size", "cache_size", "signature"},
214 {"query-cache-hit", "cache_result", "query-hit"},
215 {"query-cache-miss", "cache_result", "query-miss"},
218 {"latency", "latency", NULL},
221 {"dnsupdate-answers", "dns_answer", "dnsupdate-answer"},
222 {"dnsupdate-changes", "dns_question", "dnsupdate-changes"},
223 {"dnsupdate-queries", "dns_question", "dnsupdate-queries"},
224 {"dnsupdate-refused", "dns_answer", "dnsupdate-refused"},
227 {"corrupt-packets", "ipt_packets", "corrupt"},
228 {"deferred-cache-inserts", "counter", "cache-deferred_insert"},
229 {"deferred-cache-lookup", "counter", "cache-deferred_lookup"},
230 {"dont-outqueries", "dns_question", "dont-outqueries"},
231 {"qsize-a", "cache_size", "answers"},
232 {"qsize-q", "cache_size", "questions"},
233 {"servfail-packets", "ipt_packets", "servfail"},
234 {"timedout-packets", "ipt_packets", "timeout"},
235 {"udp4-answers", "dns_answer", "udp4"},
236 {"udp4-queries", "dns_question", "queries-udp4"},
237 {"udp6-answers", "dns_answer", "udp6"},
238 {"udp6-queries", "dns_question", "queries-udp6"},
239 {"security-status", "dns_question", "security-status"},
240 {"udp-do-queries", "dns_question", "udp-do_queries"},
241 {"signatures", "counter", "signatures"},
243 /***********************
244 * Recursor statistics *
245 ***********************/
246 /* Answers by return code */
247 {"noerror-answers", "dns_rcode", "NOERROR"},
248 {"nxdomain-answers", "dns_rcode", "NXDOMAIN"},
249 {"servfail-answers", "dns_rcode", "SERVFAIL"},
251 /* CPU utilization */
252 {"sys-msec", "cpu", "system"},
253 {"user-msec", "cpu", "user"},
255 /* Question-to-answer latency */
256 {"qa-latency", "latency", NULL},
259 {"cache-entries", "cache_size", NULL},
260 {"cache-hits", "cache_result", "hit"},
261 {"cache-misses", "cache_result", "miss"},
263 /* Total number of questions.. */
264 {"questions", "dns_qtype", "total"},
266 /* All the other stuff.. */
267 {"all-outqueries", "dns_question", "outgoing"},
268 {"answers0-1", "dns_answer", "0_1"},
269 {"answers1-10", "dns_answer", "1_10"},
270 {"answers10-100", "dns_answer", "10_100"},
271 {"answers100-1000", "dns_answer", "100_1000"},
272 {"answers-slow", "dns_answer", "slow"},
273 {"case-mismatches", "counter", "case_mismatches"},
274 {"chain-resends", "dns_question", "chained"},
275 {"client-parse-errors", "counter", "drops-client_parse_error"},
276 {"concurrent-queries", "dns_question", "concurrent"},
277 {"dlg-only-drops", "counter", "drops-delegation_only"},
278 {"edns-ping-matches", "counter", "edns-ping_matches"},
279 {"edns-ping-mismatches", "counter", "edns-ping_mismatches"},
280 {"failed-host-entries", "counter", "entries-failed_host"},
281 {"ipv6-outqueries", "dns_question", "outgoing-ipv6"},
282 {"ipv6-questions", "dns_question", "incoming-ipv6"},
283 {"malloc-bytes", "gauge", "malloc_bytes"},
284 {"max-mthread-stack", "gauge", "max_mthread_stack"},
285 {"no-packet-error", "gauge", "no_packet_error"},
286 {"noedns-outqueries", "dns_question", "outgoing-noedns"},
287 {"noping-outqueries", "dns_question", "outgoing-noping"},
288 {"over-capacity-drops", "dns_question", "incoming-over_capacity"},
289 {"negcache-entries", "cache_size", "negative"},
290 {"nsspeeds-entries", "gauge", "entries-ns_speeds"},
291 {"nsset-invalidations", "counter", "ns_set_invalidation"},
292 {"outgoing-timeouts", "counter", "drops-timeout_outgoing"},
293 {"policy-drops", "counter", "drops-policy"},
294 {"resource-limits", "counter", "drops-resource_limit"},
295 {"server-parse-errors", "counter", "drops-server_parse_error"},
296 {"spoof-prevents", "counter", "drops-spoofed"},
297 {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"},
298 {"tcp-clients", "gauge", "clients-tcp"},
299 {"tcp-outqueries", "dns_question", "outgoing-tcp"},
300 {"tcp-questions", "dns_question", "incoming-tcp"},
301 {"throttled-out", "dns_question", "outgoing-throttled"},
302 {"throttle-entries", "gauge", "entries-throttle"},
303 {"throttled-outqueries", "dns_question", "outgoing-throttle"},
304 {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"},
305 {"unauthorized-udp", "counter", "denied-unauthorized_udp"},
306 {"unexpected-packets", "dns_answer", "unexpected"},
307 {"uptime", "uptime", NULL}}; /* }}} */
308 static int lookup_table_length = STATIC_ARRAY_SIZE(lookup_table);
310 static llist_t *list = NULL;
312 #define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR "/run/" PACKAGE_NAME "-powerdns"
313 static char *local_sockpath = NULL;
315 /* TODO: Do this before 4.4:
316 * - Update the collectd.conf(5) manpage.
321 /* <https://doc.powerdns.com/md/recursor/stats/> */
322 static void submit(const char *plugin_instance, /* {{{ */
323 const char *pdns_type, const char *value_str) {
324 value_list_t vl = VALUE_LIST_INIT;
327 const char *type = NULL;
328 const char *type_instance = NULL;
329 const data_set_t *ds;
333 for (i = 0; i < lookup_table_length; i++)
334 if (strcmp(lookup_table[i].name, pdns_type) == 0)
337 if (i >= lookup_table_length) {
338 INFO("powerdns plugin: submit: Not found in lookup table: %s = %s;",
339 pdns_type, value_str);
343 if (lookup_table[i].type == NULL)
346 type = lookup_table[i].type;
347 type_instance = lookup_table[i].type_instance;
349 ds = plugin_get_ds(type);
351 ERROR("powerdns plugin: The lookup table returned type `%s', "
352 "but I cannot find it via `plugin_get_ds'.",
357 if (ds->ds_num != 1) {
358 ERROR("powerdns plugin: type `%s' has %zu data sources, "
359 "but I can only handle one.",
364 if (0 != parse_value(value_str, &value, ds->ds[0].type)) {
365 ERROR("powerdns plugin: Cannot convert `%s' "
373 sstrncpy(vl.plugin, "powerdns", sizeof(vl.plugin));
374 sstrncpy(vl.type, type, sizeof(vl.type));
375 if (type_instance != NULL)
376 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
377 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
379 plugin_dispatch_values(&vl);
380 } /* }}} static void submit */
382 static int powerdns_get_data_dgram(list_item_t *item, /* {{{ */
383 char **ret_buffer, size_t *ret_buffer_size) {
389 size_t buffer_size = 0;
391 struct sockaddr_un sa_unix = {0};
393 cdtime_t cdt_timeout;
395 sd = socket(PF_UNIX, item->socktype, 0);
397 FUNC_ERROR("socket");
401 sa_unix.sun_family = AF_UNIX;
402 sstrncpy(sa_unix.sun_path,
403 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
404 sizeof(sa_unix.sun_path));
406 status = unlink(sa_unix.sun_path);
407 if ((status != 0) && (errno != ENOENT)) {
408 SOCK_ERROR("unlink", sa_unix.sun_path);
415 /* We need to bind to a specific path, because this is a datagram socket
416 * and otherwise the daemon cannot answer. */
417 status = bind(sd, (struct sockaddr *)&sa_unix, sizeof(sa_unix));
419 SOCK_ERROR("bind", sa_unix.sun_path);
423 /* Make the socket writeable by the daemon.. */
424 status = chmod(sa_unix.sun_path, 0666);
426 SOCK_ERROR("chmod", sa_unix.sun_path);
430 cdt_timeout = plugin_get_interval() * 3 / 4;
431 if (cdt_timeout < TIME_T_TO_CDTIME_T(2))
432 cdt_timeout = TIME_T_TO_CDTIME_T(2);
435 setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
436 &CDTIME_T_TO_TIMEVAL(cdt_timeout), sizeof(struct timeval));
438 SOCK_ERROR("setsockopt", sa_unix.sun_path);
443 connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
445 SOCK_ERROR("connect", sa_unix.sun_path);
449 status = send(sd, item->command, strlen(item->command), 0);
451 SOCK_ERROR("send", sa_unix.sun_path);
455 status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
457 SOCK_ERROR("recv", sa_unix.sun_path);
460 buffer_size = status + 1;
465 unlink(sa_unix.sun_path);
470 assert(buffer_size > 0);
471 buffer = malloc(buffer_size);
472 if (buffer == NULL) {
473 FUNC_ERROR("malloc");
477 memcpy(buffer, temp, buffer_size - 1);
478 buffer[buffer_size - 1] = 0;
480 *ret_buffer = buffer;
481 *ret_buffer_size = buffer_size;
484 } /* }}} int powerdns_get_data_dgram */
486 static int powerdns_get_data_stream(list_item_t *item, /* {{{ */
488 size_t *ret_buffer_size) {
494 size_t buffer_size = 0;
496 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));
507 FUNC_ERROR("setsockopt");
513 connect(sd, (struct sockaddr *)&item->sockaddr, sizeof(item->sockaddr));
515 SOCK_ERROR("connect", item->sockaddr.sun_path);
520 /* strlen + 1, because we need to send the terminating NULL byte, too. */
521 status = send(sd, item->command, strlen(item->command) + 1,
524 SOCK_ERROR("send", item->sockaddr.sun_path);
532 status = recv(sd, temp, sizeof(temp), /* flags = */ 0);
534 SOCK_ERROR("recv", item->sockaddr.sun_path);
536 } else if (status == 0)
539 buffer_new = realloc(buffer, buffer_size + status + 1);
540 if (buffer_new == NULL) {
541 FUNC_ERROR("realloc");
547 memcpy(buffer + buffer_size, temp, status);
548 buffer_size += status;
549 buffer[buffer_size] = 0;
557 *ret_buffer = buffer;
558 *ret_buffer_size = buffer_size;
562 } /* }}} int powerdns_get_data_stream */
564 static int powerdns_get_data(list_item_t *item, char **ret_buffer,
565 size_t *ret_buffer_size) {
566 if (item->socktype == SOCK_DGRAM)
567 return (powerdns_get_data_dgram(item, ret_buffer, ret_buffer_size));
568 else if (item->socktype == SOCK_STREAM)
569 return (powerdns_get_data_stream(item, ret_buffer, ret_buffer_size));
571 ERROR("powerdns plugin: Unknown socket type: %i", (int)item->socktype);
574 } /* int powerdns_get_data */
576 static int powerdns_read_server(list_item_t *item) /* {{{ */
578 if (item->command == NULL)
579 item->command = strdup(SERVER_COMMAND);
580 if (item->command == NULL) {
581 ERROR("powerdns plugin: strdup failed.");
586 size_t buffer_size = 0;
587 int status = powerdns_get_data(item, &buffer, &buffer_size);
590 if ((buffer == NULL) || (buffer_size == 0)) {
594 const char *const *fields = default_server_fields;
595 int fields_num = default_server_fields_num;
596 if (item->fields_num != 0) {
597 fields = (const char *const *)item->fields;
598 fields_num = item->fields_num;
601 assert(fields != NULL);
602 assert(fields_num > 0);
604 /* 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,
606 char *dummy = buffer;
607 char *saveptr = NULL;
609 while ((key = strtok_r(dummy, ",", &saveptr)) != NULL) {
612 char *value = strchr(key, '=');
619 if (value[0] == '\0')
622 /* Check if this item was requested. */
624 for (i = 0; i < fields_num; i++)
625 if (strcasecmp(key, fields[i]) == 0)
630 submit(item->instance, key, value);
631 } /* while (strtok_r) */
636 } /* }}} int powerdns_read_server */
639 * powerdns_update_recursor_command
641 * Creates a string that holds the command to be sent to the recursor. This
642 * string is stores in the `command' member of the `list_item_t' passed to the
643 * function. This function is called by `powerdns_read_recursor'.
645 static int powerdns_update_recursor_command(list_item_t *li) /* {{{ */
653 if (li->fields_num < 1) {
654 sstrncpy(buffer, RECURSOR_COMMAND, sizeof(buffer));
656 sstrncpy(buffer, "get ", sizeof(buffer));
657 status = strjoin(&buffer[strlen("get ")], sizeof(buffer) - strlen("get "),
658 li->fields, li->fields_num,
659 /* seperator = */ " ");
661 ERROR("powerdns plugin: strjoin failed.");
664 buffer[sizeof(buffer) - 1] = 0;
665 size_t len = strlen(buffer);
666 if (len < sizeof(buffer) - 2) {
668 buffer[len++] = '\n';
669 buffer[len++] = '\0';
673 buffer[sizeof(buffer) - 1] = 0;
674 li->command = strdup(buffer);
675 if (li->command == NULL) {
676 ERROR("powerdns plugin: strdup failed.");
681 } /* }}} int powerdns_update_recursor_command */
683 static int powerdns_read_recursor(list_item_t *item) /* {{{ */
686 size_t buffer_size = 0;
697 if (item->command == NULL) {
698 status = powerdns_update_recursor_command(item);
700 ERROR("powerdns plugin: powerdns_update_recursor_command failed.");
704 DEBUG("powerdns plugin: powerdns_read_recursor: item->command = %s;",
707 assert(item->command != NULL);
709 status = powerdns_get_data(item, &buffer, &buffer_size);
711 ERROR("powerdns plugin: powerdns_get_data failed.");
715 keys_list = strdup(item->command);
716 if (keys_list == NULL) {
717 FUNC_ERROR("strdup");
723 value_saveptr = NULL;
725 /* Skip the `get' at the beginning */
726 strtok_r(keys_list, " \t", &key_saveptr);
729 while ((value = strtok_r(dummy, " \t\n\r", &value_saveptr)) != NULL) {
732 key = strtok_r(NULL, " \t", &key_saveptr);
736 submit(item->instance, key, value);
737 } /* while (strtok_r) */
743 } /* }}} int powerdns_read_recursor */
745 static int powerdns_config_add_collect(list_item_t *li, /* {{{ */
746 oconfig_item_t *ci) {
749 if (ci->values_num < 1) {
750 WARNING("powerdns plugin: The `Collect' option needs "
751 "at least one argument.");
755 for (int i = 0; i < ci->values_num; i++)
756 if (ci->values[i].type != OCONFIG_TYPE_STRING) {
757 WARNING("powerdns plugin: Only string arguments are allowed to "
758 "the `Collect' option.");
763 realloc(li->fields, sizeof(char *) * (li->fields_num + ci->values_num));
765 WARNING("powerdns plugin: realloc failed.");
770 for (int i = 0; i < ci->values_num; i++) {
771 li->fields[li->fields_num] = strdup(ci->values[i].value.string);
772 if (li->fields[li->fields_num] == NULL) {
773 WARNING("powerdns plugin: strdup failed.");
779 /* Invalidate a previously computed command */
783 } /* }}} int powerdns_config_add_collect */
785 static int powerdns_config_add_server(oconfig_item_t *ci) /* {{{ */
792 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
793 WARNING("powerdns plugin: `%s' needs exactly one string argument.",
798 item = calloc(1, sizeof(*item));
800 ERROR("powerdns plugin: calloc failed.");
804 item->instance = strdup(ci->values[0].value.string);
805 if (item->instance == NULL) {
806 ERROR("powerdns plugin: strdup failed.");
812 * Set default values for the members of list_item_t
814 if (strcasecmp("Server", ci->key) == 0) {
815 item->server_type = SRV_AUTHORITATIVE;
816 item->func = powerdns_read_server;
817 item->socktype = SOCK_STREAM;
818 socket_temp = strdup(SERVER_SOCKET);
819 } else if (strcasecmp("Recursor", ci->key) == 0) {
820 item->server_type = SRV_RECURSOR;
821 item->func = powerdns_read_recursor;
822 item->socktype = SOCK_DGRAM;
823 socket_temp = strdup(RECURSOR_SOCKET);
825 /* We must never get here.. */
831 for (int i = 0; i < ci->children_num; i++) {
832 oconfig_item_t *option = ci->children + i;
834 if (strcasecmp("Collect", option->key) == 0)
835 status = powerdns_config_add_collect(item, option);
836 else if (strcasecmp("Socket", option->key) == 0)
837 status = cf_util_get_string(option, &socket_temp);
839 ERROR("powerdns plugin: Option `%s' not allowed here.", option->key);
847 while (status == 0) {
850 if (socket_temp == NULL) {
851 ERROR("powerdns plugin: socket_temp == NULL.");
856 item->sockaddr.sun_family = AF_UNIX;
857 sstrncpy(item->sockaddr.sun_path, socket_temp,
858 sizeof(item->sockaddr.sun_path));
860 e = llentry_create(item->instance, item);
862 ERROR("powerdns plugin: llentry_create failed.");
866 llist_append(list, e);
877 DEBUG("powerdns plugin: Add server: instance = %s;", item->instance);
881 } /* }}} int powerdns_config_add_server */
883 static int powerdns_config(oconfig_item_t *ci) /* {{{ */
885 DEBUG("powerdns plugin: powerdns_config (ci = %p);", (void *)ci);
888 list = llist_create();
891 ERROR("powerdns plugin: `llist_create' failed.");
896 for (int i = 0; i < ci->children_num; i++) {
897 oconfig_item_t *option = ci->children + i;
899 if ((strcasecmp("Server", option->key) == 0) ||
900 (strcasecmp("Recursor", option->key) == 0))
901 powerdns_config_add_server(option);
902 else if (strcasecmp("LocalSocket", option->key) == 0) {
903 if ((option->values_num != 1) ||
904 (option->values[0].type != OCONFIG_TYPE_STRING)) {
905 WARNING("powerdns plugin: `%s' needs exactly one string argument.",
908 char *temp = strdup(option->values[0].value.string);
911 sfree(local_sockpath);
912 local_sockpath = temp;
915 ERROR("powerdns plugin: Option `%s' not allowed here.", option->key);
917 } /* for (i = 0; i < ci->children_num; i++) */
920 } /* }}} int powerdns_config */
922 static int powerdns_read(void) {
923 for (llentry_t *e = llist_head(list); e != NULL; e = e->next) {
924 list_item_t *item = e->value;
929 } /* static int powerdns_read */
931 static int powerdns_shutdown(void) {
935 for (llentry_t *e = llist_head(list); e != NULL; e = e->next) {
936 list_item_t *item = (list_item_t *)e->value;
939 sfree(item->instance);
940 sfree(item->command);
948 } /* static int powerdns_shutdown */
950 void module_register(void) {
951 plugin_register_complex_config("powerdns", powerdns_config);
952 plugin_register_read("powerdns", powerdns_read);
953 plugin_register_shutdown("powerdns", powerdns_shutdown);
954 } /* void module_register */
956 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */