2 * collectd - src/powerdns.c
3 * Copyright (C) 2007-2008 C-Ware, Inc.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Luke Heberling <lukeh at c-ware.com>
22 * Queries a PowerDNS control socket for statistics
28 #include "configfile.h"
29 #include "utils_llist.h"
37 #include <sys/types.h>
38 #include <sys/socket.h>
42 #define BUFFER_SIZE 1000
44 #define FUNC_ERROR(func) ERROR ("%s: `%s' failed\n", "powerdns", func)
46 #define COMMAND_SERVER "SHOW *"
47 #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"
49 typedef void item_func (void*);
50 typedef ssize_t io_func (int, void*, size_t, int);
57 struct sockaddr_un remote;
58 struct sockaddr_un local;
60 typedef struct list_item_s list_item_t;
62 static llist_t *list = NULL;
64 static void submit (const char *instance, const char *name, const char *value)
66 value_list_t vl = VALUE_LIST_INIT;
72 ds = plugin_get_ds (name);
75 ERROR( "%s: DS %s not defined\n", "powerdns", name );
80 if (ds->ds->type == DS_TYPE_GAUGE)
85 ERROR ("%s: atof failed (%s->%s)", "powerdns", name, value);
90 values[0].gauge = f<0?-f:f;
98 ERROR ("%s: atol failed (%s->%s)", "powerdns", name, value);
103 values[0].counter = l < 0 ? -l : l;
109 vl.time = time (NULL);
110 strncpy (vl.host, hostname_g, sizeof (vl.host));
111 strncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
112 strncpy (vl.type_instance, "", sizeof (vl.type_instance));
113 strncpy (vl.plugin_instance,instance, sizeof (vl.plugin_instance));
115 plugin_dispatch_values (name, &vl);
116 } /* static void submit */
118 static int io (io_func *func, int fd, char* buf, int buflen)
122 for (; buflen > 0 && (cc = func (fd, buf, buflen, 0)) > 0;
123 buf += cc, bytes += cc, buflen -= cc)
127 } /* static int io */
129 static void powerdns_read_server (list_item_t *item)
133 char *name_token,*value_token,*pos;
137 if ((sck = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
139 FUNC_ERROR ("socket");
143 if (connect( sck,(struct sockaddr *) &item->remote,
144 sizeof(item->remote)) == -1)
146 FUNC_ERROR( "connect" );
151 buffer = malloc (BUFFER_SIZE + 1);
154 FUNC_ERROR ("malloc");
159 item->command == NULL ? COMMAND_SERVER : item->command,
161 buffer[BUFFER_SIZE] = '\0';
163 if (io ((io_func*) &send, sck, buffer, strlen(buffer)) < strlen(buffer))
171 bytes = io ((io_func*) &recv, sck, buffer, BUFFER_SIZE);
182 buffer[bytes] = '\0';
184 for (name_token = strtok_r (buffer, delims, &pos),
185 value_token = strtok_r (NULL, delims, &pos);
186 name_token != NULL && value_token != NULL;
187 name_token = strtok_r (NULL, delims, &pos ),
188 value_token = strtok_r (NULL, delims, &pos) )
189 submit (item->instance, name_token, value_token);
193 } /* static void powerdns_read_server */
195 static void powerdns_read_recursor (list_item_t *item) {
198 char *name_token, *name_pos;
199 char *value_token, *value_pos;
202 char *delims = " \n";
204 for (ptr = item->local.sun_path
205 + strlen(item->local.sun_path) - 1;
206 ptr > item->local.sun_path && *ptr != '/'; --ptr)
209 if (ptr <= item->local.sun_path)
211 ERROR("%s: Bad path %s\n", "powerdns", item->local.sun_path);
216 strncat (item->local.sun_path, "/lsockXXXXXX",
217 sizeof (item->local.sun_path) - strlen (item->local.sun_path));
219 if ((sck = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
220 FUNC_ERROR ("socket");
225 if (setsockopt (sck, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) < 0)
227 FUNC_ERROR ("setsockopt");
232 if ((tmp=mkstemp(item->local.sun_path))< 0)
234 FUNC_ERROR ("mkstemp");
240 if (unlink(item->local.sun_path) < 0 && errno != ENOENT)
242 FUNC_ERROR ("unlink");
247 if (bind(sck, (struct sockaddr*)&item->local, sizeof(item->local)) < 0)
251 unlink (item->local.sun_path);
255 if (chmod(item->local.sun_path,0666) < 0)
257 FUNC_ERROR ("chmod");
259 unlink (item->local.sun_path);
263 if (connect (sck,(struct sockaddr *) &item->remote, sizeof(item->remote)) == -1)
265 FUNC_ERROR ("connect");
267 unlink (item->local.sun_path);
271 send_buffer = strdup (item->command == NULL ? COMMAND_RECURSOR : item->command);
272 if (send_buffer == NULL)
274 FUNC_ERROR ("strdup");
276 unlink (item->local.sun_path);
280 if (io((io_func*)&send, sck, send_buffer, strlen (send_buffer)) < strlen (send_buffer))
284 unlink (item->local.sun_path);
289 recv_buffer = malloc (BUFFER_SIZE + 1);
290 if (recv_buffer == NULL)
292 FUNC_ERROR ("malloc");
294 unlink (item->local.sun_path);
299 bytes = recv (sck, recv_buffer, BUFFER_SIZE, 0);
303 unlink (item->local.sun_path);
308 recv_buffer[bytes]='\0';
311 unlink (item->local.sun_path);
313 for( name_token = strtok_r (send_buffer, delims, &name_pos),
314 name_token = strtok_r (NULL, delims, &name_pos),
315 value_token = strtok_r (recv_buffer, delims, &value_pos);
316 name_token != NULL && value_token != NULL;
317 name_token = strtok_r (NULL, delims, &name_pos),
318 value_token = strtok_r (NULL, delims, &value_pos) )
319 submit (item->instance, name_token, value_token);
325 } /* static void powerdns_read_recursor */
327 static int powerdns_term() {
334 for (e_this = llist_head(list); e_this != NULL; e_this = e_next)
336 item = e_this->value;
337 free (item->instance);
339 if (item->command != COMMAND_SERVER &&
340 item->command != COMMAND_RECURSOR)
341 free (item->command);
345 e_next = e_this->next;
348 llist_destroy (list);
353 } /* static int powerdns_term */
355 static int powerdns_config (oconfig_item_t *ci)
357 oconfig_item_t *gchild;
360 oconfig_item_t *child = ci->children;
361 int children = ci->children_num;
366 if (list == NULL && (list = llist_create()) == NULL )
368 ERROR ("powerdns plugin: `llist_create' failed.");
372 for (; children; --children, ++child)
374 item = malloc (sizeof (list_item_t));
377 ERROR ("powerdns plugin: `malloc' failed.");
381 if (strcmp (child->key, "Server") == 0)
383 item->func = (item_func*)&powerdns_read_server;
384 item->command = COMMAND_SERVER;
386 else if (strcmp (child->key, "Recursor") == 0)
388 item->func = (item_func*)&powerdns_read_recursor;
389 item->command = COMMAND_RECURSOR;
393 WARNING ("powerdns plugin: Ignoring unknown"
394 " config option `%s'.", child->key);
399 if ((child->values_num != 1) ||
400 (child->values[0].type != OCONFIG_TYPE_STRING))
402 WARNING ("powerdns plugin: `%s' needs exactly"
403 " one string argument.", child->key);
408 if (llist_search (list, child->values[0].value.string) != NULL)
410 ERROR ("powerdns plugin: multiple instances for %s",
411 child->values[0].value.string);
416 item->instance = strdup (child->values[0].value.string);
417 if (item->instance == NULL)
419 ERROR ("powerdns plugin: `strdup' failed.");
424 entry = llentry_create (item->instance, item);
427 ERROR ("powerdns plugin: `llentry_create' failed.");
428 free (item->instance);
433 item->remote.sun_family = ~AF_UNIX;
435 gchild = child->children;
436 gchildren = child->children_num;
438 for (; gchildren; --gchildren, ++gchild)
440 if (strcmp (gchild->key, "Socket") == 0)
442 if (gchild->values_num != 1 ||
443 gchild->values[0].type != OCONFIG_TYPE_STRING)
445 WARNING ("powerdns plugin: config option `%s'"
446 " should have exactly one string value.",
450 if (item->remote.sun_family == AF_UNIX)
452 WARNING ("powerdns plugin: ignoring extraneous"
453 " `%s' config option.", gchild->key);
456 item->remote.sun_family = item->local.sun_family = AF_UNIX;
457 strncpy (item->remote.sun_path, gchild->values[0].value.string,
458 sizeof (item->remote.sun_path));
459 strncpy (item->local.sun_path, gchild->values[0].value.string,
460 sizeof (item->remote.sun_path));
462 else if (strcmp (gchild->key, "Command") == 0)
464 if (gchild->values_num != 1
465 || gchild->values[0].type != OCONFIG_TYPE_NUMBER)
467 WARNING ("powerdns plugin: config option `%s'"
468 " should have exactly one string value.",
472 if (item->command != COMMAND_RECURSOR &&
473 item->command != COMMAND_SERVER)
475 WARNING ("powerdns plugin: ignoring extraneous"
476 " `%s' config option.", gchild->key);
479 item->command = strdup (gchild->values[0].value.string);
480 if (item->command == NULL)
482 ERROR ("powerdns plugin: `strdup' failed.");
483 llentry_destroy (entry);
484 free (item->instance);
491 WARNING ("powerdns plugin: Ignoring unknown config option"
492 " `%s'.", gchild->key);
496 if (gchild->children_num)
498 WARNING ("powerdns plugin: config option `%s' should not"
499 " have children.", gchild->key);
504 if (item->remote.sun_family != AF_UNIX)
506 if (item->func == (item_func*)&powerdns_read_server)
508 item->remote.sun_family = item->local.sun_family = AF_UNIX;
509 strncpy (item->remote.sun_path, "/var/run/pdns.controlsocket",
510 sizeof (item->remote.sun_path));
511 strncpy (item->local.sun_path, "/var/run/pdns.controlsocket",
512 sizeof (item->remote.sun_path));
516 item->remote.sun_family = item->local.sun_family = AF_UNIX;
517 strncpy (item->remote.sun_path, "/var/run/pdns_recursor.controlsocket",
518 sizeof (item->remote.sun_path));
519 strncpy (item->local.sun_path, "/var/run/pdns_recursor.controlsocket",
520 sizeof (item->remote.sun_path));
524 llist_append (list, entry);
528 } /* static int powerdns_config */
530 static int powerdns_read(void)
535 for (e_this = llist_head(list); e_this != NULL; e_this = e_this->next)
537 item = e_this->value;
542 } /* static int powerdns_read */
544 void module_register (void)
546 plugin_register_complex_config ("powerdns", powerdns_config);
547 plugin_register_read ("powerdns", powerdns_read);
548 plugin_register_shutdown ("powerdns", powerdns_term );
549 } /* void module_register */
551 /* vim: set sw=2 sts=2 ts=8 : */