1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> 8 * Sun elects to license this software under the BSD license. 9 * See README for more details. 10 */ 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <sys/time.h> 18 #include <unistd.h> 19 #include <errno.h> 20 #include <signal.h> 21 #include <poll.h> 22 23 #include "eloop.h" 24 25 static struct eloop_data eloop; 26 /* 27 * Initialize global event loop data - must be called before any other eloop_* 28 * function. user_data is a pointer to global data structure and will be passed 29 * as eloop_ctx to signal handlers. 30 */ 31 void 32 eloop_init(void *user_data) 33 { 34 (void) memset(&eloop, 0, sizeof (eloop)); 35 eloop.user_data = user_data; 36 } 37 38 /* 39 * Register handler for read event 40 */ 41 int 42 eloop_register_read_sock(int sock, 43 void (*handler)(int sock, void *eloop_ctx, 44 void *sock_ctx), void *eloop_data, void *user_data) 45 { 46 struct eloop_sock *tmp; 47 48 tmp = (struct eloop_sock *)realloc(eloop.readers, 49 (eloop.reader_count + 1) * sizeof (struct eloop_sock)); 50 if (tmp == NULL) 51 return (-1); 52 53 tmp[eloop.reader_count].sock = sock; 54 tmp[eloop.reader_count].eloop_data = eloop_data; 55 tmp[eloop.reader_count].user_data = user_data; 56 tmp[eloop.reader_count].handler = handler; 57 eloop.reader_count++; 58 eloop.readers = tmp; 59 if (sock > eloop.max_sock) 60 eloop.max_sock = sock; 61 62 return (0); 63 } 64 65 void 66 eloop_unregister_read_sock(int sock) 67 { 68 int i; 69 70 if (eloop.readers == NULL || eloop.reader_count == 0) 71 return; 72 73 for (i = 0; i < eloop.reader_count; i++) { 74 if (eloop.readers[i].sock == sock) 75 break; 76 } 77 if (i == eloop.reader_count) 78 return; 79 if (i != eloop.reader_count - 1) { 80 (void) memmove(&eloop.readers[i], &eloop.readers[i + 1], 81 (eloop.reader_count - i - 1) * 82 sizeof (struct eloop_sock)); 83 } 84 eloop.reader_count--; 85 } 86 87 /* 88 * Register timeout routines 89 */ 90 int 91 eloop_register_timeout(unsigned int secs, unsigned int usecs, 92 void (*handler)(void *eloop_ctx, void *timeout_ctx), 93 void *eloop_data, void *user_data) 94 { 95 struct eloop_timeout *timeout, *tmp, *prev; 96 97 timeout = (struct eloop_timeout *)malloc(sizeof (*timeout)); 98 if (timeout == NULL) 99 return (-1); 100 (void) gettimeofday(&timeout->time, NULL); 101 timeout->time.tv_sec += secs; 102 timeout->time.tv_usec += usecs; 103 while (timeout->time.tv_usec >= 1000000) { 104 timeout->time.tv_sec++; 105 timeout->time.tv_usec -= 1000000; 106 } 107 timeout->eloop_data = eloop_data; 108 timeout->user_data = user_data; 109 timeout->handler = handler; 110 timeout->next = NULL; 111 112 if (eloop.timeout == NULL) { 113 eloop.timeout = timeout; 114 return (0); 115 } 116 117 prev = NULL; 118 tmp = eloop.timeout; 119 while (tmp != NULL) { 120 if (timercmp(&timeout->time, &tmp->time, < /* */)) 121 break; 122 prev = tmp; 123 tmp = tmp->next; 124 } 125 126 if (prev == NULL) { 127 timeout->next = eloop.timeout; 128 eloop.timeout = timeout; 129 } else { 130 timeout->next = prev->next; 131 prev->next = timeout; 132 } 133 134 return (0); 135 } 136 137 /* 138 * Cancel timeouts matching <handler,eloop_data,user_data>. 139 * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts 140 * regardless of eloop_data/user_data. 141 */ 142 void 143 eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), 144 void *eloop_data, void *user_data) 145 { 146 struct eloop_timeout *timeout, *prev, *next; 147 148 prev = NULL; 149 timeout = eloop.timeout; 150 while (timeout != NULL) { 151 next = timeout->next; 152 153 if (timeout->handler == handler && 154 (timeout->eloop_data == eloop_data || 155 eloop_data == ELOOP_ALL_CTX) && 156 (timeout->user_data == user_data || 157 user_data == ELOOP_ALL_CTX)) { 158 if (prev == NULL) 159 eloop.timeout = next; 160 else 161 prev->next = next; 162 free(timeout); 163 } else 164 prev = timeout; 165 166 timeout = next; 167 } 168 } 169 170 static void eloop_handle_signal(int sig) 171 { 172 int i; 173 174 eloop.signaled++; 175 for (i = 0; i < eloop.signal_count; i++) { 176 if (eloop.signals[i].sig == sig) { 177 eloop.signals[i].signaled++; 178 break; 179 } 180 } 181 } 182 183 static void eloop_process_pending_signals(void) 184 { 185 int i; 186 187 if (eloop.signaled == 0) 188 return; 189 eloop.signaled = 0; 190 191 for (i = 0; i < eloop.signal_count; i++) { 192 if (eloop.signals[i].signaled) { 193 eloop.signals[i].signaled = 0; 194 eloop.signals[i].handler(eloop.signals[i].sig, 195 eloop.user_data, eloop.signals[i].user_data); 196 } 197 } 198 } 199 200 /* 201 * Register handler for signal. 202 * Note: signals are 'global' events and there is no local eloop_data pointer 203 * like with other handlers. The (global) pointer given to eloop_init() will be 204 * used as eloop_ctx for signal handlers. 205 */ 206 int 207 eloop_register_signal(int sig, 208 void (*handler)(int sig, void *eloop_ctx, void *signal_ctx), 209 void *user_data) 210 { 211 struct eloop_signal *tmp; 212 213 tmp = (struct eloop_signal *) 214 realloc(eloop.signals, 215 (eloop.signal_count + 1) * 216 sizeof (struct eloop_signal)); 217 if (tmp == NULL) 218 return (-1); 219 220 tmp[eloop.signal_count].sig = sig; 221 tmp[eloop.signal_count].user_data = user_data; 222 tmp[eloop.signal_count].handler = handler; 223 tmp[eloop.signal_count].signaled = 0; 224 eloop.signal_count++; 225 eloop.signals = tmp; 226 (void) signal(sig, eloop_handle_signal); 227 228 return (0); 229 } 230 231 /* 232 * Start event loop and continue running as long as there are any registered 233 * event handlers. 234 */ 235 void 236 eloop_run(void) 237 { 238 struct pollfd pfds[MAX_POLLFDS]; /* array of polled fd */ 239 int i, res; 240 int default_t, t; 241 struct timeval tv, now; 242 243 default_t = 5 * 1000; /* 5 seconds */ 244 while (!eloop.terminate && 245 (eloop.timeout || eloop.reader_count > 0)) { 246 if (eloop.timeout) { 247 (void) gettimeofday(&now, NULL); 248 if (timercmp(&now, &eloop.timeout->time, < /* */)) 249 /* LINTED E_CONSTANT_CONDITION */ 250 timersub(&eloop.timeout->time, &now, &tv); 251 else 252 tv.tv_sec = tv.tv_usec = 0; 253 } 254 255 t = (eloop.timeout == NULL ? 256 default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 257 for (i = 0; i < eloop.reader_count; i++) { 258 pfds[i].fd = eloop.readers[i].sock; 259 pfds[i].events = POLLIN | POLLPRI; 260 } 261 res = poll(pfds, eloop.reader_count, t); 262 if (res < 0 && errno != EINTR) 263 return; 264 265 eloop_process_pending_signals(); 266 267 /* check if some registered timeouts have occurred */ 268 if (eloop.timeout) { 269 struct eloop_timeout *tmp; 270 271 (void) gettimeofday(&now, NULL); 272 if (!timercmp(&now, &eloop.timeout->time, < /* */)) { 273 tmp = eloop.timeout; 274 eloop.timeout = eloop.timeout->next; 275 tmp->handler(tmp->eloop_data, tmp->user_data); 276 free(tmp); 277 } 278 279 } 280 281 if (res <= 0) 282 continue; 283 284 for (i = 0; i < eloop.reader_count; i++) { 285 if (pfds[i].revents) { 286 eloop.readers[i].handler( 287 eloop.readers[i].sock, 288 eloop.readers[i].eloop_data, 289 eloop.readers[i].user_data); 290 } 291 } 292 } 293 } 294 295 /* 296 * Terminate event loop even if there are registered events. 297 */ 298 void 299 eloop_terminate(void) 300 { 301 eloop.terminate = 1; 302 } 303 304 305 /* 306 * Free any reserved resources. After calling eloop_destoy(), other eloop_* 307 * functions must not be called before re-running eloop_init(). 308 */ 309 void 310 eloop_destroy(void) 311 { 312 struct eloop_timeout *timeout, *prev; 313 314 timeout = eloop.timeout; 315 while (timeout != NULL) { 316 prev = timeout; 317 timeout = timeout->next; 318 free(prev); 319 } 320 free(eloop.readers); 321 free(eloop.signals); 322 } 323