• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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