summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBeniamino Galvani <bgalvani@redhat.com>2015-07-14 18:17:33 +0200
committerBeniamino Galvani <bgalvani@redhat.com>2015-07-15 18:54:41 +0200
commitbbdc5efbe63d257f3c95d4be22cd0ca02afe2846 (patch)
tree4f7b1e0d8903f8b1f39ac44e5cc97f14b8c1c9e8
parent5cc2eabe5d24c8d9fe30aa675d48c7fa121d7c93 (diff)
downloadNetworkManager-bg/dispatch-sync-requests-rh746703-v2.tar.gz
nm-dispatcher: allow scripts to be marked as no-waitbg/dispatch-sync-requests-rh746703-v2
When a script is a symbolic link to the 'no-wait.d' subdirectory, the dispatcher now schedules it immediately and in parallel with other no-wait scripts. https://bugzilla.gnome.org/show_bug.cgi?id=746703
-rw-r--r--callouts/Makefile.am1
-rw-r--r--callouts/nm-dispatcher-api.h1
-rw-r--r--callouts/nm-dispatcher.c153
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec2
-rw-r--r--man/NetworkManager.xml10
5 files changed, 126 insertions, 41 deletions
diff --git a/callouts/Makefile.am b/callouts/Makefile.am
index 7bc7273032..c192889ef8 100644
--- a/callouts/Makefile.am
+++ b/callouts/Makefile.am
@@ -95,6 +95,7 @@ install-data-hook:
$(mkinstalldirs) -m 0755 $(DESTDIR)$(dispatcherdir)
$(mkinstalldirs) -m 0755 $(DESTDIR)$(dispatcherdir)/pre-down.d
$(mkinstalldirs) -m 0755 $(DESTDIR)$(dispatcherdir)/pre-up.d
+ $(mkinstalldirs) -m 0755 $(DESTDIR)$(dispatcherdir)/no-wait.d
CLEANFILES = $(nodist_libnmdbus_dispatcher_la_SOURCES) $(dbusactivation_DATA)
diff --git a/callouts/nm-dispatcher-api.h b/callouts/nm-dispatcher-api.h
index e84b08f489..d702ba6851 100644
--- a/callouts/nm-dispatcher-api.h
+++ b/callouts/nm-dispatcher-api.h
@@ -21,6 +21,7 @@
#define NMD_SCRIPT_DIR_DEFAULT NMCONFDIR "/dispatcher.d"
#define NMD_SCRIPT_DIR_PRE_UP NMD_SCRIPT_DIR_DEFAULT "/pre-up.d"
#define NMD_SCRIPT_DIR_PRE_DOWN NMD_SCRIPT_DIR_DEFAULT "/pre-down.d"
+#define NMD_SCRIPT_DIR_NO_WAIT NMD_SCRIPT_DIR_DEFAULT "/no-wait.d"
#define NM_DISPATCHER_DBUS_SERVICE "org.freedesktop.nm_dispatcher"
#define NM_DISPATCHER_DBUS_INTERFACE "org.freedesktop.nm_dispatcher"
diff --git a/callouts/nm-dispatcher.c b/callouts/nm-dispatcher.c
index d9fe147f4d..287b2a600c 100644
--- a/callouts/nm-dispatcher.c
+++ b/callouts/nm-dispatcher.c
@@ -31,6 +31,7 @@
#include <sys/wait.h>
#include <errno.h>
#include <arpa/inet.h>
+#include <limits.h>
#include <glib.h>
#include <glib-unix.h>
@@ -39,6 +40,8 @@
#include "nm-dispatcher-api.h"
#include "nm-dispatcher-utils.h"
#include "nm-glib-compat.h"
+#include "nm-macros-internal.h"
+#include "gsystem-local-alloc.h"
#include "nmdbus-dispatcher.h"
@@ -57,6 +60,7 @@ typedef struct {
Request *current_request;
GQueue *pending_requests;
+ guint num_pending;
} Handler;
typedef struct {
@@ -102,8 +106,6 @@ handler_class_init (HandlerClass *h_class)
{
}
-static void dispatch_one_script (Request *request);
-
typedef struct {
Request *request;
@@ -111,6 +113,10 @@ typedef struct {
GPid pid;
DispatchResult result;
char *error;
+ gboolean wait;
+ gboolean dispatched;
+ guint watch_id;
+ guint timeout_id;
} ScriptInfo;
struct Request {
@@ -122,13 +128,16 @@ struct Request {
char **envp;
gboolean debug;
- GPtrArray *scripts; /* list of ScriptInfo */
+ GPtrArray *scripts; /* list of ScriptInfo */
guint idx;
+ guint num_pending; /* number of pending scripts */
guint script_watch_id;
guint script_timeout_id;
};
+static void dispatch_one_script (ScriptInfo *script);
+
static void
script_info_free (gpointer ptr)
{
@@ -139,16 +148,6 @@ script_info_free (gpointer ptr)
g_free (info);
}
-static void
-request_free (Request *request)
-{
- g_free (request->action);
- g_free (request->iface);
- g_strfreev (request->envp);
- if (request->scripts)
- g_ptr_array_free (request->scripts, TRUE);
-}
-
static gboolean
quit_timeout_cb (gpointer user_data)
{
@@ -174,6 +173,22 @@ quit_timeout_reschedule (void)
}
static void
+request_free (Request *request)
+{
+ g_free (request->action);
+ g_free (request->iface);
+ g_strfreev (request->envp);
+ if (request->scripts)
+ g_ptr_array_free (request->scripts, TRUE);
+
+ g_assert_cmpuint (request->handler->num_pending, >, 0);
+ if (--request->handler->num_pending == 0)
+ quit_timeout_reschedule ();
+
+ g_free (request);
+}
+
+static void
start_request (Request *request)
{
if (request->iface)
@@ -182,7 +197,7 @@ start_request (Request *request)
g_message ("Dispatching action '%s'", request->action);
request->handler->current_request = request;
- dispatch_one_script (request);
+ dispatch_one_script (g_ptr_array_index (request->scripts, 0));
}
static void
@@ -196,23 +211,17 @@ next_request (Handler *h)
}
h->current_request = NULL;
- quit_timeout_reschedule ();
}
-static gboolean
-next_script (gpointer user_data)
+static void
+complete_request (Request *request)
{
- Request *request = user_data;
- Handler *h = request->handler;
GVariantBuilder results;
GVariant *ret;
guint i;
- request->idx++;
- if (request->idx < request->scripts->len) {
- dispatch_one_script (request);
- return FALSE;
- }
+ if (request->num_pending || request->idx < request->scripts->len)
+ return;
/* All done */
g_variant_builder_init (&results, G_VARIANT_TYPE ("a(sus)"));
@@ -235,7 +244,21 @@ next_script (gpointer user_data)
g_message ("Dispatch '%s' complete", request->action);
}
request_free (request);
+}
+
+static gboolean
+next_script (gpointer user_data)
+{
+ Request *request = user_data;
+ Handler *h = request->handler;
+
+ request->idx++;
+ if (request->idx < request->scripts->len) {
+ dispatch_one_script (g_ptr_array_index (request->scripts, request->idx));
+ return FALSE;
+ }
+ complete_request (request);
next_request (h);
return FALSE;
}
@@ -248,9 +271,8 @@ script_watch_cb (GPid pid, gint status, gpointer user_data)
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;
+ script->watch_id = 0;
+ nm_clear_g_source (&script->timeout_id);
if (WIFEXITED (status)) {
err = WEXITSTATUS (status);
@@ -280,7 +302,12 @@ script_watch_cb (GPid pid, gint status, gpointer user_data)
}
g_spawn_close_pid (script->pid);
- next_script (script->request);
+ script->request->num_pending--;
+
+ if (script->wait)
+ next_script (script->request);
+ else
+ complete_request (script->request);
}
static gboolean
@@ -288,8 +315,7 @@ script_timeout_cb (gpointer user_data)
{
ScriptInfo *script = user_data;
- g_source_remove (script->request->script_watch_id);
- script->request->script_watch_id = 0;
+ nm_clear_g_source (&script->watch_id);
script->request->script_timeout_id = 0;
g_warning ("Script '%s' took too long; killing it.", script->script);
@@ -305,7 +331,13 @@ again:
script->result = DISPATCH_RESULT_TIMEOUT;
g_spawn_close_pid (script->pid);
- g_idle_add (next_script, script->request);
+ script->request->num_pending--;
+
+ if (script->wait)
+ g_idle_add (next_script, script->request);
+ else
+ complete_request (script->request);
+
return FALSE;
}
@@ -367,11 +399,18 @@ check_filename (const char *file_name)
#define SCRIPT_TIMEOUT 600 /* 10 minutes */
static void
-dispatch_one_script (Request *request)
+dispatch_one_script (ScriptInfo *script)
{
GError *error = NULL;
gchar *argv[4];
- ScriptInfo *script = g_ptr_array_index (request->scripts, request->idx);
+ Request *request = script->request;
+
+ if (script->dispatched) {
+ g_idle_add (next_script, request);
+ return;
+ }
+
+ script->dispatched = TRUE;
argv[0] = script->script;
argv[1] = request->iface ? request->iface : "none";
@@ -382,8 +421,9 @@ dispatch_one_script (Request *request)
g_message ("Running script '%s'", script->script);
if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, NULL, 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 (SCRIPT_TIMEOUT, script_timeout_cb, script);
+ request->num_pending++;
+ script->watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script);
+ script->timeout_id = g_timeout_add_seconds (SCRIPT_TIMEOUT, script_timeout_cb, script);
} else {
g_warning ("Failed to execute script '%s': (%d) %s",
script->script, error->code, error->message);
@@ -392,7 +432,8 @@ dispatch_one_script (Request *request)
g_clear_error (&error);
/* Try the next script */
- g_idle_add (next_script, request);
+ if (script->wait)
+ g_idle_add (next_script, request);
}
}
@@ -452,6 +493,34 @@ find_scripts (const char *str_action)
}
static gboolean
+script_must_wait (const char *path)
+{
+ gs_free char *link = NULL;
+ gs_free char *dir = NULL;
+ gs_free char *real = NULL;
+ char *tmp;
+
+ link = g_file_read_link (path, NULL);
+ if (link) {
+ if (!g_path_is_absolute (link)) {
+ dir = g_path_get_dirname (path);
+ tmp = g_build_path ("/", dir, link, NULL);
+ g_free (link);
+ g_free (dir);
+ link = tmp;
+ }
+
+ dir = g_path_get_dirname (link);
+ real = realpath (dir, NULL);
+
+ if (real && !strcmp (real, NMD_SCRIPT_DIR_NO_WAIT))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
handle_action (NMDBusDispatcher *dbus_dispatcher,
GDBusMethodInvocation *context,
const char *str_action,
@@ -474,6 +543,7 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
Request *request;
char **p;
char *iface = NULL;
+ guint i;
sorted_scripts = find_scripts (str_action);
@@ -514,16 +584,25 @@ handle_action (NMDBusDispatcher *dbus_dispatcher,
g_message ("\n");
}
- request->iface = g_strdup (iface);
+ request->iface = iface;
request->scripts = g_ptr_array_new_full (5, script_info_free);
for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) {
ScriptInfo *s = g_malloc0 (sizeof (*s));
s->request = request;
s->script = iter->data;
+ s->wait = script_must_wait (s->script);
g_ptr_array_add (request->scripts, s);
}
g_slist_free (sorted_scripts);
+ h->num_pending++;
+
+ for (i = 0; i < request->scripts->len; i++) {
+ ScriptInfo *s = g_ptr_array_index (request->scripts, i);
+
+ if (!s->wait)
+ dispatch_one_script (g_ptr_array_index (request->scripts, i));
+ }
if (h->current_request)
g_queue_push_tail (h->pending_requests, request);
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 5211045801..cdb7bb8e1b 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -456,6 +456,7 @@ mkdir -p $RPM_BUILD_ROOT%{nmlibdir}/conf.d
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d/pre-up.d
mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d/pre-down.d
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d/no-wait.d
%{__cp} examples/dispatcher/10-ifcfg-rh-routes.sh $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d/
%{__ln_s} ../10-ifcfg-rh-routes.sh $RPM_BUILD_ROOT%{_sysconfdir}/%{name}/dispatcher.d/pre-up.d/
@@ -524,6 +525,7 @@ fi
%{_sysconfdir}/%{name}/dispatcher.d/10-ifcfg-rh-routes.sh
%dir %{_sysconfdir}/%{name}/dispatcher.d/pre-down.d
%dir %{_sysconfdir}/%{name}/dispatcher.d/pre-up.d
+%dir %{_sysconfdir}/%{name}/dispatcher.d/non-blocking.d
%{_sysconfdir}/%{name}/dispatcher.d/pre-up.d/10-ifcfg-rh-routes.sh
%dir %{_sysconfdir}/%{name}/dnsmasq.d
%dir %{_sysconfdir}/%{name}/VPN
diff --git a/man/NetworkManager.xml b/man/NetworkManager.xml
index ceb810e3b4..1c0572d875 100644
--- a/man/NetworkManager.xml
+++ b/man/NetworkManager.xml
@@ -267,10 +267,12 @@
Dispatcher scripts are run one at a time, but asynchronously from the main
NetworkManager process, and will be killed if they run for too long. If your script
might take arbitrarily long to complete, you should spawn a child process and have the
- parent return immediately. Also beware that once a script is queued, it will always be
- run, even if a later event renders it obsolete. (Eg, if an interface goes up, and then
- back down again quickly, it is possible that one or more "up" scripts will be run
- after the interface has gone down.)
+ parent return immediately. Scripts that are a link into the
+ /etc/NetworkManager/dispatcher.d/no-wait.d/ directory are run immediately, without
+ waiting for the termination of previous scripts, and in parallel. Also beware that
+ once a script is queued, it will always be run, even if a later event renders it
+ obsolete. (Eg, if an interface goes up, and then back down again quickly, it is
+ possible that one or more "up" scripts will be run after the interface has gone down.)
</para>
</refsect1>