Home | History | Annotate | Download | only in wpad
      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 (at) 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