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 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