]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/event.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
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.
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.
22 #include <event2/event.h>
25 levent_log_cb(int severity
, const char *msg
)
28 case _EVENT_LOG_DEBUG
: log_debug("libevent[debug]: %s", msg
); break;
29 case _EVENT_LOG_MSG
: log_info ("libevent[info]: %s", msg
); break;
30 case _EVENT_LOG_WARN
: log_warnx("libevent[warn]: %s", msg
); break;
31 case _EVENT_LOG_ERR
: log_warnx("libevent[error]: %s", msg
); break;
36 TAILQ_ENTRY(lldpd_events
) next
;
39 TAILQ_HEAD(ev_l
, lldpd_events
);
41 #define levent_snmp_fds(cfg) ((struct ev_l*)(cfg)->g_snmp_fds)
42 #define levent_hardware_fds(hardware) ((struct ev_l*)(hardware)->h_recv)
45 #include <net-snmp/net-snmp-config.h>
46 #include <net-snmp/net-snmp-includes.h>
47 #include <net-snmp/agent/net-snmp-agent-includes.h>
48 #include <net-snmp/agent/snmp_vars.h>
50 static void levent_snmp_update(struct lldpd
*);
53 * Callback function when we have something to read from SNMP.
55 * This function is called because we have a read event on one SNMP
56 * file descriptor. When need to call snmp_read() on it.
59 levent_snmp_read(evutil_socket_t fd
, short what
, void *arg
)
61 struct lldpd
*cfg
= arg
;
67 levent_snmp_update(cfg
);
71 * Callback function for a SNMP timeout.
73 * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it.
76 levent_snmp_timeout(evutil_socket_t fd
, short what
, void *arg
)
78 struct lldpd
*cfg
= arg
;
82 levent_snmp_update(cfg
);
86 * Watch a new SNMP FD.
88 * @param base The libevent base we are working on.
89 * @param fd The file descriptor we want to watch.
91 * The file descriptor is appended to the list of file descriptors we
95 levent_snmp_add_fd(struct lldpd
*cfg
, int fd
)
97 struct event_base
*base
= cfg
->g_base
;
98 struct lldpd_events
*snmpfd
= calloc(1, sizeof(struct lldpd_events
));
100 LLOG_WARN("unable to allocate memory for new SNMP event");
103 evutil_make_socket_nonblocking(fd
);
104 if ((snmpfd
->ev
= event_new(base
, fd
,
105 EV_READ
| EV_PERSIST
,
108 LLOG_WARNX("unable to allocate a new SNMP event for FD %d", fd
);
112 if (event_add(snmpfd
->ev
, NULL
) == -1) {
113 LLOG_WARNX("unable to schedule new SNMP event for FD %d", fd
);
114 event_free(snmpfd
->ev
);
118 TAILQ_INSERT_TAIL(levent_snmp_fds(cfg
), snmpfd
, next
);
122 * Update SNMP event loop.
124 * New events are added and some other are removed. This function
125 * should be called every time a SNMP event happens: either when
126 * handling a SNMP packet, a SNMP timeout or when sending a SNMP
127 * packet. This function will keep libevent in sync with NetSNMP.
129 * @param base The libevent base we are working on.
132 levent_snmp_update(struct lldpd
*cfg
)
137 struct timeval timeout
;
138 static int howmany
= 0;
139 int added
= 0, removed
= 0, current
= 0;
140 struct lldpd_events
*snmpfd
, *snmpfd_next
;
142 /* snmp_select_info() can be tricky to understand. We set `block` to
143 1 to means that we don't request a timeout. snmp_select_info()
144 will reset `block` to 0 if it wants us to setup a timeout. In
145 this timeout, `snmp_timeout()` should be invoked.
147 Each FD in `fdset` will need to be watched for reading. If one of
148 them become active, `snmp_read()` should be called on it.
152 snmp_select_info(&maxfd
, &fdset
, &timeout
, &block
);
154 /* We need to untrack any event whose FD is not in `fdset`
156 for (snmpfd
= TAILQ_FIRST(levent_snmp_fds(cfg
));
158 snmpfd
= snmpfd_next
) {
159 snmpfd_next
= TAILQ_NEXT(snmpfd
, next
);
160 if (event_get_fd(snmpfd
->ev
) >= maxfd
||
161 (!FD_ISSET(event_get_fd(snmpfd
->ev
), &fdset
))) {
162 event_free(snmpfd
->ev
);
163 TAILQ_REMOVE(levent_snmp_fds(cfg
), snmpfd
, next
);
167 FD_CLR(event_get_fd(snmpfd
->ev
), &fdset
);
172 /* Invariant: FD in `fdset` are not in list of FD */
173 for (int fd
= 0; fd
< maxfd
; fd
++) {
174 if (FD_ISSET(fd
, &fdset
)) {
175 levent_snmp_add_fd(cfg
, fd
);
180 if (howmany
!= current
) {
181 LLOG_DEBUG("added %d events, removed %d events, total of %d events",
182 added
, removed
, current
);
186 /* If needed, handle timeout */
187 if (evtimer_add(cfg
->g_snmp_timeout
, block
?NULL
:&timeout
) == -1)
188 LLOG_WARNX("unable to schedule timeout function for SNMP");
190 #endif /* USE_SNMP */
192 struct lldpd_one_client
{
198 levent_ctl_recv(evutil_socket_t fd
, short what
, void *arg
)
200 struct lldpd_one_client
*client
= arg
;
206 if ((n
= ctl_msg_recv(fd
, &type
, &buffer
)) == -1 ||
207 client_handle_client(client
->cfg
, fd
, type
, buffer
, n
) == -1) {
209 event_free(client
->ev
);
216 levent_ctl_accept(evutil_socket_t fd
, short what
, void *arg
)
218 struct lldpd
*cfg
= arg
;
219 struct lldpd_one_client
*client
= NULL
;
223 if ((s
= accept(fd
, NULL
, NULL
)) == -1) {
224 LLOG_WARN("unable to accept connection from socket");
227 client
= calloc(1, sizeof(struct lldpd_one_client
));
229 LLOG_WARNX("unable to allocate memory for new client");
233 evutil_make_socket_nonblocking(s
);
234 if ((client
->ev
= event_new(cfg
->g_base
, s
,
235 EV_READ
| EV_PERSIST
,
238 LLOG_WARNX("unable to allocate a new event for new client");
241 if (event_add(client
->ev
, NULL
) == -1) {
242 LLOG_WARNX("unable to schedule new event for new client");
247 if (client
&& client
->ev
) event_free(client
->ev
);
253 levent_dump(evutil_socket_t fd
, short what
, void *arg
)
255 struct event_base
*base
= arg
;
256 (void)fd
; (void)what
;
257 event_base_dump_events(base
, stderr
);
260 levent_stop(evutil_socket_t fd
, short what
, void *arg
)
262 struct event_base
*base
= arg
;
263 (void)fd
; (void)what
;
264 event_base_loopbreak(base
);
268 levent_update_and_send(evutil_socket_t fd
, short what
, void *arg
)
270 struct lldpd
*cfg
= arg
;
271 struct timeval tv
= {cfg
->g_delay
, 0};
272 (void)fd
; (void)what
;
274 event_add(cfg
->g_main_loop
, &tv
);
278 levent_init(struct lldpd
*cfg
)
281 event_set_log_callback(levent_log_cb
);
282 if (!(cfg
->g_base
= event_base_new()))
283 fatalx("unable to create a new libevent base");
284 LLOG_INFO("libevent %s initialized with %s method",
286 event_base_get_method(cfg
->g_base
));
291 agent_init(cfg
, cfg
->g_snmp_agentx
);
292 cfg
->g_snmp_timeout
= evtimer_new(cfg
->g_base
,
295 if (!cfg
->g_snmp_timeout
)
296 fatalx("unable to setup timeout function for SNMP");
297 if ((cfg
->g_snmp_fds
=
298 malloc(sizeof(struct ev_l
))) == NULL
)
299 fatalx("unable to allocate memory for SNMP events");
300 TAILQ_INIT(levent_snmp_fds(cfg
));
304 /* Setup loop that will run every 30 seconds. */
305 if (!(cfg
->g_main_loop
= event_new(cfg
->g_base
, -1, 0,
306 levent_update_and_send
,
308 fatalx("unable to setup main timer");
309 event_active(cfg
->g_main_loop
, EV_TIMEOUT
, 1);
311 /* Setup unix socket */
312 evutil_make_socket_nonblocking(cfg
->g_ctl
);
313 if ((cfg
->g_ctl_event
= event_new(cfg
->g_base
, cfg
->g_ctl
,
314 EV_READ
|EV_PERSIST
, levent_ctl_accept
, cfg
)) == NULL
)
315 fatalx("unable to setup control socket event");
316 event_add(cfg
->g_ctl_event
, NULL
);
319 evsignal_add(evsignal_new(cfg
->g_base
, SIGUSR1
,
320 levent_dump
, cfg
->g_base
),
322 evsignal_add(evsignal_new(cfg
->g_base
, SIGHUP
,
323 levent_stop
, cfg
->g_base
),
325 evsignal_add(evsignal_new(cfg
->g_base
, SIGINT
,
326 levent_stop
, cfg
->g_base
),
328 evsignal_add(evsignal_new(cfg
->g_base
, SIGTERM
,
329 levent_stop
, cfg
->g_base
),
333 /* Initialize libevent and start the event loop */
335 levent_loop(struct lldpd
*cfg
)
341 if (event_base_got_break(cfg
->g_base
) ||
342 event_base_got_exit(cfg
->g_base
))
346 /* We don't use delegated requests (request
347 whose answer is delayed). However, we keep
348 the call here in case we use it some
349 day. We don't call run_alarms() here. We do
350 it on timeout only. */
351 netsnmp_check_outstanding_agent_requests();
352 levent_snmp_update(cfg
);
355 } while (event_base_loop(cfg
->g_base
, EVLOOP_ONCE
) == 0);
360 #endif /* USE_SNMP */
365 levent_hardware_recv(evutil_socket_t fd
, short what
, void *arg
)
367 struct lldpd_hardware
*hardware
= arg
;
368 struct lldpd
*cfg
= hardware
->h_cfg
;
370 lldpd_recv(cfg
, hardware
, fd
);
374 levent_hardware_init(struct lldpd_hardware
*hardware
)
376 if ((hardware
->h_recv
=
377 malloc(sizeof(struct ev_l
))) == NULL
) {
378 LLOG_WARNX("unable to allocate memory for %s",
382 TAILQ_INIT(levent_hardware_fds(hardware
));
386 levent_hardware_add_fd(struct lldpd_hardware
*hardware
, int fd
)
388 struct lldpd_events
*hfd
= NULL
;
389 if (!hardware
->h_recv
) return;
391 hfd
= calloc(1, sizeof(struct lldpd_events
));
393 LLOG_WARNX("unable to allocate new event for %s",
397 evutil_make_socket_nonblocking(fd
);
398 if ((hfd
->ev
= event_new(hardware
->h_cfg
->g_base
, fd
,
399 EV_READ
| EV_PERSIST
,
400 levent_hardware_recv
,
401 hardware
)) == NULL
) {
402 LLOG_WARNX("unable to allocate a new event for %s",
407 if (event_add(hfd
->ev
, NULL
) == -1) {
408 LLOG_WARNX("unable to schedule new event for %s",
414 TAILQ_INSERT_TAIL(levent_hardware_fds(hardware
), hfd
, next
);
418 levent_hardware_release(struct lldpd_hardware
*hardware
)
420 struct lldpd_events
*ev
, *ev_next
;
421 if (!hardware
->h_recv
) return;
423 for (ev
= TAILQ_FIRST(levent_hardware_fds(hardware
));
426 ev_next
= TAILQ_NEXT(ev
, next
);
427 /* We may close several time the same FD. This is harmless. */
428 close(event_get_fd(ev
->ev
));
430 TAILQ_REMOVE(levent_hardware_fds(hardware
), ev
, next
);
433 free(levent_hardware_fds(hardware
));