-/**
- @file supp.c
-
- Copyright (C) 2009 Javier S. Pedro
-
- @author Javier S. Pedro <javispedro@javispedro.com>
-
- This file is part of libicd-network-wpa.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-*/
-
-#include <signal.h>
-#include <string.h>
-
-#include <glib.h>
-#include <dbus/dbus.h>
-#include <gconf/gconf-client.h>
-
-#include <osso-ic-dbus.h>
-#include <osso-ic-gconf.h>
-
-#include "log.h"
-#include "common.h"
-#include "icd.h"
-#include "dbus.h"
-#include "dbus-helper.h"
-#include "supp.h"
-
-/* Errors */
-#define WPAS_ERROR_INVALID_NETWORK \
- WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork"
-#define WPAS_ERROR_INVALID_BSSID \
- WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID"
-
-#define WPAS_ERROR_INVALID_OPTS \
- WPAS_DBUS_INTERFACE ".InvalidOptions"
-#define WPAS_ERROR_INVALID_IFACE \
- WPAS_DBUS_INTERFACE ".InvalidInterface"
-
-#define WPAS_ERROR_ADD_ERROR \
- WPAS_DBUS_INTERFACE ".AddError"
-#define WPAS_ERROR_EXISTS_ERROR \
- WPAS_DBUS_INTERFACE ".ExistsError"
-#define WPAS_ERROR_REMOVE_ERROR \
- WPAS_DBUS_INTERFACE ".RemoveError"
-
-#define WPAS_ERROR_SCAN_ERROR \
- WPAS_DBUS_IFACE_INTERFACE ".ScanError"
-#define WPAS_ERROR_ADD_NETWORK_ERROR \
- WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError"
-#define WPAS_ERROR_INTERNAL_ERROR \
- WPAS_DBUS_IFACE_INTERFACE ".InternalError"
-#define WPAS_ERROR_REMOVE_NETWORK_ERROR \
- WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError"
-
-#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x"
-
-static pid_t supp_pid = 0;
-static int supp_activation_tries = 0;
-
-static gchar* supp_iface = NULL;
-static gchar* supp_iface_path = NULL;
-
-static gchar* supp_network_id = NULL;
-static gchar* supp_config_path = NULL;
-
-static gboolean supp_configured = FALSE; // Unused right now
-
-/* Callback for supplicant events */
-static supp_cb_fn supp_cb = NULL;
-static gpointer supp_cb_data = NULL;
-
-static void supp_configure();
-
-void supp_set_callback(supp_cb_fn cb, gpointer user_data)
-{
- supp_cb = cb;
- supp_cb_data = user_data;
-}
-
-static inline void supp_callback(int result, const char * message)
-{
- if (supp_cb) supp_cb(result, message, supp_cb_data);
-}
-
-static gboolean supp_set_interface_retry(gpointer data)
-{
- DLOG_DEBUG(__func__);
-
- if (supp_pid && supp_iface) supp_set_interface(supp_iface);
- return FALSE;
-}
-
-static void add_iface_reply_cb(DBusPendingCall *pending, void *user_data)
-{
- DBusMessage *reply;
- DBusError error;
-
- DLOG_DEBUG("%s", __func__);
-
- dbus_error_init(&error);
-
- reply = dbus_pending_call_steal_reply(pending);
-
- if (dbus_set_error_from_message(&error, reply)) {
- DLOG_WARN_L("Error in %s:%s", __func__, error.name);
-
- if (strcmp(DBUS_ERROR_SERVICE_UNKNOWN, error.name) == 0)
- {
- // Supplicant not (yet) active? Try later
- DLOG_DEBUG("Still waiting for supplicant");
- supp_activation_tries++;
- if (supp_activation_tries >= 3) {
- supp_callback(-1, error.name);
- } else {
- g_timeout_add(1000,
- supp_set_interface_retry,
- NULL);
- }
- } else {
- supp_callback(-1, error.name);
- }
-
- dbus_error_free(&error);
- } else if (reply) {
- // Move on to next step
- gchar* path;
- if (!dbus_message_get_args(
- reply, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- {
- supp_callback(-1, error.name);
- goto iface_reply_error;
- }
-
- supp_iface_path = g_strdup(path);
- DLOG_DEBUG("Got interface path: %s", supp_iface_path);
- if (supp_network_id && !supp_config_path) {
- supp_set_network_id(supp_network_id);
- } else if (supp_config_path && !supp_configured) {
- supp_configure();
- }
- }
-
-iface_reply_error:
- if (reply)
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
-}
-
-void supp_unset_interface()
-{
- // TODO
- // mostly uneeded, since we're killing the supplicant instead
- g_free(supp_iface_path);
- supp_iface_path = NULL;
- g_free(supp_iface);
- supp_iface = NULL;
-}
-
-void supp_set_interface(const char * iface)
-{
- DBusMessage *msg;
- DBusPendingCall *pending;
-
- DLOG_DEBUG("%s: %s", __func__, iface);
-
- if (supp_iface_path) {
- supp_unset_interface();
- }
-
- if (iface != supp_iface) {
- if (supp_iface) {
- g_free(supp_iface);
- }
-
- supp_iface = g_strdup(iface);
- }
-
- msg = new_dbus_method_call(
- WPAS_DBUS_SERVICE,
- WPAS_DBUS_PATH,
- WPAS_DBUS_INTERFACE,
- "addInterface");
-
- append_dbus_args(
- msg,
- DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_INVALID);
-
- if (!dbus_connection_send_with_reply(get_dbus_connection(),
- msg, &pending, -1))
- die("Out of memory");
-
- if (!dbus_pending_call_set_notify(pending,
- add_iface_reply_cb, NULL, NULL))
- die("Out of memory");
-
- dbus_message_unref(msg);
-}
-
-static void add_network_reply_cb(DBusPendingCall *pending, void *user_data)
-{
- DBusMessage *reply;
- DBusError error;
-
- DLOG_DEBUG("%s", __func__);
-
- dbus_error_init(&error);
-
- reply = dbus_pending_call_steal_reply(pending);
-
- if (dbus_set_error_from_message(&error, reply)) {
- DLOG_WARN_L("Error in %s:%s", __func__, error.name);
-
- supp_callback(-1, error.name);
- dbus_error_free(&error);
- } else if (reply) {
- // Move on to next step
- gchar* path;
- if (!dbus_message_get_args(
- reply, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- {
- supp_callback(-1, error.name);
- goto net_reply_error;
- }
-
- supp_config_path = g_strdup(path);
- DLOG_DEBUG("Got network path: %s", supp_iface_path);
- if (supp_iface_path && !supp_configured) {
- supp_configure();
- }
- }
-
-net_reply_error:
- if (reply)
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
-}
-
-void supp_unset_network_id()
-{
- g_free(supp_config_path);
- supp_config_path = NULL;
- g_free(supp_network_id);
- supp_network_id = NULL;
- // TODO
-}
-
-void supp_set_network_id(const char * network_id)
-{
- DBusMessage *msg;
- DBusPendingCall *pending;
-
- DLOG_DEBUG("%s: %s", __func__, network_id);
-
- if (supp_config_path) {
- supp_unset_network_id();
- }
-
- if (network_id != supp_network_id) {
- if (supp_network_id) {
- g_free(supp_network_id);
- }
-
- supp_network_id = g_strdup(network_id);
- }
-
- if (!supp_iface_path) {
- DLOG_DEBUG("Deferring network creation");
- return;
- }
-
- msg = new_dbus_method_call(
- WPAS_DBUS_SERVICE,
- supp_iface_path,
- WPAS_DBUS_IFACE_INTERFACE,
- "addNetwork");
-
- if (!dbus_connection_send_with_reply(get_dbus_connection(),
- msg, &pending, -1))
- die("Out of memory");
-
- if (!dbus_pending_call_set_notify(pending,
- add_network_reply_cb, NULL, NULL))
- die("Out of memory");
-
- dbus_message_unref(msg);
-}
-
-static gchar * wpas_params[3] = { "/sbin/wpa_supplicant", "-u", NULL };
-
-int supp_enable()
-{
- DLOG_DEBUG("%s", __func__);
-
- supp_activation_tries = 0;
-
- GError* error;
- GPid pid;
- if (!g_spawn_async(NULL, wpas_params, NULL,
- G_SPAWN_DO_NOT_REAP_CHILD,
- NULL, NULL, &pid, &error)) {
- DLOG_ERR("Couldn't spawn supplicant: %s", error->message);
- return -1;
- }
-
- supp_pid = pid;
-
- DLOG_INFO("Spawned %s , pid %d", wpas_params[0], pid);
-
- icd_watch_pid(supp_pid);
-
- return 0;
-}
-
-void supp_disable(void)
-{
- if (supp_pid) {
- DLOG_INFO("Killing supplicant (pid %d)", supp_pid);
- kill(supp_pid, SIGTERM);
- }
-
- // Consider everything as deconfigured
- g_free(supp_iface);
- supp_iface = NULL;
- g_free(supp_iface_path);
- supp_iface_path = NULL;
- g_free(supp_network_id);
- supp_network_id = NULL;
- g_free(supp_config_path);
- supp_config_path = NULL;
-
- supp_configured = FALSE;
-}
-
-int supp_is_active(void)
-{
- return supp_pid ? TRUE : FALSE;
-}
-
-void supp_handle_signal(gchar* old_state, gchar* new_state)
-{
- DLOG_DEBUG("Supplicant StateChange %s -> %s", old_state, new_state);
-
- if (strcmp(new_state, "COMPLETED") == 0)
- {
- supp_callback(SUPP_STATUS_CONNECTED, new_state);
- } else if (strcmp(new_state, "DISCONNECTED") == 0) {
- supp_callback(SUPP_STATUS_DISCONNECTED, new_state);
- }
-}
-
-void supp_handle_killed(void)
-{
- DLOG_DEBUG("%s", __func__);
-
- g_spawn_close_pid(supp_pid);
- supp_pid = 0;
-
- supp_disable();
-
- supp_callback(SUPP_STATUS_KILLED, NULL);
-}
-
-static void free_settings_item(gpointer data, gpointer user_data)
-{
- gconf_entry_free(data);
-}
-
-static void enable_reply_cb(DBusPendingCall *pending, void *user_data)
-{
- DBusMessage *reply;
- DBusError error;
-
- DLOG_DEBUG("%s", __func__);
-
- dbus_error_init(&error);
-
- reply = dbus_pending_call_steal_reply(pending);
-
- if (dbus_set_error_from_message(&error, reply)) {
- DLOG_WARN_L("Error in %s:%s", __func__, error.name);
-
- supp_callback(SUPP_STATUS_ERROR, error.name);
- dbus_error_free(&error);
- }
-
- if (reply)
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
-}
-
-static void supp_enable_network()
-{
- DBusMessage* message; //The full mesage we are going to send.
- DBusPendingCall *pending;
-
- message = dbus_message_new_method_call(
- WPAS_DBUS_SERVICE,
- supp_config_path,
- WPAS_DBUS_IFACE_NETWORK,
- WPAS_ENABLE_NETWORK_METHOD
- );
- if (!message) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- return;
- }
-
- // Send message
- if (!dbus_connection_send_with_reply(get_dbus_connection(),
- message, &pending, -1)) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto send_error;
- }
-
- if (!dbus_pending_call_set_notify(pending,
- enable_reply_cb, NULL, NULL)) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- }
-
- // Fall through
-send_error:
- dbus_message_unref(message);
-}
-
-static void configure_reply_cb(DBusPendingCall *pending, void *user_data)
-{
- DBusMessage *reply;
- DBusError error;
-
- DLOG_DEBUG("%s", __func__);
-
- dbus_error_init(&error);
-
- reply = dbus_pending_call_steal_reply(pending);
-
- if (dbus_set_error_from_message(&error, reply)) {
- DLOG_WARN_L("Error in %s:%s", __func__, error.name);
-
- supp_callback(-1, error.name);
- dbus_error_free(&error);
- } else if (reply) {
- supp_enable_network();
- }
-
- if (reply)
- dbus_message_unref(reply);
- dbus_pending_call_unref(pending);
-}
-
-static void supp_configure()
-{
- // This is going to be long
- DLOG_DEBUG("%s: %s", __func__, supp_network_id);
- GConfClient *client = gconf_client_get_default();
- GError *error = NULL;
-
- DBusMessage* message; //The full mesage we are going to send.
- DBusMessageIter iter, iter_dict;
- DBusPendingCall *pending;
-
- if (!client) {
- DLOG_ERR("Cannot get gconf client");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- return;
- }
-
- gchar * settings_path = g_strconcat(ICD_GCONF_PATH,
- "/", supp_network_id, NULL);
-
- GSList * settings = gconf_client_all_entries(client,
- settings_path, &error);
- if (error) {
- DLOG_ERR("Could not get setting:%s, error:%s", settings_path,
- error->message);
- g_free(settings_path);
- g_clear_error(&error);
- g_object_unref(client);
- supp_callback(-1, error->message);
- return;
- }
-
- message = dbus_message_new_method_call(
- WPAS_DBUS_SERVICE,
- supp_config_path,
- WPAS_DBUS_IFACE_NETWORK,
- WPAS_SET_NETWORK_METHOD
- );
- if (!message) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto msg_init_error;
- }
-
- dbus_message_iter_init_append(message, &iter);
- if (dbus_dict_open_write(&iter, &iter_dict) != 0) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto dict_init_error;
- }
-
- DLOG_DEBUG("Preparing to send %d settings", g_slist_length(settings));
-
- GSList* i;
- for (i = settings; i; i = g_slist_next(i)) {
- GConfEntry* entry = i->data;
- gchar * key = g_path_get_basename(gconf_entry_get_key(entry));
- GConfValue* value = gconf_entry_get_value(entry);
-
- if (g_ascii_strncasecmp(key,
- WPA_GCONF_SETTING_PREFIX,
- WPA_GCONF_SETTING_PREFIX_LEN)) {
- g_free(key);
- continue;
- }
-
- // Skip prefix
- key += WPA_GCONF_SETTING_PREFIX_LEN;
-
- switch (value->type) {
- case GCONF_VALUE_STRING:
- DLOG_DEBUG("Setting string %s = %s",
- key, gconf_value_get_string(value));
- dbus_dict_append_string(&iter_dict,
- key, gconf_value_get_string(value));
- break;
-
- case GCONF_VALUE_INT:
- DLOG_DEBUG("Setting int32 %s = %d",
- key, gconf_value_get_int(value));
- dbus_dict_append_int32(&iter_dict,
- key, gconf_value_get_int(value));
- break;
- default:
- DLOG_DEBUG("Unknown setting type for %s",
- key);
- break;
- }
-
- key -= WPA_GCONF_SETTING_PREFIX_LEN;
- g_free(key);
- }
-
- if (dbus_dict_close_write(&iter, &iter_dict) != 0) {
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto dict_close_error;
- }
-
- // Send message
- if (!dbus_connection_send_with_reply(get_dbus_connection(),
- message, &pending, -1)) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto send_error;
- }
-
- if (!dbus_pending_call_set_notify(pending,
- configure_reply_cb, NULL, NULL)) {
- DLOG_CRIT_L("Out of memory");
- supp_callback(-1, ICD_DBUS_ERROR_SYSTEM_ERROR);
- goto send_error;
- }
-
- // Fall through
-send_error:
-dict_close_error:
-dict_init_error:
- dbus_message_unref(message);
-msg_init_error:
- g_free(settings_path);
-
- g_slist_foreach(settings, free_settings_item, NULL);
- g_slist_free(settings);
-
- g_object_unref(client);
-}
-