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