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 <netinet/in.h>
34 #include <netinet/tcp.h>
36 #define OLSRD_DEFAULT_NODE "localhost"
37 #define OLSRD_DEFAULT_SERVICE "2006"
39 static const char *config_keys[] =
40 {
41 "Host",
42 "Port",
43 "CollectLinks",
44 "CollectRoutes",
45 "CollectTopology"
46 };
47 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
49 static char *config_node = NULL;
50 static char *config_service = NULL;
52 #define OLSRD_WANT_NOT 0
53 #define OLSRD_WANT_SUMMARY 1
54 #define OLSRD_WANT_DETAIL 2
55 static int config_want_links = OLSRD_WANT_DETAIL;
56 static int config_want_routes = OLSRD_WANT_SUMMARY;
57 static int config_want_topology = OLSRD_WANT_SUMMARY;
59 static const char *olsrd_get_node (void) /* {{{ */
60 {
61 if (config_node != NULL)
62 return (config_node);
63 return (OLSRD_DEFAULT_NODE);
64 } /* }}} const char *olsrd_get_node */
66 static const char *olsrd_get_service (void) /* {{{ */
67 {
68 if (config_service != NULL)
69 return (config_service);
70 return (OLSRD_DEFAULT_SERVICE);
71 } /* }}} const char *olsrd_get_service */
73 static void olsrd_set_node (const char *node) /* {{{ */
74 {
75 char *tmp;
76 if (node == NULL)
77 return;
78 tmp = strdup (node);
79 if (tmp == NULL)
80 return;
81 config_node = tmp;
82 } /* }}} void olsrd_set_node */
84 static void olsrd_set_service (const char *service) /* {{{ */
85 {
86 char *tmp;
87 if (service == NULL)
88 return;
89 tmp = strdup (service);
90 if (tmp == NULL)
91 return;
92 config_service = tmp;
93 } /* }}} void olsrd_set_service */
95 static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
96 const char *key)
97 {
98 if (strcasecmp ("No", detail) == 0)
99 *varptr = OLSRD_WANT_NOT;
100 else if (strcasecmp ("Summary", detail) == 0)
101 *varptr = OLSRD_WANT_SUMMARY;
102 else if (strcasecmp ("Detail", detail) == 0)
103 *varptr = OLSRD_WANT_DETAIL;
104 else
105 {
106 ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
107 "option: `%s'. Expected: `No', `Summary', or `Detail'.",
108 key, detail);
109 }
110 } /* }}} void olsrd_set_detail */
112 /* Strip trailing newline characters. Returns length of string. */
113 static size_t strchomp (char *buffer) /* {{{ */
114 {
115 size_t buffer_len;
117 buffer_len = strlen (buffer);
118 while ((buffer_len > 0)
119 && ((buffer[buffer_len - 1] == '\r')
120 || (buffer[buffer_len - 1] == '\n')))
121 {
122 buffer_len--;
123 buffer[buffer_len] = 0;
124 }
126 return (buffer_len);
127 } /* }}} size_t strchomp */
129 static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
130 {
131 size_t i;
132 char *ptr;
133 char *saveptr;
135 i = 0;
136 ptr = string;
137 saveptr = NULL;
138 while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
139 {
140 ptr = NULL;
141 i++;
143 if (i >= size)
144 break;
145 }
147 return (i);
148 } /* }}} size_t strtabsplit */
150 static FILE *olsrd_connect (void) /* {{{ */
151 {
152 struct addrinfo ai_hints;
153 struct addrinfo *ai_list, *ai_ptr;
154 int ai_return;
156 FILE *fh;
158 memset (&ai_hints, 0, sizeof (ai_hints));
159 ai_hints.ai_flags = 0;
160 #ifdef AI_ADDRCONFIG
161 ai_hints.ai_flags |= AI_ADDRCONFIG;
162 #endif
163 ai_hints.ai_family = PF_UNSPEC;
164 ai_hints.ai_socktype = SOCK_STREAM;
165 ai_hints.ai_protocol = IPPROTO_TCP;
167 ai_list = NULL;
168 ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
169 &ai_hints, &ai_list);
170 if (ai_return != 0)
171 {
172 ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
173 olsrd_get_node (), olsrd_get_service (),
174 gai_strerror (ai_return));
175 return (NULL);
176 }
178 fh = NULL;
179 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
180 {
181 int fd;
182 int status;
183 char errbuf[1024];
185 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
186 if (fd < 0)
187 {
188 ERROR ("olsrd plugin: socket failed: %s",
189 sstrerror (errno, errbuf, sizeof (errbuf)));
190 continue;
191 }
193 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
194 if (status != 0)
195 {
196 ERROR ("olsrd plugin: connect failed: %s",
197 sstrerror (errno, errbuf, sizeof (errbuf)));
198 close (fd);
199 continue;
200 }
202 fh = fdopen (fd, "r+");
203 if (fh == NULL)
204 {
205 ERROR ("olsrd plugin: fdopen failed.");
206 close (fd);
207 continue;
208 }
210 break;
211 } /* for (ai_ptr) */
213 freeaddrinfo (ai_list);
215 return (fh);
216 } /* }}} FILE *olsrd_connect */
218 __attribute__ ((nonnull(2)))
219 static void olsrd_submit (const char *plugin_instance, /* {{{ */
220 const char *type, const char *type_instance, gauge_t value)
221 {
222 value_t values[1];
223 value_list_t vl = VALUE_LIST_INIT;
225 values[0].gauge = value;
227 vl.values = values;
228 vl.values_len = 1;
230 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
231 sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
232 if (plugin_instance != NULL)
233 sstrncpy (vl.plugin_instance, plugin_instance,
234 sizeof (vl.plugin_instance));
235 sstrncpy (vl.type, type, sizeof (vl.type));
236 if (type_instance != NULL)
237 sstrncpy (vl.type_instance, type_instance,
238 sizeof (vl.type_instance));
240 plugin_dispatch_values (&vl);
241 } /* }}} void olsrd_submit */
243 static int olsrd_cb_ignore (int lineno, /* {{{ */
244 size_t fields_num, char **fields)
245 {
246 return (0);
247 } /* }}} int olsrd_cb_ignore */
249 static int olsrd_cb_links (int lineno, /* {{{ */
250 size_t fields_num, char **fields)
251 {
252 /* Fields:
253 * 0 = Local IP
254 * 1 = Remote IP
255 * 2 = Hyst.
256 * 3 = LQ
257 * 4 = NLQ
258 * 5 = Cost */
260 static uint32_t links_num;
261 static double lq_sum;
262 static uint32_t lq_num;
263 static double nlq_sum;
264 static uint32_t nlq_num;
266 double lq;
267 double nlq;
269 char *endptr;
271 if (config_want_links == OLSRD_WANT_NOT)
272 return (0);
274 /* Special handling of the first line. */
275 if (lineno <= 0)
276 {
277 links_num = 0;
278 lq_sum = 0.0;
279 lq_num = 0;
280 nlq_sum = 0.0;
281 nlq_num = 0;
283 return (0);
284 }
286 /* Special handling of the last line. */
287 if (fields_num == 0)
288 {
289 DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
290 olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
291 /* t.-inst = */ NULL, (gauge_t) links_num);
293 lq = NAN;
294 if (lq_num > 0)
295 lq = lq_sum / ((double) lq_num);
296 DEBUG ("olsrd plugin: Average LQ: %g", lq);
297 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
298 "average-lq", lq);
300 nlq = NAN;
301 if (nlq_num > 0)
302 nlq = nlq_sum / ((double) nlq_num);
303 DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
304 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
305 "average-nlq", nlq);
307 return (0);
308 }
310 if (fields_num != 6)
311 return (-1);
313 links_num++;
315 errno = 0;
316 endptr = NULL;
317 lq = strtod (fields[3], &endptr);
318 if ((errno != 0) || (endptr == fields[3]))
319 {
320 ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
321 }
322 else
323 {
324 if (!isnan (lq))
325 {
326 lq_sum += lq;
327 lq_num++;
328 }
330 if (config_want_links == OLSRD_WANT_DETAIL)
331 {
332 char type_instance[DATA_MAX_NAME_LEN];
334 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
335 fields[0], fields[1]);
337 DEBUG ("olsrd plugin: links: type_instance = %s; lq = %g;",
338 type_instance, lq);
339 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
340 type_instance, lq);
341 }
342 }
344 errno = 0;
345 endptr = NULL;
346 nlq = strtod (fields[4], &endptr);
347 if ((errno != 0) || (endptr == fields[4]))
348 {
349 ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
350 }
351 else
352 {
353 if (!isnan (nlq))
354 {
355 nlq_sum += nlq;
356 nlq_num++;
357 }
359 if (config_want_links == OLSRD_WANT_DETAIL)
360 {
361 char type_instance[DATA_MAX_NAME_LEN];
363 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
364 fields[0], fields[1]);
366 DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
367 type_instance, lq);
368 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
369 type_instance, nlq);
370 }
371 }
373 return (0);
374 } /* }}} int olsrd_cb_links */
376 static int olsrd_cb_routes (int lineno, /* {{{ */
377 size_t fields_num, char **fields)
378 {
379 /* Fields:
380 * 0 = Destination
381 * 1 = Gateway IP
382 * 2 = Metric
383 * 3 = ETX
384 * 4 = Interface */
386 static uint32_t routes_num;
387 static uint32_t metric_sum;
388 static uint32_t metric_num;
389 static double etx_sum;
390 static uint32_t etx_num;
392 uint32_t metric;
393 double etx;
394 char *endptr;
396 if (config_want_routes == OLSRD_WANT_NOT)
397 return (0);
399 /* Special handling of the first line */
400 if (lineno <= 0)
401 {
402 routes_num = 0;
403 metric_num = 0;
404 metric_sum = 0;
405 etx_sum = 0.0;
406 etx_num = 0;
408 return (0);
409 }
411 /* Special handling after the last line */
412 if (fields_num == 0)
413 {
414 double metric_avg;
416 DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
417 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
418 /* t.-inst = */ NULL, (gauge_t) routes_num);
420 metric_avg = NAN;
421 if (metric_num > 0)
422 metric_avg = ((double) metric_sum) / ((double) metric_num);
423 DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
424 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
425 "average", metric_avg);
427 etx = NAN;
428 if (etx_num > 0)
429 etx = etx_sum / ((double) etx_sum);
430 DEBUG ("olsrd plugin: Average ETX: %g", etx);
431 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
432 "average", etx);
434 return (0);
435 }
437 if (fields_num != 5)
438 return (-1);
440 routes_num++;
442 errno = 0;
443 endptr = NULL;
444 metric = (uint32_t) strtoul (fields[2], &endptr, 0);
445 if ((errno != 0) || (endptr == fields[2]))
446 {
447 ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
448 }
449 else
450 {
451 metric_num++;
452 metric_sum += metric;
454 if (config_want_routes == OLSRD_WANT_DETAIL)
455 {
456 DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
457 fields[0], metric);
458 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
459 /* t.-inst = */ fields[0], (gauge_t) metric);
460 }
461 }
463 errno = 0;
464 endptr = NULL;
465 etx = strtod (fields[3], &endptr);
466 if ((errno != 0) || (endptr == fields[3]))
467 {
468 ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
469 }
470 else
471 {
472 if (!isnan (etx))
473 {
474 etx_sum += etx;
475 etx_num++;
476 }
478 if (config_want_routes == OLSRD_WANT_DETAIL)
479 {
480 DEBUG ("olsrd plugin: destination = %s; etx = %g;",
481 fields[0], etx);
482 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
483 /* t.-inst = */ fields[0], etx);
484 }
485 }
487 return (0);
488 } /* }}} int olsrd_cb_routes */
490 static int olsrd_cb_topology (int lineno, /* {{{ */
491 size_t fields_num, char **fields)
492 {
493 /* Fields:
494 * 0 = Dest. IP
495 * 1 = Last hop IP
496 * 2 = LQ
497 * 3 = NLQ
498 * 4 = Cost */
500 static double lq_sum;
501 static uint32_t lq_num;
503 static uint32_t links_num;
505 double lq;
506 char *endptr;
508 if (config_want_topology == OLSRD_WANT_NOT)
509 return (0);
511 /* Special handling of the first line */
512 if (lineno <= 0)
513 {
514 lq_sum = 0.0;
515 lq_num = 0;
516 links_num = 0;
518 return (0);
519 }
521 /* Special handling after the last line */
522 if (fields_num == 0)
523 {
524 DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
525 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
526 /* t.-inst = */ NULL, (gauge_t) links_num);
528 lq = NAN;
529 if (lq_num > 0)
530 lq = lq_sum / ((double) lq_sum);
531 DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
532 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
533 /* t.-inst = */ "average", lq);
535 return (0);
536 }
538 if (fields_num != 5)
539 return (-1);
541 links_num++;
543 errno = 0;
544 endptr = NULL;
545 lq = strtod (fields[2], &endptr);
546 if ((errno != 0) || (endptr == fields[2]))
547 {
548 ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
549 }
550 else
551 {
552 if (!isnan (lq))
553 {
554 lq_sum += lq;
555 lq_num++;
556 }
558 if (config_want_topology == OLSRD_WANT_DETAIL)
559 {
560 char type_instance[DATA_MAX_NAME_LEN];
562 memset (type_instance, 0, sizeof (type_instance));
563 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
564 fields[0], fields[1]);
565 DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
566 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
567 type_instance, lq);
568 }
569 }
571 if (config_want_topology == OLSRD_WANT_DETAIL)
572 {
573 double nlq;
575 errno = 0;
576 endptr = NULL;
577 nlq = strtod (fields[3], &endptr);
578 if ((errno != 0) || (endptr == fields[3]))
579 {
580 ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
581 }
582 else
583 {
584 char type_instance[DATA_MAX_NAME_LEN];
586 memset (type_instance, 0, sizeof (type_instance));
587 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
588 fields[0], fields[1]);
589 DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
590 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
591 type_instance, nlq);
592 }
593 }
595 return (0);
596 } /* }}} int olsrd_cb_topology */
598 static int olsrd_read_table (FILE *fh, /* {{{ */
599 int (*callback) (int lineno, size_t fields_num, char **fields))
600 {
601 char buffer[1024];
602 size_t buffer_len;
604 char *fields[32];
605 size_t fields_num;
607 int lineno;
609 lineno = 0;
610 while (fgets (buffer, sizeof (buffer), fh) != NULL)
611 {
612 /* An empty line ends the table. */
613 buffer_len = strchomp (buffer);
614 if (buffer_len == 0)
615 {
616 (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
617 break;
618 }
620 fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
622 (*callback) (lineno, fields_num, fields);
623 lineno++;
624 } /* while (fgets) */
626 return (0);
627 } /* }}} int olsrd_read_table */
629 static int olsrd_config (const char *key, const char *value) /* {{{ */
630 {
631 if (strcasecmp ("Host", key) == 0)
632 olsrd_set_node (value);
633 else if (strcasecmp ("Port", key) == 0)
634 olsrd_set_service (value);
635 else if (strcasecmp ("CollectLinks", key) == 0)
636 olsrd_set_detail (&config_want_links, value, key);
637 else if (strcasecmp ("CollectRoutes", key) == 0)
638 olsrd_set_detail (&config_want_routes, value, key);
639 else if (strcasecmp ("CollectTopology", key) == 0)
640 olsrd_set_detail (&config_want_topology, value, key);
641 else
642 {
643 ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
644 return (-1);
645 }
647 return (0);
648 } /* }}} int olsrd_config */
650 static int olsrd_read (void) /* {{{ */
651 {
652 FILE *fh;
653 char buffer[1024];
654 size_t buffer_len;
656 fh = olsrd_connect ();
657 if (fh == NULL)
658 return (-1);
660 fputs ("\r\n", fh);
661 fflush (fh);
663 while (fgets (buffer, sizeof (buffer), fh) != NULL)
664 {
665 buffer_len = strchomp (buffer);
666 if (buffer_len == 0)
667 continue;
669 if (strcmp ("Table: Links", buffer) == 0)
670 olsrd_read_table (fh, olsrd_cb_links);
671 else if (strcmp ("Table: Neighbors", buffer) == 0)
672 olsrd_read_table (fh, olsrd_cb_ignore);
673 else if (strcmp ("Table: Topology", buffer) == 0)
674 olsrd_read_table (fh, olsrd_cb_topology);
675 else if (strcmp ("Table: HNA", buffer) == 0)
676 olsrd_read_table (fh, olsrd_cb_ignore);
677 else if (strcmp ("Table: MID", buffer) == 0)
678 olsrd_read_table (fh, olsrd_cb_ignore);
679 else if (strcmp ("Table: Routes", buffer) == 0)
680 olsrd_read_table (fh, olsrd_cb_routes);
681 else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
682 || (strcmp ("Content-type: text/plain", buffer) == 0))
683 {
684 /* ignore */
685 }
686 else
687 {
688 DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
689 }
690 } /* while (fgets) */
692 fclose (fh);
694 return (0);
695 } /* }}} int olsrd_read */
697 static int olsrd_shutdown (void) /* {{{ */
698 {
699 sfree (config_node);
700 sfree (config_service);
702 return (0);
703 } /* }}} int olsrd_shutdown */
705 void module_register (void)
706 {
707 plugin_register_config ("olsrd", olsrd_config,
708 config_keys, config_keys_num);
709 plugin_register_read ("olsrd", olsrd_read);
710 plugin_register_shutdown ("olsrd", olsrd_shutdown);
711 } /* void module_register */
713 /* vim: set sw=2 sts=2 et fdm=marker : */