Code

configure: Add missing end fold marker (for Vim).
[collectd.git] / src / powerdns.c
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[] = /* {{{ */
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[] = /* {{{ */
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)
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)
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 = 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)
488   int sd;
489   int status;
491   char temp[4096];
492   char *buffer = NULL;
493   size_t buffer_size = 0;
495   sd = socket (PF_UNIX, item->socktype, 0);
496   if (sd < 0)
497   {
498     FUNC_ERROR ("socket");
499     return (-1);
500   }
502   struct timeval timeout;
503   timeout.tv_sec=5;
504   timeout.tv_usec=0;
505   status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
506   if (status != 0)
507   {
508     FUNC_ERROR ("setsockopt");
509     close (sd);
510     return (-1);
511   }
513   status = connect (sd, (struct sockaddr *) &item->sockaddr,
514       sizeof (item->sockaddr));
515   if (status != 0)
516   {
517     FUNC_ERROR ("connect");
518     close (sd);
519     return (-1);
520   }
522   /* strlen + 1, because we need to send the terminating NULL byte, too. */
523   status = send (sd, item->command, strlen (item->command) + 1,
524       /* flags = */ 0);
525   if (status < 0)
526   {
527     FUNC_ERROR ("send");
528     close (sd);
529     return (-1);
530   }
532   while (42)
533   {
534     char *buffer_new;
536     status = recv (sd, temp, sizeof (temp), /* flags = */ 0);
537     if (status < 0)
538     {
539       FUNC_ERROR ("recv");
540       break;
541     }
542     else if (status == 0)
543       break;
545     buffer_new = realloc (buffer, buffer_size + status + 1);
546     if (buffer_new == NULL)
547     {
548       FUNC_ERROR ("realloc");
549       status = -1;
550       break;
551     }
552     buffer = buffer_new;
554     memcpy (buffer + buffer_size, temp, status);
555     buffer_size += status;
556     buffer[buffer_size] = 0;
557   } /* while (42) */
558   close (sd);
560   if (status < 0)
561   {
562     sfree (buffer);
563   }
564   else
565   {
566     assert (status == 0);
567     *ret_buffer = buffer;
568     *ret_buffer_size = buffer_size;
569   }
571   return (status);
572 } /* }}} int powerdns_get_data_stream */
574 static int powerdns_get_data (list_item_t *item, char **ret_buffer,
575     size_t *ret_buffer_size)
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) /* {{{ */
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) /* {{{ */
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) /* {{{ */
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)
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 = 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) /* {{{ */
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 = calloc (1, sizeof (*item));
844   if (item == NULL)
845   {
846     ERROR ("powerdns plugin: calloc failed.");
847     return (-1);
848   }
850   item->instance = strdup (ci->values[0].value.string);
851   if (item->instance == NULL)
852   {
853     ERROR ("powerdns plugin: strdup failed.");
854     sfree (item);
855     return (-1);
856   }
858   /*
859    * Set default values for the members of list_item_t
860    */
861   if (strcasecmp ("Server", ci->key) == 0)
862   {
863     item->server_type = SRV_AUTHORITATIVE;
864     item->func = powerdns_read_server;
865     item->socktype = SOCK_STREAM;
866     socket_temp = strdup (SERVER_SOCKET);
867   }
868   else if (strcasecmp ("Recursor", ci->key) == 0)
869   {
870     item->server_type = SRV_RECURSOR;
871     item->func = powerdns_read_recursor;
872     item->socktype = SOCK_DGRAM;
873     socket_temp = strdup (RECURSOR_SOCKET);
874   }
875   else
876   {
877     /* We must never get here.. */
878     assert (0);
879     return (-1);
880   }
882   status = 0;
883   for (i = 0; i < ci->children_num; i++)
884   {
885     oconfig_item_t *option = ci->children + i;
887     if (strcasecmp ("Collect", option->key) == 0)
888       status = powerdns_config_add_collect (item, option);
889     else if (strcasecmp ("Socket", option->key) == 0)
890       status = cf_util_get_string (option, &socket_temp);
891     else
892     {
893       ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
894       status = -1;
895     }
897     if (status != 0)
898       break;
899   }
901   while (status == 0)
902   {
903     llentry_t *e;
905     if (socket_temp == NULL)
906     {
907       ERROR ("powerdns plugin: socket_temp == NULL.");
908       status = -1;
909       break;
910     }
912     item->sockaddr.sun_family = AF_UNIX;
913     sstrncpy (item->sockaddr.sun_path, socket_temp,
914       sizeof (item->sockaddr.sun_path));
916     e = llentry_create (item->instance, item);
917     if (e == NULL)
918     {
919       ERROR ("powerdns plugin: llentry_create failed.");
920       status = -1;
921       break;
922     }
923     llist_append (list, e);
925     break;
926   }
928   if (status != 0)
929   {
930     sfree (socket_temp);
931     sfree (item);
932     return (-1);
933   }
935   DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance);
937   sfree (socket_temp);
938   return (0);
939 } /* }}} int powerdns_config_add_server */
941 static int powerdns_config (oconfig_item_t *ci) /* {{{ */
943   int i;
945   DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci);
947   if (list == NULL)
948   {
949     list = llist_create ();
951     if (list == NULL)
952     {
953       ERROR ("powerdns plugin: `llist_create' failed.");
954       return (-1);
955     }
956   }
958   for (i = 0; i < ci->children_num; i++)
959   {
960     oconfig_item_t *option = ci->children + i;
962     if ((strcasecmp ("Server", option->key) == 0)
963         || (strcasecmp ("Recursor", option->key) == 0))
964       powerdns_config_add_server (option);
965     else if (strcasecmp ("LocalSocket", option->key) == 0)
966     {
967       if ((option->values_num != 1) || (option->values[0].type != OCONFIG_TYPE_STRING))
968       {
969         WARNING ("powerdns plugin: `%s' needs exactly one string argument.", option->key);
970       }
971       else
972       {
973         char *temp = strdup (option->values[0].value.string);
974         if (temp == NULL)
975           return (1);
976         sfree (local_sockpath);
977         local_sockpath = temp;
978       }
979     }
980     else
981     {
982       ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
983     }
984   } /* for (i = 0; i < ci->children_num; i++) */
986   return (0);
987 } /* }}} int powerdns_config */
989 static int powerdns_read (void)
991   llentry_t *e;
993   for (e = llist_head (list); e != NULL; e = e->next)
994   {
995     list_item_t *item = e->value;
996     item->func (item);
997   }
999   return (0);
1000 } /* static int powerdns_read */
1002 static int powerdns_shutdown (void)
1004   llentry_t *e;
1006   if (list == NULL)
1007     return (0);
1009   for (e = llist_head (list); e != NULL; e = e->next)
1010   {
1011     list_item_t *item = (list_item_t *) e->value;
1012     e->value = NULL;
1014     sfree (item->instance);
1015     sfree (item->command);
1016     sfree (item);
1017   }
1019   llist_destroy (list);
1020   list = NULL;
1022   return (0);
1023 } /* static int powerdns_shutdown */
1025 void module_register (void)
1027   plugin_register_complex_config ("powerdns", powerdns_config);
1028   plugin_register_read ("powerdns", powerdns_read);
1029   plugin_register_shutdown ("powerdns", powerdns_shutdown );
1030 } /* void module_register */
1032 /* vim: set sw=2 sts=2 ts=8 fdm=marker : */