]>
Commit | Line | Data |
---|---|---|
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 | |
27 | static void | |
28 | levent_log_cb(int severity, const char *msg) | |
29 | { | |
30 | switch (severity) { | |
31 | case _EVENT_LOG_DEBUG: log_debug("libevent[debug]: %s", msg); break; | |
32 | case _EVENT_LOG_MSG: log_info ("libevent[info]: %s", msg); break; | |
33 | case _EVENT_LOG_WARN: log_warnx("libevent[warn]: %s", msg); break; | |
34 | case _EVENT_LOG_ERR: log_warnx("libevent[error]: %s", msg); break; | |
35 | } | |
36 | } | |
37 | ||
d6e889b6 VB |
38 | struct lldpd_events { |
39 | TAILQ_ENTRY(lldpd_events) next; | |
40 | struct event *ev; | |
d6e889b6 VB |
41 | }; |
42 | TAILQ_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 | ||
53 | static 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 | */ | |
61 | static void | |
62 | levent_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 | */ | |
78 | static void | |
79 | levent_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 | */ | |
97 | static void | |
98 | levent_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) { | |
103 | LLOG_WARN("unable to allocate memory for new SNMP event"); | |
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) { | |
111 | LLOG_WARNX("unable to allocate a new SNMP event for FD %d", fd); | |
112 | free(snmpfd); | |
113 | return; | |
114 | } | |
115 | if (event_add(snmpfd->ev, NULL) == -1) { | |
116 | LLOG_WARNX("unable to schedule new SNMP event for FD %d", fd); | |
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 | */ | |
134 | static void | |
135 | levent_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) { | |
184 | LLOG_DEBUG("added %d events, removed %d events, total of %d events", | |
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) | |
191 | LLOG_WARNX("unable to schedule timeout function for SNMP"); | |
192 | } | |
193 | #endif /* USE_SNMP */ | |
194 | ||
257db885 | 195 | struct 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 |
201 | TAILQ_HEAD(, lldpd_one_client) lldpd_clients; |
202 | ||
203 | static void | |
204 | levent_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 | 213 | static ssize_t |
4e90a9e0 | 214 | levent_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)) { | |
221 | LLOG_WARNX("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 |
229 | void |
230 | levent_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 VB |
241 | /* Don't use TAILQ_FOREACH, the client may be deleted in case of errors. */ |
242 | for (client = TAILQ_FIRST(&lldpd_clients); | |
243 | client; | |
244 | client = client_next) { | |
245 | client_next = TAILQ_NEXT(client, next); | |
246 | if (!client->subscribed) continue; | |
be969691 VB |
247 | |
248 | if (output == NULL) { | |
249 | /* Ugly hack: we don't want to transmit a list of | |
74e0080e | 250 | * ports. We patch the port to avoid this. */ |
be969691 VB |
251 | TAILQ_ENTRY(lldpd_port) backup_p_entries; |
252 | memcpy(&backup_p_entries, &neighbor->p_entries, | |
253 | sizeof(backup_p_entries)); | |
be969691 VB |
254 | memset(&neighbor->p_entries, 0, |
255 | sizeof(backup_p_entries)); | |
be969691 VB |
256 | output_len = marshal_serialize(lldpd_neighbor_change, |
257 | &neigh, &output); | |
258 | memcpy(&neighbor->p_entries, &backup_p_entries, | |
259 | sizeof(backup_p_entries)); | |
be969691 VB |
260 | |
261 | if (output_len <= 0) { | |
262 | LLOG_WARNX("unable to serialize changed neighbor"); | |
263 | return; | |
264 | } | |
265 | } | |
266 | ||
4e90a9e0 VB |
267 | levent_ctl_send(client, NOTIFICATION, output, output_len); |
268 | } | |
269 | ||
270 | free(output); | |
271 | } | |
272 | ||
273 | static ssize_t | |
274 | levent_ctl_send_cb(void *out, int type, void *data, size_t len) | |
275 | { | |
276 | struct lldpd_one_client *client = out; | |
277 | return levent_ctl_send(client, type, data, len); | |
278 | } | |
279 | ||
d6e889b6 | 280 | static void |
e0478a46 | 281 | levent_ctl_recv(struct bufferevent *bev, void *ptr) |
d6e889b6 | 282 | { |
e0478a46 VB |
283 | struct lldpd_one_client *client = ptr; |
284 | struct evbuffer *buffer = bufferevent_get_input(bev); | |
285 | size_t buffer_len = evbuffer_get_length(buffer); | |
286 | struct hmsg_header hdr; | |
287 | void *data = NULL; | |
288 | ||
289 | if (buffer_len < sizeof(struct hmsg_header)) | |
290 | return; /* Not enough data yet */ | |
291 | if (evbuffer_copyout(buffer, &hdr, | |
292 | sizeof(struct hmsg_header)) != sizeof(struct hmsg_header)) { | |
293 | LLOG_WARNX("not able to read header"); | |
294 | return; | |
295 | } | |
82374540 | 296 | if (hdr.len > HMSG_MAX_SIZE) { |
e0478a46 VB |
297 | LLOG_WARNX("message received is too large"); |
298 | goto recv_error; | |
299 | } | |
d6e889b6 | 300 | |
e0478a46 VB |
301 | if (buffer_len < hdr.len + sizeof(struct hmsg_header)) |
302 | return; /* Not enough data yet */ | |
303 | if (hdr.len > 0 && (data = malloc(hdr.len)) == NULL) { | |
304 | LLOG_WARNX("not enough memory"); | |
305 | goto recv_error; | |
306 | } | |
307 | evbuffer_drain(buffer, sizeof(struct hmsg_header)); | |
308 | if (hdr.len > 0) evbuffer_remove(buffer, data, hdr.len); | |
4e90a9e0 VB |
309 | |
310 | /* Currently, we should not receive notification acknowledgment. But if | |
311 | * we receive one, we can discard it. */ | |
312 | if (hdr.len == 0 && hdr.type == NOTIFICATION) return; | |
e0478a46 | 313 | if (client_handle_client(client->cfg, |
4e90a9e0 VB |
314 | levent_ctl_send_cb, client, |
315 | hdr.type, data, hdr.len, | |
316 | &client->subscribed) == -1) goto recv_error; | |
e0478a46 VB |
317 | free(data); |
318 | return; | |
319 | ||
320 | recv_error: | |
321 | free(data); | |
4e90a9e0 | 322 | levent_ctl_free_client(client); |
e0478a46 VB |
323 | } |
324 | ||
325 | static void | |
326 | levent_ctl_event(struct bufferevent *bev, short events, void *ptr) | |
327 | { | |
328 | struct lldpd_one_client *client = ptr; | |
329 | if (events & BEV_EVENT_ERROR) { | |
330 | LLOG_WARNX("an error occurred with client: %s", | |
331 | evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); | |
4e90a9e0 | 332 | levent_ctl_free_client(client); |
e0478a46 VB |
333 | } else if (events & BEV_EVENT_EOF) { |
334 | LLOG_DEBUG("client has been disconnected"); | |
4e90a9e0 | 335 | levent_ctl_free_client(client); |
d6e889b6 | 336 | } |
d6e889b6 VB |
337 | } |
338 | ||
339 | static void | |
340 | levent_ctl_accept(evutil_socket_t fd, short what, void *arg) | |
341 | { | |
d6e889b6 | 342 | struct lldpd *cfg = arg; |
257db885 | 343 | struct lldpd_one_client *client = NULL; |
d6e889b6 | 344 | int s; |
5fd6695c VB |
345 | (void)what; |
346 | ||
d6e889b6 VB |
347 | if ((s = accept(fd, NULL, NULL)) == -1) { |
348 | LLOG_WARN("unable to accept connection from socket"); | |
349 | return; | |
350 | } | |
257db885 VB |
351 | client = calloc(1, sizeof(struct lldpd_one_client)); |
352 | if (!client) { | |
d6e889b6 | 353 | LLOG_WARNX("unable to allocate memory for new client"); |
4e90a9e0 | 354 | close(s); |
257db885 | 355 | goto accept_failed; |
d6e889b6 | 356 | } |
257db885 | 357 | client->cfg = cfg; |
d6e889b6 | 358 | evutil_make_socket_nonblocking(s); |
e0478a46 VB |
359 | if ((client->bev = bufferevent_socket_new(cfg->g_base, s, |
360 | BEV_OPT_CLOSE_ON_FREE)) == NULL) { | |
361 | LLOG_WARNX("unable to allocate a new buffer event for new client"); | |
4e90a9e0 | 362 | close(s); |
257db885 | 363 | goto accept_failed; |
d6e889b6 | 364 | } |
e0478a46 VB |
365 | bufferevent_setcb(client->bev, |
366 | levent_ctl_recv, NULL, levent_ctl_event, | |
367 | client); | |
368 | bufferevent_enable(client->bev, EV_READ | EV_WRITE); | |
4e90a9e0 VB |
369 | LLOG_DEBUG("new client accepted"); |
370 | TAILQ_INSERT_TAIL(&lldpd_clients, client, next); | |
257db885 VB |
371 | return; |
372 | accept_failed: | |
4e90a9e0 | 373 | levent_ctl_free_client(client); |
d6e889b6 VB |
374 | } |
375 | ||
376 | static void | |
377 | levent_dump(evutil_socket_t fd, short what, void *arg) | |
378 | { | |
d6e889b6 | 379 | struct event_base *base = arg; |
5fd6695c | 380 | (void)fd; (void)what; |
d6e889b6 VB |
381 | event_base_dump_events(base, stderr); |
382 | } | |
383 | static void | |
384 | levent_stop(evutil_socket_t fd, short what, void *arg) | |
385 | { | |
d6e889b6 | 386 | struct event_base *base = arg; |
5fd6695c | 387 | (void)fd; (void)what; |
d6e889b6 VB |
388 | event_base_loopbreak(base); |
389 | } | |
390 | ||
391 | static void | |
392 | levent_update_and_send(evutil_socket_t fd, short what, void *arg) | |
393 | { | |
d6e889b6 | 394 | struct lldpd *cfg = arg; |
8843f168 | 395 | struct timeval tv = {cfg->g_config.c_tx_interval, 0}; |
5fd6695c | 396 | (void)fd; (void)what; |
d6e889b6 VB |
397 | lldpd_loop(cfg); |
398 | event_add(cfg->g_main_loop, &tv); | |
399 | } | |
400 | ||
47287a61 VB |
401 | void |
402 | levent_send_now(struct lldpd *cfg) | |
403 | { | |
404 | event_active(cfg->g_main_loop, EV_TIMEOUT, 1); | |
405 | } | |
406 | ||
d6e889b6 VB |
407 | static void |
408 | levent_init(struct lldpd *cfg) | |
409 | { | |
410 | /* Setup libevent */ | |
411 | event_set_log_callback(levent_log_cb); | |
412 | if (!(cfg->g_base = event_base_new())) | |
413 | fatalx("unable to create a new libevent base"); | |
414 | LLOG_INFO("libevent %s initialized with %s method", | |
415 | event_get_version(), | |
416 | event_base_get_method(cfg->g_base)); | |
417 | ||
418 | /* Setup SNMP */ | |
419 | #ifdef USE_SNMP | |
420 | if (cfg->g_snmp) { | |
421 | agent_init(cfg, cfg->g_snmp_agentx); | |
422 | cfg->g_snmp_timeout = evtimer_new(cfg->g_base, | |
423 | levent_snmp_timeout, | |
424 | cfg); | |
425 | if (!cfg->g_snmp_timeout) | |
426 | fatalx("unable to setup timeout function for SNMP"); | |
427 | if ((cfg->g_snmp_fds = | |
428 | malloc(sizeof(struct ev_l))) == NULL) | |
429 | fatalx("unable to allocate memory for SNMP events"); | |
430 | TAILQ_INIT(levent_snmp_fds(cfg)); | |
431 | } | |
432 | #endif | |
433 | ||
434 | /* Setup loop that will run every 30 seconds. */ | |
435 | if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0, | |
436 | levent_update_and_send, | |
437 | cfg))) | |
438 | fatalx("unable to setup main timer"); | |
47287a61 | 439 | levent_send_now(cfg); |
d6e889b6 VB |
440 | |
441 | /* Setup unix socket */ | |
4e90a9e0 | 442 | TAILQ_INIT(&lldpd_clients); |
d6e889b6 VB |
443 | evutil_make_socket_nonblocking(cfg->g_ctl); |
444 | if ((cfg->g_ctl_event = event_new(cfg->g_base, cfg->g_ctl, | |
445 | EV_READ|EV_PERSIST, levent_ctl_accept, cfg)) == NULL) | |
446 | fatalx("unable to setup control socket event"); | |
447 | event_add(cfg->g_ctl_event, NULL); | |
448 | ||
449 | /* Signals */ | |
450 | evsignal_add(evsignal_new(cfg->g_base, SIGUSR1, | |
451 | levent_dump, cfg->g_base), | |
452 | NULL); | |
453 | evsignal_add(evsignal_new(cfg->g_base, SIGHUP, | |
454 | levent_stop, cfg->g_base), | |
455 | NULL); | |
456 | evsignal_add(evsignal_new(cfg->g_base, SIGINT, | |
457 | levent_stop, cfg->g_base), | |
458 | NULL); | |
459 | evsignal_add(evsignal_new(cfg->g_base, SIGTERM, | |
460 | levent_stop, cfg->g_base), | |
461 | NULL); | |
462 | } | |
463 | ||
464 | /* Initialize libevent and start the event loop */ | |
465 | void | |
466 | levent_loop(struct lldpd *cfg) | |
467 | { | |
468 | levent_init(cfg); | |
469 | ||
470 | /* libevent loop */ | |
471 | do { | |
472 | if (event_base_got_break(cfg->g_base) || | |
473 | event_base_got_exit(cfg->g_base)) | |
474 | break; | |
475 | #ifdef USE_SNMP | |
476 | if (cfg->g_snmp) { | |
219c432e VB |
477 | /* We don't use delegated requests (request |
478 | whose answer is delayed). However, we keep | |
479 | the call here in case we use it some | |
480 | day. We don't call run_alarms() here. We do | |
481 | it on timeout only. */ | |
d6e889b6 | 482 | netsnmp_check_outstanding_agent_requests(); |
d6e889b6 VB |
483 | levent_snmp_update(cfg); |
484 | } | |
485 | #endif | |
486 | } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0); | |
487 | ||
488 | #ifdef USE_SNMP | |
489 | if (cfg->g_snmp) | |
490 | agent_shutdown(); | |
491 | #endif /* USE_SNMP */ | |
492 | ||
493 | } | |
494 | ||
495 | static void | |
496 | levent_hardware_recv(evutil_socket_t fd, short what, void *arg) | |
497 | { | |
d6e889b6 VB |
498 | struct lldpd_hardware *hardware = arg; |
499 | struct lldpd *cfg = hardware->h_cfg; | |
5fd6695c | 500 | (void)what; |
d6e889b6 VB |
501 | lldpd_recv(cfg, hardware, fd); |
502 | } | |
503 | ||
504 | void | |
505 | levent_hardware_init(struct lldpd_hardware *hardware) | |
506 | { | |
507 | if ((hardware->h_recv = | |
508 | malloc(sizeof(struct ev_l))) == NULL) { | |
509 | LLOG_WARNX("unable to allocate memory for %s", | |
510 | hardware->h_ifname); | |
511 | return; | |
512 | } | |
513 | TAILQ_INIT(levent_hardware_fds(hardware)); | |
514 | } | |
515 | ||
516 | void | |
517 | levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd) | |
518 | { | |
5fd6695c | 519 | struct lldpd_events *hfd = NULL; |
d6e889b6 VB |
520 | if (!hardware->h_recv) return; |
521 | ||
5fd6695c | 522 | hfd = calloc(1, sizeof(struct lldpd_events)); |
d6e889b6 VB |
523 | if (!hfd) { |
524 | LLOG_WARNX("unable to allocate new event for %s", | |
525 | hardware->h_ifname); | |
526 | return; | |
527 | } | |
528 | evutil_make_socket_nonblocking(fd); | |
529 | if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd, | |
530 | EV_READ | EV_PERSIST, | |
531 | levent_hardware_recv, | |
532 | hardware)) == NULL) { | |
533 | LLOG_WARNX("unable to allocate a new event for %s", | |
534 | hardware->h_ifname); | |
535 | free(hfd); | |
536 | return; | |
537 | } | |
538 | if (event_add(hfd->ev, NULL) == -1) { | |
539 | LLOG_WARNX("unable to schedule new event for %s", | |
540 | hardware->h_ifname); | |
541 | event_free(hfd->ev); | |
542 | free(hfd); | |
543 | return; | |
544 | } | |
545 | TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next); | |
546 | } | |
547 | ||
548 | void | |
549 | levent_hardware_release(struct lldpd_hardware *hardware) | |
550 | { | |
5fd6695c | 551 | struct lldpd_events *ev, *ev_next; |
d6e889b6 VB |
552 | if (!hardware->h_recv) return; |
553 | ||
d6e889b6 VB |
554 | for (ev = TAILQ_FIRST(levent_hardware_fds(hardware)); |
555 | ev; | |
556 | ev = ev_next) { | |
557 | ev_next = TAILQ_NEXT(ev, next); | |
558 | /* We may close several time the same FD. This is harmless. */ | |
559 | close(event_get_fd(ev->ev)); | |
560 | event_free(ev->ev); | |
561 | TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next); | |
562 | free(ev); | |
563 | } | |
564 | free(levent_hardware_fds(hardware)); | |
565 | } |