Coverage Report

Created: 2025-08-28 06:14

/src/unbound/util/mini_event.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * mini_event.c - implementation of part of libevent api, portably.
3
 *
4
 * Copyright (c) 2007, NLnet Labs. All rights reserved.
5
 * 
6
 * This software is open source.
7
 * 
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 
12
 * Redistributions of source code must retain the above copyright notice,
13
 * this list of conditions and the following disclaimer.
14
 * 
15
 * Redistributions in binary form must reproduce the above copyright notice,
16
 * this list of conditions and the following disclaimer in the documentation
17
 * and/or other materials provided with the distribution.
18
 * 
19
 * Neither the name of the NLNET LABS nor the names of its contributors may
20
 * be used to endorse or promote products derived from this software without
21
 * specific prior written permission.
22
 * 
23
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
 * 
35
 */
36
37
/**
38
 * \file
39
 * fake libevent implementation. Less broad in functionality, and only
40
 * supports select(2).
41
 */
42
43
#include "config.h"
44
#include "util/mini_event.h"
45
#ifdef HAVE_TIME_H
46
#include <time.h>
47
#endif
48
#include <sys/time.h>
49
50
#if defined(USE_MINI_EVENT) && !defined(USE_WINSOCK)
51
#include <signal.h>
52
#include "util/fptr_wlist.h"
53
54
/** compare events in tree, based on timevalue, ptr for uniqueness */
55
int mini_ev_cmp(const void* a, const void* b)
56
0
{
57
0
  const struct event *e = (const struct event*)a;
58
0
  const struct event *f = (const struct event*)b;
59
0
  if(e->ev_timeout.tv_sec < f->ev_timeout.tv_sec)
60
0
    return -1;
61
0
  if(e->ev_timeout.tv_sec > f->ev_timeout.tv_sec)
62
0
    return 1;
63
0
  if(e->ev_timeout.tv_usec < f->ev_timeout.tv_usec)
64
0
    return -1;
65
0
  if(e->ev_timeout.tv_usec > f->ev_timeout.tv_usec)
66
0
    return 1;
67
0
  if(e < f)
68
0
    return -1;
69
0
  if(e > f)
70
0
    return 1;
71
0
  return 0;
72
0
}
73
74
/** set time */
75
static int
76
settime(struct event_base* base)
77
4.40k
{
78
4.40k
  if(gettimeofday(base->time_tv, NULL) < 0) {
79
0
    return -1;
80
0
  }
81
4.40k
#ifndef S_SPLINT_S
82
4.40k
  *base->time_secs = (time_t)base->time_tv->tv_sec;
83
4.40k
#endif
84
4.40k
  return 0;
85
4.40k
}
86
87
/** create event base */
88
void *event_init(time_t* time_secs, struct timeval* time_tv)
89
4.40k
{
90
4.40k
  struct event_base* base = (struct event_base*)malloc(
91
4.40k
    sizeof(struct event_base));
92
4.40k
  if(!base)
93
0
    return NULL;
94
4.40k
  memset(base, 0, sizeof(*base));
95
4.40k
  base->time_secs = time_secs;
96
4.40k
  base->time_tv = time_tv;
97
4.40k
  if(settime(base) < 0) {
98
0
    event_base_free(base);
99
0
    return NULL;
100
0
  }
101
4.40k
  base->times = rbtree_create(mini_ev_cmp);
102
4.40k
  if(!base->times) {
103
0
    event_base_free(base);
104
0
    return NULL;
105
0
  }
106
4.40k
  base->capfd = MAX_FDS;
107
4.40k
#ifdef FD_SETSIZE
108
4.40k
  if((int)FD_SETSIZE < base->capfd)
109
0
    base->capfd = (int)FD_SETSIZE;
110
4.40k
#endif
111
4.40k
  base->fds = (struct event**)calloc((size_t)base->capfd, 
112
4.40k
    sizeof(struct event*));
113
4.40k
  if(!base->fds) {
114
0
    event_base_free(base);
115
0
    return NULL;
116
0
  }
117
4.40k
  base->signals = (struct event**)calloc(MAX_SIG, sizeof(struct event*));
118
4.40k
  if(!base->signals) {
119
0
    event_base_free(base);
120
0
    return NULL;
121
0
  }
122
4.40k
#ifndef S_SPLINT_S
123
4.40k
  FD_ZERO(&base->reads);
124
4.40k
  FD_ZERO(&base->writes);
125
4.40k
#endif
126
4.40k
  return base;
127
4.40k
}
128
129
/** get version */
130
const char *event_get_version(void)
131
0
{
132
0
  return "mini-event-"PACKAGE_VERSION;
133
0
}
134
135
/** get polling method, select */
136
const char *event_get_method(void)
137
0
{
138
0
  return "select";
139
0
}
140
141
/** call timeouts handlers, and return how long to wait for next one or -1 */
142
static void handle_timeouts(struct event_base* base, struct timeval* now, 
143
  struct timeval* wait)
144
0
{
145
0
  struct event* p;
146
0
#ifndef S_SPLINT_S
147
0
  wait->tv_sec = (time_t)-1;
148
0
#endif
149
150
0
  while((rbnode_type*)(p = (struct event*)rbtree_first(base->times))
151
0
    !=RBTREE_NULL) {
152
0
#ifndef S_SPLINT_S
153
0
    if(p->ev_timeout.tv_sec > now->tv_sec ||
154
0
      (p->ev_timeout.tv_sec==now->tv_sec && 
155
0
      p->ev_timeout.tv_usec > now->tv_usec)) {
156
      /* there is a next larger timeout. wait for it */
157
0
      wait->tv_sec = p->ev_timeout.tv_sec - now->tv_sec;
158
0
      if(now->tv_usec > p->ev_timeout.tv_usec) {
159
0
        wait->tv_sec--;
160
0
        wait->tv_usec = 1000000 - (now->tv_usec -
161
0
          p->ev_timeout.tv_usec);
162
0
      } else {
163
0
        wait->tv_usec = p->ev_timeout.tv_usec 
164
0
          - now->tv_usec;
165
0
      }
166
0
      return;
167
0
    }
168
0
#endif
169
    /* event times out, remove it */
170
0
    (void)rbtree_delete(base->times, p);
171
0
    p->ev_events &= ~EV_TIMEOUT;
172
0
    fptr_ok(fptr_whitelist_event(p->ev_callback));
173
0
    (*p->ev_callback)(p->ev_fd, EV_TIMEOUT, p->ev_arg);
174
0
  }
175
0
}
176
177
/** call select and callbacks for that */
178
static int handle_select(struct event_base* base, struct timeval* wait)
179
0
{
180
0
  fd_set r, w;
181
0
  int ret, i;
182
183
0
#ifndef S_SPLINT_S
184
0
  if(wait->tv_sec==(time_t)-1)
185
0
    wait = NULL;
186
0
#endif
187
0
  memmove(&r, &base->reads, sizeof(fd_set));
188
0
  memmove(&w, &base->writes, sizeof(fd_set));
189
0
  memmove(&base->ready, &base->content, sizeof(fd_set));
190
191
0
  if((ret = select(base->maxfd+1, &r, &w, NULL, wait)) == -1) {
192
0
    ret = errno;
193
0
    if(settime(base) < 0)
194
0
      return -1;
195
0
    errno = ret;
196
0
    if(ret == EAGAIN || ret == EINTR)
197
0
      return 0;
198
0
    return -1;
199
0
  }
200
0
  if(settime(base) < 0)
201
0
    return -1;
202
  
203
0
  for(i=0; i<base->maxfd+1; i++) {
204
0
    short bits = 0;
205
0
    if(!base->fds[i] || !(FD_ISSET(i, &base->ready))) {
206
0
      continue;
207
0
    }
208
0
    if(FD_ISSET(i, &r)) {
209
0
      bits |= EV_READ;
210
0
      ret--;
211
0
    }
212
0
    if(FD_ISSET(i, &w)) {
213
0
      bits |= EV_WRITE;
214
0
      ret--;
215
0
    }
216
0
    bits &= base->fds[i]->ev_events;
217
0
    if(bits) {
218
0
      fptr_ok(fptr_whitelist_event(
219
0
        base->fds[i]->ev_callback));
220
0
      (*base->fds[i]->ev_callback)(base->fds[i]->ev_fd, 
221
0
        bits, base->fds[i]->ev_arg);
222
0
      if(ret==0)
223
0
        break;
224
0
    }
225
0
  }
226
0
  return 0;
227
0
}
228
229
/** run select in a loop */
230
int event_base_dispatch(struct event_base* base)
231
0
{
232
0
  struct timeval wait;
233
0
  if(settime(base) < 0)
234
0
    return -1;
235
0
  while(!base->need_to_exit)
236
0
  {
237
    /* see if timeouts need handling */
238
0
    handle_timeouts(base, base->time_tv, &wait);
239
0
    if(base->need_to_exit)
240
0
      return 0;
241
    /* do select */
242
0
    if(handle_select(base, &wait) < 0) {
243
0
      if(base->need_to_exit)
244
0
        return 0;
245
0
      return -1;
246
0
    }
247
0
  }
248
0
  return 0;
249
0
}
250
251
/** exit that loop */
252
int event_base_loopexit(struct event_base* base, 
253
  struct timeval* ATTR_UNUSED(tv))
254
0
{
255
0
  base->need_to_exit = 1;
256
0
  return 0;
257
0
}
258
259
/* free event base, free events yourself */
260
void event_base_free(struct event_base* base)
261
4.40k
{
262
4.40k
  if(!base)
263
0
    return;
264
4.40k
  free(base->times);
265
4.40k
  free(base->fds);
266
4.40k
  free(base->signals);
267
4.40k
  free(base);
268
4.40k
}
269
270
/** set content of event */
271
void event_set(struct event* ev, int fd, short bits, 
272
  void (*cb)(int, short, void *), void* arg)
273
0
{
274
0
  ev->node.key = ev;
275
0
  ev->ev_fd = fd;
276
0
  ev->ev_events = bits;
277
0
  ev->ev_callback = cb;
278
0
  fptr_ok(fptr_whitelist_event(ev->ev_callback));
279
0
  ev->ev_arg = arg;
280
0
  ev->added = 0;
281
0
}
282
283
/* add event to a base */
284
int event_base_set(struct event_base* base, struct event* ev)
285
0
{
286
0
  ev->ev_base = base;
287
0
  ev->added = 0;
288
0
  return 0;
289
0
}
290
291
/* add event to make it active, you may not change it with event_set anymore */
292
int event_add(struct event* ev, struct timeval* tv)
293
0
{
294
0
  if(ev->added)
295
0
    event_del(ev);
296
0
  if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
297
0
    return -1;
298
0
  if( (ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
299
0
    ev->ev_base->fds[ev->ev_fd] = ev;
300
0
    if((ev->ev_events&EV_READ)) {
301
0
      FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
302
0
    }
303
0
    if((ev->ev_events&EV_WRITE)) {
304
0
      FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
305
0
    }
306
0
    FD_SET(FD_SET_T ev->ev_fd, &ev->ev_base->content);
307
0
    FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
308
0
    if(ev->ev_fd > ev->ev_base->maxfd)
309
0
      ev->ev_base->maxfd = ev->ev_fd;
310
0
  }
311
0
  if(tv && (ev->ev_events&EV_TIMEOUT)) {
312
0
#ifndef S_SPLINT_S
313
0
    struct timeval *now = ev->ev_base->time_tv;
314
0
    ev->ev_timeout.tv_sec = tv->tv_sec + now->tv_sec;
315
0
    ev->ev_timeout.tv_usec = tv->tv_usec + now->tv_usec;
316
0
    while(ev->ev_timeout.tv_usec >= 1000000) {
317
0
      ev->ev_timeout.tv_usec -= 1000000;
318
0
      ev->ev_timeout.tv_sec++;
319
0
    }
320
0
#endif
321
0
    (void)rbtree_insert(ev->ev_base->times, &ev->node);
322
0
  }
323
0
  ev->added = 1;
324
0
  return 0;
325
0
}
326
327
/* remove event, you may change it again */
328
int event_del(struct event* ev)
329
0
{
330
0
  if(ev->ev_fd != -1 && ev->ev_fd >= ev->ev_base->capfd)
331
0
    return -1;
332
0
  if((ev->ev_events&EV_TIMEOUT))
333
0
    (void)rbtree_delete(ev->ev_base->times, &ev->node);
334
0
  if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
335
0
    ev->ev_base->fds[ev->ev_fd] = NULL;
336
0
    FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->reads);
337
0
    FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->writes);
338
0
    FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->ready);
339
0
    FD_CLR(FD_SET_T ev->ev_fd, &ev->ev_base->content);
340
0
    if(ev->ev_fd == ev->ev_base->maxfd) {
341
0
                        int i = ev->ev_base->maxfd - 1;
342
0
                        for (; i > 3; i--) {
343
0
                                if (NULL != ev->ev_base->fds[i]) {
344
0
                                        break;
345
0
                                }
346
0
                        }
347
0
                        ev->ev_base->maxfd = i;
348
0
                }
349
0
  }
350
0
  ev->added = 0;
351
0
  return 0;
352
0
}
353
354
/** which base gets to handle signals */
355
static struct event_base* signal_base = NULL;
356
/** signal handler */
357
static RETSIGTYPE sigh(int sig)
358
0
{
359
0
  struct event* ev;
360
0
  if(!signal_base || sig < 0 || sig >= MAX_SIG)
361
0
    return;
362
0
  ev = signal_base->signals[sig];
363
0
  if(!ev)
364
0
    return;
365
0
  fptr_ok(fptr_whitelist_event(ev->ev_callback));
366
0
  (*ev->ev_callback)(sig, EV_SIGNAL, ev->ev_arg);
367
0
}
368
369
/** install signal handler */
370
int signal_add(struct event* ev, struct timeval* ATTR_UNUSED(tv))
371
0
{
372
0
  if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
373
0
    return -1;
374
0
  signal_base = ev->ev_base;
375
0
  ev->ev_base->signals[ev->ev_fd] = ev;
376
0
  ev->added = 1;
377
0
  if(signal(ev->ev_fd, sigh) == SIG_ERR) {
378
0
    return -1;
379
0
  }
380
0
  return 0;
381
0
}
382
383
/** remove signal handler */
384
int signal_del(struct event* ev)
385
0
{
386
0
  if(ev->ev_fd == -1 || ev->ev_fd >= MAX_SIG)
387
0
    return -1;
388
0
  ev->ev_base->signals[ev->ev_fd] = NULL;
389
0
  ev->added = 0;
390
0
  return 0;
391
0
}
392
393
#else /* USE_MINI_EVENT */
394
#ifndef USE_WINSOCK
395
int mini_ev_cmp(const void* ATTR_UNUSED(a), const void* ATTR_UNUSED(b))
396
{
397
  return 0;
398
}
399
#endif /* not USE_WINSOCK */
400
#endif /* USE_MINI_EVENT */