2 * collectd - src/olsrd.c
3 * Copyright (C) 2009 Florian octo Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian octo Forster <octo at collectd.org>
33 #include <netinet/in.h>
34 #include <netinet/tcp.h>
35 #include <sys/types.h>
37 #define OLSRD_DEFAULT_NODE "localhost"
38 #define OLSRD_DEFAULT_SERVICE "2006"
40 static const char *config_keys[] = {"Host", "Port", "CollectLinks",
41 "CollectRoutes", "CollectTopology"};
42 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
44 static char *config_node = NULL;
45 static char *config_service = NULL;
47 #define OLSRD_WANT_NOT 0
48 #define OLSRD_WANT_SUMMARY 1
49 #define OLSRD_WANT_DETAIL 2
50 static int config_want_links = OLSRD_WANT_DETAIL;
51 static int config_want_routes = OLSRD_WANT_SUMMARY;
52 static int config_want_topology = OLSRD_WANT_SUMMARY;
54 static const char *olsrd_get_node(void) /* {{{ */
56 if (config_node != NULL)
58 return OLSRD_DEFAULT_NODE;
59 } /* }}} const char *olsrd_get_node */
61 static const char *olsrd_get_service(void) /* {{{ */
63 if (config_service != NULL)
64 return config_service;
65 return OLSRD_DEFAULT_SERVICE;
66 } /* }}} const char *olsrd_get_service */
68 static void olsrd_set_node(const char *node) /* {{{ */
77 } /* }}} void olsrd_set_node */
79 static void olsrd_set_service(const char *service) /* {{{ */
84 tmp = strdup(service);
88 } /* }}} void olsrd_set_service */
90 static void olsrd_set_detail(int *varptr, const char *detail, /* {{{ */
92 if (strcasecmp("No", detail) == 0)
93 *varptr = OLSRD_WANT_NOT;
94 else if (strcasecmp("Summary", detail) == 0)
95 *varptr = OLSRD_WANT_SUMMARY;
96 else if (strcasecmp("Detail", detail) == 0)
97 *varptr = OLSRD_WANT_DETAIL;
99 ERROR("olsrd plugin: Invalid argument given to the `%s' configuration "
100 "option: `%s'. Expected: `No', `Summary', or `Detail'.",
103 } /* }}} void olsrd_set_detail */
105 /* Strip trailing newline characters. Returns length of string. */
106 static size_t strchomp(char *buffer) /* {{{ */
110 buffer_len = strlen(buffer);
111 while ((buffer_len > 0) && ((buffer[buffer_len - 1] == '\r') ||
112 (buffer[buffer_len - 1] == '\n'))) {
114 buffer[buffer_len] = 0;
118 } /* }}} size_t strchomp */
120 static size_t strtabsplit(char *string, char **fields, size_t size) /* {{{ */
129 while ((fields[i] = strtok_r(ptr, " \t\r\n", &saveptr)) != NULL) {
138 } /* }}} size_t strtabsplit */
140 static FILE *olsrd_connect(void) /* {{{ */
142 struct addrinfo *ai_list;
147 struct addrinfo ai_hints = {.ai_family = AF_UNSPEC,
148 .ai_flags = AI_ADDRCONFIG,
149 .ai_protocol = IPPROTO_TCP,
150 .ai_socktype = SOCK_STREAM};
153 getaddrinfo(olsrd_get_node(), olsrd_get_service(), &ai_hints, &ai_list);
154 if (ai_return != 0) {
155 ERROR("olsrd plugin: getaddrinfo (%s, %s) failed: %s", olsrd_get_node(),
156 olsrd_get_service(), gai_strerror(ai_return));
161 for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL;
162 ai_ptr = ai_ptr->ai_next) {
166 fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
168 ERROR("olsrd plugin: socket failed: %s", STRERRNO);
172 status = connect(fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
174 ERROR("olsrd plugin: connect failed: %s", STRERRNO);
179 fh = fdopen(fd, "r+");
181 ERROR("olsrd plugin: fdopen failed.");
189 freeaddrinfo(ai_list);
192 } /* }}} FILE *olsrd_connect */
194 __attribute__((nonnull(2))) static void
195 olsrd_submit(const char *plugin_instance, /* {{{ */
196 const char *type, const char *type_instance, gauge_t value) {
197 value_list_t vl = VALUE_LIST_INIT;
199 vl.values = &(value_t){.gauge = value};
202 sstrncpy(vl.plugin, "olsrd", sizeof(vl.plugin));
203 if (plugin_instance != NULL)
204 sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance));
205 sstrncpy(vl.type, type, sizeof(vl.type));
206 if (type_instance != NULL)
207 sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance));
209 plugin_dispatch_values(&vl);
210 } /* }}} void olsrd_submit */
212 static int olsrd_cb_ignore(int lineno, /* {{{ */
213 size_t fields_num, char **fields) {
215 } /* }}} int olsrd_cb_ignore */
217 static int olsrd_cb_links(int lineno, /* {{{ */
218 size_t fields_num, char **fields) {
227 static uint32_t links_num;
228 static double lq_sum;
229 static uint32_t lq_num;
230 static double nlq_sum;
231 static uint32_t nlq_num;
238 if (config_want_links == OLSRD_WANT_NOT)
241 /* Special handling of the first line. */
252 /* Special handling of the last line. */
253 if (fields_num == 0) {
254 DEBUG("olsrd plugin: Number of links: %" PRIu32, links_num);
255 olsrd_submit(/* p.-inst = */ "links", /* type = */ "links",
256 /* t.-inst = */ NULL, (gauge_t)links_num);
260 lq = lq_sum / ((double)lq_num);
261 DEBUG("olsrd plugin: Average LQ: %g", lq);
262 olsrd_submit(/* p.-inst = */ "links", /* type = */ "signal_quality",
267 nlq = nlq_sum / ((double)nlq_num);
268 DEBUG("olsrd plugin: Average NLQ: %g", nlq);
269 olsrd_submit(/* p.-inst = */ "links", /* type = */ "signal_quality",
282 lq = strtod(fields[3], &endptr);
283 if ((errno != 0) || (endptr == fields[3])) {
284 ERROR("olsrd plugin: Cannot parse link quality: %s", fields[3]);
291 if (config_want_links == OLSRD_WANT_DETAIL) {
292 char type_instance[DATA_MAX_NAME_LEN];
294 snprintf(type_instance, sizeof(type_instance), "%s-%s-lq", fields[0],
297 DEBUG("olsrd plugin: links: type_instance = %s; lq = %g;", type_instance,
299 olsrd_submit(/* p.-inst = */ "links", /* type = */ "signal_quality",
306 nlq = strtod(fields[4], &endptr);
307 if ((errno != 0) || (endptr == fields[4])) {
308 ERROR("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
315 if (config_want_links == OLSRD_WANT_DETAIL) {
316 char type_instance[DATA_MAX_NAME_LEN];
318 snprintf(type_instance, sizeof(type_instance), "%s-%s-rx", fields[0],
321 DEBUG("olsrd plugin: links: type_instance = %s; nlq = %g;", type_instance,
323 olsrd_submit(/* p.-inst = */ "links", /* type = */ "signal_quality",
329 } /* }}} int olsrd_cb_links */
331 static int olsrd_cb_routes(int lineno, /* {{{ */
332 size_t fields_num, char **fields) {
340 static uint32_t routes_num;
341 static uint32_t metric_sum;
342 static uint32_t metric_num;
343 static double etx_sum;
344 static uint32_t etx_num;
350 if (config_want_routes == OLSRD_WANT_NOT)
353 /* Special handling of the first line */
364 /* Special handling after the last line */
365 if (fields_num == 0) {
368 DEBUG("olsrd plugin: Number of routes: %" PRIu32, routes_num);
369 olsrd_submit(/* p.-inst = */ "routes", /* type = */ "routes",
370 /* t.-inst = */ NULL, (gauge_t)routes_num);
374 metric_avg = ((double)metric_sum) / ((double)metric_num);
375 DEBUG("olsrd plugin: Average metric: %g", metric_avg);
376 olsrd_submit(/* p.-inst = */ "routes", /* type = */ "route_metric",
377 "average", metric_avg);
381 etx = etx_sum / ((double)etx_sum);
382 DEBUG("olsrd plugin: Average ETX: %g", etx);
383 olsrd_submit(/* p.-inst = */ "routes", /* type = */ "route_etx", "average",
396 metric = (uint32_t)strtoul(fields[2], &endptr, 0);
397 if ((errno != 0) || (endptr == fields[2])) {
398 ERROR("olsrd plugin: Unable to parse metric: %s", fields[2]);
401 metric_sum += metric;
403 if (config_want_routes == OLSRD_WANT_DETAIL) {
404 DEBUG("olsrd plugin: destination = %s; metric = %" PRIu32 ";", fields[0],
406 olsrd_submit(/* p.-inst = */ "routes", /* type = */ "route_metric",
407 /* t.-inst = */ fields[0], (gauge_t)metric);
413 etx = strtod(fields[3], &endptr);
414 if ((errno != 0) || (endptr == fields[3])) {
415 ERROR("olsrd plugin: Unable to parse ETX: %s", fields[3]);
422 if (config_want_routes == OLSRD_WANT_DETAIL) {
423 DEBUG("olsrd plugin: destination = %s; etx = %g;", fields[0], etx);
424 olsrd_submit(/* p.-inst = */ "routes", /* type = */ "route_etx",
425 /* t.-inst = */ fields[0], etx);
430 } /* }}} int olsrd_cb_routes */
432 static int olsrd_cb_topology(int lineno, /* {{{ */
433 size_t fields_num, char **fields) {
441 static double lq_sum;
442 static uint32_t lq_num;
444 static uint32_t links_num;
449 if (config_want_topology == OLSRD_WANT_NOT)
452 /* Special handling of the first line */
461 /* Special handling after the last line */
462 if (fields_num == 0) {
463 DEBUG("olsrd plugin: topology: Number of links: %" PRIu32, links_num);
464 olsrd_submit(/* p.-inst = */ "topology", /* type = */ "links",
465 /* t.-inst = */ NULL, (gauge_t)links_num);
469 lq = lq_sum / ((double)lq_sum);
470 DEBUG("olsrd plugin: topology: Average link quality: %g", lq);
471 olsrd_submit(/* p.-inst = */ "topology", /* type = */ "signal_quality",
472 /* t.-inst = */ "average", lq);
484 lq = strtod(fields[2], &endptr);
485 if ((errno != 0) || (endptr == fields[2])) {
486 ERROR("olsrd plugin: Unable to parse LQ: %s", fields[2]);
493 if (config_want_topology == OLSRD_WANT_DETAIL) {
494 char type_instance[DATA_MAX_NAME_LEN] = {0};
496 snprintf(type_instance, sizeof(type_instance), "%s-%s-lq", fields[0],
498 DEBUG("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
499 olsrd_submit(/* p.-inst = */ "topology", /* type = */ "signal_quality",
504 if (config_want_topology == OLSRD_WANT_DETAIL) {
509 nlq = strtod(fields[3], &endptr);
510 if ((errno != 0) || (endptr == fields[3])) {
511 ERROR("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
513 char type_instance[DATA_MAX_NAME_LEN] = {0};
515 snprintf(type_instance, sizeof(type_instance), "%s-%s-nlq", fields[0],
517 DEBUG("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
518 olsrd_submit(/* p.-inst = */ "topology", /* type = */ "signal_quality",
524 } /* }}} int olsrd_cb_topology */
526 static int olsrd_read_table(FILE *fh, /* {{{ */
527 int (*callback)(int lineno, size_t fields_num,
538 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
539 /* An empty line ends the table. */
540 buffer_len = strchomp(buffer);
541 if (buffer_len == 0) {
542 (*callback)(lineno, /* fields_num = */ 0, /* fields = */ NULL);
546 fields_num = strtabsplit(buffer, fields, STATIC_ARRAY_SIZE(fields));
548 (*callback)(lineno, fields_num, fields);
550 } /* while (fgets) */
553 } /* }}} int olsrd_read_table */
555 static int olsrd_config(const char *key, const char *value) /* {{{ */
557 if (strcasecmp("Host", key) == 0)
558 olsrd_set_node(value);
559 else if (strcasecmp("Port", key) == 0)
560 olsrd_set_service(value);
561 else if (strcasecmp("CollectLinks", key) == 0)
562 olsrd_set_detail(&config_want_links, value, key);
563 else if (strcasecmp("CollectRoutes", key) == 0)
564 olsrd_set_detail(&config_want_routes, value, key);
565 else if (strcasecmp("CollectTopology", key) == 0)
566 olsrd_set_detail(&config_want_topology, value, key);
568 ERROR("olsrd plugin: Unknown configuration option given: %s", key);
573 } /* }}} int olsrd_config */
575 static int olsrd_read(void) /* {{{ */
581 fh = olsrd_connect();
588 while (fgets(buffer, sizeof(buffer), fh) != NULL) {
589 buffer_len = strchomp(buffer);
593 if (strcmp("Table: Links", buffer) == 0)
594 olsrd_read_table(fh, olsrd_cb_links);
595 else if (strcmp("Table: Neighbors", buffer) == 0)
596 olsrd_read_table(fh, olsrd_cb_ignore);
597 else if (strcmp("Table: Topology", buffer) == 0)
598 olsrd_read_table(fh, olsrd_cb_topology);
599 else if (strcmp("Table: HNA", buffer) == 0)
600 olsrd_read_table(fh, olsrd_cb_ignore);
601 else if (strcmp("Table: MID", buffer) == 0)
602 olsrd_read_table(fh, olsrd_cb_ignore);
603 else if (strcmp("Table: Routes", buffer) == 0)
604 olsrd_read_table(fh, olsrd_cb_routes);
605 else if ((strcmp("HTTP/1.0 200 OK", buffer) == 0) ||
606 (strcmp("Content-type: text/plain", buffer) == 0)) {
609 DEBUG("olsrd plugin: Unable to handle line: %s", buffer);
611 } /* while (fgets) */
616 } /* }}} int olsrd_read */
618 static int olsrd_shutdown(void) /* {{{ */
621 sfree(config_service);
624 } /* }}} int olsrd_shutdown */
626 void module_register(void) {
627 plugin_register_config("olsrd", olsrd_config, config_keys, config_keys_num);
628 plugin_register_read("olsrd", olsrd_read);
629 plugin_register_shutdown("olsrd", olsrd_shutdown);
630 } /* void module_register */