From d6e889b6888b20cda3cf54192db9efce2f308c48 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 1 May 2012 01:33:42 +0200 Subject: [PATCH] lldpd: switch to libevent loop This is a major change. Instead of a custom loop, we switch to libevent. Only `levent.c` is aware of the event loop. Other parts do not manipulate events. Currently, we still have a loop triggered every 30 seconds. This could change to be a per port handling. --- src/Makefile.am | 2 +- src/agent.c | 2 +- src/client.c | 4 +- src/ctl-server.c | 53 ------ src/event.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++ src/interfaces.c | 24 +-- src/lldpd.c | 249 ++++----------------------- src/lldpd.h | 45 +++-- 8 files changed, 511 insertions(+), 306 deletions(-) delete mode 100644 src/ctl-server.c create mode 100644 src/event.c diff --git a/src/Makefile.am b/src/Makefile.am index a82a5f45..637331e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ libcommon_la_LIBADD = @LTLIBOBJS@ ## Convenience library for lldpd and tests liblldpd_la_SOURCES = frame.h frame.c lldpd.c lldp.c cdp.c sonmp.c edp.c \ - interfaces.c client.c priv.c privsep_fdpass.c dmi.c ctl-server.c + interfaces.c client.c priv.c privsep_fdpass.c dmi.c event.c liblldpd_la_CFLAGS = @LIBEVENT_CFLAGS@ liblldpd_la_LIBADD = libcommon.la @LIBEVENT_LIBS@ # Add SNMP support if needed diff --git a/src/agent.c b/src/agent.c index ef66150e..0f37ca7f 100644 --- a/src/agent.c +++ b/src/agent.c @@ -1683,7 +1683,7 @@ agent_log_callback(int major, int minor, } void -agent_init(struct lldpd *cfg, char *agentx, int debug) +agent_init(struct lldpd *cfg, char *agentx) { int rc; diff --git a/src/client.c b/src/client.c index b9bfca48..d32b4490 100644 --- a/src/client.c +++ b/src/client.c @@ -199,7 +199,7 @@ static struct client_handle client_handles[] = { { 0, NULL } }; int -client_handle_client(struct lldpd *cfg, struct lldpd_callback *callback, +client_handle_client(struct lldpd *cfg, int fd, enum hmsg_type type, void *buffer, int n) { struct client_handle *ch; @@ -208,7 +208,7 @@ client_handle_client(struct lldpd *cfg, struct lldpd_callback *callback, if (ch->type == type) { answer = NULL; len = 0; len = ch->handle(cfg, &type, buffer, n, &answer); - if ((sent = ctl_msg_send(callback->fd, type, answer, len)) == -1) + if ((sent = ctl_msg_send(fd, type, answer, len)) == -1) LLOG_WARN("unable to send answer to client"); free(answer); return sent; diff --git a/src/ctl-server.c b/src/ctl-server.c deleted file mode 100644 index a0d5e962..00000000 --- a/src/ctl-server.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2008 Vincent Bernat - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "lldpd.h" - -#include -#include -#include -#include -#include - -static void -ctl_callback(struct lldpd *cfg, struct lldpd_callback *callback) -{ - enum hmsg_type type; - void *buffer = NULL; - int n; - - if ((n = ctl_msg_recv(callback->fd, &type, &buffer)) == -1 || - client_handle_client(cfg, callback, type, buffer, n) == -1) { - close(callback->fd); - lldpd_callback_del(cfg, callback->fd, ctl_callback); - } - free(buffer); -} - -void -ctl_accept(struct lldpd *cfg, struct lldpd_callback *callback) -{ - int s; - if ((s = accept(callback->fd, NULL, NULL)) == -1) { - LLOG_WARN("unable to accept connection from socket"); - return; - } - if (lldpd_callback_add(cfg, s, ctl_callback, NULL) != 0) { - LLOG_WARN("unable to add callback for new client"); - close(s); - } - return; -} diff --git a/src/event.c b/src/event.c new file mode 100644 index 00000000..a95461b7 --- /dev/null +++ b/src/event.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2012 Vincent Bernat + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "lldpd.h" + +#include +#include +#include + +static void +levent_log_cb(int severity, const char *msg) +{ + switch (severity) { + case _EVENT_LOG_DEBUG: log_debug("libevent[debug]: %s", msg); break; + case _EVENT_LOG_MSG: log_info ("libevent[info]: %s", msg); break; + case _EVENT_LOG_WARN: log_warnx("libevent[warn]: %s", msg); break; + case _EVENT_LOG_ERR: log_warnx("libevent[error]: %s", msg); break; + } +} + +#if LLDPD_FD_SETSIZE != FD_SETSIZE +# warning "FD_SETSIZE is set to an inconsistent value." +#endif + +struct lldpd_events { + TAILQ_ENTRY(lldpd_events) next; + struct event *ev; + void *more; +}; +TAILQ_HEAD(ev_l, lldpd_events); + +#define levent_snmp_fds(cfg) ((struct ev_l*)(cfg)->g_snmp_fds) +#define levent_client_fds(cfg) ((struct ev_l*)(cfg)->g_ctl_clients) +#define levent_hardware_fds(hardware) ((struct ev_l*)(hardware)->h_recv) + +#ifdef USE_SNMP +#include +#include +#include +#include + +static void levent_snmp_update(struct lldpd *); + +/* + * Callback function when we have something to read from SNMP. + * + * This function is called because we have a read event on one SNMP + * file descriptor. When need to call snmp_read() on it. + */ +static void +levent_snmp_read(evutil_socket_t fd, short what, void *arg) +{ + (void)what; + struct lldpd *cfg = arg; + fd_set fdset; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + snmp_read(&fdset); + levent_snmp_update(cfg); +} + +/* + * Callback function for a SNMP timeout. + * + * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it. + */ +static void +levent_snmp_timeout(evutil_socket_t fd, short what, void *arg) +{ + (void)what; (void)fd; + struct lldpd *cfg = arg; + snmp_timeout(); + levent_snmp_update(cfg); +} + +/* + * Watch a new SNMP FD. + * + * @param base The libevent base we are working on. + * @param fd The file descriptor we want to watch. + * + * The file descriptor is appended to the list of file descriptors we + * want to watch. + */ +static void +levent_snmp_add_fd(struct lldpd *cfg, int fd) +{ + struct event_base *base = cfg->g_base; + struct lldpd_events *snmpfd = calloc(1, sizeof(struct lldpd_events)); + if (!snmpfd) { + LLOG_WARN("unable to allocate memory for new SNMP event"); + return; + } + evutil_make_socket_nonblocking(fd); + if ((snmpfd->ev = event_new(base, fd, + EV_READ | EV_PERSIST, + levent_snmp_read, + cfg)) == NULL) { + LLOG_WARNX("unable to allocate a new SNMP event for FD %d", fd); + free(snmpfd); + return; + } + if (event_add(snmpfd->ev, NULL) == -1) { + LLOG_WARNX("unable to schedule new SNMP event for FD %d", fd); + event_free(snmpfd->ev); + free(snmpfd); + return; + } + TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next); +} + +/* + * Update SNMP event loop. + * + * New events are added and some other are removed. This function + * should be called every time a SNMP event happens: either when + * handling a SNMP packet, a SNMP timeout or when sending a SNMP + * packet. This function will keep libevent in sync with NetSNMP. + * + * @param base The libevent base we are working on. + */ +static void +levent_snmp_update(struct lldpd *cfg) +{ + int maxfd = 0; + int block = 1; + fd_set fdset; + struct timeval timeout; + static int howmany = 0; + + /* snmp_select_info() can be tricky to understand. We set `block` to + 1 to means that we don't request a timeout. snmp_select_info() + will reset `block` to 0 if it wants us to setup a timeout. In + this timeout, `snmp_timeout()` should be invoked. + + Each FD in `fdset` will need to be watched for reading. If one of + them become active, `snmp_read()` should be called on it. + */ + + FD_ZERO(&fdset); + snmp_select_info(&maxfd, &fdset, &timeout, &block); + + /* We need to untrack any event whose FD is not in `fdset` + anymore */ + int added = 0, removed = 0, current = 0; + struct lldpd_events *snmpfd, *snmpfd_next; + for (snmpfd = TAILQ_FIRST(levent_snmp_fds(cfg)); + snmpfd; + snmpfd = snmpfd_next) { + snmpfd_next = TAILQ_NEXT(snmpfd, next); + if (event_get_fd(snmpfd->ev) >= maxfd || + (!FD_ISSET(event_get_fd(snmpfd->ev), &fdset))) { + event_free(snmpfd->ev); + TAILQ_REMOVE(levent_snmp_fds(cfg), snmpfd, next); + free(snmpfd); + removed++; + } else { + FD_CLR(event_get_fd(snmpfd->ev), &fdset); + current++; + } + } + + /* Invariant: FD in `fdset` are not in list of FD */ + for (int fd = 0; fd < maxfd; fd++) { + if (FD_ISSET(fd, &fdset)) { + levent_snmp_add_fd(cfg, fd); + added++; + } + } + current += added; + if (howmany != current) { + LLOG_DEBUG("added %d events, removed %d events, total of %d events", + added, removed, current); + howmany = current; + } + + /* If needed, handle timeout */ + if (evtimer_add(cfg->g_snmp_timeout, block?NULL:&timeout) == -1) + LLOG_WARNX("unable to schedule timeout function for SNMP"); +} +#endif /* USE_SNMP */ + +static void +levent_ctl_recv(evutil_socket_t fd, short what, void *arg) +{ + (void)what; + struct lldpd_events *cfd = arg; + struct lldpd *cfg = cfd->more; + enum hmsg_type type; + void *buffer = NULL; + int n; + + if ((n = ctl_msg_recv(fd, &type, &buffer)) == -1 || + client_handle_client(cfg, fd, type, buffer, n) == -1) { + close(fd); + event_free(cfd->ev); + TAILQ_REMOVE(levent_client_fds(cfg), cfd, next); + free(cfd); + } + free(buffer); +} + +static void +levent_ctl_accept(evutil_socket_t fd, short what, void *arg) +{ + (void)what; + struct lldpd *cfg = arg; + struct lldpd_events *cfd = NULL; + int s; + if ((s = accept(fd, NULL, NULL)) == -1) { + LLOG_WARN("unable to accept connection from socket"); + return; + } + cfd = calloc(1, sizeof(struct lldpd_events)); + if (!cfd) { + LLOG_WARNX("unable to allocate memory for new client"); + close(s); + return; + } + evutil_make_socket_nonblocking(s); + if ((cfd->ev = event_new(cfg->g_base, s, + EV_READ | EV_PERSIST, + levent_ctl_recv, + cfd)) == NULL) { + LLOG_WARNX("unable to allocate a new event for new client"); + free(cfd); + close(s); + return; + } + cfd->more = cfg; + if (event_add(cfd->ev, NULL) == -1) { + LLOG_WARNX("unable to schedule new event for new client"); + event_free(cfd->ev); + free(cfd); + close(s); + return; + } + TAILQ_INSERT_TAIL(levent_client_fds(cfg), cfd, next); +} + +static void +levent_dump(evutil_socket_t fd, short what, void *arg) +{ + (void)fd; (void)what; + struct event_base *base = arg; + event_base_dump_events(base, stderr); +} +static void +levent_stop(evutil_socket_t fd, short what, void *arg) +{ + (void)fd; (void)what; + struct event_base *base = arg; + event_base_loopbreak(base); +} + +static void +levent_update_and_send(evutil_socket_t fd, short what, void *arg) +{ + (void)fd; (void)what; + struct lldpd *cfg = arg; + struct timeval tv = {cfg->g_delay, 0}; + lldpd_loop(cfg); + event_add(cfg->g_main_loop, &tv); +} + +static void +levent_init(struct lldpd *cfg) +{ + /* Setup libevent */ + event_set_log_callback(levent_log_cb); + if (!(cfg->g_base = event_base_new())) + fatalx("unable to create a new libevent base"); + LLOG_INFO("libevent %s initialized with %s method", + event_get_version(), + event_base_get_method(cfg->g_base)); + + /* Setup SNMP */ +#ifdef USE_SNMP + if (cfg->g_snmp) { + agent_init(cfg, cfg->g_snmp_agentx); + cfg->g_snmp_timeout = evtimer_new(cfg->g_base, + levent_snmp_timeout, + cfg); + if (!cfg->g_snmp_timeout) + fatalx("unable to setup timeout function for SNMP"); + if ((cfg->g_snmp_fds = + malloc(sizeof(struct ev_l))) == NULL) + fatalx("unable to allocate memory for SNMP events"); + TAILQ_INIT(levent_snmp_fds(cfg)); + } +#endif + + /* Setup loop that will run every 30 seconds. */ + if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0, + levent_update_and_send, + cfg))) + fatalx("unable to setup main timer"); + event_active(cfg->g_main_loop, EV_TIMEOUT, 1); + + /* Setup unix socket */ + if ((cfg->g_ctl_clients = + malloc(sizeof(struct ev_l))) == NULL) + fatalx("unable to allocate memory for control socket events"); + TAILQ_INIT(levent_client_fds(cfg)); + evutil_make_socket_nonblocking(cfg->g_ctl); + if ((cfg->g_ctl_event = event_new(cfg->g_base, cfg->g_ctl, + EV_READ|EV_PERSIST, levent_ctl_accept, cfg)) == NULL) + fatalx("unable to setup control socket event"); + event_add(cfg->g_ctl_event, NULL); + + /* Signals */ + evsignal_add(evsignal_new(cfg->g_base, SIGUSR1, + levent_dump, cfg->g_base), + NULL); + evsignal_add(evsignal_new(cfg->g_base, SIGHUP, + levent_stop, cfg->g_base), + NULL); + evsignal_add(evsignal_new(cfg->g_base, SIGINT, + levent_stop, cfg->g_base), + NULL); + evsignal_add(evsignal_new(cfg->g_base, SIGTERM, + levent_stop, cfg->g_base), + NULL); +} + +/* Initialize libevent and start the event loop */ +void +levent_loop(struct lldpd *cfg) +{ + levent_init(cfg); + + /* libevent loop */ + do { + if (event_base_got_break(cfg->g_base) || + event_base_got_exit(cfg->g_base)) + break; +#ifdef USE_SNMP + if (cfg->g_snmp) { + run_alarms(); + netsnmp_check_outstanding_agent_requests(); + /* run_alarms() may establish new connections and then + synchronously modify the set of SNMP FD. We need to + update them. */ + levent_snmp_update(cfg); + } +#endif + } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0); + +#ifdef USE_SNMP + if (cfg->g_snmp) + agent_shutdown(); +#endif /* USE_SNMP */ + +} + +static void +levent_hardware_recv(evutil_socket_t fd, short what, void *arg) +{ + (void)what; + struct lldpd_hardware *hardware = arg; + struct lldpd *cfg = hardware->h_cfg; + lldpd_recv(cfg, hardware, fd); +} + +void +levent_hardware_init(struct lldpd_hardware *hardware) +{ + if ((hardware->h_recv = + malloc(sizeof(struct ev_l))) == NULL) { + LLOG_WARNX("unable to allocate memory for %s", + hardware->h_ifname); + return; + } + TAILQ_INIT(levent_hardware_fds(hardware)); +} + +void +levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd) +{ + if (!hardware->h_recv) return; + + struct lldpd_events *hfd = calloc(1, sizeof(struct lldpd_events)); + if (!hfd) { + LLOG_WARNX("unable to allocate new event for %s", + hardware->h_ifname); + return; + } + evutil_make_socket_nonblocking(fd); + if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd, + EV_READ | EV_PERSIST, + levent_hardware_recv, + hardware)) == NULL) { + LLOG_WARNX("unable to allocate a new event for %s", + hardware->h_ifname); + free(hfd); + return; + } + if (event_add(hfd->ev, NULL) == -1) { + LLOG_WARNX("unable to schedule new event for %s", + hardware->h_ifname); + event_free(hfd->ev); + free(hfd); + return; + } + TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next); +} + +void +levent_hardware_release(struct lldpd_hardware *hardware) +{ + if (!hardware->h_recv) return; + + struct lldpd_events *ev, *ev_next; + for (ev = TAILQ_FIRST(levent_hardware_fds(hardware)); + ev; + ev = ev_next) { + ev_next = TAILQ_NEXT(ev, next); + /* We may close several time the same FD. This is harmless. */ + close(event_get_fd(ev->ev)); + event_free(ev->ev); + TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next); + free(ev); + } + free(levent_hardware_fds(hardware)); +} diff --git a/src/interfaces.c b/src/interfaces.c index e9ce4cef..df54f17e 100644 --- a/src/interfaces.c +++ b/src/interfaces.c @@ -632,18 +632,6 @@ iface_multicast(struct lldpd *cfg, const char *name, int remove) } } -static void -iface_fds_close(struct lldpd *cfg, struct lldpd_hardware *hardware) -{ - int i; - for (i=0; i < LLDPD_FD_SETSIZE; i++) - if (FD_ISSET(i, &hardware->h_recvfds)) { - FD_CLR(i, &hardware->h_recvfds); - close(i); - } -} - - static int iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) { @@ -651,10 +639,7 @@ iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) if ((fd = priv_iface_init(hardware->h_ifname)) == -1) return -1; - hardware->h_sendfd = fd; - - /* fd to receive is the same as fd to send */ - FD_SET(fd, &hardware->h_recvfds); + hardware->h_sendfd = fd; /* Send */ /* Set filter */ if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) { @@ -664,6 +649,7 @@ iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware) iface_multicast(cfg, hardware->h_ifname, 0); + levent_hardware_add_fd(hardware, fd); /* Receive */ LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname, fd); return 0; @@ -704,7 +690,6 @@ iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, static int iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware) { - close(hardware->h_sendfd); iface_multicast(cfg, hardware->h_ifname, 1); return 0; } @@ -794,7 +779,7 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware) if ((fd = priv_iface_init(hardware->h_ifname)) == -1) return -1; hardware->h_sendfd = fd; - FD_SET(fd, &hardware->h_recvfds); + levent_hardware_add_fd(hardware, fd); if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) { close(fd); return status; @@ -806,7 +791,6 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware) close(hardware->h_sendfd); return -1; } - FD_SET(fd, &hardware->h_recvfds); if ((status = iface_set_filter(mastername, fd)) != 0) { close(hardware->h_sendfd); close(fd); @@ -824,6 +808,7 @@ iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware) } iface_multicast(cfg, mastername, 0); + levent_hardware_add_fd(hardware, fd); LLOG_DEBUG("interface %s initialized (fd=%d,master=%s[%d])", hardware->h_ifname, hardware->h_sendfd, @@ -894,7 +879,6 @@ iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, static int iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware) { - iface_fds_close(cfg, hardware); /* h_sendfd is here too */ iface_multicast(cfg, hardware->h_ifname, 1); iface_multicast(cfg, (char*)hardware->h_data, 1); free(hardware->h_data); diff --git a/src/lldpd.c b/src/lldpd.c index 5c935fcc..685c50a6 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -35,17 +35,6 @@ #include #include -#if LLDPD_FD_SETSIZE != FD_SETSIZE -# warning "FD_SETSIZE is set to an inconsistent value." -#endif - -#ifdef USE_SNMP -#include -#include -#include -#include -#endif /* USE_SNMP */ - static void usage(void); static struct protocol protos[] = @@ -154,6 +143,7 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name) calloc(1, sizeof(struct lldpd_hardware))) == NULL) return NULL; + hardware->h_cfg = cfg; strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname)); hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg); hardware->h_lport.p_chassis->c_refcount++; @@ -171,6 +161,8 @@ lldpd_alloc_hardware(struct lldpd *cfg, char *name) TAILQ_INIT(&hardware->h_lport.p_ppvids); TAILQ_INIT(&hardware->h_lport.p_pids); #endif + + levent_hardware_init(hardware); return hardware; } @@ -329,20 +321,10 @@ lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all void lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware) { - int i; lldpd_port_cleanup(cfg, &hardware->h_lport, 1); - /* If we have a dedicated cleanup function, use it. Otherwise, - we just free the hardware-dependent data and close all FD - in h_recvfds and h_sendfd. */ if (hardware->h_ops->cleanup) hardware->h_ops->cleanup(cfg, hardware); - else { - free(hardware->h_data); - for (i=0; i < LLDPD_FD_SETSIZE; i++) - if (FD_ISSET(i, &hardware->h_recvfds)) - close(i); - if (hardware->h_sendfd) close(hardware->h_sendfd); - } + levent_hardware_release(hardware); free(hardware); } @@ -666,37 +648,6 @@ lldpd_get_os_release() { return release; } -int -lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data) -{ - struct lldpd_callback *callback; - if ((callback = (struct lldpd_callback *) - malloc(sizeof(struct lldpd_callback))) == NULL) - return -1; - callback->fd = fd; - callback->function = fn; - callback->data = data; - TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next); - return 0; -} - -void -lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG)) -{ - struct lldpd_callback *callback, *callback_next; - for (callback = TAILQ_FIRST(&cfg->g_callbacks); - callback; - callback = callback_next) { - callback_next = TAILQ_NEXT(callback, next); - if ((callback->fd == fd) && - (callback->function = fn)) { - free(callback->data); - TAILQ_REMOVE(&cfg->g_callbacks, callback, next); - free(callback); - } - } -} - static void lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) { struct lldpd_port *port; @@ -800,118 +751,25 @@ lldpd_hide_all(struct lldpd *cfg) } } -static void -lldpd_recv_all(struct lldpd *cfg) +void +lldpd_recv(struct lldpd *cfg, struct lldpd_hardware *hardware, int fd) { - struct lldpd_hardware *hardware; - struct lldpd_callback *callback, *callback_next; - fd_set rfds; - struct timeval tv; -#ifdef USE_SNMP - struct timeval snmptv; - int snmpblock = 0; -#endif - int rc, nfds, n; - char *buffer; - - do { - tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent); - if (tv.tv_sec < 0) - /* We did not send packets in a long time, - just give up receive for now. */ - break; - if (tv.tv_sec >= cfg->g_delay) - tv.tv_sec = cfg->g_delay; - tv.tv_usec = 0; - - FD_ZERO(&rfds); - nfds = -1; - - TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { - /* Ignore if interface is down */ - if ((hardware->h_flags & IFF_RUNNING) == 0) - continue; - /* This is quite expensive but we don't rely on internal - * structure of fd_set. */ - for (n = 0; n < LLDPD_FD_SETSIZE; n++) - if (FD_ISSET(n, &hardware->h_recvfds)) { - FD_SET(n, &rfds); - if (nfds < n) - nfds = n; - } - } - TAILQ_FOREACH(callback, &cfg->g_callbacks, next) { - FD_SET(callback->fd, &rfds); - if (nfds < callback->fd) - nfds = callback->fd; - } - -#ifdef USE_SNMP - if (cfg->g_snmp) { - snmpblock = 0; - memcpy(&snmptv, &tv, sizeof(struct timeval)); - snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock); - if (snmpblock == 0) - memcpy(&tv, &snmptv, sizeof(struct timeval)); - } -#endif /* USE_SNMP */ - if (nfds == -1) { - sleep(cfg->g_delay); - return; - } - - rc = select(nfds + 1, &rfds, NULL, NULL, &tv); - if (rc == -1) { - if (errno == EINTR) - continue; - LLOG_WARN("failure on select"); - break; - } -#ifdef USE_SNMP - if (cfg->g_snmp) { - if (rc > 0) - snmp_read(&rfds); - else if (rc == 0) - snmp_timeout(); - } -#endif /* USE_SNMP */ - TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { - for (n = 0; n < LLDPD_FD_SETSIZE; n++) - if ((FD_ISSET(n, &hardware->h_recvfds)) && - (FD_ISSET(n, &rfds))) break; - if (n == LLDPD_FD_SETSIZE) continue; - if ((buffer = (char *)malloc( - hardware->h_mtu)) == NULL) { - LLOG_WARN("failed to alloc reception buffer"); - continue; - } - if ((n = hardware->h_ops->recv(cfg, hardware, - n, buffer, hardware->h_mtu)) == -1) { - free(buffer); - continue; - } - hardware->h_rx_cnt++; - lldpd_decode(cfg, buffer, n, hardware); - lldpd_hide_all(cfg); /* Immediatly hide */ - free(buffer); - break; - } - for (callback = TAILQ_FIRST(&cfg->g_callbacks); - callback; - callback = callback_next) { - /* Callback function can use TAILQ_REMOVE */ - callback_next = TAILQ_NEXT(callback, next); - if (FD_ISSET(callback->fd, &rfds)) - callback->function(cfg, callback); - } - -#ifdef USE_SNMP - if (cfg->g_snmp) { - run_alarms(); - netsnmp_check_outstanding_agent_requests(); - } -#endif /* USE_SNMP */ - } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay)); + char *buffer = NULL; + int n; + if ((buffer = (char *)malloc(hardware->h_mtu)) == NULL) { + LLOG_WARN("failed to alloc reception buffer"); + return; + } + if ((n = hardware->h_ops->recv(cfg, hardware, + fd, buffer, + hardware->h_mtu)) == -1) { + free(buffer); + return; + } + hardware->h_rx_cnt++; + lldpd_decode(cfg, buffer, n, hardware); + lldpd_hide_all(cfg); /* Immediatly hide */ + free(buffer); } static void @@ -1086,7 +944,7 @@ lldpd_update_localports(struct lldpd *cfg) freeifaddrs(ifap); } -static void +void lldpd_loop(struct lldpd *cfg) { /* Main loop. @@ -1095,43 +953,27 @@ lldpd_loop(struct lldpd *cfg) 2. Clean unwanted (removed) local ports 3. Update local chassis information 4. Send packets - 5. Receive packets - 6. Update smart mode + 5. Update events */ LOCAL_CHASSIS(cfg)->c_cap_enabled = 0; lldpd_update_localports(cfg); lldpd_cleanup(cfg); lldpd_update_localchassis(cfg); lldpd_send_all(cfg); - lldpd_recv_all(cfg); } static void -lldpd_shutdown(int sig) -{ - LLOG_INFO("signal received, exiting"); - exit(0); -} - -/* For signal handling */ -static struct lldpd *gcfg = NULL; - -static void -lldpd_exit() +lldpd_exit(struct lldpd *cfg) { struct lldpd_hardware *hardware, *hardware_next; - close(gcfg->g_ctl); + close(cfg->g_ctl); priv_ctl_cleanup(); - for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL; + for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; hardware = hardware_next) { hardware_next = TAILQ_NEXT(hardware, h_entries); - lldpd_remote_cleanup(gcfg, hardware, 1); - lldpd_hardware_cleanup(gcfg, hardware); + lldpd_remote_cleanup(cfg, hardware, 1); + lldpd_hardware_cleanup(cfg, hardware); } -#ifdef USE_SNMP - if (gcfg->g_snmp) - agent_shutdown(); -#endif /* USE_SNMP */ } struct intint { int a; int b; }; @@ -1323,6 +1165,7 @@ lldpd_main(int argc, char *argv[]) priv_init(PRIVSEP_CHROOT); + /* Initialization of global configuration */ if ((cfg = (struct lldpd *) calloc(1, sizeof(struct lldpd))) == NULL) fatal(NULL); @@ -1332,6 +1175,10 @@ lldpd_main(int argc, char *argv[]) cfg->g_interfaces = interfaces; cfg->g_smart = smart; cfg->g_receiveonly = receiveonly; +#ifdef USE_SNMP + cfg->g_snmp = snmp; + cfg->g_snmp_agentx = agentx; +#endif /* USE_SNMP */ /* Get ioctl socket */ if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) @@ -1386,35 +1233,13 @@ lldpd_main(int argc, char *argv[]) TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries); lchassis->c_refcount++; /* We should always keep a reference to local chassis */ - TAILQ_INIT(&cfg->g_callbacks); - -#ifdef USE_SNMP - if (snmp) { - cfg->g_snmp = 1; - agent_init(cfg, agentx, debug); - } -#endif /* USE_SNMP */ - /* Create socket */ if ((cfg->g_ctl = priv_ctl_create()) == -1) fatalx("unable to create control socket " LLDPD_CTL_SOCKET); - if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0) - fatalx("unable to add callback for control socket"); - - gcfg = cfg; - if (atexit(lldpd_exit) != 0) { - close(cfg->g_ctl); - priv_ctl_cleanup(); - fatal("unable to set exit function"); - } - - /* Signal handling */ - signal(SIGHUP, lldpd_shutdown); - signal(SIGINT, lldpd_shutdown); - signal(SIGTERM, lldpd_shutdown); - for (;;) - lldpd_loop(cfg); + /* Main loop */ + levent_loop(cfg); + lldpd_exit(cfg); return (0); } diff --git a/src/lldpd.h b/src/lldpd.h index 90f3d47b..fec00b4b 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -62,6 +62,11 @@ #endif #include "marshal.h" +/* We don't want to import event2/event.h. We only need those as + opaque structs. */ +struct event; +struct event_base; + #define SYSFS_CLASS_NET "/sys/class/net/" #define SYSFS_CLASS_DMI "/sys/class/dmi/id/" #define LLDPD_TTL 120 @@ -353,7 +358,8 @@ struct lldpd_ops { struct lldpd_hardware { TAILQ_ENTRY(lldpd_hardware) h_entries; - fd_set h_recvfds; /* FD for reception */ + struct lldpd *h_cfg; /* Pointer to main configuration */ + void *h_recv; /* FD for reception */ int h_sendfd; /* FD for sending, only used by h_ops */ struct lldpd_ops *h_ops; /* Hardware-dependent functions */ void *h_data; /* Hardware-dependent data */ @@ -381,6 +387,7 @@ MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_next) MARSHAL_IGNORE(lldpd_hardware, h_entries.tqe_prev) MARSHAL_IGNORE(lldpd_hardware, h_ops) MARSHAL_IGNORE(lldpd_hardware, h_data) +MARSHAL_IGNORE(lldpd_hardware, h_cfg) MARSHAL_SUBSTRUCT(lldpd_hardware, lldpd_port, h_lport) MARSHAL_SUBTQ(lldpd_hardware, lldpd_port, h_rports) MARSHAL_END; @@ -433,32 +440,31 @@ struct protocol { SMART_OUTGOING_ONE_NEIGH) #define SMART_HIDDEN(port) (port->p_hidden_in) - -#define CALLBACK_SIG struct lldpd*, struct lldpd_callback* -struct lldpd_callback { - TAILQ_ENTRY(lldpd_callback) next; - int fd; /* FD that will trigger this callback */ - void(*function)(CALLBACK_SIG); /* Function called */ - void *data; /* Optional data for this callback*/ -}; - struct lldpd { int g_sock; int g_delay; + struct event_base *g_base; +#ifdef USE_SNMP +#endif + struct protocol *g_protocols; time_t g_lastsent; int g_lastrid; int g_smart; int g_receiveonly; + struct event *g_main_loop; #ifdef USE_SNMP int g_snmp; + struct event *g_snmp_timeout; + void *g_snmp_fds; + char *g_snmp_agentx; #endif /* USE_SNMP */ /* Unix socket handling */ int g_ctl; - - TAILQ_HEAD(, lldpd_callback) g_callbacks; + struct event *g_ctl_event; + void *g_ctl_clients; char *g_mgmt_pattern; char *g_cid_pattern; @@ -501,10 +507,16 @@ void lldpd_port_cleanup(struct lldpd*, struct lldpd_port *, int); struct lldpd_mgmt *lldpd_alloc_mgmt(int family, void *addr, size_t addrsize, u_int32_t iface); void lldpd_chassis_mgmt_cleanup(struct lldpd_chassis *); void lldpd_chassis_cleanup(struct lldpd_chassis *, int); -int lldpd_callback_add(struct lldpd *, int, void(*fn)(CALLBACK_SIG), void *); -void lldpd_callback_del(struct lldpd *, int, void(*fn)(CALLBACK_SIG)); +void lldpd_recv(struct lldpd *, struct lldpd_hardware *, int); +void lldpd_loop(struct lldpd *); int lldpd_main(int, char **); +/* event.c */ +void levent_loop(struct lldpd *); +void levent_hardware_init(struct lldpd_hardware *); +void levent_hardware_add_fd(struct lldpd_hardware *, int); +void levent_hardware_release(struct lldpd_hardware *); + /* lldp.c */ int lldp_send(PROTO_SEND_SIG); int lldp_decode(PROTO_DECODE_SIG); @@ -539,7 +551,6 @@ int edp_decode(PROTO_DECODE_SIG); int ctl_create(char *); int ctl_connect(char *); void ctl_cleanup(char *); -void ctl_accept(struct lldpd *, struct lldpd_callback *); int ctl_msg_send(int, enum hmsg_type, void *, size_t); int ctl_msg_recv(int, enum hmsg_type *, void **); int ctl_msg_send_recv(int, enum hmsg_type, @@ -582,7 +593,7 @@ void fatalx(const char *); /* agent.c */ void agent_shutdown(void); -void agent_init(struct lldpd *, char *, int); +void agent_init(struct lldpd *, char *); /* agent_priv.c */ void agent_priv_register_domain(void); @@ -594,7 +605,7 @@ struct client_handle { void *, int, void **); }; -int client_handle_client(struct lldpd *, struct lldpd_callback *, +int client_handle_client(struct lldpd *, int, enum hmsg_type, void *, int); /* priv.c */ -- 2.39.5