diff options
author | Beniamino Galvani <bgalvani@redhat.com> | 2015-07-14 18:17:33 +0200 |
---|---|---|
committer | Beniamino Galvani <bgalvani@redhat.com> | 2015-07-15 18:54:41 +0200 |
commit | bbdc5efbe63d257f3c95d4be22cd0ca02afe2846 (patch) | |
tree | 4f7b1e0d8903f8b1f39ac44e5cc97f14b8c1c9e8 | |
parent | 5cc2eabe5d24c8d9fe30aa675d48c7fa121d7c93 (diff) | |
download | NetworkManager-bbdc5efbe63d257f3c95d4be22cd0ca02afe2846.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.am | 1 | ||||
-rw-r--r-- | callouts/nm-dispatcher-api.h | 1 | ||||
-rw-r--r-- | callouts/nm-dispatcher.c | 153 | ||||
-rw-r--r-- | contrib/fedora/rpm/NetworkManager.spec | 2 | ||||
-rw-r--r-- | man/NetworkManager.xml | 10 |
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> |