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