]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
lldpd: switch to libevent loop
authorVincent Bernat <bernat@luffy.cx>
Mon, 30 Apr 2012 23:33:42 +0000 (01:33 +0200)
committerVincent Bernat <bernat@luffy.cx>
Tue, 1 May 2012 09:26:45 +0000 (11:26 +0200)
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
src/agent.c
src/client.c
src/ctl-server.c [deleted file]
src/event.c [new file with mode: 0644]
src/interfaces.c
src/lldpd.c
src/lldpd.h

index a82a5f4541e0bb0f1f29fbe34a52462e489ee1b7..637331e443e603150235e4a4b58dcccfc72f46b5 100644 (file)
@@ -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
index ef66150e0f1d1891d325a334e2b9512e77e902c0..0f37ca7fdf5804d7e9ed76074ac335910104c9b8 100644 (file)
@@ -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;
 
index b9bfca489e7ec0810c132c60c78a26e0c7ba817a..d32b4490e8d419ab36321f1e94b617a3f5995bca 100644 (file)
@@ -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 (file)
index a0d5e96..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
- *
- * 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 <unistd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-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 (file)
index 0000000..a95461b
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
+ *
+ * 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 <unistd.h>
+#include <signal.h>
+#include <event2/event.h>
+
+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 <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/agent/snmp_vars.h>
+
+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));
+}
index e9ce4cefe086f0fed78a3011b472a1d5a42ce990..df54f17e3e4ac7019213dbf5e1cedd184587d1a7 100644 (file)
@@ -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);
index 5c935fcc9feba692a49b7904ea40f366e1809322..685c50a67ccf7e8a202d36a15877eb37cd084051 100644 (file)
 #include <arpa/inet.h>
 #include <net/if_arp.h>
 
-#if LLDPD_FD_SETSIZE != FD_SETSIZE
-# warning "FD_SETSIZE is set to an inconsistent value."
-#endif
-
-#ifdef USE_SNMP
-#include <net-snmp/net-snmp-config.h>
-#include <net-snmp/net-snmp-includes.h>
-#include <net-snmp/agent/net-snmp-agent-includes.h>
-#include <net-snmp/agent/snmp_vars.h>
-#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);
 }
index 90f3d47b83a6f493a83ee8dfaa8d07447bbf408f..fec00b4b40e735412d8cd2f66f91f360ba82cbad 100644 (file)
 #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 */