1189251Ssam/*
2189251Ssam * Event loop based on select() loop
3214734Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12214734Srpaulo#include "trace.h"
13214734Srpaulo#include "list.h"
14189251Ssam#include "eloop.h"
15189251Ssam
16252726Srpaulo#ifdef CONFIG_ELOOP_POLL
17252726Srpaulo#include <assert.h>
18252726Srpaulo#include <poll.h>
19252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
20189251Ssam
21252726Srpaulo
22189251Ssamstruct eloop_sock {
23189251Ssam	int sock;
24189251Ssam	void *eloop_data;
25189251Ssam	void *user_data;
26189251Ssam	eloop_sock_handler handler;
27214734Srpaulo	WPA_TRACE_REF(eloop);
28214734Srpaulo	WPA_TRACE_REF(user);
29214734Srpaulo	WPA_TRACE_INFO
30189251Ssam};
31189251Ssam
32189251Ssamstruct eloop_timeout {
33214734Srpaulo	struct dl_list list;
34189251Ssam	struct os_time time;
35189251Ssam	void *eloop_data;
36189251Ssam	void *user_data;
37189251Ssam	eloop_timeout_handler handler;
38214734Srpaulo	WPA_TRACE_REF(eloop);
39214734Srpaulo	WPA_TRACE_REF(user);
40214734Srpaulo	WPA_TRACE_INFO
41189251Ssam};
42189251Ssam
43189251Ssamstruct eloop_signal {
44189251Ssam	int sig;
45189251Ssam	void *user_data;
46189251Ssam	eloop_signal_handler handler;
47189251Ssam	int signaled;
48189251Ssam};
49189251Ssam
50189251Ssamstruct eloop_sock_table {
51189251Ssam	int count;
52189251Ssam	struct eloop_sock *table;
53189251Ssam	int changed;
54189251Ssam};
55189251Ssam
56189251Ssamstruct eloop_data {
57189251Ssam	int max_sock;
58189251Ssam
59252726Srpaulo	int count; /* sum of all table counts */
60252726Srpaulo#ifdef CONFIG_ELOOP_POLL
61252726Srpaulo	int max_pollfd_map; /* number of pollfds_map currently allocated */
62252726Srpaulo	int max_poll_fds; /* number of pollfds currently allocated */
63252726Srpaulo	struct pollfd *pollfds;
64252726Srpaulo	struct pollfd **pollfds_map;
65252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
66189251Ssam	struct eloop_sock_table readers;
67189251Ssam	struct eloop_sock_table writers;
68189251Ssam	struct eloop_sock_table exceptions;
69189251Ssam
70214734Srpaulo	struct dl_list timeout;
71189251Ssam
72189251Ssam	int signal_count;
73189251Ssam	struct eloop_signal *signals;
74189251Ssam	int signaled;
75189251Ssam	int pending_terminate;
76189251Ssam
77189251Ssam	int terminate;
78189251Ssam	int reader_table_changed;
79189251Ssam};
80189251Ssam
81189251Ssamstatic struct eloop_data eloop;
82189251Ssam
83189251Ssam
84214734Srpaulo#ifdef WPA_TRACE
85214734Srpaulo
86214734Srpaulostatic void eloop_sigsegv_handler(int sig)
87189251Ssam{
88214734Srpaulo	wpa_trace_show("eloop SIGSEGV");
89214734Srpaulo	abort();
90214734Srpaulo}
91214734Srpaulo
92214734Srpaulostatic void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
93214734Srpaulo{
94214734Srpaulo	int i;
95214734Srpaulo	if (table == NULL || table->table == NULL)
96214734Srpaulo		return;
97214734Srpaulo	for (i = 0; i < table->count; i++) {
98214734Srpaulo		wpa_trace_add_ref(&table->table[i], eloop,
99214734Srpaulo				  table->table[i].eloop_data);
100214734Srpaulo		wpa_trace_add_ref(&table->table[i], user,
101214734Srpaulo				  table->table[i].user_data);
102214734Srpaulo	}
103214734Srpaulo}
104214734Srpaulo
105214734Srpaulo
106214734Srpaulostatic void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
107214734Srpaulo{
108214734Srpaulo	int i;
109214734Srpaulo	if (table == NULL || table->table == NULL)
110214734Srpaulo		return;
111214734Srpaulo	for (i = 0; i < table->count; i++) {
112214734Srpaulo		wpa_trace_remove_ref(&table->table[i], eloop,
113214734Srpaulo				     table->table[i].eloop_data);
114214734Srpaulo		wpa_trace_remove_ref(&table->table[i], user,
115214734Srpaulo				     table->table[i].user_data);
116214734Srpaulo	}
117214734Srpaulo}
118214734Srpaulo
119214734Srpaulo#else /* WPA_TRACE */
120214734Srpaulo
121214734Srpaulo#define eloop_trace_sock_add_ref(table) do { } while (0)
122214734Srpaulo#define eloop_trace_sock_remove_ref(table) do { } while (0)
123214734Srpaulo
124214734Srpaulo#endif /* WPA_TRACE */
125214734Srpaulo
126214734Srpaulo
127214734Srpauloint eloop_init(void)
128214734Srpaulo{
129189251Ssam	os_memset(&eloop, 0, sizeof(eloop));
130214734Srpaulo	dl_list_init(&eloop.timeout);
131214734Srpaulo#ifdef WPA_TRACE
132214734Srpaulo	signal(SIGSEGV, eloop_sigsegv_handler);
133214734Srpaulo#endif /* WPA_TRACE */
134189251Ssam	return 0;
135189251Ssam}
136189251Ssam
137189251Ssam
138189251Ssamstatic int eloop_sock_table_add_sock(struct eloop_sock_table *table,
139189251Ssam                                     int sock, eloop_sock_handler handler,
140189251Ssam                                     void *eloop_data, void *user_data)
141189251Ssam{
142189251Ssam	struct eloop_sock *tmp;
143252726Srpaulo	int new_max_sock;
144189251Ssam
145252726Srpaulo	if (sock > eloop.max_sock)
146252726Srpaulo		new_max_sock = sock;
147252726Srpaulo	else
148252726Srpaulo		new_max_sock = eloop.max_sock;
149252726Srpaulo
150189251Ssam	if (table == NULL)
151189251Ssam		return -1;
152189251Ssam
153252726Srpaulo#ifdef CONFIG_ELOOP_POLL
154252726Srpaulo	if (new_max_sock >= eloop.max_pollfd_map) {
155252726Srpaulo		struct pollfd **nmap;
156252726Srpaulo		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
157252726Srpaulo					sizeof(struct pollfd *));
158252726Srpaulo		if (nmap == NULL)
159252726Srpaulo			return -1;
160252726Srpaulo
161252726Srpaulo		eloop.max_pollfd_map = new_max_sock + 50;
162252726Srpaulo		eloop.pollfds_map = nmap;
163252726Srpaulo	}
164252726Srpaulo
165252726Srpaulo	if (eloop.count + 1 > eloop.max_poll_fds) {
166252726Srpaulo		struct pollfd *n;
167252726Srpaulo		int nmax = eloop.count + 1 + 50;
168252726Srpaulo		n = os_realloc_array(eloop.pollfds, nmax,
169252726Srpaulo				     sizeof(struct pollfd));
170252726Srpaulo		if (n == NULL)
171252726Srpaulo			return -1;
172252726Srpaulo
173252726Srpaulo		eloop.max_poll_fds = nmax;
174252726Srpaulo		eloop.pollfds = n;
175252726Srpaulo	}
176252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
177252726Srpaulo
178214734Srpaulo	eloop_trace_sock_remove_ref(table);
179252726Srpaulo	tmp = os_realloc_array(table->table, table->count + 1,
180252726Srpaulo			       sizeof(struct eloop_sock));
181189251Ssam	if (tmp == NULL)
182189251Ssam		return -1;
183189251Ssam
184189251Ssam	tmp[table->count].sock = sock;
185189251Ssam	tmp[table->count].eloop_data = eloop_data;
186189251Ssam	tmp[table->count].user_data = user_data;
187189251Ssam	tmp[table->count].handler = handler;
188214734Srpaulo	wpa_trace_record(&tmp[table->count]);
189189251Ssam	table->count++;
190189251Ssam	table->table = tmp;
191252726Srpaulo	eloop.max_sock = new_max_sock;
192252726Srpaulo	eloop.count++;
193189251Ssam	table->changed = 1;
194214734Srpaulo	eloop_trace_sock_add_ref(table);
195189251Ssam
196189251Ssam	return 0;
197189251Ssam}
198189251Ssam
199189251Ssam
200189251Ssamstatic void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
201189251Ssam                                         int sock)
202189251Ssam{
203189251Ssam	int i;
204189251Ssam
205189251Ssam	if (table == NULL || table->table == NULL || table->count == 0)
206189251Ssam		return;
207189251Ssam
208189251Ssam	for (i = 0; i < table->count; i++) {
209189251Ssam		if (table->table[i].sock == sock)
210189251Ssam			break;
211189251Ssam	}
212189251Ssam	if (i == table->count)
213189251Ssam		return;
214214734Srpaulo	eloop_trace_sock_remove_ref(table);
215189251Ssam	if (i != table->count - 1) {
216189251Ssam		os_memmove(&table->table[i], &table->table[i + 1],
217189251Ssam			   (table->count - i - 1) *
218189251Ssam			   sizeof(struct eloop_sock));
219189251Ssam	}
220189251Ssam	table->count--;
221252726Srpaulo	eloop.count--;
222189251Ssam	table->changed = 1;
223214734Srpaulo	eloop_trace_sock_add_ref(table);
224189251Ssam}
225189251Ssam
226189251Ssam
227252726Srpaulo#ifdef CONFIG_ELOOP_POLL
228252726Srpaulo
229252726Srpaulostatic struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
230252726Srpaulo{
231252726Srpaulo	if (fd < mx && fd >= 0)
232252726Srpaulo		return pollfds_map[fd];
233252726Srpaulo	return NULL;
234252726Srpaulo}
235252726Srpaulo
236252726Srpaulo
237252726Srpaulostatic int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
238252726Srpaulo				    struct eloop_sock_table *writers,
239252726Srpaulo				    struct eloop_sock_table *exceptions,
240252726Srpaulo				    struct pollfd *pollfds,
241252726Srpaulo				    struct pollfd **pollfds_map,
242252726Srpaulo				    int max_pollfd_map)
243252726Srpaulo{
244252726Srpaulo	int i;
245252726Srpaulo	int nxt = 0;
246252726Srpaulo	int fd;
247252726Srpaulo	struct pollfd *pfd;
248252726Srpaulo
249252726Srpaulo	/* Clear pollfd lookup map. It will be re-populated below. */
250252726Srpaulo	os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
251252726Srpaulo
252252726Srpaulo	if (readers && readers->table) {
253252726Srpaulo		for (i = 0; i < readers->count; i++) {
254252726Srpaulo			fd = readers->table[i].sock;
255252726Srpaulo			assert(fd >= 0 && fd < max_pollfd_map);
256252726Srpaulo			pollfds[nxt].fd = fd;
257252726Srpaulo			pollfds[nxt].events = POLLIN;
258252726Srpaulo			pollfds[nxt].revents = 0;
259252726Srpaulo			pollfds_map[fd] = &(pollfds[nxt]);
260252726Srpaulo			nxt++;
261252726Srpaulo		}
262252726Srpaulo	}
263252726Srpaulo
264252726Srpaulo	if (writers && writers->table) {
265252726Srpaulo		for (i = 0; i < writers->count; i++) {
266252726Srpaulo			/*
267252726Srpaulo			 * See if we already added this descriptor, update it
268252726Srpaulo			 * if so.
269252726Srpaulo			 */
270252726Srpaulo			fd = writers->table[i].sock;
271252726Srpaulo			assert(fd >= 0 && fd < max_pollfd_map);
272252726Srpaulo			pfd = pollfds_map[fd];
273252726Srpaulo			if (!pfd) {
274252726Srpaulo				pfd = &(pollfds[nxt]);
275252726Srpaulo				pfd->events = 0;
276252726Srpaulo				pfd->fd = fd;
277252726Srpaulo				pollfds[i].revents = 0;
278252726Srpaulo				pollfds_map[fd] = pfd;
279252726Srpaulo				nxt++;
280252726Srpaulo			}
281252726Srpaulo			pfd->events |= POLLOUT;
282252726Srpaulo		}
283252726Srpaulo	}
284252726Srpaulo
285252726Srpaulo	/*
286252726Srpaulo	 * Exceptions are always checked when using poll, but I suppose it's
287252726Srpaulo	 * possible that someone registered a socket *only* for exception
288252726Srpaulo	 * handling. Set the POLLIN bit in this case.
289252726Srpaulo	 */
290252726Srpaulo	if (exceptions && exceptions->table) {
291252726Srpaulo		for (i = 0; i < exceptions->count; i++) {
292252726Srpaulo			/*
293252726Srpaulo			 * See if we already added this descriptor, just use it
294252726Srpaulo			 * if so.
295252726Srpaulo			 */
296252726Srpaulo			fd = exceptions->table[i].sock;
297252726Srpaulo			assert(fd >= 0 && fd < max_pollfd_map);
298252726Srpaulo			pfd = pollfds_map[fd];
299252726Srpaulo			if (!pfd) {
300252726Srpaulo				pfd = &(pollfds[nxt]);
301252726Srpaulo				pfd->events = POLLIN;
302252726Srpaulo				pfd->fd = fd;
303252726Srpaulo				pollfds[i].revents = 0;
304252726Srpaulo				pollfds_map[fd] = pfd;
305252726Srpaulo				nxt++;
306252726Srpaulo			}
307252726Srpaulo		}
308252726Srpaulo	}
309252726Srpaulo
310252726Srpaulo	return nxt;
311252726Srpaulo}
312252726Srpaulo
313252726Srpaulo
314252726Srpaulostatic int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
315252726Srpaulo					   struct pollfd **pollfds_map,
316252726Srpaulo					   int max_pollfd_map,
317252726Srpaulo					   short int revents)
318252726Srpaulo{
319252726Srpaulo	int i;
320252726Srpaulo	struct pollfd *pfd;
321252726Srpaulo
322252726Srpaulo	if (!table || !table->table)
323252726Srpaulo		return 0;
324252726Srpaulo
325252726Srpaulo	table->changed = 0;
326252726Srpaulo	for (i = 0; i < table->count; i++) {
327252726Srpaulo		pfd = find_pollfd(pollfds_map, table->table[i].sock,
328252726Srpaulo				  max_pollfd_map);
329252726Srpaulo		if (!pfd)
330252726Srpaulo			continue;
331252726Srpaulo
332252726Srpaulo		if (!(pfd->revents & revents))
333252726Srpaulo			continue;
334252726Srpaulo
335252726Srpaulo		table->table[i].handler(table->table[i].sock,
336252726Srpaulo					table->table[i].eloop_data,
337252726Srpaulo					table->table[i].user_data);
338252726Srpaulo		if (table->changed)
339252726Srpaulo			return 1;
340252726Srpaulo	}
341252726Srpaulo
342252726Srpaulo	return 0;
343252726Srpaulo}
344252726Srpaulo
345252726Srpaulo
346252726Srpaulostatic void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
347252726Srpaulo				      struct eloop_sock_table *writers,
348252726Srpaulo				      struct eloop_sock_table *exceptions,
349252726Srpaulo				      struct pollfd **pollfds_map,
350252726Srpaulo				      int max_pollfd_map)
351252726Srpaulo{
352252726Srpaulo	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
353252726Srpaulo					    max_pollfd_map, POLLIN | POLLERR |
354252726Srpaulo					    POLLHUP))
355252726Srpaulo		return; /* pollfds may be invalid at this point */
356252726Srpaulo
357252726Srpaulo	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
358252726Srpaulo					    max_pollfd_map, POLLOUT))
359252726Srpaulo		return; /* pollfds may be invalid at this point */
360252726Srpaulo
361252726Srpaulo	eloop_sock_table_dispatch_table(exceptions, pollfds_map,
362252726Srpaulo					max_pollfd_map, POLLERR | POLLHUP);
363252726Srpaulo}
364252726Srpaulo
365252726Srpaulo#else /* CONFIG_ELOOP_POLL */
366252726Srpaulo
367189251Ssamstatic void eloop_sock_table_set_fds(struct eloop_sock_table *table,
368189251Ssam				     fd_set *fds)
369189251Ssam{
370189251Ssam	int i;
371189251Ssam
372189251Ssam	FD_ZERO(fds);
373189251Ssam
374189251Ssam	if (table->table == NULL)
375189251Ssam		return;
376189251Ssam
377189251Ssam	for (i = 0; i < table->count; i++)
378189251Ssam		FD_SET(table->table[i].sock, fds);
379189251Ssam}
380189251Ssam
381189251Ssam
382189251Ssamstatic void eloop_sock_table_dispatch(struct eloop_sock_table *table,
383189251Ssam				      fd_set *fds)
384189251Ssam{
385189251Ssam	int i;
386189251Ssam
387189251Ssam	if (table == NULL || table->table == NULL)
388189251Ssam		return;
389189251Ssam
390189251Ssam	table->changed = 0;
391189251Ssam	for (i = 0; i < table->count; i++) {
392189251Ssam		if (FD_ISSET(table->table[i].sock, fds)) {
393189251Ssam			table->table[i].handler(table->table[i].sock,
394189251Ssam						table->table[i].eloop_data,
395189251Ssam						table->table[i].user_data);
396189251Ssam			if (table->changed)
397189251Ssam				break;
398189251Ssam		}
399189251Ssam	}
400189251Ssam}
401189251Ssam
402252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
403189251Ssam
404252726Srpaulo
405189251Ssamstatic void eloop_sock_table_destroy(struct eloop_sock_table *table)
406189251Ssam{
407189251Ssam	if (table) {
408189251Ssam		int i;
409189251Ssam		for (i = 0; i < table->count && table->table; i++) {
410214734Srpaulo			wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
411214734Srpaulo				   "sock=%d eloop_data=%p user_data=%p "
412214734Srpaulo				   "handler=%p",
413214734Srpaulo				   table->table[i].sock,
414214734Srpaulo				   table->table[i].eloop_data,
415214734Srpaulo				   table->table[i].user_data,
416214734Srpaulo				   table->table[i].handler);
417214734Srpaulo			wpa_trace_dump_funcname("eloop unregistered socket "
418214734Srpaulo						"handler",
419214734Srpaulo						table->table[i].handler);
420214734Srpaulo			wpa_trace_dump("eloop sock", &table->table[i]);
421189251Ssam		}
422189251Ssam		os_free(table->table);
423189251Ssam	}
424189251Ssam}
425189251Ssam
426189251Ssam
427189251Ssamint eloop_register_read_sock(int sock, eloop_sock_handler handler,
428189251Ssam			     void *eloop_data, void *user_data)
429189251Ssam{
430189251Ssam	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
431189251Ssam				   eloop_data, user_data);
432189251Ssam}
433189251Ssam
434189251Ssam
435189251Ssamvoid eloop_unregister_read_sock(int sock)
436189251Ssam{
437189251Ssam	eloop_unregister_sock(sock, EVENT_TYPE_READ);
438189251Ssam}
439189251Ssam
440189251Ssam
441189251Ssamstatic struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
442189251Ssam{
443189251Ssam	switch (type) {
444189251Ssam	case EVENT_TYPE_READ:
445189251Ssam		return &eloop.readers;
446189251Ssam	case EVENT_TYPE_WRITE:
447189251Ssam		return &eloop.writers;
448189251Ssam	case EVENT_TYPE_EXCEPTION:
449189251Ssam		return &eloop.exceptions;
450189251Ssam	}
451189251Ssam
452189251Ssam	return NULL;
453189251Ssam}
454189251Ssam
455189251Ssam
456189251Ssamint eloop_register_sock(int sock, eloop_event_type type,
457189251Ssam			eloop_sock_handler handler,
458189251Ssam			void *eloop_data, void *user_data)
459189251Ssam{
460189251Ssam	struct eloop_sock_table *table;
461189251Ssam
462189251Ssam	table = eloop_get_sock_table(type);
463189251Ssam	return eloop_sock_table_add_sock(table, sock, handler,
464189251Ssam					 eloop_data, user_data);
465189251Ssam}
466189251Ssam
467189251Ssam
468189251Ssamvoid eloop_unregister_sock(int sock, eloop_event_type type)
469189251Ssam{
470189251Ssam	struct eloop_sock_table *table;
471189251Ssam
472189251Ssam	table = eloop_get_sock_table(type);
473189251Ssam	eloop_sock_table_remove_sock(table, sock);
474189251Ssam}
475189251Ssam
476189251Ssam
477189251Ssamint eloop_register_timeout(unsigned int secs, unsigned int usecs,
478189251Ssam			   eloop_timeout_handler handler,
479189251Ssam			   void *eloop_data, void *user_data)
480189251Ssam{
481214734Srpaulo	struct eloop_timeout *timeout, *tmp;
482252726Srpaulo	os_time_t now_sec;
483189251Ssam
484214734Srpaulo	timeout = os_zalloc(sizeof(*timeout));
485189251Ssam	if (timeout == NULL)
486189251Ssam		return -1;
487189251Ssam	if (os_get_time(&timeout->time) < 0) {
488189251Ssam		os_free(timeout);
489189251Ssam		return -1;
490189251Ssam	}
491252726Srpaulo	now_sec = timeout->time.sec;
492189251Ssam	timeout->time.sec += secs;
493252726Srpaulo	if (timeout->time.sec < now_sec) {
494252726Srpaulo		/*
495252726Srpaulo		 * Integer overflow - assume long enough timeout to be assumed
496252726Srpaulo		 * to be infinite, i.e., the timeout would never happen.
497252726Srpaulo		 */
498252726Srpaulo		wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
499252726Srpaulo			   "ever happen - ignore it", secs);
500252726Srpaulo		os_free(timeout);
501252726Srpaulo		return 0;
502252726Srpaulo	}
503189251Ssam	timeout->time.usec += usecs;
504189251Ssam	while (timeout->time.usec >= 1000000) {
505189251Ssam		timeout->time.sec++;
506189251Ssam		timeout->time.usec -= 1000000;
507189251Ssam	}
508189251Ssam	timeout->eloop_data = eloop_data;
509189251Ssam	timeout->user_data = user_data;
510189251Ssam	timeout->handler = handler;
511214734Srpaulo	wpa_trace_add_ref(timeout, eloop, eloop_data);
512214734Srpaulo	wpa_trace_add_ref(timeout, user, user_data);
513214734Srpaulo	wpa_trace_record(timeout);
514189251Ssam
515214734Srpaulo	/* Maintain timeouts in order of increasing time */
516214734Srpaulo	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
517214734Srpaulo		if (os_time_before(&timeout->time, &tmp->time)) {
518214734Srpaulo			dl_list_add(tmp->list.prev, &timeout->list);
519214734Srpaulo			return 0;
520214734Srpaulo		}
521189251Ssam	}
522214734Srpaulo	dl_list_add_tail(&eloop.timeout, &timeout->list);
523189251Ssam
524214734Srpaulo	return 0;
525214734Srpaulo}
526189251Ssam
527189251Ssam
528214734Srpaulostatic void eloop_remove_timeout(struct eloop_timeout *timeout)
529214734Srpaulo{
530214734Srpaulo	dl_list_del(&timeout->list);
531214734Srpaulo	wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
532214734Srpaulo	wpa_trace_remove_ref(timeout, user, timeout->user_data);
533214734Srpaulo	os_free(timeout);
534189251Ssam}
535189251Ssam
536189251Ssam
537189251Ssamint eloop_cancel_timeout(eloop_timeout_handler handler,
538189251Ssam			 void *eloop_data, void *user_data)
539189251Ssam{
540214734Srpaulo	struct eloop_timeout *timeout, *prev;
541189251Ssam	int removed = 0;
542189251Ssam
543214734Srpaulo	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
544214734Srpaulo			      struct eloop_timeout, list) {
545189251Ssam		if (timeout->handler == handler &&
546189251Ssam		    (timeout->eloop_data == eloop_data ||
547189251Ssam		     eloop_data == ELOOP_ALL_CTX) &&
548189251Ssam		    (timeout->user_data == user_data ||
549189251Ssam		     user_data == ELOOP_ALL_CTX)) {
550214734Srpaulo			eloop_remove_timeout(timeout);
551189251Ssam			removed++;
552214734Srpaulo		}
553189251Ssam	}
554189251Ssam
555189251Ssam	return removed;
556189251Ssam}
557189251Ssam
558189251Ssam
559189251Ssamint eloop_is_timeout_registered(eloop_timeout_handler handler,
560189251Ssam				void *eloop_data, void *user_data)
561189251Ssam{
562189251Ssam	struct eloop_timeout *tmp;
563189251Ssam
564214734Srpaulo	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
565189251Ssam		if (tmp->handler == handler &&
566189251Ssam		    tmp->eloop_data == eloop_data &&
567189251Ssam		    tmp->user_data == user_data)
568189251Ssam			return 1;
569189251Ssam	}
570189251Ssam
571189251Ssam	return 0;
572189251Ssam}
573189251Ssam
574189251Ssam
575189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
576189251Ssamstatic void eloop_handle_alarm(int sig)
577189251Ssam{
578214734Srpaulo	wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
579214734Srpaulo		   "two seconds. Looks like there\n"
580214734Srpaulo		   "is a bug that ends up in a busy loop that "
581214734Srpaulo		   "prevents clean shutdown.\n"
582214734Srpaulo		   "Killing program forcefully.\n");
583189251Ssam	exit(1);
584189251Ssam}
585189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
586189251Ssam
587189251Ssam
588189251Ssamstatic void eloop_handle_signal(int sig)
589189251Ssam{
590189251Ssam	int i;
591189251Ssam
592189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
593189251Ssam	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
594189251Ssam		/* Use SIGALRM to break out from potential busy loops that
595189251Ssam		 * would not allow the program to be killed. */
596189251Ssam		eloop.pending_terminate = 1;
597189251Ssam		signal(SIGALRM, eloop_handle_alarm);
598189251Ssam		alarm(2);
599189251Ssam	}
600189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
601189251Ssam
602189251Ssam	eloop.signaled++;
603189251Ssam	for (i = 0; i < eloop.signal_count; i++) {
604189251Ssam		if (eloop.signals[i].sig == sig) {
605189251Ssam			eloop.signals[i].signaled++;
606189251Ssam			break;
607189251Ssam		}
608189251Ssam	}
609189251Ssam}
610189251Ssam
611189251Ssam
612189251Ssamstatic void eloop_process_pending_signals(void)
613189251Ssam{
614189251Ssam	int i;
615189251Ssam
616189251Ssam	if (eloop.signaled == 0)
617189251Ssam		return;
618189251Ssam	eloop.signaled = 0;
619189251Ssam
620189251Ssam	if (eloop.pending_terminate) {
621189251Ssam#ifndef CONFIG_NATIVE_WINDOWS
622189251Ssam		alarm(0);
623189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
624189251Ssam		eloop.pending_terminate = 0;
625189251Ssam	}
626189251Ssam
627189251Ssam	for (i = 0; i < eloop.signal_count; i++) {
628189251Ssam		if (eloop.signals[i].signaled) {
629189251Ssam			eloop.signals[i].signaled = 0;
630189251Ssam			eloop.signals[i].handler(eloop.signals[i].sig,
631189251Ssam						 eloop.signals[i].user_data);
632189251Ssam		}
633189251Ssam	}
634189251Ssam}
635189251Ssam
636189251Ssam
637189251Ssamint eloop_register_signal(int sig, eloop_signal_handler handler,
638189251Ssam			  void *user_data)
639189251Ssam{
640189251Ssam	struct eloop_signal *tmp;
641189251Ssam
642252726Srpaulo	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
643252726Srpaulo			       sizeof(struct eloop_signal));
644189251Ssam	if (tmp == NULL)
645189251Ssam		return -1;
646189251Ssam
647189251Ssam	tmp[eloop.signal_count].sig = sig;
648189251Ssam	tmp[eloop.signal_count].user_data = user_data;
649189251Ssam	tmp[eloop.signal_count].handler = handler;
650189251Ssam	tmp[eloop.signal_count].signaled = 0;
651189251Ssam	eloop.signal_count++;
652189251Ssam	eloop.signals = tmp;
653189251Ssam	signal(sig, eloop_handle_signal);
654189251Ssam
655189251Ssam	return 0;
656189251Ssam}
657189251Ssam
658189251Ssam
659189251Ssamint eloop_register_signal_terminate(eloop_signal_handler handler,
660189251Ssam				    void *user_data)
661189251Ssam{
662189251Ssam	int ret = eloop_register_signal(SIGINT, handler, user_data);
663189251Ssam	if (ret == 0)
664189251Ssam		ret = eloop_register_signal(SIGTERM, handler, user_data);
665189251Ssam	return ret;
666189251Ssam}
667189251Ssam
668189251Ssam
669189251Ssamint eloop_register_signal_reconfig(eloop_signal_handler handler,
670189251Ssam				   void *user_data)
671189251Ssam{
672189251Ssam#ifdef CONFIG_NATIVE_WINDOWS
673189251Ssam	return 0;
674189251Ssam#else /* CONFIG_NATIVE_WINDOWS */
675189251Ssam	return eloop_register_signal(SIGHUP, handler, user_data);
676189251Ssam#endif /* CONFIG_NATIVE_WINDOWS */
677189251Ssam}
678189251Ssam
679189251Ssam
680189251Ssamvoid eloop_run(void)
681189251Ssam{
682252726Srpaulo#ifdef CONFIG_ELOOP_POLL
683252726Srpaulo	int num_poll_fds;
684252726Srpaulo	int timeout_ms = 0;
685252726Srpaulo#else /* CONFIG_ELOOP_POLL */
686189251Ssam	fd_set *rfds, *wfds, *efds;
687252726Srpaulo	struct timeval _tv;
688252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
689189251Ssam	int res;
690189251Ssam	struct os_time tv, now;
691189251Ssam
692252726Srpaulo#ifndef CONFIG_ELOOP_POLL
693189251Ssam	rfds = os_malloc(sizeof(*rfds));
694189251Ssam	wfds = os_malloc(sizeof(*wfds));
695189251Ssam	efds = os_malloc(sizeof(*efds));
696214734Srpaulo	if (rfds == NULL || wfds == NULL || efds == NULL)
697189251Ssam		goto out;
698252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
699189251Ssam
700189251Ssam	while (!eloop.terminate &&
701214734Srpaulo	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
702189251Ssam		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
703214734Srpaulo		struct eloop_timeout *timeout;
704214734Srpaulo		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
705214734Srpaulo					list);
706214734Srpaulo		if (timeout) {
707189251Ssam			os_get_time(&now);
708214734Srpaulo			if (os_time_before(&now, &timeout->time))
709214734Srpaulo				os_time_sub(&timeout->time, &now, &tv);
710189251Ssam			else
711189251Ssam				tv.sec = tv.usec = 0;
712252726Srpaulo#ifdef CONFIG_ELOOP_POLL
713252726Srpaulo			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
714252726Srpaulo#else /* CONFIG_ELOOP_POLL */
715189251Ssam			_tv.tv_sec = tv.sec;
716189251Ssam			_tv.tv_usec = tv.usec;
717252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
718189251Ssam		}
719189251Ssam
720252726Srpaulo#ifdef CONFIG_ELOOP_POLL
721252726Srpaulo		num_poll_fds = eloop_sock_table_set_fds(
722252726Srpaulo			&eloop.readers, &eloop.writers, &eloop.exceptions,
723252726Srpaulo			eloop.pollfds, eloop.pollfds_map,
724252726Srpaulo			eloop.max_pollfd_map);
725252726Srpaulo		res = poll(eloop.pollfds, num_poll_fds,
726252726Srpaulo			   timeout ? timeout_ms : -1);
727252726Srpaulo
728252726Srpaulo		if (res < 0 && errno != EINTR && errno != 0) {
729252726Srpaulo			perror("poll");
730252726Srpaulo			goto out;
731252726Srpaulo		}
732252726Srpaulo#else /* CONFIG_ELOOP_POLL */
733189251Ssam		eloop_sock_table_set_fds(&eloop.readers, rfds);
734189251Ssam		eloop_sock_table_set_fds(&eloop.writers, wfds);
735189251Ssam		eloop_sock_table_set_fds(&eloop.exceptions, efds);
736189251Ssam		res = select(eloop.max_sock + 1, rfds, wfds, efds,
737214734Srpaulo			     timeout ? &_tv : NULL);
738189251Ssam		if (res < 0 && errno != EINTR && errno != 0) {
739189251Ssam			perror("select");
740189251Ssam			goto out;
741189251Ssam		}
742252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
743189251Ssam		eloop_process_pending_signals();
744189251Ssam
745189251Ssam		/* check if some registered timeouts have occurred */
746214734Srpaulo		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
747214734Srpaulo					list);
748214734Srpaulo		if (timeout) {
749189251Ssam			os_get_time(&now);
750214734Srpaulo			if (!os_time_before(&now, &timeout->time)) {
751214734Srpaulo				void *eloop_data = timeout->eloop_data;
752214734Srpaulo				void *user_data = timeout->user_data;
753214734Srpaulo				eloop_timeout_handler handler =
754214734Srpaulo					timeout->handler;
755214734Srpaulo				eloop_remove_timeout(timeout);
756214734Srpaulo				handler(eloop_data, user_data);
757189251Ssam			}
758189251Ssam
759189251Ssam		}
760189251Ssam
761189251Ssam		if (res <= 0)
762189251Ssam			continue;
763189251Ssam
764252726Srpaulo#ifdef CONFIG_ELOOP_POLL
765252726Srpaulo		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
766252726Srpaulo					  &eloop.exceptions, eloop.pollfds_map,
767252726Srpaulo					  eloop.max_pollfd_map);
768252726Srpaulo#else /* CONFIG_ELOOP_POLL */
769189251Ssam		eloop_sock_table_dispatch(&eloop.readers, rfds);
770189251Ssam		eloop_sock_table_dispatch(&eloop.writers, wfds);
771189251Ssam		eloop_sock_table_dispatch(&eloop.exceptions, efds);
772252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
773189251Ssam	}
774189251Ssam
775189251Ssamout:
776252726Srpaulo#ifndef CONFIG_ELOOP_POLL
777189251Ssam	os_free(rfds);
778189251Ssam	os_free(wfds);
779189251Ssam	os_free(efds);
780252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
781252726Srpaulo	return;
782189251Ssam}
783189251Ssam
784189251Ssam
785189251Ssamvoid eloop_terminate(void)
786189251Ssam{
787189251Ssam	eloop.terminate = 1;
788189251Ssam}
789189251Ssam
790189251Ssam
791189251Ssamvoid eloop_destroy(void)
792189251Ssam{
793189251Ssam	struct eloop_timeout *timeout, *prev;
794189251Ssam	struct os_time now;
795189251Ssam
796214734Srpaulo	os_get_time(&now);
797214734Srpaulo	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
798214734Srpaulo			      struct eloop_timeout, list) {
799189251Ssam		int sec, usec;
800214734Srpaulo		sec = timeout->time.sec - now.sec;
801214734Srpaulo		usec = timeout->time.usec - now.usec;
802214734Srpaulo		if (timeout->time.usec < now.usec) {
803189251Ssam			sec--;
804189251Ssam			usec += 1000000;
805189251Ssam		}
806214734Srpaulo		wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
807214734Srpaulo			   "eloop_data=%p user_data=%p handler=%p",
808214734Srpaulo			   sec, usec, timeout->eloop_data, timeout->user_data,
809214734Srpaulo			   timeout->handler);
810214734Srpaulo		wpa_trace_dump_funcname("eloop unregistered timeout handler",
811214734Srpaulo					timeout->handler);
812214734Srpaulo		wpa_trace_dump("eloop timeout", timeout);
813214734Srpaulo		eloop_remove_timeout(timeout);
814189251Ssam	}
815189251Ssam	eloop_sock_table_destroy(&eloop.readers);
816189251Ssam	eloop_sock_table_destroy(&eloop.writers);
817189251Ssam	eloop_sock_table_destroy(&eloop.exceptions);
818189251Ssam	os_free(eloop.signals);
819252726Srpaulo
820252726Srpaulo#ifdef CONFIG_ELOOP_POLL
821252726Srpaulo	os_free(eloop.pollfds);
822252726Srpaulo	os_free(eloop.pollfds_map);
823252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
824189251Ssam}
825189251Ssam
826189251Ssam
827189251Ssamint eloop_terminated(void)
828189251Ssam{
829189251Ssam	return eloop.terminate;
830189251Ssam}
831189251Ssam
832189251Ssam
833189251Ssamvoid eloop_wait_for_read_sock(int sock)
834189251Ssam{
835252726Srpaulo#ifdef CONFIG_ELOOP_POLL
836252726Srpaulo	struct pollfd pfd;
837252726Srpaulo
838252726Srpaulo	if (sock < 0)
839252726Srpaulo		return;
840252726Srpaulo
841252726Srpaulo	os_memset(&pfd, 0, sizeof(pfd));
842252726Srpaulo	pfd.fd = sock;
843252726Srpaulo	pfd.events = POLLIN;
844252726Srpaulo
845252726Srpaulo	poll(&pfd, 1, -1);
846252726Srpaulo#else /* CONFIG_ELOOP_POLL */
847189251Ssam	fd_set rfds;
848189251Ssam
849189251Ssam	if (sock < 0)
850189251Ssam		return;
851189251Ssam
852189251Ssam	FD_ZERO(&rfds);
853189251Ssam	FD_SET(sock, &rfds);
854189251Ssam	select(sock + 1, &rfds, NULL, NULL, NULL);
855252726Srpaulo#endif /* CONFIG_ELOOP_POLL */
856189251Ssam}
857