/* * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Alternatively, this software may be distributed under the terms of BSD * license. * * See README and COPYING for more details. */ #include "includes.h" #include <dbus/dbus.h> #include "common.h" #include "eloop.h" #include "wpa.h" #include "wpa_supplicant.h" #include "config.h" #include "eapol_sm.h" #include "wpa_supplicant_i.h" #include "ctrl_iface_dbus.h" #include "ctrl_iface_dbus_handlers.h" #include "l2_packet.h" #include "preauth.h" #include "wpa_ctrl.h" #include "eap.h" #define _DBUS_VERSION (DBUS_VERSION_MAJOR << 8 | DBUS_VERSION_MINOR) #define DBUS_VER(major, minor) ((major) << 8 | (minor)) #if _DBUS_VERSION < DBUS_VER(1,1) #define dbus_watch_get_unix_fd dbus_watch_get_fd #endif struct ctrl_iface_dbus_priv { DBusConnection *con; int should_dispatch; struct wpa_global *global; u32 next_objid; }; static void process_watch(struct ctrl_iface_dbus_priv *iface, DBusWatch *watch, eloop_event_type type) { dbus_connection_ref(iface->con); iface->should_dispatch = 0; if (type == EVENT_TYPE_READ) dbus_watch_handle(watch, DBUS_WATCH_READABLE); else if (type == EVENT_TYPE_WRITE) dbus_watch_handle(watch, DBUS_WATCH_WRITABLE); else if (type == EVENT_TYPE_EXCEPTION) dbus_watch_handle(watch, DBUS_WATCH_ERROR); if (iface->should_dispatch) { while (dbus_connection_get_dispatch_status(iface->con) == DBUS_DISPATCH_DATA_REMAINS) dbus_connection_dispatch(iface->con); iface->should_dispatch = 0; } dbus_connection_unref(iface->con); } static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx) { process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION); } static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx) { process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ); } static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx) { process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE); } static void connection_setup_add_watch(struct ctrl_iface_dbus_priv *iface, DBusWatch *watch) { unsigned int flags; int fd; if (!dbus_watch_get_enabled(watch)) return; flags = dbus_watch_get_flags(watch); fd = dbus_watch_get_unix_fd(watch); eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception, iface, watch); if (flags & DBUS_WATCH_READABLE) { eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read, iface, watch); } if (flags & DBUS_WATCH_WRITABLE) { eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write, iface, watch); } dbus_watch_set_data(watch, iface, NULL); } static void connection_setup_remove_watch(struct ctrl_iface_dbus_priv *iface, DBusWatch *watch) { unsigned int flags; int fd; flags = dbus_watch_get_flags(watch); fd = dbus_watch_get_unix_fd(watch); eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION); if (flags & DBUS_WATCH_READABLE) eloop_unregister_sock(fd, EVENT_TYPE_READ); if (flags & DBUS_WATCH_WRITABLE) eloop_unregister_sock(fd, EVENT_TYPE_WRITE); dbus_watch_set_data(watch, NULL, NULL); } static dbus_bool_t add_watch(DBusWatch *watch, void *data) { connection_setup_add_watch(data, watch); return TRUE; } static void remove_watch(DBusWatch *watch, void *data) { connection_setup_remove_watch(data, watch); } static void watch_toggled(DBusWatch *watch, void *data) { if (dbus_watch_get_enabled(watch)) add_watch(watch, data); else remove_watch(watch, data); } static void process_timeout(void *eloop_ctx, void *sock_ctx) { DBusTimeout *timeout = sock_ctx; dbus_timeout_handle(timeout); } static void connection_setup_add_timeout(struct ctrl_iface_dbus_priv *iface, DBusTimeout *timeout) { if (!dbus_timeout_get_enabled(timeout)) return; eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000, process_timeout, iface, timeout); dbus_timeout_set_data(timeout, iface, NULL); } static void connection_setup_remove_timeout(struct ctrl_iface_dbus_priv *iface, DBusTimeout *timeout) { eloop_cancel_timeout(process_timeout, iface, timeout); dbus_timeout_set_data(timeout, NULL, NULL); } static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { if (!dbus_timeout_get_enabled(timeout)) return TRUE; connection_setup_add_timeout(data, timeout); return TRUE; } static void remove_timeout(DBusTimeout *timeout, void *data) { connection_setup_remove_timeout(data, timeout); } static void timeout_toggled(DBusTimeout *timeout, void *data) { if (dbus_timeout_get_enabled(timeout)) add_timeout(timeout, data); else remove_timeout(timeout, data); } static void process_wakeup_main(int sig, void *eloop_ctx, void *signal_ctx) { struct ctrl_iface_dbus_priv *iface = signal_ctx; if (sig != SIGPOLL || !iface->con) return; if (dbus_connection_get_dispatch_status(iface->con) != DBUS_DISPATCH_DATA_REMAINS) return; /* Only dispatch once - we do not want to starve other events */ dbus_connection_ref(iface->con); dbus_connection_dispatch(iface->con); dbus_connection_unref(iface->con); } /** * wakeup_main - Attempt to wake our mainloop up * @data: dbus control interface private data * * Try to wake up the main eloop so it will process * dbus events that may have happened. */ static void wakeup_main(void *data) { struct ctrl_iface_dbus_priv *iface = data; /* Use SIGPOLL to break out of the eloop select() */ raise(SIGPOLL); iface->should_dispatch = 1; } /** * connection_setup_wakeup_main - Tell dbus about our wakeup_main function * @iface: dbus control interface private data * Returns: 0 on success, -1 on failure * * Register our wakeup_main handler with dbus */ static int connection_setup_wakeup_main(struct ctrl_iface_dbus_priv *iface) { if (eloop_register_signal(SIGPOLL, process_wakeup_main, iface)) return -1; dbus_connection_set_wakeup_main_function(iface->con, wakeup_main, iface, NULL); return 0; } /** * wpa_supplicant_dbus_next_objid - Return next available object id * @iface: dbus control interface private data * Returns: Object id */ u32 wpa_supplicant_dbus_next_objid (struct ctrl_iface_dbus_priv *iface) { return iface->next_objid++; } /** * wpas_dbus_decompose_object_path - Decompose an interface object path into parts * @path: The dbus object path * @network: (out) the configured network this object path refers to, if any * @bssid: (out) the scanned bssid this object path refers to, if any * Returns: The object path of the network interface this path refers to * * For a given object path, decomposes the object path into object id, network, * and BSSID parts, if those parts exist. */ char * wpas_dbus_decompose_object_path(const char *path, char **network, char **bssid) { const unsigned int dev_path_prefix_len = strlen(WPAS_DBUS_PATH_INTERFACES "/"); char *obj_path_only; char *next_sep; /* Be a bit paranoid about path */ if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/", dev_path_prefix_len)) return NULL; /* Ensure there's something at the end of the path */ if ((path + dev_path_prefix_len)[0] == '\0') return NULL; obj_path_only = strdup(path); if (obj_path_only == NULL) return NULL; next_sep = strchr(obj_path_only + dev_path_prefix_len, '/'); if (next_sep != NULL) { const char *net_part = strstr(next_sep, WPAS_DBUS_NETWORKS_PART "/"); const char *bssid_part = strstr(next_sep, WPAS_DBUS_BSSIDS_PART "/"); if (network && net_part) { /* Deal with a request for a configured network */ const char *net_name = net_part + strlen(WPAS_DBUS_NETWORKS_PART "/"); *network = NULL; if (strlen(net_name)) *network = strdup(net_name); } else if (bssid && bssid_part) { /* Deal with a request for a scanned BSSID */ const char *bssid_name = bssid_part + strlen(WPAS_DBUS_BSSIDS_PART "/"); if (strlen(bssid_name)) *bssid = strdup(bssid_name); else *bssid = NULL; } /* Cut off interface object path before "/" */ *next_sep = '\0'; } return obj_path_only; } /** * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message * @message: Pointer to incoming dbus message this error refers to * Returns: A dbus error message * * Convenience function to create and return an invalid interface error */ DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) { return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE, "wpa_supplicant knows nothing about " "this interface."); } /** * wpas_dbus_new_invalid_network_error - Return a new invalid network error message * @message: Pointer to incoming dbus message this error refers to * Returns: a dbus error message * * Convenience function to create and return an invalid network error */ DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message) { return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK, "The requested network does not exist."); } /** * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message * @message: Pointer to incoming dbus message this error refers to * Returns: a dbus error message * * Convenience function to create and return an invalid bssid error */ static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message) { return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID, "The BSSID requested was invalid."); } /** * wpas_dispatch_network_method - dispatch messages for configured networks * @message: the incoming dbus message * @wpa_s: a network interface's data * @network_id: id of the configured network we're interested in * Returns: a reply dbus message, or a dbus error message * * This function dispatches all incoming dbus messages for configured networks. */ static DBusMessage * wpas_dispatch_network_method(DBusMessage *message, struct wpa_supplicant *wpa_s, int network_id) { DBusMessage *reply = NULL; const char *method = dbus_message_get_member(message); struct wpa_ssid *ssid; ssid = wpa_config_get_network(wpa_s->conf, network_id); if (ssid == NULL) return wpas_dbus_new_invalid_network_error(message); if (!strcmp(method, "set")) reply = wpas_dbus_iface_set_network(message, wpa_s, ssid); else if (!strcmp(method, "enable")) reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid); else if (!strcmp(method, "disable")) reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid); return reply; } /** * wpas_dispatch_bssid_method - dispatch messages for scanned networks * @message: the incoming dbus message * @wpa_s: a network interface's data * @bssid: bssid of the scanned network we're interested in * Returns: a reply dbus message, or a dbus error message * * This function dispatches all incoming dbus messages for scanned networks. */ static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message, struct wpa_supplicant *wpa_s, const char *bssid) { DBusMessage *reply = NULL; const char *method = dbus_message_get_member(message); struct wpa_scan_result * res = NULL; int i; /* Ensure we actually have scan data */ if (wpa_s->scan_results == NULL && wpa_supplicant_get_scan_results(wpa_s) < 0) { reply = wpas_dbus_new_invalid_bssid_error(message); goto out; } /* Find the bssid's scan data */ for (i = 0; i < wpa_s->num_scan_results; i++) { struct wpa_scan_result * search_res = &wpa_s->scan_results[i]; char mac_str[18]; memset(mac_str, 0, sizeof(mac_str)); snprintf(mac_str, sizeof(mac_str) - 1, WPAS_DBUS_BSSID_FORMAT, MAC2STR(search_res->bssid)); if (!strcmp(bssid, mac_str)) { res = search_res; } } if (!res) { reply = wpas_dbus_new_invalid_bssid_error(message); goto out; } /* Dispatch the method call against the scanned bssid */ if (!strcmp(method, "properties")) reply = wpas_dbus_bssid_properties(message, wpa_s, res); out: return reply; } /** * wpas_iface_message_handler - Dispatch messages for interfaces or networks * @connection: Connection to the system message bus * @message: An incoming dbus message * @user_data: A pointer to a dbus control interface data structure * Returns: Whether or not the message was handled * * This function dispatches all incoming dbus messages for network interfaces, * or objects owned by them, such as scanned BSSIDs and configured networks. */ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, DBusMessage *message, void *user_data) { struct wpa_supplicant *wpa_s = user_data; const char *method = dbus_message_get_member(message); const char *path = dbus_message_get_path(message); const char *msg_interface = dbus_message_get_interface(message); char *iface_obj_path = NULL; char *network = NULL; char *bssid = NULL; DBusMessage *reply = NULL; /* Caller must specify a message interface */ if (!msg_interface) goto out; iface_obj_path = wpas_dbus_decompose_object_path(path, &network, &bssid); if (iface_obj_path == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } /* Make sure the message's object path actually refers to the * wpa_supplicant structure it's supposed to (which is wpa_s) */ if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, iface_obj_path) != wpa_s) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { /* A method for one of this interface's configured networks */ int nid = strtoul(network, NULL, 10); if (errno != EINVAL) reply = wpas_dispatch_network_method(message, wpa_s, nid); else reply = wpas_dbus_new_invalid_network_error(message); } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) { /* A method for one of this interface's scanned BSSIDs */ reply = wpas_dispatch_bssid_method(message, wpa_s, bssid); } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) { /* A method for an interface only. */ if (!strcmp(method, "scan")) reply = wpas_dbus_iface_scan(message, wpa_s); else if (!strcmp(method, "scanResults")) reply = wpas_dbus_iface_scan_results(message, wpa_s); else if (!strcmp(method, "addNetwork")) reply = wpas_dbus_iface_add_network(message, wpa_s); else if (!strcmp(method, "removeNetwork")) reply = wpas_dbus_iface_remove_network(message, wpa_s); else if (!strcmp(method, "selectNetwork")) reply = wpas_dbus_iface_select_network(message, wpa_s); else if (!strcmp(method, "capabilities")) reply = wpas_dbus_iface_capabilities(message, wpa_s); else if (!strcmp(method, "disconnect")) reply = wpas_dbus_iface_disconnect(message, wpa_s); else if (!strcmp(method, "setAPScan")) reply = wpas_dbus_iface_set_ap_scan(message, wpa_s); else if (!strcmp(method, "state")) reply = wpas_dbus_iface_get_state(message, wpa_s); else if (!strcmp(method, "setBlobs")) reply = wpas_dbus_iface_set_blobs(message, wpa_s); else if (!strcmp(method, "removeBlobs")) reply = wpas_dbus_iface_remove_blobs(message, wpa_s); } /* If the message was handled, send back the reply */ if (reply) { dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } out: free(iface_obj_path); free(network); free(bssid); return reply ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /** * wpas_message_handler - dispatch incoming dbus messages * @connection: connection to the system message bus * @message: an incoming dbus message * @user_data: a pointer to a dbus control interface data structure * Returns: whether or not the message was handled * * This function dispatches all incoming dbus messages to the correct * handlers, depending on what the message's target object path is, * and what the method call is. */ static DBusHandlerResult wpas_message_handler(DBusConnection *connection, DBusMessage *message, void *user_data) { struct ctrl_iface_dbus_priv *ctrl_iface = user_data; const char *method; const char *path; const char *msg_interface; DBusMessage *reply = NULL; method = dbus_message_get_member(message); path = dbus_message_get_path(message); msg_interface = dbus_message_get_interface(message); if (!method || !path || !ctrl_iface || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* Validate the method interface */ if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (!strcmp(path, WPAS_DBUS_PATH)) { /* dispatch methods against our global dbus interface here */ if (!strcmp(method, "addInterface")) { reply = wpas_dbus_global_add_interface( message, ctrl_iface->global); } else if (!strcmp(method, "removeInterface")) { reply = wpas_dbus_global_remove_interface( message, ctrl_iface->global); } else if (!strcmp(method, "getInterface")) { reply = wpas_dbus_global_get_interface( message, ctrl_iface->global); } } /* If the message was handled, send back the reply */ if (reply) { dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } return reply ? DBUS_HANDLER_RESULT_HANDLED : DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } /** * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal * @wpa_s: %wpa_supplicant network interface data * Returns: 0 on success, -1 on failure * * Notify listeners that this interface has updated scan results. */ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) { struct ctrl_iface_dbus_priv *iface = wpa_s->global->dbus_ctrl_iface; DBusMessage *signal; const char *path; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; path = wpa_supplicant_get_dbus_path(wpa_s); if (path == NULL) { perror("wpa_supplicant_dbus_notify_scan_results[dbus]: " "interface didn't have a dbus path"); wpa_printf(MSG_ERROR, "wpa_supplicant_dbus_notify_scan_results[dbus]: " "interface didn't have a dbus path; can't send " "scan result signal."); return; } signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, "ScanResultsAvailable"); if (signal == NULL) { perror("wpa_supplicant_dbus_notify_scan_results[dbus]: " "couldn't create dbus signal; likely out of memory"); wpa_printf(MSG_ERROR, "dbus control interface: not enough " "memory to send scan results signal."); return; } dbus_connection_send(iface->con, signal, NULL); dbus_message_unref(signal); } /** * wpa_supplicant_dbus_notify_state_change - Send a state change signal * @wpa_s: %wpa_supplicant network interface data * @new_state: new state wpa_supplicant is entering * @old_state: old state wpa_supplicant is leaving * Returns: 0 on success, -1 on failure * * Notify listeners that wpa_supplicant has changed state */ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, wpa_states new_state, wpa_states old_state) { struct ctrl_iface_dbus_priv *iface; DBusMessage *signal = NULL; const char *path; const char *new_state_str, *old_state_str; /* Do nothing if the control interface is not turned on */ if (wpa_s->global == NULL) return; iface = wpa_s->global->dbus_ctrl_iface; if (iface == NULL) return; /* Only send signal if state really changed */ if (new_state == old_state) return; path = wpa_supplicant_get_dbus_path(wpa_s); if (path == NULL) { perror("wpa_supplicant_dbus_notify_state_change[dbus]: " "interface didn't have a dbus path"); wpa_printf(MSG_ERROR, "wpa_supplicant_dbus_notify_state_change[dbus]: " "interface didn't have a dbus path; can't send " "signal."); return; } signal = dbus_message_new_signal(path, WPAS_DBUS_IFACE_INTERFACE, "StateChange"); if (signal == NULL) { perror("wpa_supplicant_dbus_notify_state_change[dbus]: " "couldn't create dbus signal; likely out of memory"); wpa_printf(MSG_ERROR, "wpa_supplicant_dbus_notify_state_change[dbus]: " "couldn't create dbus signal; likely out of " "memory."); return; } new_state_str = wpa_supplicant_state_txt(new_state); old_state_str = wpa_supplicant_state_txt(old_state); if (new_state_str == NULL || old_state_str == NULL) { perror("wpa_supplicant_dbus_notify_state_change[dbus]: " "couldn't convert state strings"); wpa_printf(MSG_ERROR, "wpa_supplicant_dbus_notify_state_change[dbus]: " "couldn't convert state strings."); goto out; } if (!dbus_message_append_args(signal, DBUS_TYPE_STRING, &new_state_str, DBUS_TYPE_STRING, &old_state_str, DBUS_TYPE_INVALID)) { perror("wpa_supplicant_dbus_notify_state_change[dbus]: " "not enough memory to construct state change signal."); wpa_printf(MSG_ERROR, "wpa_supplicant_dbus_notify_state_change[dbus]: " "not enough memory to construct state change " "signal."); goto out; } dbus_connection_send(iface->con, signal, NULL); out: dbus_message_unref(signal); } /** * integrate_with_eloop - Register our mainloop integration with dbus * @connection: connection to the system message bus * @iface: a dbus control interface data structure * Returns: 0 on success, -1 on failure * * We register our mainloop integration functions with dbus here. */ static int integrate_with_eloop(DBusConnection *connection, struct ctrl_iface_dbus_priv *iface) { if (!dbus_connection_set_watch_functions(connection, add_watch, remove_watch, watch_toggled, iface, NULL)) { perror("dbus_connection_set_watch_functions[dbus]"); wpa_printf(MSG_ERROR, "Not enough memory to set up dbus."); return -1; } if (!dbus_connection_set_timeout_functions(connection, add_timeout, remove_timeout, timeout_toggled, iface, NULL)) { perror("dbus_connection_set_timeout_functions[dbus]"); wpa_printf(MSG_ERROR, "Not enough memory to set up dbus."); return -1; } if (connection_setup_wakeup_main(iface) < 0) { perror("connection_setup_wakeup_main[dbus]"); wpa_printf(MSG_ERROR, "Could not setup main wakeup function."); return -1; } return 0; } /** * dispatch_initial_dbus_messages - Dispatch initial dbus messages after * claiming bus name * @eloop_ctx: the DBusConnection to dispatch on * @timeout_ctx: unused * * If clients are quick to notice that wpa_supplicant claimed its bus name, * there may have been messages that came in before initialization was * all finished. Dispatch those here. */ static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx) { DBusConnection *con = eloop_ctx; while (dbus_connection_get_dispatch_status(con) == DBUS_DISPATCH_DATA_REMAINS) dbus_connection_dispatch(con); } /** * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface * @global: Pointer to global data from wpa_supplicant_init() * Returns: Pointer to dbus_ctrl_iface date or %NULL on failure * * Initialize the dbus control interface and start receiving commands from * external programs over the bus. */ struct ctrl_iface_dbus_priv * wpa_supplicant_dbus_ctrl_iface_init(struct wpa_global *global) { struct ctrl_iface_dbus_priv *iface; DBusError error; int ret = -1; DBusObjectPathVTable wpas_vtable = { NULL, &wpas_message_handler, NULL, NULL, NULL, NULL }; iface = wpa_zalloc(sizeof(struct ctrl_iface_dbus_priv)); if (iface == NULL) return NULL; iface->global = global; /* Get a reference to the system bus */ dbus_error_init(&error); iface->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); dbus_error_free(&error); if (!iface->con) { perror("dbus_bus_get[ctrl_iface_dbus]"); wpa_printf(MSG_ERROR, "Could not acquire the system bus."); goto fail; } /* Tell dbus about our mainloop integration functions */ if (integrate_with_eloop(iface->con, iface)) goto fail; /* Register the message handler for the global dbus interface */ if (!dbus_connection_register_object_path(iface->con, WPAS_DBUS_PATH, &wpas_vtable, iface)) { perror("dbus_connection_register_object_path[dbus]"); wpa_printf(MSG_ERROR, "Could not set up DBus message " "handler."); goto fail; } /* Register our service with the message bus */ dbus_error_init(&error); switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE, 0, &error)) { case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: ret = 0; break; case DBUS_REQUEST_NAME_REPLY_EXISTS: case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: perror("dbus_bus_request_name[dbus]"); wpa_printf(MSG_ERROR, "Could not request DBus service name: " "already registered."); break; default: perror("dbus_bus_request_name[dbus]"); wpa_printf(MSG_ERROR, "Could not request DBus service name: " "%s %s.", error.name, error.message); break; } dbus_error_free(&error); if (ret != 0) goto fail; wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE "'."); /* * Dispatch initial DBus messages that may have come in since the bus * name was claimed above. Happens when clients are quick to notice the * wpa_supplicant service. * * FIXME: is there a better solution to this problem? */ eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, iface->con, NULL); return iface; fail: wpa_supplicant_dbus_ctrl_iface_deinit(iface); return NULL; } /** * wpa_supplicant_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface * @iface: Pointer to dbus private data from * wpa_supplicant_dbus_ctrl_iface_init() * * Deinitialize the dbus control interface that was initialized with * wpa_supplicant_dbus_ctrl_iface_init(). */ void wpa_supplicant_dbus_ctrl_iface_deinit(struct ctrl_iface_dbus_priv *iface) { if (iface == NULL) return; if (iface->con) { eloop_cancel_timeout(dispatch_initial_dbus_messages, iface->con, NULL); dbus_connection_set_watch_functions(iface->con, NULL, NULL, NULL, NULL, NULL); dbus_connection_set_timeout_functions(iface->con, NULL, NULL, NULL, NULL, NULL); dbus_connection_unref(iface->con); } memset(iface, 0, sizeof(struct ctrl_iface_dbus_priv)); free(iface); } /** * wpas_dbus_register_new_iface - Register a new interface with dbus * @global: Global %wpa_supplicant data * @wpa_s: %wpa_supplicant interface description structure to register * Returns: 0 on success, -1 on error * * Registers a new interface with dbus and assigns it a dbus object path. */ int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) { struct ctrl_iface_dbus_priv *ctrl_iface = wpa_s->global->dbus_ctrl_iface; DBusConnection * con; u32 next; DBusObjectPathVTable vtable = { NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL }; char *path; int ret = -1; /* Do nothing if the control interface is not turned on */ if (ctrl_iface == NULL) return 0; con = ctrl_iface->con; next = wpa_supplicant_dbus_next_objid(ctrl_iface); /* Create and set the interface's object path */ path = wpa_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (path == NULL) return -1; snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, WPAS_DBUS_PATH_INTERFACES "/%u", next); if (wpa_supplicant_set_dbus_path(wpa_s, path)) { wpa_printf(MSG_DEBUG, "Failed to set dbus path for interface %s", wpa_s->ifname); goto out; } /* Register the message handler for the interface functions */ if (!dbus_connection_register_fallback(con, path, &vtable, wpa_s)) { perror("wpas_dbus_register_iface [dbus]"); wpa_printf(MSG_ERROR, "Could not set up DBus message " "handler for interface %s.", wpa_s->ifname); goto out; } ret = 0; out: free(path); return ret; } /** * wpas_dbus_unregister_iface - Unregister an interface from dbus * @wpa_s: wpa_supplicant interface structure * Returns: 0 on success, -1 on failure * * Unregisters the interface with dbus */ int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) { struct ctrl_iface_dbus_priv *ctrl_iface; DBusConnection *con; const char *path; /* Do nothing if the control interface is not turned on */ if (wpa_s == NULL || wpa_s->global == NULL) return 0; ctrl_iface = wpa_s->global->dbus_ctrl_iface; if (ctrl_iface == NULL) return 0; con = ctrl_iface->con; path = wpa_supplicant_get_dbus_path(wpa_s); if (!dbus_connection_unregister_object_path(con, path)) return -1; free(wpa_s->dbus_path); wpa_s->dbus_path = NULL; return 0; } /** * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface * @global: Pointer to global data from wpa_supplicant_init() * @path: Pointer to a dbus object path representing an interface * Returns: Pointer to the interface or %NULL if not found */ struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path( struct wpa_global *global, const char *path) { struct wpa_supplicant *wpa_s; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (strcmp(wpa_s->dbus_path, path) == 0) return wpa_s; } return NULL; } /** * wpa_supplicant_set_dbus_path - Assign a dbus path to an interface * @wpa_s: wpa_supplicant interface structure * @path: dbus path to set on the interface * Returns: 0 on succes, -1 on error */ int wpa_supplicant_set_dbus_path(struct wpa_supplicant *wpa_s, const char *path) { u32 len = strlen (path); if (len >= WPAS_DBUS_OBJECT_PATH_MAX) return -1; if (wpa_s->dbus_path) return -1; wpa_s->dbus_path = strdup(path); return 0; } /** * wpa_supplicant_get_dbus_path - Get an interface's dbus path * @wpa_s: %wpa_supplicant interface structure * Returns: Interface's dbus object path, or %NULL on error */ const char * wpa_supplicant_get_dbus_path(struct wpa_supplicant *wpa_s) { return wpa_s->dbus_path; }