123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- /*
- * Event loop based on select() loop
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
- #include "includes.h"
- #include "common.h"
- #include "trace.h"
- #include "list.h"
- #include "eloop.h"
- struct eloop_sock {
- int sock;
- void *eloop_data;
- void *user_data;
- eloop_sock_handler handler;
- WPA_TRACE_REF(eloop);
- WPA_TRACE_REF(user);
- WPA_TRACE_INFO
- };
- struct eloop_timeout {
- struct dl_list list;
- struct os_time time;
- void *eloop_data;
- void *user_data;
- eloop_timeout_handler handler;
- WPA_TRACE_REF(eloop);
- WPA_TRACE_REF(user);
- WPA_TRACE_INFO
- };
- struct eloop_signal {
- int sig;
- void *user_data;
- eloop_signal_handler handler;
- int signaled;
- };
- struct eloop_sock_table {
- int count;
- struct eloop_sock *table;
- int changed;
- };
- struct eloop_data {
- int max_sock;
- struct eloop_sock_table readers;
- struct eloop_sock_table writers;
- struct eloop_sock_table exceptions;
- struct dl_list timeout;
- int signal_count;
- struct eloop_signal *signals;
- int signaled;
- int pending_terminate;
- int terminate;
- int reader_table_changed;
- };
- static struct eloop_data eloop;
- #ifdef WPA_TRACE
- static void eloop_sigsegv_handler(int sig)
- {
- wpa_trace_show("eloop SIGSEGV");
- abort();
- }
- static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
- {
- int i;
- if (table == NULL || table->table == NULL)
- return;
- for (i = 0; i < table->count; i++) {
- wpa_trace_add_ref(&table->table[i], eloop,
- table->table[i].eloop_data);
- wpa_trace_add_ref(&table->table[i], user,
- table->table[i].user_data);
- }
- }
- static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
- {
- int i;
- if (table == NULL || table->table == NULL)
- return;
- for (i = 0; i < table->count; i++) {
- wpa_trace_remove_ref(&table->table[i], eloop,
- table->table[i].eloop_data);
- wpa_trace_remove_ref(&table->table[i], user,
- table->table[i].user_data);
- }
- }
- #else /* WPA_TRACE */
- #define eloop_trace_sock_add_ref(table) do { } while (0)
- #define eloop_trace_sock_remove_ref(table) do { } while (0)
- #endif /* WPA_TRACE */
- int eloop_init(void)
- {
- os_memset(&eloop, 0, sizeof(eloop));
- dl_list_init(&eloop.timeout);
- #ifdef WPA_TRACE
- signal(SIGSEGV, eloop_sigsegv_handler);
- #endif /* WPA_TRACE */
- return 0;
- }
- static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
- int sock, eloop_sock_handler handler,
- void *eloop_data, void *user_data)
- {
- struct eloop_sock *tmp;
- if (table == NULL)
- return -1;
- eloop_trace_sock_remove_ref(table);
- tmp = (struct eloop_sock *)
- os_realloc(table->table,
- (table->count + 1) * sizeof(struct eloop_sock));
- if (tmp == NULL)
- return -1;
- tmp[table->count].sock = sock;
- tmp[table->count].eloop_data = eloop_data;
- tmp[table->count].user_data = user_data;
- tmp[table->count].handler = handler;
- wpa_trace_record(&tmp[table->count]);
- table->count++;
- table->table = tmp;
- if (sock > eloop.max_sock)
- eloop.max_sock = sock;
- table->changed = 1;
- eloop_trace_sock_add_ref(table);
- return 0;
- }
- static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
- int sock)
- {
- int i;
- if (table == NULL || table->table == NULL || table->count == 0)
- return;
- for (i = 0; i < table->count; i++) {
- if (table->table[i].sock == sock)
- break;
- }
- if (i == table->count)
- return;
- eloop_trace_sock_remove_ref(table);
- if (i != table->count - 1) {
- os_memmove(&table->table[i], &table->table[i + 1],
- (table->count - i - 1) *
- sizeof(struct eloop_sock));
- }
- table->count--;
- table->changed = 1;
- eloop_trace_sock_add_ref(table);
- }
- static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
- fd_set *fds)
- {
- int i;
- FD_ZERO(fds);
- if (table->table == NULL)
- return;
- for (i = 0; i < table->count; i++)
- FD_SET(table->table[i].sock, fds);
- }
- static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
- fd_set *fds)
- {
- int i;
- if (table == NULL || table->table == NULL)
- return;
- table->changed = 0;
- for (i = 0; i < table->count; i++) {
- if (FD_ISSET(table->table[i].sock, fds)) {
- table->table[i].handler(table->table[i].sock,
- table->table[i].eloop_data,
- table->table[i].user_data);
- if (table->changed)
- break;
- }
- }
- }
- static void eloop_sock_table_destroy(struct eloop_sock_table *table)
- {
- if (table) {
- int i;
- for (i = 0; i < table->count && table->table; i++) {
- wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
- "sock=%d eloop_data=%p user_data=%p "
- "handler=%p",
- table->table[i].sock,
- table->table[i].eloop_data,
- table->table[i].user_data,
- table->table[i].handler);
- wpa_trace_dump_funcname("eloop unregistered socket "
- "handler",
- table->table[i].handler);
- wpa_trace_dump("eloop sock", &table->table[i]);
- }
- os_free(table->table);
- }
- }
- int eloop_register_read_sock(int sock, eloop_sock_handler handler,
- void *eloop_data, void *user_data)
- {
- return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
- eloop_data, user_data);
- }
- void eloop_unregister_read_sock(int sock)
- {
- eloop_unregister_sock(sock, EVENT_TYPE_READ);
- }
- static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
- {
- switch (type) {
- case EVENT_TYPE_READ:
- return &eloop.readers;
- case EVENT_TYPE_WRITE:
- return &eloop.writers;
- case EVENT_TYPE_EXCEPTION:
- return &eloop.exceptions;
- }
- return NULL;
- }
- int eloop_register_sock(int sock, eloop_event_type type,
- eloop_sock_handler handler,
- void *eloop_data, void *user_data)
- {
- struct eloop_sock_table *table;
- table = eloop_get_sock_table(type);
- return eloop_sock_table_add_sock(table, sock, handler,
- eloop_data, user_data);
- }
- void eloop_unregister_sock(int sock, eloop_event_type type)
- {
- struct eloop_sock_table *table;
- table = eloop_get_sock_table(type);
- eloop_sock_table_remove_sock(table, sock);
- }
- int eloop_register_timeout(unsigned int secs, unsigned int usecs,
- eloop_timeout_handler handler,
- void *eloop_data, void *user_data)
- {
- struct eloop_timeout *timeout, *tmp;
- timeout = os_malloc(sizeof(*timeout));
- if (timeout == NULL)
- return -1;
- if (os_get_time(&timeout->time) < 0) {
- os_free(timeout);
- return -1;
- }
- timeout->time.sec += secs;
- timeout->time.usec += usecs;
- while (timeout->time.usec >= 1000000) {
- timeout->time.sec++;
- timeout->time.usec -= 1000000;
- }
- timeout->eloop_data = eloop_data;
- timeout->user_data = user_data;
- timeout->handler = handler;
- wpa_trace_add_ref(timeout, eloop, eloop_data);
- wpa_trace_add_ref(timeout, user, user_data);
- wpa_trace_record(timeout);
- /* Maintain timeouts in order of increasing time */
- dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
- if (os_time_before(&timeout->time, &tmp->time)) {
- dl_list_add(tmp->list.prev, &timeout->list);
- return 0;
- }
- }
- dl_list_add_tail(&eloop.timeout, &timeout->list);
- return 0;
- }
- static void eloop_remove_timeout(struct eloop_timeout *timeout)
- {
- dl_list_del(&timeout->list);
- wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
- wpa_trace_remove_ref(timeout, user, timeout->user_data);
- os_free(timeout);
- }
- int eloop_cancel_timeout(eloop_timeout_handler handler,
- void *eloop_data, void *user_data)
- {
- struct eloop_timeout *timeout, *prev;
- int removed = 0;
- dl_list_for_each_safe(timeout, prev, &eloop.timeout,
- struct eloop_timeout, list) {
- if (timeout->handler == handler &&
- (timeout->eloop_data == eloop_data ||
- eloop_data == ELOOP_ALL_CTX) &&
- (timeout->user_data == user_data ||
- user_data == ELOOP_ALL_CTX)) {
- eloop_remove_timeout(timeout);
- removed++;
- }
- }
- return removed;
- }
- int eloop_is_timeout_registered(eloop_timeout_handler handler,
- void *eloop_data, void *user_data)
- {
- struct eloop_timeout *tmp;
- dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
- if (tmp->handler == handler &&
- tmp->eloop_data == eloop_data &&
- tmp->user_data == user_data)
- return 1;
- }
- return 0;
- }
- #ifndef CONFIG_NATIVE_WINDOWS
- static void eloop_handle_alarm(int sig)
- {
- wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
- "two seconds. Looks like there\n"
- "is a bug that ends up in a busy loop that "
- "prevents clean shutdown.\n"
- "Killing program forcefully.\n");
- exit(1);
- }
- #endif /* CONFIG_NATIVE_WINDOWS */
- static void eloop_handle_signal(int sig)
- {
- int i;
- #ifndef CONFIG_NATIVE_WINDOWS
- if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
- /* Use SIGALRM to break out from potential busy loops that
- * would not allow the program to be killed. */
- eloop.pending_terminate = 1;
- signal(SIGALRM, eloop_handle_alarm);
- alarm(2);
- }
- #endif /* CONFIG_NATIVE_WINDOWS */
- eloop.signaled++;
- for (i = 0; i < eloop.signal_count; i++) {
- if (eloop.signals[i].sig == sig) {
- eloop.signals[i].signaled++;
- break;
- }
- }
- }
- static void eloop_process_pending_signals(void)
- {
- int i;
- if (eloop.signaled == 0)
- return;
- eloop.signaled = 0;
- if (eloop.pending_terminate) {
- #ifndef CONFIG_NATIVE_WINDOWS
- alarm(0);
- #endif /* CONFIG_NATIVE_WINDOWS */
- eloop.pending_terminate = 0;
- }
- for (i = 0; i < eloop.signal_count; i++) {
- if (eloop.signals[i].signaled) {
- eloop.signals[i].signaled = 0;
- eloop.signals[i].handler(eloop.signals[i].sig,
- eloop.signals[i].user_data);
- }
- }
- }
- int eloop_register_signal(int sig, eloop_signal_handler handler,
- void *user_data)
- {
- struct eloop_signal *tmp;
- tmp = (struct eloop_signal *)
- os_realloc(eloop.signals,
- (eloop.signal_count + 1) *
- sizeof(struct eloop_signal));
- if (tmp == NULL)
- return -1;
- tmp[eloop.signal_count].sig = sig;
- tmp[eloop.signal_count].user_data = user_data;
- tmp[eloop.signal_count].handler = handler;
- tmp[eloop.signal_count].signaled = 0;
- eloop.signal_count++;
- eloop.signals = tmp;
- signal(sig, eloop_handle_signal);
- return 0;
- }
- int eloop_register_signal_terminate(eloop_signal_handler handler,
- void *user_data)
- {
- int ret = eloop_register_signal(SIGINT, handler, user_data);
- if (ret == 0)
- ret = eloop_register_signal(SIGTERM, handler, user_data);
- return ret;
- }
- int eloop_register_signal_reconfig(eloop_signal_handler handler,
- void *user_data)
- {
- #ifdef CONFIG_NATIVE_WINDOWS
- return 0;
- #else /* CONFIG_NATIVE_WINDOWS */
- return eloop_register_signal(SIGHUP, handler, user_data);
- #endif /* CONFIG_NATIVE_WINDOWS */
- }
- void eloop_run(void)
- {
- fd_set *rfds, *wfds, *efds;
- int res;
- struct timeval _tv;
- struct os_time tv, now;
- rfds = os_malloc(sizeof(*rfds));
- wfds = os_malloc(sizeof(*wfds));
- efds = os_malloc(sizeof(*efds));
- if (rfds == NULL || wfds == NULL || efds == NULL)
- goto out;
- while (!eloop.terminate &&
- (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
- eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
- struct eloop_timeout *timeout;
- timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
- list);
- if (timeout) {
- os_get_time(&now);
- if (os_time_before(&now, &timeout->time))
- os_time_sub(&timeout->time, &now, &tv);
- else
- tv.sec = tv.usec = 0;
- _tv.tv_sec = tv.sec;
- _tv.tv_usec = tv.usec;
- }
- eloop_sock_table_set_fds(&eloop.readers, rfds);
- eloop_sock_table_set_fds(&eloop.writers, wfds);
- eloop_sock_table_set_fds(&eloop.exceptions, efds);
- res = select(eloop.max_sock + 1, rfds, wfds, efds,
- timeout ? &_tv : NULL);
- if (res < 0 && errno != EINTR && errno != 0) {
- perror("select");
- goto out;
- }
- eloop_process_pending_signals();
- /* check if some registered timeouts have occurred */
- if (timeout) {
- os_get_time(&now);
- if (!os_time_before(&now, &timeout->time)) {
- void *eloop_data = timeout->eloop_data;
- void *user_data = timeout->user_data;
- eloop_remove_timeout(timeout);
- timeout->handler(eloop_data,
- user_data);
- }
- }
- if (res <= 0)
- continue;
- eloop_sock_table_dispatch(&eloop.readers, rfds);
- eloop_sock_table_dispatch(&eloop.writers, wfds);
- eloop_sock_table_dispatch(&eloop.exceptions, efds);
- }
- out:
- os_free(rfds);
- os_free(wfds);
- os_free(efds);
- }
- void eloop_terminate(void)
- {
- eloop.terminate = 1;
- }
- void eloop_destroy(void)
- {
- struct eloop_timeout *timeout, *prev;
- struct os_time now;
- os_get_time(&now);
- dl_list_for_each_safe(timeout, prev, &eloop.timeout,
- struct eloop_timeout, list) {
- int sec, usec;
- sec = timeout->time.sec - now.sec;
- usec = timeout->time.usec - now.usec;
- if (timeout->time.usec < now.usec) {
- sec--;
- usec += 1000000;
- }
- wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
- "eloop_data=%p user_data=%p handler=%p",
- sec, usec, timeout->eloop_data, timeout->user_data,
- timeout->handler);
- wpa_trace_dump_funcname("eloop unregistered timeout handler",
- timeout->handler);
- wpa_trace_dump("eloop timeout", timeout);
- eloop_remove_timeout(timeout);
- }
- eloop_sock_table_destroy(&eloop.readers);
- eloop_sock_table_destroy(&eloop.writers);
- eloop_sock_table_destroy(&eloop.exceptions);
- os_free(eloop.signals);
- }
- int eloop_terminated(void)
- {
- return eloop.terminate;
- }
- void eloop_wait_for_read_sock(int sock)
- {
- fd_set rfds;
- if (sock < 0)
- return;
- FD_ZERO(&rfds);
- FD_SET(sock, &rfds);
- select(sock + 1, &rfds, NULL, NULL, NULL);
- }
|