summary refs log tree commit diff
path: root/src/supplicant-manager/nm-supplicant-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplicant-manager/nm-supplicant-manager.c')
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.c427
1 files changed, 200 insertions, 227 deletions
diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c
index a2cf58eb..8209da21 100644
--- a/src/supplicant-manager/nm-supplicant-manager.c
+++ b/src/supplicant-manager/nm-supplicant-manager.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2010 Red Hat, Inc.
  * Copyright (C) 2007 - 2008 Novell, Inc.
  */
 
@@ -26,19 +26,7 @@
 #include "nm-supplicant-manager.h"
 #include "nm-supplicant-interface.h"
 #include "nm-dbus-manager.h"
-#include "nm-marshal.h"
 #include "nm-logging.h"
-#include "nm-glib-compat.h"
-
-#define SUPPLICANT_POKE_INTERVAL 120
-
-typedef struct {
-	NMDBusManager *	dbus_mgr;
-	guint32         state;
-	GSList *        ifaces;
-	gboolean        dispose_has_run;
-	guint			poke_id;
-} NMSupplicantManagerPrivate;
 
 #define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
                                               NM_TYPE_SUPPLICANT_MANAGER, \
@@ -46,298 +34,283 @@ typedef struct {
 
 G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
 
+/* Properties */
+enum {
+	PROP_0 = 0,
+	PROP_AVAILABLE,
+	LAST_PROP
+};
 
-static void nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
-                                                      const char *name,
-                                                      const char *old,
-                                                      const char *new,
-                                                      gpointer user_data);
+typedef struct {
+	NMDBusManager * dbus_mgr;
+	guint           name_owner_id;
+	DBusGProxy *    proxy;
+	gboolean        running;
+	GHashTable *    ifaces;
+	guint           die_count_reset_id;
+	guint           die_count;
+	gboolean        disposed;
+} NMSupplicantManagerPrivate;
 
-static void nm_supplicant_manager_set_state (NMSupplicantManager * self,
-                                             guint32 new_state);
+/********************************************************************/
 
-static gboolean nm_supplicant_manager_startup (NMSupplicantManager * self);
+static inline gboolean
+die_count_exceeded (guint32 count)
+{
+	return count > 2;
+}
 
+NMSupplicantInterface *
+nm_supplicant_manager_iface_get (NMSupplicantManager * self,
+                                 const char *ifname,
+                                 gboolean is_wireless)
+{
+	NMSupplicantManagerPrivate *priv;
+	NMSupplicantInterface *iface = NULL;
+	gboolean start_now;
 
-/* Signals */
-enum {
-	STATE,       /* change in the manager's state */
-	LAST_SIGNAL
-};
-static guint nm_supplicant_manager_signals[LAST_SIGNAL] = { 0 };
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
+	g_return_val_if_fail (ifname != NULL, NULL);
 
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-NMSupplicantManager *
-nm_supplicant_manager_get (void)
-{
-	static NMSupplicantManager * singleton = NULL;
+	iface = g_hash_table_lookup (priv->ifaces, ifname);
+	if (!iface) {
+		/* If we're making the supplicant take a time out for a bit, don't
+		 * let the supplicant interface start immediately, just let it hang
+		 * around in INIT state until we're ready to talk to the supplicant
+		 * again.
+		 */
+		start_now = !die_count_exceeded (priv->die_count);
 
-	if (!singleton) {
-		singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL));
+		nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
+		iface = nm_supplicant_interface_new (self, ifname, is_wireless, start_now);
+		if (iface)
+			g_hash_table_insert (priv->ifaces, g_strdup (ifname), iface);
 	} else {
-		g_object_ref (singleton);
+		nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
 	}
 
-	g_assert (singleton);
-	return singleton;
+	return iface;
 }
 
-static gboolean
-poke_supplicant_cb (gpointer user_data)
+void
+nm_supplicant_manager_iface_release (NMSupplicantManager *self,
+                                     NMSupplicantInterface *iface)
 {
-	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
-	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	DBusGConnection *g_connection;
-	DBusGProxy *proxy;
-	const char *tmp = "ignoreme";
-
-	g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
-	proxy = dbus_g_proxy_new_for_name (g_connection,
-	                                   WPAS_DBUS_SERVICE,
-	                                   WPAS_DBUS_PATH,
-	                                   WPAS_DBUS_INTERFACE);
-	if (!proxy) {
-		nm_log_warn (LOGD_SUPPLICANT, "Error: could not init wpa_supplicant proxy");
-		goto out;
-	}
+	NMSupplicantManagerPrivate *priv;
+	const char *ifname, *op;
 
-	nm_log_info (LOGD_SUPPLICANT, "Trying to start the supplicant...");
-	dbus_g_proxy_call_no_reply (proxy, "getInterface", G_TYPE_STRING, tmp, G_TYPE_INVALID);
-	g_object_unref (proxy);
+	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
+	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));
 
-out:
-	/* Reschedule the poke */	
-	priv->poke_id = g_timeout_add_seconds (SUPPLICANT_POKE_INTERVAL,
-	                               poke_supplicant_cb,
-	                               (gpointer) self);
+	ifname = nm_supplicant_interface_get_ifname (iface);
+	g_assert (ifname);
 
-	return FALSE;
-}
+	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 
-static void
-nm_supplicant_manager_init (NMSupplicantManager * self)
-{
-	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	gboolean running;
+	g_return_if_fail (g_hash_table_lookup (priv->ifaces, ifname) == iface);
 
-	priv->dispose_has_run = FALSE;
-	priv->state = NM_SUPPLICANT_MANAGER_STATE_DOWN;
-	priv->dbus_mgr = nm_dbus_manager_get ();
-	priv->poke_id = 0;
+	/* Ask wpa_supplicant to remove this interface */
+	op = nm_supplicant_interface_get_object_path (iface);
+	if (priv->running && priv->proxy && op) {
+		dbus_g_proxy_call_no_reply (priv->proxy, "RemoveInterface",
+			                        DBUS_TYPE_G_OBJECT_PATH, op,
+			                        G_TYPE_INVALID);
+	}
 
-	running = nm_supplicant_manager_startup (self);
+	g_hash_table_remove (priv->ifaces, ifname);
+}
 
-	g_signal_connect (priv->dbus_mgr,
-	                  "name-owner-changed",
-	                  G_CALLBACK (nm_supplicant_manager_name_owner_changed),
-	                  self);
+gboolean
+nm_supplicant_manager_available (NMSupplicantManager *self)
+{
+	g_return_val_if_fail (self != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);
 
-	if (!running) {
-		/* Try to activate the supplicant */
-		priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
-	}
+	if (die_count_exceeded (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->die_count))
+		return FALSE;
+	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->running;
 }
 
 static void
-nm_supplicant_manager_dispose (GObject *object)
+set_running (NMSupplicantManager *self, gboolean now_running)
 {
-	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);
-
-	if (priv->dispose_has_run) {
-		G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
-		return;
-	}
-
-	priv->dispose_has_run = TRUE;
-
-	if (priv->poke_id) {
-		g_source_remove (priv->poke_id);
-		priv->poke_id = 0;
-	}
-
-	if (priv->dbus_mgr) {
-		g_object_unref (G_OBJECT (priv->dbus_mgr));
-		priv->dbus_mgr = NULL;
-	}
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	gboolean old_available = nm_supplicant_manager_available (self);
 
-	/* Chain up to the parent class */
-	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
+	priv->running = now_running;
+	if (old_available != nm_supplicant_manager_available (self))
+		g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
 }
 
 static void
-nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
+set_die_count (NMSupplicantManager *self, guint new_die_count)
 {
-	GObjectClass * object_class = G_OBJECT_CLASS (klass);
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	gboolean old_available = nm_supplicant_manager_available (self);
 
-	g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate));
+	priv->die_count = new_die_count;
+	if (old_available != nm_supplicant_manager_available (self))
+		g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
+}
 
-	object_class->dispose = nm_supplicant_manager_dispose;
-
-	/* Signals */
-	nm_supplicant_manager_signals[STATE] =
-		g_signal_new ("state",
-		              G_OBJECT_CLASS_TYPE (object_class),
-		              G_SIGNAL_RUN_LAST,
-		              G_STRUCT_OFFSET (NMSupplicantManagerClass, state),
-		              NULL, NULL,
-		              _nm_marshal_VOID__UINT_UINT,
-		              G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+static gboolean
+wpas_die_count_reset_cb (gpointer user_data)
+{
+	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+	/* Reset the die count back to zero, which allows use of the supplicant again */
+	priv->die_count_reset_id = 0;
+	set_die_count (self, 0);
+	nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant die count reset");
+	return FALSE;
 }
 
 static void
-nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
-                                          const char *name,
-										  const char *old_owner,
-										  const char *new_owner,
-                                          gpointer user_data)
+name_owner_changed (NMDBusManager *dbus_mgr,
+                    const char *name,
+                    const char *old_owner,
+                    const char *new_owner,
+                    gpointer user_data)
 {
-	NMSupplicantManager * self = (NMSupplicantManager *) user_data;
+	NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
 	gboolean old_owner_good = (old_owner && strlen (old_owner));
 	gboolean new_owner_good = (new_owner && strlen (new_owner));
 
-	/* Can't handle the signal if its not from the supplicant service */
+	/* We only care about the supplicant here */
 	if (strcmp (WPAS_DBUS_SERVICE, name) != 0)
 		return;
 
 	if (!old_owner_good && new_owner_good) {
-		gboolean running;
-
-		running = nm_supplicant_manager_startup (self);
-
-		if (running && priv->poke_id) {
-			g_source_remove (priv->poke_id);
-			priv->poke_id = 0;
-		}
+		nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant started");
+		set_running (self, TRUE);
 	} else if (old_owner_good && !new_owner_good) {
-		nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_DOWN);
+		nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant stopped");
 
-		if (priv->poke_id)
-			g_source_remove (priv->poke_id);
-
-		/* Poke the supplicant so that it gets activated by dbus system bus
-		 * activation.
+		/* Reschedule the die count reset timeout.  Every time the supplicant
+		 * dies we wait 10 seconds before resetting the counter.  If the
+		 * supplicant died more than twice before the timer is reset, then
+		 * we don't try to talk to the supplicant for a while.
 		 */
-		priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
+		if (priv->die_count_reset_id)
+			g_source_remove (priv->die_count_reset_id);
+		priv->die_count_reset_id = g_timeout_add_seconds (10, wpas_die_count_reset_cb, self);
+		set_die_count (self, priv->die_count + 1);
+
+		if (die_count_exceeded (priv->die_count)) {
+			nm_log_info (LOGD_SUPPLICANT,
+			             "wpa_supplicant die count %d; ignoring for 10 seconds",
+			             priv->die_count);
+		}
+
+		set_running (self, FALSE);
 	}
 }
 
+/*******************************************************************/
 
-guint32
-nm_supplicant_manager_get_state (NMSupplicantManager * self)
+NMSupplicantManager *
+nm_supplicant_manager_get (void)
 {
-	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);
+	static NMSupplicantManager *singleton = NULL;
 
-	return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->state;
+	if (!singleton)
+		singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL));
+	else
+		g_object_ref (singleton);
+
+	g_assert (singleton);
+	return singleton;
 }
 
 static void
-nm_supplicant_manager_set_state (NMSupplicantManager * self, guint32 new_state)
+nm_supplicant_manager_init (NMSupplicantManager * self)
 {
 	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-	guint32 old_state;
-
-	if (new_state == priv->state)
-		return;
+	DBusGConnection *bus;
 
-	old_state = priv->state;
-	priv->state = new_state;
-	g_signal_emit (self,
-	               nm_supplicant_manager_signals[STATE],
-	               0,
-	               priv->state,
-	               old_state);
+	priv->dbus_mgr = nm_dbus_manager_get ();
+	priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
+	                                        NM_DBUS_MANAGER_NAME_OWNER_CHANGED,
+	                                        G_CALLBACK (name_owner_changed),
+	                                        self);
+	priv->running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, WPAS_DBUS_SERVICE);
+
+	bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+	priv->proxy = dbus_g_proxy_new_for_name (bus,
+	                                         WPAS_DBUS_SERVICE,
+	                                         WPAS_DBUS_PATH,
+	                                         WPAS_DBUS_INTERFACE);
+
+	priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 }
 
-static gboolean
-nm_supplicant_manager_startup (NMSupplicantManager * self)
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 {
-	gboolean running;
-
-	/* FIXME: convert to pending call */
-	running = nm_dbus_manager_name_has_owner (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_mgr,
-	                                          WPAS_DBUS_SERVICE);
-	if (running)
-		nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_IDLE);
-
-	return running;
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 }
 
-NMSupplicantInterface *
-nm_supplicant_manager_get_iface (NMSupplicantManager * self,
-								 const char *ifname,
-								 gboolean is_wireless)
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
-	NMSupplicantManagerPrivate *priv;
-	NMSupplicantInterface * iface = NULL;
-	GSList * elt;
+	switch (prop_id) {
+	case PROP_AVAILABLE:
+		g_value_set_boolean (value, nm_supplicant_manager_available (NM_SUPPLICANT_MANAGER (object)));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
 
-	g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
-	g_return_val_if_fail (ifname != NULL, NULL);
+static void
+dispose (GObject *object)
+{
+	NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);
 
-	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	if (priv->disposed)
+		goto out;
+	priv->disposed = TRUE;
 
-	/* Ensure we don't already have this interface */
-	for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
-		NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
+	if (priv->die_count_reset_id)
+		g_source_remove (priv->die_count_reset_id);
 
-		if (!strcmp (ifname, nm_supplicant_interface_get_device (if_tmp))) {
-			iface = if_tmp;
-			break;
-		}
+	if (priv->dbus_mgr) {
+		if (priv->name_owner_id)
+			g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+		g_object_unref (G_OBJECT (priv->dbus_mgr));
 	}
 
-	if (!iface) {
-		nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
-		iface = nm_supplicant_interface_new (self, ifname, is_wireless);
-		if (iface)
-			priv->ifaces = g_slist_append (priv->ifaces, iface);
-	} else {
-		nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
-	}
+	g_hash_table_destroy (priv->ifaces);
 
-	return iface;
+	if (priv->proxy)
+		g_object_unref (priv->proxy);
+
+out:
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
 }
 
-void
-nm_supplicant_manager_release_iface (NMSupplicantManager * self,
-                                     NMSupplicantInterface * iface)
+static void
+nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
 {
-	NMSupplicantManagerPrivate *priv;
-	GSList * elt;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-	g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
-	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));
-
-	priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+	g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate));
 
-	for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
-		NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
-
-		if (if_tmp == iface) {
-			/* Remove the iface from the supplicant manager's list and
-			 * dereference to match additional reference in get_iface.
-			 */
-			priv->ifaces = g_slist_remove_link (priv->ifaces, elt);
-			g_slist_free_1 (elt);
-			g_object_unref (iface);
-			break;
-		}
-	}
-}
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
 
-const char *
-nm_supplicant_manager_state_to_string (guint32 state)
-{
-	switch (state) {
-	case NM_SUPPLICANT_MANAGER_STATE_DOWN:
-		return "down";
-	case NM_SUPPLICANT_MANAGER_STATE_IDLE:
-		return "idle";
-	default:
-		break;
-	}
-	return "unknown";
+	g_object_class_install_property (object_class, PROP_AVAILABLE,
+		g_param_spec_boolean (NM_SUPPLICANT_MANAGER_AVAILABLE,
+		                      "Available",
+		                      "Available",
+		                      FALSE,
+		                      G_PARAM_READABLE));
 }
 
-