]> git.ipfire.org Git - thirdparty/dhcpcd.git/blame - eloop.c
Release dhcpcd-5.0.5
[thirdparty/dhcpcd.git] / eloop.c
CommitLineData
fd05b7dc
RM
1/*
2 * dhcpcd - DHCP client daemon
601fb3d5 3 * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
fd05b7dc
RM
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/time.h>
765fbf7d 29
fd05b7dc
RM
30#include <errno.h>
31#include <limits.h>
32#include <poll.h>
33#include <stdarg.h>
34#include <stdlib.h>
765fbf7d 35#include <syslog.h>
fd05b7dc
RM
36
37#include "common.h"
38#include "eloop.h"
fd05b7dc 39
35a8183f 40static struct timeval now;
fd05b7dc
RM
41
42static struct event {
43 int fd;
f43e5853
RM
44 void (*callback)(void *);
45 void *arg;
fd05b7dc 46 struct event *next;
35a8183f
RM
47} *events;
48static struct event *free_events;
fd05b7dc
RM
49
50static struct timeout {
51 struct timeval when;
f43e5853
RM
52 void (*callback)(void *);
53 void *arg;
fd05b7dc 54 struct timeout *next;
35a8183f
RM
55} *timeouts;
56static struct timeout *free_timeouts;
fd05b7dc 57
35a8183f
RM
58static struct pollfd *fds;
59static size_t fds_len;
fd05b7dc 60
fd05b7dc 61void
f43e5853 62add_event(int fd, void (*callback)(void *), void *arg)
fd05b7dc
RM
63{
64 struct event *e, *last = NULL;
65
66 /* We should only have one callback monitoring the fd */
67 for (e = events; e; e = e->next) {
68 if (e->fd == fd) {
69 e->callback = callback;
f43e5853 70 e->arg = arg;
fd05b7dc
RM
71 return;
72 }
73 last = e;
74 }
75
76 /* Allocate a new event if no free ones already allocated */
77 if (free_events) {
78 e = free_events;
79 free_events = e->next;
80 } else
81 e = xmalloc(sizeof(*e));
82 e->fd = fd;
83 e->callback = callback;
f43e5853 84 e->arg = arg;
fd05b7dc
RM
85 e->next = NULL;
86 if (last)
87 last->next = e;
88 else
89 events = e;
90}
91
92void
93delete_event(int fd)
94{
95 struct event *e, *last = NULL;
96
97 for (e = events; e; e = e->next) {
98 if (e->fd == fd) {
99 if (last)
100 last->next = e->next;
101 else
102 events = e->next;
103 e->next = free_events;
104 free_events = e;
105 break;
106 }
107 last = e;
108 }
109}
110
111void
eab2229c 112add_timeout_tv(const struct timeval *when, void (*callback)(void *), void *arg)
fd05b7dc
RM
113{
114 struct timeval w;
115 struct timeout *t, *tt = NULL;
116
117 get_monotonic(&now);
118 timeradd(&now, when, &w);
f7038ab8
RM
119 /* Check for time_t overflow. */
120 if (timercmp(&w, &now, <)) {
121 errno = ERANGE;
122 return;
123 }
fd05b7dc
RM
124
125 /* Remove existing timeout if present */
126 for (t = timeouts; t; t = t->next) {
f43e5853 127 if (t->callback == callback && t->arg == arg) {
fd05b7dc
RM
128 if (tt)
129 tt->next = t->next;
130 else
131 timeouts = t->next;
132 break;
133 }
134 tt = t;
135 }
136
137 if (!t) {
138 /* No existing, so allocate or grab one from the free pool */
139 if (free_timeouts) {
140 t = free_timeouts;
141 free_timeouts = t->next;
142 } else
143 t = xmalloc(sizeof(*t));
144 }
145
146 t->when.tv_sec = w.tv_sec;
147 t->when.tv_usec = w.tv_usec;
148 t->callback = callback;
f43e5853 149 t->arg = arg;
fd05b7dc
RM
150
151 /* The timeout list should be in chronological order,
152 * soonest first.
153 * This is the easiest algorithm - check the head, then middle
154 * and finally the end. */
155 if (!timeouts || timercmp(&t->when, &timeouts->when, <)) {
156 t->next = timeouts;
157 timeouts = t;
158 return;
159 }
160 for (tt = timeouts; tt->next; tt = tt->next)
161 if (timercmp(&t->when, &tt->next->when, <)) {
162 t->next = tt->next;
163 tt->next = t;
164 return;
165 }
166 tt->next = t;
167 t->next = NULL;
168}
169
170void
eab2229c 171add_timeout_sec(time_t when, void (*callback)(void *), void *arg)
fd05b7dc
RM
172{
173 struct timeval tv;
174
175 tv.tv_sec = when;
176 tv.tv_usec = 0;
f43e5853 177 add_timeout_tv(&tv, callback, arg);
fd05b7dc
RM
178}
179
180/* This deletes all timeouts for the interface EXCEPT for ones with the
181 * callbacks given. Handy for deleting everything apart from the expire
182 * timeout. */
183void
eab2229c 184delete_timeouts(void *arg, void (*callback)(void *), ...)
fd05b7dc
RM
185{
186 struct timeout *t, *tt, *last = NULL;
187 va_list va;
f43e5853 188 void (*f)(void *);
fd05b7dc
RM
189
190 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
f43e5853 191 if (t->arg == arg && t->callback != callback) {
fd05b7dc 192 va_start(va, callback);
f43e5853 193 while ((f = va_arg(va, void (*)(void *))))
fd05b7dc
RM
194 if (f == t->callback)
195 break;
196 va_end(va);
197 if (!f) {
198 if (last)
199 last->next = t->next;
200 else
201 timeouts = t->next;
202 t->next = free_timeouts;
203 free_timeouts = t;
204 continue;
205 }
206 }
b6b24881 207 last = t;
fd05b7dc
RM
208 }
209}
210
211void
f43e5853 212delete_timeout(void (*callback)(void *), void *arg)
fd05b7dc
RM
213{
214 struct timeout *t, *tt, *last = NULL;
215
216 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
f43e5853 217 if (t->arg == arg &&
fd05b7dc
RM
218 (!callback || t->callback == callback))
219 {
220 if (last)
221 last->next = t->next;
222 else
223 timeouts = t->next;
224 t->next = free_timeouts;
225 free_timeouts = t;
226 continue;
227 }
228 last = t;
229 }
230}
231
9d94cb78
RM
232#ifdef DEBUG_MEMORY
233/* Define this to free all malloced memory.
234 * Normally we don't do this as the OS will do it for us at exit,
235 * but it's handy for debugging other leaks in valgrind. */
236static void
237cleanup(void)
238{
239 struct event *e;
240 struct timeout *t;
241
242 while (events) {
243 e = events->next;
244 free(events);
245 events = e;
246 }
247 while (free_events) {
248 e = free_events->next;
249 free(free_events);
250 free_events = e;
251 }
252 while (timeouts) {
253 t = timeouts->next;
254 free(timeouts);
255 timeouts = t;
256 }
257 while (free_timeouts) {
258 t = free_timeouts->next;
259 free(free_timeouts);
260 free_timeouts = t;
261 }
262 free(fds);
263}
264#endif
265
0ce8caf2 266_noreturn void
fd05b7dc
RM
267start_eloop(void)
268{
269 int msecs, n;
270 nfds_t nfds, i;
271 struct event *e;
272 struct timeout *t;
273 struct timeval tv;
274
275#ifdef DEBUG_MEMORY
276 atexit(cleanup);
277#endif
278
279 for (;;) {
280 /* Run all timeouts first.
281 * When we have one that has not yet occured,
282 * calculate milliseconds until it does for use in poll. */
283 if (timeouts) {
284 if (timercmp(&now, &timeouts->when, >)) {
285 t = timeouts;
286 timeouts = timeouts->next;
f43e5853 287 t->callback(t->arg);
fd05b7dc
RM
288 t->next = free_timeouts;
289 free_timeouts = t;
290 continue;
291 }
292 timersub(&timeouts->when, &now, &tv);
293 if (tv.tv_sec > INT_MAX / 1000 ||
294 (tv.tv_sec == INT_MAX / 1000 &&
eab2229c 295 (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
fd05b7dc
RM
296 msecs = INT_MAX;
297 else
298 msecs = tv.tv_sec * 1000 +
eab2229c 299 (tv.tv_usec + 999) / 1000;
fd05b7dc
RM
300 } else
301 /* No timeouts, so wait forever. */
302 msecs = -1;
303
304 /* Allocate memory for our pollfds as and when needed.
305 * We don't bother shrinking it. */
306 nfds = 0;
307 for (e = events; e; e = e->next)
308 nfds++;
309 if (msecs == -1 && nfds == 0) {
765fbf7d 310 syslog(LOG_ERR, "nothing to do");
fd05b7dc
RM
311 exit(EXIT_FAILURE);
312 }
313 if (nfds > fds_len) {
314 free(fds);
cd93080d
RM
315 /* Allocate 5 more than we need for future use */
316 fds_len = nfds + 5;
317 fds = xmalloc(sizeof(*fds) * fds_len);
fd05b7dc
RM
318 }
319 nfds = 0;
320 for (e = events; e; e = e->next) {
321 fds[nfds].fd = e->fd;
322 fds[nfds].events = POLLIN;
323 fds[nfds].revents = 0;
324 nfds++;
325 }
326 n = poll(fds, nfds, msecs);
327 if (n == -1) {
328 if (errno == EAGAIN || errno == EINTR) {
329 get_monotonic(&now);
330 continue;
331 }
765fbf7d 332 syslog(LOG_ERR, "poll: %m");
fd05b7dc
RM
333 exit(EXIT_FAILURE);
334 }
335
336 /* Get the now time and process any triggered events. */
337 get_monotonic(&now);
338 if (n == 0)
339 continue;
340 for (i = 0; i < nfds; i++) {
341 if (!(fds[i].revents & (POLLIN | POLLHUP)))
342 continue;
343 for (e = events; e; e = e->next) {
344 if (e->fd == fds[i].fd) {
f43e5853 345 e->callback(e->arg);
fd05b7dc
RM
346 break;
347 }
348 }
349 }
350 }
351}