X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fpowerdns.c;h=189c46fec1bbf2cb7383096418da4b5649f94d97;hb=e628f39838a67b40d52dfb8425b4d8474fbd0550;hp=980141e789324146a775795dd74e81f2870594de;hpb=bd150991dbf79289690365b8ebd9865cdfc2157b;p=collectd.git diff --git a/src/powerdns.c b/src/powerdns.c index 980141e7..189c46fe 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -1,6 +1,7 @@ /** * collectd - src/powerdns.c * Copyright (C) 2007-2008 C-Ware, Inc. + * Copyright (C) 2008 Florian Forster * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,13 +18,14 @@ * * Author: * Luke Heberling + * Florian Forster * * DESCRIPTION - * Queries a PowerDNS control socket for statistics - * + * Queries a PowerDNS control socket for statistics **/ #include "collectd.h" +#include "common.h" #include "plugin.h" #include "configfile.h" #include "utils_llist.h" @@ -37,515 +39,935 @@ #include #include #include -#include -#define BUFFER_SIZE 1000 +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path) +#endif +#define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0) -#define FUNC_ERROR(func) ERROR ("%s: `%s' failed\n", "powerdns", func) +#define SERVER_SOCKET LOCALSTATEDIR"/run/pdns.controlsocket" +#define SERVER_COMMAND "SHOW *" -#define COMMAND_SERVER "SHOW *" -#define COMMAND_RECURSOR "get all-outqueries answers0-1 answers100-1000 answers10-100 answers1-10 answers-slow cache-entries cache-hits cache-misses chain-resends client-parse-errors concurrent-queries dlg-only-drops ipv6-outqueries negcache-entries noerror-answers nsset-invalidations nsspeeds-entries nxdomain-answers outgoing-timeouts qa-latency questions resource-limits server-parse-errors servfail-answers spoof-prevents sys-msec tcp-client-overflow tcp-outqueries tcp-questions throttled-out throttled-outqueries throttle-entries unauthorized-tcp unauthorized-udp unexpected-packets unreachables user-msec" +#define RECURSOR_SOCKET LOCALSTATEDIR"/run/pdns_recursor.controlsocket" +#define RECURSOR_COMMAND "get noerror-answers nxdomain-answers " \ + "servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \ + "cache-misses questions" -typedef void item_func (void*); -typedef ssize_t io_func (int, void*, size_t, int); +struct list_item_s; +typedef struct list_item_s list_item_t; struct list_item_s { - item_func *func; + enum + { + SRV_AUTHORITATIVE, + SRV_RECURSOR + } server_type; + int (*func) (list_item_t *item); char *instance; + + char **fields; + int fields_num; char *command; - struct sockaddr_un remote; - struct sockaddr_un local; + + struct sockaddr_un sockaddr; + int socktype; }; -typedef struct list_item_s list_item_t; + +struct statname_lookup_s +{ + char *name; + char *type; + char *type_instance; +}; +typedef struct statname_lookup_s statname_lookup_t; + +/* Description of statistics returned by the recursor: {{{ +all-outqueries counts the number of outgoing UDP queries since starting +answers0-1 counts the number of queries answered within 1 milisecond +answers100-1000 counts the number of queries answered within 1 second +answers10-100 counts the number of queries answered within 100 miliseconds +answers1-10 counts the number of queries answered within 10 miliseconds +answers-slow counts the number of queries answered after 1 second +cache-entries shows the number of entries in the cache +cache-hits counts the number of cache hits since starting +cache-misses counts the number of cache misses since starting +chain-resends number of queries chained to existing outstanding query +client-parse-errors counts number of client packets that could not be parsed +concurrent-queries shows the number of MThreads currently running +dlg-only-drops number of records dropped because of delegation only setting +negcache-entries shows the number of entries in the Negative answer cache +noerror-answers counts the number of times it answered NOERROR since starting +nsspeeds-entries shows the number of entries in the NS speeds map +nsset-invalidations number of times an nsset was dropped because it no longer worked +nxdomain-answers counts the number of times it answered NXDOMAIN since starting +outgoing-timeouts counts the number of timeouts on outgoing UDP queries since starting +qa-latency shows the current latency average +questions counts all End-user initiated queries with the RD bit set +resource-limits counts number of queries that could not be performed because of resource limits +server-parse-errors counts number of server replied packets that could not be parsed +servfail-answers counts the number of times it answered SERVFAIL since starting +spoof-prevents number of times PowerDNS considered itself spoofed, and dropped the data +sys-msec number of CPU milliseconds spent in 'system' mode +tcp-client-overflow number of times an IP address was denied TCP access because it already had too many connections +tcp-outqueries counts the number of outgoing TCP queries since starting +tcp-questions counts all incoming TCP queries (since starting) +throttled-out counts the number of throttled outgoing UDP queries since starting +throttle-entries shows the number of entries in the throttle map +unauthorized-tcp number of TCP questions denied because of allow-from restrictions +unauthorized-udp number of UDP questions denied because of allow-from restrictions +unexpected-packets number of answers from remote servers that were unexpected (might point to spoofing) +uptime number of seconds process has been running (since 3.1.5) +user-msec number of CPU milliseconds spent in 'user' mode +}}} */ + +const char* const default_server_fields[] = /* {{{ */ +{ + "latency" + "packetcache-hit", + "packetcache-miss", + "packetcache-size", + "query-cache-hit", + "query-cache-miss", + "recursing-answers", + "recursing-questions", + "tcp-answers", + "tcp-queries", + "udp-answers", + "udp-queries", +}; /* }}} */ +int default_server_fields_num = STATIC_ARRAY_SIZE (default_server_fields); + +statname_lookup_t lookup_table[] = /* {{{ */ +{ + /********************* + * Server statistics * + *********************/ + /* Questions */ + {"recursing-questions", "dns_question", "recurse"}, + {"tcp-queries", "dns_question", "tcp"}, + {"udp-queries", "dns_question", "udp"}, + + /* Answers */ + {"recursing-answers", "dns_answer", "recurse"}, + {"tcp-answers", "dns_answer", "tcp"}, + {"udp-answers", "dns_answer", "udp"}, + + /* Cache stuff */ + {"packetcache-hit", "cache_result", "packet-hit"}, + {"packetcache-miss", "cache_result", "packet-miss"}, + {"packetcache-size", "cache_size", "packet"}, + {"query-cache-hit", "cache_result", "query-hit"}, + {"query-cache-miss", "cache_result", "query-miss"}, + + /* Latency */ + {"latency", "latency", NULL}, + + /* Other stuff.. */ + {"corrupt-packets", "io_packets", "corrupt"}, + {"deferred-cache-inserts", "counter", "cache-deferred_insert"}, + {"deferred-cache-lookup", "counter", "cache-deferred_lookup"}, + {"qsize-a", "cache_size", "answers"}, + {"qsize-q", "cache_size", "questions"}, + {"servfail-packets", "io_packets", "servfail"}, + {"timedout-packets", "io_packets", "timeout"}, + {"udp4-answers", "dns_answer", "udp4"}, + {"udp4-queries", "dns_question", "queries-udp4"}, + {"udp6-answers", "dns_answer", "udp6"}, + {"udp6-queries", "dns_question", "queries-udp6"}, + + /*********************** + * Recursor statistics * + ***********************/ + /* Answers by return code */ + {"noerror-answers", "dns_rcode", "NOERROR"}, + {"nxdomain-answers", "dns_rcode", "NXDOMAIN"}, + {"servfail-answers", "dns_rcode", "SERVFAIL"}, + + /* CPU utilization */ + {"sys-msec", "cpu", "system"}, + {"user-msec", "cpu", "user"}, + + /* Question-to-answer latency */ + {"qa-latency", "latency", NULL}, + + /* Cache */ + {"cache-entries", "cache_size", NULL}, + {"cache-hits", "cache_result", "hit"}, + {"cache-misses", "cache_result", "miss"}, + + /* Total number of questions.. */ + {"questions", "dns_qtype", "total"}, + + /* All the other stuff.. */ + {"all-outqueries", "dns_question", "outgoing"}, + {"answers0-1", "dns_answer", "0_1"}, + {"answers1-10", "dns_answer", "1_10"}, + {"answers10-100", "dns_answer", "10_100"}, + {"answers100-1000", "dns_answer", "100_1000"}, + {"answers-slow", "dns_answer", "slow"}, + {"chain-resends", "dns_question", "chained"}, + {"client-parse-errors", "counter", "drops-client_parse_error"}, + {"concurrent-queries", "dns_question", "concurrent"}, + {"dlg-only-drops", "counter", "drops-delegation_only"}, + {"negcache-entries", "cache_size", "negative"}, + {"nsspeeds-entries", "gauge", "entries-ns_speeds"}, + {"nsset-invalidations", "counter", "ns_set_invalidation"}, + {"outgoing-timeouts", "counter", "drops-timeout_outgoing"}, + {"resource-limits", "counter", "drops-resource_limit"}, + {"server-parse-errors", "counter", "drops-server_parse_error"}, + {"spoof-prevents", "counter", "drops-spoofed"}, + {"tcp-client-overflow", "counter", "denied-client_overflow_tcp"}, + {"tcp-outqueries", "dns_question", "outgoing-tcp"}, + {"tcp-questions", "dns_question", "incoming-tcp"}, + {"throttled-out", "dns_question", "outgoing-throttled"}, + {"throttle-entries", "gauge", "entries-throttle"}, + {"unauthorized-tcp", "counter", "denied-unauthorized_tcp"}, + {"unauthorized-udp", "counter", "denied-unauthorized_udp"}, + {"unexpected-packets", "dns_answer", "unexpected"} + /* {"uptime", "", ""} */ +}; /* }}} */ +int lookup_table_length = STATIC_ARRAY_SIZE (lookup_table); static llist_t *list = NULL; -static void submit (const char *instance, const char *name, const char *value) +#define PDNS_LOCAL_SOCKPATH LOCALSTATEDIR"/run/"PACKAGE_NAME"-powerdns" +static char *local_sockpath = NULL; + +/* TODO: Do this before 4.4: + * - Recursor: + * - Complete list of known pdns -> collectd mappings. + * - Update the collectd.conf(5) manpage. + * + * -octo + */ + +/* */ +static void submit (const char *plugin_instance, /* {{{ */ + const char *pdns_type, const char *value) { value_list_t vl = VALUE_LIST_INIT; value_t values[1]; + + const char *type = NULL; + const char *type_instance = NULL; const data_set_t *ds; - float f; - long l; - ds = plugin_get_ds (name); + int i; + + for (i = 0; i < lookup_table_length; i++) + if (strcmp (lookup_table[i].name, pdns_type) == 0) + break; + + if (lookup_table[i].type == NULL) + return; + + if (i >= lookup_table_length) + { + INFO ("powerdns plugin: submit: Not found in lookup table: %s = %s;", + pdns_type, value); + return; + } + + type = lookup_table[i].type; + type_instance = lookup_table[i].type_instance; + + ds = plugin_get_ds (type); if (ds == NULL) { - ERROR( "%s: DS %s not defined\n", "powerdns", name ); + ERROR ("powerdns plugin: The lookup table returned type `%s', " + "but I cannot find it via `plugin_get_ds'.", + type); return; } - errno = 0; - if (ds->ds->type == DS_TYPE_GAUGE) + if (ds->ds_num != 1) { - f = atof(value); - if (errno != 0) + ERROR ("powerdns plugin: type `%s' has %i data sources, " + "but I can only handle one.", + type, ds->ds_num); + return; + } + + if (ds->ds[0].type == DS_TYPE_GAUGE) + { + char *endptr = NULL; + + values[0].gauge = strtod (value, &endptr); + + if (endptr == value) { - ERROR ("%s: atof failed (%s->%s)", "powerdns", name, value); + ERROR ("powerdns plugin: Cannot convert `%s' " + "to a floating point number.", value); return; } - else - { - values[0].gauge = f<0?-f:f; - } } else { - l = atol(value); - if (errno != 0) + char *endptr = NULL; + + values[0].counter = strtoll (value, &endptr, 0); + if (endptr == value) { - ERROR ("%s: atol failed (%s->%s)", "powerdns", name, value); + ERROR ("powerdns plugin: Cannot convert `%s' " + "to an integer number.", value); return; } - else - { - values[0].counter = l < 0 ? -l : l; - } } vl.values = values; vl.values_len = 1; vl.time = time (NULL); - strncpy (vl.host, hostname_g, sizeof (vl.host)); - strncpy (vl.plugin, "powerdns", sizeof (vl.plugin)); - strncpy (vl.type_instance, "", sizeof (vl.type_instance)); - strncpy (vl.plugin_instance,instance, sizeof (vl.plugin_instance)); - - plugin_dispatch_values (name, &vl); -} /* static void submit */ - -static int io (io_func *func, int fd, char* buf, int buflen) + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin)); + sstrncpy (vl.type, type, sizeof (vl.type)); + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); + + plugin_dispatch_values (&vl); +} /* }}} static void submit */ + +static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */ + char **ret_buffer, + size_t *ret_buffer_size) { - int bytes = 0; - int cc = 1; - for (; buflen > 0 && (cc = func (fd, buf, buflen, 0)) > 0; - buf += cc, bytes += cc, buflen -= cc) - ; + int sd; + int status; - return bytes; -} /* static int io */ + char temp[4096]; + char *buffer = NULL; + size_t buffer_size = 0; -static void powerdns_read_server (list_item_t *item) -{ - int bytes; - int sck; - char *name_token,*value_token,*pos; - char *buffer; - char *delims = ",="; + struct sockaddr_un sa_unix; - if ((sck = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + sd = socket (PF_UNIX, item->socktype, 0); + if (sd < 0) { FUNC_ERROR ("socket"); - return; + return (-1); } - if (connect( sck,(struct sockaddr *) &item->remote, - sizeof(item->remote)) == -1) + memset (&sa_unix, 0, sizeof (sa_unix)); + sa_unix.sun_family = AF_UNIX; + sstrncpy (sa_unix.sun_path, + (local_sockpath != NULL) ? local_sockpath : PDNS_LOCAL_SOCKPATH, + sizeof (sa_unix.sun_path)); + + status = unlink (sa_unix.sun_path); + if ((status != 0) && (errno != ENOENT)) { - FUNC_ERROR( "connect" ); - close (sck); - return; + FUNC_ERROR ("unlink"); + close (sd); + return (-1); } - buffer = malloc (BUFFER_SIZE + 1); + do /* while (0) */ + { + /* We need to bind to a specific path, because this is a datagram socket + * and otherwise the daemon cannot answer. */ + status = bind (sd, (struct sockaddr *) &sa_unix, sizeof (sa_unix)); + if (status != 0) + { + FUNC_ERROR ("bind"); + break; + } + + /* Make the socket writeable by the daemon.. */ + status = chmod (sa_unix.sun_path, 0666); + if (status != 0) + { + FUNC_ERROR ("chmod"); + break; + } + + status = connect (sd, (struct sockaddr *) &item->sockaddr, + sizeof (item->sockaddr)); + if (status != 0) + { + FUNC_ERROR ("connect"); + break; + } + + status = send (sd, item->command, strlen (item->command), 0); + if (status < 0) + { + FUNC_ERROR ("send"); + break; + } + + status = recv (sd, temp, sizeof (temp), /* flags = */ 0); + if (status < 0) + { + FUNC_ERROR ("recv"); + break; + } + buffer_size = status + 1; + status = 0; + } while (0); + + close (sd); + unlink (sa_unix.sun_path); + + if (status != 0) + return (-1); + + assert (buffer_size > 0); + buffer = (char *) malloc (buffer_size); if (buffer == NULL) { FUNC_ERROR ("malloc"); - close (sck); - return; + return (-1); } - strncpy (buffer, - item->command == NULL ? COMMAND_SERVER : item->command, - BUFFER_SIZE); - buffer[BUFFER_SIZE] = '\0'; - if (io ((io_func*) &send, sck, buffer, strlen(buffer)) < strlen(buffer)) + memcpy (buffer, temp, buffer_size - 1); + buffer[buffer_size - 1] = 0; + + *ret_buffer = buffer; + *ret_buffer_size = buffer_size; + + return (0); +} /* }}} int powerdns_get_data_dgram */ + +static int powerdns_get_data_stream (list_item_t *item, /* {{{ */ + char **ret_buffer, + size_t *ret_buffer_size) +{ + int sd; + int status; + + char temp[4096]; + char *buffer = NULL; + size_t buffer_size = 0; + + sd = socket (PF_UNIX, item->socktype, 0); + if (sd < 0) { - FUNC_ERROR ("send"); - free (buffer); - close (sck); - return; + FUNC_ERROR ("socket"); + return (-1); } - bytes = io ((io_func*) &recv, sck, buffer, BUFFER_SIZE); - if (bytes < 1) + status = connect (sd, (struct sockaddr *) &item->sockaddr, + sizeof (item->sockaddr)); + if (status != 0) { - FUNC_ERROR ("recv"); - free (buffer); - close (sck); - return; + FUNC_ERROR ("connect"); + close (sd); + return (-1); } - close(sck); - - buffer[bytes] = '\0'; + /* strlen + 1, because we need to send the terminating NULL byte, too. */ + status = send (sd, item->command, strlen (item->command) + 1, + /* flags = */ 0); + if (status < 0) + { + FUNC_ERROR ("send"); + close (sd); + return (-1); + } - for (name_token = strtok_r (buffer, delims, &pos), - value_token = strtok_r (NULL, delims, &pos); - name_token != NULL && value_token != NULL; - name_token = strtok_r (NULL, delims, &pos ), - value_token = strtok_r (NULL, delims, &pos) ) - submit (item->instance, name_token, value_token); + while (42) + { + char *buffer_new; - free (buffer); - return; -} /* static void powerdns_read_server */ + status = recv (sd, temp, sizeof (temp), /* flags = */ 0); + if (status < 0) + { + FUNC_ERROR ("recv"); + break; + } + else if (status == 0) + break; -static void powerdns_read_recursor (list_item_t *item) { - int sck,tmp,bytes; - char *ptr; - char *name_token, *name_pos; - char *value_token, *value_pos; - char *send_buffer; - char *recv_buffer; - char *delims = " \n"; + buffer_new = (char *) realloc (buffer, buffer_size + status + 1); + if (buffer_new == NULL) + { + FUNC_ERROR ("realloc"); + status = -1; + break; + } + buffer = buffer_new; - for (ptr = item->local.sun_path - + strlen(item->local.sun_path) - 1; - ptr > item->local.sun_path && *ptr != '/'; --ptr) - ; + memcpy (buffer + buffer_size, temp, status); + buffer_size += status; + buffer[buffer_size] = 0; + } /* while (42) */ + close (sd); + sd = -1; - if (ptr <= item->local.sun_path) + if (status < 0) { - ERROR("%s: Bad path %s\n", "powerdns", item->local.sun_path); - return; + sfree (buffer); + } + else + { + assert (status == 0); + *ret_buffer = buffer; + *ret_buffer_size = buffer_size; } - *ptr='\0'; - strncat (item->local.sun_path, "/lsockXXXXXX", - sizeof (item->local.sun_path) - strlen (item->local.sun_path)); + return (status); +} /* }}} int powerdns_get_data_stream */ - if ((sck = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { - FUNC_ERROR ("socket"); - return; +static int powerdns_get_data (list_item_t *item, char **ret_buffer, + size_t *ret_buffer_size) +{ + if (item->socktype == SOCK_DGRAM) + return (powerdns_get_data_dgram (item, ret_buffer, ret_buffer_size)); + else if (item->socktype == SOCK_STREAM) + return (powerdns_get_data_stream (item, ret_buffer, ret_buffer_size)); + else + { + ERROR ("powerdns plugin: Unknown socket type: %i", (int) item->socktype); + return (-1); } +} /* int powerdns_get_data */ + +static int powerdns_read_server (list_item_t *item) /* {{{ */ +{ + char *buffer = NULL; + size_t buffer_size = 0; + int status; + + char *dummy; + char *saveptr; + + char *key; + char *value; + + const char* const *fields; + int fields_num; - tmp = 1; - if (setsockopt (sck, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) < 0) + if (item->command == NULL) + item->command = strdup ("SHOW *"); + if (item->command == NULL) { - FUNC_ERROR ("setsockopt"); - close (sck); - return; + ERROR ("powerdns plugin: strdup failed."); + return (-1); } - if ((tmp=mkstemp(item->local.sun_path))< 0) + status = powerdns_get_data (item, &buffer, &buffer_size); + if (status != 0) + return (-1); + + if (item->fields_num != 0) { - FUNC_ERROR ("mkstemp"); - close (sck); - return; + fields = (const char* const *) item->fields; + fields_num = item->fields_num; } - close (tmp); - - if (unlink(item->local.sun_path) < 0 && errno != ENOENT) + else { - FUNC_ERROR ("unlink"); - close (sck); - return; + fields = default_server_fields; + fields_num = default_server_fields_num; } - if (bind(sck, (struct sockaddr*)&item->local, sizeof(item->local)) < 0) + assert (fields != NULL); + assert (fields_num > 0); + + /* 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, */ + dummy = buffer; + saveptr = NULL; + while ((key = strtok_r (dummy, ",", &saveptr)) != NULL) { - FUNC_ERROR ("bind"); - close (sck); - unlink (item->local.sun_path); - return; - } + int i; + + dummy = NULL; + + value = strchr (key, '='); + if (value == NULL) + break; + + *value = '\0'; + value++; + + if (value[0] == '\0') + continue; + + /* Check if this item was requested. */ + for (i = 0; i < fields_num; i++) + if (strcasecmp (key, fields[i]) == 0) + break; + if (i >= fields_num) + continue; - if (chmod(item->local.sun_path,0666) < 0) + submit (item->instance, key, value); + } /* while (strtok_r) */ + + sfree (buffer); + + return (0); +} /* }}} int powerdns_read_server */ + +/* + * powerdns_update_recursor_command + * + * Creates a string that holds the command to be sent to the recursor. This + * string is stores in the `command' member of the `list_item_t' passed to the + * function. This function is called by `powerdns_read_recursor'. + */ +static int powerdns_update_recursor_command (list_item_t *li) /* {{{ */ +{ + char buffer[4096]; + int status; + + if (li == NULL) + return (0); + + if (li->fields_num < 1) { - FUNC_ERROR ("chmod"); - close (sck); - unlink (item->local.sun_path); - return; + sstrncpy (buffer, RECURSOR_COMMAND, sizeof (buffer)); } - - if (connect (sck,(struct sockaddr *) &item->remote, sizeof(item->remote)) == -1) + else { - FUNC_ERROR ("connect"); - close (sck); - unlink (item->local.sun_path); - return; + sstrncpy (buffer, "get ", sizeof (buffer)); + status = strjoin (&buffer[4], sizeof (buffer) - strlen ("get "), + li->fields, li->fields_num, + /* seperator = */ " "); + if (status < 0) + { + ERROR ("powerdns plugin: strjoin failed."); + return (-1); + } } - send_buffer = strdup (item->command == NULL ? COMMAND_RECURSOR : item->command); - if (send_buffer == NULL) + buffer[sizeof (buffer) - 1] = 0; + li->command = strdup (buffer); + if (li->command == NULL) { - FUNC_ERROR ("strdup"); - close (sck); - unlink (item->local.sun_path); - return; + ERROR ("powerdns plugin: strdup failed."); + return (-1); } - if (io((io_func*)&send, sck, send_buffer, strlen (send_buffer)) < strlen (send_buffer)) + return (0); +} /* }}} int powerdns_update_recursor_command */ + +static int powerdns_read_recursor (list_item_t *item) /* {{{ */ +{ + char *buffer = NULL; + size_t buffer_size = 0; + int status; + + char *dummy; + + char *keys_list; + char *key; + char *key_saveptr; + char *value; + char *value_saveptr; + + if (item->command == NULL) { - FUNC_ERROR ("send"); - close (sck); - unlink (item->local.sun_path); - free (send_buffer); - return; + status = powerdns_update_recursor_command (item); + if (status != 0) + { + ERROR ("powerdns plugin: powerdns_update_recursor_command failed."); + return (-1); + } + + DEBUG ("powerdns plugin: powerdns_read_recursor: item->command = %s;", + item->command); } + assert (item->command != NULL); - recv_buffer = malloc (BUFFER_SIZE + 1); - if (recv_buffer == NULL) + status = powerdns_get_data (item, &buffer, &buffer_size); + if (status != 0) { - FUNC_ERROR ("malloc"); - close (sck); - unlink (item->local.sun_path); - free (send_buffer); - return; + ERROR ("powerdns plugin: powerdns_get_data failed."); + return (-1); } - bytes = recv (sck, recv_buffer, BUFFER_SIZE, 0); - if (bytes < 1) { - FUNC_ERROR ("recv"); - close (sck); - unlink (item->local.sun_path); - free (send_buffer); - free (recv_buffer); - return; + keys_list = strdup (item->command); + if (keys_list == NULL) + { + FUNC_ERROR ("strdup"); + sfree (buffer); + return (-1); } - recv_buffer[bytes]='\0'; - close (sck); - unlink (item->local.sun_path); + key_saveptr = NULL; + value_saveptr = NULL; - for( name_token = strtok_r (send_buffer, delims, &name_pos), - name_token = strtok_r (NULL, delims, &name_pos), - value_token = strtok_r (recv_buffer, delims, &value_pos); - name_token != NULL && value_token != NULL; - name_token = strtok_r (NULL, delims, &name_pos), - value_token = strtok_r (NULL, delims, &value_pos) ) - submit (item->instance, name_token, value_token); + /* Skip the `get' at the beginning */ + strtok_r (keys_list, " \t", &key_saveptr); - free (send_buffer); - free (recv_buffer); - return; + dummy = buffer; + while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL) + { + dummy = NULL; -} /* static void powerdns_read_recursor */ + key = strtok_r (NULL, " \t", &key_saveptr); + if (key == NULL) + break; -static int powerdns_term() { - llentry_t *e_this; - llentry_t *e_next; - list_item_t *item; + submit (item->instance, key, value); + } /* while (strtok_r) */ - if (list != NULL) + sfree (buffer); + sfree (keys_list); + + return (0); +} /* }}} int powerdns_read_recursor */ + +static int powerdns_config_add_string (const char *name, /* {{{ */ + char **dest, + oconfig_item_t *ci) +{ + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - for (e_this = llist_head(list); e_this != NULL; e_this = e_next) - { - item = e_this->value; - free (item->instance); + WARNING ("powerdns plugin: `%s' needs exactly one string argument.", + name); + return (-1); + } - if (item->command != COMMAND_SERVER && - item->command != COMMAND_RECURSOR) - free (item->command); + sfree (*dest); + *dest = strdup (ci->values[0].value.string); + if (*dest == NULL) + return (-1); - free (item); + return (0); +} /* }}} int powerdns_config_add_string */ + +static int powerdns_config_add_collect (list_item_t *li, /* {{{ */ + oconfig_item_t *ci) +{ + int i; + char **temp; - e_next = e_this->next; + if (ci->values_num < 1) + { + WARNING ("powerdns plugin: The `Collect' option needs " + "at least one argument."); + return (-1); + } + + for (i = 0; i < ci->values_num; i++) + if (ci->values[i].type != OCONFIG_TYPE_STRING) + { + WARNING ("powerdns plugin: Only string arguments are allowed to " + "the `Collect' option."); + return (-1); } - llist_destroy (list); - list = NULL; + temp = (char **) realloc (li->fields, + sizeof (char *) * (li->fields_num + ci->values_num)); + if (temp == NULL) + { + WARNING ("powerdns plugin: realloc failed."); + return (-1); + } + li->fields = temp; + + for (i = 0; i < ci->values_num; i++) + { + li->fields[li->fields_num] = strdup (ci->values[i].value.string); + if (li->fields[li->fields_num] == NULL) + { + WARNING ("powerdns plugin: strdup failed."); + continue; + } + li->fields_num++; } + /* Invalidate a previously computed command */ + sfree (li->command); + return (0); -} /* static int powerdns_term */ +} /* }}} int powerdns_config_add_collect */ -static int powerdns_config (oconfig_item_t *ci) +static int powerdns_config_add_server (oconfig_item_t *ci) /* {{{ */ { - oconfig_item_t *gchild; - int gchildren; - - oconfig_item_t *child = ci->children; - int children = ci->children_num; + char *socket_temp; - llentry_t *entry; list_item_t *item; + int status; + int i; - if (list == NULL && (list = llist_create()) == NULL ) + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { - ERROR ("powerdns plugin: `llist_create' failed."); - return 1; - } + WARNING ("powerdns plugin: `%s' needs exactly one string argument.", + ci->key); + return (-1); + } - for (; children; --children, ++child) + item = (list_item_t *) malloc (sizeof (list_item_t)); + if (item == NULL) { - item = malloc (sizeof (list_item_t)); - if (item == NULL) - { - ERROR ("powerdns plugin: `malloc' failed."); - return 1; - } + ERROR ("powerdns plugin: malloc failed."); + return (-1); + } + memset (item, '\0', sizeof (list_item_t)); - if (strcmp (child->key, "Server") == 0) - { - item->func = (item_func*)&powerdns_read_server; - item->command = COMMAND_SERVER; - } - else if (strcmp (child->key, "Recursor") == 0) - { - item->func = (item_func*)&powerdns_read_recursor; - item->command = COMMAND_RECURSOR; - } + item->instance = strdup (ci->values[0].value.string); + if (item->instance == NULL) + { + ERROR ("powerdns plugin: strdup failed."); + sfree (item); + return (-1); + } + + /* + * Set default values for the members of list_item_t + */ + if (strcasecmp ("Server", ci->key) == 0) + { + item->server_type = SRV_AUTHORITATIVE; + item->func = powerdns_read_server; + item->socktype = SOCK_STREAM; + socket_temp = strdup (SERVER_SOCKET); + } + else if (strcasecmp ("Recursor", ci->key) == 0) + { + item->server_type = SRV_RECURSOR; + item->func = powerdns_read_recursor; + item->socktype = SOCK_DGRAM; + socket_temp = strdup (RECURSOR_SOCKET); + } + else + { + /* We must never get here.. */ + assert (0); + return (-1); + } + + status = 0; + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; + + if (strcasecmp ("Collect", option->key) == 0) + status = powerdns_config_add_collect (item, option); + else if (strcasecmp ("Socket", option->key) == 0) + status = powerdns_config_add_string ("Socket", &socket_temp, option); else { - WARNING ("powerdns plugin: Ignoring unknown" - " config option `%s'.", child->key); - free (item); - continue; + ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key); + status = -1; } - if ((child->values_num != 1) || - (child->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("powerdns plugin: `%s' needs exactly" - " one string argument.", child->key); - free (item); - continue; - } + if (status != 0) + break; + } - if (llist_search (list, child->values[0].value.string) != NULL) - { - ERROR ("powerdns plugin: multiple instances for %s", - child->values[0].value.string); - free (item); - return 1; - } + while (status == 0) + { + llentry_t *e; - item->instance = strdup (child->values[0].value.string); - if (item->instance == NULL) + if (socket_temp == NULL) { - ERROR ("powerdns plugin: `strdup' failed."); - free (item); - return 1; + ERROR ("powerdns plugin: socket_temp == NULL."); + status = -1; + break; } - entry = llentry_create (item->instance, item); - if (entry == NULL) + item->sockaddr.sun_family = AF_UNIX; + sstrncpy (item->sockaddr.sun_path, socket_temp, + sizeof (item->sockaddr.sun_path)); + + e = llentry_create (item->instance, item); + if (e == NULL) { - ERROR ("powerdns plugin: `llentry_create' failed."); - free (item->instance); - free (item); - return 1; + ERROR ("powerdns plugin: llentry_create failed."); + status = -1; + break; } + llist_append (list, e); - item->remote.sun_family = ~AF_UNIX; + break; + } + + if (status != 0) + { + sfree (item); + return (-1); + } + + DEBUG ("powerdns plugin: Add server: instance = %s;", item->instance); + + return (0); +} /* }}} int powerdns_config_add_server */ - gchild = child->children; - gchildren = child->children_num; +static int powerdns_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + DEBUG ("powerdns plugin: powerdns_config (ci = %p);", (void *) ci); + + if (list == NULL) + { + list = llist_create (); - for (; gchildren; --gchildren, ++gchild) + if (list == NULL) { - if (strcmp (gchild->key, "Socket") == 0) - { - if (gchild->values_num != 1 || - gchild->values[0].type != OCONFIG_TYPE_STRING) - { - WARNING ("powerdns plugin: config option `%s'" - " should have exactly one string value.", - gchild->key); - continue; - } - if (item->remote.sun_family == AF_UNIX) - { - WARNING ("powerdns plugin: ignoring extraneous" - " `%s' config option.", gchild->key); - continue; - } - item->remote.sun_family = item->local.sun_family = AF_UNIX; - strncpy (item->remote.sun_path, gchild->values[0].value.string, - sizeof (item->remote.sun_path)); - strncpy (item->local.sun_path, gchild->values[0].value.string, - sizeof (item->remote.sun_path)); - } - else if (strcmp (gchild->key, "Command") == 0) - { - if (gchild->values_num != 1 - || gchild->values[0].type != OCONFIG_TYPE_NUMBER) - { - WARNING ("powerdns plugin: config option `%s'" - " should have exactly one string value.", - gchild->key); - continue; - } - if (item->command != COMMAND_RECURSOR && - item->command != COMMAND_SERVER) - { - WARNING ("powerdns plugin: ignoring extraneous" - " `%s' config option.", gchild->key); - continue; - } - item->command = strdup (gchild->values[0].value.string); - if (item->command == NULL) - { - ERROR ("powerdns plugin: `strdup' failed."); - llentry_destroy (entry); - free (item->instance); - free (item); - return 1; - } - } - else - { - WARNING ("powerdns plugin: Ignoring unknown config option" - " `%s'.", gchild->key); - continue; - } - - if (gchild->children_num) - { - WARNING ("powerdns plugin: config option `%s' should not" - " have children.", gchild->key); - } + ERROR ("powerdns plugin: `llist_create' failed."); + return (-1); } + } + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; - if (item->remote.sun_family != AF_UNIX) + if ((strcasecmp ("Server", option->key) == 0) + || (strcasecmp ("Recursor", option->key) == 0)) + powerdns_config_add_server (option); + else if (strcasecmp ("LocalSocket", option->key) == 0) + { + char *temp = strdup (option->key); + if (temp == NULL) + return (1); + sfree (local_sockpath); + local_sockpath = temp; + } + else { - if (item->func == (item_func*)&powerdns_read_server) - { - item->remote.sun_family = item->local.sun_family = AF_UNIX; - strncpy (item->remote.sun_path, "/var/run/pdns.controlsocket", - sizeof (item->remote.sun_path)); - strncpy (item->local.sun_path, "/var/run/pdns.controlsocket", - sizeof (item->remote.sun_path)); - } - else - { - item->remote.sun_family = item->local.sun_family = AF_UNIX; - strncpy (item->remote.sun_path, "/var/run/pdns_recursor.controlsocket", - sizeof (item->remote.sun_path)); - strncpy (item->local.sun_path, "/var/run/pdns_recursor.controlsocket", - sizeof (item->remote.sun_path)); - } + ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key); } + } /* for (i = 0; i < ci->children_num; i++) */ + + return (0); +} /* }}} int powerdns_config */ - llist_append (list, entry); +static int powerdns_read (void) +{ + llentry_t *e; + + for (e = llist_head (list); e != NULL; e = e->next) + { + list_item_t *item = e->value; + item->func (item); } - return 0; -} /* static int powerdns_config */ + return (0); +} /* static int powerdns_read */ -static int powerdns_read(void) +static int powerdns_shutdown (void) { - llentry_t *e_this; - list_item_t *item; + llentry_t *e; + + if (list == NULL) + return (0); - for (e_this = llist_head(list); e_this != NULL; e_this = e_this->next) + for (e = llist_head (list); e != NULL; e = e->next) { - item = e_this->value; - item->func(item); + list_item_t *item = (list_item_t *) e->value; + e->value = NULL; + + sfree (item->instance); + sfree (item->command); + sfree (item); } + llist_destroy (list); + list = NULL; + return (0); -} /* static int powerdns_read */ +} /* static int powerdns_shutdown */ void module_register (void) { plugin_register_complex_config ("powerdns", powerdns_config); plugin_register_read ("powerdns", powerdns_read); - plugin_register_shutdown ("powerdns", powerdns_term ); + plugin_register_shutdown ("powerdns", powerdns_shutdown ); } /* void module_register */ -/* vim: set sw=2 sts=2 ts=8 : */ +/* vim: set sw=2 sts=2 ts=8 fdm=marker : */