1 /*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2008 Roy Marples <roy@marples.name>
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>
29
30 #include <errno.h>
31 #include <limits.h>
32 #include <poll.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <syslog.h>
36
37 #include "common.h"
38 #include "eloop.h"
39
40 static struct timeval now;
41
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;
49
50 static struct timeout {
51 struct timeval when;
52 void (*callback)(void *);
53 void *arg;
54 int queue;
55 struct timeout *next;
56 } *timeouts;
57 static struct timeout *free_timeouts;
58
59 static struct pollfd *fds;
60 static size_t fds_len;
61
62 void
add_event(int fd,void (* callback)(void *),void * arg)63 add_event(int fd, void (*callback)(void *), void *arg)
64 {
65 struct event *e, *last = NULL;
66
67 /* We should only have one callback monitoring the fd */
68 for (e = events; e; e = e->next) {
69 if (e->fd == fd) {
70 e->callback = callback;
71 e->arg = arg;
72 return;
73 }
74 last = e;
75 }
76
77 /* Allocate a new event if no free ones already allocated */
78 if (free_events) {
79 e = free_events;
80 free_events = e->next;
81 } else
82 e = xmalloc(sizeof(*e));
83 e->fd = fd;
84 e->callback = callback;
85 e->arg = arg;
86 e->next = NULL;
87 if (last)
88 last->next = e;
89 else
90 events = e;
91 }
92
93 void
delete_event(int fd)94 delete_event(int fd)
95 {
96 struct event *e, *last = NULL;
97
98 for (e = events; e; e = e->next) {
99 if (e->fd == fd) {
100 if (last)
101 last->next = e->next;
102 else
103 events = e->next;
104 e->next = free_events;
105 free_events = e;
106 break;
107 }
108 last = e;
109 }
110 }
111
112 void
add_q_timeout_tv(int queue,const struct timeval * when,void (* callback)(void *),void * arg)113 add_q_timeout_tv(int queue,
114 const struct timeval *when, void (*callback)(void *), void *arg)
115 {
116 struct timeval w;
117 struct timeout *t, *tt = NULL;
118
119 get_monotonic(&now);
120 timeradd(&now, when, &w);
121 /* Check for time_t overflow. */
122 if (timercmp(&w, &now, <)) {
123 errno = ERANGE;
124 return;
125 }
126
127 /* Remove existing timeout if present */
128 for (t = timeouts; t; t = t->next) {
129 if (t->callback == callback && t->arg == arg) {
130 if (tt)
131 tt->next = t->next;
132 else
133 timeouts = t->next;
134 break;
135 }
136 tt = t;
137 }
138
139 if (!t) {
140 /* No existing, so allocate or grab one from the free pool */
141 if (free_timeouts) {
142 t = free_timeouts;
143 free_timeouts = t->next;
144 } else
145 t = xmalloc(sizeof(*t));
146 }
147
148 t->when.tv_sec = w.tv_sec;
149 t->when.tv_usec = w.tv_usec;
150 t->callback = callback;
151 t->arg = arg;
152 t->queue = queue;
153
154 /* The timeout list should be in chronological order,
155 * soonest first.
156 * This is the easiest algorithm - check the head, then middle
157 * and finally the end. */
158 if (!timeouts || timercmp(&t->when, &timeouts->when, <)) {
159 t->next = timeouts;
160 timeouts = t;
161 return;
162 }
163 for (tt = timeouts; tt->next; tt = tt->next)
164 if (timercmp(&t->when, &tt->next->when, <)) {
165 t->next = tt->next;
166 tt->next = t;
167 return;
168 }
169 tt->next = t;
170 t->next = NULL;
171 }
172
173 void
add_q_timeout_sec(int queue,time_t when,void (* callback)(void *),void * arg)174 add_q_timeout_sec(int queue, time_t when, void (*callback)(void *), void *arg)
175 {
176 struct timeval tv;
177
178 tv.tv_sec = when;
179 tv.tv_usec = 0;
180 add_q_timeout_tv(queue, &tv, callback, arg);
181 }
182
183 /* This deletes all timeouts for the interface EXCEPT for ones with the
184 * callbacks given. Handy for deleting everything apart from the expire
185 * timeout. */
186 static void
v_delete_q_timeouts(int queue,void * arg,void (* callback)(void *),va_list v)187 v_delete_q_timeouts(int queue, void *arg, void (*callback)(void *), va_list v)
188 {
189 struct timeout *t, *tt, *last = NULL;
190 va_list va;
191 void (*f)(void *);
192
193 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
194 if (t->queue == queue && t->arg == arg &&
195 t->callback != callback)
196 {
197 va_copy(va, v);
198 while ((f = va_arg(va, void (*)(void *))))
199 if (f == t->callback)
200 break;
201 va_end(va);
202 if (!f) {
203 if (last)
204 last->next = t->next;
205 else
206 timeouts = t->next;
207 t->next = free_timeouts;
208 free_timeouts = t;
209 continue;
210 }
211 }
212 last = t;
213 }
214 }
215
216 void
delete_q_timeouts(int queue,void * arg,void (* callback)(void *),...)217 delete_q_timeouts(int queue, void *arg, void (*callback)(void *), ...)
218 {
219 va_list va;
220
221 va_start(va, callback);
222 v_delete_q_timeouts(queue, arg, callback, va);
223 va_end(va);
224 }
225
226 void
delete_q_timeout(int queue,void (* callback)(void *),void * arg)227 delete_q_timeout(int queue, void (*callback)(void *), void *arg)
228 {
229 struct timeout *t, *tt, *last = NULL;
230
231 for (t = timeouts; t && (tt = t->next, 1); t = tt) {
232 if (t->queue == queue && t->arg == arg &&
233 (!callback || t->callback == callback))
234 {
235 if (last)
236 last->next = t->next;
237 else
238 timeouts = t->next;
239 t->next = free_timeouts;
240 free_timeouts = t;
241 continue;
242 }
243 last = t;
244 }
245 }
246
247 #ifdef DEBUG_MEMORY
248 /* Define this to free all malloced memory.
249 * Normally we don't do this as the OS will do it for us at exit,
250 * but it's handy for debugging other leaks in valgrind. */
251 static void
cleanup(void)252 cleanup(void)
253 {
254 struct event *e;
255 struct timeout *t;
256
257 while (events) {
258 e = events->next;
259 free(events);
260 events = e;
261 }
262 while (free_events) {
263 e = free_events->next;
264 free(free_events);
265 free_events = e;
266 }
267 while (timeouts) {
268 t = timeouts->next;
269 free(timeouts);
270 timeouts = t;
271 }
272 while (free_timeouts) {
273 t = free_timeouts->next;
274 free(free_timeouts);
275 free_timeouts = t;
276 }
277 free(fds);
278 }
279 #endif
280
281 _noreturn void
start_eloop(void)282 start_eloop(void)
283 {
284 int msecs, n;
285 nfds_t nfds, i;
286 struct event *e;
287 struct timeout *t;
288 struct timeval tv;
289
290 #ifdef DEBUG_MEMORY
291 atexit(cleanup);
292 #endif
293
294 for (;;) {
295 /* Run all timeouts first.
296 * When we have one that has not yet occured,
297 * calculate milliseconds until it does for use in poll. */
298 if (timeouts) {
299 if (timercmp(&now, &timeouts->when, >)) {
300 t = timeouts;
301 timeouts = timeouts->next;
302 t->callback(t->arg);
303 t->next = free_timeouts;
304 free_timeouts = t;
305 continue;
306 }
307 timersub(&timeouts->when, &now, &tv);
308 if (tv.tv_sec > INT_MAX / 1000 ||
309 (tv.tv_sec == INT_MAX / 1000 &&
310 (tv.tv_usec + 999) / 1000 > INT_MAX % 1000))
311 msecs = INT_MAX;
312 else
313 msecs = tv.tv_sec * 1000 +
314 (tv.tv_usec + 999) / 1000;
315 } else
316 /* No timeouts, so wait forever. */
317 msecs = -1;
318
319 /* Allocate memory for our pollfds as and when needed.
320 * We don't bother shrinking it. */
321 nfds = 0;
322 for (e = events; e; e = e->next)
323 nfds++;
324 if (msecs == -1 && nfds == 0) {
325 syslog(LOG_ERR, "nothing to do");
326 exit(EXIT_FAILURE);
327 }
328 if (nfds > fds_len) {
329 free(fds);
330 /* Allocate 5 more than we need for future use */
331 fds_len = nfds + 5;
332 fds = xmalloc(sizeof(*fds) * fds_len);
333 }
334 nfds = 0;
335 for (e = events; e; e = e->next) {
336 fds[nfds].fd = e->fd;
337 fds[nfds].events = POLLIN;
338 fds[nfds].revents = 0;
339 nfds++;
340 }
341 n = poll(fds, nfds, msecs);
342 if (n == -1) {
343 if (errno == EAGAIN || errno == EINTR) {
344 get_monotonic(&now);
345 continue;
346 }
347 syslog(LOG_ERR, "poll: %m");
348 exit(EXIT_FAILURE);
349 }
350
351 /* Get the now time and process any triggered events. */
352 get_monotonic(&now);
353 if (n == 0)
354 continue;
355 for (i = 0; i < nfds; i++) {
356 if (!(fds[i].revents & (POLLIN | POLLHUP)))
357 continue;
358 for (e = events; e; e = e->next) {
359 if (e->fd == fds[i].fd) {
360 e->callback(e->arg);
361 break;
362 }
363 }
364 }
365 }
366 }
367