1 /**
2 * collectd - src/olsrd.c
3 * Copyright (C) 2009 Florian octo Forster
4 *
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:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
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.
22 *
23 * Authors:
24 * Florian octo Forster <octo at collectd.org>
25 **/
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
31 #include <sys/types.h>
32 #include <netdb.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
37 #define OLSRD_DEFAULT_NODE "localhost"
38 #define OLSRD_DEFAULT_SERVICE "2006"
40 static const char *config_keys[] =
41 {
42 "Host",
43 "Port",
44 "CollectLinks",
45 "CollectRoutes",
46 "CollectTopology"
47 };
48 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
50 static char *config_node = NULL;
51 static char *config_service = NULL;
53 #define OLSRD_WANT_NOT 0
54 #define OLSRD_WANT_SUMMARY 1
55 #define OLSRD_WANT_DETAIL 2
56 static int config_want_links = OLSRD_WANT_DETAIL;
57 static int config_want_routes = OLSRD_WANT_SUMMARY;
58 static int config_want_topology = OLSRD_WANT_SUMMARY;
60 static const char *olsrd_get_node (void) /* {{{ */
61 {
62 if (config_node != NULL)
63 return (config_node);
64 return (OLSRD_DEFAULT_NODE);
65 } /* }}} const char *olsrd_get_node */
67 static const char *olsrd_get_service (void) /* {{{ */
68 {
69 if (config_service != NULL)
70 return (config_service);
71 return (OLSRD_DEFAULT_SERVICE);
72 } /* }}} const char *olsrd_get_service */
74 static void olsrd_set_node (const char *node) /* {{{ */
75 {
76 char *tmp;
77 if (node == NULL)
78 return;
79 tmp = strdup (node);
80 if (tmp == NULL)
81 return;
82 config_node = tmp;
83 } /* }}} void olsrd_set_node */
85 static void olsrd_set_service (const char *service) /* {{{ */
86 {
87 char *tmp;
88 if (service == NULL)
89 return;
90 tmp = strdup (service);
91 if (tmp == NULL)
92 return;
93 config_service = tmp;
94 } /* }}} void olsrd_set_service */
96 static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
97 const char *key)
98 {
99 if (strcasecmp ("No", detail) == 0)
100 *varptr = OLSRD_WANT_NOT;
101 else if (strcasecmp ("Summary", detail) == 0)
102 *varptr = OLSRD_WANT_SUMMARY;
103 else if (strcasecmp ("Detail", detail) == 0)
104 *varptr = OLSRD_WANT_DETAIL;
105 else
106 {
107 ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
108 "option: `%s'. Expected: `No', `Summary', or `Detail'.",
109 key, detail);
110 }
111 } /* }}} void olsrd_set_detail */
113 /* Strip trailing newline characters. Returns length of string. */
114 static size_t strchomp (char *buffer) /* {{{ */
115 {
116 size_t buffer_len;
118 buffer_len = strlen (buffer);
119 while ((buffer_len > 0)
120 && ((buffer[buffer_len - 1] == '\r')
121 || (buffer[buffer_len - 1] == '\n')))
122 {
123 buffer_len--;
124 buffer[buffer_len] = 0;
125 }
127 return (buffer_len);
128 } /* }}} size_t strchomp */
130 static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
131 {
132 size_t i;
133 char *ptr;
134 char *saveptr;
136 i = 0;
137 ptr = string;
138 saveptr = NULL;
139 while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
140 {
141 ptr = NULL;
142 i++;
144 if (i >= size)
145 break;
146 }
148 return (i);
149 } /* }}} size_t strtabsplit */
151 static FILE *olsrd_connect (void) /* {{{ */
152 {
153 struct addrinfo ai_hints;
154 struct addrinfo *ai_list, *ai_ptr;
155 int ai_return;
157 FILE *fh;
159 memset (&ai_hints, 0, sizeof (ai_hints));
160 ai_hints.ai_flags = 0;
161 #ifdef AI_ADDRCONFIG
162 ai_hints.ai_flags |= AI_ADDRCONFIG;
163 #endif
164 ai_hints.ai_family = PF_UNSPEC;
165 ai_hints.ai_socktype = SOCK_STREAM;
166 ai_hints.ai_protocol = IPPROTO_TCP;
168 ai_list = NULL;
169 ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
170 &ai_hints, &ai_list);
171 if (ai_return != 0)
172 {
173 ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
174 olsrd_get_node (), olsrd_get_service (),
175 gai_strerror (ai_return));
176 return (NULL);
177 }
179 fh = NULL;
180 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
181 {
182 int fd;
183 int status;
184 char errbuf[1024];
186 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
187 if (fd < 0)
188 {
189 ERROR ("olsrd plugin: socket failed: %s",
190 sstrerror (errno, errbuf, sizeof (errbuf)));
191 continue;
192 }
194 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
195 if (status != 0)
196 {
197 ERROR ("olsrd plugin: connect failed: %s",
198 sstrerror (errno, errbuf, sizeof (errbuf)));
199 close (fd);
200 continue;
201 }
203 fh = fdopen (fd, "r+");
204 if (fh == NULL)
205 {
206 ERROR ("olsrd plugin: fdopen failed.");
207 close (fd);
208 continue;
209 }
211 break;
212 } /* for (ai_ptr) */
214 freeaddrinfo (ai_list);
216 return (fh);
217 } /* }}} FILE *olsrd_connect */
219 __attribute__ ((nonnull(2)))
220 static void olsrd_submit (const char *plugin_instance, /* {{{ */
221 const char *type, const char *type_instance, gauge_t value)
222 {
223 value_t values[1];
224 value_list_t vl = VALUE_LIST_INIT;
226 values[0].gauge = value;
228 vl.values = values;
229 vl.values_len = 1;
231 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
232 sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
233 if (plugin_instance != NULL)
234 sstrncpy (vl.plugin_instance, plugin_instance,
235 sizeof (vl.plugin_instance));
236 sstrncpy (vl.type, type, sizeof (vl.type));
237 if (type_instance != NULL)
238 sstrncpy (vl.type_instance, type_instance,
239 sizeof (vl.type_instance));
241 plugin_dispatch_values (&vl);
242 } /* }}} void olsrd_submit */
244 static int olsrd_cb_ignore (int lineno, /* {{{ */
245 size_t fields_num, char **fields)
246 {
247 return (0);
248 } /* }}} int olsrd_cb_ignore */
250 static int olsrd_cb_links (int lineno, /* {{{ */
251 size_t fields_num, char **fields)
252 {
253 /* Fields:
254 * 0 = Local IP
255 * 1 = Remote IP
256 * 2 = Hyst.
257 * 3 = LQ
258 * 4 = NLQ
259 * 5 = Cost */
261 static uint32_t links_num;
262 static double lq_sum;
263 static uint32_t lq_num;
264 static double nlq_sum;
265 static uint32_t nlq_num;
267 double lq;
268 double nlq;
270 char *endptr;
272 if (config_want_links == OLSRD_WANT_NOT)
273 return (0);
275 /* Special handling of the first line. */
276 if (lineno <= 0)
277 {
278 links_num = 0;
279 lq_sum = 0.0;
280 lq_num = 0;
281 nlq_sum = 0.0;
282 nlq_num = 0;
284 return (0);
285 }
287 /* Special handling of the last line. */
288 if (fields_num == 0)
289 {
290 DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
291 olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
292 /* t.-inst = */ NULL, (gauge_t) links_num);
294 lq = NAN;
295 if (lq_num > 0)
296 lq = lq_sum / ((double) lq_num);
297 DEBUG ("olsrd plugin: Average LQ: %g", lq);
298 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
299 "average-lq", lq);
301 nlq = NAN;
302 if (nlq_num > 0)
303 nlq = nlq_sum / ((double) nlq_num);
304 DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
305 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
306 "average-nlq", nlq);
308 return (0);
309 }
311 if (fields_num != 6)
312 return (-1);
314 links_num++;
316 errno = 0;
317 endptr = NULL;
318 lq = strtod (fields[3], &endptr);
319 if ((errno != 0) || (endptr == fields[3]))
320 {
321 ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
322 }
323 else
324 {
325 if (!isnan (lq))
326 {
327 lq_sum += lq;
328 lq_num++;
329 }
331 if (config_want_links == OLSRD_WANT_DETAIL)
332 {
333 char type_instance[DATA_MAX_NAME_LEN];
335 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
336 fields[0], fields[1]);
338 DEBUG ("olsrd plugin: links: type_instance = %s; lq = %g;",
339 type_instance, lq);
340 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
341 type_instance, lq);
342 }
343 }
345 errno = 0;
346 endptr = NULL;
347 nlq = strtod (fields[4], &endptr);
348 if ((errno != 0) || (endptr == fields[4]))
349 {
350 ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
351 }
352 else
353 {
354 if (!isnan (nlq))
355 {
356 nlq_sum += nlq;
357 nlq_num++;
358 }
360 if (config_want_links == OLSRD_WANT_DETAIL)
361 {
362 char type_instance[DATA_MAX_NAME_LEN];
364 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
365 fields[0], fields[1]);
367 DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
368 type_instance, lq);
369 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
370 type_instance, nlq);
371 }
372 }
374 return (0);
375 } /* }}} int olsrd_cb_links */
377 static int olsrd_cb_routes (int lineno, /* {{{ */
378 size_t fields_num, char **fields)
379 {
380 /* Fields:
381 * 0 = Destination
382 * 1 = Gateway IP
383 * 2 = Metric
384 * 3 = ETX
385 * 4 = Interface */
387 static uint32_t routes_num;
388 static uint32_t metric_sum;
389 static uint32_t metric_num;
390 static double etx_sum;
391 static uint32_t etx_num;
393 uint32_t metric;
394 double etx;
395 char *endptr;
397 if (config_want_routes == OLSRD_WANT_NOT)
398 return (0);
400 /* Special handling of the first line */
401 if (lineno <= 0)
402 {
403 routes_num = 0;
404 metric_num = 0;
405 metric_sum = 0;
406 etx_sum = 0.0;
407 etx_num = 0;
409 return (0);
410 }
412 /* Special handling after the last line */
413 if (fields_num == 0)
414 {
415 double metric_avg;
417 DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
418 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
419 /* t.-inst = */ NULL, (gauge_t) routes_num);
421 metric_avg = NAN;
422 if (metric_num > 0)
423 metric_avg = ((double) metric_sum) / ((double) metric_num);
424 DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
425 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
426 "average", metric_avg);
428 etx = NAN;
429 if (etx_num > 0)
430 etx = etx_sum / ((double) etx_sum);
431 DEBUG ("olsrd plugin: Average ETX: %g", etx);
432 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
433 "average", etx);
435 return (0);
436 }
438 if (fields_num != 5)
439 return (-1);
441 routes_num++;
443 errno = 0;
444 endptr = NULL;
445 metric = (uint32_t) strtoul (fields[2], &endptr, 0);
446 if ((errno != 0) || (endptr == fields[2]))
447 {
448 ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
449 }
450 else
451 {
452 metric_num++;
453 metric_sum += metric;
455 if (config_want_routes == OLSRD_WANT_DETAIL)
456 {
457 DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
458 fields[0], metric);
459 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
460 /* t.-inst = */ fields[0], (gauge_t) metric);
461 }
462 }
464 errno = 0;
465 endptr = NULL;
466 etx = strtod (fields[3], &endptr);
467 if ((errno != 0) || (endptr == fields[3]))
468 {
469 ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
470 }
471 else
472 {
473 if (!isnan (etx))
474 {
475 etx_sum += etx;
476 etx_num++;
477 }
479 if (config_want_routes == OLSRD_WANT_DETAIL)
480 {
481 DEBUG ("olsrd plugin: destination = %s; etx = %g;",
482 fields[0], etx);
483 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
484 /* t.-inst = */ fields[0], etx);
485 }
486 }
488 return (0);
489 } /* }}} int olsrd_cb_routes */
491 static int olsrd_cb_topology (int lineno, /* {{{ */
492 size_t fields_num, char **fields)
493 {
494 /* Fields:
495 * 0 = Dest. IP
496 * 1 = Last hop IP
497 * 2 = LQ
498 * 3 = NLQ
499 * 4 = Cost */
501 static double lq_sum;
502 static uint32_t lq_num;
504 static uint32_t links_num;
506 double lq;
507 char *endptr;
509 if (config_want_topology == OLSRD_WANT_NOT)
510 return (0);
512 /* Special handling of the first line */
513 if (lineno <= 0)
514 {
515 lq_sum = 0.0;
516 lq_num = 0;
517 links_num = 0;
519 return (0);
520 }
522 /* Special handling after the last line */
523 if (fields_num == 0)
524 {
525 DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
526 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
527 /* t.-inst = */ NULL, (gauge_t) links_num);
529 lq = NAN;
530 if (lq_num > 0)
531 lq = lq_sum / ((double) lq_sum);
532 DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
533 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
534 /* t.-inst = */ "average", lq);
536 return (0);
537 }
539 if (fields_num != 5)
540 return (-1);
542 links_num++;
544 errno = 0;
545 endptr = NULL;
546 lq = strtod (fields[2], &endptr);
547 if ((errno != 0) || (endptr == fields[2]))
548 {
549 ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
550 }
551 else
552 {
553 if (!isnan (lq))
554 {
555 lq_sum += lq;
556 lq_num++;
557 }
559 if (config_want_topology == OLSRD_WANT_DETAIL)
560 {
561 char type_instance[DATA_MAX_NAME_LEN];
563 memset (type_instance, 0, sizeof (type_instance));
564 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
565 fields[0], fields[1]);
566 DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
567 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
568 type_instance, lq);
569 }
570 }
572 if (config_want_topology == OLSRD_WANT_DETAIL)
573 {
574 double nlq;
576 errno = 0;
577 endptr = NULL;
578 nlq = strtod (fields[3], &endptr);
579 if ((errno != 0) || (endptr == fields[3]))
580 {
581 ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
582 }
583 else
584 {
585 char type_instance[DATA_MAX_NAME_LEN];
587 memset (type_instance, 0, sizeof (type_instance));
588 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
589 fields[0], fields[1]);
590 DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
591 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
592 type_instance, nlq);
593 }
594 }
596 return (0);
597 } /* }}} int olsrd_cb_topology */
599 static int olsrd_read_table (FILE *fh, /* {{{ */
600 int (*callback) (int lineno, size_t fields_num, char **fields))
601 {
602 char buffer[1024];
603 size_t buffer_len;
605 char *fields[32];
606 size_t fields_num;
608 int lineno;
610 lineno = 0;
611 while (fgets (buffer, sizeof (buffer), fh) != NULL)
612 {
613 /* An empty line ends the table. */
614 buffer_len = strchomp (buffer);
615 if (buffer_len <= 0)
616 {
617 (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
618 break;
619 }
621 fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
623 (*callback) (lineno, fields_num, fields);
624 lineno++;
625 } /* while (fgets) */
627 return (0);
628 } /* }}} int olsrd_read_table */
630 static int olsrd_config (const char *key, const char *value) /* {{{ */
631 {
632 if (strcasecmp ("Host", key) == 0)
633 olsrd_set_node (value);
634 else if (strcasecmp ("Port", key) == 0)
635 olsrd_set_service (value);
636 else if (strcasecmp ("CollectLinks", key) == 0)
637 olsrd_set_detail (&config_want_links, value, key);
638 else if (strcasecmp ("CollectRoutes", key) == 0)
639 olsrd_set_detail (&config_want_routes, value, key);
640 else if (strcasecmp ("CollectTopology", key) == 0)
641 olsrd_set_detail (&config_want_topology, value, key);
642 else
643 {
644 ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
645 return (-1);
646 }
648 return (0);
649 } /* }}} int olsrd_config */
651 static int olsrd_read (void) /* {{{ */
652 {
653 FILE *fh;
654 char buffer[1024];
655 size_t buffer_len;
657 fh = olsrd_connect ();
658 if (fh == NULL)
659 return (-1);
661 fputs ("\r\n", fh);
662 fflush (fh);
664 while (fgets (buffer, sizeof (buffer), fh) != NULL)
665 {
666 buffer_len = strchomp (buffer);
667 if (buffer_len <= 0)
668 continue;
670 if (strcmp ("Table: Links", buffer) == 0)
671 olsrd_read_table (fh, olsrd_cb_links);
672 else if (strcmp ("Table: Neighbors", buffer) == 0)
673 olsrd_read_table (fh, olsrd_cb_ignore);
674 else if (strcmp ("Table: Topology", buffer) == 0)
675 olsrd_read_table (fh, olsrd_cb_topology);
676 else if (strcmp ("Table: HNA", buffer) == 0)
677 olsrd_read_table (fh, olsrd_cb_ignore);
678 else if (strcmp ("Table: MID", buffer) == 0)
679 olsrd_read_table (fh, olsrd_cb_ignore);
680 else if (strcmp ("Table: Routes", buffer) == 0)
681 olsrd_read_table (fh, olsrd_cb_routes);
682 else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
683 || (strcmp ("Content-type: text/plain", buffer) == 0))
684 {
685 /* ignore */
686 }
687 else
688 {
689 DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
690 }
691 } /* while (fgets) */
693 fclose (fh);
695 return (0);
696 } /* }}} int olsrd_read */
698 static int olsrd_shutdown (void) /* {{{ */
699 {
700 sfree (config_node);
701 sfree (config_service);
703 return (0);
704 } /* }}} int olsrd_shutdown */
706 void module_register (void)
707 {
708 plugin_register_config ("olsrd", olsrd_config,
709 config_keys, config_keys_num);
710 plugin_register_read ("olsrd", olsrd_read);
711 plugin_register_shutdown ("olsrd", olsrd_shutdown);
712 } /* void module_register */
714 /* vim: set sw=2 sts=2 et fdm=marker : */