]>
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) { | |
26fa5d17 VB |
31 | case _EVENT_LOG_DEBUG: log_debug("libevent", "%s", msg); break; |
32 | case _EVENT_LOG_MSG: log_info ("libevent", "%s", msg); break; | |
33 | case _EVENT_LOG_WARN: log_warnx("libevent", "%s", msg); break; | |
34 | case _EVENT_LOG_ERR: log_warnx("libevent", "%s", msg); break; | |
d6e889b6 VB |
35 | } |
36 | } | |
37 | ||
d6e889b6 VB |
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) { | |
6f8925be | 103 | log_warn("event", "unable to allocate memory for new SNMP event"); |
d6e889b6 VB |
104 | return; |
105 | } | |
106 | evutil_make_socket_nonblocking(fd); | |
107 | if ((snmpfd->ev = event_new(base, fd, | |
108 | EV_READ | EV_PERSIST, | |
109 | levent_snmp_read, | |
110 | cfg)) == NULL) { | |
6f8925be | 111 | log_warnx("event", "unable to allocate a new SNMP event for FD %d", fd); |
d6e889b6 VB |
112 | free(snmpfd); |
113 | return; | |
114 | } | |
115 | if (event_add(snmpfd->ev, NULL) == -1) { | |
6f8925be | 116 | log_warnx("event", "unable to schedule new SNMP event for FD %d", fd); |
d6e889b6 VB |
117 | event_free(snmpfd->ev); |
118 | free(snmpfd); | |
119 | return; | |
120 | } | |
121 | TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next); | |
122 | } | |
123 | ||
124 | /* | |
125 | * Update SNMP event loop. | |
126 | * | |
127 | * New events are added and some other are removed. This function | |
128 | * should be called every time a SNMP event happens: either when | |
129 | * handling a SNMP packet, a SNMP timeout or when sending a SNMP | |
130 | * packet. This function will keep libevent in sync with NetSNMP. | |
131 | * | |
132 | * @param base The libevent base we are working on. | |
133 | */ | |
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) { | |
6f8925be | 184 | log_debug("event", "added %d events, removed %d events, total of %d events", |
d6e889b6 VB |
185 | added, removed, current); |
186 | howmany = current; | |
187 | } | |
188 | ||
189 | /* If needed, handle timeout */ | |
190 | if (evtimer_add(cfg->g_snmp_timeout, block?NULL:&timeout) == -1) | |
6f8925be | 191 | log_warnx("event", "unable to schedule timeout function for SNMP"); |
d6e889b6 VB |
192 | } |
193 | #endif /* USE_SNMP */ | |
194 | ||
257db885 | 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)) { | |
6f8925be | 221 | log_warnx("event", "unable to create answer to client"); |
4e90a9e0 | 222 | levent_ctl_free_client(client); |
e0478a46 VB |
223 | return -1; |
224 | } | |
225 | bufferevent_enable(bev, EV_WRITE); | |
226 | return len; | |
227 | } | |
228 | ||
4e90a9e0 VB |
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 | 241 | /* Don't use TAILQ_FOREACH, the client may be deleted in case of errors. */ |
6f8925be | 242 | log_debug("control", "notify clients of neighbor changes"); |
4e90a9e0 VB |
243 | for (client = TAILQ_FIRST(&lldpd_clients); |
244 | client; | |
245 | client = client_next) { | |
246 | client_next = TAILQ_NEXT(client, next); | |
247 | if (!client->subscribed) continue; | |
be969691 VB |
248 | |
249 | if (output == NULL) { | |
250 | /* Ugly hack: we don't want to transmit a list of | |
74e0080e | 251 | * ports. We patch the port to avoid this. */ |
be969691 VB |
252 | TAILQ_ENTRY(lldpd_port) backup_p_entries; |
253 | memcpy(&backup_p_entries, &neighbor->p_entries, | |
254 | sizeof(backup_p_entries)); | |
be969691 VB |
255 | memset(&neighbor->p_entries, 0, |
256 | sizeof(backup_p_entries)); | |
be969691 VB |
257 | output_len = marshal_serialize(lldpd_neighbor_change, |
258 | &neigh, &output); | |
259 | memcpy(&neighbor->p_entries, &backup_p_entries, | |
260 | sizeof(backup_p_entries)); | |
be969691 VB |
261 | |
262 | if (output_len <= 0) { | |
6f8925be | 263 | log_warnx("event", "unable to serialize changed neighbor"); |
be969691 VB |
264 | return; |
265 | } | |
266 | } | |
267 | ||
4e90a9e0 VB |
268 | levent_ctl_send(client, NOTIFICATION, output, output_len); |
269 | } | |
270 | ||
271 | free(output); | |
272 | } | |
273 | ||
274 | static ssize_t | |
275 | levent_ctl_send_cb(void *out, int type, void *data, size_t len) | |
276 | { | |
277 | struct lldpd_one_client *client = out; | |
278 | return levent_ctl_send(client, type, data, len); | |
279 | } | |
280 | ||
d6e889b6 | 281 | static void |
e0478a46 | 282 | levent_ctl_recv(struct bufferevent *bev, void *ptr) |
d6e889b6 | 283 | { |
e0478a46 VB |
284 | struct lldpd_one_client *client = ptr; |
285 | struct evbuffer *buffer = bufferevent_get_input(bev); | |
286 | size_t buffer_len = evbuffer_get_length(buffer); | |
287 | struct hmsg_header hdr; | |
288 | void *data = NULL; | |
289 | ||
6f8925be | 290 | log_debug("control", "receive data on Unix socket"); |
e0478a46 VB |
291 | if (buffer_len < sizeof(struct hmsg_header)) |
292 | return; /* Not enough data yet */ | |
293 | if (evbuffer_copyout(buffer, &hdr, | |
294 | sizeof(struct hmsg_header)) != sizeof(struct hmsg_header)) { | |
6f8925be | 295 | log_warnx("event", "not able to read header"); |
e0478a46 VB |
296 | return; |
297 | } | |
82374540 | 298 | if (hdr.len > HMSG_MAX_SIZE) { |
6f8925be | 299 | log_warnx("event", "message received is too large"); |
e0478a46 VB |
300 | goto recv_error; |
301 | } | |
d6e889b6 | 302 | |
e0478a46 VB |
303 | if (buffer_len < hdr.len + sizeof(struct hmsg_header)) |
304 | return; /* Not enough data yet */ | |
305 | if (hdr.len > 0 && (data = malloc(hdr.len)) == NULL) { | |
6f8925be | 306 | log_warnx("event", "not enough memory"); |
e0478a46 VB |
307 | goto recv_error; |
308 | } | |
309 | evbuffer_drain(buffer, sizeof(struct hmsg_header)); | |
310 | if (hdr.len > 0) evbuffer_remove(buffer, data, hdr.len); | |
4e90a9e0 VB |
311 | |
312 | /* Currently, we should not receive notification acknowledgment. But if | |
313 | * we receive one, we can discard it. */ | |
314 | if (hdr.len == 0 && hdr.type == NOTIFICATION) return; | |
e0478a46 | 315 | if (client_handle_client(client->cfg, |
4e90a9e0 VB |
316 | levent_ctl_send_cb, client, |
317 | hdr.type, data, hdr.len, | |
318 | &client->subscribed) == -1) goto recv_error; | |
e0478a46 VB |
319 | free(data); |
320 | return; | |
321 | ||
322 | recv_error: | |
323 | free(data); | |
4e90a9e0 | 324 | levent_ctl_free_client(client); |
e0478a46 VB |
325 | } |
326 | ||
327 | static void | |
328 | levent_ctl_event(struct bufferevent *bev, short events, void *ptr) | |
329 | { | |
330 | struct lldpd_one_client *client = ptr; | |
331 | if (events & BEV_EVENT_ERROR) { | |
6f8925be | 332 | log_warnx("event", "an error occurred with client: %s", |
e0478a46 | 333 | evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); |
4e90a9e0 | 334 | levent_ctl_free_client(client); |
e0478a46 | 335 | } else if (events & BEV_EVENT_EOF) { |
6f8925be | 336 | log_debug("event", "client has been disconnected"); |
4e90a9e0 | 337 | levent_ctl_free_client(client); |
d6e889b6 | 338 | } |
d6e889b6 VB |
339 | } |
340 | ||
341 | static void | |
342 | levent_ctl_accept(evutil_socket_t fd, short what, void *arg) | |
343 | { | |
d6e889b6 | 344 | struct lldpd *cfg = arg; |
257db885 | 345 | struct lldpd_one_client *client = NULL; |
d6e889b6 | 346 | int s; |
5fd6695c VB |
347 | (void)what; |
348 | ||
6f8925be | 349 | log_debug("control", "accept a new connection"); |
d6e889b6 | 350 | if ((s = accept(fd, NULL, NULL)) == -1) { |
6f8925be | 351 | log_warn("event", "unable to accept connection from socket"); |
d6e889b6 VB |
352 | return; |
353 | } | |
257db885 VB |
354 | client = calloc(1, sizeof(struct lldpd_one_client)); |
355 | if (!client) { | |
6f8925be | 356 | log_warnx("event", "unable to allocate memory for new client"); |
4e90a9e0 | 357 | close(s); |
257db885 | 358 | goto accept_failed; |
d6e889b6 | 359 | } |
257db885 | 360 | client->cfg = cfg; |
d6e889b6 | 361 | evutil_make_socket_nonblocking(s); |
e0478a46 VB |
362 | if ((client->bev = bufferevent_socket_new(cfg->g_base, s, |
363 | BEV_OPT_CLOSE_ON_FREE)) == NULL) { | |
6f8925be | 364 | log_warnx("event", "unable to allocate a new buffer event for new client"); |
4e90a9e0 | 365 | close(s); |
257db885 | 366 | goto accept_failed; |
d6e889b6 | 367 | } |
e0478a46 VB |
368 | bufferevent_setcb(client->bev, |
369 | levent_ctl_recv, NULL, levent_ctl_event, | |
370 | client); | |
371 | bufferevent_enable(client->bev, EV_READ | EV_WRITE); | |
6f8925be | 372 | log_debug("event", "new client accepted"); |
4e90a9e0 | 373 | TAILQ_INSERT_TAIL(&lldpd_clients, client, next); |
257db885 VB |
374 | return; |
375 | accept_failed: | |
4e90a9e0 | 376 | levent_ctl_free_client(client); |
d6e889b6 VB |
377 | } |
378 | ||
379 | static void | |
380 | levent_dump(evutil_socket_t fd, short what, void *arg) | |
381 | { | |
d6e889b6 | 382 | struct event_base *base = arg; |
5fd6695c | 383 | (void)fd; (void)what; |
6f8925be | 384 | log_debug("event", "dumping all events"); |
d6e889b6 VB |
385 | event_base_dump_events(base, stderr); |
386 | } | |
387 | static void | |
388 | levent_stop(evutil_socket_t fd, short what, void *arg) | |
389 | { | |
d6e889b6 | 390 | struct event_base *base = arg; |
5fd6695c | 391 | (void)fd; (void)what; |
d6e889b6 VB |
392 | event_base_loopbreak(base); |
393 | } | |
394 | ||
395 | static void | |
396 | levent_update_and_send(evutil_socket_t fd, short what, void *arg) | |
397 | { | |
d6e889b6 | 398 | struct lldpd *cfg = arg; |
579bedd5 | 399 | struct timeval tv = { cfg->g_config.c_tx_interval, 0 }; |
5fd6695c | 400 | (void)fd; (void)what; |
d6e889b6 | 401 | lldpd_loop(cfg); |
579bedd5 VB |
402 | if (cfg->g_iface_event != NULL) |
403 | tv.tv_sec *= 20; | |
d6e889b6 VB |
404 | event_add(cfg->g_main_loop, &tv); |
405 | } | |
406 | ||
47287a61 VB |
407 | void |
408 | levent_send_now(struct lldpd *cfg) | |
409 | { | |
410 | event_active(cfg->g_main_loop, EV_TIMEOUT, 1); | |
411 | } | |
412 | ||
d6e889b6 VB |
413 | static void |
414 | levent_init(struct lldpd *cfg) | |
415 | { | |
416 | /* Setup libevent */ | |
6f8925be | 417 | log_debug("event", "initialize libevent"); |
d6e889b6 VB |
418 | event_set_log_callback(levent_log_cb); |
419 | if (!(cfg->g_base = event_base_new())) | |
420 | fatalx("unable to create a new libevent base"); | |
6f8925be | 421 | log_info("event", "libevent %s initialized with %s method", |
d6e889b6 VB |
422 | event_get_version(), |
423 | event_base_get_method(cfg->g_base)); | |
424 | ||
425 | /* Setup SNMP */ | |
426 | #ifdef USE_SNMP | |
427 | if (cfg->g_snmp) { | |
428 | agent_init(cfg, cfg->g_snmp_agentx); | |
429 | cfg->g_snmp_timeout = evtimer_new(cfg->g_base, | |
430 | levent_snmp_timeout, | |
431 | cfg); | |
432 | if (!cfg->g_snmp_timeout) | |
433 | fatalx("unable to setup timeout function for SNMP"); | |
434 | if ((cfg->g_snmp_fds = | |
435 | malloc(sizeof(struct ev_l))) == NULL) | |
436 | fatalx("unable to allocate memory for SNMP events"); | |
437 | TAILQ_INIT(levent_snmp_fds(cfg)); | |
438 | } | |
439 | #endif | |
6f8925be | 440 | |
579bedd5 | 441 | /* Setup loop that will run every X seconds. */ |
6f8925be | 442 | log_debug("event", "register loop timer"); |
d6e889b6 VB |
443 | if (!(cfg->g_main_loop = event_new(cfg->g_base, -1, 0, |
444 | levent_update_and_send, | |
445 | cfg))) | |
446 | fatalx("unable to setup main timer"); | |
47287a61 | 447 | levent_send_now(cfg); |
d6e889b6 VB |
448 | |
449 | /* Setup unix socket */ | |
6f8925be | 450 | log_debug("event", "register Unix socket"); |
4e90a9e0 | 451 | TAILQ_INIT(&lldpd_clients); |
d6e889b6 VB |
452 | evutil_make_socket_nonblocking(cfg->g_ctl); |
453 | if ((cfg->g_ctl_event = event_new(cfg->g_base, cfg->g_ctl, | |
454 | EV_READ|EV_PERSIST, levent_ctl_accept, cfg)) == NULL) | |
455 | fatalx("unable to setup control socket event"); | |
456 | event_add(cfg->g_ctl_event, NULL); | |
457 | ||
458 | /* Signals */ | |
6f8925be | 459 | log_debug("event", "register signals"); |
d6e889b6 VB |
460 | evsignal_add(evsignal_new(cfg->g_base, SIGUSR1, |
461 | levent_dump, cfg->g_base), | |
462 | NULL); | |
463 | evsignal_add(evsignal_new(cfg->g_base, SIGHUP, | |
464 | levent_stop, cfg->g_base), | |
465 | NULL); | |
466 | evsignal_add(evsignal_new(cfg->g_base, SIGINT, | |
467 | levent_stop, cfg->g_base), | |
468 | NULL); | |
469 | evsignal_add(evsignal_new(cfg->g_base, SIGTERM, | |
470 | levent_stop, cfg->g_base), | |
471 | NULL); | |
472 | } | |
473 | ||
474 | /* Initialize libevent and start the event loop */ | |
475 | void | |
476 | levent_loop(struct lldpd *cfg) | |
477 | { | |
478 | levent_init(cfg); | |
479 | ||
480 | /* libevent loop */ | |
481 | do { | |
482 | if (event_base_got_break(cfg->g_base) || | |
483 | event_base_got_exit(cfg->g_base)) | |
484 | break; | |
485 | #ifdef USE_SNMP | |
486 | if (cfg->g_snmp) { | |
219c432e VB |
487 | /* We don't use delegated requests (request |
488 | whose answer is delayed). However, we keep | |
489 | the call here in case we use it some | |
490 | day. We don't call run_alarms() here. We do | |
491 | it on timeout only. */ | |
d6e889b6 | 492 | netsnmp_check_outstanding_agent_requests(); |
d6e889b6 VB |
493 | levent_snmp_update(cfg); |
494 | } | |
495 | #endif | |
496 | } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0); | |
497 | ||
498 | #ifdef USE_SNMP | |
499 | if (cfg->g_snmp) | |
500 | agent_shutdown(); | |
501 | #endif /* USE_SNMP */ | |
502 | ||
503 | } | |
504 | ||
505 | static void | |
506 | levent_hardware_recv(evutil_socket_t fd, short what, void *arg) | |
507 | { | |
d6e889b6 VB |
508 | struct lldpd_hardware *hardware = arg; |
509 | struct lldpd *cfg = hardware->h_cfg; | |
5fd6695c | 510 | (void)what; |
6f8925be VB |
511 | log_debug("event", "received something for %s", |
512 | hardware->h_ifname); | |
d6e889b6 VB |
513 | lldpd_recv(cfg, hardware, fd); |
514 | } | |
515 | ||
516 | void | |
517 | levent_hardware_init(struct lldpd_hardware *hardware) | |
518 | { | |
6f8925be | 519 | log_debug("event", "initialize events for %s", hardware->h_ifname); |
d6e889b6 VB |
520 | if ((hardware->h_recv = |
521 | malloc(sizeof(struct ev_l))) == NULL) { | |
6f8925be | 522 | log_warnx("event", "unable to allocate memory for %s", |
d6e889b6 VB |
523 | hardware->h_ifname); |
524 | return; | |
525 | } | |
526 | TAILQ_INIT(levent_hardware_fds(hardware)); | |
527 | } | |
528 | ||
529 | void | |
530 | levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd) | |
531 | { | |
5fd6695c | 532 | struct lldpd_events *hfd = NULL; |
d6e889b6 VB |
533 | if (!hardware->h_recv) return; |
534 | ||
5fd6695c | 535 | hfd = calloc(1, sizeof(struct lldpd_events)); |
d6e889b6 | 536 | if (!hfd) { |
6f8925be | 537 | log_warnx("event", "unable to allocate new event for %s", |
d6e889b6 VB |
538 | hardware->h_ifname); |
539 | return; | |
540 | } | |
541 | evutil_make_socket_nonblocking(fd); | |
542 | if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd, | |
543 | EV_READ | EV_PERSIST, | |
544 | levent_hardware_recv, | |
545 | hardware)) == NULL) { | |
6f8925be | 546 | log_warnx("event", "unable to allocate a new event for %s", |
d6e889b6 VB |
547 | hardware->h_ifname); |
548 | free(hfd); | |
549 | return; | |
550 | } | |
551 | if (event_add(hfd->ev, NULL) == -1) { | |
6f8925be | 552 | log_warnx("event", "unable to schedule new event for %s", |
d6e889b6 VB |
553 | hardware->h_ifname); |
554 | event_free(hfd->ev); | |
555 | free(hfd); | |
556 | return; | |
557 | } | |
558 | TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next); | |
559 | } | |
560 | ||
561 | void | |
562 | levent_hardware_release(struct lldpd_hardware *hardware) | |
563 | { | |
5fd6695c | 564 | struct lldpd_events *ev, *ev_next; |
579bedd5 | 565 | event_free(hardware->h_timer); hardware->h_timer = NULL; |
d6e889b6 VB |
566 | if (!hardware->h_recv) return; |
567 | ||
6f8925be | 568 | log_debug("event", "release events for %s", hardware->h_ifname); |
d6e889b6 VB |
569 | for (ev = TAILQ_FIRST(levent_hardware_fds(hardware)); |
570 | ev; | |
571 | ev = ev_next) { | |
572 | ev_next = TAILQ_NEXT(ev, next); | |
573 | /* We may close several time the same FD. This is harmless. */ | |
574 | close(event_get_fd(ev->ev)); | |
575 | event_free(ev->ev); | |
576 | TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next); | |
577 | free(ev); | |
578 | } | |
579 | free(levent_hardware_fds(hardware)); | |
580 | } | |
0484f180 VB |
581 | |
582 | static void | |
583 | levent_iface_trigger(evutil_socket_t fd, short what, void *arg) | |
584 | { | |
585 | struct lldpd *cfg = arg; | |
586 | log_info("event", | |
587 | "triggering update of all interfaces"); | |
588 | lldpd_update_localports(cfg); | |
589 | } | |
590 | ||
591 | static void | |
592 | levent_iface_recv(evutil_socket_t fd, short what, void *arg) | |
593 | { | |
594 | struct lldpd *cfg = arg; | |
595 | char buffer[100]; | |
596 | int n; | |
597 | ||
598 | /* Discard the message */ | |
599 | while (1) { | |
600 | n = read(fd, buffer, sizeof(buffer)); | |
601 | if (n == -1 && | |
602 | (errno == EWOULDBLOCK || | |
603 | errno == EAGAIN)) break; | |
604 | if (n == -1) { | |
605 | log_warn("event", | |
606 | "unable to receive interface change notification message"); | |
607 | return; | |
608 | } | |
609 | if (n == 0) { | |
610 | log_warnx("event", | |
611 | "end of file reached while getting interface change notification message"); | |
612 | return; | |
613 | } | |
614 | } | |
615 | ||
616 | /* Schedule local port update. We don't run it right away because we may | |
617 | * receive a batch of events like this. */ | |
618 | struct timeval one_sec = {1, 0}; | |
619 | log_debug("event", | |
620 | "received notification change, schedule an update of all interfaces in one second"); | |
621 | if (cfg->g_iface_timer_event == NULL) { | |
622 | if ((cfg->g_iface_timer_event = evtimer_new(cfg->g_base, | |
623 | levent_iface_trigger, cfg)) == NULL) { | |
624 | log_warnx("event", | |
625 | "unable to create a new event to trigger interface update"); | |
626 | return; | |
627 | } | |
628 | } | |
629 | if (evtimer_add(cfg->g_iface_timer_event, &one_sec) == -1) { | |
630 | log_warnx("event", | |
631 | "unable to schedule interface updates"); | |
632 | return; | |
633 | } | |
634 | } | |
635 | ||
aa313f2a | 636 | int |
0484f180 VB |
637 | levent_iface_subscribe(struct lldpd *cfg, int socket) |
638 | { | |
639 | log_debug("event", "subscribe to interface changes from socket %d", | |
640 | socket); | |
641 | evutil_make_socket_nonblocking(socket); | |
642 | cfg->g_iface_event = event_new(cfg->g_base, socket, | |
643 | EV_READ | EV_PERSIST, levent_iface_recv, cfg); | |
644 | if (cfg->g_iface_event == NULL) { | |
645 | log_warnx("event", | |
646 | "unable to allocate a new event for interface changes"); | |
aa313f2a | 647 | return -1; |
0484f180 VB |
648 | } |
649 | if (event_add(cfg->g_iface_event, NULL) == -1) { | |
650 | log_warnx("event", | |
651 | "unable to schedule new interface changes event"); | |
652 | event_free(cfg->g_iface_event); | |
653 | cfg->g_iface_event = NULL; | |
aa313f2a | 654 | return -1; |
0484f180 | 655 | } |
aa313f2a | 656 | return 0; |
0484f180 | 657 | } |
579bedd5 VB |
658 | |
659 | static void | |
660 | levent_send_pdu(evutil_socket_t fd, short what, void *arg) | |
661 | { | |
662 | struct lldpd_hardware *hardware = arg; | |
663 | log_debug("event", "trigger sending PDU for port %s", | |
664 | hardware->h_ifname); | |
665 | lldpd_send(hardware); | |
666 | ||
667 | struct timeval tv = { hardware->h_cfg->g_config.c_tx_interval, 0 }; | |
668 | if (event_add(hardware->h_timer, &tv) == -1) { | |
669 | log_warnx("event", "unable to re-register timer event for port %s", | |
670 | hardware->h_ifname); | |
671 | event_free(hardware->h_timer); | |
672 | hardware->h_timer = NULL; | |
673 | return; | |
674 | } | |
675 | } | |
676 | ||
677 | void | |
678 | levent_schedule_pdu(struct lldpd_hardware *hardware) | |
679 | { | |
680 | log_debug("event", "schedule sending PDU on %s", | |
681 | hardware->h_ifname); | |
682 | if (hardware->h_timer == NULL) { | |
683 | hardware->h_timer = evtimer_new(hardware->h_cfg->g_base, | |
684 | levent_send_pdu, hardware); | |
685 | if (hardware->h_timer == NULL) { | |
686 | log_warnx("event", "unable to schedule PDU sending for port %s", | |
687 | hardware->h_ifname); | |
688 | return; | |
689 | } | |
690 | } | |
691 | ||
692 | struct timeval tv = { 0, 0 }; | |
693 | if (event_add(hardware->h_timer, &tv) == -1) { | |
694 | log_warnx("event", "unable to register timer event for port %s", | |
695 | hardware->h_ifname); | |
696 | event_free(hardware->h_timer); | |
697 | hardware->h_timer = NULL; | |
698 | return; | |
699 | } | |
700 | } |