d7aa4e9dbe8ea8d94ca739baa2750b1d2026a707
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 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 SOCK_ERROR ("unlink", sa_unix.sun_path);
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 SOCK_ERROR ("bind", sa_unix.sun_path);
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 SOCK_ERROR ("chmod", sa_unix.sun_path);
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 SOCK_ERROR ("setsockopt", sa_unix.sun_path);
432 break;
433 }
435 status = connect (sd, (struct sockaddr *) &item->sockaddr,
436 sizeof (item->sockaddr));
437 if (status != 0)
438 {
439 SOCK_ERROR ("connect", sa_unix.sun_path);
440 break;
441 }
443 status = send (sd, item->command, strlen (item->command), 0);
444 if (status < 0)
445 {
446 SOCK_ERROR ("send", sa_unix.sun_path);
447 break;
448 }
450 status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
451 if (status < 0)
452 {
453 SOCK_ERROR ("recv", sa_unix.sun_path);
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 SOCK_ERROR ("connect", item->sockaddr.sun_path);
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 SOCK_ERROR ("send", item->sockaddr.sun_path);
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 SOCK_ERROR ("recv", item->sockaddr.sun_path);
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 dummy = NULL;
635 value = strchr (key, '=');
636 if (value == NULL)
637 break;
639 *value = '\0';
640 value++;
642 if (value[0] == '\0')
643 continue;
645 /* Check if this item was requested. */
646 int i;
647 for (i = 0; i < fields_num; i++)
648 if (strcasecmp (key, fields[i]) == 0)
649 break;
650 if (i >= fields_num)
651 continue;
653 submit (item->instance, key, value);
654 } /* while (strtok_r) */
656 sfree (buffer);
658 return (0);
659 } /* }}} int powerdns_read_server */
661 /*
662 * powerdns_update_recursor_command
663 *
664 * Creates a string that holds the command to be sent to the recursor. This
665 * string is stores in the `command' member of the `list_item_t' passed to the
666 * function. This function is called by `powerdns_read_recursor'.
667 */
668 static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */
669 {
670 char buffer[4096];
671 int status;
673 if (li == NULL)
674 return (0);
676 if (li->fields_num < 1)
677 {
678 sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer));
679 }
680 else
681 {
682 sstrncpy (buffer, "get ", sizeof (buffer));
683 status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
684 li->fields, li->fields_num,
685 /* seperator = */ " ");
686 if (status < 0)
687 {
688 ERROR ("powerdns plugin: strjoin failed.");
689 return (-1);
690 }
691 buffer[sizeof (buffer) - 1] = 0;
692 size_t len = strlen (buffer);
693 if (len < sizeof (buffer) - 2)
694 {
695 buffer[len++] = ' ';
696 buffer[len++] = '\n';
697 buffer[len++] = '\0';
698 }
699 }
701 buffer[sizeof (buffer) - 1] = 0;
702 li->command = strdup (buffer);
703 if (li->command == NULL)
704 {
705 ERROR ("powerdns plugin: strdup failed.");
706 return (-1);
707 }
709 return (0);
710 } /* }}} int powerdns_update_recursor_command */
712 static int powerdns_read_recursor (list_item_t *item) /* {{{ */
713 {
714 char *buffer = NULL;
715 size_t buffer_size = 0;
716 int status;
718 char *dummy;
720 char *keys_list;
721 char *key;
722 char *key_saveptr;
723 char *value;
724 char *value_saveptr;
726 if (item->command == NULL)
727 {
728 status = powerdns_update_recursor_command (item);
729 if (status != 0)
730 {
731 ERROR ("powerdns plugin: powerdns_update_recursor_command failed.");
732 return (-1);
733 }
735 DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;",
736 item->command);
737 }
738 assert (item->command != NULL);
740 status = powerdns_get_data (item, &buffer, &buffer_size);
741 if (status != 0)
742 {
743 ERROR ("powerdns plugin: powerdns_get_data failed.");
744 return (-1);
745 }
747 keys_list = strdup (item->command);
748 if (keys_list == NULL)
749 {
750 FUNC_ERROR ("strdup");
751 sfree (buffer);
752 return (-1);
753 }
755 key_saveptr = NULL;
756 value_saveptr = NULL;
758 /* Skip the `get' at the beginning */
759 strtok_r (keys_list, " \t", &key_saveptr);
761 dummy = buffer;
762 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
763 {
764 dummy = NULL;
766 key = strtok_r (NULL, " \t", &key_saveptr);
767 if (key == NULL)
768 break;
770 submit (item->instance, key, value);
771 } /* while (strtok_r) */
773 sfree (buffer);
774 sfree (keys_list);
776 return (0);
777 } /* }}} int powerdns_read_recursor */
779 static int powerdns_config_add_collect (list_item_t *li, /* {{{ */
780 oconfig_item_t *ci)
781 {
782 char **temp;
784 if (ci->values_num < 1)
785 {
786 WARNING ("powerdns plugin: The `Collect' option needs "
787 "at least one argument.");
788 return (-1);
789 }
791 for (int i = 0; i < ci->values_num; i++)
792 if (ci->values[i].type != OCONFIG_TYPE_STRING)
793 {
794 WARNING ("powerdns plugin: Only string arguments are allowed to "
795 "the `Collect' option.");
796 return (-1);
797 }
799 temp = realloc (li->fields,
800 sizeof (char *) * (li->fields_num + ci->values_num));
801 if (temp == NULL)
802 {
803 WARNING ("powerdns plugin: realloc failed.");
804 return (-1);
805 }
806 li->fields = temp;
808 for (int i = 0; i < ci->values_num; i++)
809 {
810 li->fields[li->fields_num] = strdup (ci->values[i].value.string);
811 if (li->fields[li->fields_num] == NULL)
812 {
813 WARNING ("powerdns plugin: strdup failed.");
814 continue;
815 }
816 li->fields_num++;
817 }
819 /* Invalidate a previously computed command */
820 sfree (li->command);
822 return (0);
823 } /* }}} int powerdns_config_add_collect */
825 static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */
826 {
827 char *socket_temp;
829 list_item_t *item;
830 int status;
832 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
833 {
834 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
835 ci->key);
836 return (-1);
837 }
839 item = calloc (1, sizeof (*item));
840 if (item == NULL)
841 {
842 ERROR ("powerdns plugin: calloc failed.");
843 return (-1);
844 }
846 item->instance = strdup (ci->values[0].value.string);
847 if (item->instance == NULL)
848 {
849 ERROR ("powerdns plugin: strdup failed.");
850 sfree (item);
851 return (-1);
852 }
854 /*
855 * Set default values for the members of list_item_t
856 */
857 if (strcasecmp ("Server", ci->key) == 0)
858 {
859 item->server_type = SRV_AUTHORITATIVE;
860 item->func = powerdns_read_server;
861 item->socktype = SOCK_STREAM;
862 socket_temp = strdup (SERVER_SOCKET);
863 }
864 else if (strcasecmp ("Recursor", ci->key) == 0)
865 {
866 item->server_type = SRV_RECURSOR;
867 item->func = powerdns_read_recursor;
868 item->socktype = SOCK_DGRAM;
869 socket_temp = strdup (RECURSOR_SOCKET);
870 }
871 else
872 {
873 /* We must never get here.. */
874 assert (0);
875 return (-1);
876 }
878 status = 0;
879 for (int i = 0; i < ci->children_num; i++)
880 {
881 oconfig_item_t *option = ci->children + i;
883 if (strcasecmp ("Collect", option->key) == 0)
884 status = powerdns_config_add_collect (item, option);
885 else if (strcasecmp ("Socket", option->key) == 0)
886 status = cf_util_get_string (option, &socket_temp);
887 else
888 {
889 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
890 status = -1;
891 }
893 if (status != 0)
894 break;
895 }
897 while (status == 0)
898 {
899 llentry_t *e;
901 if (socket_temp == NULL)
902 {
903 ERROR ("powerdns plugin: socket_temp == NULL.");
904 status = -1;
905 break;
906 }
908 item->sockaddr.sun_family = AF_UNIX;
909 sstrncpy (item->sockaddr.sun_path, socket_temp,
910 sizeof (item->sockaddr.sun_path));
912 e = llentry_create (item->instance, item);
913 if (e == NULL)
914 {
915 ERROR ("powerdns plugin: llentry_create failed.");
916 status = -1;
917 break;
918 }
919 llist_append (list, e);
921 break;
922 }
924 if (status != 0)
925 {
926 sfree (socket_temp);
927 sfree (item);
928 return (-1);
929 }
931 DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
933 sfree (socket_temp);
934 return (0);
935 } /* }}} int powerdns_config_add_server */
937 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
938 {
939 DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
941 if (list == NULL)
942 {
943 list = llist_create ();
945 if (list == NULL)
946 {
947 ERROR ("powerdns plugin: `llist_create' failed.");
948 return (-1);
949 }
950 }
952 for (int i = 0; i < ci->children_num; i++)
953 {
954 oconfig_item_t *option = ci->children + i;
956 if ((strcasecmp ("Server", option->key) == 0)
957 || (strcasecmp ("Recursor", option->key) == 0))
958 powerdns_config_add_server (option);
959 else if (strcasecmp ("LocalSocket", option->key) == 0)
960 {
961 if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
962 {
963 WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
964 }
965 else
966 {
967 char *temp = strdup (option->values[0].value.string);
968 if (temp == NULL)
969 return (1);
970 sfree (local_sockpath);
971 local_sockpath = temp;
972 }
973 }
974 else
975 {
976 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
977 }
978 } /* for (i = 0; i < ci->children_num; i++) */
980 return (0);
981 } /* }}} int powerdns_config */
983 static int powerdns_read (void)
984 {
985 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
986 {
987 list_item_t *item = e->value;
988 item->func (item);
989 }
991 return (0);
992 } /* static int powerdns_read */
994 static int powerdns_shutdown (void)
995 {
996 if (list == NULL)
997 return (0);
999 for (llentry_t *e = llist_head (list); e != NULL; e = e->next)
1000 {
1001 list_item_t *item = (list_item_t *) e->value;
1002 e->value = NULL;
1004 sfree (item->instance);
1005 sfree (item->command);
1006 sfree (item);
1007 }
1009 llist_destroy (list);
1010 list = NULL;
1012 return (0);
1013 } /* static int powerdns_shutdown */
1015 void module_register (void)
1016 {
1017 plugin_register_complex_config ("powerdns", powerdns_config);
1018 plugin_register_read ("powerdns", powerdns_read);
1019 plugin_register_shutdown ("powerdns", powerdns_shutdown );
1020 } /* void module_register */
1022 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */