xref: /freebsd/contrib/wpa/src/utils/eloop.c (revision 4b72b91a7132df1f77bbae194e1071ac621f1edb)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * Event loop based on select() loop
3e28a4053SRui Paulo  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
105b9c547cSRui Paulo #include <assert.h>
1139beb93cSSam Leffler 
1239beb93cSSam Leffler #include "common.h"
13e28a4053SRui Paulo #include "trace.h"
14e28a4053SRui Paulo #include "list.h"
1539beb93cSSam Leffler #include "eloop.h"
1639beb93cSSam Leffler 
175b9c547cSRui Paulo #if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
185b9c547cSRui Paulo #error Do not define both of poll and epoll
195b9c547cSRui Paulo #endif
205b9c547cSRui Paulo 
21780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE)
22780fb4a2SCy Schubert #error Do not define both of poll and kqueue
23780fb4a2SCy Schubert #endif
24780fb4a2SCy Schubert 
25780fb4a2SCy Schubert #if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \
26780fb4a2SCy Schubert     !defined(CONFIG_ELOOP_KQUEUE)
275b9c547cSRui Paulo #define CONFIG_ELOOP_SELECT
285b9c547cSRui Paulo #endif
295b9c547cSRui Paulo 
30f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
31f05cddf9SRui Paulo #include <poll.h>
32f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
33f05cddf9SRui Paulo 
345b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
355b9c547cSRui Paulo #include <sys/epoll.h>
365b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
3739beb93cSSam Leffler 
38780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
39780fb4a2SCy Schubert #include <sys/event.h>
40780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
41780fb4a2SCy Schubert 
4239beb93cSSam Leffler struct eloop_sock {
4339beb93cSSam Leffler 	int sock;
4439beb93cSSam Leffler 	void *eloop_data;
4539beb93cSSam Leffler 	void *user_data;
4639beb93cSSam Leffler 	eloop_sock_handler handler;
47e28a4053SRui Paulo 	WPA_TRACE_REF(eloop);
48e28a4053SRui Paulo 	WPA_TRACE_REF(user);
49e28a4053SRui Paulo 	WPA_TRACE_INFO
5039beb93cSSam Leffler };
5139beb93cSSam Leffler 
5239beb93cSSam Leffler struct eloop_timeout {
53e28a4053SRui Paulo 	struct dl_list list;
545b9c547cSRui Paulo 	struct os_reltime time;
5539beb93cSSam Leffler 	void *eloop_data;
5639beb93cSSam Leffler 	void *user_data;
5739beb93cSSam Leffler 	eloop_timeout_handler handler;
58e28a4053SRui Paulo 	WPA_TRACE_REF(eloop);
59e28a4053SRui Paulo 	WPA_TRACE_REF(user);
60e28a4053SRui Paulo 	WPA_TRACE_INFO
6139beb93cSSam Leffler };
6239beb93cSSam Leffler 
6339beb93cSSam Leffler struct eloop_signal {
6439beb93cSSam Leffler 	int sig;
6539beb93cSSam Leffler 	void *user_data;
6639beb93cSSam Leffler 	eloop_signal_handler handler;
6739beb93cSSam Leffler 	int signaled;
6839beb93cSSam Leffler };
6939beb93cSSam Leffler 
7039beb93cSSam Leffler struct eloop_sock_table {
71c1d255d3SCy Schubert 	size_t count;
7239beb93cSSam Leffler 	struct eloop_sock *table;
735b9c547cSRui Paulo 	eloop_event_type type;
7439beb93cSSam Leffler 	int changed;
7539beb93cSSam Leffler };
7639beb93cSSam Leffler 
7739beb93cSSam Leffler struct eloop_data {
7839beb93cSSam Leffler 	int max_sock;
7939beb93cSSam Leffler 
80c1d255d3SCy Schubert 	size_t count; /* sum of all table counts */
81f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
82c1d255d3SCy Schubert 	size_t max_pollfd_map; /* number of pollfds_map currently allocated */
83c1d255d3SCy Schubert 	size_t max_poll_fds; /* number of pollfds currently allocated */
84f05cddf9SRui Paulo 	struct pollfd *pollfds;
85f05cddf9SRui Paulo 	struct pollfd **pollfds_map;
86f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
87780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
88780fb4a2SCy Schubert 	int max_fd;
89780fb4a2SCy Schubert 	struct eloop_sock *fd_table;
90780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
915b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
925b9c547cSRui Paulo 	int epollfd;
93c1d255d3SCy Schubert 	size_t epoll_max_event_num;
945b9c547cSRui Paulo 	struct epoll_event *epoll_events;
955b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
96780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
97780fb4a2SCy Schubert 	int kqueuefd;
98c1d255d3SCy Schubert 	size_t kqueue_nevents;
99780fb4a2SCy Schubert 	struct kevent *kqueue_events;
100780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
10139beb93cSSam Leffler 	struct eloop_sock_table readers;
10239beb93cSSam Leffler 	struct eloop_sock_table writers;
10339beb93cSSam Leffler 	struct eloop_sock_table exceptions;
10439beb93cSSam Leffler 
105e28a4053SRui Paulo 	struct dl_list timeout;
10639beb93cSSam Leffler 
107c1d255d3SCy Schubert 	size_t signal_count;
10839beb93cSSam Leffler 	struct eloop_signal *signals;
10939beb93cSSam Leffler 	int signaled;
11039beb93cSSam Leffler 	int pending_terminate;
11139beb93cSSam Leffler 
11239beb93cSSam Leffler 	int terminate;
11339beb93cSSam Leffler };
11439beb93cSSam Leffler 
11539beb93cSSam Leffler static struct eloop_data eloop;
11639beb93cSSam Leffler 
11739beb93cSSam Leffler 
118e28a4053SRui Paulo #ifdef WPA_TRACE
119e28a4053SRui Paulo 
eloop_sigsegv_handler(int sig)120e28a4053SRui Paulo static void eloop_sigsegv_handler(int sig)
121e28a4053SRui Paulo {
122e28a4053SRui Paulo 	wpa_trace_show("eloop SIGSEGV");
123e28a4053SRui Paulo 	abort();
124e28a4053SRui Paulo }
125e28a4053SRui Paulo 
eloop_trace_sock_add_ref(struct eloop_sock_table * table)126e28a4053SRui Paulo static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
127e28a4053SRui Paulo {
128c1d255d3SCy Schubert 	size_t i;
129c1d255d3SCy Schubert 
130e28a4053SRui Paulo 	if (table == NULL || table->table == NULL)
131e28a4053SRui Paulo 		return;
132e28a4053SRui Paulo 	for (i = 0; i < table->count; i++) {
133e28a4053SRui Paulo 		wpa_trace_add_ref(&table->table[i], eloop,
134e28a4053SRui Paulo 				  table->table[i].eloop_data);
135e28a4053SRui Paulo 		wpa_trace_add_ref(&table->table[i], user,
136e28a4053SRui Paulo 				  table->table[i].user_data);
137e28a4053SRui Paulo 	}
138e28a4053SRui Paulo }
139e28a4053SRui Paulo 
140e28a4053SRui Paulo 
eloop_trace_sock_remove_ref(struct eloop_sock_table * table)141e28a4053SRui Paulo static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
142e28a4053SRui Paulo {
143c1d255d3SCy Schubert 	size_t i;
144c1d255d3SCy Schubert 
145e28a4053SRui Paulo 	if (table == NULL || table->table == NULL)
146e28a4053SRui Paulo 		return;
147e28a4053SRui Paulo 	for (i = 0; i < table->count; i++) {
148e28a4053SRui Paulo 		wpa_trace_remove_ref(&table->table[i], eloop,
149e28a4053SRui Paulo 				     table->table[i].eloop_data);
150e28a4053SRui Paulo 		wpa_trace_remove_ref(&table->table[i], user,
151e28a4053SRui Paulo 				     table->table[i].user_data);
152e28a4053SRui Paulo 	}
153e28a4053SRui Paulo }
154e28a4053SRui Paulo 
155e28a4053SRui Paulo #else /* WPA_TRACE */
156e28a4053SRui Paulo 
157e28a4053SRui Paulo #define eloop_trace_sock_add_ref(table) do { } while (0)
158e28a4053SRui Paulo #define eloop_trace_sock_remove_ref(table) do { } while (0)
159e28a4053SRui Paulo 
160e28a4053SRui Paulo #endif /* WPA_TRACE */
161e28a4053SRui Paulo 
162e28a4053SRui Paulo 
eloop_init(void)163e28a4053SRui Paulo int eloop_init(void)
16439beb93cSSam Leffler {
16539beb93cSSam Leffler 	os_memset(&eloop, 0, sizeof(eloop));
166e28a4053SRui Paulo 	dl_list_init(&eloop.timeout);
1675b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
1685b9c547cSRui Paulo 	eloop.epollfd = epoll_create1(0);
1695b9c547cSRui Paulo 	if (eloop.epollfd < 0) {
170780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s",
1715b9c547cSRui Paulo 			   __func__, strerror(errno));
1725b9c547cSRui Paulo 		return -1;
1735b9c547cSRui Paulo 	}
174780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL */
175780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
176780fb4a2SCy Schubert 	eloop.kqueuefd = kqueue();
177780fb4a2SCy Schubert 	if (eloop.kqueuefd < 0) {
178780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
179780fb4a2SCy Schubert 			   __func__, strerror(errno));
180780fb4a2SCy Schubert 		return -1;
181780fb4a2SCy Schubert 	}
182780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
183780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
1845b9c547cSRui Paulo 	eloop.readers.type = EVENT_TYPE_READ;
1855b9c547cSRui Paulo 	eloop.writers.type = EVENT_TYPE_WRITE;
1865b9c547cSRui Paulo 	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
187780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
188e28a4053SRui Paulo #ifdef WPA_TRACE
189e28a4053SRui Paulo 	signal(SIGSEGV, eloop_sigsegv_handler);
190e28a4053SRui Paulo #endif /* WPA_TRACE */
19139beb93cSSam Leffler 	return 0;
19239beb93cSSam Leffler }
19339beb93cSSam Leffler 
19439beb93cSSam Leffler 
195780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_EPOLL
eloop_sock_queue(int sock,eloop_event_type type)196780fb4a2SCy Schubert static int eloop_sock_queue(int sock, eloop_event_type type)
197780fb4a2SCy Schubert {
198780fb4a2SCy Schubert 	struct epoll_event ev;
199780fb4a2SCy Schubert 
200780fb4a2SCy Schubert 	os_memset(&ev, 0, sizeof(ev));
201780fb4a2SCy Schubert 	switch (type) {
202780fb4a2SCy Schubert 	case EVENT_TYPE_READ:
203780fb4a2SCy Schubert 		ev.events = EPOLLIN;
204780fb4a2SCy Schubert 		break;
205780fb4a2SCy Schubert 	case EVENT_TYPE_WRITE:
206780fb4a2SCy Schubert 		ev.events = EPOLLOUT;
207780fb4a2SCy Schubert 		break;
208780fb4a2SCy Schubert 	/*
209780fb4a2SCy Schubert 	 * Exceptions are always checked when using epoll, but I suppose it's
210780fb4a2SCy Schubert 	 * possible that someone registered a socket *only* for exception
211780fb4a2SCy Schubert 	 * handling.
212780fb4a2SCy Schubert 	 */
213780fb4a2SCy Schubert 	case EVENT_TYPE_EXCEPTION:
214780fb4a2SCy Schubert 		ev.events = EPOLLERR | EPOLLHUP;
215780fb4a2SCy Schubert 		break;
216780fb4a2SCy Schubert 	}
217780fb4a2SCy Schubert 	ev.data.fd = sock;
218780fb4a2SCy Schubert 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
219780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s",
220780fb4a2SCy Schubert 			   __func__, sock, strerror(errno));
221780fb4a2SCy Schubert 		return -1;
222780fb4a2SCy Schubert 	}
223780fb4a2SCy Schubert 	return 0;
224780fb4a2SCy Schubert }
225780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL */
226780fb4a2SCy Schubert 
227780fb4a2SCy Schubert 
228780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
229780fb4a2SCy Schubert 
event_type_kevent_filter(eloop_event_type type)2304bc52338SCy Schubert static short event_type_kevent_filter(eloop_event_type type)
2314bc52338SCy Schubert {
232780fb4a2SCy Schubert 	switch (type) {
233780fb4a2SCy Schubert 	case EVENT_TYPE_READ:
2344bc52338SCy Schubert 		return EVFILT_READ;
235780fb4a2SCy Schubert 	case EVENT_TYPE_WRITE:
2364bc52338SCy Schubert 		return EVFILT_WRITE;
237780fb4a2SCy Schubert 	default:
2384bc52338SCy Schubert 		return 0;
239780fb4a2SCy Schubert 	}
2404bc52338SCy Schubert }
2414bc52338SCy Schubert 
2424bc52338SCy Schubert 
eloop_sock_queue(int sock,eloop_event_type type)2434bc52338SCy Schubert static int eloop_sock_queue(int sock, eloop_event_type type)
2444bc52338SCy Schubert {
2454bc52338SCy Schubert 	struct kevent ke;
2464bc52338SCy Schubert 
2474bc52338SCy Schubert 	EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0);
248780fb4a2SCy Schubert 	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
249780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
250780fb4a2SCy Schubert 			   __func__, sock, strerror(errno));
251780fb4a2SCy Schubert 		return -1;
252780fb4a2SCy Schubert 	}
253780fb4a2SCy Schubert 	return 0;
254780fb4a2SCy Schubert }
2554bc52338SCy Schubert 
256780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
257780fb4a2SCy Schubert 
258780fb4a2SCy Schubert 
eloop_sock_table_add_sock(struct eloop_sock_table * table,int sock,eloop_sock_handler handler,void * eloop_data,void * user_data)25939beb93cSSam Leffler static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
26039beb93cSSam Leffler                                      int sock, eloop_sock_handler handler,
26139beb93cSSam Leffler                                      void *eloop_data, void *user_data)
26239beb93cSSam Leffler {
2635b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
264780fb4a2SCy Schubert 	struct epoll_event *temp_events;
2655b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
266780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
267780fb4a2SCy Schubert 	struct kevent *temp_events;
268780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL */
269780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
270780fb4a2SCy Schubert 	struct eloop_sock *temp_table;
271c1d255d3SCy Schubert 	size_t next;
272780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
27339beb93cSSam Leffler 	struct eloop_sock *tmp;
274f05cddf9SRui Paulo 	int new_max_sock;
275f05cddf9SRui Paulo 
276f05cddf9SRui Paulo 	if (sock > eloop.max_sock)
277f05cddf9SRui Paulo 		new_max_sock = sock;
278f05cddf9SRui Paulo 	else
279f05cddf9SRui Paulo 		new_max_sock = eloop.max_sock;
28039beb93cSSam Leffler 
28139beb93cSSam Leffler 	if (table == NULL)
28239beb93cSSam Leffler 		return -1;
28339beb93cSSam Leffler 
284f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
285c1d255d3SCy Schubert 	if ((size_t) new_max_sock >= eloop.max_pollfd_map) {
286f05cddf9SRui Paulo 		struct pollfd **nmap;
287f05cddf9SRui Paulo 		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
288f05cddf9SRui Paulo 					sizeof(struct pollfd *));
289f05cddf9SRui Paulo 		if (nmap == NULL)
290f05cddf9SRui Paulo 			return -1;
291f05cddf9SRui Paulo 
292f05cddf9SRui Paulo 		eloop.max_pollfd_map = new_max_sock + 50;
293f05cddf9SRui Paulo 		eloop.pollfds_map = nmap;
294f05cddf9SRui Paulo 	}
295f05cddf9SRui Paulo 
296f05cddf9SRui Paulo 	if (eloop.count + 1 > eloop.max_poll_fds) {
297f05cddf9SRui Paulo 		struct pollfd *n;
298c1d255d3SCy Schubert 		size_t nmax = eloop.count + 1 + 50;
299c1d255d3SCy Schubert 
300f05cddf9SRui Paulo 		n = os_realloc_array(eloop.pollfds, nmax,
301f05cddf9SRui Paulo 				     sizeof(struct pollfd));
302f05cddf9SRui Paulo 		if (n == NULL)
303f05cddf9SRui Paulo 			return -1;
304f05cddf9SRui Paulo 
305f05cddf9SRui Paulo 		eloop.max_poll_fds = nmax;
306f05cddf9SRui Paulo 		eloop.pollfds = n;
307f05cddf9SRui Paulo 	}
308f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
309780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
310780fb4a2SCy Schubert 	if (new_max_sock >= eloop.max_fd) {
3114bc52338SCy Schubert 		next = new_max_sock + 16;
312780fb4a2SCy Schubert 		temp_table = os_realloc_array(eloop.fd_table, next,
3135b9c547cSRui Paulo 					      sizeof(struct eloop_sock));
3145b9c547cSRui Paulo 		if (temp_table == NULL)
3155b9c547cSRui Paulo 			return -1;
3165b9c547cSRui Paulo 
317780fb4a2SCy Schubert 		eloop.max_fd = next;
318780fb4a2SCy Schubert 		eloop.fd_table = temp_table;
3195b9c547cSRui Paulo 	}
320780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
3215b9c547cSRui Paulo 
322780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_EPOLL
3235b9c547cSRui Paulo 	if (eloop.count + 1 > eloop.epoll_max_event_num) {
3245b9c547cSRui Paulo 		next = eloop.epoll_max_event_num == 0 ? 8 :
3255b9c547cSRui Paulo 			eloop.epoll_max_event_num * 2;
3265b9c547cSRui Paulo 		temp_events = os_realloc_array(eloop.epoll_events, next,
3275b9c547cSRui Paulo 					       sizeof(struct epoll_event));
3285b9c547cSRui Paulo 		if (temp_events == NULL) {
329780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s",
330780fb4a2SCy Schubert 				   __func__, strerror(errno));
3315b9c547cSRui Paulo 			return -1;
3325b9c547cSRui Paulo 		}
3335b9c547cSRui Paulo 
3345b9c547cSRui Paulo 		eloop.epoll_max_event_num = next;
3355b9c547cSRui Paulo 		eloop.epoll_events = temp_events;
3365b9c547cSRui Paulo 	}
3375b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
338780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
339780fb4a2SCy Schubert 	if (eloop.count + 1 > eloop.kqueue_nevents) {
340780fb4a2SCy Schubert 		next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2;
341780fb4a2SCy Schubert 		temp_events = os_malloc(next * sizeof(*temp_events));
342780fb4a2SCy Schubert 		if (!temp_events) {
343780fb4a2SCy Schubert 			wpa_printf(MSG_ERROR,
344780fb4a2SCy Schubert 				   "%s: malloc for kqueue failed: %s",
345780fb4a2SCy Schubert 				   __func__, strerror(errno));
346780fb4a2SCy Schubert 			return -1;
347780fb4a2SCy Schubert 		}
348780fb4a2SCy Schubert 
349780fb4a2SCy Schubert 		os_free(eloop.kqueue_events);
350780fb4a2SCy Schubert 		eloop.kqueue_events = temp_events;
351780fb4a2SCy Schubert 		eloop.kqueue_nevents = next;
352780fb4a2SCy Schubert 	}
353780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
354f05cddf9SRui Paulo 
355e28a4053SRui Paulo 	eloop_trace_sock_remove_ref(table);
356f05cddf9SRui Paulo 	tmp = os_realloc_array(table->table, table->count + 1,
357f05cddf9SRui Paulo 			       sizeof(struct eloop_sock));
3585b9c547cSRui Paulo 	if (tmp == NULL) {
3595b9c547cSRui Paulo 		eloop_trace_sock_add_ref(table);
36039beb93cSSam Leffler 		return -1;
3615b9c547cSRui Paulo 	}
36239beb93cSSam Leffler 
36339beb93cSSam Leffler 	tmp[table->count].sock = sock;
36439beb93cSSam Leffler 	tmp[table->count].eloop_data = eloop_data;
36539beb93cSSam Leffler 	tmp[table->count].user_data = user_data;
36639beb93cSSam Leffler 	tmp[table->count].handler = handler;
367e28a4053SRui Paulo 	wpa_trace_record(&tmp[table->count]);
36839beb93cSSam Leffler 	table->count++;
36939beb93cSSam Leffler 	table->table = tmp;
370f05cddf9SRui Paulo 	eloop.max_sock = new_max_sock;
371f05cddf9SRui Paulo 	eloop.count++;
37239beb93cSSam Leffler 	table->changed = 1;
373e28a4053SRui Paulo 	eloop_trace_sock_add_ref(table);
37439beb93cSSam Leffler 
375780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
376780fb4a2SCy Schubert 	if (eloop_sock_queue(sock, table->type) < 0)
3775b9c547cSRui Paulo 		return -1;
378780fb4a2SCy Schubert 	os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1],
3795b9c547cSRui Paulo 		  sizeof(struct eloop_sock));
380780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
38139beb93cSSam Leffler 	return 0;
38239beb93cSSam Leffler }
38339beb93cSSam Leffler 
38439beb93cSSam Leffler 
eloop_sock_table_remove_sock(struct eloop_sock_table * table,int sock)38539beb93cSSam Leffler static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
38639beb93cSSam Leffler                                          int sock)
38739beb93cSSam Leffler {
388780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
389780fb4a2SCy Schubert 	struct kevent ke;
390780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
391c1d255d3SCy Schubert 	size_t i;
39239beb93cSSam Leffler 
39339beb93cSSam Leffler 	if (table == NULL || table->table == NULL || table->count == 0)
39439beb93cSSam Leffler 		return;
39539beb93cSSam Leffler 
39639beb93cSSam Leffler 	for (i = 0; i < table->count; i++) {
39739beb93cSSam Leffler 		if (table->table[i].sock == sock)
39839beb93cSSam Leffler 			break;
39939beb93cSSam Leffler 	}
40039beb93cSSam Leffler 	if (i == table->count)
40139beb93cSSam Leffler 		return;
402e28a4053SRui Paulo 	eloop_trace_sock_remove_ref(table);
40339beb93cSSam Leffler 	if (i != table->count - 1) {
40439beb93cSSam Leffler 		os_memmove(&table->table[i], &table->table[i + 1],
40539beb93cSSam Leffler 			   (table->count - i - 1) *
40639beb93cSSam Leffler 			   sizeof(struct eloop_sock));
40739beb93cSSam Leffler 	}
40839beb93cSSam Leffler 	table->count--;
409f05cddf9SRui Paulo 	eloop.count--;
41039beb93cSSam Leffler 	table->changed = 1;
411e28a4053SRui Paulo 	eloop_trace_sock_add_ref(table);
4125b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
4135b9c547cSRui Paulo 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
414780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s",
415780fb4a2SCy Schubert 			   __func__, sock, strerror(errno));
4165b9c547cSRui Paulo 		return;
4175b9c547cSRui Paulo 	}
418780fb4a2SCy Schubert 	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
4195b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
420780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
4214bc52338SCy Schubert 	EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0,
4224bc52338SCy Schubert 	       0, 0);
423780fb4a2SCy Schubert 	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
424780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
425780fb4a2SCy Schubert 			   __func__, sock, strerror(errno));
426780fb4a2SCy Schubert 		return;
427780fb4a2SCy Schubert 	}
428780fb4a2SCy Schubert 	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
429780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
43039beb93cSSam Leffler }
43139beb93cSSam Leffler 
43239beb93cSSam Leffler 
433f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
434f05cddf9SRui Paulo 
find_pollfd(struct pollfd ** pollfds_map,int fd,int mx)435f05cddf9SRui Paulo static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
436f05cddf9SRui Paulo {
437f05cddf9SRui Paulo 	if (fd < mx && fd >= 0)
438f05cddf9SRui Paulo 		return pollfds_map[fd];
439f05cddf9SRui Paulo 	return NULL;
440f05cddf9SRui Paulo }
441f05cddf9SRui Paulo 
442f05cddf9SRui Paulo 
eloop_sock_table_set_fds(struct eloop_sock_table * readers,struct eloop_sock_table * writers,struct eloop_sock_table * exceptions,struct pollfd * pollfds,struct pollfd ** pollfds_map,int max_pollfd_map)443f05cddf9SRui Paulo static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
444f05cddf9SRui Paulo 				    struct eloop_sock_table *writers,
445f05cddf9SRui Paulo 				    struct eloop_sock_table *exceptions,
446f05cddf9SRui Paulo 				    struct pollfd *pollfds,
447f05cddf9SRui Paulo 				    struct pollfd **pollfds_map,
448f05cddf9SRui Paulo 				    int max_pollfd_map)
449f05cddf9SRui Paulo {
450c1d255d3SCy Schubert 	size_t i;
451f05cddf9SRui Paulo 	int nxt = 0;
452f05cddf9SRui Paulo 	int fd;
453f05cddf9SRui Paulo 	struct pollfd *pfd;
454f05cddf9SRui Paulo 
455f05cddf9SRui Paulo 	/* Clear pollfd lookup map. It will be re-populated below. */
456f05cddf9SRui Paulo 	os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
457f05cddf9SRui Paulo 
458f05cddf9SRui Paulo 	if (readers && readers->table) {
459f05cddf9SRui Paulo 		for (i = 0; i < readers->count; i++) {
460f05cddf9SRui Paulo 			fd = readers->table[i].sock;
461f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
462f05cddf9SRui Paulo 			pollfds[nxt].fd = fd;
463f05cddf9SRui Paulo 			pollfds[nxt].events = POLLIN;
464f05cddf9SRui Paulo 			pollfds[nxt].revents = 0;
465f05cddf9SRui Paulo 			pollfds_map[fd] = &(pollfds[nxt]);
466f05cddf9SRui Paulo 			nxt++;
467f05cddf9SRui Paulo 		}
468f05cddf9SRui Paulo 	}
469f05cddf9SRui Paulo 
470f05cddf9SRui Paulo 	if (writers && writers->table) {
471f05cddf9SRui Paulo 		for (i = 0; i < writers->count; i++) {
472f05cddf9SRui Paulo 			/*
473f05cddf9SRui Paulo 			 * See if we already added this descriptor, update it
474f05cddf9SRui Paulo 			 * if so.
475f05cddf9SRui Paulo 			 */
476f05cddf9SRui Paulo 			fd = writers->table[i].sock;
477f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
478f05cddf9SRui Paulo 			pfd = pollfds_map[fd];
479f05cddf9SRui Paulo 			if (!pfd) {
480f05cddf9SRui Paulo 				pfd = &(pollfds[nxt]);
481f05cddf9SRui Paulo 				pfd->events = 0;
482f05cddf9SRui Paulo 				pfd->fd = fd;
483f05cddf9SRui Paulo 				pollfds[i].revents = 0;
484f05cddf9SRui Paulo 				pollfds_map[fd] = pfd;
485f05cddf9SRui Paulo 				nxt++;
486f05cddf9SRui Paulo 			}
487f05cddf9SRui Paulo 			pfd->events |= POLLOUT;
488f05cddf9SRui Paulo 		}
489f05cddf9SRui Paulo 	}
490f05cddf9SRui Paulo 
491f05cddf9SRui Paulo 	/*
492f05cddf9SRui Paulo 	 * Exceptions are always checked when using poll, but I suppose it's
493f05cddf9SRui Paulo 	 * possible that someone registered a socket *only* for exception
494f05cddf9SRui Paulo 	 * handling. Set the POLLIN bit in this case.
495f05cddf9SRui Paulo 	 */
496f05cddf9SRui Paulo 	if (exceptions && exceptions->table) {
497f05cddf9SRui Paulo 		for (i = 0; i < exceptions->count; i++) {
498f05cddf9SRui Paulo 			/*
499f05cddf9SRui Paulo 			 * See if we already added this descriptor, just use it
500f05cddf9SRui Paulo 			 * if so.
501f05cddf9SRui Paulo 			 */
502f05cddf9SRui Paulo 			fd = exceptions->table[i].sock;
503f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
504f05cddf9SRui Paulo 			pfd = pollfds_map[fd];
505f05cddf9SRui Paulo 			if (!pfd) {
506f05cddf9SRui Paulo 				pfd = &(pollfds[nxt]);
507f05cddf9SRui Paulo 				pfd->events = POLLIN;
508f05cddf9SRui Paulo 				pfd->fd = fd;
509f05cddf9SRui Paulo 				pollfds[i].revents = 0;
510f05cddf9SRui Paulo 				pollfds_map[fd] = pfd;
511f05cddf9SRui Paulo 				nxt++;
512f05cddf9SRui Paulo 			}
513f05cddf9SRui Paulo 		}
514f05cddf9SRui Paulo 	}
515f05cddf9SRui Paulo 
516f05cddf9SRui Paulo 	return nxt;
517f05cddf9SRui Paulo }
518f05cddf9SRui Paulo 
519f05cddf9SRui Paulo 
eloop_sock_table_dispatch_table(struct eloop_sock_table * table,struct pollfd ** pollfds_map,int max_pollfd_map,short int revents)520f05cddf9SRui Paulo static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
521f05cddf9SRui Paulo 					   struct pollfd **pollfds_map,
522f05cddf9SRui Paulo 					   int max_pollfd_map,
523f05cddf9SRui Paulo 					   short int revents)
524f05cddf9SRui Paulo {
525c1d255d3SCy Schubert 	size_t i;
526f05cddf9SRui Paulo 	struct pollfd *pfd;
527f05cddf9SRui Paulo 
528f05cddf9SRui Paulo 	if (!table || !table->table)
529f05cddf9SRui Paulo 		return 0;
530f05cddf9SRui Paulo 
531f05cddf9SRui Paulo 	table->changed = 0;
532f05cddf9SRui Paulo 	for (i = 0; i < table->count; i++) {
533f05cddf9SRui Paulo 		pfd = find_pollfd(pollfds_map, table->table[i].sock,
534f05cddf9SRui Paulo 				  max_pollfd_map);
535f05cddf9SRui Paulo 		if (!pfd)
536f05cddf9SRui Paulo 			continue;
537f05cddf9SRui Paulo 
538f05cddf9SRui Paulo 		if (!(pfd->revents & revents))
539f05cddf9SRui Paulo 			continue;
540f05cddf9SRui Paulo 
541f05cddf9SRui Paulo 		table->table[i].handler(table->table[i].sock,
542f05cddf9SRui Paulo 					table->table[i].eloop_data,
543f05cddf9SRui Paulo 					table->table[i].user_data);
544f05cddf9SRui Paulo 		if (table->changed)
545f05cddf9SRui Paulo 			return 1;
546f05cddf9SRui Paulo 	}
547f05cddf9SRui Paulo 
548f05cddf9SRui Paulo 	return 0;
549f05cddf9SRui Paulo }
550f05cddf9SRui Paulo 
551f05cddf9SRui Paulo 
eloop_sock_table_dispatch(struct eloop_sock_table * readers,struct eloop_sock_table * writers,struct eloop_sock_table * exceptions,struct pollfd ** pollfds_map,int max_pollfd_map)552f05cddf9SRui Paulo static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
553f05cddf9SRui Paulo 				      struct eloop_sock_table *writers,
554f05cddf9SRui Paulo 				      struct eloop_sock_table *exceptions,
555f05cddf9SRui Paulo 				      struct pollfd **pollfds_map,
556f05cddf9SRui Paulo 				      int max_pollfd_map)
557f05cddf9SRui Paulo {
558f05cddf9SRui Paulo 	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
559f05cddf9SRui Paulo 					    max_pollfd_map, POLLIN | POLLERR |
560f05cddf9SRui Paulo 					    POLLHUP))
561f05cddf9SRui Paulo 		return; /* pollfds may be invalid at this point */
562f05cddf9SRui Paulo 
563f05cddf9SRui Paulo 	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
564f05cddf9SRui Paulo 					    max_pollfd_map, POLLOUT))
565f05cddf9SRui Paulo 		return; /* pollfds may be invalid at this point */
566f05cddf9SRui Paulo 
567f05cddf9SRui Paulo 	eloop_sock_table_dispatch_table(exceptions, pollfds_map,
568f05cddf9SRui Paulo 					max_pollfd_map, POLLERR | POLLHUP);
569f05cddf9SRui Paulo }
570f05cddf9SRui Paulo 
5715b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
5725b9c547cSRui Paulo 
5735b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
574f05cddf9SRui Paulo 
eloop_sock_table_set_fds(struct eloop_sock_table * table,fd_set * fds)57539beb93cSSam Leffler static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
57639beb93cSSam Leffler 				     fd_set *fds)
57739beb93cSSam Leffler {
578c1d255d3SCy Schubert 	size_t i;
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 	FD_ZERO(fds);
58139beb93cSSam Leffler 
58239beb93cSSam Leffler 	if (table->table == NULL)
58339beb93cSSam Leffler 		return;
58439beb93cSSam Leffler 
5855b9c547cSRui Paulo 	for (i = 0; i < table->count; i++) {
5865b9c547cSRui Paulo 		assert(table->table[i].sock >= 0);
58739beb93cSSam Leffler 		FD_SET(table->table[i].sock, fds);
58839beb93cSSam Leffler 	}
5895b9c547cSRui Paulo }
59039beb93cSSam Leffler 
59139beb93cSSam Leffler 
eloop_sock_table_dispatch(struct eloop_sock_table * table,fd_set * fds)59239beb93cSSam Leffler static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
59339beb93cSSam Leffler 				      fd_set *fds)
59439beb93cSSam Leffler {
595c1d255d3SCy Schubert 	size_t i;
59639beb93cSSam Leffler 
59739beb93cSSam Leffler 	if (table == NULL || table->table == NULL)
59839beb93cSSam Leffler 		return;
59939beb93cSSam Leffler 
60039beb93cSSam Leffler 	table->changed = 0;
60139beb93cSSam Leffler 	for (i = 0; i < table->count; i++) {
60239beb93cSSam Leffler 		if (FD_ISSET(table->table[i].sock, fds)) {
60339beb93cSSam Leffler 			table->table[i].handler(table->table[i].sock,
60439beb93cSSam Leffler 						table->table[i].eloop_data,
60539beb93cSSam Leffler 						table->table[i].user_data);
60639beb93cSSam Leffler 			if (table->changed)
60739beb93cSSam Leffler 				break;
60839beb93cSSam Leffler 		}
60939beb93cSSam Leffler 	}
61039beb93cSSam Leffler }
61139beb93cSSam Leffler 
6125b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
6135b9c547cSRui Paulo 
6145b9c547cSRui Paulo 
6155b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
eloop_sock_table_dispatch(struct epoll_event * events,int nfds)6165b9c547cSRui Paulo static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
6175b9c547cSRui Paulo {
6185b9c547cSRui Paulo 	struct eloop_sock *table;
6195b9c547cSRui Paulo 	int i;
6205b9c547cSRui Paulo 
6215b9c547cSRui Paulo 	for (i = 0; i < nfds; i++) {
622780fb4a2SCy Schubert 		table = &eloop.fd_table[events[i].data.fd];
6235b9c547cSRui Paulo 		if (table->handler == NULL)
6245b9c547cSRui Paulo 			continue;
6255b9c547cSRui Paulo 		table->handler(table->sock, table->eloop_data,
6265b9c547cSRui Paulo 			       table->user_data);
627325151a3SRui Paulo 		if (eloop.readers.changed ||
628325151a3SRui Paulo 		    eloop.writers.changed ||
629325151a3SRui Paulo 		    eloop.exceptions.changed)
630325151a3SRui Paulo 			break;
6315b9c547cSRui Paulo 	}
6325b9c547cSRui Paulo }
6335b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
634f05cddf9SRui Paulo 
63539beb93cSSam Leffler 
636780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
637780fb4a2SCy Schubert 
eloop_sock_table_dispatch(struct kevent * events,int nfds)638780fb4a2SCy Schubert static void eloop_sock_table_dispatch(struct kevent *events, int nfds)
639780fb4a2SCy Schubert {
640780fb4a2SCy Schubert 	struct eloop_sock *table;
641780fb4a2SCy Schubert 	int i;
642780fb4a2SCy Schubert 
643780fb4a2SCy Schubert 	for (i = 0; i < nfds; i++) {
644780fb4a2SCy Schubert 		table = &eloop.fd_table[events[i].ident];
645780fb4a2SCy Schubert 		if (table->handler == NULL)
646780fb4a2SCy Schubert 			continue;
647780fb4a2SCy Schubert 		table->handler(table->sock, table->eloop_data,
648780fb4a2SCy Schubert 			       table->user_data);
649780fb4a2SCy Schubert 		if (eloop.readers.changed ||
650780fb4a2SCy Schubert 		    eloop.writers.changed ||
651780fb4a2SCy Schubert 		    eloop.exceptions.changed)
652780fb4a2SCy Schubert 			break;
653780fb4a2SCy Schubert 	}
654780fb4a2SCy Schubert }
655780fb4a2SCy Schubert 
656780fb4a2SCy Schubert 
eloop_sock_table_requeue(struct eloop_sock_table * table)657780fb4a2SCy Schubert static int eloop_sock_table_requeue(struct eloop_sock_table *table)
658780fb4a2SCy Schubert {
659c1d255d3SCy Schubert 	size_t i;
660c1d255d3SCy Schubert 	int r;
661780fb4a2SCy Schubert 
662780fb4a2SCy Schubert 	r = 0;
663780fb4a2SCy Schubert 	for (i = 0; i < table->count && table->table; i++) {
664780fb4a2SCy Schubert 		if (eloop_sock_queue(table->table[i].sock, table->type) == -1)
665780fb4a2SCy Schubert 			r = -1;
666780fb4a2SCy Schubert 	}
667780fb4a2SCy Schubert 	return r;
668780fb4a2SCy Schubert }
669780fb4a2SCy Schubert 
670780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
671780fb4a2SCy Schubert 
672780fb4a2SCy Schubert 
eloop_sock_requeue(void)673780fb4a2SCy Schubert int eloop_sock_requeue(void)
674780fb4a2SCy Schubert {
675780fb4a2SCy Schubert 	int r = 0;
676780fb4a2SCy Schubert 
677780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
678780fb4a2SCy Schubert 	close(eloop.kqueuefd);
679780fb4a2SCy Schubert 	eloop.kqueuefd = kqueue();
680780fb4a2SCy Schubert 	if (eloop.kqueuefd < 0) {
681780fb4a2SCy Schubert 		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
682780fb4a2SCy Schubert 			   __func__, strerror(errno));
683780fb4a2SCy Schubert 		return -1;
684780fb4a2SCy Schubert 	}
685780fb4a2SCy Schubert 
686780fb4a2SCy Schubert 	if (eloop_sock_table_requeue(&eloop.readers) < 0)
687780fb4a2SCy Schubert 		r = -1;
688780fb4a2SCy Schubert 	if (eloop_sock_table_requeue(&eloop.writers) < 0)
689780fb4a2SCy Schubert 		r = -1;
690780fb4a2SCy Schubert 	if (eloop_sock_table_requeue(&eloop.exceptions) < 0)
691780fb4a2SCy Schubert 		r = -1;
692780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
693780fb4a2SCy Schubert 
694780fb4a2SCy Schubert 	return r;
695780fb4a2SCy Schubert }
696780fb4a2SCy Schubert 
697780fb4a2SCy Schubert 
eloop_sock_table_destroy(struct eloop_sock_table * table)69839beb93cSSam Leffler static void eloop_sock_table_destroy(struct eloop_sock_table *table)
69939beb93cSSam Leffler {
70039beb93cSSam Leffler 	if (table) {
701c1d255d3SCy Schubert 		size_t i;
702c1d255d3SCy Schubert 
70339beb93cSSam Leffler 		for (i = 0; i < table->count && table->table; i++) {
704e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
705e28a4053SRui Paulo 				   "sock=%d eloop_data=%p user_data=%p "
706e28a4053SRui Paulo 				   "handler=%p",
70739beb93cSSam Leffler 				   table->table[i].sock,
70839beb93cSSam Leffler 				   table->table[i].eloop_data,
70939beb93cSSam Leffler 				   table->table[i].user_data,
71039beb93cSSam Leffler 				   table->table[i].handler);
711e28a4053SRui Paulo 			wpa_trace_dump_funcname("eloop unregistered socket "
712e28a4053SRui Paulo 						"handler",
713e28a4053SRui Paulo 						table->table[i].handler);
714e28a4053SRui Paulo 			wpa_trace_dump("eloop sock", &table->table[i]);
71539beb93cSSam Leffler 		}
71639beb93cSSam Leffler 		os_free(table->table);
71739beb93cSSam Leffler 	}
71839beb93cSSam Leffler }
71939beb93cSSam Leffler 
72039beb93cSSam Leffler 
eloop_register_read_sock(int sock,eloop_sock_handler handler,void * eloop_data,void * user_data)72139beb93cSSam Leffler int eloop_register_read_sock(int sock, eloop_sock_handler handler,
72239beb93cSSam Leffler 			     void *eloop_data, void *user_data)
72339beb93cSSam Leffler {
72439beb93cSSam Leffler 	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
72539beb93cSSam Leffler 				   eloop_data, user_data);
72639beb93cSSam Leffler }
72739beb93cSSam Leffler 
72839beb93cSSam Leffler 
eloop_unregister_read_sock(int sock)72939beb93cSSam Leffler void eloop_unregister_read_sock(int sock)
73039beb93cSSam Leffler {
73139beb93cSSam Leffler 	eloop_unregister_sock(sock, EVENT_TYPE_READ);
73239beb93cSSam Leffler }
73339beb93cSSam Leffler 
73439beb93cSSam Leffler 
eloop_get_sock_table(eloop_event_type type)73539beb93cSSam Leffler static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
73639beb93cSSam Leffler {
73739beb93cSSam Leffler 	switch (type) {
73839beb93cSSam Leffler 	case EVENT_TYPE_READ:
73939beb93cSSam Leffler 		return &eloop.readers;
74039beb93cSSam Leffler 	case EVENT_TYPE_WRITE:
74139beb93cSSam Leffler 		return &eloop.writers;
74239beb93cSSam Leffler 	case EVENT_TYPE_EXCEPTION:
74339beb93cSSam Leffler 		return &eloop.exceptions;
74439beb93cSSam Leffler 	}
74539beb93cSSam Leffler 
74639beb93cSSam Leffler 	return NULL;
74739beb93cSSam Leffler }
74839beb93cSSam Leffler 
74939beb93cSSam Leffler 
eloop_register_sock(int sock,eloop_event_type type,eloop_sock_handler handler,void * eloop_data,void * user_data)75039beb93cSSam Leffler int eloop_register_sock(int sock, eloop_event_type type,
75139beb93cSSam Leffler 			eloop_sock_handler handler,
75239beb93cSSam Leffler 			void *eloop_data, void *user_data)
75339beb93cSSam Leffler {
75439beb93cSSam Leffler 	struct eloop_sock_table *table;
75539beb93cSSam Leffler 
7565b9c547cSRui Paulo 	assert(sock >= 0);
75739beb93cSSam Leffler 	table = eloop_get_sock_table(type);
75839beb93cSSam Leffler 	return eloop_sock_table_add_sock(table, sock, handler,
75939beb93cSSam Leffler 					 eloop_data, user_data);
76039beb93cSSam Leffler }
76139beb93cSSam Leffler 
76239beb93cSSam Leffler 
eloop_unregister_sock(int sock,eloop_event_type type)76339beb93cSSam Leffler void eloop_unregister_sock(int sock, eloop_event_type type)
76439beb93cSSam Leffler {
76539beb93cSSam Leffler 	struct eloop_sock_table *table;
76639beb93cSSam Leffler 
76739beb93cSSam Leffler 	table = eloop_get_sock_table(type);
76839beb93cSSam Leffler 	eloop_sock_table_remove_sock(table, sock);
76939beb93cSSam Leffler }
77039beb93cSSam Leffler 
77139beb93cSSam Leffler 
eloop_register_timeout(unsigned int secs,unsigned int usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data)77239beb93cSSam Leffler int eloop_register_timeout(unsigned int secs, unsigned int usecs,
77339beb93cSSam Leffler 			   eloop_timeout_handler handler,
77439beb93cSSam Leffler 			   void *eloop_data, void *user_data)
77539beb93cSSam Leffler {
776e28a4053SRui Paulo 	struct eloop_timeout *timeout, *tmp;
777f05cddf9SRui Paulo 	os_time_t now_sec;
77839beb93cSSam Leffler 
779e28a4053SRui Paulo 	timeout = os_zalloc(sizeof(*timeout));
78039beb93cSSam Leffler 	if (timeout == NULL)
78139beb93cSSam Leffler 		return -1;
7825b9c547cSRui Paulo 	if (os_get_reltime(&timeout->time) < 0) {
78339beb93cSSam Leffler 		os_free(timeout);
78439beb93cSSam Leffler 		return -1;
78539beb93cSSam Leffler 	}
786f05cddf9SRui Paulo 	now_sec = timeout->time.sec;
78739beb93cSSam Leffler 	timeout->time.sec += secs;
788*4b72b91aSCy Schubert 	if (timeout->time.sec < now_sec)
789*4b72b91aSCy Schubert 		goto overflow;
79039beb93cSSam Leffler 	timeout->time.usec += usecs;
79139beb93cSSam Leffler 	while (timeout->time.usec >= 1000000) {
79239beb93cSSam Leffler 		timeout->time.sec++;
79339beb93cSSam Leffler 		timeout->time.usec -= 1000000;
79439beb93cSSam Leffler 	}
795*4b72b91aSCy Schubert 	if (timeout->time.sec < now_sec)
796*4b72b91aSCy Schubert 		goto overflow;
79739beb93cSSam Leffler 	timeout->eloop_data = eloop_data;
79839beb93cSSam Leffler 	timeout->user_data = user_data;
79939beb93cSSam Leffler 	timeout->handler = handler;
800e28a4053SRui Paulo 	wpa_trace_add_ref(timeout, eloop, eloop_data);
801e28a4053SRui Paulo 	wpa_trace_add_ref(timeout, user, user_data);
802e28a4053SRui Paulo 	wpa_trace_record(timeout);
80339beb93cSSam Leffler 
804e28a4053SRui Paulo 	/* Maintain timeouts in order of increasing time */
805e28a4053SRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
8065b9c547cSRui Paulo 		if (os_reltime_before(&timeout->time, &tmp->time)) {
807e28a4053SRui Paulo 			dl_list_add(tmp->list.prev, &timeout->list);
808e28a4053SRui Paulo 			return 0;
809e28a4053SRui Paulo 		}
810e28a4053SRui Paulo 	}
811e28a4053SRui Paulo 	dl_list_add_tail(&eloop.timeout, &timeout->list);
812e28a4053SRui Paulo 
81339beb93cSSam Leffler 	return 0;
814*4b72b91aSCy Schubert 
815*4b72b91aSCy Schubert overflow:
816*4b72b91aSCy Schubert 	/*
817*4b72b91aSCy Schubert 	 * Integer overflow - assume long enough timeout to be assumed
818*4b72b91aSCy Schubert 	 * to be infinite, i.e., the timeout would never happen.
819*4b72b91aSCy Schubert 	 */
820*4b72b91aSCy Schubert 	wpa_printf(MSG_DEBUG,
821*4b72b91aSCy Schubert 		   "ELOOP: Too long timeout (secs=%u usecs=%u) to ever happen - ignore it",
822*4b72b91aSCy Schubert 		   secs,usecs);
823*4b72b91aSCy Schubert 	os_free(timeout);
824*4b72b91aSCy Schubert 	return 0;
82539beb93cSSam Leffler }
82639beb93cSSam Leffler 
82739beb93cSSam Leffler 
eloop_remove_timeout(struct eloop_timeout * timeout)828e28a4053SRui Paulo static void eloop_remove_timeout(struct eloop_timeout *timeout)
829e28a4053SRui Paulo {
830e28a4053SRui Paulo 	dl_list_del(&timeout->list);
831e28a4053SRui Paulo 	wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
832e28a4053SRui Paulo 	wpa_trace_remove_ref(timeout, user, timeout->user_data);
833e28a4053SRui Paulo 	os_free(timeout);
83439beb93cSSam Leffler }
83539beb93cSSam Leffler 
83639beb93cSSam Leffler 
eloop_cancel_timeout(eloop_timeout_handler handler,void * eloop_data,void * user_data)83739beb93cSSam Leffler int eloop_cancel_timeout(eloop_timeout_handler handler,
83839beb93cSSam Leffler 			 void *eloop_data, void *user_data)
83939beb93cSSam Leffler {
840e28a4053SRui Paulo 	struct eloop_timeout *timeout, *prev;
84139beb93cSSam Leffler 	int removed = 0;
84239beb93cSSam Leffler 
843e28a4053SRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
844e28a4053SRui Paulo 			      struct eloop_timeout, list) {
84539beb93cSSam Leffler 		if (timeout->handler == handler &&
84639beb93cSSam Leffler 		    (timeout->eloop_data == eloop_data ||
84739beb93cSSam Leffler 		     eloop_data == ELOOP_ALL_CTX) &&
84839beb93cSSam Leffler 		    (timeout->user_data == user_data ||
84939beb93cSSam Leffler 		     user_data == ELOOP_ALL_CTX)) {
850e28a4053SRui Paulo 			eloop_remove_timeout(timeout);
85139beb93cSSam Leffler 			removed++;
852e28a4053SRui Paulo 		}
85339beb93cSSam Leffler 	}
85439beb93cSSam Leffler 
85539beb93cSSam Leffler 	return removed;
85639beb93cSSam Leffler }
85739beb93cSSam Leffler 
85839beb93cSSam Leffler 
eloop_cancel_timeout_one(eloop_timeout_handler handler,void * eloop_data,void * user_data,struct os_reltime * remaining)8595b9c547cSRui Paulo int eloop_cancel_timeout_one(eloop_timeout_handler handler,
8605b9c547cSRui Paulo 			     void *eloop_data, void *user_data,
8615b9c547cSRui Paulo 			     struct os_reltime *remaining)
8625b9c547cSRui Paulo {
8635b9c547cSRui Paulo 	struct eloop_timeout *timeout, *prev;
8645b9c547cSRui Paulo 	int removed = 0;
8655b9c547cSRui Paulo 	struct os_reltime now;
8665b9c547cSRui Paulo 
8675b9c547cSRui Paulo 	os_get_reltime(&now);
8685b9c547cSRui Paulo 	remaining->sec = remaining->usec = 0;
8695b9c547cSRui Paulo 
8705b9c547cSRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
8715b9c547cSRui Paulo 			      struct eloop_timeout, list) {
8725b9c547cSRui Paulo 		if (timeout->handler == handler &&
8735b9c547cSRui Paulo 		    (timeout->eloop_data == eloop_data) &&
8745b9c547cSRui Paulo 		    (timeout->user_data == user_data)) {
8755b9c547cSRui Paulo 			removed = 1;
8765b9c547cSRui Paulo 			if (os_reltime_before(&now, &timeout->time))
8775b9c547cSRui Paulo 				os_reltime_sub(&timeout->time, &now, remaining);
8785b9c547cSRui Paulo 			eloop_remove_timeout(timeout);
8795b9c547cSRui Paulo 			break;
8805b9c547cSRui Paulo 		}
8815b9c547cSRui Paulo 	}
8825b9c547cSRui Paulo 	return removed;
8835b9c547cSRui Paulo }
8845b9c547cSRui Paulo 
8855b9c547cSRui Paulo 
eloop_is_timeout_registered(eloop_timeout_handler handler,void * eloop_data,void * user_data)88639beb93cSSam Leffler int eloop_is_timeout_registered(eloop_timeout_handler handler,
88739beb93cSSam Leffler 				void *eloop_data, void *user_data)
88839beb93cSSam Leffler {
88939beb93cSSam Leffler 	struct eloop_timeout *tmp;
89039beb93cSSam Leffler 
891e28a4053SRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
89239beb93cSSam Leffler 		if (tmp->handler == handler &&
89339beb93cSSam Leffler 		    tmp->eloop_data == eloop_data &&
89439beb93cSSam Leffler 		    tmp->user_data == user_data)
89539beb93cSSam Leffler 			return 1;
89639beb93cSSam Leffler 	}
89739beb93cSSam Leffler 
89839beb93cSSam Leffler 	return 0;
89939beb93cSSam Leffler }
90039beb93cSSam Leffler 
90139beb93cSSam Leffler 
eloop_deplete_timeout(unsigned int req_secs,unsigned int req_usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data)9025b9c547cSRui Paulo int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
9035b9c547cSRui Paulo 			  eloop_timeout_handler handler, void *eloop_data,
9045b9c547cSRui Paulo 			  void *user_data)
9055b9c547cSRui Paulo {
9065b9c547cSRui Paulo 	struct os_reltime now, requested, remaining;
9075b9c547cSRui Paulo 	struct eloop_timeout *tmp;
9085b9c547cSRui Paulo 
9095b9c547cSRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
9105b9c547cSRui Paulo 		if (tmp->handler == handler &&
9115b9c547cSRui Paulo 		    tmp->eloop_data == eloop_data &&
9125b9c547cSRui Paulo 		    tmp->user_data == user_data) {
9135b9c547cSRui Paulo 			requested.sec = req_secs;
9145b9c547cSRui Paulo 			requested.usec = req_usecs;
9155b9c547cSRui Paulo 			os_get_reltime(&now);
9165b9c547cSRui Paulo 			os_reltime_sub(&tmp->time, &now, &remaining);
9175b9c547cSRui Paulo 			if (os_reltime_before(&requested, &remaining)) {
9185b9c547cSRui Paulo 				eloop_cancel_timeout(handler, eloop_data,
9195b9c547cSRui Paulo 						     user_data);
9205b9c547cSRui Paulo 				eloop_register_timeout(requested.sec,
9215b9c547cSRui Paulo 						       requested.usec,
9225b9c547cSRui Paulo 						       handler, eloop_data,
9235b9c547cSRui Paulo 						       user_data);
9245b9c547cSRui Paulo 				return 1;
9255b9c547cSRui Paulo 			}
9265b9c547cSRui Paulo 			return 0;
9275b9c547cSRui Paulo 		}
9285b9c547cSRui Paulo 	}
9295b9c547cSRui Paulo 
9305b9c547cSRui Paulo 	return -1;
9315b9c547cSRui Paulo }
9325b9c547cSRui Paulo 
9335b9c547cSRui Paulo 
eloop_replenish_timeout(unsigned int req_secs,unsigned int req_usecs,eloop_timeout_handler handler,void * eloop_data,void * user_data)9345b9c547cSRui Paulo int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
9355b9c547cSRui Paulo 			    eloop_timeout_handler handler, void *eloop_data,
9365b9c547cSRui Paulo 			    void *user_data)
9375b9c547cSRui Paulo {
9385b9c547cSRui Paulo 	struct os_reltime now, requested, remaining;
9395b9c547cSRui Paulo 	struct eloop_timeout *tmp;
9405b9c547cSRui Paulo 
9415b9c547cSRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
9425b9c547cSRui Paulo 		if (tmp->handler == handler &&
9435b9c547cSRui Paulo 		    tmp->eloop_data == eloop_data &&
9445b9c547cSRui Paulo 		    tmp->user_data == user_data) {
9455b9c547cSRui Paulo 			requested.sec = req_secs;
9465b9c547cSRui Paulo 			requested.usec = req_usecs;
9475b9c547cSRui Paulo 			os_get_reltime(&now);
9485b9c547cSRui Paulo 			os_reltime_sub(&tmp->time, &now, &remaining);
9495b9c547cSRui Paulo 			if (os_reltime_before(&remaining, &requested)) {
9505b9c547cSRui Paulo 				eloop_cancel_timeout(handler, eloop_data,
9515b9c547cSRui Paulo 						     user_data);
9525b9c547cSRui Paulo 				eloop_register_timeout(requested.sec,
9535b9c547cSRui Paulo 						       requested.usec,
9545b9c547cSRui Paulo 						       handler, eloop_data,
9555b9c547cSRui Paulo 						       user_data);
9565b9c547cSRui Paulo 				return 1;
9575b9c547cSRui Paulo 			}
9585b9c547cSRui Paulo 			return 0;
9595b9c547cSRui Paulo 		}
9605b9c547cSRui Paulo 	}
9615b9c547cSRui Paulo 
9625b9c547cSRui Paulo 	return -1;
9635b9c547cSRui Paulo }
9645b9c547cSRui Paulo 
9655b9c547cSRui Paulo 
96639beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
eloop_handle_alarm(int sig)96739beb93cSSam Leffler static void eloop_handle_alarm(int sig)
96839beb93cSSam Leffler {
969e28a4053SRui Paulo 	wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
970e28a4053SRui Paulo 		   "two seconds. Looks like there\n"
97139beb93cSSam Leffler 		   "is a bug that ends up in a busy loop that "
97239beb93cSSam Leffler 		   "prevents clean shutdown.\n"
97339beb93cSSam Leffler 		   "Killing program forcefully.\n");
97439beb93cSSam Leffler 	exit(1);
97539beb93cSSam Leffler }
97639beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
97739beb93cSSam Leffler 
97839beb93cSSam Leffler 
eloop_handle_signal(int sig)97939beb93cSSam Leffler static void eloop_handle_signal(int sig)
98039beb93cSSam Leffler {
981c1d255d3SCy Schubert 	size_t i;
98239beb93cSSam Leffler 
98339beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
98439beb93cSSam Leffler 	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
98539beb93cSSam Leffler 		/* Use SIGALRM to break out from potential busy loops that
98639beb93cSSam Leffler 		 * would not allow the program to be killed. */
98739beb93cSSam Leffler 		eloop.pending_terminate = 1;
98839beb93cSSam Leffler 		signal(SIGALRM, eloop_handle_alarm);
98939beb93cSSam Leffler 		alarm(2);
99039beb93cSSam Leffler 	}
99139beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
99239beb93cSSam Leffler 
99339beb93cSSam Leffler 	eloop.signaled++;
99439beb93cSSam Leffler 	for (i = 0; i < eloop.signal_count; i++) {
99539beb93cSSam Leffler 		if (eloop.signals[i].sig == sig) {
99639beb93cSSam Leffler 			eloop.signals[i].signaled++;
99739beb93cSSam Leffler 			break;
99839beb93cSSam Leffler 		}
99939beb93cSSam Leffler 	}
100039beb93cSSam Leffler }
100139beb93cSSam Leffler 
100239beb93cSSam Leffler 
eloop_process_pending_signals(void)100339beb93cSSam Leffler static void eloop_process_pending_signals(void)
100439beb93cSSam Leffler {
1005c1d255d3SCy Schubert 	size_t i;
100639beb93cSSam Leffler 
100739beb93cSSam Leffler 	if (eloop.signaled == 0)
100839beb93cSSam Leffler 		return;
100939beb93cSSam Leffler 	eloop.signaled = 0;
101039beb93cSSam Leffler 
101139beb93cSSam Leffler 	if (eloop.pending_terminate) {
101239beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
101339beb93cSSam Leffler 		alarm(0);
101439beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
101539beb93cSSam Leffler 		eloop.pending_terminate = 0;
101639beb93cSSam Leffler 	}
101739beb93cSSam Leffler 
101839beb93cSSam Leffler 	for (i = 0; i < eloop.signal_count; i++) {
101939beb93cSSam Leffler 		if (eloop.signals[i].signaled) {
102039beb93cSSam Leffler 			eloop.signals[i].signaled = 0;
102139beb93cSSam Leffler 			eloop.signals[i].handler(eloop.signals[i].sig,
102239beb93cSSam Leffler 						 eloop.signals[i].user_data);
102339beb93cSSam Leffler 		}
102439beb93cSSam Leffler 	}
102539beb93cSSam Leffler }
102639beb93cSSam Leffler 
102739beb93cSSam Leffler 
eloop_register_signal(int sig,eloop_signal_handler handler,void * user_data)102839beb93cSSam Leffler int eloop_register_signal(int sig, eloop_signal_handler handler,
102939beb93cSSam Leffler 			  void *user_data)
103039beb93cSSam Leffler {
103139beb93cSSam Leffler 	struct eloop_signal *tmp;
103239beb93cSSam Leffler 
1033f05cddf9SRui Paulo 	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
103439beb93cSSam Leffler 			       sizeof(struct eloop_signal));
103539beb93cSSam Leffler 	if (tmp == NULL)
103639beb93cSSam Leffler 		return -1;
103739beb93cSSam Leffler 
103839beb93cSSam Leffler 	tmp[eloop.signal_count].sig = sig;
103939beb93cSSam Leffler 	tmp[eloop.signal_count].user_data = user_data;
104039beb93cSSam Leffler 	tmp[eloop.signal_count].handler = handler;
104139beb93cSSam Leffler 	tmp[eloop.signal_count].signaled = 0;
104239beb93cSSam Leffler 	eloop.signal_count++;
104339beb93cSSam Leffler 	eloop.signals = tmp;
104439beb93cSSam Leffler 	signal(sig, eloop_handle_signal);
104539beb93cSSam Leffler 
104639beb93cSSam Leffler 	return 0;
104739beb93cSSam Leffler }
104839beb93cSSam Leffler 
104939beb93cSSam Leffler 
eloop_register_signal_terminate(eloop_signal_handler handler,void * user_data)105039beb93cSSam Leffler int eloop_register_signal_terminate(eloop_signal_handler handler,
105139beb93cSSam Leffler 				    void *user_data)
105239beb93cSSam Leffler {
105339beb93cSSam Leffler 	int ret = eloop_register_signal(SIGINT, handler, user_data);
105439beb93cSSam Leffler 	if (ret == 0)
105539beb93cSSam Leffler 		ret = eloop_register_signal(SIGTERM, handler, user_data);
105639beb93cSSam Leffler 	return ret;
105739beb93cSSam Leffler }
105839beb93cSSam Leffler 
105939beb93cSSam Leffler 
eloop_register_signal_reconfig(eloop_signal_handler handler,void * user_data)106039beb93cSSam Leffler int eloop_register_signal_reconfig(eloop_signal_handler handler,
106139beb93cSSam Leffler 				   void *user_data)
106239beb93cSSam Leffler {
106339beb93cSSam Leffler #ifdef CONFIG_NATIVE_WINDOWS
106439beb93cSSam Leffler 	return 0;
106539beb93cSSam Leffler #else /* CONFIG_NATIVE_WINDOWS */
106639beb93cSSam Leffler 	return eloop_register_signal(SIGHUP, handler, user_data);
106739beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
106839beb93cSSam Leffler }
106939beb93cSSam Leffler 
107039beb93cSSam Leffler 
eloop_run(void)107139beb93cSSam Leffler void eloop_run(void)
107239beb93cSSam Leffler {
1073f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1074f05cddf9SRui Paulo 	int num_poll_fds;
1075f05cddf9SRui Paulo 	int timeout_ms = 0;
10765b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
10775b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
107839beb93cSSam Leffler 	fd_set *rfds, *wfds, *efds;
107939beb93cSSam Leffler 	struct timeval _tv;
10805b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
10815b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
10825b9c547cSRui Paulo 	int timeout_ms = -1;
10835b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
1084780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1085780fb4a2SCy Schubert 	struct timespec ts;
1086780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
1087f05cddf9SRui Paulo 	int res;
10885b9c547cSRui Paulo 	struct os_reltime tv, now;
108939beb93cSSam Leffler 
10905b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
109139beb93cSSam Leffler 	rfds = os_malloc(sizeof(*rfds));
109239beb93cSSam Leffler 	wfds = os_malloc(sizeof(*wfds));
109339beb93cSSam Leffler 	efds = os_malloc(sizeof(*efds));
1094e28a4053SRui Paulo 	if (rfds == NULL || wfds == NULL || efds == NULL)
109539beb93cSSam Leffler 		goto out;
10965b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
109739beb93cSSam Leffler 
109839beb93cSSam Leffler 	while (!eloop.terminate &&
1099e28a4053SRui Paulo 	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
110039beb93cSSam Leffler 		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
1101e28a4053SRui Paulo 		struct eloop_timeout *timeout;
1102325151a3SRui Paulo 
1103325151a3SRui Paulo 		if (eloop.pending_terminate) {
1104325151a3SRui Paulo 			/*
1105325151a3SRui Paulo 			 * This may happen in some corner cases where a signal
1106325151a3SRui Paulo 			 * is received during a blocking operation. We need to
1107325151a3SRui Paulo 			 * process the pending signals and exit if requested to
1108325151a3SRui Paulo 			 * avoid hitting the SIGALRM limit if the blocking
1109325151a3SRui Paulo 			 * operation took more than two seconds.
1110325151a3SRui Paulo 			 */
1111325151a3SRui Paulo 			eloop_process_pending_signals();
1112325151a3SRui Paulo 			if (eloop.terminate)
1113325151a3SRui Paulo 				break;
1114325151a3SRui Paulo 		}
1115325151a3SRui Paulo 
1116e28a4053SRui Paulo 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
1117e28a4053SRui Paulo 					list);
1118e28a4053SRui Paulo 		if (timeout) {
11195b9c547cSRui Paulo 			os_get_reltime(&now);
11205b9c547cSRui Paulo 			if (os_reltime_before(&now, &timeout->time))
11215b9c547cSRui Paulo 				os_reltime_sub(&timeout->time, &now, &tv);
112239beb93cSSam Leffler 			else
112339beb93cSSam Leffler 				tv.sec = tv.usec = 0;
11245b9c547cSRui Paulo #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
1125f05cddf9SRui Paulo 			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
11265b9c547cSRui Paulo #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
11275b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
112839beb93cSSam Leffler 			_tv.tv_sec = tv.sec;
112939beb93cSSam Leffler 			_tv.tv_usec = tv.usec;
11305b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1131780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1132780fb4a2SCy Schubert 			ts.tv_sec = tv.sec;
1133780fb4a2SCy Schubert 			ts.tv_nsec = tv.usec * 1000L;
1134780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
113539beb93cSSam Leffler 		}
113639beb93cSSam Leffler 
1137f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1138f05cddf9SRui Paulo 		num_poll_fds = eloop_sock_table_set_fds(
1139f05cddf9SRui Paulo 			&eloop.readers, &eloop.writers, &eloop.exceptions,
1140f05cddf9SRui Paulo 			eloop.pollfds, eloop.pollfds_map,
1141f05cddf9SRui Paulo 			eloop.max_pollfd_map);
1142f05cddf9SRui Paulo 		res = poll(eloop.pollfds, num_poll_fds,
1143f05cddf9SRui Paulo 			   timeout ? timeout_ms : -1);
11445b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
11455b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
114639beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.readers, rfds);
114739beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.writers, wfds);
114839beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.exceptions, efds);
114939beb93cSSam Leffler 		res = select(eloop.max_sock + 1, rfds, wfds, efds,
1150e28a4053SRui Paulo 			     timeout ? &_tv : NULL);
11515b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
11525b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
11535b9c547cSRui Paulo 		if (eloop.count == 0) {
11545b9c547cSRui Paulo 			res = 0;
11555b9c547cSRui Paulo 		} else {
11565b9c547cSRui Paulo 			res = epoll_wait(eloop.epollfd, eloop.epoll_events,
11575b9c547cSRui Paulo 					 eloop.count, timeout_ms);
11585b9c547cSRui Paulo 		}
11595b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
1160780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1161780fb4a2SCy Schubert 		if (eloop.count == 0) {
1162780fb4a2SCy Schubert 			res = 0;
1163780fb4a2SCy Schubert 		} else {
1164780fb4a2SCy Schubert 			res = kevent(eloop.kqueuefd, NULL, 0,
1165780fb4a2SCy Schubert 				     eloop.kqueue_events, eloop.kqueue_nevents,
1166780fb4a2SCy Schubert 				     timeout ? &ts : NULL);
1167780fb4a2SCy Schubert 		}
1168780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
116939beb93cSSam Leffler 		if (res < 0 && errno != EINTR && errno != 0) {
11705b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "eloop: %s: %s",
11715b9c547cSRui Paulo #ifdef CONFIG_ELOOP_POLL
11725b9c547cSRui Paulo 				   "poll"
11735b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
11745b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
11755b9c547cSRui Paulo 				   "select"
11765b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
11775b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
11785b9c547cSRui Paulo 				   "epoll"
11795b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
1180780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1181780fb4a2SCy Schubert 				   "kqueue"
1182780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EKQUEUE */
1183780fb4a2SCy Schubert 
11845b9c547cSRui Paulo 				   , strerror(errno));
118539beb93cSSam Leffler 			goto out;
118639beb93cSSam Leffler 		}
1187325151a3SRui Paulo 
1188325151a3SRui Paulo 		eloop.readers.changed = 0;
1189325151a3SRui Paulo 		eloop.writers.changed = 0;
1190325151a3SRui Paulo 		eloop.exceptions.changed = 0;
1191325151a3SRui Paulo 
119239beb93cSSam Leffler 		eloop_process_pending_signals();
119339beb93cSSam Leffler 
1194780fb4a2SCy Schubert 
119539beb93cSSam Leffler 		/* check if some registered timeouts have occurred */
1196e28a4053SRui Paulo 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
1197e28a4053SRui Paulo 					list);
1198e28a4053SRui Paulo 		if (timeout) {
11995b9c547cSRui Paulo 			os_get_reltime(&now);
12005b9c547cSRui Paulo 			if (!os_reltime_before(&now, &timeout->time)) {
1201e28a4053SRui Paulo 				void *eloop_data = timeout->eloop_data;
1202e28a4053SRui Paulo 				void *user_data = timeout->user_data;
1203e28a4053SRui Paulo 				eloop_timeout_handler handler =
1204e28a4053SRui Paulo 					timeout->handler;
1205e28a4053SRui Paulo 				eloop_remove_timeout(timeout);
1206e28a4053SRui Paulo 				handler(eloop_data, user_data);
120739beb93cSSam Leffler 			}
120839beb93cSSam Leffler 
120939beb93cSSam Leffler 		}
121039beb93cSSam Leffler 
121139beb93cSSam Leffler 		if (res <= 0)
121239beb93cSSam Leffler 			continue;
121339beb93cSSam Leffler 
1214325151a3SRui Paulo 		if (eloop.readers.changed ||
1215325151a3SRui Paulo 		    eloop.writers.changed ||
1216325151a3SRui Paulo 		    eloop.exceptions.changed) {
1217325151a3SRui Paulo 			 /*
1218325151a3SRui Paulo 			  * Sockets may have been closed and reopened with the
1219325151a3SRui Paulo 			  * same FD in the signal or timeout handlers, so we
1220325151a3SRui Paulo 			  * must skip the previous results and check again
1221325151a3SRui Paulo 			  * whether any of the currently registered sockets have
1222325151a3SRui Paulo 			  * events.
1223325151a3SRui Paulo 			  */
1224325151a3SRui Paulo 			continue;
1225325151a3SRui Paulo 		}
1226325151a3SRui Paulo 
1227f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1228f05cddf9SRui Paulo 		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
1229f05cddf9SRui Paulo 					  &eloop.exceptions, eloop.pollfds_map,
1230f05cddf9SRui Paulo 					  eloop.max_pollfd_map);
12315b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
12325b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
123339beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.readers, rfds);
123439beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.writers, wfds);
123539beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.exceptions, efds);
12365b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
12375b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
12385b9c547cSRui Paulo 		eloop_sock_table_dispatch(eloop.epoll_events, res);
12395b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
1240780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1241780fb4a2SCy Schubert 		eloop_sock_table_dispatch(eloop.kqueue_events, res);
1242780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
124339beb93cSSam Leffler 	}
124439beb93cSSam Leffler 
12455b9c547cSRui Paulo 	eloop.terminate = 0;
124639beb93cSSam Leffler out:
12475b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
124839beb93cSSam Leffler 	os_free(rfds);
124939beb93cSSam Leffler 	os_free(wfds);
125039beb93cSSam Leffler 	os_free(efds);
12515b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1252f05cddf9SRui Paulo 	return;
125339beb93cSSam Leffler }
125439beb93cSSam Leffler 
125539beb93cSSam Leffler 
eloop_terminate(void)125639beb93cSSam Leffler void eloop_terminate(void)
125739beb93cSSam Leffler {
125839beb93cSSam Leffler 	eloop.terminate = 1;
125939beb93cSSam Leffler }
126039beb93cSSam Leffler 
126139beb93cSSam Leffler 
eloop_destroy(void)126239beb93cSSam Leffler void eloop_destroy(void)
126339beb93cSSam Leffler {
126439beb93cSSam Leffler 	struct eloop_timeout *timeout, *prev;
12655b9c547cSRui Paulo 	struct os_reltime now;
126639beb93cSSam Leffler 
12675b9c547cSRui Paulo 	os_get_reltime(&now);
1268e28a4053SRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
1269e28a4053SRui Paulo 			      struct eloop_timeout, list) {
127039beb93cSSam Leffler 		int sec, usec;
1271e28a4053SRui Paulo 		sec = timeout->time.sec - now.sec;
1272e28a4053SRui Paulo 		usec = timeout->time.usec - now.usec;
1273e28a4053SRui Paulo 		if (timeout->time.usec < now.usec) {
127439beb93cSSam Leffler 			sec--;
127539beb93cSSam Leffler 			usec += 1000000;
127639beb93cSSam Leffler 		}
1277e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
1278e28a4053SRui Paulo 			   "eloop_data=%p user_data=%p handler=%p",
1279e28a4053SRui Paulo 			   sec, usec, timeout->eloop_data, timeout->user_data,
1280e28a4053SRui Paulo 			   timeout->handler);
1281e28a4053SRui Paulo 		wpa_trace_dump_funcname("eloop unregistered timeout handler",
1282e28a4053SRui Paulo 					timeout->handler);
1283e28a4053SRui Paulo 		wpa_trace_dump("eloop timeout", timeout);
1284e28a4053SRui Paulo 		eloop_remove_timeout(timeout);
128539beb93cSSam Leffler 	}
128639beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.readers);
128739beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.writers);
128839beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.exceptions);
128939beb93cSSam Leffler 	os_free(eloop.signals);
1290f05cddf9SRui Paulo 
1291f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1292f05cddf9SRui Paulo 	os_free(eloop.pollfds);
1293f05cddf9SRui Paulo 	os_free(eloop.pollfds_map);
1294f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
1295780fb4a2SCy Schubert #if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
1296780fb4a2SCy Schubert 	os_free(eloop.fd_table);
1297780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
12985b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
12995b9c547cSRui Paulo 	os_free(eloop.epoll_events);
13005b9c547cSRui Paulo 	close(eloop.epollfd);
13015b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
1302780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1303780fb4a2SCy Schubert 	os_free(eloop.kqueue_events);
1304780fb4a2SCy Schubert 	close(eloop.kqueuefd);
1305780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
130639beb93cSSam Leffler }
130739beb93cSSam Leffler 
130839beb93cSSam Leffler 
eloop_terminated(void)130939beb93cSSam Leffler int eloop_terminated(void)
131039beb93cSSam Leffler {
1311325151a3SRui Paulo 	return eloop.terminate || eloop.pending_terminate;
131239beb93cSSam Leffler }
131339beb93cSSam Leffler 
131439beb93cSSam Leffler 
eloop_wait_for_read_sock(int sock)131539beb93cSSam Leffler void eloop_wait_for_read_sock(int sock)
131639beb93cSSam Leffler {
1317f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1318f05cddf9SRui Paulo 	struct pollfd pfd;
1319f05cddf9SRui Paulo 
1320f05cddf9SRui Paulo 	if (sock < 0)
1321f05cddf9SRui Paulo 		return;
1322f05cddf9SRui Paulo 
1323f05cddf9SRui Paulo 	os_memset(&pfd, 0, sizeof(pfd));
1324f05cddf9SRui Paulo 	pfd.fd = sock;
1325f05cddf9SRui Paulo 	pfd.events = POLLIN;
1326f05cddf9SRui Paulo 
1327f05cddf9SRui Paulo 	poll(&pfd, 1, -1);
13285b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
13295b9c547cSRui Paulo #if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
13305b9c547cSRui Paulo 	/*
13315b9c547cSRui Paulo 	 * We can use epoll() here. But epoll() requres 4 system calls.
13325b9c547cSRui Paulo 	 * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
13335b9c547cSRui Paulo 	 * epoll fd. So select() is better for performance here.
13345b9c547cSRui Paulo 	 */
133539beb93cSSam Leffler 	fd_set rfds;
133639beb93cSSam Leffler 
133739beb93cSSam Leffler 	if (sock < 0)
133839beb93cSSam Leffler 		return;
133939beb93cSSam Leffler 
134039beb93cSSam Leffler 	FD_ZERO(&rfds);
134139beb93cSSam Leffler 	FD_SET(sock, &rfds);
134239beb93cSSam Leffler 	select(sock + 1, &rfds, NULL, NULL, NULL);
13435b9c547cSRui Paulo #endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
1344780fb4a2SCy Schubert #ifdef CONFIG_ELOOP_KQUEUE
1345780fb4a2SCy Schubert 	int kfd;
1346780fb4a2SCy Schubert 	struct kevent ke1, ke2;
1347780fb4a2SCy Schubert 
1348780fb4a2SCy Schubert 	kfd = kqueue();
1349780fb4a2SCy Schubert 	if (kfd == -1)
1350780fb4a2SCy Schubert 		return;
1351780fb4a2SCy Schubert 	EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
1352780fb4a2SCy Schubert 	kevent(kfd, &ke1, 1, &ke2, 1, NULL);
1353780fb4a2SCy Schubert 	close(kfd);
1354780fb4a2SCy Schubert #endif /* CONFIG_ELOOP_KQUEUE */
135539beb93cSSam Leffler }
13565b9c547cSRui Paulo 
13575b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
13585b9c547cSRui Paulo #undef CONFIG_ELOOP_SELECT
13595b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1360