Open usvi opened 1 year ago
Been verifying this. Nothing else seems to work. I have been trying many gimmics.. nothing. Of course there are no guarantees that own loop would work either. But maybe it is worth checkin.
I found that wpa_supplicant may have what we need.
Researching wpa_supplicant.
(Some sources say something about self pipe trick.)
About pselect / select:
"If none of the selected descriptors are ready for the requested operation, the pselect() or select() function shall block until at least one of the requested operations becomes ready, until the timeout occurs, or until interrupted by a signal. The timeout parameter controls how long the" ... "EINTR The function was interrupted before any of the selected events occurred and before the timeout interval expired."
src/utils/eloop.c has: #ifdef CONFIG_ELOOP_SELECT 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); #endif /* CONFIG_ELOOP_SELECT */
...
if (res < 0 && errno != EINTR && errno != 0) { wpa_printf(MSG_ERROR, "eloop: %s: %s",
... Also:
static void eloop_handle_signal(int sig)
and
int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data)
...
signal(sig, eloop_handle_signal);
/** * integrate_with_eloop - Register our mainloop integration with dbus * @connection: connection to the system message bus * @priv: a dbus control interface data structure * Returns: 0 on success, -1 on failure */ static int integrate_with_eloop(struct wpas_dbus_priv *priv) { if (!dbus_connection_set_watch_functions(priv->con, add_watch, remove_watch, watch_toggled, priv, NULL) || !dbus_connection_set_timeout_functions(priv->con, add_timeout, remove_timeout, timeout_toggled, priv, NULL)) { wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions"); return -1; } if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv)) return -1; dbus_connection_set_wakeup_main_function(priv->con, wakeup_main, priv, NULL); return 0; }
static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv) { /* Tell dbus about our mainloop integration functions */ integrate_with_eloop(priv); /* * Dispatch initial DBus messages that may have come in since the bus * name was claimed above. Happens when clients are quick to notice the * service. * * FIXME: is there a better solution to this problem? */ eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, priv->con, NULL); return 0; }
struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) { struct wpas_dbus_priv *priv; priv = os_zalloc(sizeof(*priv)); if (priv == NULL) return NULL; priv->global = global; if (wpas_dbus_init_common(priv) < 0 || #ifdef CONFIG_CTRL_IFACE_DBUS_NEW wpas_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ wpas_dbus_init_common_finish(priv) < 0) { wpas_dbus_deinit(priv); return NULL; } return priv; }
int wpas_notify_supplicant_initialized(struct wpa_global *global) { #ifdef CONFIG_CTRL_IFACE_DBUS_NEW if (global->params.dbus_ctrl_interface) { global->dbus = wpas_dbus_init(global); if (global->dbus == NULL) return -1; }
/** * wpa_supplicant_init - Initialize %wpa_supplicant * @params: Parameters for %wpa_supplicant * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure * * This function is used to initialize %wpa_supplicant. After successful * initialization, the returned data pointer can be used to add and remove * network interfaces, and eventually, to deinitialize %wpa_supplicant. */ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) { struct wpa_global *global; int ret, i; if (params == NULL) return NULL; #ifdef CONFIG_DRIVER_NDIS
...
if (wpas_notify_supplicant_initialized(global)) { wpa_supplicant_deinit(global); return NULL;
int main(int argc, char *argv[]) { int c, i; struct wpa_interface *ifaces, *iface; int iface_count, exitcode = -1; struct wpa_params params; struct wpa_global *global;
...
global = wpa_supplicant_init(¶ms); if (global == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant"); exitcode = -1; goto out; } else { wpa_printf(MSG_INFO, "Successfully initialized " "wpa_supplicant");
main() => wpa_supplicant_init() => wpas_notify_supplicant_initialized() => wpas_dbus_init() => wpas_dbus_init_common() && wpas_dbus_ctrl_iface_init() && wpas_dbus_init_common_finish() => integrate_with_eloop() => eloop_register_signal( eloop_handle_signal() )
wpas_dbus_init_common() => dbus_bus_get(DBUS_BUS_SYSTEM, &error);
wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH, WPAS_DBUS_NEW_SERVICE, obj_desc) => DBusObjectPathVTable wpa_vtable = { &free_dbus_object_desc_cb, &message_handler, NULL, NULL, NULL, NULL}; && dbus_connection_register_object_path(iface->con, dbus_path, &wpa_vtable, obj_desc))
From dbus, probably does not help:
* Queues incoming messages and sends outgoing messages for this * connection, optionally blocking in the process. Each call to * _dbus_connection_do_iteration_unlocked() will call select() or poll() one * time and then read or write data if possible.
Worth checking, vtable stuff: https://gist.github.com/plauche/bb296b43e42281d4e230ec397054e468
Worth looking:
eloop_run();
Interesting comment:
"Is it possible for asynchronous sending and reading messages in D-bus using the function calls pastebin.com/4E6XB3qb ? I didn't understand why dbus_connection_set_dispatch_status_function(), dbus_connection_set_watch_functions() and dbus_connection_set_timeout_functions() functions are required? My understanding is, if we register object path(i.e DBusObjectPathVTable) then d-bus daemon will call "message_function" of vtable when any message received for the connection. Please let me know if this understanding is not correct. – user3693586 Nov 27, 2017 at 10:31"
Funny: https://lists.gnu.org/archive/html/gpsd-dev/2021-08/msg00080.html
"Except shorten the timeout, or re-write that loop. I looked at rewriting that loop, and the innertubes are full of people that failed to replace dbus_connection_read_write_dispatch()."
eloop_run() has interesting:
fd_set *rfds, *wfds, *efds;
Interesting:
eloop.readers.changed = 0;
eloop.writers.changed = 0;
eloop.exceptions.changed = 0;
eloop_process_pending_signals();
/**
dbus events that may have happened. / static void wakeup_main(void data) { struct wpas_dbus_priv *priv = data;
/ Use SIGPOLL to break out of the eloop select() / raise(SIGPOLL); priv->should_dispatch = 1; }
Extremely important:
fd = dbus_watch_get_unix_fd(watch);
It seems the best way to do things is to scavenge dbus reference and see where it is in wpa_supplicant. We can do this!
dbus_connection_set_watch_functions
Sets the watch functions for the connection.
"These functions are responsible for making the application's main loop aware of file descriptors that need to be monitored for events, using select() or poll()"
wpa_supplicant uses nothing of read write dispatch
Important:
I presented myself the question: Why we keep even loop in a thread?
The thing is the event loop is a self-contained application so to speak. But we need external functions to manipulate its state. So, regular exported functions. There is of course init and deinit. We need more, and will make more.
static int integrate_with_eloop(struct wpas_dbus_priv *priv) { if (!dbus_connection_set_watch_functions(priv->con, add_watch, remove_watch, watch_toggled, priv, NULL) ||
So, first comes dbus system connection, then integration.
This first after setting up the bus: dbus_connection_set_watch_functions
Explore if this needed: dbus_connection_set_timeout_functions
man 2 select:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
fd_set rfds;
struct timeval tv;
int retval;
/* Watch stdin (fd 0) to see when it has input. */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Don't rely on the value of tv now! */
if (retval == -1)
perror("select()");
else if (retval)
printf("Data is available now.\n");
/* FD_ISSET(0, &rfds) will be true. */
else
printf("No data within five seconds.\n");
exit(EXIT_SUCCESS);
}
I get it now. We never watch individual fds. We watch sets.
Due to the dbus-1 library locking itself on dbus_connection_read_write_dispatch, I think I need to create my own event loop. I want to cleanly break on signal from another thread.