summary refs log tree commit diff
path: root/src/devices/team
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
committerMichael Biebl <biebl@debian.org>2015-05-05 17:48:57 +0200
commitf408e27bccfacf347605a8d98649975a68f38a17 (patch)
tree654fd6695c31511baf919b1c0870d119a352ed75 /src/devices/team
parent2c032d8f1c6292c1338a615e6ec40252889ba85c (diff)
Imported Upstream version 1.0.2 upstream/1.0.2
Diffstat (limited to 'src/devices/team')
-rw-r--r--src/devices/team/Makefile.in30
-rw-r--r--src/devices/team/nm-device-team.c333
2 files changed, 226 insertions, 137 deletions
diff --git a/src/devices/team/Makefile.in b/src/devices/team/Makefile.in
index 4ee30e66..29ad0598 100644
--- a/src/devices/team/Makefile.in
+++ b/src/devices/team/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# Makefile.in generated by automake 1.15 from Makefile.am.
 # @configure_input@
 
-# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+# Copyright (C) 1994-2014 Free Software Foundation, Inc.
 
 # This Makefile.in is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -15,7 +15,17 @@
 @SET_MAKE@
 
 VPATH = @srcdir@
-am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__is_gnu_make = { \
+  if test -z '$(MAKELEVEL)'; then \
+    false; \
+  elif test -n '$(MAKE_HOST)'; then \
+    true; \
+  elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+    true; \
+  else \
+    false; \
+  fi; \
+}
 am__make_running_with_option = \
   case $${target_option-} in \
       ?) ;; \
@@ -81,8 +91,6 @@ host_triplet = @host@
 @WITH_TEAMDCTL_TRUE@am__append_1 = ${LIBTEAMDCTL_CFLAGS}
 @WITH_TEAMDCTL_TRUE@am__append_2 = $(LIBTEAMDCTL_LIBS)
 subdir = src/devices/team
-DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
-	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/compiler_warnings.m4 \
@@ -99,6 +107,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
 	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
@@ -211,6 +220,8 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+	$(top_srcdir)/build-aux/depcomp
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 ACLOCAL = @ACLOCAL@
 ALL_LINGUAS = @ALL_LINGUAS@
@@ -237,7 +248,6 @@ CXXCPP = @CXXCPP@
 CXXDEPMODE = @CXXDEPMODE@
 CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
-DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
 DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
@@ -272,6 +282,7 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -523,7 +534,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__confi
 	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/devices/team/Makefile'; \
 	$(am__cd) $(top_srcdir) && \
 	  $(AUTOMAKE) --gnu src/devices/team/Makefile
-.PRECIOUS: Makefile
 Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
 	@case '$?' in \
 	  *config.status*) \
@@ -595,14 +605,14 @@ distclean-compile:
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
 
 .c.obj:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
 .c.lo:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -830,6 +840,8 @@ uninstall-am: uninstall-pkglibLTLIBRARIES
 	mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
 	uninstall-am uninstall-pkglibLTLIBRARIES
 
+.PRECIOUS: Makefile
+
 include $(GLIB_MAKEFILE)
 
 @GNOME_CODE_COVERAGE_RULES@
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 16f1fcc2..49202c36 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -41,6 +41,7 @@
 #include "nm-team-enum-types.h"
 #include "nm-posix-signals.h"
 #include "nm-core-internal.h"
+#include "gsystem-local-alloc.h"
 
 #include "nm-device-team-glue.h"
 
@@ -51,8 +52,6 @@ G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
 #define NM_DEVICE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TEAM, NMDeviceTeamPrivate))
 
-static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
-
 typedef struct {
 	struct teamdctl *tdc;
 	GPid teamd_pid;
@@ -68,6 +67,8 @@ enum {
 	LAST_PROP
 };
 
+static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
+
 /******************************************************************/
 
 static guint32
@@ -77,7 +78,7 @@ get_generic_capabilities (NMDevice *device)
 }
 
 static gboolean
-is_available (NMDevice *device)
+is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	if (NM_DEVICE_GET_CLASS (device)->is_up)
 		return NM_DEVICE_GET_CLASS (device)->is_up (device);
@@ -87,6 +88,7 @@ is_available (NMDevice *device)
 static gboolean
 check_connection_available (NMDevice *device,
                             NMConnection *connection,
+                            NMDeviceCheckConAvailableFlags flags,
                             const char *specific_object)
 {
 	/* Connections are always available because the carrier state is determined
@@ -171,6 +173,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMSettingTeam *s_team = nm_connection_get_setting_team (connection);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 
 	if (!s_team) {
 		s_team = (NMSettingTeam *) nm_setting_team_new ();
@@ -178,8 +181,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 	g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, NULL, NULL);
 
-	teamd_start (device, s_team);
-	if (NM_DEVICE_TEAM_GET_PRIVATE (device)->teamd_pid > 0 && ensure_teamd_connection (device)) {
+	if (priv->tdc) {
 		const char *config = NULL;
 		int err;
 
@@ -261,49 +263,30 @@ master_update_slave_connection (NMDevice *self,
 /******************************************************************/
 
 static void
-teamd_timeout_remove (NMDevice *device)
+teamd_cleanup (NMDevice *device, gboolean free_tdc)
 {
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
-	if (priv->teamd_timeout) {
-		g_source_remove (priv->teamd_timeout);
-		priv->teamd_timeout = 0;
-	}
-}
-
-static void
-teamd_cleanup (NMDevice *device, gboolean device_state_failed)
-{
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
-
-	if (priv->teamd_dbus_watch) {
-		g_bus_unwatch_name (priv->teamd_dbus_watch);
-		priv->teamd_dbus_watch = 0;
-	}
-
 	if (priv->teamd_process_watch) {
 		g_source_remove (priv->teamd_process_watch);
 		priv->teamd_process_watch = 0;
 	}
 
+	if (priv->teamd_timeout) {
+		g_source_remove (priv->teamd_timeout);
+		priv->teamd_timeout = 0;
+	}
+
 	if (priv->teamd_pid > 0) {
 		nm_utils_kill_child_async (priv->teamd_pid, SIGTERM, LOGD_TEAM, "teamd", 2000, NULL, NULL);
 		priv->teamd_pid = 0;
 	}
 
-	if (priv->tdc) {
+	if (priv->tdc && free_tdc) {
 		teamdctl_disconnect (priv->tdc);
 		teamdctl_free (priv->tdc);
 		priv->tdc = NULL;
 	}
-
-	teamd_timeout_remove (device);
-
-	if (device_state_failed) {
-		if (nm_device_is_activating (device) ||
-		    (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED))
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-	}
 }
 
 static gboolean
@@ -314,11 +297,18 @@ teamd_timeout_cb (gpointer user_data)
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
 
 	g_return_val_if_fail (priv->teamd_timeout, FALSE);
+	priv->teamd_timeout = 0;
 
-	_LOGI (LOGD_TEAM, "teamd timed out.");
-	teamd_cleanup (device, TRUE);
+	if (priv->teamd_pid && !priv->tdc) {
+		/* Timed out launching our own teamd process */
+		_LOGW (LOGD_TEAM, "teamd timed out.");
+		teamd_cleanup (device, TRUE);
 
-	return FALSE;
+		g_warn_if_fail (nm_device_is_activating (device));
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
+
+	return G_SOURCE_REMOVE;
 }
 
 static void
@@ -335,43 +325,77 @@ teamd_dbus_appeared (GDBusConnection *connection,
 	g_return_if_fail (priv->teamd_dbus_watch);
 
 	_LOGI (LOGD_TEAM, "teamd appeared on D-Bus");
-	teamd_timeout_remove (device);
+	nm_device_queue_recheck_assume (device);
+
+	/* If another teamd grabbed the bus name while our teamd was starting,
+	 * just ignore the death of our teamd and run with the existing one.
+	 */
+	if (priv->teamd_process_watch) {
+		gs_unref_variant GVariant *ret = NULL;
+		guint32 pid;
+
+		ret = g_dbus_connection_call_sync (connection,
+		                                   "org.freedesktop.DBus",
+		                                   "/org/freedesktop/DBus",
+		                                   "org.freedesktop.DBus",
+		                                   "GetConnectionUnixProcessID",
+		                                   g_variant_new ("(s)", name_owner),
+		                                   NULL,
+		                                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
+		                                   2000,
+		                                   NULL,
+		                                   NULL);
+		g_variant_get (ret, "(u)", &pid);
+
+		if (pid != priv->teamd_pid)
+			teamd_cleanup (device, FALSE);
+	}
 
+	/* Grab a teamd control handle even if we aren't going to use it
+	 * immediately.  But if we are, and grabbing it failed, fail the
+	 * device activation.
+	 */
 	success = ensure_teamd_connection (device);
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) {
 		if (success)
 			nm_device_activate_schedule_stage2_device_config (device);
 		else if (!nm_device_uses_assumed_connection (device))
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
-		return;
 	}
 }
 
 static void
-teamd_dbus_vanished (GDBusConnection *connection,
+teamd_dbus_vanished (GDBusConnection *dbus_connection,
                      const gchar *name,
                      gpointer user_data)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state = nm_device_get_state (device);
 
 	g_return_if_fail (priv->teamd_dbus_watch);
 
-	if (priv->teamd_timeout) {
+	if (!priv->tdc) {
 		/* g_bus_watch_name will always raise an initial signal, to indicate whether the
-		 * name exists/not exists initially. Do not take this as a failure, until the
-		 * startup timeout is over.
-		 *
-		 * Note that g_bus_watch_name is guaranteed to alternate vanished/appeared signals,
-		 * so we won't hit this condition again (because the next signal is either 'appeared'
-		 * or 'timeout'). */
-		_LOGD (LOGD_TEAM, "teamd vanished from D-Bus (ignored)");
+		 * name exists/not exists initially. Do not take this as a failure if it hadn't
+		 * previously appeared.
+		 */
+		_LOGD (LOGD_TEAM, "teamd not on D-Bus (ignored)");
 		return;
 	}
 
 	_LOGI (LOGD_TEAM, "teamd vanished from D-Bus");
 	teamd_cleanup (device, TRUE);
+
+	/* Attempt to respawn teamd */
+	if (state >= NM_DEVICE_STATE_PREPARE && state <= NM_DEVICE_STATE_ACTIVATED) {
+		NMConnection *connection = nm_device_get_connection (device);
+
+		g_assert (connection);
+		if (!teamd_start (device, nm_connection_get_setting_team (connection)))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
 }
 
 static void
@@ -380,13 +404,24 @@ teamd_process_watch_cb (GPid pid, gint status, gpointer user_data)
 	NMDeviceTeam *self = NM_DEVICE_TEAM (user_data);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceState state = nm_device_get_state (device);
 
 	g_return_if_fail (priv->teamd_process_watch);
 
-	_LOGI (LOGD_TEAM, "teamd died with status %d", status);
-	priv->teamd_process_watch = 0;
+	_LOGD (LOGD_TEAM, "teamd died with status %d", status);
 	priv->teamd_pid = 0;
-	teamd_cleanup (device, TRUE);
+	priv->teamd_process_watch = 0;
+
+	/* If teamd quit within 5 seconds of starting, it's probably hosed
+	 * and will just die again, so fail the activation.
+	 */
+	if (priv->teamd_timeout &&
+	    (state >= NM_DEVICE_STATE_PREPARE) &&
+	    (state <= NM_DEVICE_STATE_ACTIVATED)) {
+		_LOGW (LOGD_TEAM, "teamd process quit unexpectedly; failing activation");
+		teamd_cleanup (device, TRUE);
+		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+	}
 }
 
 static void
@@ -407,28 +442,41 @@ teamd_child_setup (gpointer user_data G_GNUC_UNUSED)
 }
 
 static gboolean
+teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
+{
+	gs_unref_ptrarray GPtrArray *argv = NULL;
+	gs_free char *tmp_str = NULL;
+
+	if (!teamd_binary) {
+		teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
+		if (!teamd_binary) {
+			_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: teamd binary not found");
+			return FALSE;
+		}
+	}
+
+	argv = g_ptr_array_new ();
+	g_ptr_array_add (argv, (gpointer) teamd_binary);
+	g_ptr_array_add (argv, (gpointer) "-k");
+	g_ptr_array_add (argv, (gpointer) "-t");
+	g_ptr_array_add (argv, (gpointer) nm_device_get_iface (NM_DEVICE (self)));
+	g_ptr_array_add (argv, NULL);
+
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, NULL, error);
+}
+
+static gboolean
 teamd_start (NMDevice *device, NMSettingTeam *s_team)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	const char *iface = nm_device_get_ip_iface (device);
-	char *tmp_str = NULL;
-	const char *config;
+	gs_unref_ptrarray GPtrArray *argv = NULL;
+	gs_free_error GError *error = NULL;
+	gs_free char *tmp_str = NULL;
 	const char *teamd_binary;
-	GPtrArray *argv;
-	GError *error = NULL;
-	gboolean ret;
-	int status;
-
-	if (priv->teamd_dbus_watch ||
-	    priv->teamd_process_watch ||
-	    priv->teamd_pid > 0 ||
-	    priv->tdc ||
-	    priv->teamd_timeout)
-	{
-		/* Just return if teamd_start() was already called */
-		return TRUE;
-	}
+	const char *config;
 
 	teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
 	if (!teamd_binary) {
@@ -436,20 +484,12 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 		return FALSE;
 	}
 
-	/* Kill teamd for same named device first if it is there */
-	argv = g_ptr_array_new ();
-	g_ptr_array_add (argv, (gpointer) teamd_binary);
-	g_ptr_array_add (argv, (gpointer) "-k");
-	g_ptr_array_add (argv, (gpointer) "-t");
-	g_ptr_array_add (argv, (gpointer) iface);
-	g_ptr_array_add (argv, NULL);
-
-	_LOGD (LOGD_TEAM, "running: %s",
-	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
-	g_clear_pointer (&tmp_str, g_free);
-
-	ret = g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, nm_unblock_posix_signals, NULL, NULL, NULL, &status, &error);
-	g_ptr_array_free (argv, TRUE);
+	if (priv->teamd_process_watch || priv->teamd_pid > 0 || priv->tdc) {
+		g_warn_if_reached ();
+		if (!priv->teamd_pid)
+			teamd_kill (self, teamd_binary, NULL);
+		teamd_cleanup (device, TRUE);
+	}
 
 	/* Start teamd now */
 	argv = g_ptr_array_new ();
@@ -472,83 +512,90 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 		g_ptr_array_add (argv, (gpointer) "-gg");
 	g_ptr_array_add (argv, NULL);
 
-	_LOGD (LOGD_TEAM, "running: %s",
-	       (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
-	g_clear_pointer (&tmp_str, g_free);
-
-	/* Start a timeout for teamd to appear at D-Bus */
-	priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
-
-	/* Register D-Bus name watcher */
-	tmp_str = g_strdup_printf ("org.libteam.teamd.%s", iface);
-	priv->teamd_dbus_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
-	                                           tmp_str,
-	                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
-	                                           teamd_dbus_appeared,
-	                                           teamd_dbus_vanished,
-	                                           device,
-	                                           NULL);
-	g_free (tmp_str);
-
-	ret = g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-	                    &teamd_child_setup, NULL, &priv->teamd_pid, &error);
-	g_ptr_array_free (argv, TRUE);
-	if (!ret) {
+	_LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (gchar **) argv->pdata)));
+	if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+	                    teamd_child_setup, NULL, &priv->teamd_pid, &error)) {
 		_LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message);
-		g_clear_error (&error);
-		teamd_cleanup (device, FALSE);
+		teamd_cleanup (device, TRUE);
 		return FALSE;
 	}
 
+	/* Start a timeout for teamd to appear at D-Bus */
+	if (!priv->teamd_timeout)
+		priv->teamd_timeout = g_timeout_add_seconds (5, teamd_timeout_cb, device);
+
 	/* Monitor the child process so we know when it dies */
 	priv->teamd_process_watch = g_child_watch_add (priv->teamd_pid,
 	                                               teamd_process_watch_cb,
 	                                               device);
 
-	_LOGI (LOGD_TEAM, "Activation: (team) started teamd...");
+	_LOGI (LOGD_TEAM, "Activation: (team) started teamd [pid %u]...", (guint) priv->teamd_pid);
 	return TRUE;
 }
 
-static void
-teamd_stop (NMDevice *device)
-{
-	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
-	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
-
-	if (priv->teamd_pid > 0)
-		_LOGI (LOGD_TEAM, "Deactivation: stopping teamd...");
-	else
-		_LOGD (LOGD_TEAM, "Deactivation: stopping teamd (not started)...");
-	teamd_cleanup (device, FALSE);
-}
-
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 {
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+	gs_free_error GError *error = NULL;
 	NMConnection *connection;
 	NMSettingTeam *s_team;
+	const char *cfg;
 
 	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, reason);
-	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
-		connection = nm_device_get_connection (device);
-		g_assert (connection);
-		s_team = nm_connection_get_setting_team (connection);
-		g_assert (s_team);
-		if (teamd_start (device, s_team))
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-		else
-			ret = NM_ACT_STAGE_RETURN_FAILURE;
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	connection = nm_device_get_connection (device);
+	g_assert (connection);
+	s_team = nm_connection_get_setting_team (connection);
+	g_assert (s_team);
+
+	if (priv->tdc) {
+		/* If the existing teamd config is the same as we're about to use,
+		 * then we can proceed.  If it's not the same, and we have a PID,
+		 * kill it so we can respawn it with the right config.  If we don't
+		 * have a PID, then we must fail.
+		 */
+		cfg = teamdctl_config_get_raw (priv->tdc);
+		if (cfg && strcmp (cfg,  nm_setting_team_get_config (s_team)) == 0) {
+			_LOGD (LOGD_TEAM, "using existing matching teamd config");
+			return NM_ACT_STAGE_RETURN_SUCCESS;
+		}
+
+		if (!priv->teamd_pid) {
+			_LOGD (LOGD_TEAM, "existing teamd config mismatch; killing existing via teamdctl");
+			if (!teamd_kill (self, NULL, &error)) {
+				_LOGW (LOGD_TEAM, "existing teamd config mismatch; failed to kill existing teamd: %s", error->message);
+				*reason = NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED;
+				return NM_ACT_STAGE_RETURN_FAILURE;
+			}
+		}
+
+		_LOGD (LOGD_TEAM, "existing teamd config mismatch; respawning...");
+		teamd_cleanup (device, TRUE);
 	}
-	return ret;
+
+	return teamd_start (device, s_team) ?
+		NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 static void
 deactivate (NMDevice *device)
 {
-	teamd_stop (device);
+	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
+
+	if (priv->teamd_pid || priv->tdc)
+		_LOGI (LOGD_TEAM, "deactivation: stopping teamd...");
+
+	if (!priv->teamd_pid)
+		teamd_kill (self, NULL, NULL);
+	teamd_cleanup (device, TRUE);
 }
 
 static gboolean
@@ -694,6 +741,27 @@ nm_device_team_init (NMDeviceTeam * self)
 }
 
 static void
+constructed (GObject *object)
+{
+	NMDevice *device = NM_DEVICE (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+	char *tmp_str = NULL;
+
+	G_OBJECT_CLASS (nm_device_team_parent_class)->constructed (object);
+
+	/* Register D-Bus name watcher */
+	tmp_str = g_strdup_printf ("org.libteam.teamd.%s", nm_device_get_ip_iface (device));
+	priv->teamd_dbus_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM,
+	                                           tmp_str,
+	                                           G_BUS_NAME_WATCHER_FLAGS_NONE,
+	                                           teamd_dbus_appeared,
+	                                           teamd_dbus_vanished,
+	                                           NM_DEVICE (device),
+	                                           NULL);
+	g_free (tmp_str);
+}
+
+static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
@@ -730,7 +798,15 @@ set_property (GObject *object, guint prop_id,
 static void
 dispose (GObject *object)
 {
-	teamd_cleanup (NM_DEVICE (object), FALSE);
+	NMDevice *device = NM_DEVICE (object);
+	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (object);
+
+	if (priv->teamd_dbus_watch) {
+		g_bus_unwatch_name (priv->teamd_dbus_watch);
+		priv->teamd_dbus_watch = 0;
+	}
+
+	teamd_cleanup (device, TRUE);
 
 	G_OBJECT_CLASS (nm_device_team_parent_class)->dispose (object);
 }
@@ -746,6 +822,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
 	parent_class->connection_type = NM_SETTING_TEAM_SETTING_NAME;
 
 	/* virtual methods */
+	object_class->constructed = constructed;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->dispose = dispose;