5bbd9d53e915c5f64868e2c3ce77b6b1f929c0aa
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 "utils_llist.h"
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/un.h>
42 #ifndef UNIX_PATH_MAX
43 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
44 #endif
45 #define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0)
46 #define SOCK_ERROR(func, sockpath) do { char errbuf[1024]; ERROR ("powerdns plugin: Socket `%s` %s failed: %s", sockpath, 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_str)
305 {
306 value_list_t vl = VALUE_LIST_INIT;
307 value_t value;
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_str);
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_str, &value, ds->ds[0].type))
350 {
351 ERROR ("powerdns plugin: Cannot convert `%s' "
352 "to a number.", value_str);
353 return;
354 }
356 vl.values = &value;
357 vl.values_len = 1;
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, /* {{{ */
368 char **ret_buffer,
369 size_t *ret_buffer_size)
370 {
371 int sd;
372 int status;
374 char temp[4096];
375 char *buffer = NULL;
376 size_t buffer_size = 0;
378 struct sockaddr_un sa_unix = { 0 };
380 cdtime_t cdt_timeout;
382 sd = socket (PF_UNIX, item->socktype, 0);
383 if (sd < 0)
384 {
385 FUNC_ERROR ("socket");
386 return (-1);
387 }
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))
396 {
397 SOCK_ERROR ("unlink", sa_unix.sun_path);
398 close (sd);
399 return (-1);
400 }
402 do /* while (0) */
403 {
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));
407 if (status != 0)
408 {
409 SOCK_ERROR ("bind", sa_unix.sun_path);
410 break;
411 }
413 /* Make the socket writeable by the daemon.. */
414 status = chmod (sa_unix.sun_path, 0666);
415 if (status != 0)
416 {
417 SOCK_ERROR ("chmod", sa_unix.sun_path);
418 break;
419 }
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 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO,
426 &CDTIME_T_TO_TIMEVAL(cdt_timeout),
427 sizeof(struct timeval));
428 if (status != 0)
429 {
430 SOCK_ERROR ("setsockopt", sa_unix.sun_path);
431 break;
432 }
434 status = connect (sd, (struct sockaddr *) &item->sockaddr,
435 sizeof (item->sockaddr));
436 if (status != 0)
437 {
438 SOCK_ERROR ("connect", sa_unix.sun_path);
439 break;
440 }
442 status = send (sd, item->command, strlen (item->command), 0);
443 if (status < 0)
444 {
445 SOCK_ERROR ("send", sa_unix.sun_path);
446 break;
447 }
449 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
450 if (status < 0)
451 {
452 SOCK_ERROR ("recv", sa_unix.sun_path);
453 break;
454 }
455 buffer_size = status + 1;
456 status = 0;
457 } while (0);
459 close (sd);
460 unlink (sa_unix.sun_path);
462 if (status != 0)
463 return (-1);
465 assert (buffer_size > 0);
466 buffer = malloc (buffer_size);
467 if (buffer == NULL)
468 {
469 FUNC_ERROR ("malloc");
470 return (-1);
471 }
473 memcpy (buffer, temp, buffer_size - 1);
474 buffer[buffer_size - 1] = 0;
476 *ret_buffer = buffer;
477 *ret_buffer_size = buffer_size;
479 return (0);
480 } /* }}} int powerdns_get_data_dgram */
482 static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
483 char **ret_buffer,
484 size_t *ret_buffer_size)
485 {
486 int sd;
487 int status;
489 char temp[4096];
490 char *buffer = NULL;
491 size_t buffer_size = 0;
493 sd = socket (PF_UNIX, item->socktype, 0);
494 if (sd < 0)
495 {
496 FUNC_ERROR ("socket");
497 return (-1);
498 }
500 struct timeval timeout;
501 timeout.tv_sec=5;
502 timeout.tv_usec=0;
503 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
504 if (status != 0)
505 {
506 FUNC_ERROR ("setsockopt");
507 close (sd);
508 return (-1);
509 }
511 status = connect (sd, (struct sockaddr *) &item->sockaddr,
512 sizeof (item->sockaddr));
513 if (status != 0)
514 {
515 SOCK_ERROR ("connect", item->sockaddr.sun_path);
516 close (sd);
517 return (-1);
518 }
520 /* strlen + 1, because we need to send the terminating NULL byte, too. */
521 status = send (sd, item->command, strlen (item->command) + 1,
522 /* flags = */ 0);
523 if (status < 0)
524 {
525 SOCK_ERROR ("send", item->sockaddr.sun_path);
526 close (sd);
527 return (-1);
528 }
530 while (42)
531 {
532 char *buffer_new;
534 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
535 if (status < 0)
536 {
537 SOCK_ERROR ("recv", item->sockaddr.sun_path);
538 break;
539 }
540 else if (status == 0)
541 break;
543 buffer_new = realloc (buffer, buffer_size + status + 1);
544 if (buffer_new == NULL)
545 {
546 FUNC_ERROR ("realloc");
547 status = -1;
548 break;
549 }
550 buffer = buffer_new;
552 memcpy (buffer + buffer_size, temp, status);
553 buffer_size += status;
554 buffer[buffer_size] = 0;
555 } /* while (42) */
556 close (sd);
558 if (status < 0)
559 {
560 sfree (buffer);
561 }
562 else
563 {
564 assert (status == 0);
565 *ret_buffer = buffer;
566 *ret_buffer_size = buffer_size;
567 }
569 return (status);
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)
574 {
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));
579 else
580 {
581 ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
582 return (-1);
583 }
584 } /* int powerdns_get_data */
586 static int powerdns_read_server (list_item_t *item) /* {{{ */
587 {
588 char *buffer = NULL;
589 size_t buffer_size = 0;
590 int status;
592 char *dummy;
593 char *saveptr;
595 char *key;
596 char *value;
598 const char* const *fields;
599 int fields_num;
601 if (item->command == NULL)
602 item->command = strdup (SERVER_COMMAND);
603 if (item->command == NULL)
604 {
605 ERROR ("powerdns plugin: strdup failed.");
606 return (-1);
607 }
609 status = powerdns_get_data (item, &buffer, &buffer_size);
610 if (status != 0)
611 return (-1);
613 if (item->fields_num != 0)
614 {
615 fields = (const char* const *) item->fields;
616 fields_num = item->fields_num;
617 }
618 else
619 {
620 fields = default_server_fields;
621 fields_num = default_server_fields_num;
622 }
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, */
628 dummy = buffer;
629 saveptr = NULL;
630 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
631 {
632 dummy = NULL;
634 value = strchr (key, '=');
635 if (value == NULL)
636 break;
638 *value = '\0';
639 value++;
641 if (value[0] == '\0')
642 continue;
644 /* Check if this item was requested. */
645 int i;
646 for (i = 0; i < fields_num; i++)
647 if (strcasecmp (key, fields[i]) == 0)
648 break;
649 if (i >= fields_num)
650 continue;
652 submit (item->instance, key, value);
653 } /* while (strtok_r) */
655 sfree (buffer);
657 return (0);
658 } /* }}} int powerdns_read_server */
660 /*
661 * powerdns_update_recursor_command
662 *
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'.
666 */
667 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
668 {
669 char buffer[4096];
670 int status;
672 if (li == NULL)
673 return (0);
675 if (li->fields_num < 1)
676 {
677 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
678 }
679 else
680 {
681 sstrncpy (buffer, "get ", sizeof (buffer));
682 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
683 li->fields, li->fields_num,
684 /* seperator = */ " ");
685 if (status < 0)
686 {
687 ERROR ("powerdns plugin: strjoin failed.");
688 return (-1);
689 }
690 buffer[sizeof (buffer) - 1] = 0;
691 size_t len = strlen (buffer);
692 if (len < sizeof (buffer) - 2)
693 {
694 buffer[len++] = ' ';
695 buffer[len++] = '\n';
696 buffer[len++] = '\0';
697 }
698 }
700 buffer[sizeof (buffer) - 1] = 0;
701 li->command = strdup (buffer);
702 if (li->command == NULL)
703 {
704 ERROR ("powerdns plugin: strdup failed.");
705 return (-1);
706 }
708 return (0);
709 } /* }}} int powerdns_update_recursor_command */
711 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
712 {
713 char *buffer = NULL;
714 size_t buffer_size = 0;
715 int status;
717 char *dummy;
719 char *keys_list;
720 char *key;
721 char *key_saveptr;
722 char *value;
723 char *value_saveptr;
725 if (item->command == NULL)
726 {
727 status = powerdns_update_recursor_command (item);
728 if (status != 0)
729 {
730 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
731 return (-1);
732 }
734 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
735 item->command);
736 }
737 assert (item->command != NULL);
739 status = powerdns_get_data (item, &buffer, &buffer_size);
740 if (status != 0)
741 {
742 ERROR ("powerdns plugin: powerdns_get_data failed.");
743 return (-1);
744 }
746 keys_list = strdup (item->command);
747 if (keys_list == NULL)
748 {
749 FUNC_ERROR ("strdup");
750 sfree (buffer);
751 return (-1);
752 }
754 key_saveptr = NULL;
755 value_saveptr = NULL;
757 /* Skip the `get' at the beginning */
758 strtok_r (keys_list, " \t", &key_saveptr);
760 dummy = buffer;
761 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
762 {
763 dummy = NULL;
765 key = strtok_r (NULL, " \t", &key_saveptr);
766 if (key == NULL)
767 break;
769 submit (item->instance, key, value);
770 } /* while (strtok_r) */
772 sfree (buffer);
773 sfree (keys_list);
775 return (0);
776 } /* }}} int powerdns_read_recursor */
778 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
779 oconfig_item_t *ci)
780 {
781 char **temp;
783 if (ci->values_num < 1)
784 {
785 WARNING ("powerdns plugin: The `Collect' option needs "
786 "at least one argument.");
787 return (-1);
788 }
790 for (int i = 0; i < ci->values_num; i++)
791 if (ci->values[i].type != OCONFIG_TYPE_STRING)
792 {
793 WARNING ("powerdns plugin: Only string arguments are allowed to "
794 "the `Collect' option.");
795 return (-1);
796 }
798 temp = realloc (li->fields,
799 sizeof (char *) * (li->fields_num + ci->values_num));
800 if (temp == NULL)
801 {
802 WARNING ("powerdns plugin: realloc failed.");
803 return (-1);
804 }
805 li->fields = temp;
807 for (int i = 0; i < ci->values_num; i++)
808 {
809 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
810 if (li->fields[li->fields_num] == NULL)
811 {
812 WARNING ("powerdns plugin: strdup failed.");
813 continue;
814 }
815 li->fields_num++;
816 }
818 /* Invalidate a previously computed command */
819 sfree (li->command);
821 return (0);
822 } /* }}} int powerdns_config_add_collect */
824 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
825 {
826 char *socket_temp;
828 list_item_t *item;
829 int status;
831 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
832 {
833 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
834 ci->key);
835 return (-1);
836 }
838 item = calloc (1, sizeof (*item));
839 if (item == NULL)
840 {
841 ERROR ("powerdns plugin: calloc failed.");
842 return (-1);
843 }
845 item->instance = strdup (ci->values[0].value.string);
846 if (item->instance == NULL)
847 {
848 ERROR ("powerdns plugin: strdup failed.");
849 sfree (item);
850 return (-1);
851 }
853 /*
854 * Set default values for the members of list_item_t
855 */
856 if (strcasecmp ("Server", ci->key) == 0)
857 {
858 item->server_type = SRV_AUTHORITATIVE;
859 item->func = powerdns_read_server;
860 item->socktype = SOCK_STREAM;
861 socket_temp = strdup (SERVER_SOCKET);
862 }
863 else if (strcasecmp ("Recursor", ci->key) == 0)
864 {
865 item->server_type = SRV_RECURSOR;
866 item->func = powerdns_read_recursor;
867 item->socktype = SOCK_DGRAM;
868 socket_temp = strdup (RECURSOR_SOCKET);
869 }
870 else
871 {
872 /* We must never get here.. */
873 assert (0);
874 return (-1);
875 }
877 status = 0;
878 for (int i = 0; i < ci->children_num; i++)
879 {
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);
886 else
887 {
888 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
889 status = -1;
890 }
892 if (status != 0)
893 break;
894 }
896 while (status == 0)
897 {
898 llentry_t *e;
900 if (socket_temp == NULL)
901 {
902 ERROR ("powerdns plugin: socket_temp == NULL.");
903 status = -1;
904 break;
905 }
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);
912 if (e == NULL)
913 {
914 ERROR ("powerdns plugin: llentry_create failed.");
915 status = -1;
916 break;
917 }
918 llist_append (list, e);
920 break;
921 }
923 if (status != 0)
924 {
925 sfree (socket_temp);
926 sfree (item);
927 return (-1);
928 }
930 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
932 sfree (socket_temp);
933 return (0);
934 } /* }}} int powerdns_config_add_server */
936 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
937 {
938 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
940 if (list == NULL)
941 {
942 list = llist_create ();
944 if (list == NULL)
945 {
946 ERROR ("powerdns plugin: `llist_create' failed.");
947 return (-1);
948 }
949 }
951 for (int i = 0; i < ci->children_num; i++)
952 {
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)
959 {
960 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
961 {
962 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
963 }
964 else
965 {
966 char *temp = strdup (option->values[0].value.string);
967 if (temp == NULL)
968 return (1);
969 sfree (local_sockpath);
970 local_sockpath = temp;
971 }
972 }
973 else
974 {
975 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
976 }
977 } /* for (i = 0; i < ci->children_num; i++) */
979 return (0);
980 } /* }}} int powerdns_config */
982 static int powerdns_read (void)
983 {
984 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
985 {
986 list_item_t *item = e->value;
987 item->func (item);
988 }
990 return (0);
991 } /* static int powerdns_read */
993 static int powerdns_shutdown (void)
994 {
995 if (list == NULL)
996 return (0);
998 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
999 {
1000 list_item_t *item = (list_item_t *) e->value;
1001 e->value = NULL;
1003 sfree (item->instance);
1004 sfree (item->command);
1005 sfree (item);
1006 }
1008 llist_destroy (list);
1009 list = NULL;
1011 return (0);
1012 } /* static int powerdns_shutdown */
1014 void module_register (void)
1015 {
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 : */