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"
29 #include "common.h"
30 #include "plugin.h"
32 #include <sys/types.h>
33 #include <netdb.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_list;
154 int ai_return;
156 FILE *fh;
158 struct addrinfo ai_hints = {
159 .ai_family = AF_UNSPEC,
160 .ai_flags = AI_ADDRCONFIG,
161 .ai_protocol = IPPROTO_TCP,
162 .ai_socktype = SOCK_STREAM
163 };
165 ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
166 &ai_hints, &ai_list);
167 if (ai_return != 0)
168 {
169 ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
170 olsrd_get_node (), olsrd_get_service (),
171 gai_strerror (ai_return));
172 return (NULL);
173 }
175 fh = NULL;
176 for (struct addrinfo *ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
177 {
178 int fd;
179 int status;
180 char errbuf[1024];
182 fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
183 if (fd < 0)
184 {
185 ERROR ("olsrd plugin: socket failed: %s",
186 sstrerror (errno, errbuf, sizeof (errbuf)));
187 continue;
188 }
190 status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
191 if (status != 0)
192 {
193 ERROR ("olsrd plugin: connect failed: %s",
194 sstrerror (errno, errbuf, sizeof (errbuf)));
195 close (fd);
196 continue;
197 }
199 fh = fdopen (fd, "r+");
200 if (fh == NULL)
201 {
202 ERROR ("olsrd plugin: fdopen failed.");
203 close (fd);
204 continue;
205 }
207 break;
208 } /* for (ai_ptr) */
210 freeaddrinfo (ai_list);
212 return (fh);
213 } /* }}} FILE *olsrd_connect */
215 __attribute__ ((nonnull(2)))
216 static void olsrd_submit (const char *plugin_instance, /* {{{ */
217 const char *type, const char *type_instance, gauge_t value)
218 {
219 value_list_t vl = VALUE_LIST_INIT;
221 vl.values = &(value_t) { .gauge = value };
222 vl.values_len = 1;
224 sstrncpy (vl.plugin, "olsrd", sizeof (vl.plugin));
225 if (plugin_instance != NULL)
226 sstrncpy (vl.plugin_instance, plugin_instance,
227 sizeof (vl.plugin_instance));
228 sstrncpy (vl.type, type, sizeof (vl.type));
229 if (type_instance != NULL)
230 sstrncpy (vl.type_instance, type_instance,
231 sizeof (vl.type_instance));
233 plugin_dispatch_values (&vl);
234 } /* }}} void olsrd_submit */
236 static int olsrd_cb_ignore (int lineno, /* {{{ */
237 size_t fields_num, char **fields)
238 {
239 return (0);
240 } /* }}} int olsrd_cb_ignore */
242 static int olsrd_cb_links (int lineno, /* {{{ */
243 size_t fields_num, char **fields)
244 {
245 /* Fields:
246 * 0 = Local IP
247 * 1 = Remote IP
248 * 2 = Hyst.
249 * 3 = LQ
250 * 4 = NLQ
251 * 5 = Cost */
253 static uint32_t links_num;
254 static double lq_sum;
255 static uint32_t lq_num;
256 static double nlq_sum;
257 static uint32_t nlq_num;
259 double lq;
260 double nlq;
262 char *endptr;
264 if (config_want_links == OLSRD_WANT_NOT)
265 return (0);
267 /* Special handling of the first line. */
268 if (lineno <= 0)
269 {
270 links_num = 0;
271 lq_sum = 0.0;
272 lq_num = 0;
273 nlq_sum = 0.0;
274 nlq_num = 0;
276 return (0);
277 }
279 /* Special handling of the last line. */
280 if (fields_num == 0)
281 {
282 DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
283 olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
284 /* t.-inst = */ NULL, (gauge_t) links_num);
286 lq = NAN;
287 if (lq_num > 0)
288 lq = lq_sum / ((double) lq_num);
289 DEBUG ("olsrd plugin: Average LQ: %g", lq);
290 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
291 "average-lq", lq);
293 nlq = NAN;
294 if (nlq_num > 0)
295 nlq = nlq_sum / ((double) nlq_num);
296 DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
297 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
298 "average-nlq", nlq);
300 return (0);
301 }
303 if (fields_num != 6)
304 return (-1);
306 links_num++;
308 errno = 0;
309 endptr = NULL;
310 lq = strtod (fields[3], &endptr);
311 if ((errno != 0) || (endptr == fields[3]))
312 {
313 ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
314 }
315 else
316 {
317 if (!isnan (lq))
318 {
319 lq_sum += lq;
320 lq_num++;
321 }
323 if (config_want_links == OLSRD_WANT_DETAIL)
324 {
325 char type_instance[DATA_MAX_NAME_LEN];
327 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
328 fields[0], fields[1]);
330 DEBUG ("olsrd plugin: links: type_instance = %s; lq = %g;",
331 type_instance, lq);
332 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
333 type_instance, lq);
334 }
335 }
337 errno = 0;
338 endptr = NULL;
339 nlq = strtod (fields[4], &endptr);
340 if ((errno != 0) || (endptr == fields[4]))
341 {
342 ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
343 }
344 else
345 {
346 if (!isnan (nlq))
347 {
348 nlq_sum += nlq;
349 nlq_num++;
350 }
352 if (config_want_links == OLSRD_WANT_DETAIL)
353 {
354 char type_instance[DATA_MAX_NAME_LEN];
356 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
357 fields[0], fields[1]);
359 DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
360 type_instance, lq);
361 olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
362 type_instance, nlq);
363 }
364 }
366 return (0);
367 } /* }}} int olsrd_cb_links */
369 static int olsrd_cb_routes (int lineno, /* {{{ */
370 size_t fields_num, char **fields)
371 {
372 /* Fields:
373 * 0 = Destination
374 * 1 = Gateway IP
375 * 2 = Metric
376 * 3 = ETX
377 * 4 = Interface */
379 static uint32_t routes_num;
380 static uint32_t metric_sum;
381 static uint32_t metric_num;
382 static double etx_sum;
383 static uint32_t etx_num;
385 uint32_t metric;
386 double etx;
387 char *endptr;
389 if (config_want_routes == OLSRD_WANT_NOT)
390 return (0);
392 /* Special handling of the first line */
393 if (lineno <= 0)
394 {
395 routes_num = 0;
396 metric_num = 0;
397 metric_sum = 0;
398 etx_sum = 0.0;
399 etx_num = 0;
401 return (0);
402 }
404 /* Special handling after the last line */
405 if (fields_num == 0)
406 {
407 double metric_avg;
409 DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
410 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
411 /* t.-inst = */ NULL, (gauge_t) routes_num);
413 metric_avg = NAN;
414 if (metric_num > 0)
415 metric_avg = ((double) metric_sum) / ((double) metric_num);
416 DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
417 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
418 "average", metric_avg);
420 etx = NAN;
421 if (etx_num > 0)
422 etx = etx_sum / ((double) etx_sum);
423 DEBUG ("olsrd plugin: Average ETX: %g", etx);
424 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
425 "average", etx);
427 return (0);
428 }
430 if (fields_num != 5)
431 return (-1);
433 routes_num++;
435 errno = 0;
436 endptr = NULL;
437 metric = (uint32_t) strtoul (fields[2], &endptr, 0);
438 if ((errno != 0) || (endptr == fields[2]))
439 {
440 ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
441 }
442 else
443 {
444 metric_num++;
445 metric_sum += metric;
447 if (config_want_routes == OLSRD_WANT_DETAIL)
448 {
449 DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
450 fields[0], metric);
451 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
452 /* t.-inst = */ fields[0], (gauge_t) metric);
453 }
454 }
456 errno = 0;
457 endptr = NULL;
458 etx = strtod (fields[3], &endptr);
459 if ((errno != 0) || (endptr == fields[3]))
460 {
461 ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
462 }
463 else
464 {
465 if (!isnan (etx))
466 {
467 etx_sum += etx;
468 etx_num++;
469 }
471 if (config_want_routes == OLSRD_WANT_DETAIL)
472 {
473 DEBUG ("olsrd plugin: destination = %s; etx = %g;",
474 fields[0], etx);
475 olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
476 /* t.-inst = */ fields[0], etx);
477 }
478 }
480 return (0);
481 } /* }}} int olsrd_cb_routes */
483 static int olsrd_cb_topology (int lineno, /* {{{ */
484 size_t fields_num, char **fields)
485 {
486 /* Fields:
487 * 0 = Dest. IP
488 * 1 = Last hop IP
489 * 2 = LQ
490 * 3 = NLQ
491 * 4 = Cost */
493 static double lq_sum;
494 static uint32_t lq_num;
496 static uint32_t links_num;
498 double lq;
499 char *endptr;
501 if (config_want_topology == OLSRD_WANT_NOT)
502 return (0);
504 /* Special handling of the first line */
505 if (lineno <= 0)
506 {
507 lq_sum = 0.0;
508 lq_num = 0;
509 links_num = 0;
511 return (0);
512 }
514 /* Special handling after the last line */
515 if (fields_num == 0)
516 {
517 DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
518 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
519 /* t.-inst = */ NULL, (gauge_t) links_num);
521 lq = NAN;
522 if (lq_num > 0)
523 lq = lq_sum / ((double) lq_sum);
524 DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
525 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
526 /* t.-inst = */ "average", lq);
528 return (0);
529 }
531 if (fields_num != 5)
532 return (-1);
534 links_num++;
536 errno = 0;
537 endptr = NULL;
538 lq = strtod (fields[2], &endptr);
539 if ((errno != 0) || (endptr == fields[2]))
540 {
541 ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
542 }
543 else
544 {
545 if (!isnan (lq))
546 {
547 lq_sum += lq;
548 lq_num++;
549 }
551 if (config_want_topology == OLSRD_WANT_DETAIL)
552 {
553 char type_instance[DATA_MAX_NAME_LEN] = { 0 };
555 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
556 fields[0], fields[1]);
557 DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
558 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
559 type_instance, lq);
560 }
561 }
563 if (config_want_topology == OLSRD_WANT_DETAIL)
564 {
565 double nlq;
567 errno = 0;
568 endptr = NULL;
569 nlq = strtod (fields[3], &endptr);
570 if ((errno != 0) || (endptr == fields[3]))
571 {
572 ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
573 }
574 else
575 {
576 char type_instance[DATA_MAX_NAME_LEN] = { 0 };
578 ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
579 fields[0], fields[1]);
580 DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
581 olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
582 type_instance, nlq);
583 }
584 }
586 return (0);
587 } /* }}} int olsrd_cb_topology */
589 static int olsrd_read_table (FILE *fh, /* {{{ */
590 int (*callback) (int lineno, size_t fields_num, char **fields))
591 {
592 char buffer[1024];
593 size_t buffer_len;
595 char *fields[32];
596 size_t fields_num;
598 int lineno;
600 lineno = 0;
601 while (fgets (buffer, sizeof (buffer), fh) != NULL)
602 {
603 /* An empty line ends the table. */
604 buffer_len = strchomp (buffer);
605 if (buffer_len == 0)
606 {
607 (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
608 break;
609 }
611 fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
613 (*callback) (lineno, fields_num, fields);
614 lineno++;
615 } /* while (fgets) */
617 return (0);
618 } /* }}} int olsrd_read_table */
620 static int olsrd_config (const char *key, const char *value) /* {{{ */
621 {
622 if (strcasecmp ("Host", key) == 0)
623 olsrd_set_node (value);
624 else if (strcasecmp ("Port", key) == 0)
625 olsrd_set_service (value);
626 else if (strcasecmp ("CollectLinks", key) == 0)
627 olsrd_set_detail (&config_want_links, value, key);
628 else if (strcasecmp ("CollectRoutes", key) == 0)
629 olsrd_set_detail (&config_want_routes, value, key);
630 else if (strcasecmp ("CollectTopology", key) == 0)
631 olsrd_set_detail (&config_want_topology, value, key);
632 else
633 {
634 ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
635 return (-1);
636 }
638 return (0);
639 } /* }}} int olsrd_config */
641 static int olsrd_read (void) /* {{{ */
642 {
643 FILE *fh;
644 char buffer[1024];
645 size_t buffer_len;
647 fh = olsrd_connect ();
648 if (fh == NULL)
649 return (-1);
651 fputs ("\r\n", fh);
652 fflush (fh);
654 while (fgets (buffer, sizeof (buffer), fh) != NULL)
655 {
656 buffer_len = strchomp (buffer);
657 if (buffer_len == 0)
658 continue;
660 if (strcmp ("Table: Links", buffer) == 0)
661 olsrd_read_table (fh, olsrd_cb_links);
662 else if (strcmp ("Table: Neighbors", buffer) == 0)
663 olsrd_read_table (fh, olsrd_cb_ignore);
664 else if (strcmp ("Table: Topology", buffer) == 0)
665 olsrd_read_table (fh, olsrd_cb_topology);
666 else if (strcmp ("Table: HNA", buffer) == 0)
667 olsrd_read_table (fh, olsrd_cb_ignore);
668 else if (strcmp ("Table: MID", buffer) == 0)
669 olsrd_read_table (fh, olsrd_cb_ignore);
670 else if (strcmp ("Table: Routes", buffer) == 0)
671 olsrd_read_table (fh, olsrd_cb_routes);
672 else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
673 || (strcmp ("Content-type: text/plain", buffer) == 0))
674 {
675 /* ignore */
676 }
677 else
678 {
679 DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
680 }
681 } /* while (fgets) */
683 fclose (fh);
685 return (0);
686 } /* }}} int olsrd_read */
688 static int olsrd_shutdown (void) /* {{{ */
689 {
690 sfree (config_node);
691 sfree (config_service);
693 return (0);
694 } /* }}} int olsrd_shutdown */
696 void module_register (void)
697 {
698 plugin_register_config ("olsrd", olsrd_config,
699 config_keys, config_keys_num);
700 plugin_register_read ("olsrd", olsrd_read);
701 plugin_register_shutdown ("olsrd", olsrd_shutdown);
702 } /* void module_register */
704 /* vim: set sw=2 sts=2 et fdm=marker : */