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
eloop_init(void * user_data)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
eloop_register_read_sock(int sock,void (* handler)(int sock,void * eloop_ctx,void * sock_ctx),void * eloop_data,void * user_data)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
eloop_unregister_read_sock(int sock)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
eloop_register_timeout(unsigned int secs,unsigned int usecs,void (* handler)(void * eloop_ctx,void * timeout_ctx),void * eloop_data,void * user_data)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
eloop_cancel_timeout(void (* handler)(void * eloop_ctx,void * sock_ctx),void * eloop_data,void * user_data)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
eloop_handle_signal(int sig)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
eloop_process_pending_signals(void)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
eloop_register_signal(int sig,void (* handler)(int sig,void * eloop_ctx,void * signal_ctx),void * user_data)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
eloop_run(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
eloop_terminate(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
eloop_destroy(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