1 /* 2 * Copyright 2008 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 timersub(&eloop.timeout->time, &now, &tv); 250 else 251 tv.tv_sec = tv.tv_usec = 0; 252 } 253 254 t = (eloop.timeout == NULL ? 255 default_t : (tv.tv_sec * 1000 + tv.tv_usec / 1000)); 256 for (i = 0; i < eloop.reader_count; i++) { 257 pfds[i].fd = eloop.readers[i].sock; 258 pfds[i].events = POLLIN | POLLPRI; 259 } 260 res = poll(pfds, eloop.reader_count, t); 261 if (res < 0 && errno != EINTR) 262 return; 263 264 eloop_process_pending_signals(); 265 266 /* check if some registered timeouts have occurred */ 267 if (eloop.timeout) { 268 struct eloop_timeout *tmp; 269 270 (void) gettimeofday(&now, NULL); 271 if (!timercmp(&now, &eloop.timeout->time, < /* */)) { 272 tmp = eloop.timeout; 273 eloop.timeout = eloop.timeout->next; 274 tmp->handler(tmp->eloop_data, tmp->user_data); 275 free(tmp); 276 } 277 278 } 279 280 if (res <= 0) 281 continue; 282 283 for (i = 0; i < eloop.reader_count; i++) { 284 if (pfds[i].revents) { 285 eloop.readers[i].handler( 286 eloop.readers[i].sock, 287 eloop.readers[i].eloop_data, 288 eloop.readers[i].user_data); 289 } 290 } 291 } 292 } 293 294 /* 295 * Terminate event loop even if there are registered events. 296 */ 297 void 298 eloop_terminate(void) 299 { 300 eloop.terminate = 1; 301 } 302 303 304 /* 305 * Free any reserved resources. After calling eloop_destoy(), other eloop_* 306 * functions must not be called before re-running eloop_init(). 307 */ 308 void 309 eloop_destroy(void) 310 { 311 struct eloop_timeout *timeout, *prev; 312 313 timeout = eloop.timeout; 314 while (timeout != NULL) { 315 prev = timeout; 316 timeout = timeout->next; 317 free(prev); 318 } 319 free(eloop.readers); 320 free(eloop.signals); 321 } 322