]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/event.c
NEWS: add a word about detecting interface changes
[thirdparty/lldpd.git] / src / daemon / event.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
d6e889b6
VB
2/*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include "lldpd.h"
19
20#include <unistd.h>
21#include <signal.h>
e0478a46 22#include <errno.h>
d6e889b6 23#include <event2/event.h>
e0478a46
VB
24#include <event2/bufferevent.h>
25#include <event2/buffer.h>
d6e889b6
VB
26
27static void
28levent_log_cb(int severity, const char *msg)
29{
30 switch (severity) {
26fa5d17
VB
31 case _EVENT_LOG_DEBUG: log_debug("libevent", "%s", msg); break;
32 case _EVENT_LOG_MSG: log_info ("libevent", "%s", msg); break;
33 case _EVENT_LOG_WARN: log_warnx("libevent", "%s", msg); break;
34 case _EVENT_LOG_ERR: log_warnx("libevent", "%s", msg); break;
d6e889b6
VB
35 }
36}
37
d6e889b6
VB
38struct lldpd_events {
39 TAILQ_ENTRY(lldpd_events) next;
40 struct event *ev;
d6e889b6
VB
41};
42TAILQ_HEAD(ev_l, lldpd_events);
43
44#define levent_snmp_fds(cfg) ((struct ev_l*)(cfg)->g_snmp_fds)
d6e889b6
VB
45#define levent_hardware_fds(hardware) ((struct ev_l*)(hardware)->h_recv)
46
47#ifdef USE_SNMP
48#include <net-snmp/net-snmp-config.h>
49#include <net-snmp/net-snmp-includes.h>
50#include <net-snmp/agent/net-snmp-agent-includes.h>
51#include <net-snmp/agent/snmp_vars.h>
52
53static void levent_snmp_update(struct lldpd *);
54
55/*
56 * Callback function when we have something to read from SNMP.
57 *
58 * This function is called because we have a read event on one SNMP
59 * file descriptor. When need to call snmp_read() on it.
60 */
61static void
62levent_snmp_read(evutil_socket_t fd, short what, void *arg)
63{
d6e889b6
VB
64 struct lldpd *cfg = arg;
65 fd_set fdset;
5fd6695c 66 (void)what;
d6e889b6
VB
67 FD_ZERO(&fdset);
68 FD_SET(fd, &fdset);
69 snmp_read(&fdset);
70 levent_snmp_update(cfg);
71}
72
73/*
74 * Callback function for a SNMP timeout.
75 *
76 * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it.
77 */
78static void
79levent_snmp_timeout(evutil_socket_t fd, short what, void *arg)
80{
d6e889b6 81 struct lldpd *cfg = arg;
5fd6695c 82 (void)what; (void)fd;
d6e889b6 83 snmp_timeout();
219c432e 84 run_alarms();
d6e889b6
VB
85 levent_snmp_update(cfg);
86}
87
88/*
89 * Watch a new SNMP FD.
90 *
91 * @param base The libevent base we are working on.
92 * @param fd The file descriptor we want to watch.
93 *
94 * The file descriptor is appended to the list of file descriptors we
95 * want to watch.
96 */
97static void
98levent_snmp_add_fd(struct lldpd *cfg, int fd)
99{
100 struct event_base *base = cfg->g_base;
101 struct lldpd_events *snmpfd = calloc(1, sizeof(struct lldpd_events));
102 if (!snmpfd) {
6f8925be 103 log_warn("event", "unable to allocate memory for new SNMP event");
d6e889b6
VB
104 return;
105 }
106 evutil_make_socket_nonblocking(fd);
107 if ((snmpfd->ev = event_new(base, fd,
108 EV_READ | EV_PERSIST,
109 levent_snmp_read,
110 cfg)) == NULL) {
6f8925be 111 log_warnx("event", "unable to allocate a new SNMP event for FD %d", fd);
d6e889b6
VB
112 free(snmpfd);
113 return;
114 }
115 if (event_add(snmpfd->ev, NULL) == -1) {
6f8925be 116 log_warnx("event", "unable to schedule new SNMP event for FD %d", fd);
d6e889b6
VB
117 event_free(snmpfd->ev);
118 free(snmpfd);
119 return;
120 }
121 TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next);
122}
123
124/*
125 * Update SNMP event loop.
126 *
127 * New events are added and some other are removed. This function
128 * should be called every time a SNMP event happens: either when
129 * handling a SNMP packet, a SNMP timeout or when sending a SNMP
130 * packet. This function will keep libevent in sync with NetSNMP.
131 *
132 * @param base The libevent base we are working on.
133 */
134static void
135levent_snmp_update(struct lldpd *cfg)
136{
137 int maxfd = 0;
138 int block = 1;
139 fd_set fdset;
140 struct timeval timeout;
141 static int howmany = 0;
5fd6695c
VB
142 int added = 0, removed = 0, current = 0;
143 struct lldpd_events *snmpfd, *snmpfd_next;
d6e889b6
VB
144
145 /* snmp_select_info() can be tricky to understand. We set `block` to
146 1 to means that we don't request a timeout. snmp_select_info()
147 will reset `block` to 0 if it wants us to setup a timeout. In
148 this timeout, `snmp_timeout()` should be invoked.
149
150 Each FD in `fdset` will need to be watched for reading. If one of
151 them become active, `snmp_read()` should be called on it.
152 */
153
154 FD_ZERO(&fdset);
155 snmp_select_info(&maxfd, &fdset, &timeout, &block);
156
157 /* We need to untrack any event whose FD is not in `fdset`
158 anymore */
d6e889b6
VB
159 for (snmpfd = TAILQ_FIRST(levent_snmp_fds(cfg));
160 snmpfd;
161 snmpfd = snmpfd_next) {
162 snmpfd_next = TAILQ_NEXT(snmpfd, next);
163 if (event_get_fd(snmpfd->ev) >= maxfd ||
164 (!FD_ISSET(event_get_fd(snmpfd->ev), &fdset))) {
165 event_free(snmpfd->ev);
166 TAILQ_REMOVE(levent_snmp_fds(cfg), snmpfd, next);
167 free(snmpfd);
168 removed++;
169 } else {
170 FD_CLR(event_get_fd(snmpfd->ev), &fdset);
171 current++;
172 }
173 }
174
175 /* Invariant: FD in `fdset` are not in list of FD */
176 for (int fd = 0; fd < maxfd; fd++) {
177 if (FD_ISSET(fd, &fdset)) {
178 levent_snmp_add_fd(cfg, fd);
179 added++;
180 }
181 }
182 current += added;
183 if (howmany != current) {
6f8925be 184 log_debug("event", "added %d events, removed %d events, total of %d events",
d6e889b6
VB
185 added, removed, current);
186 howmany = current;
187 }
188
189 /* If needed, handle timeout */
190 if (evtimer_add(cfg->g_snmp_timeout, block?NULL:&timeout) == -1)
6f8925be 191 log_warnx("event", "unable to schedule timeout function for SNMP");
d6e889b6
VB
192}
193#endif /* USE_SNMP */
194
257db885 195struct lldpd_one_client {
4e90a9e0 196 TAILQ_ENTRY(lldpd_one_client) next;
257db885 197 struct lldpd *cfg;
e0478a46 198 struct bufferevent *bev;
4e90a9e0 199 int subscribed; /* Is this client subscribed to changes? */
257db885 200};
4e90a9e0
VB
201TAILQ_HEAD(, lldpd_one_client) lldpd_clients;
202
203static void
204levent_ctl_free_client(struct lldpd_one_client *client)
205{
206 if (client && client->bev) bufferevent_free(client->bev);
207 if (client) {
208 TAILQ_REMOVE(&lldpd_clients, client, next);
209 free(client);
210 }
211}
257db885 212
e0478a46 213static ssize_t
4e90a9e0 214levent_ctl_send(struct lldpd_one_client *client, int type, void *data, size_t len)
e0478a46 215{
e0478a46
VB
216 struct bufferevent *bev = client->bev;
217 struct hmsg_header hdr = { .len = len, .type = type };
218 bufferevent_disable(bev, EV_WRITE);
219 if (bufferevent_write(bev, &hdr, sizeof(struct hmsg_header)) == -1 ||
220 (len > 0 && bufferevent_write(bev, data, len) == -1)) {
6f8925be 221 log_warnx("event", "unable to create answer to client");
4e90a9e0 222 levent_ctl_free_client(client);
e0478a46
VB
223 return -1;
224 }
225 bufferevent_enable(bev, EV_WRITE);
226 return len;
227}
228
4e90a9e0
VB
229void
230levent_ctl_notify(char *ifname, int state, struct lldpd_port *neighbor)
231{
232 struct lldpd_one_client *client, *client_next;
233 struct lldpd_neighbor_change neigh = {
234 .ifname = ifname,
235 .state = state,
236 .neighbor = neighbor
237 };
238 void *output = NULL;
d79c3de4 239 ssize_t output_len = 0;
4e90a9e0 240
4e90a9e0 241 /* Don't use TAILQ_FOREACH, the client may be deleted in case of errors. */
6f8925be 242 log_debug("control", "notify clients of neighbor changes");
4e90a9e0
VB
243 for (client = TAILQ_FIRST(&lldpd_clients);
244 client;
245 client = client_next) {
246 client_next = TAILQ_NEXT(client, next);
247 if (!client->subscribed) continue;
be969691
VB
248
249 if (output == NULL) {
250 /* Ugly hack: we don't want to transmit a list of
74e0080e 251 * ports. We patch the port to avoid this. */
be969691
VB
252 TAILQ_ENTRY(lldpd_port) backup_p_entries;
253 memcpy(&backup_p_entries, &neighbor->p_entries,
254 sizeof(backup_p_entries));
be969691
VB
255 memset(&neighbor->p_entries, 0,
256 sizeof(backup_p_entries));
be969691
VB
257 output_len = marshal_serialize(lldpd_neighbor_change,
258 &neigh, &output);
259 memcpy(&neighbor->p_entries, &backup_p_entries,
260 sizeof(backup_p_entries));
be969691
VB
261
262 if (output_len <= 0) {
6f8925be 263 log_warnx("event", "unable to serialize changed neighbor");
be969691
VB
264 return;
265 }
266 }
267
4e90a9e0
VB
268 levent_ctl_send(client, NOTIFICATION, output, output_len);
269 }
270
271 free(output);
272}
273
274static ssize_t
275levent_ctl_send_cb(void *out, int type, void *data, size_t len)
276{
277 struct lldpd_one_client *client = out;
278 return levent_ctl_send(client, type, data, len);
279}
280
d6e889b6 281static void
e0478a46 282levent_ctl_recv(struct bufferevent *bev, void *ptr)
d6e889b6 283{
e0478a46
VB
284 struct lldpd_one_client *client = ptr;
285 struct evbuffer *buffer = bufferevent_get_input(bev);
286 size_t buffer_len = evbuffer_get_length(buffer);
287 struct hmsg_header hdr;
288 void *data = NULL;
289
6f8925be 290 log_debug("control", "receive data on Unix socket");
e0478a46
VB
291 if (buffer_len < sizeof(struct hmsg_header))
292 return; /* Not enough data yet */
293 if (evbuffer_copyout(buffer, &hdr,
294 sizeof(struct hmsg_header)) != sizeof(struct hmsg_header)) {
6f8925be 295 log_warnx("event", "not able to read header");
e0478a46
VB
296 return;
297 }
82374540 298 if (hdr.len > HMSG_MAX_SIZE) {
6f8925be 299 log_warnx("event", "message received is too large");
e0478a46
VB
300 goto recv_error;
301 }
d6e889b6 302
e0478a46
VB
303 if (buffer_len < hdr.len + sizeof(struct hmsg_header))
304 return; /* Not enough data yet */
305 if (hdr.len > 0 && (data = malloc(hdr.len)) == NULL) {
6f8925be 306 log_warnx("event", "not enough memory");
e0478a46
VB
307 goto recv_error;
308 }
309 evbuffer_drain(buffer, sizeof(struct hmsg_header));
310 if (hdr.len > 0) evbuffer_remove(buffer, data, hdr.len);
4e90a9e0
VB
311
312 /* Currently, we should not receive notification acknowledgment. But if
313 * we receive one, we can discard it. */
314 if (hdr.len == 0 && hdr.type == NOTIFICATION) return;
e0478a46 315 if (client_handle_client(client->cfg,
4e90a9e0
VB
316 levent_ctl_send_cb, client,
317 hdr.type, data, hdr.len,
318 &client->subscribed) == -1) goto recv_error;
e0478a46
VB
319 free(data);
320 return;
321
322recv_error:
323 free(data);
4e90a9e0 324 levent_ctl_free_client(client);
e0478a46
VB
325}
326
327static void
328levent_ctl_event(struct bufferevent *bev, short events, void *ptr)
329{
330 struct lldpd_one_client *client = ptr;
331 if (events & BEV_EVENT_ERROR) {
6f8925be 332 log_warnx("event", "an error occurred with client: %s",
e0478a46 333 evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
4e90a9e0 334 levent_ctl_free_client(client);
e0478a46 335 } else if (events & BEV_EVENT_EOF) {
6f8925be 336 log_debug("event", "client has been disconnected");
4e90a9e0 337 levent_ctl_free_client(client);
d6e889b6 338 }
d6e889b6
VB
339}
340
341static void
342levent_ctl_accept(evutil_socket_t fd, short what, void *arg)
343{
d6e889b6 344 struct lldpd *cfg = arg;
257db885 345 struct lldpd_one_client *client = NULL;
d6e889b6 346 int s;
5fd6695c
VB
347 (void)what;
348
6f8925be 349 log_debug("control", "accept a new connection");
d6e889b6 350 if ((s = accept(fd, NULL, NULL)) == -1) {
6f8925be 351 log_warn("event", "unable to accept connection from socket");
d6e889b6
VB
352 return;
353 }
257db885
VB
354 client = calloc(1, sizeof(struct lldpd_one_client));
355 if (!client) {
6f8925be 356 log_warnx("event", "unable to allocate memory for new client");
4e90a9e0 357 close(s);
257db885 358 goto accept_failed;
d6e889b6 359 }
257db885 360 client->cfg = cfg;
d6e889b6 361 evutil_make_socket_nonblocking(s);
e0478a46
VB
362 if ((client->bev = bufferevent_socket_new(cfg->g_base, s,
363 BEV_OPT_CLOSE_ON_FREE)) == NULL) {
6f8925be 364 log_warnx("event", "unable to allocate a new buffer event for new client");
4e90a9e0 365 close(s);
257db885 366 goto accept_failed;
d6e889b6 367 }
e0478a46
VB
368 bufferevent_setcb(client->bev,
369 levent_ctl_recv, NULL, levent_ctl_event,
370 client);
371 bufferevent_enable(client->bev, EV_READ | EV_WRITE);
6f8925be 372 log_debug("event", "new client accepted");
4e90a9e0 373 TAILQ_INSERT_TAIL(&lldpd_clients, client, next);
257db885
VB
374 return;
375accept_failed:
4e90a9e0 376 levent_ctl_free_client(client);
d6e889b6
VB
377}
378
379static void
380levent_dump(evutil_socket_t fd, short what, void *arg)
381{
d6e889b6 382 struct event_base *base = arg;
5fd6695c 383 (void)fd; (void)what;
6f8925be 384 log_debug("event", "dumping all events");
d6e889b6
VB
385 event_base_dump_events(base, stderr);
386}
387static void
388levent_stop(evutil_socket_t fd, short what, void *arg)
389{
d6e889b6 390 struct event_base *base = arg;
5fd6695c 391 (void)fd; (void)what;
d6e889b6
VB
392 event_base_loopbreak(base);
393}
394
395static void
396levent_update_and_send(evutil_socket_t fd, short what, void *arg)
397{
d6e889b6 398 struct lldpd *cfg = arg;
579bedd5 399 struct timeval tv = { cfg->g_config.c_tx_interval, 0 };
5fd6695c 400 (void)fd; (void)what;
d6e889b6 401 lldpd_loop(cfg);
579bedd5
VB
402 if (cfg->g_iface_event != NULL)
403 tv.tv_sec *= 20;
d6e889b6
VB
404 event_add(cfg->g_main_loop, &tv);
405}
406
47287a61
VB
407void
408levent_send_now(struct lldpd *cfg)
409{
410 event_active(cfg->g_main_loop, EV_TIMEOUT, 1);
411}
412
d6e889b6
VB
413static void
414levent_init(struct lldpd *cfg)
415{
416 /* Setup libevent */
6f8925be 417 log_debug("event", "initialize libevent");
d6e889b6
VB
418 event_set_log_callback(levent_log_cb);
419 if (!(cfg->g_base = event_base_new()))
420 fatalx("unable to create a new libevent base");
6f8925be 421 log_info("event", "libevent %s initialized with %s method",
d6e889b6
VB
422 event_get_version(),
423 event_base_get_method(cfg->g_base));
424
425 /* Setup SNMP */
426#ifdef USE_SNMP
427 if (cfg->g_snmp) {
428 agent_init(cfg, cfg->g_snmp_agentx);
429 cfg->g_snmp_timeout = evtimer_new(cfg->g_base,
430 levent_snmp_timeout,
431 cfg);
432 if (!cfg->g_snmp_timeout)
433 fatalx("unable to setup timeout function for SNMP");
434 if ((cfg->g_snmp_fds =
435 malloc(sizeof(struct ev_l))) == NULL)
436 fatalx("unable to allocate memory for SNMP events");
437 TAILQ_INIT(levent_snmp_fds(cfg));
438 }
439#endif
6f8925be 440
579bedd5 441 /* Setup loop that will run every X seconds. */
6f8925be 442 log_debug("event", "register loop timer");
d6e889b6
VB
443 if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0,
444 levent_update_and_send,
445 cfg)))
446 fatalx("unable to setup main timer");
47287a61 447 levent_send_now(cfg);
d6e889b6
VB
448
449 /* Setup unix socket */
6f8925be 450 log_debug("event", "register Unix socket");
4e90a9e0 451 TAILQ_INIT(&lldpd_clients);
d6e889b6
VB
452 evutil_make_socket_nonblocking(cfg->g_ctl);
453 if ((cfg->g_ctl_event = event_new(cfg->g_base, cfg->g_ctl,
454 EV_READ|EV_PERSIST, levent_ctl_accept, cfg)) == NULL)
455 fatalx("unable to setup control socket event");
456 event_add(cfg->g_ctl_event, NULL);
457
458 /* Signals */
6f8925be 459 log_debug("event", "register signals");
d6e889b6
VB
460 evsignal_add(evsignal_new(cfg->g_base, SIGUSR1,
461 levent_dump, cfg->g_base),
462 NULL);
463 evsignal_add(evsignal_new(cfg->g_base, SIGHUP,
464 levent_stop, cfg->g_base),
465 NULL);
466 evsignal_add(evsignal_new(cfg->g_base, SIGINT,
467 levent_stop, cfg->g_base),
468 NULL);
469 evsignal_add(evsignal_new(cfg->g_base, SIGTERM,
470 levent_stop, cfg->g_base),
471 NULL);
472}
473
474/* Initialize libevent and start the event loop */
475void
476levent_loop(struct lldpd *cfg)
477{
478 levent_init(cfg);
479
480 /* libevent loop */
481 do {
482 if (event_base_got_break(cfg->g_base) ||
483 event_base_got_exit(cfg->g_base))
484 break;
485#ifdef USE_SNMP
486 if (cfg->g_snmp) {
219c432e
VB
487 /* We don't use delegated requests (request
488 whose answer is delayed). However, we keep
489 the call here in case we use it some
490 day. We don't call run_alarms() here. We do
491 it on timeout only. */
d6e889b6 492 netsnmp_check_outstanding_agent_requests();
d6e889b6
VB
493 levent_snmp_update(cfg);
494 }
495#endif
496 } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0);
497
498#ifdef USE_SNMP
499 if (cfg->g_snmp)
500 agent_shutdown();
501#endif /* USE_SNMP */
502
503}
504
505static void
506levent_hardware_recv(evutil_socket_t fd, short what, void *arg)
507{
d6e889b6
VB
508 struct lldpd_hardware *hardware = arg;
509 struct lldpd *cfg = hardware->h_cfg;
5fd6695c 510 (void)what;
6f8925be
VB
511 log_debug("event", "received something for %s",
512 hardware->h_ifname);
d6e889b6
VB
513 lldpd_recv(cfg, hardware, fd);
514}
515
516void
517levent_hardware_init(struct lldpd_hardware *hardware)
518{
6f8925be 519 log_debug("event", "initialize events for %s", hardware->h_ifname);
d6e889b6
VB
520 if ((hardware->h_recv =
521 malloc(sizeof(struct ev_l))) == NULL) {
6f8925be 522 log_warnx("event", "unable to allocate memory for %s",
d6e889b6
VB
523 hardware->h_ifname);
524 return;
525 }
526 TAILQ_INIT(levent_hardware_fds(hardware));
527}
528
529void
530levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd)
531{
5fd6695c 532 struct lldpd_events *hfd = NULL;
d6e889b6
VB
533 if (!hardware->h_recv) return;
534
5fd6695c 535 hfd = calloc(1, sizeof(struct lldpd_events));
d6e889b6 536 if (!hfd) {
6f8925be 537 log_warnx("event", "unable to allocate new event for %s",
d6e889b6
VB
538 hardware->h_ifname);
539 return;
540 }
541 evutil_make_socket_nonblocking(fd);
542 if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd,
543 EV_READ | EV_PERSIST,
544 levent_hardware_recv,
545 hardware)) == NULL) {
6f8925be 546 log_warnx("event", "unable to allocate a new event for %s",
d6e889b6
VB
547 hardware->h_ifname);
548 free(hfd);
549 return;
550 }
551 if (event_add(hfd->ev, NULL) == -1) {
6f8925be 552 log_warnx("event", "unable to schedule new event for %s",
d6e889b6
VB
553 hardware->h_ifname);
554 event_free(hfd->ev);
555 free(hfd);
556 return;
557 }
558 TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next);
559}
560
561void
562levent_hardware_release(struct lldpd_hardware *hardware)
563{
5fd6695c 564 struct lldpd_events *ev, *ev_next;
579bedd5 565 event_free(hardware->h_timer); hardware->h_timer = NULL;
d6e889b6
VB
566 if (!hardware->h_recv) return;
567
6f8925be 568 log_debug("event", "release events for %s", hardware->h_ifname);
d6e889b6
VB
569 for (ev = TAILQ_FIRST(levent_hardware_fds(hardware));
570 ev;
571 ev = ev_next) {
572 ev_next = TAILQ_NEXT(ev, next);
573 /* We may close several time the same FD. This is harmless. */
574 close(event_get_fd(ev->ev));
575 event_free(ev->ev);
576 TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next);
577 free(ev);
578 }
579 free(levent_hardware_fds(hardware));
580}
0484f180
VB
581
582static void
583levent_iface_trigger(evutil_socket_t fd, short what, void *arg)
584{
585 struct lldpd *cfg = arg;
586 log_info("event",
587 "triggering update of all interfaces");
588 lldpd_update_localports(cfg);
589}
590
591static void
592levent_iface_recv(evutil_socket_t fd, short what, void *arg)
593{
594 struct lldpd *cfg = arg;
595 char buffer[100];
596 int n;
597
598 /* Discard the message */
599 while (1) {
600 n = read(fd, buffer, sizeof(buffer));
601 if (n == -1 &&
602 (errno == EWOULDBLOCK ||
603 errno == EAGAIN)) break;
604 if (n == -1) {
605 log_warn("event",
606 "unable to receive interface change notification message");
607 return;
608 }
609 if (n == 0) {
610 log_warnx("event",
611 "end of file reached while getting interface change notification message");
612 return;
613 }
614 }
615
616 /* Schedule local port update. We don't run it right away because we may
617 * receive a batch of events like this. */
618 struct timeval one_sec = {1, 0};
619 log_debug("event",
620 "received notification change, schedule an update of all interfaces in one second");
621 if (cfg->g_iface_timer_event == NULL) {
622 if ((cfg->g_iface_timer_event = evtimer_new(cfg->g_base,
623 levent_iface_trigger, cfg)) == NULL) {
624 log_warnx("event",
625 "unable to create a new event to trigger interface update");
626 return;
627 }
628 }
629 if (evtimer_add(cfg->g_iface_timer_event, &one_sec) == -1) {
630 log_warnx("event",
631 "unable to schedule interface updates");
632 return;
633 }
634}
635
aa313f2a 636int
0484f180
VB
637levent_iface_subscribe(struct lldpd *cfg, int socket)
638{
639 log_debug("event", "subscribe to interface changes from socket %d",
640 socket);
641 evutil_make_socket_nonblocking(socket);
642 cfg->g_iface_event = event_new(cfg->g_base, socket,
643 EV_READ | EV_PERSIST, levent_iface_recv, cfg);
644 if (cfg->g_iface_event == NULL) {
645 log_warnx("event",
646 "unable to allocate a new event for interface changes");
aa313f2a 647 return -1;
0484f180
VB
648 }
649 if (event_add(cfg->g_iface_event, NULL) == -1) {
650 log_warnx("event",
651 "unable to schedule new interface changes event");
652 event_free(cfg->g_iface_event);
653 cfg->g_iface_event = NULL;
aa313f2a 654 return -1;
0484f180 655 }
aa313f2a 656 return 0;
0484f180 657}
579bedd5
VB
658
659static void
660levent_send_pdu(evutil_socket_t fd, short what, void *arg)
661{
662 struct lldpd_hardware *hardware = arg;
663 log_debug("event", "trigger sending PDU for port %s",
664 hardware->h_ifname);
665 lldpd_send(hardware);
666
667 struct timeval tv = { hardware->h_cfg->g_config.c_tx_interval, 0 };
668 if (event_add(hardware->h_timer, &tv) == -1) {
669 log_warnx("event", "unable to re-register timer event for port %s",
670 hardware->h_ifname);
671 event_free(hardware->h_timer);
672 hardware->h_timer = NULL;
673 return;
674 }
675}
676
677void
678levent_schedule_pdu(struct lldpd_hardware *hardware)
679{
680 log_debug("event", "schedule sending PDU on %s",
681 hardware->h_ifname);
682 if (hardware->h_timer == NULL) {
683 hardware->h_timer = evtimer_new(hardware->h_cfg->g_base,
684 levent_send_pdu, hardware);
685 if (hardware->h_timer == NULL) {
686 log_warnx("event", "unable to schedule PDU sending for port %s",
687 hardware->h_ifname);
688 return;
689 }
690 }
691
692 struct timeval tv = { 0, 0 };
693 if (event_add(hardware->h_timer, &tv) == -1) {
694 log_warnx("event", "unable to register timer event for port %s",
695 hardware->h_ifname);
696 event_free(hardware->h_timer);
697 hardware->h_timer = NULL;
698 return;
699 }
700}