summary refs log tree commit diff
path: root/callouts
diff options
context:
space:
mode:
Diffstat (limited to 'callouts')
-rw-r--r--callouts/Makefile.am8
-rw-r--r--callouts/Makefile.in82
-rw-r--r--callouts/nm-dispatcher-action.c675
-rw-r--r--callouts/nm-dispatcher-action.h16
-rw-r--r--callouts/nm-dispatcher.xml12
-rw-r--r--callouts/tests/Makefile.am7
-rw-r--r--callouts/tests/Makefile.in140
-rw-r--r--callouts/tests/test-dispatcher-envp.c1
8 files changed, 541 insertions, 400 deletions
diff --git a/callouts/Makefile.am b/callouts/Makefile.am
index 45d38b61..b27fdd79 100644
--- a/callouts/Makefile.am
+++ b/callouts/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS=. tests
+SUBDIRS = . tests
 
 ###########################################
 # Test libraries
@@ -24,7 +24,7 @@ nm_dhcp_client_action_SOURCES = \
 
 nm_dhcp_client_action_CPPFLAGS = \
 	$(DBUS_CFLAGS) \
-	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DNMCONFDIR=\"$(nmconfdir)\" \
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
 nm_dhcp_client_action_LDADD = $(DBUS_LIBS)
@@ -36,7 +36,6 @@ nm_avahi_autoipd_action_SOURCES = \
 nm_avahi_autoipd_action_CPPFLAGS = 		\
 	$(DBUS_CFLAGS)						\
 	$(GLIB_CFLAGS)					\
-	-DG_DISABLE_DEPRECATED				\
 	-DSYSCONFDIR=\"$(sysconfdir)\"		\
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
@@ -59,8 +58,7 @@ nm_dispatcher_action_CPPFLAGS = \
 	-I${top_builddir}/libnm-util \
 	$(DBUS_CFLAGS) \
 	$(GLIB_CFLAGS) \
-	-DG_DISABLE_DEPRECATED \
-	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DNMCONFDIR=\"$(nmconfdir)\" \
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
 nm_dispatcher_action_LDADD = \
diff --git a/callouts/Makefile.in b/callouts/Makefile.in
index f6650cec..c4ff5aa8 100644
--- a/callouts/Makefile.in
+++ b/callouts/Makefile.in
@@ -59,16 +59,18 @@ subdir = callouts
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
-	$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gtk-doc.m4 \
-	$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
-	$(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \
-	$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
-	$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libnl-check.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
-	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
-	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+	$(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -228,6 +230,10 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CXX = @CXX@
@@ -242,9 +248,7 @@ DBUS_SYS_DIR = @DBUS_SYS_DIR@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DHCLIENT_PATH = @DHCLIENT_PATH@
-DHCLIENT_VERSION = @DHCLIENT_VERSION@
 DHCPCD_PATH = @DHCPCD_PATH@
-DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
 DLLTOOL = @DLLTOOL@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
@@ -254,17 +258,14 @@ ECHO_T = @ECHO_T@
 EGREP = @EGREP@
 EXEEXT = @EXEEXT@
 FGREP = @FGREP@
+GENHTML = @GENHTML@
 GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
-GIO_CFLAGS = @GIO_CFLAGS@
-GIO_LIBS = @GIO_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GLIB_MAKEFILE = @GLIB_MAKEFILE@
 GLIB_MKENUMS = @GLIB_MKENUMS@
-GMODULE_CFLAGS = @GMODULE_CFLAGS@
-GMODULE_LIBS = @GMODULE_LIBS@
 GMSGFMT = @GMSGFMT@
 GMSGFMT_015 = @GMSGFMT_015@
 GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
@@ -305,6 +306,7 @@ IPTABLES_PATH = @IPTABLES_PATH@
 IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
 IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBDL = @LIBDL@
@@ -321,11 +323,7 @@ LIBNL2_LIBS = @LIBNL2_LIBS@
 LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
 LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
-LIBNL_GENL3_CFLAGS = @LIBNL_GENL3_CFLAGS@
-LIBNL_GENL3_LIBS = @LIBNL_GENL3_LIBS@
 LIBNL_LIBS = @LIBNL_LIBS@
-LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
-LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
@@ -340,6 +338,8 @@ MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
 MOC = @MOC@
 MSGFMT = @MSGFMT@
 MSGFMT_015 = @MSGFMT_015@
@@ -349,6 +349,7 @@ NMEDIT = @NMEDIT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
 NM_VERSION = @NM_VERSION@
 NSS_CFLAGS = @NSS_CFLAGS@
 NSS_LIBS = @NSS_LIBS@
@@ -375,13 +376,14 @@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
 QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
-RESOLVCONF_PATH = @RESOLVCONF_PATH@
 SED = @SED@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
-SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
-SYSTEMD_LIBS = @SYSTEMD_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
 UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
@@ -436,6 +438,11 @@ localedir = @localedir@
 localstatedir = @localstatedir@
 mandir = @mandir@
 mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
 pdfdir = @pdfdir@
 prefix = @prefix@
@@ -450,6 +457,10 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
 SUBDIRS = . tests
 
 ###########################################
@@ -469,7 +480,7 @@ nm_dhcp_client_action_SOURCES = \
 
 nm_dhcp_client_action_CPPFLAGS = \
 	$(DBUS_CFLAGS) \
-	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DNMCONFDIR=\"$(nmconfdir)\" \
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
 nm_dhcp_client_action_LDADD = $(DBUS_LIBS)
@@ -479,7 +490,6 @@ nm_avahi_autoipd_action_SOURCES = \
 nm_avahi_autoipd_action_CPPFLAGS = \
 	$(DBUS_CFLAGS)						\
 	$(GLIB_CFLAGS)					\
-	-DG_DISABLE_DEPRECATED				\
 	-DSYSCONFDIR=\"$(sysconfdir)\"		\
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
@@ -501,8 +511,7 @@ nm_dispatcher_action_CPPFLAGS = \
 	-I${top_builddir}/libnm-util \
 	$(DBUS_CFLAGS) \
 	$(GLIB_CFLAGS) \
-	-DG_DISABLE_DEPRECATED \
-	-DSYSCONFDIR=\"$(sysconfdir)\" \
+	-DNMCONFDIR=\"$(nmconfdir)\" \
 	-DLIBEXECDIR=\"$(libexecdir)\"
 
 nm_dispatcher_action_LDADD = \
@@ -666,25 +675,22 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm_dispatcher_action-nm-dispatcher-utils.Po@am__quote@
 
 .c.o:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
-@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@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 -o $@ $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
 
 .c.obj:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
-@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@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 -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
 
 .c.lo:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
-@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
diff --git a/callouts/nm-dispatcher-action.c b/callouts/nm-dispatcher-action.c
index a4ccde10..0e4b11a7 100644
--- a/callouts/nm-dispatcher-action.c
+++ b/callouts/nm-dispatcher-action.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) 2008 - 2011 Red Hat, Inc.
+ * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
 #include <syslog.h>
@@ -39,95 +39,237 @@
 #include "nm-dispatcher-action.h"
 #include "nm-dispatcher-utils.h"
 
-#define NMD_SCRIPT_DIR    SYSCONFDIR "/NetworkManager/dispatcher.d"
+#define NMD_SCRIPT_DIR NMCONFDIR "/dispatcher.d"
 
 static GMainLoop *loop = NULL;
 static gboolean debug = FALSE;
 
-static gboolean quit_timeout_cb (gpointer user_data);
+typedef struct {
+	GObject parent;
 
-typedef struct Handler Handler;
-typedef struct HandlerClass HandlerClass;
+	/* Private data */
+	guint quit_id;
+	gboolean persist;
+} Handler;
+
+typedef struct {
+  GObjectClass parent;
+} HandlerClass;
 
 GType handler_get_type (void);
 
-struct Handler {
-	GObject parent;
-};
+#define HANDLER_TYPE         (handler_get_type ())
+#define HANDLER(object)      (G_TYPE_CHECK_INSTANCE_CAST ((object), HANDLER_TYPE, Handler))
+#define HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HANDLER_TYPE, HandlerClass))
 
-struct HandlerClass {
-  GObjectClass parent;
-};
+G_DEFINE_TYPE(Handler, handler, G_TYPE_OBJECT)
 
-#define HANDLER_TYPE              (handler_get_type ())
-#define HANDLER(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), HANDLER_TYPE, Handler))
-#define HANDLER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), HANDLER_TYPE, HandlerClass))
-#define IS_HANDLER(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), HANDLER_TYPE))
-#define IS_HANDLER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), HANDLER_TYPE))
-#define HANDLER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), HANDLER_TYPE, HandlerClass))
+static void
+impl_dispatch (Handler *h,
+               const char *action,
+               GHashTable *connection_hash,
+               GHashTable *connection_props,
+               GHashTable *device_props,
+               GHashTable *device_ip4_props,
+               GHashTable *device_ip6_props,
+               GHashTable *device_dhcp4_props,
+               GHashTable *device_dhcp6_props,
+               const char *vpn_ip_iface,
+               GHashTable *vpn_ip4_props,
+               GHashTable *vpn_ip6_props,
+               DBusGMethodInvocation *context);
 
-G_DEFINE_TYPE(Handler, handler, G_TYPE_OBJECT)
+#include "nm-dispatcher-glue.h"
+
+
+static void
+handler_init (Handler *h)
+{
+}
+
+static void
+handler_class_init (HandlerClass *h_class)
+{
+}
+
+typedef struct Request Request;
+
+static void dispatch_one_script (Request *request);
 
 typedef struct {
-	DBusGConnection *g_connection;
-	DBusGProxy *bus_proxy;
-	guint quit_timeout;
-	gboolean persist;
+	Request *request;
+
+	char *script;
+	GPid pid;
+	DispatchResult result;
+	char *error;
+} ScriptInfo;
 
+struct Request {
 	Handler *handler;
-} Dispatcher;
 
-static gboolean
-nm_dispatcher_action (Handler *h,
-                      const char *action,
-                      GHashTable *connection_hash,
-                      GHashTable *connection_props,
-                      GHashTable *device_props,
-                      GHashTable *device_ip4_props,
-                      GHashTable *device_ip6_props,
-                      GHashTable *device_dhcp4_props,
-                      GHashTable *device_dhcp6_props,
-                      const char *vpn_ip_iface,
-                      GHashTable *vpn_ip4_props,
-                      GHashTable *vpn_ip6_props,
-                      GError **error);
+	DBusGMethodInvocation *context;
+	char *action;
+	char *iface;
+	char **envp;
+	GPtrArray *scripts;  /* list of ScriptInfo */
+	guint idx;
 
-#include "nm-dispatcher-glue.h"
+	guint script_watch_id;
+	guint script_timeout_id;
+};
 
+static void
+script_info_free (ScriptInfo *info)
+{
+	g_free (info->script);
+	g_free (info->error);
+	g_free (info);
+}
 
 static void
-handler_init (Handler *h)
+request_free (Request *request)
 {
+	g_free (request->action);
+	g_free (request->iface);
+	g_strfreev (request->envp);
+	if (request->scripts)
+		g_ptr_array_foreach (request->scripts, (GFunc) script_info_free, NULL);
+	g_ptr_array_free (request->scripts, TRUE);
+}
+
+static gboolean
+quit_timeout_cb (gpointer user_data)
+{
+	g_main_loop_quit (loop);
+	return FALSE;
 }
 
 static void
-handler_finalize (GObject *object)
+quit_timeout_reschedule (Handler *h)
 {
-	G_OBJECT_CLASS (handler_parent_class)->finalize (object);
+	if (h->quit_id)
+		g_source_remove (h->quit_id);
+	if (!h->persist)
+		h->quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
+}
+
+static gboolean
+next_script (gpointer user_data)
+{
+	Request *request = user_data;
+	GPtrArray *results;
+	GValueArray *item;
+	guint i;
+
+	quit_timeout_reschedule (request->handler);
+
+	request->idx++;
+	if (request->idx < request->scripts->len) {
+		dispatch_one_script (request);
+		return FALSE;
+	}
+
+	/* All done */
+	results = g_ptr_array_sized_new (request->scripts->len);
+	for (i = 0; i < request->scripts->len; i++) {
+		ScriptInfo *script = g_ptr_array_index (request->scripts, i);
+		GValue elt = {0, };
+
+		item = g_value_array_new (3);
+
+		/* Script path */
+		g_value_init (&elt, G_TYPE_STRING);
+		g_value_set_string (&elt, script->script);
+		g_value_array_append (item, &elt);
+		g_value_unset (&elt);
+
+		/* Result */
+		g_value_init (&elt, G_TYPE_UINT);
+		g_value_set_uint (&elt, script->result);
+		g_value_array_append (item, &elt);
+		g_value_unset (&elt);
+
+		/* Error */
+		g_value_init (&elt, G_TYPE_STRING);
+		g_value_set_string (&elt, script->error ? script->error : "");
+		g_value_array_append (item, &elt);
+		g_value_unset (&elt);
+
+		g_ptr_array_add (results, item);
+	}
+
+	dbus_g_method_return (request->context, results);
+
+	request_free (request);
+	return FALSE;
 }
 
 static void
-handler_class_init (HandlerClass *h_class)
+script_watch_cb (GPid pid, gint status, gpointer user_data)
 {
-  GObjectClass *gobject_class = G_OBJECT_CLASS (h_class);
+	ScriptInfo *script = user_data;
+	guint err;
+
+	g_assert (pid == script->pid);
+
+	script->request->script_watch_id = 0;
+	g_source_remove (script->request->script_timeout_id);
+	script->request->script_timeout_id = 0;
+
+	if (WIFEXITED (status)) {
+		err = WEXITSTATUS (status);
+		if (err == 0)
+			script->result = DISPATCH_RESULT_SUCCESS;
+		else {
+			script->error = g_strdup_printf ("Script '%s' exited with error status %d.",
+			                                 script->script, err);
+		}
+	} else if (WIFSTOPPED (status)) {
+		script->error = g_strdup_printf ("Script '%s' stopped unexpectedly with signal %d.",
+		                                 script->script, WSTOPSIG (status));
+	} else if (WIFSIGNALED (status)) {
+		script->error = g_strdup_printf ("Script '%s' died with signal %d",
+		                                 script->script, WTERMSIG (status));
+	} else {
+		script->error = g_strdup_printf ("Script '%s' died from an unknown cause",
+		                                 script->script);
+	}
 
-  gobject_class->finalize = handler_finalize;
+	if (script->result != DISPATCH_RESULT_SUCCESS) {
+		script->result = DISPATCH_RESULT_FAILED;
+		g_warning ("%s", script->error);
+	}
+
+	g_spawn_close_pid (script->pid);
+	next_script (script->request);
+}
+
+static gboolean
+script_timeout_cb (gpointer user_data)
+{
+	ScriptInfo *script = user_data;
+
+	g_source_remove (script->request->script_watch_id);
+	script->request->script_watch_id = 0;
+	script->request->script_timeout_id = 0;
+
+	g_warning ("Script '%s' took too long; killing it.", script->script);
+
+	if (kill (script->pid, 0) == 0)
+		kill (script->pid, SIGKILL);
+	waitpid (script->pid, NULL, 0);
+
+	script->error = g_strdup_printf ("Script '%s' timed out.", script->script);
+	script->result = DISPATCH_RESULT_TIMEOUT;
+
+	g_spawn_close_pid (script->pid);
+	g_idle_add (next_script, script->request);
+	return FALSE;
 }
 
-/*
- * nmd_permission_check
- *
- * Verify that the given script has the permissions we want.  Specifically,
- * ensure that the file is
- *	- A regular file.
- *	- Owned by root.
- *	- Not writable by the group or by other.
- *	- Not setuid.
- *	- Executable by the owner.
- *
- */
 static inline gboolean
-nmd_permission_check (struct stat *s, GError **error)
+check_permissions (struct stat *s, GError **error)
 {
 	g_return_val_if_fail (s != NULL, FALSE);
 	g_return_val_if_fail (error != NULL, FALSE);
@@ -160,223 +302,176 @@ nmd_permission_check (struct stat *s, GError **error)
 	return TRUE;
 }
 
-
-/*
- * nmd_is_valid_filename
- *
- * Verify that the given script is a valid file name. Specifically,
- * ensure that the file:
- *	- is not a editor backup file
- *	- is not a package management file
- *	- does not start with '.'
- */
-static inline gboolean
-nmd_is_valid_filename (const char *file_name)
+static gboolean
+check_filename (const char *file_name)
 {
 	char *bad_suffixes[] = { "~", ".rpmsave", ".rpmorig", ".rpmnew", NULL };
 	char *tmp;
-	int i;
+	guint i;
+
+	/* File must not be a backup file, package management file, or start with '.' */
 
 	if (file_name[0] == '.')
 		return FALSE;
 	for (i = 0; bad_suffixes[i]; i++) {
-		if (g_str_has_suffix(file_name, bad_suffixes[i]))
+		if (g_str_has_suffix (file_name, bad_suffixes[i]))
 			return FALSE;
 	}
-	tmp = g_strrstr(file_name, ".dpkg-");
-	if (tmp && (tmp == strrchr(file_name,'.')))
+	tmp = g_strrstr (file_name, ".dpkg-");
+	if (tmp && (tmp == strrchr (file_name, '.')))
 		return FALSE;
 	return TRUE;
 }
 
-static gint
-sort_files (gconstpointer a, gconstpointer b)
+static void
+child_setup (gpointer user_data G_GNUC_UNUSED)
 {
-	char *a_base = NULL, *b_base = NULL;
-	int ret = 0;
-
-	if (a && !b)
-		return 1;
-	if (!a && !b)
-		return 0;
-	if (!a && b)
-		return -1;
-
-	a_base = g_path_get_basename (a);
-	b_base = g_path_get_basename (b);
-
-	ret = strcmp (a_base, b_base);
-
-	g_free (a_base);
-	g_free (b_base);
-	return ret;
+	/* We are in the child process at this point */
+	/* Give child a different process group to ensure signal separation. */
+	pid_t pid = getpid ();
+	setpgid (pid, pid);
 }
 
 static void
-child_setup (gpointer user_data G_GNUC_UNUSED)
+dispatch_one_script (Request *request)
 {
-        /* We are in the child process at this point */
-		/* Give child a different process group to ensure signal separation. */
-        pid_t pid = getpid ();
-        setpgid (pid, pid);
+	GError *error = NULL;
+	gchar *argv[4];
+	ScriptInfo *script = g_ptr_array_index (request->scripts, request->idx);
+
+	argv[0] = script->script;
+	argv[1] = request->iface ? request->iface : "none";
+	argv[2] = request->action;
+	argv[3] = NULL;
+
+	if (debug)
+		g_message ("Script: %s %s %s", script->script, request->iface ? request->iface : "(none)", request->action);
+
+	if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, child_setup, request, &script->pid, &error)) {
+		request->script_watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script);
+		request->script_timeout_id = g_timeout_add_seconds (3, script_timeout_cb, script);
+	} else {
+		g_warning ("Failed to execute script '%s': (%d) %s",
+		           script->script, error->code, error->message);
+		script->result = DISPATCH_RESULT_EXEC_FAILED;
+		script->error = g_strdup (error->message);
+		g_clear_error (&error);
+
+		/* Try the next script */
+		g_idle_add (next_script, request);
+	}
 }
 
-static void
-dispatch_scripts (const char *action, const char *iface, char **envp)
+static GSList *
+find_scripts (void)
 {
 	GDir *dir;
 	const char *filename;
-	GSList *scripts = NULL, *iter;
+	GSList *sorted = NULL;
 	GError *error = NULL;
 
 	if (!(dir = g_dir_open (NMD_SCRIPT_DIR, 0, &error))) {
-		g_warning ("g_dir_open() could not open '" NMD_SCRIPT_DIR "'.  '%s'",
-		           error->message);
+		g_warning ("Failed to open dispatcher directory '%s': (%d) %s",
+		           NMD_SCRIPT_DIR, error->code, error->message);
 		g_error_free (error);
-		return;
+		return NULL;
 	}
 
 	while ((filename = g_dir_read_name (dir))) {
-		char *file_path;
-		struct stat	s;
-		GError *pc_error = NULL;
+		char *path;
+		struct stat	st;
 		int err;
 
-		if (!nmd_is_valid_filename (filename))
+		if (!check_filename (filename))
 			continue;
 
-		file_path = g_build_filename (NMD_SCRIPT_DIR, filename, NULL);
+		path = g_build_filename (NMD_SCRIPT_DIR, filename, NULL);
 
-		err = stat (file_path, &s);
-		if (err) {
-			g_warning ("Script '%s' could not be stated: %d", file_path, err);
-			g_free (file_path);
-			continue;
-		}
-
-		if (!nmd_permission_check (&s, &pc_error)) {
-			g_warning ("Script '%s' could not be executed: %s", file_path, pc_error->message);
-			g_error_free (pc_error);
-			g_free (file_path);
+		err = stat (path, &st);
+		if (err)
+			g_warning ("Failed to stat '%s': %d", path, err);
+		else if (!check_permissions (&st, &error)) {
+			g_warning ("Cannot execute '%s': %s", path, error->message);
+			g_clear_error (&error);
 		} else {
 			/* success */
-			scripts = g_slist_insert_sorted (scripts, file_path, sort_files);
+			sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0);
 		}
 	}
 	g_dir_close (dir);
 
-	for (iter = scripts; iter; iter = g_slist_next (iter)) {
-		gchar *argv[4];
-		gint status = -1;
-
-		argv[0] = (char *) iter->data;
-		argv[1] = iface ? (char *) iface : "none";
-		argv[2] = (char *) action;
-		argv[3] = NULL;
-
-		if (debug)
-			g_message ("Script: %s %s %s", (char *) iter->data, iface ? (char *) iface : "(none)", (char *) action);
-
-		error = NULL;
-		if (g_spawn_sync ("/", argv, envp, 0, child_setup, NULL, NULL, NULL, &status, &error)) {
-			if (WIFEXITED (status)) {
-				if (WEXITSTATUS (status) != 0)
-					g_warning ("Script '%s' exited with error status %d.",
-					           (char *) iter->data, WEXITSTATUS (status));
-			} else
-				g_warning ("Script '%s' exited abnormally.", (char *) iter->data);
-		} else {
-			g_warning ("Could not run script '%s': (%d) %s",
-			           (char *) iter->data, error->code, error->message);
-			g_error_free (error);
-		}
-	}
-
-	g_slist_foreach (scripts, (GFunc) g_free, NULL);
-	g_slist_free (scripts);
+	return sorted;
 }
 
-static gboolean
-nm_dispatcher_action (Handler *h,
-                      const char *action,
-                      GHashTable *connection_hash,
-                      GHashTable *connection_props,
-                      GHashTable *device_props,
-                      GHashTable *device_ip4_props,
-                      GHashTable *device_ip6_props,
-                      GHashTable *device_dhcp4_props,
-                      GHashTable *device_dhcp6_props,
-                      const char *vpn_ip_iface,
-                      GHashTable *vpn_ip4_props,
-                      GHashTable *vpn_ip6_props,
-                      GError **error)
+static void
+impl_dispatch (Handler *h,
+               const char *str_action,
+               GHashTable *connection_hash,
+               GHashTable *connection_props,
+               GHashTable *device_props,
+               GHashTable *device_ip4_props,
+               GHashTable *device_ip6_props,
+               GHashTable *device_dhcp4_props,
+               GHashTable *device_dhcp6_props,
+               const char *vpn_ip_iface,
+               GHashTable *vpn_ip4_props,
+               GHashTable *vpn_ip6_props,
+               DBusGMethodInvocation *context)
 {
-	Dispatcher *d = g_object_get_data (G_OBJECT (h), "dispatcher");
-	char **envp, **p;
+	GSList *sorted_scripts = NULL;
+	GSList *iter;
+	Request *request;
+	char **p;
 	char *iface = NULL;
 
-	/* Back off the quit timeout */
-	if (d->quit_timeout)
-		g_source_remove (d->quit_timeout);
-	if (!d->persist)
-		d->quit_timeout = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
-
-	envp = nm_dispatcher_utils_construct_envp (action,
-	                                           connection_hash,
-	                                           connection_props,
-	                                           device_props,
-	                                           device_ip4_props,
-	                                           device_ip6_props,
-	                                           device_dhcp4_props,
-	                                           device_dhcp6_props,
-	                                           vpn_ip_iface,
-	                                           vpn_ip4_props,
-	                                           vpn_ip6_props,
-	                                           &iface);
+	sorted_scripts = find_scripts ();
+
+	if (!sorted_scripts) {
+		dbus_g_method_return (context, g_ptr_array_new ());
+		return;
+	}
+
+	quit_timeout_reschedule (h);
+
+	request = g_malloc0 (sizeof (*request));
+	request->handler = h;
+	request->context = context;
+	request->action = g_strdup (str_action);
+
+	request->envp = nm_dispatcher_utils_construct_envp (str_action,
+	                                                    connection_hash,
+	                                                    connection_props,
+	                                                    device_props,
+	                                                    device_ip4_props,
+	                                                    device_ip6_props,
+	                                                    device_dhcp4_props,
+	                                                    device_dhcp6_props,
+	                                                    vpn_ip_iface,
+	                                                    vpn_ip4_props,
+	                                                    vpn_ip6_props,
+	                                                    &iface);
 
 	if (debug) {
-		g_message ("------------ Script Environment ------------");
-		for (p = envp; *p; p++)
+		g_message ("------------ Action ID %p '%s' Interface %s Environment ------------",
+		           context, str_action, iface ? iface : "(none)");
+		for (p = request->envp; *p; p++)
 			g_message ("  %s", *p);
 		g_message ("\n");
 	}
 
-	dispatch_scripts (action, iface, envp);
-	g_strfreev (envp);
-	g_free (iface);
+	request->iface = g_strdup (iface);
 
-	return TRUE;
-}
-
-static gboolean
-start_dbus_service (Dispatcher *d)
-{
-	int request_name_result;
-	GError *err = NULL;
-	gboolean success = FALSE;
-
-	if (!dbus_g_proxy_call (d->bus_proxy, "RequestName", &err,
-							G_TYPE_STRING, NM_DISPATCHER_DBUS_SERVICE,
-							G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
-							G_TYPE_INVALID,
-							G_TYPE_UINT, &request_name_result,
-							G_TYPE_INVALID)) {
-		g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service.\n"
-		           "  Message: '%s'", err->message);
-		g_error_free (err);
-		goto out;
-	}
-
-	if (request_name_result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-		g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service "
-		           "as it is already taken.  Return: %d",
-		           request_name_result);
-		goto out;
+	request->scripts = g_ptr_array_sized_new (5);
+	for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) {
+		ScriptInfo *s = g_malloc0 (sizeof (*s));
+		s->request = request;
+		s->script = iter->data;
+		g_ptr_array_add (request->scripts, s);
 	}
-	success = TRUE;
+	g_slist_free (sorted_scripts);
 
-out:
-	return success;
+	/* start dispatching scripts */
+	dispatch_one_script (request);
 }
 
 static void
@@ -386,42 +481,67 @@ destroy_cb (DBusGProxy *proxy, gpointer user_data)
 	g_main_loop_quit (loop);
 }
 
-static gboolean
-dbus_init (Dispatcher *d)
+static DBusGConnection *
+dbus_init (void)
 {
-	GError *err = NULL;
+	GError *error = NULL;
+	DBusGConnection *bus;
 	DBusConnection *connection;
-	
+	DBusGProxy *proxy;
+	int result;
+
 	dbus_connection_set_change_sigpipe (TRUE);
 
-	d->g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &err);
-	if (!d->g_connection) {
+	bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+	if (!bus) {
 		g_warning ("Could not get the system bus.  Make sure "
 		           "the message bus daemon is running!  Message: %s",
-		           err->message);
-		g_error_free (err);
-		return FALSE;
+		           error->message);
+		g_error_free (error);
+		return NULL;
 	}
 
 	/* Clean up nicely if we get kicked off the bus */
-	connection = dbus_g_connection_get_connection (d->g_connection);
+	connection = dbus_g_connection_get_connection (bus);
 	dbus_connection_set_exit_on_disconnect (connection, FALSE);
 
-	d->bus_proxy = dbus_g_proxy_new_for_name (d->g_connection,
-	                                          "org.freedesktop.DBus",
-	                                          "/org/freedesktop/DBus",
-	                                          "org.freedesktop.DBus");
-	if (!d->bus_proxy) {
-		g_warning ("Could not get the DBus object!");
+	proxy = dbus_g_proxy_new_for_name (bus,
+	                                   "org.freedesktop.DBus",
+	                                   "/org/freedesktop/DBus",
+	                                   "org.freedesktop.DBus");
+	if (!proxy) {
+		g_warning ("Could not create the DBus proxy!");
 		goto error;
 	}
 
-	g_signal_connect (d->bus_proxy, "destroy", G_CALLBACK (destroy_cb), NULL);
+	g_signal_connect (proxy, "destroy", G_CALLBACK (destroy_cb), NULL);
 
-	return TRUE;
+	if (!dbus_g_proxy_call (proxy, "RequestName", &error,
+	                        G_TYPE_STRING, NM_DISPATCHER_DBUS_SERVICE,
+	                        G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
+	                        G_TYPE_INVALID,
+	                        G_TYPE_UINT, &result,
+	                        G_TYPE_INVALID)) {
+		g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service.\n"
+		           "  Message: '%s'", error->message);
+		g_error_free (error);
+		goto error;
+	}
 
-error:	
-	return FALSE;
+	if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+		g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service "
+		           "as it is already taken.  Result: %d",
+		           result);
+		goto error;
+	}
+
+	return bus;
+
+error:
+	if (proxy)
+		g_object_unref (proxy);
+	dbus_g_connection_unref (bus);
+	return NULL;
 }
 
 static void
@@ -433,30 +553,25 @@ log_handler (const gchar *log_domain,
 	int syslog_priority;	
 
 	switch (log_level) {
-		case G_LOG_LEVEL_ERROR:
-			syslog_priority = LOG_CRIT;
-			break;
-
-		case G_LOG_LEVEL_CRITICAL:
-			syslog_priority = LOG_ERR;
-			break;
-
-		case G_LOG_LEVEL_WARNING:
-			syslog_priority = LOG_WARNING;
-			break;
-
-		case G_LOG_LEVEL_MESSAGE:
-			syslog_priority = LOG_NOTICE;
-			break;
-
-		case G_LOG_LEVEL_DEBUG:
-			syslog_priority = LOG_DEBUG;
-			break;
-
-		case G_LOG_LEVEL_INFO:
-		default:
-			syslog_priority = LOG_INFO;
-			break;
+	case G_LOG_LEVEL_ERROR:
+		syslog_priority = LOG_CRIT;
+		break;
+	case G_LOG_LEVEL_CRITICAL:
+		syslog_priority = LOG_ERR;
+		break;
+	case G_LOG_LEVEL_WARNING:
+		syslog_priority = LOG_WARNING;
+		break;
+	case G_LOG_LEVEL_MESSAGE:
+		syslog_priority = LOG_NOTICE;
+		break;
+	case G_LOG_LEVEL_DEBUG:
+		syslog_priority = LOG_DEBUG;
+		break;
+	case G_LOG_LEVEL_INFO:
+	default:
+		syslog_priority = LOG_INFO;
+		break;
 	}
 
 	syslog (syslog_priority, "%s", message);
@@ -502,20 +617,14 @@ setup_signals (void)
 	sigaction (SIGINT,  &action, NULL);
 }
 
-static gboolean
-quit_timeout_cb (gpointer user_data)
-{
-	g_main_loop_quit (loop);
-	return FALSE;
-}
-
 int
 main (int argc, char **argv)
 {
-	Dispatcher *d = g_malloc0 (sizeof (Dispatcher));
 	GOptionContext *opt_ctx;
 	GError *error = NULL;
 	gboolean persist = FALSE;
+	DBusGConnection *bus;
+	Handler *handler;
 
 	GOptionEntry entries[] = {
 		{ "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL },
@@ -530,7 +639,6 @@ main (int argc, char **argv)
 	if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) {
 		g_warning ("%s\n", error->message);
 		g_error_free (error);
-		g_free (d);
 		return 1;
 	}
 
@@ -544,30 +652,27 @@ main (int argc, char **argv)
 
 	loop = g_main_loop_new (NULL, FALSE);
 
-	if (!dbus_init (d))
-		return 1;
-	if (!start_dbus_service (d))
+	bus = dbus_init ();
+	if (!bus)
 		return 1;
 
-	d->persist = persist;
-	d->handler = g_object_new (HANDLER_TYPE, NULL);
-	if (!d->handler)
+	handler = g_object_new (HANDLER_TYPE, NULL);
+	if (!handler)
 		return 1;
-	g_object_set_data (G_OBJECT (d->handler), "dispatcher", d);
+	handler->persist = persist;
 
 	dbus_g_object_type_install_info (HANDLER_TYPE, &dbus_glib_nm_dispatcher_object_info);
-	dbus_g_connection_register_g_object (d->g_connection,
+	dbus_g_connection_register_g_object (bus,
 	                                     NM_DISPATCHER_DBUS_PATH,
-	                                     G_OBJECT (d->handler));
+	                                     G_OBJECT (handler));
 
 	if (!persist)
-		d->quit_timeout = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
+		handler->quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL);
 
 	g_main_loop_run (loop);
 
-	g_object_unref (d->handler);
-	dbus_g_connection_unref (d->g_connection);
-	g_free (d);
+	g_object_unref (handler);
+	dbus_g_connection_unref (bus);
 
 	if (!debug)
 		logging_shutdown ();
diff --git a/callouts/nm-dispatcher-action.h b/callouts/nm-dispatcher-action.h
index e4f2fab2..9fea487a 100644
--- a/callouts/nm-dispatcher-action.h
+++ b/callouts/nm-dispatcher-action.h
@@ -15,9 +15,15 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 - 2012 Red Hat, Inc.
  */
 
+#include <dbus/dbus-glib.h>
+
+/* dbus-glib types for dispatcher call return value */
+#define DISPATCHER_TYPE_RESULT       (dbus_g_type_get_struct ("GValueArray", G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_INVALID))
+#define DISPATCHER_TYPE_RESULT_ARRAY (dbus_g_type_get_collection ("GPtrArray", DISPATCHER_TYPE_RESULT))
+
 #define NM_DISPATCHER_DBUS_SERVICE "org.freedesktop.nm_dispatcher"
 #define NM_DISPATCHER_DBUS_IFACE   "org.freedesktop.nm_dispatcher"
 #define NM_DISPATCHER_DBUS_PATH    "/org/freedesktop/nm_dispatcher"
@@ -30,3 +36,11 @@
 #define NMD_DEVICE_PROPS_STATE            "state"
 #define NMD_DEVICE_PROPS_PATH             "path"
 
+typedef enum {
+	DISPATCH_RESULT_UNKNOWN = 0,
+	DISPATCH_RESULT_SUCCESS = 1,
+	DISPATCH_RESULT_EXEC_FAILED = 2,
+	DISPATCH_RESULT_FAILED = 3,
+	DISPATCH_RESULT_TIMEOUT = 4,
+} DispatchResult;
+
diff --git a/callouts/nm-dispatcher.xml b/callouts/nm-dispatcher.xml
index 92780b17..cba61508 100644
--- a/callouts/nm-dispatcher.xml
+++ b/callouts/nm-dispatcher.xml
@@ -8,6 +8,9 @@
         INTERNAL; not public API.  Perform an action.
       </tp:docstring>
 
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_dispatch"/>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
       <arg name="action" type="s" direction="in">
         <tp:docstring>
           The action being performed.
@@ -72,6 +75,15 @@
         </tp:docstring>
       </arg>
 
+      <arg name="results" type="a(sus)" direction="out">
+        <tp:docstring>
+          Results of dispatching operations.  Each element of the returned
+          array is a struct containing the path of an executed script (s),
+          the result of running that script (u), and a description of the
+          result (s).
+        </tp:docstring>
+      </arg>
+
     </method>
   </interface>
 </node>
diff --git a/callouts/tests/Makefile.am b/callouts/tests/Makefile.am
index 93d3fae7..e8a13fc6 100644
--- a/callouts/tests/Makefile.am
+++ b/callouts/tests/Makefile.am
@@ -1,3 +1,5 @@
+if ENABLE_TESTS
+
 INCLUDES = \
 	-I$(top_srcdir)/include \
 	-I${top_builddir}/include \
@@ -25,16 +27,13 @@ test_dispatcher_envp_LDADD = \
 
 ###########################################
 
-if WITH_TESTS
-
 check-local: test-dispatcher-envp
 	$(abs_builddir)/test-dispatcher-envp $(abs_srcdir)
 
-endif
-
 EXTRA_DIST= \
 	dispatcher-old-down \
 	dispatcher-old-up \
 	dispatcher-old-vpn-down \
 	dispatcher-old-vpn-up
 
+endif
diff --git a/callouts/tests/Makefile.in b/callouts/tests/Makefile.in
index b00728aa..ca39f716 100644
--- a/callouts/tests/Makefile.in
+++ b/callouts/tests/Makefile.in
@@ -51,21 +51,23 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-noinst_PROGRAMS = test-dispatcher-envp$(EXEEXT)
+@ENABLE_TESTS_TRUE@noinst_PROGRAMS = test-dispatcher-envp$(EXEEXT)
 subdir = callouts/tests
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
-	$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/gtk-doc.m4 \
-	$(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \
-	$(top_srcdir)/m4/intltool.m4 $(top_srcdir)/m4/introspection.m4 \
-	$(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
-	$(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libnl-check.m4 \
-	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
-	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
-	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
-	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
-	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libnl-check.m4 $(top_srcdir)/m4/libtool.m4 \
+	$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+	$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+	$(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+	$(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/vapigen.m4 \
+	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
 mkinstalldirs = $(install_sh) -d
@@ -73,14 +75,14 @@ CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
-am_test_dispatcher_envp_OBJECTS =  \
-	test_dispatcher_envp-test-dispatcher-envp.$(OBJEXT)
+am__test_dispatcher_envp_SOURCES_DIST = test-dispatcher-envp.c
+@ENABLE_TESTS_TRUE@am_test_dispatcher_envp_OBJECTS = test_dispatcher_envp-test-dispatcher-envp.$(OBJEXT)
 test_dispatcher_envp_OBJECTS = $(am_test_dispatcher_envp_OBJECTS)
 am__DEPENDENCIES_1 =
-test_dispatcher_envp_DEPENDENCIES =  \
-	$(top_builddir)/libnm-util/libnm-util.la \
-	$(top_builddir)/callouts/libtest-dispatcher-envp.la \
-	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+@ENABLE_TESTS_TRUE@test_dispatcher_envp_DEPENDENCIES =  \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-util/libnm-util.la \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/callouts/libtest-dispatcher-envp.la \
+@ENABLE_TESTS_TRUE@	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
@@ -111,7 +113,7 @@ AM_V_GEN = $(am__v_GEN_@AM_V@)
 am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
 am__v_GEN_0 = @echo "  GEN   " $@;
 SOURCES = $(test_dispatcher_envp_SOURCES)
-DIST_SOURCES = $(test_dispatcher_envp_SOURCES)
+DIST_SOURCES = $(am__test_dispatcher_envp_SOURCES_DIST)
 am__can_run_installinfo = \
   case $$AM_UPDATE_INFO_DIR in \
     n|no|NO) false;; \
@@ -132,6 +134,10 @@ AWK = @AWK@
 CC = @CC@
 CCDEPMODE = @CCDEPMODE@
 CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
 CPP = @CPP@
 CPPFLAGS = @CPPFLAGS@
 CXX = @CXX@
@@ -146,9 +152,7 @@ DBUS_SYS_DIR = @DBUS_SYS_DIR@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DHCLIENT_PATH = @DHCLIENT_PATH@
-DHCLIENT_VERSION = @DHCLIENT_VERSION@
 DHCPCD_PATH = @DHCPCD_PATH@
-DISABLE_DEPRECATED = @DISABLE_DEPRECATED@
 DLLTOOL = @DLLTOOL@
 DSYMUTIL = @DSYMUTIL@
 DUMPBIN = @DUMPBIN@
@@ -158,17 +162,14 @@ ECHO_T = @ECHO_T@
 EGREP = @EGREP@
 EXEEXT = @EXEEXT@
 FGREP = @FGREP@
+GENHTML = @GENHTML@
 GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
 GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
-GIO_CFLAGS = @GIO_CFLAGS@
-GIO_LIBS = @GIO_LIBS@
 GLIB_CFLAGS = @GLIB_CFLAGS@
 GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
 GLIB_LIBS = @GLIB_LIBS@
 GLIB_MAKEFILE = @GLIB_MAKEFILE@
 GLIB_MKENUMS = @GLIB_MKENUMS@
-GMODULE_CFLAGS = @GMODULE_CFLAGS@
-GMODULE_LIBS = @GMODULE_LIBS@
 GMSGFMT = @GMSGFMT@
 GMSGFMT_015 = @GMSGFMT_015@
 GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
@@ -209,6 +210,7 @@ IPTABLES_PATH = @IPTABLES_PATH@
 IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
 IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
 KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
 LIBDL = @LIBDL@
@@ -225,11 +227,7 @@ LIBNL2_LIBS = @LIBNL2_LIBS@
 LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
 LIBNL3_LIBS = @LIBNL3_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
-LIBNL_GENL3_CFLAGS = @LIBNL_GENL3_CFLAGS@
-LIBNL_GENL3_LIBS = @LIBNL_GENL3_LIBS@
 LIBNL_LIBS = @LIBNL_LIBS@
-LIBNL_ROUTE3_CFLAGS = @LIBNL_ROUTE3_CFLAGS@
-LIBNL_ROUTE3_LIBS = @LIBNL_ROUTE3_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
@@ -244,6 +242,8 @@ MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MANIFEST_TOOL = @MANIFEST_TOOL@
 MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
 MOC = @MOC@
 MSGFMT = @MSGFMT@
 MSGFMT_015 = @MSGFMT_015@
@@ -253,6 +253,7 @@ NMEDIT = @NMEDIT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
 NM_MICRO_VERSION = @NM_MICRO_VERSION@
 NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
 NM_VERSION = @NM_VERSION@
 NSS_CFLAGS = @NSS_CFLAGS@
 NSS_LIBS = @NSS_LIBS@
@@ -279,13 +280,14 @@ PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
 QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
-RESOLVCONF_PATH = @RESOLVCONF_PATH@
 SED = @SED@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
-SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@
-SYSTEMD_LIBS = @SYSTEMD_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
 SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
 UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
@@ -340,6 +342,11 @@ localedir = @localedir@
 localstatedir = @localstatedir@
 mandir = @mandir@
 mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
 oldincludedir = @oldincludedir@
 pdfdir = @pdfdir@
 prefix = @prefix@
@@ -354,33 +361,37 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-INCLUDES = \
-	-I$(top_srcdir)/include \
-	-I${top_builddir}/include \
-	-I$(top_srcdir)/libnm-util \
-	-I$(top_builddir)/libnm-util \
-	-I$(top_srcdir)/callouts
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+@ENABLE_TESTS_TRUE@INCLUDES = \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/include \
+@ENABLE_TESTS_TRUE@	-I${top_builddir}/include \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/libnm-util \
+@ENABLE_TESTS_TRUE@	-I$(top_builddir)/libnm-util \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/callouts
 
 
 ####### dispatcher envp #######
-test_dispatcher_envp_SOURCES = \
-	test-dispatcher-envp.c
+@ENABLE_TESTS_TRUE@test_dispatcher_envp_SOURCES = \
+@ENABLE_TESTS_TRUE@	test-dispatcher-envp.c
 
-test_dispatcher_envp_CPPFLAGS = \
-	$(GLIB_CFLAGS) \
-	$(DBUS_CFLAGS)
+@ENABLE_TESTS_TRUE@test_dispatcher_envp_CPPFLAGS = \
+@ENABLE_TESTS_TRUE@	$(GLIB_CFLAGS) \
+@ENABLE_TESTS_TRUE@	$(DBUS_CFLAGS)
 
-test_dispatcher_envp_LDADD = \
-	$(top_builddir)/libnm-util/libnm-util.la \
-	$(top_builddir)/callouts/libtest-dispatcher-envp.la \
-	$(GLIB_LIBS) \
-	$(DBUS_LIBS)
+@ENABLE_TESTS_TRUE@test_dispatcher_envp_LDADD = \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-util/libnm-util.la \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/callouts/libtest-dispatcher-envp.la \
+@ENABLE_TESTS_TRUE@	$(GLIB_LIBS) \
+@ENABLE_TESTS_TRUE@	$(DBUS_LIBS)
 
-EXTRA_DIST = \
-	dispatcher-old-down \
-	dispatcher-old-up \
-	dispatcher-old-vpn-down \
-	dispatcher-old-vpn-up
+@ENABLE_TESTS_TRUE@EXTRA_DIST = \
+@ENABLE_TESTS_TRUE@	dispatcher-old-down \
+@ENABLE_TESTS_TRUE@	dispatcher-old-up \
+@ENABLE_TESTS_TRUE@	dispatcher-old-vpn-down \
+@ENABLE_TESTS_TRUE@	dispatcher-old-vpn-up
 
 all: all-am
 
@@ -438,25 +449,22 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dispatcher_envp-test-dispatcher-envp.Po@am__quote@
 
 .c.o:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
-@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@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 -o $@ $<
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
 
 .c.obj:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
-@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Po
+@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 -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
 
 .c.lo:
-@am__fastdepCC_TRUE@	$(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
-@am__fastdepCC_TRUE@	$(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
-@am__fastdepCC_TRUE@	$(am__mv) $$depbase.Tpo $$depbase.Plo
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
@@ -563,7 +571,7 @@ distdir: $(DISTFILES)
 	    || exit 1; \
 	  fi; \
 	done
-@WITH_TESTS_FALSE@check-local:
+@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
 	$(MAKE) $(AM_MAKEFLAGS) check-local
 check: check-am
@@ -689,8 +697,8 @@ uninstall-am:
 
 ###########################################
 
-@WITH_TESTS_TRUE@check-local: test-dispatcher-envp
-@WITH_TESTS_TRUE@	$(abs_builddir)/test-dispatcher-envp $(abs_srcdir)
+@ENABLE_TESTS_TRUE@check-local: test-dispatcher-envp
+@ENABLE_TESTS_TRUE@	$(abs_builddir)/test-dispatcher-envp $(abs_srcdir)
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/callouts/tests/test-dispatcher-envp.c b/callouts/tests/test-dispatcher-envp.c
index 34b9dbf5..67737690 100644
--- a/callouts/tests/test-dispatcher-envp.c
+++ b/callouts/tests/test-dispatcher-envp.c
@@ -22,7 +22,6 @@
 #include <arpa/inet.h>
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <glib.h>
 #include <glib-object.h>