2 * collectd - src/connectivity.c
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
24 * Andrew Bays <abays at redhat.com>
25 * Aneesh Puttur <aputtur at redhat.com>
32 #include "utils_complain.h"
34 #include <asm/types.h>
37 #include <netinet/in.h>
41 #include <sys/socket.h>
44 #include <libmnl/libmnl.h>
45 #include <linux/netlink.h>
46 #include <linux/rtnetlink.h>
48 #define MYPROTO NETLINK_ROUTE
53 struct interfacelist_s {
62 struct interfacelist_s *next;
64 typedef struct interfacelist_s interfacelist_t;
69 static interfacelist_t *interfacelist_head = NULL;
71 static int connectivity_thread_loop = 0;
72 static int connectivity_thread_error = 0;
73 static pthread_t connectivity_thread_id;
74 static pthread_mutex_t connectivity_lock = PTHREAD_MUTEX_INITIALIZER;
75 static pthread_cond_t connectivity_cond = PTHREAD_COND_INITIALIZER;
76 static struct mnl_socket *sock;
78 static const char *config_keys[] = {"Interface"};
79 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
85 static int connectivity_link_state(struct nlmsghdr *msg) {
87 struct ifinfomsg *ifi = mnl_nlmsg_get_payload(msg);
89 const char *dev = NULL;
91 pthread_mutex_lock(&connectivity_lock);
95 /* Scan attribute list for device name. */
96 mnl_attr_for_each(attr, msg, sizeof(*ifi)) {
97 if (mnl_attr_get_type(attr) != IFLA_IFNAME)
100 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) {
101 ERROR("connectivity plugin: connectivity_link_state: IFLA_IFNAME "
104 pthread_mutex_unlock(&connectivity_lock);
108 dev = mnl_attr_get_str(attr);
110 for (il = interfacelist_head; il != NULL; il = il->next)
111 if (strcmp(dev, il->interface) == 0)
115 INFO("connectivity plugin: Ignoring link state change for unmonitored "
119 uint32_t prev_status;
122 gettimeofday(&tv, NULL);
124 unsigned long long millisecondsSinceEpoch =
125 (unsigned long long)(tv.tv_sec) * 1000 +
126 (unsigned long long)(tv.tv_usec) / 1000;
128 INFO("connectivity plugin (%llu): Interface %s status is now %s",
129 millisecondsSinceEpoch, dev,
130 ((ifi->ifi_flags & IFF_RUNNING) ? "UP" : "DOWN"));
131 prev_status = il->status;
132 il->status = ((ifi->ifi_flags & IFF_RUNNING) ? 1 : 0);
134 il->usec = tv.tv_usec;
135 // If the new status is different than the previous status,
136 // store the previous status and set sent to zero
137 if (il->status != prev_status) {
138 il->prev_status = prev_status;
143 // no need to loop again, we found the interface name
144 // (otherwise the first if-statement in the loop would
145 // have moved us on with 'continue')
149 pthread_mutex_unlock(&connectivity_lock);
154 static int msg_handler(struct nlmsghdr *msg) {
155 switch (msg->nlmsg_type) {
165 connectivity_link_state(msg);
170 ERROR("connectivity plugin: msg_handler: Unknown netlink nlmsg_type %d\n",
177 static int read_event(struct mnl_socket *nl,
178 int (*msg_handler)(struct nlmsghdr *)) {
187 status = mnl_socket_recvfrom(nl, buf, sizeof(buf));
190 /* Socket non-blocking so bail out once we have read everything */
191 if (errno == EWOULDBLOCK || errno == EAGAIN)
194 /* Anything else is an error */
195 ERROR("connectivity plugin: read_event: Error mnl_socket_recvfrom: %d\n",
201 DEBUG("connectivity plugin: read_event: EOF\n");
204 /* We need to handle more than one message per 'recvmsg' */
205 for (h = (struct nlmsghdr *)buf; NLMSG_OK(h, (unsigned int)status);
206 h = NLMSG_NEXT(h, status)) {
208 if (h->nlmsg_type == NLMSG_DONE)
211 /* Message is some kind of error */
212 if (h->nlmsg_type == NLMSG_ERROR) {
213 ERROR("connectivity plugin: read_event: Message is an error - decode "
218 /* Call message handler */
220 ret = (*msg_handler)(h);
222 ERROR("connectivity plugin: read_event: Message handler error %d\n",
227 ERROR("connectivity plugin: read_event: Error NULL message handler\n");
235 static void *connectivity_thread(void *arg) /* {{{ */
237 pthread_mutex_lock(&connectivity_lock);
239 while (connectivity_thread_loop > 0) {
242 pthread_mutex_unlock(&connectivity_lock);
244 status = read_event(sock, msg_handler);
246 pthread_mutex_lock(&connectivity_lock);
249 connectivity_thread_error = 1;
253 if (connectivity_thread_loop <= 0)
255 } /* while (connectivity_thread_loop > 0) */
257 pthread_mutex_unlock(&connectivity_lock);
260 } /* }}} void *connectivity_thread */
262 static int start_thread(void) /* {{{ */
266 pthread_mutex_lock(&connectivity_lock);
268 if (connectivity_thread_loop != 0) {
269 pthread_mutex_unlock(&connectivity_lock);
273 connectivity_thread_loop = 1;
274 connectivity_thread_error = 0;
277 sock = mnl_socket_open(NETLINK_ROUTE);
280 "connectivity plugin: connectivity_thread: mnl_socket_open failed.");
281 pthread_mutex_unlock(&connectivity_lock);
285 if (mnl_socket_bind(sock, RTMGRP_LINK, MNL_SOCKET_AUTOPID) < 0) {
287 "connectivity plugin: connectivity_thread: mnl_socket_bind failed.");
288 pthread_mutex_unlock(&connectivity_lock);
293 status = plugin_thread_create(&connectivity_thread_id, /* attr = */ NULL,
295 /* arg = */ (void *)0, "connectivity");
297 connectivity_thread_loop = 0;
298 ERROR("connectivity plugin: Starting thread failed.");
299 pthread_mutex_unlock(&connectivity_lock);
300 mnl_socket_close(sock);
304 pthread_mutex_unlock(&connectivity_lock);
306 } /* }}} int start_thread */
308 static int stop_thread(int shutdown) /* {{{ */
313 mnl_socket_close(sock);
315 pthread_mutex_lock(&connectivity_lock);
317 if (connectivity_thread_loop == 0) {
318 pthread_mutex_unlock(&connectivity_lock);
322 connectivity_thread_loop = 0;
323 pthread_cond_broadcast(&connectivity_cond);
324 pthread_mutex_unlock(&connectivity_lock);
327 // Since the thread is blocking, calling pthread_join
328 // doesn't actually succeed in stopping it. It will stick around
329 // until a NETLINK message is received on the socket (at which
330 // it will realize that "connectivity_thread_loop" is 0 and will
331 // break out of the read loop and be allowed to die). This is
332 // fine when the process isn't supposed to be exiting, but in
333 // the case of a process shutdown, we don't want to have an
334 // idle thread hanging around. Calling pthread_cancel here in
335 // the case of a shutdown is just assures that the thread is
336 // gone and that the process has been fully terminated.
338 INFO("connectivity plugin: Canceling thread for process shutdown");
340 status = pthread_cancel(connectivity_thread_id);
343 ERROR("connectivity plugin: Unable to cancel thread: %d", status);
347 status = pthread_join(connectivity_thread_id, /* return = */ NULL);
349 ERROR("connectivity plugin: Stopping thread failed.");
354 pthread_mutex_lock(&connectivity_lock);
355 memset(&connectivity_thread_id, 0, sizeof(connectivity_thread_id));
356 connectivity_thread_error = 0;
357 pthread_mutex_unlock(&connectivity_lock);
359 INFO("connectivity plugin: Finished requesting stop of thread");
362 } /* }}} int stop_thread */
364 static int connectivity_init(void) /* {{{ */
366 if (interfacelist_head == NULL) {
367 NOTICE("connectivity plugin: No interfaces have been configured.");
371 return (start_thread());
372 } /* }}} int connectivity_init */
374 static int connectivity_config(const char *key, const char *value) /* {{{ */
376 if (strcasecmp(key, "Interface") == 0) {
380 il = malloc(sizeof(*il));
383 ERROR("connectivity plugin: malloc failed during connectivity_config: %s",
384 sstrerror(errno, errbuf, sizeof(errbuf)));
388 interface = strdup(value);
389 if (interface == NULL) {
392 ERROR("connectivity plugin: strdup failed connectivity_config: %s",
393 sstrerror(errno, errbuf, sizeof(errbuf)));
397 il->interface = interface;
398 il->status = 2; // "unknown"
401 il->next = interfacelist_head;
402 interfacelist_head = il;
409 } /* }}} int connectivity_config */
411 static void submit(const char *interface, const char *type, /* {{{ */
412 gauge_t value, uint32_t sec, uint32_t usec) {
413 value_list_t vl = VALUE_LIST_INIT;
415 vl.values = &(value_t){.gauge = value};
417 sstrncpy(vl.plugin, "connectivity", sizeof(vl.plugin));
418 sstrncpy(vl.type_instance, interface, sizeof(vl.type_instance));
419 sstrncpy(vl.type, type, sizeof(vl.type));
421 // Create metadata to store JSON key-values
422 meta_data_t *meta = meta_data_create();
425 // For latency measurement
427 gettimeofday(&tv, NULL);
428 gethostname(hostname, sizeof(hostname));
431 snprintf(strSec, sizeof strSec, "%" PRIu32, sec);
432 snprintf(struSec, sizeof struSec, "%" PRIu32, usec);
434 meta_data_add_string(meta, "condition", "interface_up");
435 meta_data_add_string(meta, "entity", interface);
436 meta_data_add_string(meta, "source", hostname);
437 meta_data_add_string(meta, "sec", strSec);
438 meta_data_add_string(meta, "usec", struSec);
439 meta_data_add_string(meta, "dest", "interface_down");
441 meta_data_add_string(meta, "condition", "interface_down");
442 meta_data_add_string(meta, "entity", interface);
443 meta_data_add_string(meta, "source", hostname);
444 meta_data_add_string(meta, "sec", strSec);
445 meta_data_add_string(meta, "usec", struSec);
446 meta_data_add_string(meta, "dest", "interface_up");
449 plugin_dispatch_values(&vl);
450 } /* }}} void interface_submit */
452 static int connectivity_read(void) /* {{{ */
454 if (connectivity_thread_error != 0) {
455 ERROR("connectivity plugin: The interface thread had a problem. Restarting "
460 for (interfacelist_t *il = interfacelist_head; il != NULL; il = il->next) {
461 il->status = 2; // signifies "unknown"
469 } /* if (connectivity_thread_error != 0) */
471 for (interfacelist_t *il = interfacelist_head; il != NULL;
472 il = il->next) /* {{{ */
475 uint32_t prev_status;
478 /* Locking here works, because the structure of the linked list is only
479 * changed during configure and shutdown. */
480 pthread_mutex_lock(&connectivity_lock);
483 prev_status = il->prev_status;
486 if (status != prev_status && sent == 0) {
487 submit(il->interface, "gauge", status, il->sec, il->usec);
492 pthread_mutex_unlock(&connectivity_lock);
493 } /* }}} for (il = interfacelist_head; il != NULL; il = il->next) */
496 } /* }}} int connectivity_read */
498 static int connectivity_shutdown(void) /* {{{ */
502 INFO("connectivity plugin: Shutting down thread.");
503 if (stop_thread(1) < 0)
506 il = interfacelist_head;
508 interfacelist_t *il_next;
512 sfree(il->interface);
519 } /* }}} int connectivity_shutdown */
521 void module_register(void) {
522 plugin_register_config("connectivity", connectivity_config, config_keys,
524 plugin_register_init("connectivity", connectivity_init);
525 plugin_register_read("connectivity", connectivity_read);
526 plugin_register_shutdown("connectivity", connectivity_shutdown);
527 } /* void module_register */