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