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 char *name;
79 char *type;
80 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 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 int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields);
163 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 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;
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 memset (&sa_unix, 0, sizeof (sa_unix));
391 sa_unix.sun_family = AF_UNIX;
392 sstrncpy (sa_unix.sun_path,
393 (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH,
394 sizeof (sa_unix.sun_path));
396 status = unlink (sa_unix.sun_path);
397 if ((status != 0) && (errno != ENOENT))
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 = (char *) 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 = (char *) 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 int i;
636 dummy = NULL;
638 value = strchr (key, '=');
639 if (value == NULL)
640 break;
642 *value = '\0';
643 value++;
645 if (value[0] == '\0')
646 continue;
648 /* Check if this item was requested. */
649 for (i = 0; i < fields_num; i++)
650 if (strcasecmp (key, fields[i]) == 0)
651 break;
652 if (i >= fields_num)
653 continue;
655 submit (item->instance, key, value);
656 } /* while (strtok_r) */
658 sfree (buffer);
660 return (0);
661 } /* }}} int powerdns_read_server */
663 /*
664 * powerdns_update_recursor_command
665 *
666 * Creates a string that holds the command to be sent to the recursor. This
667 * string is stores in the `command' member of the `list_item_t' passed to the
668 * function. This function is called by `powerdns_read_recursor'.
669 */
670 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
671 {
672 char buffer[4096];
673 int status;
675 if (li == NULL)
676 return (0);
678 if (li->fields_num < 1)
679 {
680 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
681 }
682 else
683 {
684 sstrncpy (buffer, "get ", sizeof (buffer));
685 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
686 li->fields, li->fields_num,
687 /* seperator = */ " ");
688 if (status < 0)
689 {
690 ERROR ("powerdns plugin: strjoin failed.");
691 return (-1);
692 }
693 buffer[sizeof (buffer) - 1] = 0;
694 size_t len = strlen (buffer);
695 if (len < sizeof (buffer) - 2)
696 {
697 buffer[len++] = ' ';
698 buffer[len++] = '\n';
699 buffer[len++] = '\0';
700 }
701 }
703 buffer[sizeof (buffer) - 1] = 0;
704 li->command = strdup (buffer);
705 if (li->command == NULL)
706 {
707 ERROR ("powerdns plugin: strdup failed.");
708 return (-1);
709 }
711 return (0);
712 } /* }}} int powerdns_update_recursor_command */
714 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
715 {
716 char *buffer = NULL;
717 size_t buffer_size = 0;
718 int status;
720 char *dummy;
722 char *keys_list;
723 char *key;
724 char *key_saveptr;
725 char *value;
726 char *value_saveptr;
728 if (item->command == NULL)
729 {
730 status = powerdns_update_recursor_command (item);
731 if (status != 0)
732 {
733 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
734 return (-1);
735 }
737 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
738 item->command);
739 }
740 assert (item->command != NULL);
742 status = powerdns_get_data (item, &buffer, &buffer_size);
743 if (status != 0)
744 {
745 ERROR ("powerdns plugin: powerdns_get_data failed.");
746 return (-1);
747 }
749 keys_list = strdup (item->command);
750 if (keys_list == NULL)
751 {
752 FUNC_ERROR ("strdup");
753 sfree (buffer);
754 return (-1);
755 }
757 key_saveptr = NULL;
758 value_saveptr = NULL;
760 /* Skip the `get' at the beginning */
761 strtok_r (keys_list, " \t", &key_saveptr);
763 dummy = buffer;
764 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
765 {
766 dummy = NULL;
768 key = strtok_r (NULL, " \t", &key_saveptr);
769 if (key == NULL)
770 break;
772 submit (item->instance, key, value);
773 } /* while (strtok_r) */
775 sfree (buffer);
776 sfree (keys_list);
778 return (0);
779 } /* }}} int powerdns_read_recursor */
781 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
782 oconfig_item_t *ci)
783 {
784 int i;
785 char **temp;
787 if (ci->values_num < 1)
788 {
789 WARNING ("powerdns plugin: The `Collect' option needs "
790 "at least one argument.");
791 return (-1);
792 }
794 for (i = 0; i < ci->values_num; i++)
795 if (ci->values[i].type != OCONFIG_TYPE_STRING)
796 {
797 WARNING ("powerdns plugin: Only string arguments are allowed to "
798 "the `Collect' option.");
799 return (-1);
800 }
802 temp = (char **) realloc (li->fields,
803 sizeof (char *) * (li->fields_num + ci->values_num));
804 if (temp == NULL)
805 {
806 WARNING ("powerdns plugin: realloc failed.");
807 return (-1);
808 }
809 li->fields = temp;
811 for (i = 0; i < ci->values_num; i++)
812 {
813 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
814 if (li->fields[li->fields_num] == NULL)
815 {
816 WARNING ("powerdns plugin: strdup failed.");
817 continue;
818 }
819 li->fields_num++;
820 }
822 /* Invalidate a previously computed command */
823 sfree (li->command);
825 return (0);
826 } /* }}} int powerdns_config_add_collect */
828 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
829 {
830 char *socket_temp;
832 list_item_t *item;
833 int status;
834 int i;
836 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
837 {
838 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
839 ci->key);
840 return (-1);
841 }
843 item = (list_item_t *) malloc (sizeof (list_item_t));
844 if (item == NULL)
845 {
846 ERROR ("powerdns plugin: malloc failed.");
847 return (-1);
848 }
849 memset (item, '\0', sizeof (list_item_t));
851 item->instance = strdup (ci->values[0].value.string);
852 if (item->instance == NULL)
853 {
854 ERROR ("powerdns plugin: strdup failed.");
855 sfree (item);
856 return (-1);
857 }
859 /*
860 * Set default values for the members of list_item_t
861 */
862 if (strcasecmp ("Server", ci->key) == 0)
863 {
864 item->server_type = SRV_AUTHORITATIVE;
865 item->func = powerdns_read_server;
866 item->socktype = SOCK_STREAM;
867 socket_temp = strdup (SERVER_SOCKET);
868 }
869 else if (strcasecmp ("Recursor", ci->key) == 0)
870 {
871 item->server_type = SRV_RECURSOR;
872 item->func = powerdns_read_recursor;
873 item->socktype = SOCK_DGRAM;
874 socket_temp = strdup (RECURSOR_SOCKET);
875 }
876 else
877 {
878 /* We must never get here.. */
879 assert (0);
880 return (-1);
881 }
883 status = 0;
884 for (i = 0; i < ci->children_num; i++)
885 {
886 oconfig_item_t *option = ci->children + i;
888 if (strcasecmp ("Collect", option->key) == 0)
889 status = powerdns_config_add_collect (item, option);
890 else if (strcasecmp ("Socket", option->key) == 0)
891 status = cf_util_get_string (option, &socket_temp);
892 else
893 {
894 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
895 status = -1;
896 }
898 if (status != 0)
899 break;
900 }
902 while (status == 0)
903 {
904 llentry_t *e;
906 if (socket_temp == NULL)
907 {
908 ERROR ("powerdns plugin: socket_temp == NULL.");
909 status = -1;
910 break;
911 }
913 item->sockaddr.sun_family = AF_UNIX;
914 sstrncpy (item->sockaddr.sun_path, socket_temp,
915 sizeof (item->sockaddr.sun_path));
917 e = llentry_create (item->instance, item);
918 if (e == NULL)
919 {
920 ERROR ("powerdns plugin: llentry_create failed.");
921 status = -1;
922 break;
923 }
924 llist_append (list, e);
926 break;
927 }
929 if (status != 0)
930 {
931 sfree (socket_temp);
932 sfree (item);
933 return (-1);
934 }
936 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
938 sfree (socket_temp);
939 return (0);
940 } /* }}} int powerdns_config_add_server */
942 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
943 {
944 int i;
946 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
948 if (list == NULL)
949 {
950 list = llist_create ();
952 if (list == NULL)
953 {
954 ERROR ("powerdns plugin: `llist_create' failed.");
955 return (-1);
956 }
957 }
959 for (i = 0; i < ci->children_num; i++)
960 {
961 oconfig_item_t *option = ci->children + i;
963 if ((strcasecmp ("Server", option->key) == 0)
964 || (strcasecmp ("Recursor", option->key) == 0))
965 powerdns_config_add_server (option);
966 else if (strcasecmp ("LocalSocket", option->key) == 0)
967 {
968 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
969 {
970 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
971 }
972 else
973 {
974 char *temp = strdup (option->values[0].value.string);
975 if (temp == NULL)
976 return (1);
977 sfree (local_sockpath);
978 local_sockpath = temp;
979 }
980 }
981 else
982 {
983 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
984 }
985 } /* for (i = 0; i < ci->children_num; i++) */
987 return (0);
988 } /* }}} int powerdns_config */
990 static int powerdns_read (void)
991 {
992 llentry_t *e;
994 for (e = llist_head (list); e != NULL; e = e->next)
995 {
996 list_item_t *item = e->value;
997 item->func (item);
998 }
1000 return (0);
1001 } /* static int powerdns_read */
1003 static int powerdns_shutdown (void)
1004 {
1005 llentry_t *e;
1007 if (list == NULL)
1008 return (0);
1010 for (e = llist_head (list); e != NULL; e = e->next)
1011 {
1012 list_item_t *item = (list_item_t *) e->value;
1013 e->value = NULL;
1015 sfree (item->instance);
1016 sfree (item->command);
1017 sfree (item);
1018 }
1020 llist_destroy (list);
1021 list = NULL;
1023 return (0);
1024 } /* static int powerdns_shutdown */
1026 void module_register (void)
1027 {
1028 plugin_register_complex_config ("powerdns", powerdns_config);
1029 plugin_register_read ("powerdns", powerdns_read);
1030 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1031 } /* void module_register */
1033 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */