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