Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / dhcpcd / dist / eloop.c
blob5c61445ee3da8e7e5a31e534e0578b45622ae81f
1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
4 * All rights reserved
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.
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.
28 #include <sys/time.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <poll.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <syslog.h>
37 #include "common.h"
38 #include "eloop.h"
40 static struct timeval now;
42 static struct event {
43 int fd;
44 void (*callback)(void *);
45 void *arg;
46 struct event *next;
47 } *events;
48 static struct event *free_events;
50 static struct timeout {
51 struct timeval when;
52 void (*callback)(void *);
53 void *arg;
54 struct timeout *next;
55 } *timeouts;
56 static struct timeout *free_timeouts;
58 static struct pollfd *fds;
59 static size_t fds_len;
61 void
62 add_event(int fd, void (*callback)(void *), void *arg)
64 struct event *e, *last = NULL;
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;
70 e->arg = arg;
71 return;
73 last = e;
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;
84 e->arg = arg;
85 e->next = NULL;
86 if (last)
87 last->next = e;
88 else
89 events = e;
92 void
93 delete_event(int fd)
95 struct event *e, *last = NULL;
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;
107 last = e;
111 void
112 add_timeout_tv(const struct timeval *when, void (*callback)(void *), void *arg)
114 struct timeval w;
115 struct timeout *t, *tt = NULL;
117 get_monotonic(&now);
118 timeradd(&now, when, &w);
119 /* Check for time_t overflow. */
120 if (timercmp(&w, &now, <)) {
121 errno = ERANGE;
122 return;
125 /* Remove existing timeout if present */
126 for (t = timeouts; t; t = t->next) {
127 if (t->callback == callback && t->arg == arg) {
128 if (tt)
129 tt->next = t->next;
130 else
131 timeouts = t->next;
132 break;
134 tt = t;
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));
146 t->when.tv_sec = w.tv_sec;
147 t->when.tv_usec = w.tv_usec;
148 t->callback = callback;
149 t->arg = arg;
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;
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;
166 tt->next = t;
167 t->next = NULL;
170 void
171 add_timeout_sec(time_t when, void (*callback)(void *), void *arg)
173 struct timeval tv;
175 tv.tv_sec = when;
176 tv.tv_usec = 0;
177 add_timeout_tv(&tv, callback, arg);
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. */
183 void
184 delete_timeouts(void *arg, void (*callback)(void *), ...)
186 struct timeout *t, *tt, *last = NULL;
187 va_list va;
188 void (*f)(void *);
190 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
191 if (t->arg == arg && t->callback != callback) {
192 va_start(va, callback);
193 while ((f = va_arg(va, void (*)(void *))))
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;
207 last = t;
211 void
212 delete_timeout(void (*callback)(void *), void *arg)
214 struct timeout *t, *tt, *last = NULL;
216 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
217 if (t->arg == arg &&
218 (!callback || t->callback == callback))
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;
228 last = t;
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. */
236 static void
237 cleanup(void)
239 struct event *e;
240 struct timeout *t;
242 while (events) {
243 e = events->next;
244 free(events);
245 events = e;
247 while (free_events) {
248 e = free_events->next;
249 free(free_events);
250 free_events = e;
252 while (timeouts) {
253 t = timeouts->next;
254 free(timeouts);
255 timeouts = t;
257 while (free_timeouts) {
258 t = free_timeouts->next;
259 free(free_timeouts);
260 free_timeouts = t;
262 free(fds);
264 #endif
266 _noreturn void
267 start_eloop(void)
269 int msecs, n;
270 nfds_t nfds, i;
271 struct event *e;
272 struct timeout *t;
273 struct timeval tv;
275 #ifdef DEBUG_MEMORY
276 atexit(cleanup);
277 #endif
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;
287 t->callback(t->arg);
288 t->next = free_timeouts;
289 free_timeouts = t;
290 continue;
292 timersub(&timeouts->when, &now, &tv);
293 if (tv.tv_sec > INT_MAX / 1000 ||
294 (tv.tv_sec == INT_MAX / 1000 &&
295 (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
296 msecs = INT_MAX;
297 else
298 msecs = tv.tv_sec * 1000 +
299 (tv.tv_usec + 999) / 1000;
300 } else
301 /* No timeouts, so wait forever. */
302 msecs = -1;
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) {
310 syslog(LOG_ERR, "nothing to do");
311 exit(EXIT_FAILURE);
313 if (nfds > fds_len) {
314 free(fds);
315 /* Allocate 5 more than we need for future use */
316 fds_len = nfds + 5;
317 fds = xmalloc(sizeof(*fds) * fds_len);
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++;
326 n = poll(fds, nfds, msecs);
327 if (n == -1) {
328 if (errno == EAGAIN || errno == EINTR) {
329 get_monotonic(&now);
330 continue;
332 syslog(LOG_ERR, "poll: %m");
333 exit(EXIT_FAILURE);
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) {
345 e->callback(e->arg);
346 break;