Code

958a3410075788378ac0015d17974a6bebd255d7
[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"
29 #include "common.h"
30 #include "plugin.h"
31 #include "configfile.h"
32 #include "utils_llist.h"
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <sys/types.h>
41 #include <sys/un.h>
43 #ifndef UNIX_PATH_MAX
44 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
45 #endif
46 #define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", 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[] = /* {{{ */
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[] = /* {{{ */
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)
306   value_list_t vl = VALUE_LIST_INIT;
307   value_t values[1];
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);
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, &values[0], ds->ds[0].type))
350   {
351     ERROR ("powerdns plugin: Cannot convert `%s' "
352         "to a number.", value);
353     return;
354   }
356   vl.values = values;
357   vl.values_len = 1;
358   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
359   sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
360   sstrncpy (vl.type, type, sizeof (vl.type));
361   if (type_instance != NULL)
362     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
363   sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
365   plugin_dispatch_values (&vl);
366 } /* }}} static void submit */
368 static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */
369     char **ret_buffer,
370     size_t *ret_buffer_size)
372   int sd;
373   int status;
375   char temp[4096];
376   char *buffer = NULL;
377   size_t buffer_size = 0;
379   struct sockaddr_un sa_unix = { 0 };
381   struct timeval stv_timeout;
382   cdtime_t cdt_timeout;
384   sd = socket (PF_UNIX, item->socktype, 0);
385   if (sd < 0)
386   {
387     FUNC_ERROR ("socket");
388     return (-1);
389   }
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 : */