1 /* 2 * dhcpcd - DHCP client daemon 3 * Copyright (c) 2006-2010 Roy Marples <roy (at) 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 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 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 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 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 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 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 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 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 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