1 /**
2 * collectd - src/powerdns.c
3 * Copyright (C) 2007-2008 C-Ware, Inc.
4 * Copyright (C) 2008 Florian Forster
5 *
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.
9 *
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.
14 *
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
18 *
19 * Author:
20 * Luke Heberling <lukeh at c-ware.com>
21 * Florian Forster <octo at collectd.org>
22 *
23 * DESCRIPTION
24 * Queries a PowerDNS control socket for statistics
25 **/
27 #include "collectd.h"
29 #include "common.h"
30 #include "plugin.h"
31 #include "configfile.h"
32 #include "utils_llist.h"
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/un.h>
43 #ifndef UNIX_PATH_MAX
44 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
45 #endif
46 #define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0)
48 #define SERVER_SOCKET LOCALSTATEDIR"/run/pdns.controlsocket"
49 #define SERVER_COMMAND "SHOW * \n"
51 #define RECURSOR_SOCKET LOCALSTATEDIR"/run/pdns_recursor.controlsocket"
52 #define RECURSOR_COMMAND "get noerror-answers nxdomain-answers " \
53 "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
54 "cache-misses questions\n"
56 struct list_item_s;
57 typedef struct list_item_s list_item_t;
59 struct list_item_s
60 {
61 enum
62 {
63 SRV_AUTHORITATIVE,
64 SRV_RECURSOR
65 } server_type;
66 int (*func) (list_item_t *item);
67 char *instance;
69 char **fields;
70 int fields_num;
71 char *command;
73 struct sockaddr_un sockaddr;
74 int socktype;
75 };
77 struct statname_lookup_s
78 {
79 const char *name;
80 const char *type;
81 const char *type_instance;
82 };
83 typedef struct statname_lookup_s statname_lookup_t;
85 /* Description of statistics returned by the recursor: {{{
86 all-outqueries counts the number of outgoing UDP queries since starting
87 answers-slow counts the number of queries answered after 1 second
88 answers0-1 counts the number of queries answered within 1 millisecond
89 answers1-10 counts the number of queries answered within 10 milliseconds
90 answers10-100 counts the number of queries answered within 100 milliseconds
91 answers100-1000 counts the number of queries answered within 1 second
92 cache-bytes size of the cache in bytes (since 3.3.1)
93 cache-entries shows the number of entries in the cache
94 cache-hits counts the number of cache hits since starting, this does not include hits that got answered from the packet-cache
95 cache-misses counts the number of cache misses since starting
96 case-mismatches counts the number of mismatches in character case since starting
97 chain-resends number of queries chained to existing outstanding query
98 client-parse-errors counts number of client packets that could not be parsed
99 concurrent-queries shows the number of MThreads currently running
100 dlg-only-drops number of records dropped because of delegation only setting
101 dont-outqueries number of outgoing queries dropped because of 'dont-query' setting (since 3.3)
102 edns-ping-matches number of servers that sent a valid EDNS PING respons
103 edns-ping-mismatches number of servers that sent an invalid EDNS PING response
104 failed-host-entries number of servers that failed to resolve
105 ipv6-outqueries number of outgoing queries over IPv6
106 ipv6-questions counts all End-user initiated queries with the RD bit set, received over IPv6 UDP
107 malloc-bytes returns the number of bytes allocated by the process (broken, always returns 0)
108 max-mthread-stack maximum amount of thread stack ever used
109 negcache-entries shows the number of entries in the Negative answer cache
110 no-packet-error number of errorneous received packets
111 noedns-outqueries number of queries sent out without EDNS
112 noerror-answers counts the number of times it answered NOERROR since starting
113 noping-outqueries number of queries sent out without ENDS PING
114 nsset-invalidations number of times an nsset was dropped because it no longer worked
115 nsspeeds-entries shows the number of entries in the NS speeds map
116 nxdomain-answers counts the number of times it answered NXDOMAIN since starting
117 outgoing-timeouts counts the number of timeouts on outgoing UDP queries since starting
118 over-capacity-drops questions dropped because over maximum concurrent query limit (since 3.2)
119 packetcache-bytes size of the packet cache in bytes (since 3.3.1)
120 packetcache-entries size of packet cache (since 3.2)
121 packetcache-hits packet cache hits (since 3.2)
122 packetcache-misses packet cache misses (since 3.2)
123 policy-drops packets dropped because of (Lua) policy decision
124 qa-latency shows the current latency average
125 questions counts all end-user initiated queries with the RD bit set
126 resource-limits counts number of queries that could not be performed because of resource limits
127 security-status security status based on security polling
128 server-parse-errors counts number of server replied packets that could not be parsed
129 servfail-answers counts the number of times it answered SERVFAIL since starting
130 spoof-prevents number of times PowerDNS considered itself spoofed, and dropped the data
131 sys-msec number of CPU milliseconds spent in 'system' mode
132 tcp-client-overflow number of times an IP address was denied TCP access because it already had too many connections
133 tcp-clients counts the number of currently active TCP/IP clients
134 tcp-outqueries counts the number of outgoing TCP queries since starting
135 tcp-questions counts all incoming TCP queries (since starting)
136 throttle-entries shows the number of entries in the throttle map
137 throttled-out counts the number of throttled outgoing UDP queries since starting
138 throttled-outqueries idem to throttled-out
139 unauthorized-tcp number of TCP questions denied because of allow-from restrictions
140 unauthorized-udp number of UDP questions denied because of allow-from restrictions
141 unexpected-packets number of answers from remote servers that were unexpected (might point to spoofing)
142 unreachables number of times nameservers were unreachable since starting
143 uptime number of seconds process has been running (since 3.1.5)
144 user-msec number of CPU milliseconds spent in 'user' mode
145 }}} */
147 static const char* const default_server_fields[] = /* {{{ */
148 {
149 "latency",
150 "packetcache-hit",
151 "packetcache-miss",
152 "packetcache-size",
153 "query-cache-hit",
154 "query-cache-miss",
155 "recursing-answers",
156 "recursing-questions",
157 "tcp-answers",
158 "tcp-queries",
159 "udp-answers",
160 "udp-queries",
161 }; /* }}} */
162 static int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields);
164 static statname_lookup_t lookup_table[] = /* {{{ */
165 {
166 /*********************
167 * Server statistics *
168 *********************/
169 /* Questions */
170 {"recursing-questions", "dns_question", "recurse"},
171 {"tcp-queries", "dns_question", "tcp"},
172 {"udp-queries", "dns_question", "udp"},
173 {"rd-queries", "dns_question", "rd"},
175 /* Answers */
176 {"recursing-answers", "dns_answer", "recurse"},
177 {"tcp-answers", "dns_answer", "tcp"},
178 {"udp-answers", "dns_answer", "udp"},
179 {"recursion-unanswered", "dns_answer", "recursion-unanswered"},
180 {"udp-answers-bytes", "total_bytes", "udp-answers-bytes"},
182 /* Cache stuff */
183 {"cache-bytes", "cache_size", "cache-bytes"},
184 {"packetcache-bytes", "cache_size", "packet-bytes"},
185 {"packetcache-entries", "cache_size", "packet-entries"},
186 {"packetcache-hit", "cache_result", "packet-hit"},
187 {"packetcache-hits", "cache_result", "packet-hit"},
188 {"packetcache-miss", "cache_result", "packet-miss"},
189 {"packetcache-misses", "cache_result", "packet-miss"},
190 {"packetcache-size", "cache_size", "packet"},
191 {"key-cache-size", "cache_size", "key"},
192 {"meta-cache-size", "cache_size", "meta"},
193 {"signature-cache-size", "cache_size", "signature"},
194 {"query-cache-hit", "cache_result", "query-hit"},
195 {"query-cache-miss", "cache_result", "query-miss"},
197 /* Latency */
198 {"latency", "latency", NULL},
200 /* DNS updates */
201 {"dnsupdate-answers", "dns_answer", "dnsupdate-answer"},
202 {"dnsupdate-changes", "dns_question", "dnsupdate-changes"},
203 {"dnsupdate-queries", "dns_question", "dnsupdate-queries"},
204 {"dnsupdate-refused", "dns_answer", "dnsupdate-refused"},
206 /* Other stuff.. */
207 {"corrupt-packets", "ipt_packets", "corrupt"},
208 {"deferred-cache-inserts", "counter", "cache-deferred_insert"},
209 {"deferred-cache-lookup", "counter", "cache-deferred_lookup"},
210 {"dont-outqueries", "dns_question", "dont-outqueries"},
211 {"qsize-a", "cache_size", "answers"},
212 {"qsize-q", "cache_size", "questions"},
213 {"servfail-packets", "ipt_packets", "servfail"},
214 {"timedout-packets", "ipt_packets", "timeout"},
215 {"udp4-answers", "dns_answer", "udp4"},
216 {"udp4-queries", "dns_question", "queries-udp4"},
217 {"udp6-answers", "dns_answer", "udp6"},
218 {"udp6-queries", "dns_question", "queries-udp6"},
219 {"security-status", "dns_question", "security-status"},
220 {"udp-do-queries", "dns_question", "udp-do_queries"},
221 {"signatures", "counter", "signatures"},
223 /***********************
224 * Recursor statistics *
225 ***********************/
226 /* Answers by return code */
227 {"noerror-answers", "dns_rcode", "NOERROR"},
228 {"nxdomain-answers", "dns_rcode", "NXDOMAIN"},
229 {"servfail-answers", "dns_rcode", "SERVFAIL"},
231 /* CPU utilization */
232 {"sys-msec", "cpu", "system"},
233 {"user-msec", "cpu", "user"},
235 /* Question-to-answer latency */
236 {"qa-latency", "latency", NULL},
238 /* Cache */
239 {"cache-entries", "cache_size", NULL},
240 {"cache-hits", "cache_result", "hit"},
241 {"cache-misses", "cache_result", "miss"},
243 /* Total number of questions.. */
244 {"questions", "dns_qtype", "total"},
246 /* All the other stuff.. */
247 {"all-outqueries", "dns_question", "outgoing"},
248 {"answers0-1", "dns_answer", "0_1"},
249 {"answers1-10", "dns_answer", "1_10"},
250 {"answers10-100", "dns_answer", "10_100"},
251 {"answers100-1000", "dns_answer", "100_1000"},
252 {"answers-slow", "dns_answer", "slow"},
253 {"case-mismatches", "counter", "case_mismatches"},
254 {"chain-resends", "dns_question", "chained"},
255 {"client-parse-errors", "counter", "drops-client_parse_error"},
256 {"concurrent-queries", "dns_question", "concurrent"},
257 {"dlg-only-drops", "counter", "drops-delegation_only"},
258 {"edns-ping-matches", "counter", "edns-ping_matches"},
259 {"edns-ping-mismatches", "counter", "edns-ping_mismatches"},
260 {"failed-host-entries", "counter", "entries-failed_host"},
261 {"ipv6-outqueries", "dns_question", "outgoing-ipv6"},
262 {"ipv6-questions", "dns_question", "incoming-ipv6"},
263 {"malloc-bytes", "gauge", "malloc_bytes"},
264 {"max-mthread-stack", "gauge", "max_mthread_stack"},
265 {"no-packet-error", "gauge", "no_packet_error"},
266 {"noedns-outqueries", "dns_question", "outgoing-noedns"},
267 {"noping-outqueries", "dns_question", "outgoing-noping"},
268 {"over-capacity-drops", "dns_question", "incoming-over_capacity"},
269 {"negcache-entries", "cache_size", "negative"},
270 {"nsspeeds-entries", "gauge", "entries-ns_speeds"},
271 {"nsset-invalidations", "counter", "ns_set_invalidation"},
272 {"outgoing-timeouts", "counter", "drops-timeout_outgoing"},
273 {"policy-drops", "counter", "drops-policy"},
274 {"resource-limits", "counter", "drops-resource_limit"},
275 {"server-parse-errors", "counter", "drops-server_parse_error"},
276 {"spoof-prevents", "counter", "drops-spoofed"},
277 {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"},
278 {"tcp-clients", "gauge", "clients-tcp"},
279 {"tcp-outqueries", "dns_question", "outgoing-tcp"},
280 {"tcp-questions", "dns_question", "incoming-tcp"},
281 {"throttled-out", "dns_question", "outgoing-throttled"},
282 {"throttle-entries", "gauge", "entries-throttle"},
283 {"throttled-outqueries", "dns_question", "outgoing-throttle"},
284 {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"},
285 {"unauthorized-udp", "counter", "denied-unauthorized_udp"},
286 {"unexpected-packets", "dns_answer", "unexpected"},
287 {"uptime", "uptime", NULL}
288 }; /* }}} */
289 static int lookup_table_length = STATIC_ARRAY_SIZE (lookup_table);
291 static llist_t *list = NULL;
293 #define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-powerdns"
294 static char *local_sockpath = NULL;
296 /* TODO: Do this before 4.4:
297 * - Update the collectd.conf(5) manpage.
298 *
299 * -octo
300 */
302 /* <https://doc.powerdns.com/md/recursor/stats/> */
303 static void submit (const char *plugin_instance, /* {{{ */
304 const char *pdns_type, const char *value)
305 {
306 value_list_t vl = VALUE_LIST_INIT;
307 value_t values[1];
309 const char *type = NULL;
310 const char *type_instance = NULL;
311 const data_set_t *ds;
313 int i;
315 for (i = 0; i < lookup_table_length; i++)
316 if (strcmp (lookup_table[i].name, pdns_type) == 0)
317 break;
319 if (i >= lookup_table_length)
320 {
321 INFO ("powerdns plugin: submit: Not found in lookup table: %s = %s;",
322 pdns_type, value);
323 return;
324 }
326 if (lookup_table[i].type == NULL)
327 return;
329 type = lookup_table[i].type;
330 type_instance = lookup_table[i].type_instance;
332 ds = plugin_get_ds (type);
333 if (ds == NULL)
334 {
335 ERROR ("powerdns plugin: The lookup table returned type `%s', "
336 "but I cannot find it via `plugin_get_ds'.",
337 type);
338 return;
339 }
341 if (ds->ds_num != 1)
342 {
343 ERROR ("powerdns plugin: type `%s' has %zu data sources, "
344 "but I can only handle one.",
345 type, ds->ds_num);
346 return;
347 }
349 if (0 != parse_value (value, &values[0], ds->ds[0].type))
350 {
351 ERROR ("powerdns plugin: Cannot convert `%s' "
352 "to a number.", value);
353 return;
354 }
356 vl.values = values;
357 vl.values_len = 1;
358 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
359 sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
360 sstrncpy (vl.type, type, sizeof (vl.type));
361 if (type_instance != NULL)
362 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
363 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
365 plugin_dispatch_values (&vl);
366 } /* }}} static void submit */
368 static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
369 char **ret_buffer,
370 size_t *ret_buffer_size)
371 {
372 int sd;
373 int status;
375 char temp[4096];
376 char *buffer = NULL;
377 size_t buffer_size = 0;
379 struct sockaddr_un sa_unix = { 0 };
381 struct timeval stv_timeout;
382 cdtime_t cdt_timeout;
384 sd = socket (PF_UNIX, item->socktype, 0);
385 if (sd < 0)
386 {
387 FUNC_ERROR ("socket");
388 return (-1);
389 }
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))
398 {
399 FUNC_ERROR ("unlink");
400 close (sd);
401 return (-1);
402 }
404 do /* while (0) */
405 {
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));
409 if (status != 0)
410 {
411 FUNC_ERROR ("bind");
412 break;
413 }
415 /* Make the socket writeable by the daemon.. */
416 status = chmod (sa_unix.sun_path, 0666);
417 if (status != 0)
418 {
419 FUNC_ERROR ("chmod");
420 break;
421 }
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));
430 if (status != 0)
431 {
432 FUNC_ERROR ("setsockopt");
433 break;
434 }
436 status = connect (sd, (struct sockaddr *) &item->sockaddr,
437 sizeof (item->sockaddr));
438 if (status != 0)
439 {
440 FUNC_ERROR ("connect");
441 break;
442 }
444 status = send (sd, item->command, strlen (item->command), 0);
445 if (status < 0)
446 {
447 FUNC_ERROR ("send");
448 break;
449 }
451 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
452 if (status < 0)
453 {
454 FUNC_ERROR ("recv");
455 break;
456 }
457 buffer_size = status + 1;
458 status = 0;
459 } while (0);
461 close (sd);
462 unlink (sa_unix.sun_path);
464 if (status != 0)
465 return (-1);
467 assert (buffer_size > 0);
468 buffer = malloc (buffer_size);
469 if (buffer == NULL)
470 {
471 FUNC_ERROR ("malloc");
472 return (-1);
473 }
475 memcpy (buffer, temp, buffer_size - 1);
476 buffer[buffer_size - 1] = 0;
478 *ret_buffer = buffer;
479 *ret_buffer_size = buffer_size;
481 return (0);
482 } /* }}} int powerdns_get_data_dgram */
484 static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
485 char **ret_buffer,
486 size_t *ret_buffer_size)
487 {
488 int sd;
489 int status;
491 char temp[4096];
492 char *buffer = NULL;
493 size_t buffer_size = 0;
495 sd = socket (PF_UNIX, item->socktype, 0);
496 if (sd < 0)
497 {
498 FUNC_ERROR ("socket");
499 return (-1);
500 }
502 struct timeval timeout;
503 timeout.tv_sec=5;
504 timeout.tv_usec=0;
505 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
506 if (status != 0)
507 {
508 FUNC_ERROR ("setsockopt");
509 close (sd);
510 return (-1);
511 }
513 status = connect (sd, (struct sockaddr *) &item->sockaddr,
514 sizeof (item->sockaddr));
515 if (status != 0)
516 {
517 FUNC_ERROR ("connect");
518 close (sd);
519 return (-1);
520 }
522 /* strlen + 1, because we need to send the terminating NULL byte, too. */
523 status = send (sd, item->command, strlen (item->command) + 1,
524 /* flags = */ 0);
525 if (status < 0)
526 {
527 FUNC_ERROR ("send");
528 close (sd);
529 return (-1);
530 }
532 while (42)
533 {
534 char *buffer_new;
536 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
537 if (status < 0)
538 {
539 FUNC_ERROR ("recv");
540 break;
541 }
542 else if (status == 0)
543 break;
545 buffer_new = realloc (buffer, buffer_size + status + 1);
546 if (buffer_new == NULL)
547 {
548 FUNC_ERROR ("realloc");
549 status = -1;
550 break;
551 }
552 buffer = buffer_new;
554 memcpy (buffer + buffer_size, temp, status);
555 buffer_size += status;
556 buffer[buffer_size] = 0;
557 } /* while (42) */
558 close (sd);
560 if (status < 0)
561 {
562 sfree (buffer);
563 }
564 else
565 {
566 assert (status == 0);
567 *ret_buffer = buffer;
568 *ret_buffer_size = buffer_size;
569 }
571 return (status);
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)
576 {
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));
581 else
582 {
583 ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
584 return (-1);
585 }
586 } /* int powerdns_get_data */
588 static int powerdns_read_server (list_item_t *item) /* {{{ */
589 {
590 char *buffer = NULL;
591 size_t buffer_size = 0;
592 int status;
594 char *dummy;
595 char *saveptr;
597 char *key;
598 char *value;
600 const char* const *fields;
601 int fields_num;
603 if (item->command == NULL)
604 item->command = strdup (SERVER_COMMAND);
605 if (item->command == NULL)
606 {
607 ERROR ("powerdns plugin: strdup failed.");
608 return (-1);
609 }
611 status = powerdns_get_data (item, &buffer, &buffer_size);
612 if (status != 0)
613 return (-1);
615 if (item->fields_num != 0)
616 {
617 fields = (const char* const *) item->fields;
618 fields_num = item->fields_num;
619 }
620 else
621 {
622 fields = default_server_fields;
623 fields_num = default_server_fields_num;
624 }
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, */
630 dummy = buffer;
631 saveptr = NULL;
632 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
633 {
634 dummy = NULL;
636 value = strchr (key, '=');
637 if (value == NULL)
638 break;
640 *value = '\0';
641 value++;
643 if (value[0] == '\0')
644 continue;
646 /* Check if this item was requested. */
647 int i;
648 for (i = 0; i < fields_num; i++)
649 if (strcasecmp (key, fields[i]) == 0)
650 break;
651 if (i >= fields_num)
652 continue;
654 submit (item->instance, key, value);
655 } /* while (strtok_r) */
657 sfree (buffer);
659 return (0);
660 } /* }}} int powerdns_read_server */
662 /*
663 * powerdns_update_recursor_command
664 *
665 * Creates a string that holds the command to be sent to the recursor. This
666 * string is stores in the `command' member of the `list_item_t' passed to the
667 * function. This function is called by `powerdns_read_recursor'.
668 */
669 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
670 {
671 char buffer[4096];
672 int status;
674 if (li == NULL)
675 return (0);
677 if (li->fields_num < 1)
678 {
679 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
680 }
681 else
682 {
683 sstrncpy (buffer, "get ", sizeof (buffer));
684 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
685 li->fields, li->fields_num,
686 /* seperator = */ " ");
687 if (status < 0)
688 {
689 ERROR ("powerdns plugin: strjoin failed.");
690 return (-1);
691 }
692 buffer[sizeof (buffer) - 1] = 0;
693 size_t len = strlen (buffer);
694 if (len < sizeof (buffer) - 2)
695 {
696 buffer[len++] = ' ';
697 buffer[len++] = '\n';
698 buffer[len++] = '\0';
699 }
700 }
702 buffer[sizeof (buffer) - 1] = 0;
703 li->command = strdup (buffer);
704 if (li->command == NULL)
705 {
706 ERROR ("powerdns plugin: strdup failed.");
707 return (-1);
708 }
710 return (0);
711 } /* }}} int powerdns_update_recursor_command */
713 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
714 {
715 char *buffer = NULL;
716 size_t buffer_size = 0;
717 int status;
719 char *dummy;
721 char *keys_list;
722 char *key;
723 char *key_saveptr;
724 char *value;
725 char *value_saveptr;
727 if (item->command == NULL)
728 {
729 status = powerdns_update_recursor_command (item);
730 if (status != 0)
731 {
732 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
733 return (-1);
734 }
736 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
737 item->command);
738 }
739 assert (item->command != NULL);
741 status = powerdns_get_data (item, &buffer, &buffer_size);
742 if (status != 0)
743 {
744 ERROR ("powerdns plugin: powerdns_get_data failed.");
745 return (-1);
746 }
748 keys_list = strdup (item->command);
749 if (keys_list == NULL)
750 {
751 FUNC_ERROR ("strdup");
752 sfree (buffer);
753 return (-1);
754 }
756 key_saveptr = NULL;
757 value_saveptr = NULL;
759 /* Skip the `get' at the beginning */
760 strtok_r (keys_list, " \t", &key_saveptr);
762 dummy = buffer;
763 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
764 {
765 dummy = NULL;
767 key = strtok_r (NULL, " \t", &key_saveptr);
768 if (key == NULL)
769 break;
771 submit (item->instance, key, value);
772 } /* while (strtok_r) */
774 sfree (buffer);
775 sfree (keys_list);
777 return (0);
778 } /* }}} int powerdns_read_recursor */
780 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
781 oconfig_item_t *ci)
782 {
783 char **temp;
785 if (ci->values_num < 1)
786 {
787 WARNING ("powerdns plugin: The `Collect' option needs "
788 "at least one argument.");
789 return (-1);
790 }
792 for (int i = 0; i < ci->values_num; i++)
793 if (ci->values[i].type != OCONFIG_TYPE_STRING)
794 {
795 WARNING ("powerdns plugin: Only string arguments are allowed to "
796 "the `Collect' option.");
797 return (-1);
798 }
800 temp = realloc (li->fields,
801 sizeof (char *) * (li->fields_num + ci->values_num));
802 if (temp == NULL)
803 {
804 WARNING ("powerdns plugin: realloc failed.");
805 return (-1);
806 }
807 li->fields = temp;
809 for (int i = 0; i < ci->values_num; i++)
810 {
811 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
812 if (li->fields[li->fields_num] == NULL)
813 {
814 WARNING ("powerdns plugin: strdup failed.");
815 continue;
816 }
817 li->fields_num++;
818 }
820 /* Invalidate a previously computed command */
821 sfree (li->command);
823 return (0);
824 } /* }}} int powerdns_config_add_collect */
826 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
827 {
828 char *socket_temp;
830 list_item_t *item;
831 int status;
833 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
834 {
835 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
836 ci->key);
837 return (-1);
838 }
840 item = calloc (1, sizeof (*item));
841 if (item == NULL)
842 {
843 ERROR ("powerdns plugin: calloc failed.");
844 return (-1);
845 }
847 item->instance = strdup (ci->values[0].value.string);
848 if (item->instance == NULL)
849 {
850 ERROR ("powerdns plugin: strdup failed.");
851 sfree (item);
852 return (-1);
853 }
855 /*
856 * Set default values for the members of list_item_t
857 */
858 if (strcasecmp ("Server", ci->key) == 0)
859 {
860 item->server_type = SRV_AUTHORITATIVE;
861 item->func = powerdns_read_server;
862 item->socktype = SOCK_STREAM;
863 socket_temp = strdup (SERVER_SOCKET);
864 }
865 else if (strcasecmp ("Recursor", ci->key) == 0)
866 {
867 item->server_type = SRV_RECURSOR;
868 item->func = powerdns_read_recursor;
869 item->socktype = SOCK_DGRAM;
870 socket_temp = strdup (RECURSOR_SOCKET);
871 }
872 else
873 {
874 /* We must never get here.. */
875 assert (0);
876 return (-1);
877 }
879 status = 0;
880 for (int i = 0; i < ci->children_num; i++)
881 {
882 oconfig_item_t *option = ci->children + i;
884 if (strcasecmp ("Collect", option->key) == 0)
885 status = powerdns_config_add_collect (item, option);
886 else if (strcasecmp ("Socket", option->key) == 0)
887 status = cf_util_get_string (option, &socket_temp);
888 else
889 {
890 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
891 status = -1;
892 }
894 if (status != 0)
895 break;
896 }
898 while (status == 0)
899 {
900 llentry_t *e;
902 if (socket_temp == NULL)
903 {
904 ERROR ("powerdns plugin: socket_temp == NULL.");
905 status = -1;
906 break;
907 }
909 item->sockaddr.sun_family = AF_UNIX;
910 sstrncpy (item->sockaddr.sun_path, socket_temp,
911 sizeof (item->sockaddr.sun_path));
913 e = llentry_create (item->instance, item);
914 if (e == NULL)
915 {
916 ERROR ("powerdns plugin: llentry_create failed.");
917 status = -1;
918 break;
919 }
920 llist_append (list, e);
922 break;
923 }
925 if (status != 0)
926 {
927 sfree (socket_temp);
928 sfree (item);
929 return (-1);
930 }
932 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
934 sfree (socket_temp);
935 return (0);
936 } /* }}} int powerdns_config_add_server */
938 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
939 {
940 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
942 if (list == NULL)
943 {
944 list = llist_create ();
946 if (list == NULL)
947 {
948 ERROR ("powerdns plugin: `llist_create' failed.");
949 return (-1);
950 }
951 }
953 for (int i = 0; i < ci->children_num; i++)
954 {
955 oconfig_item_t *option = ci->children + i;
957 if ((strcasecmp ("Server", option->key) == 0)
958 || (strcasecmp ("Recursor", option->key) == 0))
959 powerdns_config_add_server (option);
960 else if (strcasecmp ("LocalSocket", option->key) == 0)
961 {
962 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
963 {
964 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
965 }
966 else
967 {
968 char *temp = strdup (option->values[0].value.string);
969 if (temp == NULL)
970 return (1);
971 sfree (local_sockpath);
972 local_sockpath = temp;
973 }
974 }
975 else
976 {
977 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
978 }
979 } /* for (i = 0; i < ci->children_num; i++) */
981 return (0);
982 } /* }}} int powerdns_config */
984 static int powerdns_read (void)
985 {
986 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
987 {
988 list_item_t *item = e->value;
989 item->func (item);
990 }
992 return (0);
993 } /* static int powerdns_read */
995 static int powerdns_shutdown (void)
996 {
997 if (list == NULL)
998 return (0);
1000 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
1001 {
1002 list_item_t *item = (list_item_t *) e->value;
1003 e->value = NULL;
1005 sfree (item->instance);
1006 sfree (item->command);
1007 sfree (item);
1008 }
1010 llist_destroy (list);
1011 list = NULL;
1013 return (0);
1014 } /* static int powerdns_shutdown */
1016 void module_register (void)
1017 {
1018 plugin_register_complex_config ("powerdns", powerdns_config);
1019 plugin_register_read ("powerdns", powerdns_read);
1020 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1021 } /* void module_register */
1023 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */