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