322fc643551934bd519019ab6363ce729e4ab7c8
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 verplant.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/socket.h>
41 #include <sys/un.h>
42 #include <malloc.h>
44 #ifndef UNIX_PATH_MAX
45 # define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
46 #endif
47 #define FUNC_ERROR(func) ERROR ("powerdns plugin: `%s' failed\n", func)
49 #define SERVER_SOCKET "/var/run/pdns.controlsocket"
50 #define SERVER_COMMAND "SHOW *"
52 #define RECURSOR_SOCKET "/var/run/pdns_recursor.controlsocket"
53 #define RECURSOR_COMMAND "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"
55 struct list_item_s;
56 typedef struct list_item_s list_item_t;
58 struct list_item_s
59 {
60 int (*func) (list_item_t *item);
61 char *instance;
62 char *command;
63 struct sockaddr_un sockaddr;
64 int socktype;
65 };
67 static llist_t *list = NULL;
69 static void submit (const char *plugin_instance, const char *type, const char *value)
70 {
71 value_list_t vl = VALUE_LIST_INIT;
72 value_t values[1];
73 const data_set_t *ds;
74 float f;
75 long l;
77 ERROR ("powerdns plugin: submit: TODO: Translate the passed-in `key' to a reasonable type (and type_instance).");
79 ds = plugin_get_ds (type);
80 if (ds == NULL)
81 {
82 ERROR( "%s: DS %s not defined\n", "powerdns", type );
83 return;
84 }
86 errno = 0;
87 if (ds->ds->type == DS_TYPE_GAUGE)
88 {
89 f = atof(value);
90 if (errno != 0)
91 {
92 ERROR ("%s: atof failed (%s->%s)", "powerdns", type, value);
93 return;
94 }
95 else
96 {
97 values[0].gauge = f<0?-f:f;
98 }
99 }
100 else
101 {
102 l = atol(value);
103 if (errno != 0)
104 {
105 ERROR ("%s: atol failed (%s->%s)", "powerdns", type, value);
106 return;
107 }
108 else
109 {
110 values[0].counter = l < 0 ? -l : l;
111 }
112 }
114 vl.values = values;
115 vl.values_len = 1;
116 vl.time = time (NULL);
117 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
118 sstrncpy (vl.plugin, "powerdns", sizeof (vl.plugin));
119 sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
120 sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
122 plugin_dispatch_values (type, &vl);
123 } /* static void submit */
125 static int powerdns_get_data (list_item_t *item, char **ret_buffer,
126 size_t *ret_buffer_size)
127 {
128 int sd;
129 int status;
131 char temp[1024];
132 char *buffer = NULL;
133 size_t buffer_size = 0;
135 sd = socket (AF_UNIX, item->socktype, 0);
136 if (sd < 0)
137 {
138 FUNC_ERROR ("socket");
139 return (-1);
140 }
142 status = connect (sd, (struct sockaddr *) &item->sockaddr,
143 sizeof(item->sockaddr));
144 if (status != 0)
145 {
146 FUNC_ERROR ("connect");
147 close (sd);
148 return (-1);
149 }
151 status = send (sd, item->command, strlen (item->command), 0);
152 if (status < 0)
153 {
154 FUNC_ERROR ("send");
155 close (sd);
156 return (-1);
157 }
159 while (42)
160 {
161 char *buffer_new;
163 status = recv (sd, temp, sizeof (temp), 0);
164 if (status < 0)
165 {
166 FUNC_ERROR ("recv");
167 break;
168 }
169 else if (status == 0)
170 break;
172 buffer_new = (char *) realloc (buffer, buffer_size + status);
173 if (buffer_new == NULL)
174 {
175 FUNC_ERROR ("realloc");
176 status = -1;
177 break;
178 }
179 buffer = buffer_new;
181 memcpy (buffer + buffer_size, temp, status);
182 buffer_size += status;
183 }
184 close (sd);
185 sd = -1;
187 if (status < 0)
188 {
189 sfree (buffer);
190 }
191 else
192 {
193 *ret_buffer = buffer;
194 *ret_buffer_size = buffer_size;
195 }
197 return (status);
198 } /* int powerdns_get_data */
200 static int powerdns_read_server (list_item_t *item)
201 {
202 char *buffer = NULL;
203 size_t buffer_size = 0;
204 int status;
206 char *dummy;
207 char *saveptr;
209 char *key;
210 char *value;
212 status = powerdns_get_data (item, &buffer, &buffer_size);
213 if (status != 0)
214 return (-1);
216 dummy = buffer;
217 saveptr = NULL;
218 while ((key = strtok_r (dummy, ",", &saveptr)) != NULL)
219 {
220 dummy = NULL;
222 value = strchr (key, '=');
223 if (value == NULL)
224 break;
226 *value = '\0';
227 value++;
229 if (value[0] == '\0')
230 continue;
232 submit (item->instance, key, value);
233 } /* while (strtok_r) */
235 sfree (buffer);
237 return (0);
238 } /* int powerdns_read_server */
240 static int powerdns_read_recursor (list_item_t *item)
241 {
242 char *buffer = NULL;
243 size_t buffer_size = 0;
244 int status;
246 char *dummy;
248 char *keys_list;
249 char *key;
250 char *key_saveptr;
251 char *value;
252 char *value_saveptr;
254 status = powerdns_get_data (item, &buffer, &buffer_size);
255 if (status != 0)
256 return (-1);
258 keys_list = strdup (item->command);
259 if (keys_list == NULL)
260 {
261 FUNC_ERROR ("strdup");
262 sfree (buffer);
263 return (-1);
264 }
266 key_saveptr = NULL;
267 value_saveptr = NULL;
269 /* Skip the `get' at the beginning */
270 strtok_r (keys_list, " \t", &key_saveptr);
272 dummy = buffer;
273 while ((value = strtok_r (dummy, " \t\n\r", &value_saveptr)) != NULL)
274 {
275 dummy = NULL;
277 key = strtok_r (NULL, " \t", &key_saveptr);
278 if (key == NULL)
279 break;
281 submit (item->instance, key, value);
282 } /* while (strtok_r) */
284 sfree (buffer);
285 sfree (keys_list);
287 return (0);
288 } /* int powerdns_read_recursor */
290 static int powerdns_config_add_string (const char *name, char **dest,
291 oconfig_item_t *ci)
292 {
293 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
294 {
295 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
296 name);
297 return (-1);
298 }
300 sfree (*dest);
301 *dest = strdup (ci->values[0].value.string);
302 if (*dest == NULL)
303 return (-1);
305 return (0);
306 } /* int ctail_config_add_string */
308 static int powerdns_config_add_server (oconfig_item_t *ci)
309 {
310 char *socket_temp;
312 list_item_t *item;
313 int status;
314 int i;
316 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
317 {
318 WARNING ("powerdns plugin: `%s' needs exactly one string argument.",
319 ci->key);
320 return (-1);
321 }
323 item = (list_item_t *) malloc (sizeof (list_item_t));
324 if (item == NULL)
325 {
326 ERROR ("powerdns plugin: malloc failed.");
327 return (-1);
328 }
329 memset (item, '\0', sizeof (list_item_t));
331 item->instance = strdup (ci->values[0].value.string);
332 if (item->instance == NULL)
333 {
334 ERROR ("powerdns plugin: strdup failed.");
335 sfree (item);
336 return (-1);
337 }
339 /*
340 * Set default values for the members of list_item_t
341 */
342 if (strcasecmp ("Server", ci->key) == 0)
343 {
344 item->func = powerdns_read_server;
345 item->command = strdup (SERVER_COMMAND);
346 item->socktype = SOCK_STREAM;
347 socket_temp = strdup (SERVER_SOCKET);
348 }
349 else if (strcasecmp ("Recursor", ci->key) == 0)
350 {
351 item->func = powerdns_read_recursor;
352 item->command = strdup (RECURSOR_COMMAND);
353 item->socktype = SOCK_DGRAM;
354 socket_temp = strdup (RECURSOR_SOCKET);
355 }
357 status = 0;
358 for (i = 0; i < ci->children_num; i++)
359 {
360 oconfig_item_t *option = ci->children + i;
362 if (strcasecmp ("Command", option->key) == 0)
363 status = powerdns_config_add_string ("Command", &item->command, option);
364 else if (strcasecmp ("Socket", option->key) == 0)
365 status = powerdns_config_add_string ("Socket", &socket_temp, option);
366 else
367 {
368 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
369 status = -1;
370 }
372 if (status != 0)
373 break;
374 }
376 while (status == 0)
377 {
378 llentry_t *e;
380 if (socket_temp == NULL)
381 {
382 ERROR ("powerdns plugin: socket_temp == NULL.");
383 status = -1;
384 break;
385 }
387 if (item->command == NULL)
388 {
389 ERROR ("powerdns plugin: item->command == NULL.");
390 status = -1;
391 break;
392 }
394 item->sockaddr.sun_family = AF_UNIX;
395 sstrncpy (item->sockaddr.sun_path, socket_temp, UNIX_PATH_MAX);
397 e = llentry_create (item->instance, item);
398 if (e == NULL)
399 {
400 ERROR ("powerdns plugin: llentry_create failed.");
401 status = -1;
402 break;
403 }
404 llist_append (list, e);
406 break;
407 }
409 if (status != 0)
410 {
411 sfree (item);
412 return (-1);
413 }
415 return (0);
416 } /* int powerdns_config_add_server */
418 static int powerdns_config (oconfig_item_t *ci)
419 {
420 int i;
422 if (list == NULL)
423 {
424 list = llist_create ();
426 if (list == NULL)
427 {
428 ERROR ("powerdns plugin: `llist_create' failed.");
429 return (-1);
430 }
431 }
433 for (i = 0; i < ci->children_num; i++)
434 {
435 oconfig_item_t *option = ci->children + i;
437 if ((strcasecmp ("Server", ci->key) == 0)
438 || (strcasecmp ("Recursor", ci->key) == 0))
439 powerdns_config_add_server (option);
440 else
441 {
442 ERROR ("powerdns plugin: Option `%s' not allowed here.", option->key);
443 }
444 } /* for (i = 0; i < ci->children_num; i++) */
446 return (0);
447 } /* int powerdns_config */
449 static int powerdns_read (void)
450 {
451 llentry_t *e;
453 for (e = llist_head (list); e != NULL; e = e->next)
454 {
455 list_item_t *item = e->value;
456 item->func (item);
457 }
459 return (0);
460 } /* static int powerdns_read */
462 static int powerdns_shutdown (void)
463 {
464 llentry_t *e;
466 if (list == NULL)
467 return (0);
469 for (e = llist_head (list); e != NULL; e = e->next)
470 {
471 list_item_t *item = (list_item_t *) e->value;
472 e->value = NULL;
474 sfree (item->instance);
475 sfree (item->command);
476 sfree (item);
477 }
479 llist_destroy (list);
480 list = NULL;
482 return (0);
483 } /* static int powerdns_shutdown */
485 void module_register (void)
486 {
487 plugin_register_complex_config ("powerdns", powerdns_config);
488 plugin_register_read ("powerdns", powerdns_read);
489 plugin_register_shutdown ("powerdns", powerdns_shutdown );
490 } /* void module_register */
492 /* vim: set sw=2 sts=2 ts=8 : */