1/*
2 * Event loop based on select() loop
3 * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * Alternatively, this software may be distributed under the terms of BSD
10 * license.
11 *
12 * See README and COPYING for more details.
13 */
14
15#include "includes.h"
16
17#include "common.h"
18#include "eloop.h"
19
20
21struct eloop_sock {
22	int sock;
23	void *eloop_data;
24	void *user_data;
25	eloop_sock_handler handler;
26};
27
28struct eloop_timeout {
29	struct os_time time;
30	void *eloop_data;
31	void *user_data;
32	eloop_timeout_handler handler;
33	struct eloop_timeout *next;
34};
35
36struct eloop_signal {
37	int sig;
38	void *user_data;
39	eloop_signal_handler handler;
40	int signaled;
41};
42
43struct eloop_sock_table {
44	int count;
45	struct eloop_sock *table;
46	int changed;
47};
48
49struct eloop_data {
50	void *user_data;
51
52	int max_sock;
53
54	struct eloop_sock_table readers;
55	struct eloop_sock_table writers;
56	struct eloop_sock_table exceptions;
57
58	struct eloop_timeout *timeout;
59
60	int signal_count;
61	struct eloop_signal *signals;
62	int signaled;
63	int pending_terminate;
64
65	int terminate;
66	int reader_table_changed;
67};
68
69static struct eloop_data eloop;
70
71
72int eloop_init(void *user_data)
73{
74	os_memset(&eloop, 0, sizeof(eloop));
75	eloop.user_data = user_data;
76	return 0;
77}
78
79
80static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
81                                     int sock, eloop_sock_handler handler,
82                                     void *eloop_data, void *user_data)
83{
84	struct eloop_sock *tmp;
85
86	if (table == NULL)
87		return -1;
88
89	tmp = (struct eloop_sock *)
90		os_realloc(table->table,
91			   (table->count + 1) * sizeof(struct eloop_sock));
92	if (tmp == NULL)
93		return -1;
94
95	tmp[table->count].sock = sock;
96	tmp[table->count].eloop_data = eloop_data;
97	tmp[table->count].user_data = user_data;
98	tmp[table->count].handler = handler;
99	table->count++;
100	table->table = tmp;
101	if (sock > eloop.max_sock)
102		eloop.max_sock = sock;
103	table->changed = 1;
104
105	return 0;
106}
107
108
109static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
110                                         int sock)
111{
112	int i;
113
114	if (table == NULL || table->table == NULL || table->count == 0)
115		return;
116
117	for (i = 0; i < table->count; i++) {
118		if (table->table[i].sock == sock)
119			break;
120	}
121	if (i == table->count)
122		return;
123	if (i != table->count - 1) {
124		os_memmove(&table->table[i], &table->table[i + 1],
125			   (table->count - i - 1) *
126			   sizeof(struct eloop_sock));
127	}
128	table->count--;
129	table->changed = 1;
130}
131
132
133static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
134				     fd_set *fds)
135{
136	int i;
137
138	FD_ZERO(fds);
139
140	if (table->table == NULL)
141		return;
142
143	for (i = 0; i < table->count; i++)
144		FD_SET(table->table[i].sock, fds);
145}
146
147
148static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
149				      fd_set *fds)
150{
151	int i;
152
153	if (table == NULL || table->table == NULL)
154		return;
155
156	table->changed = 0;
157	for (i = 0; i < table->count; i++) {
158		if (FD_ISSET(table->table[i].sock, fds)) {
159			table->table[i].handler(table->table[i].sock,
160						table->table[i].eloop_data,
161						table->table[i].user_data);
162			if (table->changed)
163				break;
164		}
165	}
166}
167
168
169static void eloop_sock_table_destroy(struct eloop_sock_table *table)
170{
171	if (table) {
172		int i;
173		for (i = 0; i < table->count && table->table; i++) {
174			printf("ELOOP: remaining socket: sock=%d "
175			       "eloop_data=%p user_data=%p handler=%p\n",
176			       table->table[i].sock,
177			       table->table[i].eloop_data,
178			       table->table[i].user_data,
179			       table->table[i].handler);
180		}
181		os_free(table->table);
182	}
183}
184
185
186int eloop_register_read_sock(int sock, eloop_sock_handler handler,
187			     void *eloop_data, void *user_data)
188{
189	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
190				   eloop_data, user_data);
191}
192
193
194void eloop_unregister_read_sock(int sock)
195{
196	eloop_unregister_sock(sock, EVENT_TYPE_READ);
197}
198
199
200static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
201{
202	switch (type) {
203	case EVENT_TYPE_READ:
204		return &eloop.readers;
205	case EVENT_TYPE_WRITE:
206		return &eloop.writers;
207	case EVENT_TYPE_EXCEPTION:
208		return &eloop.exceptions;
209	}
210
211	return NULL;
212}
213
214
215int eloop_register_sock(int sock, eloop_event_type type,
216			eloop_sock_handler handler,
217			void *eloop_data, void *user_data)
218{
219	struct eloop_sock_table *table;
220
221	table = eloop_get_sock_table(type);
222	return eloop_sock_table_add_sock(table, sock, handler,
223					 eloop_data, user_data);
224}
225
226
227void eloop_unregister_sock(int sock, eloop_event_type type)
228{
229	struct eloop_sock_table *table;
230
231	table = eloop_get_sock_table(type);
232	eloop_sock_table_remove_sock(table, sock);
233}
234
235
236int eloop_register_timeout(unsigned int secs, unsigned int usecs,
237			   eloop_timeout_handler handler,
238			   void *eloop_data, void *user_data)
239{
240	struct eloop_timeout *timeout, *tmp, *prev;
241
242	timeout = os_malloc(sizeof(*timeout));
243	if (timeout == NULL)
244		return -1;
245	if (os_get_time(&timeout->time) < 0) {
246		os_free(timeout);
247		return -1;
248	}
249	timeout->time.sec += secs;
250	timeout->time.usec += usecs;
251	while (timeout->time.usec >= 1000000) {
252		timeout->time.sec++;
253		timeout->time.usec -= 1000000;
254	}
255	timeout->eloop_data = eloop_data;
256	timeout->user_data = user_data;
257	timeout->handler = handler;
258	timeout->next = NULL;
259
260	if (eloop.timeout == NULL) {
261		eloop.timeout = timeout;
262		return 0;
263	}
264
265	prev = NULL;
266	tmp = eloop.timeout;
267	while (tmp != NULL) {
268		if (os_time_before(&timeout->time, &tmp->time))
269			break;
270		prev = tmp;
271		tmp = tmp->next;
272	}
273
274	if (prev == NULL) {
275		timeout->next = eloop.timeout;
276		eloop.timeout = timeout;
277	} else {
278		timeout->next = prev->next;
279		prev->next = timeout;
280	}
281
282	return 0;
283}
284
285
286int eloop_cancel_timeout(eloop_timeout_handler handler,
287			 void *eloop_data, void *user_data)
288{
289	struct eloop_timeout *timeout, *prev, *next;
290	int removed = 0;
291
292	prev = NULL;
293	timeout = eloop.timeout;
294	while (timeout != NULL) {
295		next = timeout->next;
296
297		if (timeout->handler == handler &&
298		    (timeout->eloop_data == eloop_data ||
299		     eloop_data == ELOOP_ALL_CTX) &&
300		    (timeout->user_data == user_data ||
301		     user_data == ELOOP_ALL_CTX)) {
302			if (prev == NULL)
303				eloop.timeout = next;
304			else
305				prev->next = next;
306			os_free(timeout);
307			removed++;
308		} else
309			prev = timeout;
310
311		timeout = next;
312	}
313
314	return removed;
315}
316
317
318int eloop_is_timeout_registered(eloop_timeout_handler handler,
319				void *eloop_data, void *user_data)
320{
321	struct eloop_timeout *tmp;
322
323	tmp = eloop.timeout;
324	while (tmp != NULL) {
325		if (tmp->handler == handler &&
326		    tmp->eloop_data == eloop_data &&
327		    tmp->user_data == user_data)
328			return 1;
329
330		tmp = tmp->next;
331	}
332
333	return 0;
334}
335
336
337#ifndef CONFIG_NATIVE_WINDOWS
338static void eloop_handle_alarm(int sig)
339{
340	fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two "
341		"seconds. Looks like there\n"
342		"is a bug that ends up in a busy loop that "
343		"prevents clean shutdown.\n"
344		"Killing program forcefully.\n");
345	exit(1);
346}
347#endif /* CONFIG_NATIVE_WINDOWS */
348
349
350static void eloop_handle_signal(int sig)
351{
352	int i;
353
354#ifndef CONFIG_NATIVE_WINDOWS
355	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
356		/* Use SIGALRM to break out from potential busy loops that
357		 * would not allow the program to be killed. */
358		eloop.pending_terminate = 1;
359		signal(SIGALRM, eloop_handle_alarm);
360		alarm(2);
361	}
362#endif /* CONFIG_NATIVE_WINDOWS */
363
364	eloop.signaled++;
365	for (i = 0; i < eloop.signal_count; i++) {
366		if (eloop.signals[i].sig == sig) {
367			eloop.signals[i].signaled++;
368			break;
369		}
370	}
371}
372
373
374static void eloop_process_pending_signals(void)
375{
376	int i;
377
378	if (eloop.signaled == 0)
379		return;
380	eloop.signaled = 0;
381
382	if (eloop.pending_terminate) {
383#ifndef CONFIG_NATIVE_WINDOWS
384		alarm(0);
385#endif /* CONFIG_NATIVE_WINDOWS */
386		eloop.pending_terminate = 0;
387	}
388
389	for (i = 0; i < eloop.signal_count; i++) {
390		if (eloop.signals[i].signaled) {
391			eloop.signals[i].signaled = 0;
392			eloop.signals[i].handler(eloop.signals[i].sig,
393						 eloop.user_data,
394						 eloop.signals[i].user_data);
395		}
396	}
397}
398
399
400int eloop_register_signal(int sig, eloop_signal_handler handler,
401			  void *user_data)
402{
403	struct eloop_signal *tmp;
404
405	tmp = (struct eloop_signal *)
406		os_realloc(eloop.signals,
407			   (eloop.signal_count + 1) *
408			   sizeof(struct eloop_signal));
409	if (tmp == NULL)
410		return -1;
411
412	tmp[eloop.signal_count].sig = sig;
413	tmp[eloop.signal_count].user_data = user_data;
414	tmp[eloop.signal_count].handler = handler;
415	tmp[eloop.signal_count].signaled = 0;
416	eloop.signal_count++;
417	eloop.signals = tmp;
418	signal(sig, eloop_handle_signal);
419
420	return 0;
421}
422
423
424int eloop_register_signal_terminate(eloop_signal_handler handler,
425				    void *user_data)
426{
427	int ret = eloop_register_signal(SIGINT, handler, user_data);
428	if (ret == 0)
429		ret = eloop_register_signal(SIGTERM, handler, user_data);
430	if (ret == 0)
431		ret = eloop_register_signal(SIGSEGV, handler, user_data);
432	return ret;
433}
434
435
436int eloop_register_signal_reconfig(eloop_signal_handler handler,
437				   void *user_data)
438{
439#ifdef CONFIG_NATIVE_WINDOWS
440	return 0;
441#else /* CONFIG_NATIVE_WINDOWS */
442	return eloop_register_signal(SIGHUP, handler, user_data);
443#endif /* CONFIG_NATIVE_WINDOWS */
444}
445
446
447void eloop_run(void)
448{
449	fd_set *rfds, *wfds, *efds;
450	int res;
451	struct timeval _tv;
452	struct os_time tv, now;
453
454	rfds = os_malloc(sizeof(*rfds));
455	wfds = os_malloc(sizeof(*wfds));
456	efds = os_malloc(sizeof(*efds));
457	if (rfds == NULL || wfds == NULL || efds == NULL) {
458		printf("eloop_run - malloc failed\n");
459		goto out;
460	}
461
462	while (!eloop.terminate &&
463	       (eloop.timeout || eloop.readers.count > 0 ||
464		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
465		if (eloop.timeout) {
466			os_get_time(&now);
467			if (os_time_before(&now, &eloop.timeout->time))
468				os_time_sub(&eloop.timeout->time, &now, &tv);
469			else
470				tv.sec = tv.usec = 0;
471#if 0
472			printf("next timeout in %lu.%06lu sec\n",
473			       tv.sec, tv.usec);
474#endif
475			_tv.tv_sec = tv.sec;
476			_tv.tv_usec = tv.usec;
477		}
478
479		eloop_sock_table_set_fds(&eloop.readers, rfds);
480		eloop_sock_table_set_fds(&eloop.writers, wfds);
481		eloop_sock_table_set_fds(&eloop.exceptions, efds);
482		res = select(eloop.max_sock + 1, rfds, wfds, efds,
483			     eloop.timeout ? &_tv : NULL);
484		if (res < 0 && errno != EINTR && errno != 0) {
485			perror("select");
486			goto out;
487		}
488		eloop_process_pending_signals();
489
490		/* check if some registered timeouts have occurred */
491		if (eloop.timeout) {
492			struct eloop_timeout *tmp;
493
494			os_get_time(&now);
495			if (!os_time_before(&now, &eloop.timeout->time)) {
496				tmp = eloop.timeout;
497				eloop.timeout = eloop.timeout->next;
498				tmp->handler(tmp->eloop_data,
499					     tmp->user_data);
500				os_free(tmp);
501			}
502
503		}
504
505		if (res <= 0)
506			continue;
507
508		eloop_sock_table_dispatch(&eloop.readers, rfds);
509		eloop_sock_table_dispatch(&eloop.writers, wfds);
510		eloop_sock_table_dispatch(&eloop.exceptions, efds);
511	}
512
513out:
514	os_free(rfds);
515	os_free(wfds);
516	os_free(efds);
517}
518
519
520void eloop_terminate(void)
521{
522	eloop.terminate = 1;
523}
524
525
526void eloop_destroy(void)
527{
528	struct eloop_timeout *timeout, *prev;
529	struct os_time now;
530
531	timeout = eloop.timeout;
532	if (timeout)
533		os_get_time(&now);
534	while (timeout != NULL) {
535		int sec, usec;
536		prev = timeout;
537		timeout = timeout->next;
538		sec = prev->time.sec - now.sec;
539		usec = prev->time.usec - now.usec;
540		if (prev->time.usec < now.usec) {
541			sec--;
542			usec += 1000000;
543		}
544		printf("ELOOP: remaining timeout: %d.%06d eloop_data=%p "
545		       "user_data=%p handler=%p\n",
546		       sec, usec, prev->eloop_data, prev->user_data,
547		       prev->handler);
548		os_free(prev);
549	}
550	eloop_sock_table_destroy(&eloop.readers);
551	eloop_sock_table_destroy(&eloop.writers);
552	eloop_sock_table_destroy(&eloop.exceptions);
553	os_free(eloop.signals);
554}
555
556
557int eloop_terminated(void)
558{
559	return eloop.terminate;
560}
561
562
563void eloop_wait_for_read_sock(int sock)
564{
565	fd_set rfds;
566
567	if (sock < 0)
568		return;
569
570	FD_ZERO(&rfds);
571	FD_SET(sock, &rfds);
572	select(sock + 1, &rfds, NULL, NULL, NULL);
573}
574
575
576void * eloop_get_user_data(void)
577{
578	return eloop.user_data;
579}
580