Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lldpd/src/daemon/event.c
Line
Count
Source
1
/* -*- mode: c; c-file-style: "openbsd" -*- */
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
#include "trace.h"
20
21
#include <unistd.h>
22
#include <signal.h>
23
#include <errno.h>
24
#include <time.h>
25
#include <fcntl.h>
26
#if defined(__clang__)
27
#  pragma clang diagnostic push
28
#  pragma clang diagnostic ignored "-Wdocumentation"
29
#endif
30
#include <event2/event.h>
31
#include <event2/bufferevent.h>
32
#include <event2/buffer.h>
33
#if defined(__clang__)
34
#  pragma clang diagnostic pop
35
#endif
36
37
#define EVENT_BUFFER 1024
38
39
static void
40
levent_log_cb(int severity, const char *msg)
41
0
{
42
0
  switch (severity) {
43
0
  case _EVENT_LOG_DEBUG:
44
0
    log_debug("libevent", "%s", msg);
45
0
    break;
46
0
  case _EVENT_LOG_MSG:
47
0
    log_info("libevent", "%s", msg);
48
0
    break;
49
0
  case _EVENT_LOG_WARN:
50
0
    log_warnx("libevent", "%s", msg);
51
0
    break;
52
0
  case _EVENT_LOG_ERR:
53
0
    log_warnx("libevent", "%s", msg);
54
0
    break;
55
0
  }
56
0
}
57
58
struct lldpd_events {
59
  TAILQ_ENTRY(lldpd_events) next;
60
  struct event *ev;
61
};
62
TAILQ_HEAD(ev_l, lldpd_events);
63
64
#define levent_snmp_fds(cfg) ((struct ev_l *)(cfg)->g_snmp_fds)
65
0
#define levent_hardware_fds(hardware) ((struct ev_l *)(hardware)->h_recv)
66
67
#ifdef USE_SNMP
68
#  include <net-snmp/net-snmp-config.h>
69
#  include <net-snmp/net-snmp-includes.h>
70
#  include <net-snmp/agent/net-snmp-agent-includes.h>
71
#  include <net-snmp/agent/snmp_vars.h>
72
73
/* Compatibility with older versions of NetSNMP */
74
#  ifndef HAVE_SNMP_SELECT_INFO2
75
#    define netsnmp_large_fd_set fd_set
76
#    define snmp_read2 snmp_read
77
#    define snmp_select_info2 snmp_select_info
78
#    define netsnmp_large_fd_set_init(...)
79
#    define netsnmp_large_fd_set_cleanup(...)
80
#    define NETSNMP_LARGE_FD_SET FD_SET
81
#    define NETSNMP_LARGE_FD_CLR FD_CLR
82
#    define NETSNMP_LARGE_FD_ZERO FD_ZERO
83
#    define NETSNMP_LARGE_FD_ISSET FD_ISSET
84
#  else
85
#    include <net-snmp/library/large_fd_set.h>
86
#  endif
87
88
static void levent_snmp_update(struct lldpd *);
89
90
/*
91
 * Callback function when we have something to read from SNMP.
92
 *
93
 * This function is called because we have a read event on one SNMP
94
 * file descriptor. When need to call snmp_read() on it.
95
 */
96
static void
97
levent_snmp_read(evutil_socket_t fd, short what, void *arg)
98
{
99
  struct lldpd *cfg = arg;
100
  netsnmp_large_fd_set fdset;
101
  (void)what;
102
  netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
103
  NETSNMP_LARGE_FD_ZERO(&fdset);
104
  NETSNMP_LARGE_FD_SET(fd, &fdset);
105
  snmp_read2(&fdset);
106
  netsnmp_large_fd_set_cleanup(&fdset);
107
  levent_snmp_update(cfg);
108
}
109
110
/*
111
 * Callback function for a SNMP timeout.
112
 *
113
 * A SNMP timeout has occurred. Call `snmp_timeout()` to handle it.
114
 */
115
static void
116
levent_snmp_timeout(evutil_socket_t fd, short what, void *arg)
117
{
118
  struct lldpd *cfg = arg;
119
  (void)what;
120
  (void)fd;
121
  snmp_timeout();
122
  run_alarms();
123
  levent_snmp_update(cfg);
124
}
125
126
/*
127
 * Watch a new SNMP FD.
128
 *
129
 * @param base The libevent base we are working on.
130
 * @param fd The file descriptor we want to watch.
131
 *
132
 * The file descriptor is appended to the list of file descriptors we
133
 * want to watch.
134
 */
135
static void
136
levent_snmp_add_fd(struct lldpd *cfg, int fd)
137
{
138
  struct event_base *base = cfg->g_base;
139
  struct lldpd_events *snmpfd = calloc(1, sizeof(struct lldpd_events));
140
  if (!snmpfd) {
141
    log_warn("event", "unable to allocate memory for new SNMP event");
142
    return;
143
  }
144
  levent_make_socket_nonblocking(fd);
145
  if ((snmpfd->ev = event_new(base, fd, EV_READ | EV_PERSIST, levent_snmp_read,
146
     cfg)) == NULL) {
147
    log_warnx("event", "unable to allocate a new SNMP event for FD %d", fd);
148
    free(snmpfd);
149
    return;
150
  }
151
  if (event_add(snmpfd->ev, NULL) == -1) {
152
    log_warnx("event", "unable to schedule new SNMP event for FD %d", fd);
153
    event_free(snmpfd->ev);
154
    free(snmpfd);
155
    return;
156
  }
157
  TAILQ_INSERT_TAIL(levent_snmp_fds(cfg), snmpfd, next);
158
}
159
160
/*
161
 * Update SNMP event loop.
162
 *
163
 * New events are added and some other are removed. This function
164
 * should be called every time a SNMP event happens: either when
165
 * handling a SNMP packet, a SNMP timeout or when sending a SNMP
166
 * packet. This function will keep libevent in sync with NetSNMP.
167
 *
168
 * @param base The libevent base we are working on.
169
 */
170
static void
171
levent_snmp_update(struct lldpd *cfg)
172
{
173
  int maxfd = 0;
174
  int block = 1;
175
  struct timeval timeout;
176
  static int howmany = 0;
177
  int added = 0, removed = 0, current = 0;
178
  struct lldpd_events *snmpfd, *snmpfd_next;
179
180
  /* snmp_select_info() can be tricky to understand. We set `block` to
181
     1 to means that we don't request a timeout. snmp_select_info()
182
     will reset `block` to 0 if it wants us to set up a timeout. In
183
     this timeout, `snmp_timeout()` should be invoked.
184
185
     Each FD in `fdset` will need to be watched for reading. If one of
186
     them become active, `snmp_read()` should be called on it.
187
  */
188
189
  netsnmp_large_fd_set fdset;
190
  netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
191
  NETSNMP_LARGE_FD_ZERO(&fdset);
192
  snmp_select_info2(&maxfd, &fdset, &timeout, &block);
193
194
  /* We need to untrack any event whose FD is not in `fdset`
195
     anymore */
196
  for (snmpfd = TAILQ_FIRST(levent_snmp_fds(cfg)); snmpfd; snmpfd = snmpfd_next) {
197
    snmpfd_next = TAILQ_NEXT(snmpfd, next);
198
    if (event_get_fd(snmpfd->ev) >= maxfd ||
199
        (!NETSNMP_LARGE_FD_ISSET(event_get_fd(snmpfd->ev), &fdset))) {
200
      event_free(snmpfd->ev);
201
      TAILQ_REMOVE(levent_snmp_fds(cfg), snmpfd, next);
202
      free(snmpfd);
203
      removed++;
204
    } else {
205
      NETSNMP_LARGE_FD_CLR(event_get_fd(snmpfd->ev), &fdset);
206
      current++;
207
    }
208
  }
209
210
  /* Invariant: FD in `fdset` are not in list of FD */
211
  for (int fd = 0; fd < maxfd; fd++) {
212
    if (NETSNMP_LARGE_FD_ISSET(fd, &fdset)) {
213
      levent_snmp_add_fd(cfg, fd);
214
      added++;
215
    }
216
  }
217
  current += added;
218
  if (howmany != current) {
219
    log_debug("event",
220
        "added %d events, removed %d events, total of %d events", added,
221
        removed, current);
222
    howmany = current;
223
  }
224
225
  /* If needed, handle timeout */
226
  if (evtimer_add(cfg->g_snmp_timeout, block ? NULL : &timeout) == -1)
227
    log_warnx("event", "unable to schedule timeout function for SNMP");
228
229
  netsnmp_large_fd_set_cleanup(&fdset);
230
}
231
#endif /* USE_SNMP */
232
233
struct lldpd_one_client {
234
  TAILQ_ENTRY(lldpd_one_client) next;
235
  struct lldpd *cfg;
236
  struct bufferevent *bev;
237
  int subscribed; /* Is this client subscribed to changes? */
238
};
239
TAILQ_HEAD(, lldpd_one_client) lldpd_clients;
240
241
static void
242
levent_ctl_free_client(struct lldpd_one_client *client)
243
0
{
244
0
  if (client && client->bev) bufferevent_free(client->bev);
245
0
  if (client) {
246
0
    TAILQ_REMOVE(&lldpd_clients, client, next);
247
0
    free(client);
248
0
  }
249
0
}
250
251
static void
252
levent_ctl_close_clients()
253
0
{
254
0
  struct lldpd_one_client *client, *client_next;
255
0
  for (client = TAILQ_FIRST(&lldpd_clients); client; client = client_next) {
256
0
    client_next = TAILQ_NEXT(client, next);
257
0
    levent_ctl_free_client(client);
258
0
  }
259
0
}
260
261
/* Returns the number of bytes written, or -1 on error. On error, the caller
262
 * is responsible for freeing the client. */
263
static ssize_t
264
levent_ctl_send(struct lldpd_one_client *client, int type, void *data, size_t len)
265
0
{
266
0
  struct bufferevent *bev = client->bev;
267
0
  struct hmsg_header hdr = { .len = len, .type = type };
268
0
  bufferevent_disable(bev, EV_WRITE);
269
0
  if (bufferevent_write(bev, &hdr, sizeof(struct hmsg_header)) == -1 ||
270
0
      (len > 0 && bufferevent_write(bev, data, len) == -1)) {
271
0
    log_warnx("event", "unable to create answer to client");
272
0
    return -1;
273
0
  }
274
0
  bufferevent_enable(bev, EV_WRITE);
275
0
  return len;
276
0
}
277
278
void
279
levent_ctl_notify(char *ifname, char *ifalias, int state, struct lldpd_port *neighbor)
280
0
{
281
0
  struct lldpd_one_client *client, *client_next;
282
0
  struct lldpd_neighbor_change neigh = { .ifname = ifname,
283
0
    .ifalias = ifalias,
284
0
    .state = state,
285
0
    .neighbor = neighbor };
286
0
  void *output = NULL;
287
0
  ssize_t output_len = 0;
288
289
  /* Don't use TAILQ_FOREACH, the client may be deleted in case of errors. */
290
0
  log_debug("control", "notify clients of neighbor changes");
291
0
  for (client = TAILQ_FIRST(&lldpd_clients); client; client = client_next) {
292
0
    client_next = TAILQ_NEXT(client, next);
293
0
    if (!client->subscribed) continue;
294
295
0
    if (output == NULL) {
296
      /* Ugly hack: we don't want to transmit a list of
297
       * ports. We patch the port to avoid this. */
298
0
      TAILQ_ENTRY(lldpd_port) backup_p_entries;
299
0
      memcpy(&backup_p_entries, &neighbor->p_entries,
300
0
          sizeof(backup_p_entries));
301
0
      memset(&neighbor->p_entries, 0, sizeof(backup_p_entries));
302
0
      output_len = lldpd_neighbor_change_serialize(&neigh, &output);
303
0
      memcpy(&neighbor->p_entries, &backup_p_entries,
304
0
          sizeof(backup_p_entries));
305
306
0
      if (output_len <= 0) {
307
0
        log_warnx("event",
308
0
            "unable to serialize changed neighbor");
309
0
        return;
310
0
      }
311
0
    }
312
313
0
    if (levent_ctl_send(client, NOTIFICATION, output, output_len) == -1)
314
0
      levent_ctl_free_client(client);
315
0
  }
316
317
0
  free(output);
318
0
}
319
320
static ssize_t
321
levent_ctl_send_cb(void *out, int type, void *data, size_t len)
322
0
{
323
0
  struct lldpd_one_client *client = out;
324
0
  return levent_ctl_send(client, type, data, len);
325
0
}
326
327
static void
328
levent_ctl_recv(struct bufferevent *bev, void *ptr)
329
0
{
330
0
  struct lldpd_one_client *client = ptr;
331
0
  struct evbuffer *buffer = bufferevent_get_input(bev);
332
0
  size_t buffer_len = evbuffer_get_length(buffer);
333
0
  struct hmsg_header hdr;
334
0
  void *data = NULL;
335
336
0
  log_debug("control", "receive data on Unix socket");
337
0
  if (buffer_len < sizeof(struct hmsg_header)) return; /* Not enough data yet */
338
0
  if (evbuffer_copyout(buffer, &hdr, sizeof(struct hmsg_header)) !=
339
0
      sizeof(struct hmsg_header)) {
340
0
    log_warnx("event", "not able to read header");
341
0
    return;
342
0
  }
343
0
  if (hdr.len > HMSG_MAX_SIZE) {
344
0
    log_warnx("event", "message received is too large");
345
0
    goto recv_error;
346
0
  }
347
348
0
  if (buffer_len < hdr.len + sizeof(struct hmsg_header))
349
0
    return; /* Not enough data yet */
350
0
  if (hdr.len > 0 && (data = malloc(hdr.len)) == NULL) {
351
0
    log_warnx("event", "not enough memory");
352
0
    goto recv_error;
353
0
  }
354
0
  evbuffer_drain(buffer, sizeof(struct hmsg_header));
355
0
  if (hdr.len > 0) evbuffer_remove(buffer, data, hdr.len);
356
357
  /* Currently, we should not receive notification acknowledgment. But if
358
   * we receive one, we can discard it. */
359
0
  if (hdr.len == 0 && hdr.type == NOTIFICATION) return;
360
0
  if (client_handle_client(client->cfg, levent_ctl_send_cb, client, hdr.type,
361
0
    data, hdr.len, &client->subscribed) == -1)
362
0
    goto recv_error;
363
0
  free(data);
364
0
  return;
365
366
0
recv_error:
367
0
  free(data);
368
0
  levent_ctl_free_client(client);
369
0
}
370
371
static void
372
levent_ctl_event(struct bufferevent *bev, short events, void *ptr)
373
0
{
374
0
  struct lldpd_one_client *client = ptr;
375
0
  if (events & BEV_EVENT_ERROR) {
376
0
    log_warnx("event", "an error occurred with client: %s",
377
0
        evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
378
0
    levent_ctl_free_client(client);
379
0
  } else if (events & BEV_EVENT_EOF) {
380
0
    log_debug("event", "client has been disconnected");
381
0
    levent_ctl_free_client(client);
382
0
  }
383
0
}
384
385
static void
386
levent_ctl_accept(evutil_socket_t fd, short what, void *arg)
387
0
{
388
0
  struct lldpd *cfg = arg;
389
0
  struct lldpd_one_client *client = NULL;
390
0
  int s;
391
0
  (void)what;
392
393
0
  log_debug("control", "accept a new connection");
394
0
  if ((s = accept(fd, NULL, NULL)) == -1) {
395
0
    log_warn("event", "unable to accept connection from socket");
396
0
    return;
397
0
  }
398
0
  client = calloc(1, sizeof(struct lldpd_one_client));
399
0
  if (!client) {
400
0
    log_warnx("event", "unable to allocate memory for new client");
401
0
    close(s);
402
0
    goto accept_failed;
403
0
  }
404
0
  client->cfg = cfg;
405
0
  levent_make_socket_nonblocking(s);
406
0
  TAILQ_INSERT_TAIL(&lldpd_clients, client, next);
407
0
  if ((client->bev = bufferevent_socket_new(cfg->g_base, s,
408
0
     BEV_OPT_CLOSE_ON_FREE)) == NULL) {
409
0
    log_warnx("event",
410
0
        "unable to allocate a new buffer event for new client");
411
0
    close(s);
412
0
    goto accept_failed;
413
0
  }
414
0
  bufferevent_setcb(client->bev, levent_ctl_recv, NULL, levent_ctl_event, client);
415
0
  bufferevent_enable(client->bev, EV_READ | EV_WRITE);
416
0
  log_debug("event", "new client accepted");
417
  /* coverity[leaked_handle]
418
     s has been saved by bufferevent_socket_new */
419
0
  return;
420
0
accept_failed:
421
0
  levent_ctl_free_client(client);
422
0
}
423
424
static void
425
levent_priv(evutil_socket_t fd, short what, void *arg)
426
0
{
427
0
  struct event_base *base = arg;
428
0
  ssize_t n;
429
0
  int err;
430
0
  char one;
431
0
  (void)what;
432
  /* Check if we have some data available. We need to pass the socket in
433
   * non-blocking mode to be able to run the check without disruption. */
434
0
  levent_make_socket_nonblocking(fd);
435
0
  n = read(fd, &one, 1);
436
0
  err = errno;
437
0
  levent_make_socket_blocking(fd);
438
439
0
  switch (n) {
440
0
  case -1:
441
0
    if (err == EAGAIN || err == EWOULDBLOCK) /* No data, all good */
442
0
      return;
443
0
    log_warnx("event", "unable to poll monitor process, exit");
444
0
    break;
445
0
  case 0:
446
0
    log_warnx("event", "monitor process has terminated, exit");
447
0
    break;
448
0
  default:
449
    /* This is a bit unsafe as we are now out-of-sync with the
450
     * monitor. It would be safer to request 0 byte, but some OS
451
     * (illumos) seem to take the shortcut that by asking 0 byte,
452
     * we can just return 0 byte. */
453
0
    log_warnx("event",
454
0
        "received unexpected data from monitor process, exit");
455
0
    break;
456
0
  }
457
0
  event_base_loopbreak(base);
458
0
}
459
460
static void
461
levent_dump(evutil_socket_t fd, short what, void *arg)
462
0
{
463
0
  struct event_base *base = arg;
464
0
  (void)fd;
465
0
  (void)what;
466
0
  log_debug("event", "dumping all events");
467
0
  event_base_dump_events(base, stderr);
468
0
}
469
static void
470
levent_stop(evutil_socket_t fd, short what, void *arg)
471
0
{
472
0
  struct event_base *base = arg;
473
0
  (void)fd;
474
0
  (void)what;
475
0
  event_base_loopbreak(base);
476
0
}
477
478
static void
479
levent_update_and_send(evutil_socket_t fd, short what, void *arg)
480
0
{
481
0
  struct lldpd *cfg = arg;
482
0
  struct timeval tv;
483
0
  long interval_ms = cfg->g_config.c_tx_interval;
484
485
0
  (void)fd;
486
0
  (void)what;
487
0
  lldpd_loop(cfg);
488
0
  if (cfg->g_iface_event != NULL) interval_ms *= 20;
489
0
  if (interval_ms < 30000) interval_ms = 30000;
490
0
  tv.tv_sec = interval_ms / 1000;
491
0
  tv.tv_usec = (interval_ms % 1000) * 1000;
492
0
  event_add(cfg->g_main_loop, &tv);
493
0
}
494
495
void
496
levent_update_now(struct lldpd *cfg)
497
0
{
498
0
  if (cfg->g_main_loop) event_active(cfg->g_main_loop, EV_TIMEOUT, 1);
499
0
}
500
501
void
502
levent_send_now(struct lldpd *cfg)
503
0
{
504
0
  struct lldpd_hardware *hardware;
505
0
  TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
506
0
    if (hardware->h_timer)
507
0
      event_active(hardware->h_timer, EV_TIMEOUT, 1);
508
0
    else
509
0
      log_warnx("event", "BUG: no timer present for interface %s",
510
0
          hardware->h_ifname);
511
0
  }
512
0
}
513
514
static void
515
levent_init(struct lldpd *cfg)
516
0
{
517
  /* Set up libevent */
518
0
  log_debug("event", "initialize libevent");
519
0
  event_set_log_callback(levent_log_cb);
520
0
  if (!(cfg->g_base = event_base_new()))
521
0
    fatalx("event", "unable to create a new libevent base");
522
0
  log_info("event", "libevent %s initialized with %s method", event_get_version(),
523
0
      event_base_get_method(cfg->g_base));
524
525
  /* Set up SNMP */
526
#ifdef USE_SNMP
527
  if (cfg->g_snmp) {
528
    agent_init(cfg, cfg->g_snmp_agentx);
529
    cfg->g_snmp_timeout =
530
        evtimer_new(cfg->g_base, levent_snmp_timeout, cfg);
531
    if (!cfg->g_snmp_timeout)
532
      fatalx("event", "unable to setup timeout function for SNMP");
533
    if ((cfg->g_snmp_fds = malloc(sizeof(struct ev_l))) == NULL)
534
      fatalx("event", "unable to allocate memory for SNMP events");
535
    TAILQ_INIT(levent_snmp_fds(cfg));
536
  }
537
#endif
538
539
  /* Setup loop that will run every X seconds. */
540
0
  log_debug("event", "register loop timer");
541
0
  if (!(cfg->g_main_loop =
542
0
        event_new(cfg->g_base, -1, 0, levent_update_and_send, cfg)))
543
0
    fatalx("event", "unable to setup main timer");
544
0
  event_active(cfg->g_main_loop, EV_TIMEOUT, 1);
545
546
  /* Set up unix socket */
547
0
  struct event *ctl_event;
548
0
  log_debug("event", "register Unix socket");
549
0
  TAILQ_INIT(&lldpd_clients);
550
0
  levent_make_socket_nonblocking(cfg->g_ctl);
551
0
  if ((ctl_event = event_new(cfg->g_base, cfg->g_ctl, EV_READ | EV_PERSIST,
552
0
     levent_ctl_accept, cfg)) == NULL)
553
0
    fatalx("event", "unable to setup control socket event");
554
0
  event_add(ctl_event, NULL);
555
556
  /* Somehow monitor the monitor process */
557
0
  struct event *monitor_event;
558
0
  log_debug("event", "monitor the monitor process");
559
0
  if ((monitor_event = event_new(cfg->g_base, priv_fd(PRIV_UNPRIVILEGED),
560
0
     EV_READ | EV_PERSIST, levent_priv, cfg->g_base)) == NULL)
561
0
    fatalx("event", "unable to monitor monitor process");
562
0
  event_add(monitor_event, NULL);
563
564
  /* Signals */
565
0
  log_debug("event", "register signals");
566
0
  evsignal_add(evsignal_new(cfg->g_base, SIGUSR1, levent_dump, cfg->g_base),
567
0
      NULL);
568
0
  evsignal_add(evsignal_new(cfg->g_base, SIGINT, levent_stop, cfg->g_base), NULL);
569
0
  evsignal_add(evsignal_new(cfg->g_base, SIGTERM, levent_stop, cfg->g_base),
570
0
      NULL);
571
0
}
572
573
/* Initialize libevent and start the event loop */
574
void
575
levent_loop(struct lldpd *cfg)
576
0
{
577
0
  levent_init(cfg);
578
0
  lldpd_loop(cfg);
579
#ifdef USE_SNMP
580
  if (cfg->g_snmp) levent_snmp_update(cfg);
581
#endif
582
583
  /* libevent loop */
584
0
  do {
585
0
    TRACE(LLDPD_EVENT_LOOP());
586
0
    if (event_base_got_break(cfg->g_base) ||
587
0
        event_base_got_exit(cfg->g_base))
588
0
      break;
589
0
  } while (event_base_loop(cfg->g_base, EVLOOP_ONCE) == 0);
590
591
0
  if (cfg->g_iface_timer_event != NULL) event_free(cfg->g_iface_timer_event);
592
593
#ifdef USE_SNMP
594
  if (cfg->g_snmp) agent_shutdown();
595
#endif /* USE_SNMP */
596
597
0
  levent_ctl_close_clients();
598
0
}
599
600
/* Release libevent resources */
601
void
602
levent_shutdown(struct lldpd *cfg)
603
0
{
604
0
  if (cfg->g_iface_event) event_free(cfg->g_iface_event);
605
0
  if (cfg->g_cleanup_timer) event_free(cfg->g_cleanup_timer);
606
0
  event_base_free(cfg->g_base);
607
0
}
608
609
static void
610
levent_hardware_recv(evutil_socket_t fd, short what, void *arg)
611
0
{
612
0
  struct lldpd_hardware *hardware = arg;
613
0
  struct lldpd *cfg = hardware->h_cfg;
614
0
  (void)what;
615
0
  log_debug("event", "received something for %s", hardware->h_ifname);
616
0
  lldpd_recv(cfg, hardware, fd);
617
0
  levent_schedule_cleanup(cfg);
618
0
}
619
620
void
621
levent_hardware_init(struct lldpd_hardware *hardware)
622
0
{
623
0
  log_debug("event", "initialize events for %s", hardware->h_ifname);
624
0
  if ((hardware->h_recv = malloc(sizeof(struct ev_l))) == NULL) {
625
0
    log_warnx("event", "unable to allocate memory for %s",
626
0
        hardware->h_ifname);
627
0
    return;
628
0
  }
629
0
  TAILQ_INIT(levent_hardware_fds(hardware));
630
0
}
631
632
void
633
levent_hardware_add_fd(struct lldpd_hardware *hardware, int fd)
634
0
{
635
0
  struct lldpd_events *hfd = NULL;
636
0
  if (!hardware->h_recv) return;
637
638
0
  hfd = calloc(1, sizeof(struct lldpd_events));
639
0
  if (!hfd) {
640
0
    log_warnx("event", "unable to allocate new event for %s",
641
0
        hardware->h_ifname);
642
0
    return;
643
0
  }
644
0
  levent_make_socket_nonblocking(fd);
645
0
  if ((hfd->ev = event_new(hardware->h_cfg->g_base, fd, EV_READ | EV_PERSIST,
646
0
     levent_hardware_recv, hardware)) == NULL) {
647
0
    log_warnx("event", "unable to allocate a new event for %s",
648
0
        hardware->h_ifname);
649
0
    free(hfd);
650
0
    return;
651
0
  }
652
0
  if (event_add(hfd->ev, NULL) == -1) {
653
0
    log_warnx("event", "unable to schedule new event for %s",
654
0
        hardware->h_ifname);
655
0
    event_free(hfd->ev);
656
0
    free(hfd);
657
0
    return;
658
0
  }
659
0
  TAILQ_INSERT_TAIL(levent_hardware_fds(hardware), hfd, next);
660
0
}
661
662
void
663
levent_hardware_release(struct lldpd_hardware *hardware)
664
0
{
665
0
  struct lldpd_events *ev, *ev_next;
666
0
  if (hardware->h_timer) {
667
0
    event_free(hardware->h_timer);
668
0
    hardware->h_timer = NULL;
669
0
  }
670
0
  if (!hardware->h_recv) return;
671
672
0
  log_debug("event", "release events for %s", hardware->h_ifname);
673
0
  for (ev = TAILQ_FIRST(levent_hardware_fds(hardware)); ev; ev = ev_next) {
674
0
    ev_next = TAILQ_NEXT(ev, next);
675
    /* We may close several time the same FD. This is harmless. */
676
0
    close(event_get_fd(ev->ev));
677
0
    event_free(ev->ev);
678
0
    TAILQ_REMOVE(levent_hardware_fds(hardware), ev, next);
679
0
    free(ev);
680
0
  }
681
0
  free(levent_hardware_fds(hardware));
682
0
}
683
684
static void
685
levent_iface_trigger(evutil_socket_t fd, short what, void *arg)
686
0
{
687
0
  struct lldpd *cfg = arg;
688
0
  log_debug("event", "triggering update of all interfaces");
689
0
  lldpd_update_localports(cfg);
690
0
}
691
692
static void
693
levent_iface_recv(evutil_socket_t fd, short what, void *arg)
694
0
{
695
0
  struct lldpd *cfg = arg;
696
0
  char buffer[EVENT_BUFFER];
697
0
  int n;
698
699
0
  if (cfg->g_iface_cb == NULL) {
700
    /* Discard the message */
701
0
    while (1) {
702
0
      n = read(fd, buffer, sizeof(buffer));
703
0
      if (n == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) break;
704
0
      if (n == -1) {
705
0
        log_warn("event",
706
0
            "unable to receive interface change notification message");
707
0
        return;
708
0
      }
709
0
      if (n == 0) {
710
0
        log_warnx("event",
711
0
            "end of file reached while getting interface change notification message");
712
0
        return;
713
0
      }
714
0
    }
715
0
  } else {
716
0
    cfg->g_iface_cb(cfg);
717
0
  }
718
719
  /* Schedule local port update. We don't run it right away because we may
720
   * receive a batch of events like this. */
721
0
  struct timeval one_sec = { 1, 0 };
722
0
  TRACE(LLDPD_INTERFACES_NOTIFICATION());
723
0
  log_debug("event",
724
0
      "received notification change, schedule an update of all interfaces in one second");
725
0
  if (cfg->g_iface_timer_event == NULL) {
726
0
    if ((cfg->g_iface_timer_event = evtimer_new(cfg->g_base,
727
0
       levent_iface_trigger, cfg)) == NULL) {
728
0
      log_warnx("event",
729
0
          "unable to create a new event to trigger interface update");
730
0
      return;
731
0
    }
732
0
  }
733
0
  if (evtimer_add(cfg->g_iface_timer_event, &one_sec) == -1) {
734
0
    log_warnx("event", "unable to schedule interface updates");
735
0
    return;
736
0
  }
737
0
}
738
739
int
740
levent_iface_subscribe(struct lldpd *cfg, int socket)
741
0
{
742
0
  log_debug("event", "subscribe to interface changes from socket %d", socket);
743
0
  levent_make_socket_nonblocking(socket);
744
0
  cfg->g_iface_event = event_new(cfg->g_base, socket, EV_READ | EV_PERSIST,
745
0
      levent_iface_recv, cfg);
746
0
  if (cfg->g_iface_event == NULL) {
747
0
    log_warnx("event",
748
0
        "unable to allocate a new event for interface changes");
749
0
    return -1;
750
0
  }
751
0
  if (event_add(cfg->g_iface_event, NULL) == -1) {
752
0
    log_warnx("event", "unable to schedule new interface changes event");
753
0
    event_free(cfg->g_iface_event);
754
0
    cfg->g_iface_event = NULL;
755
0
    return -1;
756
0
  }
757
0
  return 0;
758
0
}
759
760
static void
761
levent_trigger_cleanup(evutil_socket_t fd, short what, void *arg)
762
0
{
763
0
  struct lldpd *cfg = arg;
764
0
  lldpd_cleanup(cfg);
765
0
}
766
767
void
768
levent_schedule_cleanup(struct lldpd *cfg)
769
0
{
770
0
  log_debug("event", "schedule next cleanup");
771
0
  if (cfg->g_cleanup_timer != NULL) {
772
0
    event_free(cfg->g_cleanup_timer);
773
0
  }
774
0
  cfg->g_cleanup_timer = evtimer_new(cfg->g_base, levent_trigger_cleanup, cfg);
775
0
  if (cfg->g_cleanup_timer == NULL) {
776
0
    log_warnx("event", "unable to allocate a new event for cleanup tasks");
777
0
    return;
778
0
  }
779
780
  /* Compute the next TTL event */
781
0
  struct timeval tv = { cfg->g_config.c_ttl, 0 };
782
0
  time_t now = time(NULL);
783
0
  time_t next;
784
0
  struct lldpd_hardware *hardware;
785
0
  struct lldpd_port *port;
786
0
  TAILQ_FOREACH (hardware, &cfg->g_hardware, h_entries) {
787
0
    TAILQ_FOREACH (port, &hardware->h_rports, p_entries) {
788
0
      if (now >= port->p_lastupdate + port->p_ttl) {
789
0
        tv.tv_sec = 0;
790
0
        log_debug("event",
791
0
            "immediate cleanup on port %s (%lld, %d, %lld)",
792
0
            hardware->h_ifname, (long long)now, port->p_ttl,
793
0
            (long long)port->p_lastupdate);
794
0
        break;
795
0
      }
796
0
      next = port->p_ttl - (now - port->p_lastupdate);
797
0
      if (next < tv.tv_sec) tv.tv_sec = next;
798
0
    }
799
0
  }
800
801
0
  log_debug("event", "next cleanup in %ld seconds", (long)tv.tv_sec);
802
0
  if (event_add(cfg->g_cleanup_timer, &tv) == -1) {
803
0
    log_warnx("event", "unable to schedule cleanup task");
804
0
    event_free(cfg->g_cleanup_timer);
805
0
    cfg->g_cleanup_timer = NULL;
806
0
    return;
807
0
  }
808
0
}
809
810
static void
811
levent_send_pdu(evutil_socket_t fd, short what, void *arg)
812
0
{
813
0
  struct lldpd_hardware *hardware = arg;
814
0
  int tx_interval = hardware->h_cfg->g_config.c_tx_interval;
815
816
0
  log_debug("event", "trigger sending PDU for port %s", hardware->h_ifname);
817
0
  lldpd_send(hardware);
818
819
0
  if (hardware->h_tx_fast > 0) hardware->h_tx_fast--;
820
821
0
  if (hardware->h_tx_fast > 0)
822
0
    tx_interval = hardware->h_cfg->g_config.c_tx_fast_interval * 1000;
823
824
0
  struct timeval tv;
825
0
  tv.tv_sec = tx_interval / 1000;
826
0
  tv.tv_usec = (tx_interval % 1000) * 1000;
827
0
  if (event_add(hardware->h_timer, &tv) == -1) {
828
0
    log_warnx("event", "unable to re-register timer event for port %s",
829
0
        hardware->h_ifname);
830
0
    event_free(hardware->h_timer);
831
0
    hardware->h_timer = NULL;
832
0
    return;
833
0
  }
834
0
}
835
836
void
837
levent_schedule_pdu(struct lldpd_hardware *hardware)
838
0
{
839
0
  log_debug("event", "schedule sending PDU on %s", hardware->h_ifname);
840
0
  if (hardware->h_timer == NULL) {
841
0
    hardware->h_timer =
842
0
        evtimer_new(hardware->h_cfg->g_base, levent_send_pdu, hardware);
843
0
    if (hardware->h_timer == NULL) {
844
0
      log_warnx("event", "unable to schedule PDU sending for port %s",
845
0
          hardware->h_ifname);
846
0
      return;
847
0
    }
848
0
  }
849
850
0
  struct timeval tv = { 0, 0 };
851
0
  if (event_add(hardware->h_timer, &tv) == -1) {
852
0
    log_warnx("event", "unable to register timer event for port %s",
853
0
        hardware->h_ifname);
854
0
    event_free(hardware->h_timer);
855
0
    hardware->h_timer = NULL;
856
0
    return;
857
0
  }
858
0
}
859
860
int
861
levent_make_socket_nonblocking(int fd)
862
0
{
863
0
  int flags;
864
0
  if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
865
0
    log_warn("event", "fcntl(%d, F_GETFL)", fd);
866
0
    return -1;
867
0
  }
868
0
  if (flags & O_NONBLOCK) return 0;
869
0
  if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
870
0
    log_warn("event", "fcntl(%d, F_SETFL)", fd);
871
0
    return -1;
872
0
  }
873
0
  return 0;
874
0
}
875
876
int
877
levent_make_socket_blocking(int fd)
878
0
{
879
0
  int flags;
880
0
  if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
881
0
    log_warn("event", "fcntl(%d, F_GETFL)", fd);
882
0
    return -1;
883
0
  }
884
0
  if (!(flags & O_NONBLOCK)) return 0;
885
0
  if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) == -1) {
886
0
    log_warn("event", "fcntl(%d, F_SETFL)", fd);
887
0
    return -1;
888
0
  }
889
0
  return 0;
890
0
}
891
892
#ifdef HOST_OS_LINUX
893
/* Receive and log error from a socket when there is suspicion of an error. */
894
void
895
levent_recv_error(int fd, const char *source)
896
0
{
897
0
  do {
898
0
    ssize_t n;
899
0
    char buf[1024] = {};
900
0
    struct msghdr msg = { .msg_control = buf,
901
0
      .msg_controllen = sizeof(buf) };
902
0
    if ((n = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT)) <= 0) {
903
0
      return;
904
0
    }
905
0
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
906
0
    if (cmsg == NULL)
907
0
      log_warnx("event", "received unknown error on %s", source);
908
0
    else
909
0
      log_warnx("event", "received error (level=%d/type=%d) on %s",
910
0
          cmsg->cmsg_level, cmsg->cmsg_type, source);
911
0
  } while (1);
912
0
}
913
#endif