Home | History | Annotate | Line # | Download | only in dbus
      1      1.1  christos /*
      2      1.1  christos  * wpa_supplicant D-Bus control interface - common functionality
      3      1.1  christos  * Copyright (c) 2006, Dan Williams <dcbw (at) redhat.com> and Red Hat, Inc.
      4      1.1  christos  * Copyright (c) 2009, Witold Sowa <witold.sowa (at) gmail.com>
      5      1.1  christos  * Copyright (c) 2009, Jouni Malinen <j (at) w1.fi>
      6      1.1  christos  *
      7  1.1.1.2  christos  * This software may be distributed under the terms of the BSD license.
      8  1.1.1.2  christos  * See README for more details.
      9      1.1  christos  */
     10      1.1  christos 
     11      1.1  christos #include "utils/includes.h"
     12      1.1  christos #include <dbus/dbus.h>
     13      1.1  christos 
     14      1.1  christos #include "utils/common.h"
     15      1.1  christos #include "utils/eloop.h"
     16      1.1  christos #include "dbus_common.h"
     17      1.1  christos #include "dbus_common_i.h"
     18      1.1  christos #include "dbus_new.h"
     19  1.1.1.3  christos #include "../wpa_supplicant_i.h"
     20      1.1  christos 
     21      1.1  christos 
     22      1.1  christos #ifndef SIGPOLL
     23      1.1  christos #ifdef SIGIO
     24      1.1  christos /*
     25      1.1  christos  * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for
     26      1.1  christos  * FreeBSD.
     27      1.1  christos  */
     28      1.1  christos #define SIGPOLL SIGIO
     29      1.1  christos #endif
     30      1.1  christos #endif
     31      1.1  christos 
     32      1.1  christos 
     33      1.1  christos static void dispatch_data(DBusConnection *con)
     34      1.1  christos {
     35      1.1  christos 	while (dbus_connection_get_dispatch_status(con) ==
     36      1.1  christos 	       DBUS_DISPATCH_DATA_REMAINS)
     37      1.1  christos 		dbus_connection_dispatch(con);
     38      1.1  christos }
     39      1.1  christos 
     40      1.1  christos 
     41      1.1  christos /**
     42      1.1  christos  * dispatch_initial_dbus_messages - Dispatch initial dbus messages after
     43      1.1  christos  *     claiming bus name
     44      1.1  christos  * @eloop_ctx: the DBusConnection to dispatch on
     45      1.1  christos  * @timeout_ctx: unused
     46      1.1  christos  *
     47      1.1  christos  * If clients are quick to notice that service claimed its bus name,
     48      1.1  christos  * there may have been messages that came in before initialization was
     49      1.1  christos  * all finished.  Dispatch those here.
     50      1.1  christos  */
     51      1.1  christos static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx)
     52      1.1  christos {
     53      1.1  christos 	DBusConnection *con = eloop_ctx;
     54      1.1  christos 	dispatch_data(con);
     55      1.1  christos }
     56      1.1  christos 
     57      1.1  christos 
     58      1.1  christos static void process_watch(struct wpas_dbus_priv *priv,
     59      1.1  christos 			  DBusWatch *watch, eloop_event_type type)
     60      1.1  christos {
     61      1.1  christos 	dbus_connection_ref(priv->con);
     62      1.1  christos 
     63      1.1  christos 	priv->should_dispatch = 0;
     64      1.1  christos 
     65      1.1  christos 	if (type == EVENT_TYPE_READ)
     66      1.1  christos 		dbus_watch_handle(watch, DBUS_WATCH_READABLE);
     67      1.1  christos 	else if (type == EVENT_TYPE_WRITE)
     68      1.1  christos 		dbus_watch_handle(watch, DBUS_WATCH_WRITABLE);
     69      1.1  christos 	else if (type == EVENT_TYPE_EXCEPTION)
     70      1.1  christos 		dbus_watch_handle(watch, DBUS_WATCH_ERROR);
     71      1.1  christos 
     72      1.1  christos 	if (priv->should_dispatch) {
     73      1.1  christos 		dispatch_data(priv->con);
     74      1.1  christos 		priv->should_dispatch = 0;
     75      1.1  christos 	}
     76      1.1  christos 
     77      1.1  christos 	dbus_connection_unref(priv->con);
     78      1.1  christos }
     79      1.1  christos 
     80      1.1  christos 
     81      1.1  christos static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx)
     82      1.1  christos {
     83      1.1  christos 	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION);
     84      1.1  christos }
     85      1.1  christos 
     86      1.1  christos 
     87      1.1  christos static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx)
     88      1.1  christos {
     89      1.1  christos 	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ);
     90      1.1  christos }
     91      1.1  christos 
     92      1.1  christos 
     93      1.1  christos static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx)
     94      1.1  christos {
     95      1.1  christos 	process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE);
     96      1.1  christos }
     97      1.1  christos 
     98      1.1  christos 
     99      1.1  christos static dbus_bool_t add_watch(DBusWatch *watch, void *data)
    100      1.1  christos {
    101      1.1  christos 	struct wpas_dbus_priv *priv = data;
    102      1.1  christos 	unsigned int flags;
    103      1.1  christos 	int fd;
    104      1.1  christos 
    105      1.1  christos 	if (!dbus_watch_get_enabled(watch))
    106      1.1  christos 		return TRUE;
    107      1.1  christos 
    108      1.1  christos 	flags = dbus_watch_get_flags(watch);
    109      1.1  christos 	fd = dbus_watch_get_unix_fd(watch);
    110      1.1  christos 
    111  1.1.1.6  christos 	if (eloop_register_sock(fd, EVENT_TYPE_EXCEPTION,
    112  1.1.1.6  christos 				process_watch_exception, priv, watch) < 0)
    113  1.1.1.6  christos 		return FALSE;
    114  1.1.1.6  christos 
    115  1.1.1.6  christos 	if ((flags & DBUS_WATCH_READABLE) &&
    116  1.1.1.6  christos 	    eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read,
    117  1.1.1.6  christos 				priv, watch) < 0)
    118  1.1.1.6  christos 		return FALSE;
    119  1.1.1.6  christos 	if ((flags & DBUS_WATCH_WRITABLE) &&
    120  1.1.1.6  christos 	    eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write,
    121  1.1.1.6  christos 				priv, watch) < 0)
    122  1.1.1.6  christos 		return FALSE;
    123      1.1  christos 
    124      1.1  christos 	dbus_watch_set_data(watch, priv, NULL);
    125      1.1  christos 
    126      1.1  christos 	return TRUE;
    127      1.1  christos }
    128      1.1  christos 
    129      1.1  christos 
    130      1.1  christos static void remove_watch(DBusWatch *watch, void *data)
    131      1.1  christos {
    132      1.1  christos 	unsigned int flags;
    133      1.1  christos 	int fd;
    134      1.1  christos 
    135      1.1  christos 	flags = dbus_watch_get_flags(watch);
    136      1.1  christos 	fd = dbus_watch_get_unix_fd(watch);
    137      1.1  christos 
    138      1.1  christos 	eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION);
    139      1.1  christos 
    140      1.1  christos 	if (flags & DBUS_WATCH_READABLE)
    141      1.1  christos 		eloop_unregister_sock(fd, EVENT_TYPE_READ);
    142      1.1  christos 	if (flags & DBUS_WATCH_WRITABLE)
    143      1.1  christos 		eloop_unregister_sock(fd, EVENT_TYPE_WRITE);
    144      1.1  christos 
    145      1.1  christos 	dbus_watch_set_data(watch, NULL, NULL);
    146      1.1  christos }
    147      1.1  christos 
    148      1.1  christos 
    149      1.1  christos static void watch_toggled(DBusWatch *watch, void *data)
    150      1.1  christos {
    151      1.1  christos 	if (dbus_watch_get_enabled(watch))
    152      1.1  christos 		add_watch(watch, data);
    153      1.1  christos 	else
    154      1.1  christos 		remove_watch(watch, data);
    155      1.1  christos }
    156      1.1  christos 
    157      1.1  christos 
    158      1.1  christos static void process_timeout(void *eloop_ctx, void *sock_ctx)
    159      1.1  christos {
    160      1.1  christos 	DBusTimeout *timeout = sock_ctx;
    161      1.1  christos 	dbus_timeout_handle(timeout);
    162      1.1  christos }
    163      1.1  christos 
    164      1.1  christos 
    165      1.1  christos static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
    166      1.1  christos {
    167      1.1  christos 	struct wpas_dbus_priv *priv = data;
    168  1.1.1.4  christos 
    169      1.1  christos 	if (!dbus_timeout_get_enabled(timeout))
    170      1.1  christos 		return TRUE;
    171      1.1  christos 
    172      1.1  christos 	eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000,
    173      1.1  christos 			       process_timeout, priv, timeout);
    174      1.1  christos 
    175      1.1  christos 	dbus_timeout_set_data(timeout, priv, NULL);
    176      1.1  christos 
    177      1.1  christos 	return TRUE;
    178      1.1  christos }
    179      1.1  christos 
    180      1.1  christos 
    181      1.1  christos static void remove_timeout(DBusTimeout *timeout, void *data)
    182      1.1  christos {
    183      1.1  christos 	struct wpas_dbus_priv *priv = data;
    184  1.1.1.4  christos 
    185      1.1  christos 	eloop_cancel_timeout(process_timeout, priv, timeout);
    186      1.1  christos 	dbus_timeout_set_data(timeout, NULL, NULL);
    187      1.1  christos }
    188      1.1  christos 
    189      1.1  christos 
    190      1.1  christos static void timeout_toggled(DBusTimeout *timeout, void *data)
    191      1.1  christos {
    192      1.1  christos 	if (dbus_timeout_get_enabled(timeout))
    193      1.1  christos 		add_timeout(timeout, data);
    194      1.1  christos 	else
    195      1.1  christos 		remove_timeout(timeout, data);
    196      1.1  christos }
    197      1.1  christos 
    198      1.1  christos 
    199      1.1  christos static void process_wakeup_main(int sig, void *signal_ctx)
    200      1.1  christos {
    201      1.1  christos 	struct wpas_dbus_priv *priv = signal_ctx;
    202      1.1  christos 
    203      1.1  christos 	if (sig != SIGPOLL || !priv->con)
    204      1.1  christos 		return;
    205      1.1  christos 
    206      1.1  christos 	if (dbus_connection_get_dispatch_status(priv->con) !=
    207      1.1  christos 	    DBUS_DISPATCH_DATA_REMAINS)
    208      1.1  christos 		return;
    209      1.1  christos 
    210      1.1  christos 	/* Only dispatch once - we do not want to starve other events */
    211      1.1  christos 	dbus_connection_ref(priv->con);
    212      1.1  christos 	dbus_connection_dispatch(priv->con);
    213      1.1  christos 	dbus_connection_unref(priv->con);
    214      1.1  christos }
    215      1.1  christos 
    216      1.1  christos 
    217      1.1  christos /**
    218      1.1  christos  * wakeup_main - Attempt to wake our mainloop up
    219      1.1  christos  * @data: dbus control interface private data
    220      1.1  christos  *
    221      1.1  christos  * Try to wake up the main eloop so it will process
    222      1.1  christos  * dbus events that may have happened.
    223      1.1  christos  */
    224      1.1  christos static void wakeup_main(void *data)
    225      1.1  christos {
    226      1.1  christos 	struct wpas_dbus_priv *priv = data;
    227      1.1  christos 
    228      1.1  christos 	/* Use SIGPOLL to break out of the eloop select() */
    229      1.1  christos 	raise(SIGPOLL);
    230      1.1  christos 	priv->should_dispatch = 1;
    231      1.1  christos }
    232      1.1  christos 
    233      1.1  christos 
    234      1.1  christos /**
    235      1.1  christos  * integrate_with_eloop - Register our mainloop integration with dbus
    236      1.1  christos  * @connection: connection to the system message bus
    237      1.1  christos  * @priv: a dbus control interface data structure
    238      1.1  christos  * Returns: 0 on success, -1 on failure
    239      1.1  christos  */
    240      1.1  christos static int integrate_with_eloop(struct wpas_dbus_priv *priv)
    241      1.1  christos {
    242      1.1  christos 	if (!dbus_connection_set_watch_functions(priv->con, add_watch,
    243      1.1  christos 						 remove_watch, watch_toggled,
    244      1.1  christos 						 priv, NULL) ||
    245      1.1  christos 	    !dbus_connection_set_timeout_functions(priv->con, add_timeout,
    246      1.1  christos 						   remove_timeout,
    247      1.1  christos 						   timeout_toggled, priv,
    248      1.1  christos 						   NULL)) {
    249  1.1.1.4  christos 		wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions");
    250      1.1  christos 		return -1;
    251      1.1  christos 	}
    252      1.1  christos 
    253      1.1  christos 	if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv))
    254      1.1  christos 		return -1;
    255      1.1  christos 	dbus_connection_set_wakeup_main_function(priv->con, wakeup_main,
    256      1.1  christos 						 priv, NULL);
    257      1.1  christos 
    258      1.1  christos 	return 0;
    259      1.1  christos }
    260      1.1  christos 
    261      1.1  christos 
    262  1.1.1.3  christos static DBusHandlerResult disconnect_filter(DBusConnection *conn,
    263  1.1.1.4  christos 					   DBusMessage *message, void *data)
    264  1.1.1.3  christos {
    265  1.1.1.3  christos 	struct wpas_dbus_priv *priv = data;
    266  1.1.1.3  christos 
    267  1.1.1.3  christos 	if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
    268  1.1.1.4  christos 				   "Disconnected")) {
    269  1.1.1.3  christos 		wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
    270  1.1.1.3  christos 		dbus_connection_set_exit_on_disconnect(conn, FALSE);
    271  1.1.1.3  christos 		wpa_supplicant_terminate_proc(priv->global);
    272  1.1.1.3  christos 		return DBUS_HANDLER_RESULT_HANDLED;
    273  1.1.1.3  christos 	} else
    274  1.1.1.3  christos 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    275  1.1.1.3  christos }
    276  1.1.1.3  christos 
    277  1.1.1.3  christos 
    278      1.1  christos static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
    279      1.1  christos {
    280      1.1  christos 	DBusError error;
    281      1.1  christos 	int ret = 0;
    282      1.1  christos 
    283      1.1  christos 	/* Get a reference to the system bus */
    284      1.1  christos 	dbus_error_init(&error);
    285      1.1  christos 	priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
    286  1.1.1.3  christos 	if (priv->con) {
    287  1.1.1.3  christos 		dbus_connection_add_filter(priv->con, disconnect_filter, priv,
    288  1.1.1.4  christos 					   NULL);
    289  1.1.1.3  christos 	} else {
    290  1.1.1.4  christos 		wpa_printf(MSG_ERROR,
    291  1.1.1.4  christos 			   "dbus: Could not acquire the system bus: %s - %s",
    292  1.1.1.4  christos 			   error.name, error.message);
    293      1.1  christos 		ret = -1;
    294      1.1  christos 	}
    295      1.1  christos 	dbus_error_free(&error);
    296      1.1  christos 
    297      1.1  christos 	return ret;
    298      1.1  christos }
    299      1.1  christos 
    300      1.1  christos 
    301      1.1  christos static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv)
    302      1.1  christos {
    303      1.1  christos 	/* Tell dbus about our mainloop integration functions */
    304      1.1  christos 	integrate_with_eloop(priv);
    305      1.1  christos 
    306      1.1  christos 	/*
    307      1.1  christos 	 * Dispatch initial DBus messages that may have come in since the bus
    308      1.1  christos 	 * name was claimed above. Happens when clients are quick to notice the
    309      1.1  christos 	 * service.
    310      1.1  christos 	 *
    311      1.1  christos 	 * FIXME: is there a better solution to this problem?
    312      1.1  christos 	 */
    313      1.1  christos 	eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
    314  1.1.1.4  christos 			       priv->con, NULL);
    315      1.1  christos 
    316      1.1  christos 	return 0;
    317      1.1  christos }
    318      1.1  christos 
    319      1.1  christos 
    320      1.1  christos static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv)
    321      1.1  christos {
    322      1.1  christos 	if (priv->con) {
    323      1.1  christos 		eloop_cancel_timeout(dispatch_initial_dbus_messages,
    324      1.1  christos 				     priv->con, NULL);
    325  1.1.1.3  christos 		eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX);
    326  1.1.1.3  christos 
    327      1.1  christos 		dbus_connection_set_watch_functions(priv->con, NULL, NULL,
    328      1.1  christos 						    NULL, NULL, NULL);
    329      1.1  christos 		dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
    330      1.1  christos 						      NULL, NULL, NULL);
    331  1.1.1.3  christos 		dbus_connection_remove_filter(priv->con, disconnect_filter,
    332  1.1.1.3  christos 					      priv);
    333  1.1.1.3  christos 
    334      1.1  christos 		dbus_connection_unref(priv->con);
    335      1.1  christos 	}
    336      1.1  christos 
    337      1.1  christos 	os_free(priv);
    338      1.1  christos }
    339      1.1  christos 
    340      1.1  christos 
    341      1.1  christos struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global)
    342      1.1  christos {
    343      1.1  christos 	struct wpas_dbus_priv *priv;
    344      1.1  christos 
    345      1.1  christos 	priv = os_zalloc(sizeof(*priv));
    346      1.1  christos 	if (priv == NULL)
    347      1.1  christos 		return NULL;
    348      1.1  christos 	priv->global = global;
    349      1.1  christos 
    350  1.1.1.4  christos 	if (wpas_dbus_init_common(priv) < 0 ||
    351      1.1  christos #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
    352  1.1.1.4  christos 	    wpas_dbus_ctrl_iface_init(priv) < 0 ||
    353      1.1  christos #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
    354  1.1.1.4  christos 	    wpas_dbus_init_common_finish(priv) < 0) {
    355      1.1  christos 		wpas_dbus_deinit(priv);
    356      1.1  christos 		return NULL;
    357      1.1  christos 	}
    358      1.1  christos 
    359      1.1  christos 	return priv;
    360      1.1  christos }
    361      1.1  christos 
    362      1.1  christos 
    363      1.1  christos void wpas_dbus_deinit(struct wpas_dbus_priv *priv)
    364      1.1  christos {
    365      1.1  christos 	if (priv == NULL)
    366      1.1  christos 		return;
    367      1.1  christos 
    368      1.1  christos #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
    369      1.1  christos 	wpas_dbus_ctrl_iface_deinit(priv);
    370      1.1  christos #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
    371      1.1  christos 
    372      1.1  christos 	wpas_dbus_deinit_common(priv);
    373      1.1  christos }
    374