xref: /freebsd/contrib/wpa/src/utils/eloop.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
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"
10*5b9c547cSRui 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 
17*5b9c547cSRui Paulo #if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
18*5b9c547cSRui Paulo #error Do not define both of poll and epoll
19*5b9c547cSRui Paulo #endif
20*5b9c547cSRui Paulo 
21*5b9c547cSRui Paulo #if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
22*5b9c547cSRui Paulo #define CONFIG_ELOOP_SELECT
23*5b9c547cSRui Paulo #endif
24*5b9c547cSRui Paulo 
25f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
26f05cddf9SRui Paulo #include <poll.h>
27f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
28f05cddf9SRui Paulo 
29*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
30*5b9c547cSRui Paulo #include <sys/epoll.h>
31*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
3239beb93cSSam Leffler 
3339beb93cSSam Leffler struct eloop_sock {
3439beb93cSSam Leffler 	int sock;
3539beb93cSSam Leffler 	void *eloop_data;
3639beb93cSSam Leffler 	void *user_data;
3739beb93cSSam Leffler 	eloop_sock_handler handler;
38e28a4053SRui Paulo 	WPA_TRACE_REF(eloop);
39e28a4053SRui Paulo 	WPA_TRACE_REF(user);
40e28a4053SRui Paulo 	WPA_TRACE_INFO
4139beb93cSSam Leffler };
4239beb93cSSam Leffler 
4339beb93cSSam Leffler struct eloop_timeout {
44e28a4053SRui Paulo 	struct dl_list list;
45*5b9c547cSRui Paulo 	struct os_reltime time;
4639beb93cSSam Leffler 	void *eloop_data;
4739beb93cSSam Leffler 	void *user_data;
4839beb93cSSam Leffler 	eloop_timeout_handler handler;
49e28a4053SRui Paulo 	WPA_TRACE_REF(eloop);
50e28a4053SRui Paulo 	WPA_TRACE_REF(user);
51e28a4053SRui Paulo 	WPA_TRACE_INFO
5239beb93cSSam Leffler };
5339beb93cSSam Leffler 
5439beb93cSSam Leffler struct eloop_signal {
5539beb93cSSam Leffler 	int sig;
5639beb93cSSam Leffler 	void *user_data;
5739beb93cSSam Leffler 	eloop_signal_handler handler;
5839beb93cSSam Leffler 	int signaled;
5939beb93cSSam Leffler };
6039beb93cSSam Leffler 
6139beb93cSSam Leffler struct eloop_sock_table {
6239beb93cSSam Leffler 	int count;
6339beb93cSSam Leffler 	struct eloop_sock *table;
64*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
65*5b9c547cSRui Paulo 	eloop_event_type type;
66*5b9c547cSRui Paulo #else /* CONFIG_ELOOP_EPOLL */
6739beb93cSSam Leffler 	int changed;
68*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
6939beb93cSSam Leffler };
7039beb93cSSam Leffler 
7139beb93cSSam Leffler struct eloop_data {
7239beb93cSSam Leffler 	int max_sock;
7339beb93cSSam Leffler 
74f05cddf9SRui Paulo 	int count; /* sum of all table counts */
75f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
76f05cddf9SRui Paulo 	int max_pollfd_map; /* number of pollfds_map currently allocated */
77f05cddf9SRui Paulo 	int max_poll_fds; /* number of pollfds currently allocated */
78f05cddf9SRui Paulo 	struct pollfd *pollfds;
79f05cddf9SRui Paulo 	struct pollfd **pollfds_map;
80f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
81*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
82*5b9c547cSRui Paulo 	int epollfd;
83*5b9c547cSRui Paulo 	int epoll_max_event_num;
84*5b9c547cSRui Paulo 	int epoll_max_fd;
85*5b9c547cSRui Paulo 	struct eloop_sock *epoll_table;
86*5b9c547cSRui Paulo 	struct epoll_event *epoll_events;
87*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
8839beb93cSSam Leffler 	struct eloop_sock_table readers;
8939beb93cSSam Leffler 	struct eloop_sock_table writers;
9039beb93cSSam Leffler 	struct eloop_sock_table exceptions;
9139beb93cSSam Leffler 
92e28a4053SRui Paulo 	struct dl_list timeout;
9339beb93cSSam Leffler 
9439beb93cSSam Leffler 	int signal_count;
9539beb93cSSam Leffler 	struct eloop_signal *signals;
9639beb93cSSam Leffler 	int signaled;
9739beb93cSSam Leffler 	int pending_terminate;
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 	int terminate;
10039beb93cSSam Leffler };
10139beb93cSSam Leffler 
10239beb93cSSam Leffler static struct eloop_data eloop;
10339beb93cSSam Leffler 
10439beb93cSSam Leffler 
105e28a4053SRui Paulo #ifdef WPA_TRACE
106e28a4053SRui Paulo 
107e28a4053SRui Paulo static void eloop_sigsegv_handler(int sig)
108e28a4053SRui Paulo {
109e28a4053SRui Paulo 	wpa_trace_show("eloop SIGSEGV");
110e28a4053SRui Paulo 	abort();
111e28a4053SRui Paulo }
112e28a4053SRui Paulo 
113e28a4053SRui Paulo static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
114e28a4053SRui Paulo {
115e28a4053SRui Paulo 	int i;
116e28a4053SRui Paulo 	if (table == NULL || table->table == NULL)
117e28a4053SRui Paulo 		return;
118e28a4053SRui Paulo 	for (i = 0; i < table->count; i++) {
119e28a4053SRui Paulo 		wpa_trace_add_ref(&table->table[i], eloop,
120e28a4053SRui Paulo 				  table->table[i].eloop_data);
121e28a4053SRui Paulo 		wpa_trace_add_ref(&table->table[i], user,
122e28a4053SRui Paulo 				  table->table[i].user_data);
123e28a4053SRui Paulo 	}
124e28a4053SRui Paulo }
125e28a4053SRui Paulo 
126e28a4053SRui Paulo 
127e28a4053SRui Paulo static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
128e28a4053SRui Paulo {
129e28a4053SRui Paulo 	int i;
130e28a4053SRui Paulo 	if (table == NULL || table->table == NULL)
131e28a4053SRui Paulo 		return;
132e28a4053SRui Paulo 	for (i = 0; i < table->count; i++) {
133e28a4053SRui Paulo 		wpa_trace_remove_ref(&table->table[i], eloop,
134e28a4053SRui Paulo 				     table->table[i].eloop_data);
135e28a4053SRui Paulo 		wpa_trace_remove_ref(&table->table[i], user,
136e28a4053SRui Paulo 				     table->table[i].user_data);
137e28a4053SRui Paulo 	}
138e28a4053SRui Paulo }
139e28a4053SRui Paulo 
140e28a4053SRui Paulo #else /* WPA_TRACE */
141e28a4053SRui Paulo 
142e28a4053SRui Paulo #define eloop_trace_sock_add_ref(table) do { } while (0)
143e28a4053SRui Paulo #define eloop_trace_sock_remove_ref(table) do { } while (0)
144e28a4053SRui Paulo 
145e28a4053SRui Paulo #endif /* WPA_TRACE */
146e28a4053SRui Paulo 
147e28a4053SRui Paulo 
148e28a4053SRui Paulo int eloop_init(void)
14939beb93cSSam Leffler {
15039beb93cSSam Leffler 	os_memset(&eloop, 0, sizeof(eloop));
151e28a4053SRui Paulo 	dl_list_init(&eloop.timeout);
152*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
153*5b9c547cSRui Paulo 	eloop.epollfd = epoll_create1(0);
154*5b9c547cSRui Paulo 	if (eloop.epollfd < 0) {
155*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
156*5b9c547cSRui Paulo 			   __func__, strerror(errno));
157*5b9c547cSRui Paulo 		return -1;
158*5b9c547cSRui Paulo 	}
159*5b9c547cSRui Paulo 	eloop.readers.type = EVENT_TYPE_READ;
160*5b9c547cSRui Paulo 	eloop.writers.type = EVENT_TYPE_WRITE;
161*5b9c547cSRui Paulo 	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
162*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
163e28a4053SRui Paulo #ifdef WPA_TRACE
164e28a4053SRui Paulo 	signal(SIGSEGV, eloop_sigsegv_handler);
165e28a4053SRui Paulo #endif /* WPA_TRACE */
16639beb93cSSam Leffler 	return 0;
16739beb93cSSam Leffler }
16839beb93cSSam Leffler 
16939beb93cSSam Leffler 
17039beb93cSSam Leffler static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
17139beb93cSSam Leffler                                      int sock, eloop_sock_handler handler,
17239beb93cSSam Leffler                                      void *eloop_data, void *user_data)
17339beb93cSSam Leffler {
174*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
175*5b9c547cSRui Paulo 	struct eloop_sock *temp_table;
176*5b9c547cSRui Paulo 	struct epoll_event ev, *temp_events;
177*5b9c547cSRui Paulo 	int next;
178*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
17939beb93cSSam Leffler 	struct eloop_sock *tmp;
180f05cddf9SRui Paulo 	int new_max_sock;
181f05cddf9SRui Paulo 
182f05cddf9SRui Paulo 	if (sock > eloop.max_sock)
183f05cddf9SRui Paulo 		new_max_sock = sock;
184f05cddf9SRui Paulo 	else
185f05cddf9SRui Paulo 		new_max_sock = eloop.max_sock;
18639beb93cSSam Leffler 
18739beb93cSSam Leffler 	if (table == NULL)
18839beb93cSSam Leffler 		return -1;
18939beb93cSSam Leffler 
190f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
191f05cddf9SRui Paulo 	if (new_max_sock >= eloop.max_pollfd_map) {
192f05cddf9SRui Paulo 		struct pollfd **nmap;
193f05cddf9SRui Paulo 		nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
194f05cddf9SRui Paulo 					sizeof(struct pollfd *));
195f05cddf9SRui Paulo 		if (nmap == NULL)
196f05cddf9SRui Paulo 			return -1;
197f05cddf9SRui Paulo 
198f05cddf9SRui Paulo 		eloop.max_pollfd_map = new_max_sock + 50;
199f05cddf9SRui Paulo 		eloop.pollfds_map = nmap;
200f05cddf9SRui Paulo 	}
201f05cddf9SRui Paulo 
202f05cddf9SRui Paulo 	if (eloop.count + 1 > eloop.max_poll_fds) {
203f05cddf9SRui Paulo 		struct pollfd *n;
204f05cddf9SRui Paulo 		int nmax = eloop.count + 1 + 50;
205f05cddf9SRui Paulo 		n = os_realloc_array(eloop.pollfds, nmax,
206f05cddf9SRui Paulo 				     sizeof(struct pollfd));
207f05cddf9SRui Paulo 		if (n == NULL)
208f05cddf9SRui Paulo 			return -1;
209f05cddf9SRui Paulo 
210f05cddf9SRui Paulo 		eloop.max_poll_fds = nmax;
211f05cddf9SRui Paulo 		eloop.pollfds = n;
212f05cddf9SRui Paulo 	}
213f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
214*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
215*5b9c547cSRui Paulo 	if (new_max_sock >= eloop.epoll_max_fd) {
216*5b9c547cSRui Paulo 		next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
217*5b9c547cSRui Paulo 		temp_table = os_realloc_array(eloop.epoll_table, next,
218*5b9c547cSRui Paulo 					      sizeof(struct eloop_sock));
219*5b9c547cSRui Paulo 		if (temp_table == NULL)
220*5b9c547cSRui Paulo 			return -1;
221*5b9c547cSRui Paulo 
222*5b9c547cSRui Paulo 		eloop.epoll_max_fd = next;
223*5b9c547cSRui Paulo 		eloop.epoll_table = temp_table;
224*5b9c547cSRui Paulo 	}
225*5b9c547cSRui Paulo 
226*5b9c547cSRui Paulo 	if (eloop.count + 1 > eloop.epoll_max_event_num) {
227*5b9c547cSRui Paulo 		next = eloop.epoll_max_event_num == 0 ? 8 :
228*5b9c547cSRui Paulo 			eloop.epoll_max_event_num * 2;
229*5b9c547cSRui Paulo 		temp_events = os_realloc_array(eloop.epoll_events, next,
230*5b9c547cSRui Paulo 					       sizeof(struct epoll_event));
231*5b9c547cSRui Paulo 		if (temp_events == NULL) {
232*5b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. "
233*5b9c547cSRui Paulo 				   "%s\n", __func__, strerror(errno));
234*5b9c547cSRui Paulo 			return -1;
235*5b9c547cSRui Paulo 		}
236*5b9c547cSRui Paulo 
237*5b9c547cSRui Paulo 		eloop.epoll_max_event_num = next;
238*5b9c547cSRui Paulo 		eloop.epoll_events = temp_events;
239*5b9c547cSRui Paulo 	}
240*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
241f05cddf9SRui Paulo 
242e28a4053SRui Paulo 	eloop_trace_sock_remove_ref(table);
243f05cddf9SRui Paulo 	tmp = os_realloc_array(table->table, table->count + 1,
244f05cddf9SRui Paulo 			       sizeof(struct eloop_sock));
245*5b9c547cSRui Paulo 	if (tmp == NULL) {
246*5b9c547cSRui Paulo 		eloop_trace_sock_add_ref(table);
24739beb93cSSam Leffler 		return -1;
248*5b9c547cSRui Paulo 	}
24939beb93cSSam Leffler 
25039beb93cSSam Leffler 	tmp[table->count].sock = sock;
25139beb93cSSam Leffler 	tmp[table->count].eloop_data = eloop_data;
25239beb93cSSam Leffler 	tmp[table->count].user_data = user_data;
25339beb93cSSam Leffler 	tmp[table->count].handler = handler;
254e28a4053SRui Paulo 	wpa_trace_record(&tmp[table->count]);
25539beb93cSSam Leffler 	table->count++;
25639beb93cSSam Leffler 	table->table = tmp;
257f05cddf9SRui Paulo 	eloop.max_sock = new_max_sock;
258f05cddf9SRui Paulo 	eloop.count++;
259*5b9c547cSRui Paulo #ifndef CONFIG_ELOOP_EPOLL
26039beb93cSSam Leffler 	table->changed = 1;
261*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
262e28a4053SRui Paulo 	eloop_trace_sock_add_ref(table);
26339beb93cSSam Leffler 
264*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
265*5b9c547cSRui Paulo 	os_memset(&ev, 0, sizeof(ev));
266*5b9c547cSRui Paulo 	switch (table->type) {
267*5b9c547cSRui Paulo 	case EVENT_TYPE_READ:
268*5b9c547cSRui Paulo 		ev.events = EPOLLIN;
269*5b9c547cSRui Paulo 		break;
270*5b9c547cSRui Paulo 	case EVENT_TYPE_WRITE:
271*5b9c547cSRui Paulo 		ev.events = EPOLLOUT;
272*5b9c547cSRui Paulo 		break;
273*5b9c547cSRui Paulo 	/*
274*5b9c547cSRui Paulo 	 * Exceptions are always checked when using epoll, but I suppose it's
275*5b9c547cSRui Paulo 	 * possible that someone registered a socket *only* for exception
276*5b9c547cSRui Paulo 	 * handling.
277*5b9c547cSRui Paulo 	 */
278*5b9c547cSRui Paulo 	case EVENT_TYPE_EXCEPTION:
279*5b9c547cSRui Paulo 		ev.events = EPOLLERR | EPOLLHUP;
280*5b9c547cSRui Paulo 		break;
281*5b9c547cSRui Paulo 	}
282*5b9c547cSRui Paulo 	ev.data.fd = sock;
283*5b9c547cSRui Paulo 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
284*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
285*5b9c547cSRui Paulo 			   "failed. %s\n", __func__, sock, strerror(errno));
286*5b9c547cSRui Paulo 		return -1;
287*5b9c547cSRui Paulo 	}
288*5b9c547cSRui Paulo 	os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
289*5b9c547cSRui Paulo 		  sizeof(struct eloop_sock));
290*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
29139beb93cSSam Leffler 	return 0;
29239beb93cSSam Leffler }
29339beb93cSSam Leffler 
29439beb93cSSam Leffler 
29539beb93cSSam Leffler static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
29639beb93cSSam Leffler                                          int sock)
29739beb93cSSam Leffler {
29839beb93cSSam Leffler 	int i;
29939beb93cSSam Leffler 
30039beb93cSSam Leffler 	if (table == NULL || table->table == NULL || table->count == 0)
30139beb93cSSam Leffler 		return;
30239beb93cSSam Leffler 
30339beb93cSSam Leffler 	for (i = 0; i < table->count; i++) {
30439beb93cSSam Leffler 		if (table->table[i].sock == sock)
30539beb93cSSam Leffler 			break;
30639beb93cSSam Leffler 	}
30739beb93cSSam Leffler 	if (i == table->count)
30839beb93cSSam Leffler 		return;
309e28a4053SRui Paulo 	eloop_trace_sock_remove_ref(table);
31039beb93cSSam Leffler 	if (i != table->count - 1) {
31139beb93cSSam Leffler 		os_memmove(&table->table[i], &table->table[i + 1],
31239beb93cSSam Leffler 			   (table->count - i - 1) *
31339beb93cSSam Leffler 			   sizeof(struct eloop_sock));
31439beb93cSSam Leffler 	}
31539beb93cSSam Leffler 	table->count--;
316f05cddf9SRui Paulo 	eloop.count--;
317*5b9c547cSRui Paulo #ifndef CONFIG_ELOOP_EPOLL
31839beb93cSSam Leffler 	table->changed = 1;
319*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
320e28a4053SRui Paulo 	eloop_trace_sock_add_ref(table);
321*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
322*5b9c547cSRui Paulo 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
323*5b9c547cSRui Paulo 		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
324*5b9c547cSRui Paulo 			   "failed. %s\n", __func__, sock, strerror(errno));
325*5b9c547cSRui Paulo 		return;
326*5b9c547cSRui Paulo 	}
327*5b9c547cSRui Paulo 	os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
328*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
32939beb93cSSam Leffler }
33039beb93cSSam Leffler 
33139beb93cSSam Leffler 
332f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
333f05cddf9SRui Paulo 
334f05cddf9SRui Paulo static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
335f05cddf9SRui Paulo {
336f05cddf9SRui Paulo 	if (fd < mx && fd >= 0)
337f05cddf9SRui Paulo 		return pollfds_map[fd];
338f05cddf9SRui Paulo 	return NULL;
339f05cddf9SRui Paulo }
340f05cddf9SRui Paulo 
341f05cddf9SRui Paulo 
342f05cddf9SRui Paulo static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
343f05cddf9SRui Paulo 				    struct eloop_sock_table *writers,
344f05cddf9SRui Paulo 				    struct eloop_sock_table *exceptions,
345f05cddf9SRui Paulo 				    struct pollfd *pollfds,
346f05cddf9SRui Paulo 				    struct pollfd **pollfds_map,
347f05cddf9SRui Paulo 				    int max_pollfd_map)
348f05cddf9SRui Paulo {
349f05cddf9SRui Paulo 	int i;
350f05cddf9SRui Paulo 	int nxt = 0;
351f05cddf9SRui Paulo 	int fd;
352f05cddf9SRui Paulo 	struct pollfd *pfd;
353f05cddf9SRui Paulo 
354f05cddf9SRui Paulo 	/* Clear pollfd lookup map. It will be re-populated below. */
355f05cddf9SRui Paulo 	os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
356f05cddf9SRui Paulo 
357f05cddf9SRui Paulo 	if (readers && readers->table) {
358f05cddf9SRui Paulo 		for (i = 0; i < readers->count; i++) {
359f05cddf9SRui Paulo 			fd = readers->table[i].sock;
360f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
361f05cddf9SRui Paulo 			pollfds[nxt].fd = fd;
362f05cddf9SRui Paulo 			pollfds[nxt].events = POLLIN;
363f05cddf9SRui Paulo 			pollfds[nxt].revents = 0;
364f05cddf9SRui Paulo 			pollfds_map[fd] = &(pollfds[nxt]);
365f05cddf9SRui Paulo 			nxt++;
366f05cddf9SRui Paulo 		}
367f05cddf9SRui Paulo 	}
368f05cddf9SRui Paulo 
369f05cddf9SRui Paulo 	if (writers && writers->table) {
370f05cddf9SRui Paulo 		for (i = 0; i < writers->count; i++) {
371f05cddf9SRui Paulo 			/*
372f05cddf9SRui Paulo 			 * See if we already added this descriptor, update it
373f05cddf9SRui Paulo 			 * if so.
374f05cddf9SRui Paulo 			 */
375f05cddf9SRui Paulo 			fd = writers->table[i].sock;
376f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
377f05cddf9SRui Paulo 			pfd = pollfds_map[fd];
378f05cddf9SRui Paulo 			if (!pfd) {
379f05cddf9SRui Paulo 				pfd = &(pollfds[nxt]);
380f05cddf9SRui Paulo 				pfd->events = 0;
381f05cddf9SRui Paulo 				pfd->fd = fd;
382f05cddf9SRui Paulo 				pollfds[i].revents = 0;
383f05cddf9SRui Paulo 				pollfds_map[fd] = pfd;
384f05cddf9SRui Paulo 				nxt++;
385f05cddf9SRui Paulo 			}
386f05cddf9SRui Paulo 			pfd->events |= POLLOUT;
387f05cddf9SRui Paulo 		}
388f05cddf9SRui Paulo 	}
389f05cddf9SRui Paulo 
390f05cddf9SRui Paulo 	/*
391f05cddf9SRui Paulo 	 * Exceptions are always checked when using poll, but I suppose it's
392f05cddf9SRui Paulo 	 * possible that someone registered a socket *only* for exception
393f05cddf9SRui Paulo 	 * handling. Set the POLLIN bit in this case.
394f05cddf9SRui Paulo 	 */
395f05cddf9SRui Paulo 	if (exceptions && exceptions->table) {
396f05cddf9SRui Paulo 		for (i = 0; i < exceptions->count; i++) {
397f05cddf9SRui Paulo 			/*
398f05cddf9SRui Paulo 			 * See if we already added this descriptor, just use it
399f05cddf9SRui Paulo 			 * if so.
400f05cddf9SRui Paulo 			 */
401f05cddf9SRui Paulo 			fd = exceptions->table[i].sock;
402f05cddf9SRui Paulo 			assert(fd >= 0 && fd < max_pollfd_map);
403f05cddf9SRui Paulo 			pfd = pollfds_map[fd];
404f05cddf9SRui Paulo 			if (!pfd) {
405f05cddf9SRui Paulo 				pfd = &(pollfds[nxt]);
406f05cddf9SRui Paulo 				pfd->events = POLLIN;
407f05cddf9SRui Paulo 				pfd->fd = fd;
408f05cddf9SRui Paulo 				pollfds[i].revents = 0;
409f05cddf9SRui Paulo 				pollfds_map[fd] = pfd;
410f05cddf9SRui Paulo 				nxt++;
411f05cddf9SRui Paulo 			}
412f05cddf9SRui Paulo 		}
413f05cddf9SRui Paulo 	}
414f05cddf9SRui Paulo 
415f05cddf9SRui Paulo 	return nxt;
416f05cddf9SRui Paulo }
417f05cddf9SRui Paulo 
418f05cddf9SRui Paulo 
419f05cddf9SRui Paulo static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
420f05cddf9SRui Paulo 					   struct pollfd **pollfds_map,
421f05cddf9SRui Paulo 					   int max_pollfd_map,
422f05cddf9SRui Paulo 					   short int revents)
423f05cddf9SRui Paulo {
424f05cddf9SRui Paulo 	int i;
425f05cddf9SRui Paulo 	struct pollfd *pfd;
426f05cddf9SRui Paulo 
427f05cddf9SRui Paulo 	if (!table || !table->table)
428f05cddf9SRui Paulo 		return 0;
429f05cddf9SRui Paulo 
430f05cddf9SRui Paulo 	table->changed = 0;
431f05cddf9SRui Paulo 	for (i = 0; i < table->count; i++) {
432f05cddf9SRui Paulo 		pfd = find_pollfd(pollfds_map, table->table[i].sock,
433f05cddf9SRui Paulo 				  max_pollfd_map);
434f05cddf9SRui Paulo 		if (!pfd)
435f05cddf9SRui Paulo 			continue;
436f05cddf9SRui Paulo 
437f05cddf9SRui Paulo 		if (!(pfd->revents & revents))
438f05cddf9SRui Paulo 			continue;
439f05cddf9SRui Paulo 
440f05cddf9SRui Paulo 		table->table[i].handler(table->table[i].sock,
441f05cddf9SRui Paulo 					table->table[i].eloop_data,
442f05cddf9SRui Paulo 					table->table[i].user_data);
443f05cddf9SRui Paulo 		if (table->changed)
444f05cddf9SRui Paulo 			return 1;
445f05cddf9SRui Paulo 	}
446f05cddf9SRui Paulo 
447f05cddf9SRui Paulo 	return 0;
448f05cddf9SRui Paulo }
449f05cddf9SRui Paulo 
450f05cddf9SRui Paulo 
451f05cddf9SRui Paulo static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
452f05cddf9SRui Paulo 				      struct eloop_sock_table *writers,
453f05cddf9SRui Paulo 				      struct eloop_sock_table *exceptions,
454f05cddf9SRui Paulo 				      struct pollfd **pollfds_map,
455f05cddf9SRui Paulo 				      int max_pollfd_map)
456f05cddf9SRui Paulo {
457f05cddf9SRui Paulo 	if (eloop_sock_table_dispatch_table(readers, pollfds_map,
458f05cddf9SRui Paulo 					    max_pollfd_map, POLLIN | POLLERR |
459f05cddf9SRui Paulo 					    POLLHUP))
460f05cddf9SRui Paulo 		return; /* pollfds may be invalid at this point */
461f05cddf9SRui Paulo 
462f05cddf9SRui Paulo 	if (eloop_sock_table_dispatch_table(writers, pollfds_map,
463f05cddf9SRui Paulo 					    max_pollfd_map, POLLOUT))
464f05cddf9SRui Paulo 		return; /* pollfds may be invalid at this point */
465f05cddf9SRui Paulo 
466f05cddf9SRui Paulo 	eloop_sock_table_dispatch_table(exceptions, pollfds_map,
467f05cddf9SRui Paulo 					max_pollfd_map, POLLERR | POLLHUP);
468f05cddf9SRui Paulo }
469f05cddf9SRui Paulo 
470*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
471*5b9c547cSRui Paulo 
472*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
473f05cddf9SRui Paulo 
47439beb93cSSam Leffler static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
47539beb93cSSam Leffler 				     fd_set *fds)
47639beb93cSSam Leffler {
47739beb93cSSam Leffler 	int i;
47839beb93cSSam Leffler 
47939beb93cSSam Leffler 	FD_ZERO(fds);
48039beb93cSSam Leffler 
48139beb93cSSam Leffler 	if (table->table == NULL)
48239beb93cSSam Leffler 		return;
48339beb93cSSam Leffler 
484*5b9c547cSRui Paulo 	for (i = 0; i < table->count; i++) {
485*5b9c547cSRui Paulo 		assert(table->table[i].sock >= 0);
48639beb93cSSam Leffler 		FD_SET(table->table[i].sock, fds);
48739beb93cSSam Leffler 	}
488*5b9c547cSRui Paulo }
48939beb93cSSam Leffler 
49039beb93cSSam Leffler 
49139beb93cSSam Leffler static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
49239beb93cSSam Leffler 				      fd_set *fds)
49339beb93cSSam Leffler {
49439beb93cSSam Leffler 	int i;
49539beb93cSSam Leffler 
49639beb93cSSam Leffler 	if (table == NULL || table->table == NULL)
49739beb93cSSam Leffler 		return;
49839beb93cSSam Leffler 
49939beb93cSSam Leffler 	table->changed = 0;
50039beb93cSSam Leffler 	for (i = 0; i < table->count; i++) {
50139beb93cSSam Leffler 		if (FD_ISSET(table->table[i].sock, fds)) {
50239beb93cSSam Leffler 			table->table[i].handler(table->table[i].sock,
50339beb93cSSam Leffler 						table->table[i].eloop_data,
50439beb93cSSam Leffler 						table->table[i].user_data);
50539beb93cSSam Leffler 			if (table->changed)
50639beb93cSSam Leffler 				break;
50739beb93cSSam Leffler 		}
50839beb93cSSam Leffler 	}
50939beb93cSSam Leffler }
51039beb93cSSam Leffler 
511*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
512*5b9c547cSRui Paulo 
513*5b9c547cSRui Paulo 
514*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
515*5b9c547cSRui Paulo static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
516*5b9c547cSRui Paulo {
517*5b9c547cSRui Paulo 	struct eloop_sock *table;
518*5b9c547cSRui Paulo 	int i;
519*5b9c547cSRui Paulo 
520*5b9c547cSRui Paulo 	for (i = 0; i < nfds; i++) {
521*5b9c547cSRui Paulo 		table = &eloop.epoll_table[events[i].data.fd];
522*5b9c547cSRui Paulo 		if (table->handler == NULL)
523*5b9c547cSRui Paulo 			continue;
524*5b9c547cSRui Paulo 		table->handler(table->sock, table->eloop_data,
525*5b9c547cSRui Paulo 			       table->user_data);
526*5b9c547cSRui Paulo 	}
527*5b9c547cSRui Paulo }
528*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
529f05cddf9SRui Paulo 
53039beb93cSSam Leffler 
53139beb93cSSam Leffler static void eloop_sock_table_destroy(struct eloop_sock_table *table)
53239beb93cSSam Leffler {
53339beb93cSSam Leffler 	if (table) {
53439beb93cSSam Leffler 		int i;
53539beb93cSSam Leffler 		for (i = 0; i < table->count && table->table; i++) {
536e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
537e28a4053SRui Paulo 				   "sock=%d eloop_data=%p user_data=%p "
538e28a4053SRui Paulo 				   "handler=%p",
53939beb93cSSam Leffler 				   table->table[i].sock,
54039beb93cSSam Leffler 				   table->table[i].eloop_data,
54139beb93cSSam Leffler 				   table->table[i].user_data,
54239beb93cSSam Leffler 				   table->table[i].handler);
543e28a4053SRui Paulo 			wpa_trace_dump_funcname("eloop unregistered socket "
544e28a4053SRui Paulo 						"handler",
545e28a4053SRui Paulo 						table->table[i].handler);
546e28a4053SRui Paulo 			wpa_trace_dump("eloop sock", &table->table[i]);
54739beb93cSSam Leffler 		}
54839beb93cSSam Leffler 		os_free(table->table);
54939beb93cSSam Leffler 	}
55039beb93cSSam Leffler }
55139beb93cSSam Leffler 
55239beb93cSSam Leffler 
55339beb93cSSam Leffler int eloop_register_read_sock(int sock, eloop_sock_handler handler,
55439beb93cSSam Leffler 			     void *eloop_data, void *user_data)
55539beb93cSSam Leffler {
55639beb93cSSam Leffler 	return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
55739beb93cSSam Leffler 				   eloop_data, user_data);
55839beb93cSSam Leffler }
55939beb93cSSam Leffler 
56039beb93cSSam Leffler 
56139beb93cSSam Leffler void eloop_unregister_read_sock(int sock)
56239beb93cSSam Leffler {
56339beb93cSSam Leffler 	eloop_unregister_sock(sock, EVENT_TYPE_READ);
56439beb93cSSam Leffler }
56539beb93cSSam Leffler 
56639beb93cSSam Leffler 
56739beb93cSSam Leffler static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
56839beb93cSSam Leffler {
56939beb93cSSam Leffler 	switch (type) {
57039beb93cSSam Leffler 	case EVENT_TYPE_READ:
57139beb93cSSam Leffler 		return &eloop.readers;
57239beb93cSSam Leffler 	case EVENT_TYPE_WRITE:
57339beb93cSSam Leffler 		return &eloop.writers;
57439beb93cSSam Leffler 	case EVENT_TYPE_EXCEPTION:
57539beb93cSSam Leffler 		return &eloop.exceptions;
57639beb93cSSam Leffler 	}
57739beb93cSSam Leffler 
57839beb93cSSam Leffler 	return NULL;
57939beb93cSSam Leffler }
58039beb93cSSam Leffler 
58139beb93cSSam Leffler 
58239beb93cSSam Leffler int eloop_register_sock(int sock, eloop_event_type type,
58339beb93cSSam Leffler 			eloop_sock_handler handler,
58439beb93cSSam Leffler 			void *eloop_data, void *user_data)
58539beb93cSSam Leffler {
58639beb93cSSam Leffler 	struct eloop_sock_table *table;
58739beb93cSSam Leffler 
588*5b9c547cSRui Paulo 	assert(sock >= 0);
58939beb93cSSam Leffler 	table = eloop_get_sock_table(type);
59039beb93cSSam Leffler 	return eloop_sock_table_add_sock(table, sock, handler,
59139beb93cSSam Leffler 					 eloop_data, user_data);
59239beb93cSSam Leffler }
59339beb93cSSam Leffler 
59439beb93cSSam Leffler 
59539beb93cSSam Leffler void eloop_unregister_sock(int sock, eloop_event_type type)
59639beb93cSSam Leffler {
59739beb93cSSam Leffler 	struct eloop_sock_table *table;
59839beb93cSSam Leffler 
59939beb93cSSam Leffler 	table = eloop_get_sock_table(type);
60039beb93cSSam Leffler 	eloop_sock_table_remove_sock(table, sock);
60139beb93cSSam Leffler }
60239beb93cSSam Leffler 
60339beb93cSSam Leffler 
60439beb93cSSam Leffler int eloop_register_timeout(unsigned int secs, unsigned int usecs,
60539beb93cSSam Leffler 			   eloop_timeout_handler handler,
60639beb93cSSam Leffler 			   void *eloop_data, void *user_data)
60739beb93cSSam Leffler {
608e28a4053SRui Paulo 	struct eloop_timeout *timeout, *tmp;
609f05cddf9SRui Paulo 	os_time_t now_sec;
61039beb93cSSam Leffler 
611e28a4053SRui Paulo 	timeout = os_zalloc(sizeof(*timeout));
61239beb93cSSam Leffler 	if (timeout == NULL)
61339beb93cSSam Leffler 		return -1;
614*5b9c547cSRui Paulo 	if (os_get_reltime(&timeout->time) < 0) {
61539beb93cSSam Leffler 		os_free(timeout);
61639beb93cSSam Leffler 		return -1;
61739beb93cSSam Leffler 	}
618f05cddf9SRui Paulo 	now_sec = timeout->time.sec;
61939beb93cSSam Leffler 	timeout->time.sec += secs;
620f05cddf9SRui Paulo 	if (timeout->time.sec < now_sec) {
621f05cddf9SRui Paulo 		/*
622f05cddf9SRui Paulo 		 * Integer overflow - assume long enough timeout to be assumed
623f05cddf9SRui Paulo 		 * to be infinite, i.e., the timeout would never happen.
624f05cddf9SRui Paulo 		 */
625f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
626f05cddf9SRui Paulo 			   "ever happen - ignore it", secs);
627f05cddf9SRui Paulo 		os_free(timeout);
628f05cddf9SRui Paulo 		return 0;
629f05cddf9SRui Paulo 	}
63039beb93cSSam Leffler 	timeout->time.usec += usecs;
63139beb93cSSam Leffler 	while (timeout->time.usec >= 1000000) {
63239beb93cSSam Leffler 		timeout->time.sec++;
63339beb93cSSam Leffler 		timeout->time.usec -= 1000000;
63439beb93cSSam Leffler 	}
63539beb93cSSam Leffler 	timeout->eloop_data = eloop_data;
63639beb93cSSam Leffler 	timeout->user_data = user_data;
63739beb93cSSam Leffler 	timeout->handler = handler;
638e28a4053SRui Paulo 	wpa_trace_add_ref(timeout, eloop, eloop_data);
639e28a4053SRui Paulo 	wpa_trace_add_ref(timeout, user, user_data);
640e28a4053SRui Paulo 	wpa_trace_record(timeout);
64139beb93cSSam Leffler 
642e28a4053SRui Paulo 	/* Maintain timeouts in order of increasing time */
643e28a4053SRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
644*5b9c547cSRui Paulo 		if (os_reltime_before(&timeout->time, &tmp->time)) {
645e28a4053SRui Paulo 			dl_list_add(tmp->list.prev, &timeout->list);
646e28a4053SRui Paulo 			return 0;
647e28a4053SRui Paulo 		}
648e28a4053SRui Paulo 	}
649e28a4053SRui Paulo 	dl_list_add_tail(&eloop.timeout, &timeout->list);
650e28a4053SRui Paulo 
65139beb93cSSam Leffler 	return 0;
65239beb93cSSam Leffler }
65339beb93cSSam Leffler 
65439beb93cSSam Leffler 
655e28a4053SRui Paulo static void eloop_remove_timeout(struct eloop_timeout *timeout)
656e28a4053SRui Paulo {
657e28a4053SRui Paulo 	dl_list_del(&timeout->list);
658e28a4053SRui Paulo 	wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
659e28a4053SRui Paulo 	wpa_trace_remove_ref(timeout, user, timeout->user_data);
660e28a4053SRui Paulo 	os_free(timeout);
66139beb93cSSam Leffler }
66239beb93cSSam Leffler 
66339beb93cSSam Leffler 
66439beb93cSSam Leffler int eloop_cancel_timeout(eloop_timeout_handler handler,
66539beb93cSSam Leffler 			 void *eloop_data, void *user_data)
66639beb93cSSam Leffler {
667e28a4053SRui Paulo 	struct eloop_timeout *timeout, *prev;
66839beb93cSSam Leffler 	int removed = 0;
66939beb93cSSam Leffler 
670e28a4053SRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
671e28a4053SRui Paulo 			      struct eloop_timeout, list) {
67239beb93cSSam Leffler 		if (timeout->handler == handler &&
67339beb93cSSam Leffler 		    (timeout->eloop_data == eloop_data ||
67439beb93cSSam Leffler 		     eloop_data == ELOOP_ALL_CTX) &&
67539beb93cSSam Leffler 		    (timeout->user_data == user_data ||
67639beb93cSSam Leffler 		     user_data == ELOOP_ALL_CTX)) {
677e28a4053SRui Paulo 			eloop_remove_timeout(timeout);
67839beb93cSSam Leffler 			removed++;
679e28a4053SRui Paulo 		}
68039beb93cSSam Leffler 	}
68139beb93cSSam Leffler 
68239beb93cSSam Leffler 	return removed;
68339beb93cSSam Leffler }
68439beb93cSSam Leffler 
68539beb93cSSam Leffler 
686*5b9c547cSRui Paulo int eloop_cancel_timeout_one(eloop_timeout_handler handler,
687*5b9c547cSRui Paulo 			     void *eloop_data, void *user_data,
688*5b9c547cSRui Paulo 			     struct os_reltime *remaining)
689*5b9c547cSRui Paulo {
690*5b9c547cSRui Paulo 	struct eloop_timeout *timeout, *prev;
691*5b9c547cSRui Paulo 	int removed = 0;
692*5b9c547cSRui Paulo 	struct os_reltime now;
693*5b9c547cSRui Paulo 
694*5b9c547cSRui Paulo 	os_get_reltime(&now);
695*5b9c547cSRui Paulo 	remaining->sec = remaining->usec = 0;
696*5b9c547cSRui Paulo 
697*5b9c547cSRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
698*5b9c547cSRui Paulo 			      struct eloop_timeout, list) {
699*5b9c547cSRui Paulo 		if (timeout->handler == handler &&
700*5b9c547cSRui Paulo 		    (timeout->eloop_data == eloop_data) &&
701*5b9c547cSRui Paulo 		    (timeout->user_data == user_data)) {
702*5b9c547cSRui Paulo 			removed = 1;
703*5b9c547cSRui Paulo 			if (os_reltime_before(&now, &timeout->time))
704*5b9c547cSRui Paulo 				os_reltime_sub(&timeout->time, &now, remaining);
705*5b9c547cSRui Paulo 			eloop_remove_timeout(timeout);
706*5b9c547cSRui Paulo 			break;
707*5b9c547cSRui Paulo 		}
708*5b9c547cSRui Paulo 	}
709*5b9c547cSRui Paulo 	return removed;
710*5b9c547cSRui Paulo }
711*5b9c547cSRui Paulo 
712*5b9c547cSRui Paulo 
71339beb93cSSam Leffler int eloop_is_timeout_registered(eloop_timeout_handler handler,
71439beb93cSSam Leffler 				void *eloop_data, void *user_data)
71539beb93cSSam Leffler {
71639beb93cSSam Leffler 	struct eloop_timeout *tmp;
71739beb93cSSam Leffler 
718e28a4053SRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
71939beb93cSSam Leffler 		if (tmp->handler == handler &&
72039beb93cSSam Leffler 		    tmp->eloop_data == eloop_data &&
72139beb93cSSam Leffler 		    tmp->user_data == user_data)
72239beb93cSSam Leffler 			return 1;
72339beb93cSSam Leffler 	}
72439beb93cSSam Leffler 
72539beb93cSSam Leffler 	return 0;
72639beb93cSSam Leffler }
72739beb93cSSam Leffler 
72839beb93cSSam Leffler 
729*5b9c547cSRui Paulo int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
730*5b9c547cSRui Paulo 			  eloop_timeout_handler handler, void *eloop_data,
731*5b9c547cSRui Paulo 			  void *user_data)
732*5b9c547cSRui Paulo {
733*5b9c547cSRui Paulo 	struct os_reltime now, requested, remaining;
734*5b9c547cSRui Paulo 	struct eloop_timeout *tmp;
735*5b9c547cSRui Paulo 
736*5b9c547cSRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
737*5b9c547cSRui Paulo 		if (tmp->handler == handler &&
738*5b9c547cSRui Paulo 		    tmp->eloop_data == eloop_data &&
739*5b9c547cSRui Paulo 		    tmp->user_data == user_data) {
740*5b9c547cSRui Paulo 			requested.sec = req_secs;
741*5b9c547cSRui Paulo 			requested.usec = req_usecs;
742*5b9c547cSRui Paulo 			os_get_reltime(&now);
743*5b9c547cSRui Paulo 			os_reltime_sub(&tmp->time, &now, &remaining);
744*5b9c547cSRui Paulo 			if (os_reltime_before(&requested, &remaining)) {
745*5b9c547cSRui Paulo 				eloop_cancel_timeout(handler, eloop_data,
746*5b9c547cSRui Paulo 						     user_data);
747*5b9c547cSRui Paulo 				eloop_register_timeout(requested.sec,
748*5b9c547cSRui Paulo 						       requested.usec,
749*5b9c547cSRui Paulo 						       handler, eloop_data,
750*5b9c547cSRui Paulo 						       user_data);
751*5b9c547cSRui Paulo 				return 1;
752*5b9c547cSRui Paulo 			}
753*5b9c547cSRui Paulo 			return 0;
754*5b9c547cSRui Paulo 		}
755*5b9c547cSRui Paulo 	}
756*5b9c547cSRui Paulo 
757*5b9c547cSRui Paulo 	return -1;
758*5b9c547cSRui Paulo }
759*5b9c547cSRui Paulo 
760*5b9c547cSRui Paulo 
761*5b9c547cSRui Paulo int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
762*5b9c547cSRui Paulo 			    eloop_timeout_handler handler, void *eloop_data,
763*5b9c547cSRui Paulo 			    void *user_data)
764*5b9c547cSRui Paulo {
765*5b9c547cSRui Paulo 	struct os_reltime now, requested, remaining;
766*5b9c547cSRui Paulo 	struct eloop_timeout *tmp;
767*5b9c547cSRui Paulo 
768*5b9c547cSRui Paulo 	dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
769*5b9c547cSRui Paulo 		if (tmp->handler == handler &&
770*5b9c547cSRui Paulo 		    tmp->eloop_data == eloop_data &&
771*5b9c547cSRui Paulo 		    tmp->user_data == user_data) {
772*5b9c547cSRui Paulo 			requested.sec = req_secs;
773*5b9c547cSRui Paulo 			requested.usec = req_usecs;
774*5b9c547cSRui Paulo 			os_get_reltime(&now);
775*5b9c547cSRui Paulo 			os_reltime_sub(&tmp->time, &now, &remaining);
776*5b9c547cSRui Paulo 			if (os_reltime_before(&remaining, &requested)) {
777*5b9c547cSRui Paulo 				eloop_cancel_timeout(handler, eloop_data,
778*5b9c547cSRui Paulo 						     user_data);
779*5b9c547cSRui Paulo 				eloop_register_timeout(requested.sec,
780*5b9c547cSRui Paulo 						       requested.usec,
781*5b9c547cSRui Paulo 						       handler, eloop_data,
782*5b9c547cSRui Paulo 						       user_data);
783*5b9c547cSRui Paulo 				return 1;
784*5b9c547cSRui Paulo 			}
785*5b9c547cSRui Paulo 			return 0;
786*5b9c547cSRui Paulo 		}
787*5b9c547cSRui Paulo 	}
788*5b9c547cSRui Paulo 
789*5b9c547cSRui Paulo 	return -1;
790*5b9c547cSRui Paulo }
791*5b9c547cSRui Paulo 
792*5b9c547cSRui Paulo 
79339beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
79439beb93cSSam Leffler static void eloop_handle_alarm(int sig)
79539beb93cSSam Leffler {
796e28a4053SRui Paulo 	wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
797e28a4053SRui Paulo 		   "two seconds. Looks like there\n"
79839beb93cSSam Leffler 		   "is a bug that ends up in a busy loop that "
79939beb93cSSam Leffler 		   "prevents clean shutdown.\n"
80039beb93cSSam Leffler 		   "Killing program forcefully.\n");
80139beb93cSSam Leffler 	exit(1);
80239beb93cSSam Leffler }
80339beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
80439beb93cSSam Leffler 
80539beb93cSSam Leffler 
80639beb93cSSam Leffler static void eloop_handle_signal(int sig)
80739beb93cSSam Leffler {
80839beb93cSSam Leffler 	int i;
80939beb93cSSam Leffler 
81039beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
81139beb93cSSam Leffler 	if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
81239beb93cSSam Leffler 		/* Use SIGALRM to break out from potential busy loops that
81339beb93cSSam Leffler 		 * would not allow the program to be killed. */
81439beb93cSSam Leffler 		eloop.pending_terminate = 1;
81539beb93cSSam Leffler 		signal(SIGALRM, eloop_handle_alarm);
81639beb93cSSam Leffler 		alarm(2);
81739beb93cSSam Leffler 	}
81839beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
81939beb93cSSam Leffler 
82039beb93cSSam Leffler 	eloop.signaled++;
82139beb93cSSam Leffler 	for (i = 0; i < eloop.signal_count; i++) {
82239beb93cSSam Leffler 		if (eloop.signals[i].sig == sig) {
82339beb93cSSam Leffler 			eloop.signals[i].signaled++;
82439beb93cSSam Leffler 			break;
82539beb93cSSam Leffler 		}
82639beb93cSSam Leffler 	}
82739beb93cSSam Leffler }
82839beb93cSSam Leffler 
82939beb93cSSam Leffler 
83039beb93cSSam Leffler static void eloop_process_pending_signals(void)
83139beb93cSSam Leffler {
83239beb93cSSam Leffler 	int i;
83339beb93cSSam Leffler 
83439beb93cSSam Leffler 	if (eloop.signaled == 0)
83539beb93cSSam Leffler 		return;
83639beb93cSSam Leffler 	eloop.signaled = 0;
83739beb93cSSam Leffler 
83839beb93cSSam Leffler 	if (eloop.pending_terminate) {
83939beb93cSSam Leffler #ifndef CONFIG_NATIVE_WINDOWS
84039beb93cSSam Leffler 		alarm(0);
84139beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
84239beb93cSSam Leffler 		eloop.pending_terminate = 0;
84339beb93cSSam Leffler 	}
84439beb93cSSam Leffler 
84539beb93cSSam Leffler 	for (i = 0; i < eloop.signal_count; i++) {
84639beb93cSSam Leffler 		if (eloop.signals[i].signaled) {
84739beb93cSSam Leffler 			eloop.signals[i].signaled = 0;
84839beb93cSSam Leffler 			eloop.signals[i].handler(eloop.signals[i].sig,
84939beb93cSSam Leffler 						 eloop.signals[i].user_data);
85039beb93cSSam Leffler 		}
85139beb93cSSam Leffler 	}
85239beb93cSSam Leffler }
85339beb93cSSam Leffler 
85439beb93cSSam Leffler 
85539beb93cSSam Leffler int eloop_register_signal(int sig, eloop_signal_handler handler,
85639beb93cSSam Leffler 			  void *user_data)
85739beb93cSSam Leffler {
85839beb93cSSam Leffler 	struct eloop_signal *tmp;
85939beb93cSSam Leffler 
860f05cddf9SRui Paulo 	tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
86139beb93cSSam Leffler 			       sizeof(struct eloop_signal));
86239beb93cSSam Leffler 	if (tmp == NULL)
86339beb93cSSam Leffler 		return -1;
86439beb93cSSam Leffler 
86539beb93cSSam Leffler 	tmp[eloop.signal_count].sig = sig;
86639beb93cSSam Leffler 	tmp[eloop.signal_count].user_data = user_data;
86739beb93cSSam Leffler 	tmp[eloop.signal_count].handler = handler;
86839beb93cSSam Leffler 	tmp[eloop.signal_count].signaled = 0;
86939beb93cSSam Leffler 	eloop.signal_count++;
87039beb93cSSam Leffler 	eloop.signals = tmp;
87139beb93cSSam Leffler 	signal(sig, eloop_handle_signal);
87239beb93cSSam Leffler 
87339beb93cSSam Leffler 	return 0;
87439beb93cSSam Leffler }
87539beb93cSSam Leffler 
87639beb93cSSam Leffler 
87739beb93cSSam Leffler int eloop_register_signal_terminate(eloop_signal_handler handler,
87839beb93cSSam Leffler 				    void *user_data)
87939beb93cSSam Leffler {
88039beb93cSSam Leffler 	int ret = eloop_register_signal(SIGINT, handler, user_data);
88139beb93cSSam Leffler 	if (ret == 0)
88239beb93cSSam Leffler 		ret = eloop_register_signal(SIGTERM, handler, user_data);
88339beb93cSSam Leffler 	return ret;
88439beb93cSSam Leffler }
88539beb93cSSam Leffler 
88639beb93cSSam Leffler 
88739beb93cSSam Leffler int eloop_register_signal_reconfig(eloop_signal_handler handler,
88839beb93cSSam Leffler 				   void *user_data)
88939beb93cSSam Leffler {
89039beb93cSSam Leffler #ifdef CONFIG_NATIVE_WINDOWS
89139beb93cSSam Leffler 	return 0;
89239beb93cSSam Leffler #else /* CONFIG_NATIVE_WINDOWS */
89339beb93cSSam Leffler 	return eloop_register_signal(SIGHUP, handler, user_data);
89439beb93cSSam Leffler #endif /* CONFIG_NATIVE_WINDOWS */
89539beb93cSSam Leffler }
89639beb93cSSam Leffler 
89739beb93cSSam Leffler 
89839beb93cSSam Leffler void eloop_run(void)
89939beb93cSSam Leffler {
900f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
901f05cddf9SRui Paulo 	int num_poll_fds;
902f05cddf9SRui Paulo 	int timeout_ms = 0;
903*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
904*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
90539beb93cSSam Leffler 	fd_set *rfds, *wfds, *efds;
90639beb93cSSam Leffler 	struct timeval _tv;
907*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
908*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
909*5b9c547cSRui Paulo 	int timeout_ms = -1;
910*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
911f05cddf9SRui Paulo 	int res;
912*5b9c547cSRui Paulo 	struct os_reltime tv, now;
91339beb93cSSam Leffler 
914*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
91539beb93cSSam Leffler 	rfds = os_malloc(sizeof(*rfds));
91639beb93cSSam Leffler 	wfds = os_malloc(sizeof(*wfds));
91739beb93cSSam Leffler 	efds = os_malloc(sizeof(*efds));
918e28a4053SRui Paulo 	if (rfds == NULL || wfds == NULL || efds == NULL)
91939beb93cSSam Leffler 		goto out;
920*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
92139beb93cSSam Leffler 
92239beb93cSSam Leffler 	while (!eloop.terminate &&
923e28a4053SRui Paulo 	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
92439beb93cSSam Leffler 		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
925e28a4053SRui Paulo 		struct eloop_timeout *timeout;
926e28a4053SRui Paulo 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
927e28a4053SRui Paulo 					list);
928e28a4053SRui Paulo 		if (timeout) {
929*5b9c547cSRui Paulo 			os_get_reltime(&now);
930*5b9c547cSRui Paulo 			if (os_reltime_before(&now, &timeout->time))
931*5b9c547cSRui Paulo 				os_reltime_sub(&timeout->time, &now, &tv);
93239beb93cSSam Leffler 			else
93339beb93cSSam Leffler 				tv.sec = tv.usec = 0;
934*5b9c547cSRui Paulo #if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
935f05cddf9SRui Paulo 			timeout_ms = tv.sec * 1000 + tv.usec / 1000;
936*5b9c547cSRui Paulo #endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
937*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
93839beb93cSSam Leffler 			_tv.tv_sec = tv.sec;
93939beb93cSSam Leffler 			_tv.tv_usec = tv.usec;
940*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
94139beb93cSSam Leffler 		}
94239beb93cSSam Leffler 
943f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
944f05cddf9SRui Paulo 		num_poll_fds = eloop_sock_table_set_fds(
945f05cddf9SRui Paulo 			&eloop.readers, &eloop.writers, &eloop.exceptions,
946f05cddf9SRui Paulo 			eloop.pollfds, eloop.pollfds_map,
947f05cddf9SRui Paulo 			eloop.max_pollfd_map);
948f05cddf9SRui Paulo 		res = poll(eloop.pollfds, num_poll_fds,
949f05cddf9SRui Paulo 			   timeout ? timeout_ms : -1);
950*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
951*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
95239beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.readers, rfds);
95339beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.writers, wfds);
95439beb93cSSam Leffler 		eloop_sock_table_set_fds(&eloop.exceptions, efds);
95539beb93cSSam Leffler 		res = select(eloop.max_sock + 1, rfds, wfds, efds,
956e28a4053SRui Paulo 			     timeout ? &_tv : NULL);
957*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
958*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
959*5b9c547cSRui Paulo 		if (eloop.count == 0) {
960*5b9c547cSRui Paulo 			res = 0;
961*5b9c547cSRui Paulo 		} else {
962*5b9c547cSRui Paulo 			res = epoll_wait(eloop.epollfd, eloop.epoll_events,
963*5b9c547cSRui Paulo 					 eloop.count, timeout_ms);
964*5b9c547cSRui Paulo 		}
965*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
96639beb93cSSam Leffler 		if (res < 0 && errno != EINTR && errno != 0) {
967*5b9c547cSRui Paulo 			wpa_printf(MSG_ERROR, "eloop: %s: %s",
968*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_POLL
969*5b9c547cSRui Paulo 				   "poll"
970*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
971*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
972*5b9c547cSRui Paulo 				   "select"
973*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
974*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
975*5b9c547cSRui Paulo 				   "epoll"
976*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
977*5b9c547cSRui Paulo 				   , strerror(errno));
97839beb93cSSam Leffler 			goto out;
97939beb93cSSam Leffler 		}
98039beb93cSSam Leffler 		eloop_process_pending_signals();
98139beb93cSSam Leffler 
98239beb93cSSam Leffler 		/* check if some registered timeouts have occurred */
983e28a4053SRui Paulo 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
984e28a4053SRui Paulo 					list);
985e28a4053SRui Paulo 		if (timeout) {
986*5b9c547cSRui Paulo 			os_get_reltime(&now);
987*5b9c547cSRui Paulo 			if (!os_reltime_before(&now, &timeout->time)) {
988e28a4053SRui Paulo 				void *eloop_data = timeout->eloop_data;
989e28a4053SRui Paulo 				void *user_data = timeout->user_data;
990e28a4053SRui Paulo 				eloop_timeout_handler handler =
991e28a4053SRui Paulo 					timeout->handler;
992e28a4053SRui Paulo 				eloop_remove_timeout(timeout);
993e28a4053SRui Paulo 				handler(eloop_data, user_data);
99439beb93cSSam Leffler 			}
99539beb93cSSam Leffler 
99639beb93cSSam Leffler 		}
99739beb93cSSam Leffler 
99839beb93cSSam Leffler 		if (res <= 0)
99939beb93cSSam Leffler 			continue;
100039beb93cSSam Leffler 
1001f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1002f05cddf9SRui Paulo 		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
1003f05cddf9SRui Paulo 					  &eloop.exceptions, eloop.pollfds_map,
1004f05cddf9SRui Paulo 					  eloop.max_pollfd_map);
1005*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
1006*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
100739beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.readers, rfds);
100839beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.writers, wfds);
100939beb93cSSam Leffler 		eloop_sock_table_dispatch(&eloop.exceptions, efds);
1010*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1011*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
1012*5b9c547cSRui Paulo 		eloop_sock_table_dispatch(eloop.epoll_events, res);
1013*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
101439beb93cSSam Leffler 	}
101539beb93cSSam Leffler 
1016*5b9c547cSRui Paulo 	eloop.terminate = 0;
101739beb93cSSam Leffler out:
1018*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
101939beb93cSSam Leffler 	os_free(rfds);
102039beb93cSSam Leffler 	os_free(wfds);
102139beb93cSSam Leffler 	os_free(efds);
1022*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1023f05cddf9SRui Paulo 	return;
102439beb93cSSam Leffler }
102539beb93cSSam Leffler 
102639beb93cSSam Leffler 
102739beb93cSSam Leffler void eloop_terminate(void)
102839beb93cSSam Leffler {
102939beb93cSSam Leffler 	eloop.terminate = 1;
103039beb93cSSam Leffler }
103139beb93cSSam Leffler 
103239beb93cSSam Leffler 
103339beb93cSSam Leffler void eloop_destroy(void)
103439beb93cSSam Leffler {
103539beb93cSSam Leffler 	struct eloop_timeout *timeout, *prev;
1036*5b9c547cSRui Paulo 	struct os_reltime now;
103739beb93cSSam Leffler 
1038*5b9c547cSRui Paulo 	os_get_reltime(&now);
1039e28a4053SRui Paulo 	dl_list_for_each_safe(timeout, prev, &eloop.timeout,
1040e28a4053SRui Paulo 			      struct eloop_timeout, list) {
104139beb93cSSam Leffler 		int sec, usec;
1042e28a4053SRui Paulo 		sec = timeout->time.sec - now.sec;
1043e28a4053SRui Paulo 		usec = timeout->time.usec - now.usec;
1044e28a4053SRui Paulo 		if (timeout->time.usec < now.usec) {
104539beb93cSSam Leffler 			sec--;
104639beb93cSSam Leffler 			usec += 1000000;
104739beb93cSSam Leffler 		}
1048e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
1049e28a4053SRui Paulo 			   "eloop_data=%p user_data=%p handler=%p",
1050e28a4053SRui Paulo 			   sec, usec, timeout->eloop_data, timeout->user_data,
1051e28a4053SRui Paulo 			   timeout->handler);
1052e28a4053SRui Paulo 		wpa_trace_dump_funcname("eloop unregistered timeout handler",
1053e28a4053SRui Paulo 					timeout->handler);
1054e28a4053SRui Paulo 		wpa_trace_dump("eloop timeout", timeout);
1055e28a4053SRui Paulo 		eloop_remove_timeout(timeout);
105639beb93cSSam Leffler 	}
105739beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.readers);
105839beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.writers);
105939beb93cSSam Leffler 	eloop_sock_table_destroy(&eloop.exceptions);
106039beb93cSSam Leffler 	os_free(eloop.signals);
1061f05cddf9SRui Paulo 
1062f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1063f05cddf9SRui Paulo 	os_free(eloop.pollfds);
1064f05cddf9SRui Paulo 	os_free(eloop.pollfds_map);
1065f05cddf9SRui Paulo #endif /* CONFIG_ELOOP_POLL */
1066*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_EPOLL
1067*5b9c547cSRui Paulo 	os_free(eloop.epoll_table);
1068*5b9c547cSRui Paulo 	os_free(eloop.epoll_events);
1069*5b9c547cSRui Paulo 	close(eloop.epollfd);
1070*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_EPOLL */
107139beb93cSSam Leffler }
107239beb93cSSam Leffler 
107339beb93cSSam Leffler 
107439beb93cSSam Leffler int eloop_terminated(void)
107539beb93cSSam Leffler {
107639beb93cSSam Leffler 	return eloop.terminate;
107739beb93cSSam Leffler }
107839beb93cSSam Leffler 
107939beb93cSSam Leffler 
108039beb93cSSam Leffler void eloop_wait_for_read_sock(int sock)
108139beb93cSSam Leffler {
1082f05cddf9SRui Paulo #ifdef CONFIG_ELOOP_POLL
1083f05cddf9SRui Paulo 	struct pollfd pfd;
1084f05cddf9SRui Paulo 
1085f05cddf9SRui Paulo 	if (sock < 0)
1086f05cddf9SRui Paulo 		return;
1087f05cddf9SRui Paulo 
1088f05cddf9SRui Paulo 	os_memset(&pfd, 0, sizeof(pfd));
1089f05cddf9SRui Paulo 	pfd.fd = sock;
1090f05cddf9SRui Paulo 	pfd.events = POLLIN;
1091f05cddf9SRui Paulo 
1092f05cddf9SRui Paulo 	poll(&pfd, 1, -1);
1093*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_POLL */
1094*5b9c547cSRui Paulo #if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
1095*5b9c547cSRui Paulo 	/*
1096*5b9c547cSRui Paulo 	 * We can use epoll() here. But epoll() requres 4 system calls.
1097*5b9c547cSRui Paulo 	 * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
1098*5b9c547cSRui Paulo 	 * epoll fd. So select() is better for performance here.
1099*5b9c547cSRui Paulo 	 */
110039beb93cSSam Leffler 	fd_set rfds;
110139beb93cSSam Leffler 
110239beb93cSSam Leffler 	if (sock < 0)
110339beb93cSSam Leffler 		return;
110439beb93cSSam Leffler 
110539beb93cSSam Leffler 	FD_ZERO(&rfds);
110639beb93cSSam Leffler 	FD_SET(sock, &rfds);
110739beb93cSSam Leffler 	select(sock + 1, &rfds, NULL, NULL, NULL);
1108*5b9c547cSRui Paulo #endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
110939beb93cSSam Leffler }
1110*5b9c547cSRui Paulo 
1111*5b9c547cSRui Paulo #ifdef CONFIG_ELOOP_SELECT
1112*5b9c547cSRui Paulo #undef CONFIG_ELOOP_SELECT
1113*5b9c547cSRui Paulo #endif /* CONFIG_ELOOP_SELECT */
1114