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"
28 #include "common.h"
29 #include "plugin.h"
30 #include "configfile.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)
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"
55 struct list_item_s;
56 typedef struct list_item_s list_item_t;
58 struct list_item_s
59 {
60 enum
61 {
62 SRV_AUTHORITATIVE,
63 SRV_RECURSOR
64 } server_type;
65 int (*func) (list_item_t *item);
66 char *instance;
68 char **fields;
69 int fields_num;
70 char *command;
72 struct sockaddr_un sockaddr;
73 int socktype;
74 };
76 struct statname_lookup_s
77 {
78 const char *name;
79 const char *type;
80 const char *type_instance;
81 };
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
144 }}} */
146 static const char* const default_server_fields[] = /* {{{ */
147 {
148 "latency",
149 "packetcache-hit",
150 "packetcache-miss",
151 "packetcache-size",
152 "query-cache-hit",
153 "query-cache-miss",
154 "recursing-answers",
155 "recursing-questions",
156 "tcp-answers",
157 "tcp-queries",
158 "udp-answers",
159 "udp-queries",
160 }; /* }}} */
161 static int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields);
163 static statname_lookup_t lookup_table[] = /* {{{ */
164 {
165 /*********************
166 * Server statistics *
167 *********************/
168 /* Questions */
169 {"recursing-questions", "dns_question", "recurse"},
170 {"tcp-queries", "dns_question", "tcp"},
171 {"udp-queries", "dns_question", "udp"},
172 {"rd-queries", "dns_question", "rd"},
174 /* Answers */
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"},
181 /* Cache stuff */
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"},
196 /* Latency */
197 {"latency", "latency", NULL},
199 /* DNS updates */
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"},
205 /* Other stuff.. */
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},
237 /* Cache */
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}
287 }; /* }}} */
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.
297 *
298 * -octo
299 */
301 /* <https://doc.powerdns.com/md/recursor/stats/> */
302 static void submit (const char *plugin_instance, /* {{{ */
303 const char *pdns_type, const char *value)
304 {
305 value_list_t vl = VALUE_LIST_INIT;
306 value_t values[1];
308 const char *type = NULL;
309 const char *type_instance = NULL;
310 const data_set_t *ds;
312 int i;
314 for (i = 0; i < lookup_table_length; i++)
315 if (strcmp (lookup_table[i].name, pdns_type) == 0)
316 break;
318 if (i >= lookup_table_length)
319 {
320 INFO ("powerdns plugin: submit: Not found in lookup table: %s = %s;",
321 pdns_type, value);
322 return;
323 }
325 if (lookup_table[i].type == NULL)
326 return;
328 type = lookup_table[i].type;
329 type_instance = lookup_table[i].type_instance;
331 ds = plugin_get_ds (type);
332 if (ds == NULL)
333 {
334 ERROR ("powerdns plugin: The lookup table returned type `%s', "
335 "but I cannot find it via `plugin_get_ds'.",
336 type);
337 return;
338 }
340 if (ds->ds_num != 1)
341 {
342 ERROR ("powerdns plugin: type `%s' has %zu data sources, "
343 "but I can only handle one.",
344 type, ds->ds_num);
345 return;
346 }
348 if (0 != parse_value (value, &values[0], ds->ds[0].type))
349 {
350 ERROR ("powerdns plugin: Cannot convert `%s' "
351 "to a number.", value);
352 return;
353 }
355 vl.values = values;
356 vl.values_len = 1;
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, /* {{{ */
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 struct timeval stv_timeout;
381 cdtime_t cdt_timeout;
383 sd = socket (PF_UNIX, item->socktype, 0);
384 if (sd < 0)
385 {
386 FUNC_ERROR ("socket");
387 return (-1);
388 }
390 sa_unix.sun_family = AF_UNIX;
391 sstrncpy (sa_unix.sun_path,
392 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
393 sizeof (sa_unix.sun_path));
395 status = unlink (sa_unix.sun_path);
396 if ((status != 0) && (errno != ENOENT))
397 {
398 FUNC_ERROR ("unlink");
399 close (sd);
400 return (-1);
401 }
403 do /* while (0) */
404 {
405 /* We need to bind to a specific path, because this is a datagram socket
406 * and otherwise the daemon cannot answer. */
407 status = bind (sd, (struct sockaddr *) &sa_unix, sizeof (sa_unix));
408 if (status != 0)
409 {
410 FUNC_ERROR ("bind");
411 break;
412 }
414 /* Make the socket writeable by the daemon.. */
415 status = chmod (sa_unix.sun_path, 0666);
416 if (status != 0)
417 {
418 FUNC_ERROR ("chmod");
419 break;
420 }
422 cdt_timeout = plugin_get_interval () * 3 / 4;
423 if (cdt_timeout < TIME_T_TO_CDTIME_T (2))
424 cdt_timeout = TIME_T_TO_CDTIME_T (2);
426 CDTIME_T_TO_TIMEVAL (cdt_timeout, &stv_timeout);
428 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout, sizeof (stv_timeout));
429 if (status != 0)
430 {
431 FUNC_ERROR ("setsockopt");
432 break;
433 }
435 status = connect (sd, (struct sockaddr *) &item->sockaddr,
436 sizeof (item->sockaddr));
437 if (status != 0)
438 {
439 FUNC_ERROR ("connect");
440 break;
441 }
443 status = send (sd, item->command, strlen (item->command), 0);
444 if (status < 0)
445 {
446 FUNC_ERROR ("send");
447 break;
448 }
450 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
451 if (status < 0)
452 {
453 FUNC_ERROR ("recv");
454 break;
455 }
456 buffer_size = status + 1;
457 status = 0;
458 } while (0);
460 close (sd);
461 unlink (sa_unix.sun_path);
463 if (status != 0)
464 return (-1);
466 assert (buffer_size > 0);
467 buffer = malloc (buffer_size);
468 if (buffer == NULL)
469 {
470 FUNC_ERROR ("malloc");
471 return (-1);
472 }
474 memcpy (buffer, temp, buffer_size - 1);
475 buffer[buffer_size - 1] = 0;
477 *ret_buffer = buffer;
478 *ret_buffer_size = buffer_size;
480 return (0);
481 } /* }}} int powerdns_get_data_dgram */
483 static int powerdns_get_data_stream (list_item_t *item, /* {{{ */
484 char **ret_buffer,
485 size_t *ret_buffer_size)
486 {
487 int sd;
488 int status;
490 char temp[4096];
491 char *buffer = NULL;
492 size_t buffer_size = 0;
494 sd = socket (PF_UNIX, item->socktype, 0);
495 if (sd < 0)
496 {
497 FUNC_ERROR ("socket");
498 return (-1);
499 }
501 struct timeval timeout;
502 timeout.tv_sec=5;
503 timeout.tv_usec=0;
504 status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
505 if (status != 0)
506 {
507 FUNC_ERROR ("setsockopt");
508 close (sd);
509 return (-1);
510 }
512 status = connect (sd, (struct sockaddr *) &item->sockaddr,
513 sizeof (item->sockaddr));
514 if (status != 0)
515 {
516 FUNC_ERROR ("connect");
517 close (sd);
518 return (-1);
519 }
521 /* strlen + 1, because we need to send the terminating NULL byte, too. */
522 status = send (sd, item->command, strlen (item->command) + 1,
523 /* flags = */ 0);
524 if (status < 0)
525 {
526 FUNC_ERROR ("send");
527 close (sd);
528 return (-1);
529 }
531 while (42)
532 {
533 char *buffer_new;
535 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
536 if (status < 0)
537 {
538 FUNC_ERROR ("recv");
539 break;
540 }
541 else if (status == 0)
542 break;
544 buffer_new = realloc (buffer, buffer_size + status + 1);
545 if (buffer_new == NULL)
546 {
547 FUNC_ERROR ("realloc");
548 status = -1;
549 break;
550 }
551 buffer = buffer_new;
553 memcpy (buffer + buffer_size, temp, status);
554 buffer_size += status;
555 buffer[buffer_size] = 0;
556 } /* while (42) */
557 close (sd);
559 if (status < 0)
560 {
561 sfree (buffer);
562 }
563 else
564 {
565 assert (status == 0);
566 *ret_buffer = buffer;
567 *ret_buffer_size = buffer_size;
568 }
570 return (status);
571 } /* }}} int powerdns_get_data_stream */
573 static int powerdns_get_data (list_item_t *item, char **ret_buffer,
574 size_t *ret_buffer_size)
575 {
576 if (item->socktype == SOCK_DGRAM)
577 return (powerdns_get_data_dgram (item, ret_buffer, ret_buffer_size));
578 else if (item->socktype == SOCK_STREAM)
579 return (powerdns_get_data_stream (item, ret_buffer, ret_buffer_size));
580 else
581 {
582 ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype);
583 return (-1);
584 }
585 } /* int powerdns_get_data */
587 static int powerdns_read_server (list_item_t *item) /* {{{ */
588 {
589 char *buffer = NULL;
590 size_t buffer_size = 0;
591 int status;
593 char *dummy;
594 char *saveptr;
596 char *key;
597 char *value;
599 const char* const *fields;
600 int fields_num;
602 if (item->command == NULL)
603 item->command = strdup (SERVER_COMMAND);
604 if (item->command == NULL)
605 {
606 ERROR ("powerdns plugin: strdup failed.");
607 return (-1);
608 }
610 status = powerdns_get_data (item, &buffer, &buffer_size);
611 if (status != 0)
612 return (-1);
614 if (item->fields_num != 0)
615 {
616 fields = (const char* const *) item->fields;
617 fields_num = item->fields_num;
618 }
619 else
620 {
621 fields = default_server_fields;
622 fields_num = default_server_fields_num;
623 }
625 assert (fields != NULL);
626 assert (fields_num > 0);
628 /* 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, */
629 dummy = buffer;
630 saveptr = NULL;
631 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
632 {
633 int i;
635 dummy = NULL;
637 value = strchr (key, '=');
638 if (value == NULL)
639 break;
641 *value = '\0';
642 value++;
644 if (value[0] == '\0')
645 continue;
647 /* Check if this item was requested. */
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 int i;
784 char **temp;
786 if (ci->values_num < 1)
787 {
788 WARNING ("powerdns plugin: The `Collect' option needs "
789 "at least one argument.");
790 return (-1);
791 }
793 for (i = 0; i < ci->values_num; i++)
794 if (ci->values[i].type != OCONFIG_TYPE_STRING)
795 {
796 WARNING ("powerdns plugin: Only string arguments are allowed to "
797 "the `Collect' option.");
798 return (-1);
799 }
801 temp = realloc (li->fields,
802 sizeof (char *) * (li->fields_num + ci->values_num));
803 if (temp == NULL)
804 {
805 WARNING ("powerdns plugin: realloc failed.");
806 return (-1);
807 }
808 li->fields = temp;
810 for (i = 0; i < ci->values_num; i++)
811 {
812 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
813 if (li->fields[li->fields_num] == NULL)
814 {
815 WARNING ("powerdns plugin: strdup failed.");
816 continue;
817 }
818 li->fields_num++;
819 }
821 /* Invalidate a previously computed command */
822 sfree (li->command);
824 return (0);
825 } /* }}} int powerdns_config_add_collect */
827 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
828 {
829 char *socket_temp;
831 list_item_t *item;
832 int status;
833 int i;
835 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
836 {
837 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
838 ci->key);
839 return (-1);
840 }
842 item = calloc (1, sizeof (*item));
843 if (item == NULL)
844 {
845 ERROR ("powerdns plugin: calloc failed.");
846 return (-1);
847 }
849 item->instance = strdup (ci->values[0].value.string);
850 if (item->instance == NULL)
851 {
852 ERROR ("powerdns plugin: strdup failed.");
853 sfree (item);
854 return (-1);
855 }
857 /*
858 * Set default values for the members of list_item_t
859 */
860 if (strcasecmp ("Server", ci->key) == 0)
861 {
862 item->server_type = SRV_AUTHORITATIVE;
863 item->func = powerdns_read_server;
864 item->socktype = SOCK_STREAM;
865 socket_temp = strdup (SERVER_SOCKET);
866 }
867 else if (strcasecmp ("Recursor", ci->key) == 0)
868 {
869 item->server_type = SRV_RECURSOR;
870 item->func = powerdns_read_recursor;
871 item->socktype = SOCK_DGRAM;
872 socket_temp = strdup (RECURSOR_SOCKET);
873 }
874 else
875 {
876 /* We must never get here.. */
877 assert (0);
878 return (-1);
879 }
881 status = 0;
882 for (i = 0; i < ci->children_num; i++)
883 {
884 oconfig_item_t *option = ci->children + i;
886 if (strcasecmp ("Collect", option->key) == 0)
887 status = powerdns_config_add_collect (item, option);
888 else if (strcasecmp ("Socket", option->key) == 0)
889 status = cf_util_get_string (option, &socket_temp);
890 else
891 {
892 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
893 status = -1;
894 }
896 if (status != 0)
897 break;
898 }
900 while (status == 0)
901 {
902 llentry_t *e;
904 if (socket_temp == NULL)
905 {
906 ERROR ("powerdns plugin: socket_temp == NULL.");
907 status = -1;
908 break;
909 }
911 item->sockaddr.sun_family = AF_UNIX;
912 sstrncpy (item->sockaddr.sun_path, socket_temp,
913 sizeof (item->sockaddr.sun_path));
915 e = llentry_create (item->instance, item);
916 if (e == NULL)
917 {
918 ERROR ("powerdns plugin: llentry_create failed.");
919 status = -1;
920 break;
921 }
922 llist_append (list, e);
924 break;
925 }
927 if (status != 0)
928 {
929 sfree (socket_temp);
930 sfree (item);
931 return (-1);
932 }
934 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
936 sfree (socket_temp);
937 return (0);
938 } /* }}} int powerdns_config_add_server */
940 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
941 {
942 int i;
944 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
946 if (list == NULL)
947 {
948 list = llist_create ();
950 if (list == NULL)
951 {
952 ERROR ("powerdns plugin: `llist_create' failed.");
953 return (-1);
954 }
955 }
957 for (i = 0; i < ci->children_num; i++)
958 {
959 oconfig_item_t *option = ci->children + i;
961 if ((strcasecmp ("Server", option->key) == 0)
962 || (strcasecmp ("Recursor", option->key) == 0))
963 powerdns_config_add_server (option);
964 else if (strcasecmp ("LocalSocket", option->key) == 0)
965 {
966 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
967 {
968 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
969 }
970 else
971 {
972 char *temp = strdup (option->values[0].value.string);
973 if (temp == NULL)
974 return (1);
975 sfree (local_sockpath);
976 local_sockpath = temp;
977 }
978 }
979 else
980 {
981 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
982 }
983 } /* for (i = 0; i < ci->children_num; i++) */
985 return (0);
986 } /* }}} int powerdns_config */
988 static int powerdns_read (void)
989 {
990 llentry_t *e;
992 for (e = llist_head (list); e != NULL; e = e->next)
993 {
994 list_item_t *item = e->value;
995 item->func (item);
996 }
998 return (0);
999 } /* static int powerdns_read */
1001 static int powerdns_shutdown (void)
1002 {
1003 llentry_t *e;
1005 if (list == NULL)
1006 return (0);
1008 for (e = llist_head (list); e != NULL; e = e->next)
1009 {
1010 list_item_t *item = (list_item_t *) e->value;
1011 e->value = NULL;
1013 sfree (item->instance);
1014 sfree (item->command);
1015 sfree (item);
1016 }
1018 llist_destroy (list);
1019 list = NULL;
1021 return (0);
1022 } /* static int powerdns_shutdown */
1024 void module_register (void)
1025 {
1026 plugin_register_complex_config ("powerdns", powerdns_config);
1027 plugin_register_read ("powerdns", powerdns_read);
1028 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1029 } /* void module_register */
1031 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */