diff options
author | Thomas Haller <thaller@redhat.com> | 2019-05-27 12:44:25 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2019-05-27 12:44:25 +0200 |
commit | 51793569e18e7b7b71cdff92d8fe513f973f6e78 (patch) | |
tree | 8c1c5eb7807b1fade8432103d6cde81f1b40c382 | |
parent | 8f1b542bd7406e78bb3f56bdbeeb9d3e889c695b (diff) | |
parent | 7ea76c07e52ed6c5f4d78e52fd03e1e7e9b3da78 (diff) | |
download | NetworkManager-51793569e18e7b7b71cdff92d8fe513f973f6e78.tar.gz |
dispatcher: merge branch 'th/dispatcher-cleanup'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/merge_requests/162
-rw-r--r-- | Makefile.am | 49 | ||||
-rw-r--r-- | data/NetworkManager-dispatcher.service.in | 6 | ||||
-rw-r--r-- | dispatcher/meson.build | 2 | ||||
-rw-r--r-- | dispatcher/nm-dispatcher.c | 630 | ||||
-rw-r--r-- | dispatcher/tests/meson.build | 2 | ||||
-rw-r--r-- | dispatcher/tests/test-dispatcher-envp.c | 15 | ||||
-rw-r--r-- | shared/nm-glib-aux/nm-macros-internal.h | 2 | ||||
-rw-r--r-- | src/devices/nm-device.c | 40 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/nm-dispatcher.c | 467 | ||||
-rw-r--r-- | src/nm-dispatcher.h | 16 | ||||
-rw-r--r-- | src/vpn/nm-vpn-connection.c | 22 |
12 files changed, 726 insertions, 527 deletions
diff --git a/Makefile.am b/Makefile.am index cf2ee34a1f..c830919df7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3885,6 +3885,25 @@ EXTRA_DIST += \ # dispatcher ############################################################################### +dispatcher_nmdbus_dispatcher_sources = \ + dispatcher/nmdbus-dispatcher.h \ + dispatcher/nmdbus-dispatcher.c \ + $(NULL) + +dispatcher/nmdbus-dispatcher.h: dispatcher/nm-dispatcher.xml + @$(MKDIR_P) dispatcher/ + $(AM_V_GEN) gdbus-codegen \ + --generate-c-code $(basename $@) \ + --c-namespace NMDBus \ + --interface-prefix org.freedesktop \ + $< + +dispatcher/nmdbus-dispatcher.c: dispatcher/nmdbus-dispatcher.h + +CLEANFILES += $(dispatcher_nmdbus_dispatcher_sources) + +############################################################################### + libexec_PROGRAMS += dispatcher/nm-dispatcher noinst_LTLIBRARIES += \ @@ -3905,25 +3924,6 @@ dispatcher_cppflags = \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \ $(NULL) - -dispatcher_nmdbus_dispatcher_sources = \ - dispatcher/nmdbus-dispatcher.h \ - dispatcher/nmdbus-dispatcher.c - -dispatcher/nmdbus-dispatcher.h: dispatcher/nm-dispatcher.xml - @$(MKDIR_P) dispatcher/ - $(AM_V_GEN) gdbus-codegen \ - --generate-c-code $(basename $@) \ - --c-namespace NMDBus \ - --interface-prefix org.freedesktop \ - $< - -dispatcher/nmdbus-dispatcher.c: dispatcher/nmdbus-dispatcher.h - -$(dispatcher_nm_dispatcher_OBJECTS): $(dispatcher_nmdbus_dispatcher_sources) - -CLEANFILES += $(dispatcher_nmdbus_dispatcher_sources) - dispatcher_libnm_dispatcher_core_la_SOURCES = \ dispatcher/nm-dispatcher-utils.c \ dispatcher/nm-dispatcher-utils.h \ @@ -3939,8 +3939,6 @@ dispatcher_nm_dispatcher_SOURCES = \ dispatcher/nm-dispatcher.c \ $(NULL) -nodist_dispatcher_nm_dispatcher_SOURCES = $(dispatcher_nmdbus_dispatcher_sources) - dispatcher_nm_dispatcher_CPPFLAGS = $(dispatcher_cppflags) dispatcher_nm_dispatcher_LDFLAGS = \ @@ -3998,12 +3996,21 @@ dispatcher_tests_test_dispatcher_envp_CPPFLAGS = \ -I$(srcdir)/libnm \ -I$(builddir)/libnm \ -I$(srcdir)/dispatcher \ + -I$(builddir)/dispatcher \ -DNETWORKMANAGER_COMPILATION_TEST \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT \ $(GLIB_CFLAGS) \ $(SANITIZER_EXEC_CFLAGS) \ $(NULL) +dispatcher_tests_test_dispatcher_envp_SOURCES = \ + dispatcher/tests/test-dispatcher-envp.c \ + $(NULL) + +nodist_dispatcher_tests_test_dispatcher_envp_SOURCES = $(dispatcher_nmdbus_dispatcher_sources) + +$(dispatcher_tests_test_dispatcher_envp_OBJECTS): $(dispatcher_nmdbus_dispatcher_sources) + dispatcher_tests_test_dispatcher_envp_LDFLAGS = \ $(SANITIZER_EXEC_LDFLAGS) \ $(NULL) diff --git a/data/NetworkManager-dispatcher.service.in b/data/NetworkManager-dispatcher.service.in index c450478bac..80dc2a1b8d 100644 --- a/data/NetworkManager-dispatcher.service.in +++ b/data/NetworkManager-dispatcher.service.in @@ -1,6 +1,12 @@ [Unit] Description=Network Manager Script Dispatcher Service +# Order the dispatcher before NetworkManager. While dispatcher +# is D-Bus activate (and not intended to be explicitly wanted by +# another service/target), the ordering dependency matters during +# shutdown. We want first NetworkManager to be stopped. +Before=NetworkManager.service + [Service] Type=dbus BusName=org.freedesktop.nm_dispatcher diff --git a/dispatcher/meson.build b/dispatcher/meson.build index 7bd41935c5..da9ac7f292 100644 --- a/dispatcher/meson.build +++ b/dispatcher/meson.build @@ -44,7 +44,7 @@ libnm_dispatcher_core = static_library( sources = files('nm-dispatcher.c') -sources += gnome.gdbus_codegen( +nmdbus_dispatcher_sources = gnome.gdbus_codegen( 'nmdbus-dispatcher', name + '.xml', interface_prefix: 'org.freedesktop', diff --git a/dispatcher/nm-dispatcher.c b/dispatcher/nm-dispatcher.c index 231a3f9a83..a803c9759d 100644 --- a/dispatcher/nm-dispatcher.c +++ b/dispatcher/nm-dispatcher.c @@ -34,74 +34,24 @@ #include "nm-libnm-core-aux/nm-dispatcher-api.h" #include "nm-dispatcher-utils.h" -#include "nmdbus-dispatcher.h" - -static GMainLoop *loop = NULL; -static gboolean debug = FALSE; -static gboolean persist = FALSE; -static guint quit_id; -static guint request_id_counter = 0; +/*****************************************************************************/ typedef struct Request Request; -typedef struct { - GObject parent; - - /* Private data */ - NMDBusDispatcher *dbus_dispatcher; +static struct { + GDBusConnection *dbus_connection; + GMainLoop *loop; + gboolean debug; + gboolean persist; + guint quit_id; + guint request_id_counter; + gboolean ever_acquired_name; + bool exit_with_failure; Request *current_request; GQueue *requests_waiting; int num_requests_pending; -} Handler; - -typedef struct { - GObjectClass parent; -} HandlerClass; - -GType handler_get_type (void); - -#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)) - -G_DEFINE_TYPE(Handler, handler, G_TYPE_OBJECT) - -static gboolean -handle_action (NMDBusDispatcher *dbus_dispatcher, - GDBusMethodInvocation *context, - const char *str_action, - GVariant *connection_dict, - GVariant *connection_props, - GVariant *device_props, - GVariant *device_proxy_props, - GVariant *device_ip4_props, - GVariant *device_ip6_props, - GVariant *device_dhcp4_props, - GVariant *device_dhcp6_props, - const char *connectivity_state, - const char *vpn_ip_iface, - GVariant *vpn_proxy_props, - GVariant *vpn_ip4_props, - GVariant *vpn_ip6_props, - gboolean request_debug, - gpointer user_data); - -static void -handler_init (Handler *h) -{ - h->requests_waiting = g_queue_new (); - h->dbus_dispatcher = nmdbus_dispatcher_skeleton_new (); - g_signal_connect (h->dbus_dispatcher, "handle-action", - G_CALLBACK (handle_action), h); -} - -static void -handler_class_init (HandlerClass *h_class) -{ -} - -static gboolean dispatch_one_script (Request *request); +} gl; typedef struct { Request *request; @@ -117,8 +67,6 @@ typedef struct { } ScriptInfo; struct Request { - Handler *handler; - guint request_id; GDBusMethodInvocation *context; @@ -135,52 +83,86 @@ struct Request { /*****************************************************************************/ -#define __LOG_print(print_cmd, _request, _script, ...) \ +#define __LOG_print(print_cmd, ...) \ G_STMT_START { \ - nm_assert ((_request) && (!(_script) || (_script)->request == (_request))); \ - print_cmd ("req:%u '%s'%s%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - (_request)->request_id, \ - (_request)->action, \ - (_request)->iface ? " [" : "", \ - (_request)->iface ?: "", \ - (_request)->iface ? "]" : "", \ - (_script) ? ", \"" : "", \ - (_script) ? (_script)->script : "", \ - (_script) ? "\"" : "" \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + if (FALSE) { \ + /* g_message() alone does not warn about invalid format. Add a dummy printf() statement to + * get a compiler warning about wrong format. */ \ + printf (__VA_ARGS__); \ + } \ + print_cmd (__VA_ARGS__); \ } G_STMT_END -#define _LOG(_request, _script, log_always, print_cmd, ...) \ +#define __LOG_print_R(print_cmd, _request, ...) \ G_STMT_START { \ - const Request *__request = (_request); \ - const ScriptInfo *__script = (_script); \ + __LOG_print (print_cmd, \ + "req:%u '%s'%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + (_request)->request_id, \ + (_request)->action, \ + (_request)->iface ? " [" : "", \ + (_request)->iface ?: "", \ + (_request)->iface ? "]" : "" \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } G_STMT_END + +#define __LOG_print_S(print_cmd, _request, _script, ...) \ + G_STMT_START { \ + __LOG_print_R (print_cmd, \ + (_request), \ + "%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + (_script) ? ", \"" : "", \ + (_script) ? (_script)->script : "", \ + (_script) ? "\"" : "" \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ + } G_STMT_END + +#define _LOG_X_(enabled_cmd, print_cmd, ...) \ + G_STMT_START { \ + if (enabled_cmd) \ + __LOG_print (print_cmd, __VA_ARGS__); \ + } G_STMT_END + +#define _LOG_R_(enabled_cmd, x_request, print_cmd, ...) \ + G_STMT_START { \ + const Request *const _request = (x_request); \ \ - if (!__request) \ - __request = __script->request; \ - nm_assert (__request && (!__script || __script->request == __request)); \ - if ((log_always) || _LOG_R_D_enabled (__request)) { \ - if (FALSE) { \ - /* g_message() alone does not warn about invalid format. Add a dummy printf() statement to - * get a compiler warning about wrong format. */ \ - __LOG_print (printf, __request, __script, __VA_ARGS__); \ - } \ - __LOG_print (print_cmd, __request, __script, __VA_ARGS__); \ - } \ + nm_assert (_request); \ + if (enabled_cmd) \ + __LOG_print_R (print_cmd, _request, ": "__VA_ARGS__); \ } G_STMT_END -static gboolean -_LOG_R_D_enabled (const Request *request) -{ - return request->debug; -} +#define _LOG_S_(enabled_cmd, x_script, print_cmd, ...) \ + G_STMT_START { \ + const ScriptInfo *const _script = (x_script); \ + const Request *const _request = _script ? _script->request : NULL; \ + \ + nm_assert (_script && _request); \ + if (enabled_cmd) \ + __LOG_print_S (print_cmd, _request, _script, ": "__VA_ARGS__); \ + } G_STMT_END + +#define _LOG_X_D_enabled() (gl.debug) +#define _LOG_X_T_enabled() _LOG_X_D_enabled () + +#define _LOG_R_D_enabled(request) (_NM_ENSURE_TYPE_CONST (Request *, request)->debug) +#define _LOG_R_T_enabled(request) _LOG_R_D_enabled (request) + +#define _LOG_X_T(...) _LOG_X_ (_LOG_X_T_enabled (), g_debug, __VA_ARGS__) +#define _LOG_X_D(...) _LOG_X_ (_LOG_X_D_enabled (), g_info, __VA_ARGS__) +#define _LOG_X_I(...) _LOG_X_ (TRUE, g_message, __VA_ARGS__) +#define _LOG_X_W(...) _LOG_X_ (TRUE, g_warning, __VA_ARGS__) + +#define _LOG_R_T(request, ...) _LOG_R_ (_LOG_R_T_enabled (_request), request, g_debug, __VA_ARGS__) +#define _LOG_R_D(request, ...) _LOG_R_ (_LOG_R_D_enabled (_request), request, g_info, __VA_ARGS__) +#define _LOG_R_W(request, ...) _LOG_R_ (TRUE, request, g_warning, __VA_ARGS__) + +#define _LOG_S_T(script, ...) _LOG_S_ (_LOG_R_T_enabled (_request), script, g_debug, __VA_ARGS__) +#define _LOG_S_D(script, ...) _LOG_S_ (_LOG_R_D_enabled (_request), script, g_info, __VA_ARGS__) +#define _LOG_S_W(script, ...) _LOG_S_ (TRUE, script, g_warning, __VA_ARGS__) -#define _LOG_R_D(_request, ...) _LOG(_request, NULL, FALSE, g_debug, __VA_ARGS__) -#define _LOG_R_I(_request, ...) _LOG(_request, NULL, TRUE, g_info, __VA_ARGS__) -#define _LOG_R_W(_request, ...) _LOG(_request, NULL, TRUE, g_warning, __VA_ARGS__) +/*****************************************************************************/ -#define _LOG_S_D(_script, ...) _LOG(NULL, _script, FALSE, g_debug, __VA_ARGS__) -#define _LOG_S_I(_script, ...) _LOG(NULL, _script, TRUE, g_info, __VA_ARGS__) -#define _LOG_S_W(_script, ...) _LOG(NULL, _script, TRUE, g_warning, __VA_ARGS__) +static gboolean dispatch_one_script (Request *request); /*****************************************************************************/ @@ -211,23 +193,23 @@ request_free (Request *request) static gboolean quit_timeout_cb (gpointer user_data) { - g_main_loop_quit (loop); - return FALSE; + gl.quit_id = 0; + g_main_loop_quit (gl.loop); + return G_SOURCE_REMOVE; } static void quit_timeout_reschedule (void) { - if (!persist) { - nm_clear_g_source (&quit_id); - quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL); + if (!gl.persist) { + nm_clear_g_source (&gl.quit_id); + gl.quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL); } } /** * next_request: * - * @h: the handler * @request: (allow-none): the request to set as next. If %NULL, dequeue the next * waiting request. Otherwise, try to set the given request. * @@ -240,27 +222,27 @@ quit_timeout_reschedule (void) * a new request as current. */ static gboolean -next_request (Handler *h, Request *request) +next_request (Request *request) { if (request) { - if (h->current_request) { - g_queue_push_tail (h->requests_waiting, request); + if (gl.current_request) { + g_queue_push_tail (gl.requests_waiting, request); return FALSE; } } else { /* when calling next_request() without explicit @request, we always * forcefully clear @current_request. That one is certainly * handled already. */ - h->current_request = NULL; + gl.current_request = NULL; - request = g_queue_pop_head (h->requests_waiting); + request = g_queue_pop_head (gl.requests_waiting); if (!request) return FALSE; } - _LOG_R_I (request, "start running ordered scripts..."); + _LOG_R_D (request, "start running ordered scripts..."); - h->current_request = request; + gl.current_request = request; return TRUE; } @@ -280,7 +262,6 @@ complete_request (Request *request) GVariantBuilder results; GVariant *ret; guint i; - Handler *handler = request->handler; nm_assert (request); @@ -301,16 +282,16 @@ complete_request (Request *request) ret = g_variant_new ("(a(sus))", &results); g_dbus_method_invocation_return_value (request->context, ret); - _LOG_R_D (request, "completed (%u scripts)", request->scripts->len); + _LOG_R_T (request, "completed (%u scripts)", request->scripts->len); - if (handler->current_request == request) - handler->current_request = NULL; + if (gl.current_request == request) + gl.current_request = NULL; request_free (request); - g_assert_cmpuint (handler->num_requests_pending, >, 0); - if (--handler->num_requests_pending <= 0) { - nm_assert (!handler->current_request && !g_queue_peek_head (handler->requests_waiting)); + g_assert_cmpuint (gl.num_requests_pending, >, 0); + if (--gl.num_requests_pending <= 0) { + nm_assert (!gl.current_request && !g_queue_peek_head (gl.requests_waiting)); quit_timeout_reschedule (); } } @@ -318,7 +299,6 @@ complete_request (Request *request) static void complete_script (ScriptInfo *script) { - Handler *handler; Request *request; gboolean wait = script->wait; @@ -331,9 +311,7 @@ complete_script (ScriptInfo *script) return; } - handler = request->handler; - - nm_assert (!wait || handler->current_request == request); + nm_assert (!wait || gl.current_request == request); /* Try to complete the request. @request will be possibly free'd, * making @script and @request a dangling pointer. */ @@ -346,13 +324,13 @@ complete_script (ScriptInfo *script) * requests. However, if this was the last "no-wait" script and * there are "wait" scripts ready to run, launch them. */ - if ( handler->current_request == request - && handler->current_request->num_scripts_nowait == 0) { + if ( gl.current_request == request + && gl.current_request->num_scripts_nowait == 0) { - if (dispatch_one_script (handler->current_request)) + if (dispatch_one_script (gl.current_request)) return; - complete_request (handler->current_request); + complete_request (gl.current_request); } else return; } else { @@ -365,11 +343,11 @@ complete_script (ScriptInfo *script) * processed because only requests with "wait" scripts can become * @current_request. As there can only be one "wait" script running * at any time, it means complete_request() above completed @request. */ - nm_assert (!handler->current_request); + nm_assert (!gl.current_request); } - while (next_request (handler, NULL)) { - request = handler->current_request; + while (next_request (NULL)) { + request = gl.current_request; if (dispatch_one_script (request)) return; @@ -418,7 +396,7 @@ script_watch_cb (GPid pid, int status, gpointer user_data) } if (script->result == DISPATCH_RESULT_SUCCESS) { - _LOG_S_D (script, "complete"); + _LOG_S_T (script, "complete"); } else { script->result = DISPATCH_RESULT_FAILED; _LOG_S_W (script, "complete: failed with %s", script->error); @@ -533,7 +511,7 @@ script_dispatch (ScriptInfo *script) argv[2] = request->action; argv[3] = NULL; - _LOG_S_D (script, "run script%s", script->wait ? "" : " (no-wait)"); + _LOG_S_T (script, "run script%s", script->wait ? "" : " (no-wait)"); if (g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &script->pid, &error)) { script->watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script); @@ -586,7 +564,7 @@ _compare_basenames (gconstpointer a, gconstpointer b) } static void -_find_scripts (GHashTable *scripts, const char *base, const char *subdir) +_find_scripts (Request *request, GHashTable *scripts, const char *base, const char *subdir) { const char *filename; gs_free char *dirname = NULL; @@ -596,8 +574,10 @@ _find_scripts (GHashTable *scripts, const char *base, const char *subdir) dirname = g_build_filename (base, "dispatcher.d", subdir, NULL); if (!(dir = g_dir_open (dirname, 0, &error))) { - g_message ("find-scripts: Failed to open dispatcher directory '%s': %s", - dirname, error->message); + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + _LOG_R_W (request, "find-scripts: Failed to open dispatcher directory '%s': %s", + dirname, error->message); + } g_error_free (error); return; } @@ -615,26 +595,28 @@ _find_scripts (GHashTable *scripts, const char *base, const char *subdir) } static GSList * -find_scripts (const char *str_action) +find_scripts (Request *request) { gs_unref_hashtable GHashTable *scripts = NULL; GSList *script_list = NULL; GHashTableIter iter; - const char *subdir = NULL; + const char *subdir; char *path; char *filename; - if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0 - || strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0) + if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_UP, + NMD_ACTION_VPN_PRE_UP)) subdir = "pre-up.d"; - else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0 - || strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0) + else if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_DOWN, + NMD_ACTION_VPN_PRE_DOWN)) subdir = "pre-down.d"; + else + subdir = NULL; scripts = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - _find_scripts (scripts, NMLIBDIR, subdir); - _find_scripts (scripts, NMCONFDIR, subdir); + _find_scripts (request, scripts, NMLIBDIR, subdir); + _find_scripts (request, scripts, NMCONFDIR, subdir); g_hash_table_iter_init (&iter, scripts); while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &path)) { @@ -652,11 +634,11 @@ find_scripts (const char *str_action) err = stat (path, &st); if (err) - g_warning ("find-scripts: Failed to stat '%s': %d", path, err); + _LOG_R_W (request, "find-scripts: Failed to stat '%s': %d", path, err); else if (!S_ISREG (st.st_mode)) ; /* silently skip. */ else if (!check_permissions (&st, &err_msg)) - g_warning ("find-scripts: Cannot execute '%s': %s", path, err_msg); + _LOG_R_W (request, "find-scripts: Cannot execute '%s': %s", path, err_msg); else { /* success */ script_list = g_slist_prepend (script_list, g_strdup (path)); @@ -695,27 +677,25 @@ script_must_wait (const char *path) return TRUE; } -static gboolean -handle_action (NMDBusDispatcher *dbus_dispatcher, - GDBusMethodInvocation *context, - const char *str_action, - GVariant *connection_dict, - GVariant *connection_props, - GVariant *device_props, - GVariant *device_proxy_props, - GVariant *device_ip4_props, - GVariant *device_ip6_props, - GVariant *device_dhcp4_props, - GVariant *device_dhcp6_props, - const char *connectivity_state, - const char *vpn_ip_iface, - GVariant *vpn_proxy_props, - GVariant *vpn_ip4_props, - GVariant *vpn_ip6_props, - gboolean request_debug, - gpointer user_data) +static void +_method_call_action (GDBusMethodInvocation *invocation, + GVariant *parameters) { - Handler *h = user_data; + const char *action; + gs_unref_variant GVariant *connection = NULL; + gs_unref_variant GVariant *connection_properties = NULL; + gs_unref_variant GVariant *device_properties = NULL; + gs_unref_variant GVariant *device_proxy_properties = NULL; + gs_unref_variant GVariant *device_ip4_config = NULL; + gs_unref_variant GVariant *device_ip6_config = NULL; + gs_unref_variant GVariant *device_dhcp4_config = NULL; + gs_unref_variant GVariant *device_dhcp6_config = NULL; + const char *connectivity_state; + const char *vpn_ip_iface; + gs_unref_variant GVariant *vpn_proxy_properties = NULL; + gs_unref_variant GVariant *vpn_ip4_config = NULL; + gs_unref_variant GVariant *vpn_ip6_config = NULL; + gboolean debug; GSList *sorted_scripts = NULL; GSList *iter; Request *request; @@ -723,33 +703,65 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, guint i, num_nowait = 0; const char *error_message = NULL; - sorted_scripts = find_scripts (str_action); + g_variant_get (parameters, "(" + "&s" /* action */ + "@a{sa{sv}}" /* connection */ + "@a{sv}" /* connection_properties */ + "@a{sv}" /* device_properties */ + "@a{sv}" /* device_proxy_properties */ + "@a{sv}" /* device_ip4_config */ + "@a{sv}" /* device_ip6_config */ + "@a{sv}" /* device_dhcp4_config */ + "@a{sv}" /* device_dhcp6_config */ + "&s" /* connectivity_state */ + "&s" /* vpn_ip_iface */ + "@a{sv}" /* vpn_proxy_properties */ + "@a{sv}" /* vpn_ip4_config */ + "@a{sv}" /* vpn_ip6_config */ + "b" /* debug */ + ")", + &action, + &connection, + &connection_properties, + &device_properties, + &device_proxy_properties, + &device_ip4_config, + &device_ip6_config, + &device_dhcp4_config, + &device_dhcp6_config, + &connectivity_state, + &vpn_ip_iface, + &vpn_proxy_properties, + &vpn_ip4_config, + &vpn_ip6_config, + &debug); request = g_slice_new0 (Request); - request->request_id = ++request_id_counter; - request->handler = h; - request->debug = request_debug || debug; - request->context = context; - request->action = g_strdup (str_action); - - request->envp = nm_dispatcher_utils_construct_envp (str_action, - connection_dict, - connection_props, - device_props, - device_proxy_props, - device_ip4_props, - device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, + request->request_id = ++gl.request_id_counter; + request->debug = debug || gl.debug; + request->context = invocation; + request->action = g_strdup (action); + + request->envp = nm_dispatcher_utils_construct_envp (action, + connection, + connection_properties, + device_properties, + device_proxy_properties, + device_ip4_config, + device_ip6_config, + device_dhcp4_config, + device_dhcp6_config, connectivity_state, vpn_ip_iface, - vpn_proxy_props, - vpn_ip4_props, - vpn_ip6_props, + vpn_proxy_properties, + vpn_ip4_config, + vpn_ip6_config, &request->iface, &error_message); request->scripts = g_ptr_array_new_full (5, script_info_free); + + sorted_scripts = find_scripts (request); for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) { ScriptInfo *s; @@ -761,11 +773,11 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, } g_slist_free (sorted_scripts); - _LOG_R_I (request, "new request (%u scripts)", request->scripts->len); - if ( _LOG_R_D_enabled (request) + _LOG_R_D (request, "new request (%u scripts)", request->scripts->len); + if ( _LOG_R_T_enabled (request) && request->envp) { for (p = request->envp; *p; p++) - _LOG_R_D (request, "environment: %s", *p); + _LOG_R_T (request, "environment: %s", *p); } if (error_message || request->scripts->len == 0) { @@ -774,18 +786,18 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, if (error_message) _LOG_R_W (request, "completed: invalid request: %s", error_message); else - _LOG_R_I (request, "completed: no scripts"); + _LOG_R_D (request, "completed: no scripts"); results = g_variant_new_array (G_VARIANT_TYPE ("(sus)"), NULL, 0); - g_dbus_method_invocation_return_value (context, g_variant_new ("(@a(sus))", results)); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(sus))", results)); request->num_scripts_done = request->scripts->len; request_free (request); - return TRUE; + return; } - nm_clear_g_source (&quit_id); + nm_clear_g_source (&gl.quit_id); - h->num_requests_pending++; + gl.num_requests_pending++; for (i = 0; i < request->scripts->len; i++) { ScriptInfo *s = g_ptr_array_index (request->scripts, i); @@ -800,8 +812,8 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, /* The request has at least one wait script. * Try next_request() to schedule the request for * execution. This either enqueues the request or - * sets it as h->current_request. */ - if (next_request (h, request)) { + * sets it as gl.current_request. */ + if (next_request (request)) { /* @request is now @current_request. Go ahead and * schedule the first wait script. */ if (!dispatch_one_script (request)) { @@ -809,7 +821,7 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, * request. Try complete_request(). */ complete_request (request); - if (next_request (h, NULL)) { + if (next_request (NULL)) { /* As @request was successfully scheduled as next_request(), there is no * other request in queue that can be scheduled afterwards. Assert against * that, but call next_request() to clear current_request. */ @@ -822,24 +834,20 @@ handle_action (NMDBusDispatcher *dbus_dispatcher, * the request right away (we might have failed to schedule any * of the scripts). It will be either completed now, or later * when the pending scripts return. - * We don't enqueue it to h->requests_waiting. + * We don't enqueue it to gl.requests_waiting. * There is no need to handle next_request(), because @request is * not the current request anyway and does not interfere with requests * that have any "wait" scripts. */ complete_request (request); } - - return TRUE; } -static gboolean ever_acquired_name = FALSE; - static void on_name_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { - ever_acquired_name = TRUE; + gl.ever_acquired_name = TRUE; } static void @@ -848,22 +856,79 @@ on_name_lost (GDBusConnection *connection, gpointer user_data) { if (!connection) { - if (!ever_acquired_name) { - g_warning ("Could not get the system bus. Make sure the message bus daemon is running!"); - exit (1); + if (!gl.ever_acquired_name) { + _LOG_X_W ("Could not get the system bus. Make sure the message bus daemon is running!"); + gl.exit_with_failure = TRUE; } else { - g_message ("System bus stopped. Exiting"); - exit (0); + _LOG_X_I ("System bus stopped. Exiting"); + } + } else if (!gl.ever_acquired_name) { + _LOG_X_W ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service."); + gl.exit_with_failure = TRUE; + } else + _LOG_X_I ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting"); + + g_main_loop_quit (gl.loop); +} + +static void +_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + if (nm_streq (interface_name, NM_DISPATCHER_DBUS_INTERFACE)) { + if (nm_streq (method_name, "Action")) { + _method_call_action (invocation, parameters); + return; } - } else if (!ever_acquired_name) { - g_warning ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service."); - exit (1); - } else { - g_message ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting"); - exit (0); } + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); } +static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( + NM_DISPATCHER_DBUS_INTERFACE, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_GDBUS_METHOD_INFO ( + "Action", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("action", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO ("connection_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_proxy_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_ip4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_ip6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("connectivity_state", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip_iface", "s"), + NM_DEFINE_GDBUS_ARG_INFO ("vpn_proxy_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO ("debug", "b"), + ), + .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("results", "a(sus)"), + ), + ), + ), +); + +static const GDBusInterfaceVTable interface_vtable = { + .method_call = _method_call, +}; + +/*****************************************************************************/ + static void log_handler (const char *log_domain, GLogLevelFlags log_level, @@ -918,42 +983,55 @@ signal_handler (gpointer user_data) { int signo = GPOINTER_TO_INT (user_data); - g_message ("Caught signal %d, shutting down...", signo); - g_main_loop_quit (loop); + _LOG_X_I ("Caught signal %d, shutting down...", signo); + g_main_loop_quit (gl.loop); - return G_SOURCE_REMOVE; + return G_SOURCE_CONTINUE; } -int -main (int argc, char **argv) +static gboolean +parse_command_line (int *p_argc, + char ***p_argv, + GError **error) { GOptionContext *opt_ctx; - GError *error = NULL; - GDBusConnection *bus; - Handler *handler; - GOptionEntry entries[] = { - { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Output to console rather than syslog", NULL }, - { "persist", 0, 0, G_OPTION_ARG_NONE, &persist, "Don't quit after a short timeout", NULL }, + { "debug", 0, 0, G_OPTION_ARG_NONE, &gl.debug, "Output to console rather than syslog", NULL }, + { "persist", 0, 0, G_OPTION_ARG_NONE, &gl.persist, "Don't quit after a short timeout", NULL }, { NULL } }; + gboolean success; opt_ctx = g_option_context_new (NULL); g_option_context_set_summary (opt_ctx, "Executes scripts upon actions by NetworkManager."); g_option_context_add_main_entries (opt_ctx, entries, NULL); - if (!g_option_context_parse (opt_ctx, &argc, &argv, &error)) { - g_warning ("Error parsing command line arguments: %s", error->message); - g_error_free (error); - return 1; - } + success = g_option_context_parse (opt_ctx, p_argc, p_argv, error); g_option_context_free (opt_ctx); - g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); - g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); + return success; +} - if (debug) { +int +main (int argc, char **argv) +{ + gs_free_error GError *error = NULL; + guint signal_id_term = 0; + guint signal_id_int = 0; + guint dbus_regist_id = 0; + guint dbus_own_name_id = 0; + + if (!parse_command_line (&argc, &argv, &error)) { + _LOG_X_W ("Error parsing command line arguments: %s", error->message); + gl.exit_with_failure = TRUE; + goto done; + } + + signal_id_term = g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); + signal_id_int = g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); + + if (gl.debug) { if (!g_getenv ("G_MESSAGES_DEBUG")) { /* we log our regular messages using g_debug() and g_info(). * When we redirect glib logging to syslog, there is no problem. @@ -964,45 +1042,75 @@ main (int argc, char **argv) } else logging_setup (); - loop = g_main_loop_new (NULL, FALSE); + gl.loop = g_main_loop_new (NULL, FALSE); - bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (!bus) { - g_warning ("Could not get the system bus (%s). Make sure the message bus daemon is running!", - error->message); - g_error_free (error); - return 1; + gl.dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); + if (!gl.dbus_connection) { + _LOG_X_W ("Could not get the system bus (%s). Make sure the message bus daemon is running!", + error->message); + gl.exit_with_failure = TRUE; + goto done; } - handler = g_object_new (HANDLER_TYPE, NULL); - g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (handler->dbus_dispatcher), - bus, - NM_DISPATCHER_DBUS_PATH, - &error); - if (error) { - g_warning ("Could not export Dispatcher D-Bus interface: %s", error->message); - g_error_free (error); - return 1; + gl.requests_waiting = g_queue_new (); + + dbus_regist_id = g_dbus_connection_register_object (gl.dbus_connection, + NM_DISPATCHER_DBUS_PATH, + interface_info, + NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable), + NULL, + NULL, + &error); + if (dbus_regist_id == 0) { + _LOG_X_W ("Could not export Dispatcher D-Bus interface: %s", error->message); + gl.exit_with_failure = 1; + goto done; } - g_bus_own_name_on_connection (bus, - NM_DISPATCHER_DBUS_SERVICE, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_name_acquired, - on_name_lost, - NULL, NULL); - g_object_unref (bus); + dbus_own_name_id = g_bus_own_name_on_connection (gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_name_acquired, + on_name_lost, + NULL, NULL); quit_timeout_reschedule (); - g_main_loop_run (loop); + g_main_loop_run (gl.loop); + +done: + + if (gl.num_requests_pending > 0) { + /* this only happens when we quit due to SIGTERM (not due to the idle timer). + * + * Log a warning about pending scripts. + * + * Maybe we should notify NetworkManager that these scripts are left in an unknown state. + * But this is either a bug of a dispatcher script (not terminating in time). + * + * FIXME(shutdown): Also, currently NetworkManager behaves wrongly on shutdown. + * Note that systemd would not terminate NetworkManager-dispatcher before NetworkManager. + * It's NetworkManager's responsibility to keep running long enough so that all requests + * can complete (with a watchdog timer, and a warning that user provided scripts hang). */ + _LOG_X_W ("exiting but there are still %u requests pending", gl.num_requests_pending); + } + + if (dbus_own_name_id != 0) + g_bus_unown_name (nm_steal_int (&dbus_own_name_id)); + + if (dbus_regist_id != 0) + g_dbus_connection_unregister_object (gl.dbus_connection, nm_steal_int (&dbus_regist_id)); - g_queue_free (handler->requests_waiting); - g_object_unref (handler); + nm_clear_pointer (&gl.requests_waiting, g_queue_free); - if (!debug) + nm_clear_g_source (&signal_id_term); + nm_clear_g_source (&signal_id_int); + nm_clear_g_source (&gl.quit_id); + g_clear_pointer (&gl.loop, g_main_loop_unref); + g_clear_object (&gl.dbus_connection); + + if (!gl.debug) logging_shutdown (); - return 0; + return gl.exit_with_failure ? 1 : 0; } - diff --git a/dispatcher/tests/meson.build b/dispatcher/tests/meson.build index 3da3c3e912..10d1c6a93f 100644 --- a/dispatcher/tests/meson.build +++ b/dispatcher/tests/meson.build @@ -7,7 +7,7 @@ incs = [ exe = executable( test_unit, - test_unit + '.c', + [ test_unit + '.c' ] + [ nmdbus_dispatcher_sources ], include_directories: incs, dependencies: libnm_core_dep, c_args: [ diff --git a/dispatcher/tests/test-dispatcher-envp.c b/dispatcher/tests/test-dispatcher-envp.c index cdc3e6b25b..b1205c3235 100644 --- a/dispatcher/tests/test-dispatcher-envp.c +++ b/dispatcher/tests/test-dispatcher-envp.c @@ -28,6 +28,8 @@ #include "nm-utils/nm-test-utils.h" +#include "nmdbus-dispatcher.h" + #define TEST_DIR NM_BUILD_SRCDIR"/dispatcher/tests" /*****************************************************************************/ @@ -637,6 +639,17 @@ test_up_empty_vpn_iface (void) /*****************************************************************************/ +static void +test_gdbus_codegen (void) +{ + gs_unref_object NMDBusDispatcher *dbus_dispatcher = NULL; + + dbus_dispatcher = nmdbus_dispatcher_skeleton_new (); + g_assert (NMDBUS_IS_DISPATCHER_SKELETON (dbus_dispatcher)); +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -653,6 +666,8 @@ main (int argc, char **argv) g_test_add_func ("/dispatcher/up_empty_vpn_iface", test_up_empty_vpn_iface); + g_test_add_func ("/dispatcher/gdbus-codegen", test_gdbus_codegen); + return g_test_run (); } diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 77288dde58..1ff90dc5d4 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -635,8 +635,10 @@ NM_G_ERROR_MSG (GError *error) * It's useful to check the let the compiler ensure that @value is * of a certain type. */ #define _NM_ENSURE_TYPE(type, value) (_Generic ((value), type: (value))) +#define _NM_ENSURE_TYPE_CONST(type, value) (_Generic ((value), const type: ((const type) (value)), type: ((const type) (value)))) #else #define _NM_ENSURE_TYPE(type, value) (value) +#define _NM_ENSURE_TYPE_CONST(type, value) ((const type) (value)) #endif #if _NM_CC_SUPPORT_GENERIC diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d752f3b138..493288c197 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -328,16 +328,18 @@ typedef struct _NMDevicePrivate { ActivationHandleData act_handle4; /* for layer2 and IPv4. */ ActivationHandleData act_handle6; guint recheck_assume_id; + struct { guint call_id; NMDeviceStateReason available_reason; NMDeviceStateReason unavailable_reason; - } recheck_available; + } recheck_available; + struct { - guint call_id; + NMDispatcherCallId *call_id; NMDeviceState post_state; NMDeviceStateReason post_state_reason; - } dispatcher; + } dispatcher; /* Link stuff */ guint link_connected_id; @@ -12373,29 +12375,31 @@ nm_device_get_ip6_config (NMDevice *self) /*****************************************************************************/ -static void +static gboolean dispatcher_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->dispatcher.call_id) { - nm_dispatcher_call_cancel (priv->dispatcher.call_id); - priv->dispatcher.call_id = 0; - priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; - priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; - } + if (!priv->dispatcher.call_id) + return FALSE; + + nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher.call_id)); + priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; + priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; + return TRUE; } static void -dispatcher_complete_proceed_state (guint call_id, gpointer user_data) +dispatcher_complete_proceed_state (NMDispatcherCallId *call_id, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); g_return_if_fail (call_id == priv->dispatcher.call_id); - priv->dispatcher.call_id = 0; - nm_device_queue_state (self, priv->dispatcher.post_state, + priv->dispatcher.call_id = NULL; + nm_device_queue_state (self, + priv->dispatcher.post_state, priv->dispatcher.post_state_reason); priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; @@ -12408,10 +12412,8 @@ ip_check_pre_up (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->dispatcher.call_id != 0) { - g_warn_if_reached (); - dispatcher_cleanup (self); - } + if (dispatcher_cleanup (self)) + nm_assert_not_reached (); priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; @@ -14822,7 +14824,7 @@ deactivate_async_ready (NMDevice *self, } static void -deactivate_dispatcher_complete (guint call_id, gpointer user_data) +deactivate_dispatcher_complete (NMDispatcherCallId *call_id, gpointer user_data) { NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -14833,7 +14835,7 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data) reason = priv->dispatcher.post_state_reason; - priv->dispatcher.call_id = 0; + priv->dispatcher.call_id = NULL; priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN; priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; diff --git a/src/main.c b/src/main.c index 4737d2a164..e9e2c4306c 100644 --- a/src/main.c +++ b/src/main.c @@ -436,8 +436,6 @@ main (int argc, char *argv[]) nm_manager_dbus_set_property_handle, manager); - nm_dispatcher_init (); - g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); if (!nm_manager_start (manager, &error)) { diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c index 8ac18ed44a..d65f344949 100644 --- a/src/nm-dispatcher.c +++ b/src/nm-dispatcher.c @@ -44,8 +44,121 @@ #define _NMLOG_DOMAIN LOGD_DISPATCH #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "dispatcher", __VA_ARGS__) -static GDBusProxy *dispatcher_proxy; -static GHashTable *requests = NULL; +#define _NMLOG2_DOMAIN LOGD_DISPATCH +#define _NMLOG2(level, request_id, log_ifname, log_con_uuid, ...) \ + nm_log ((level), \ + _NMLOG2_DOMAIN, \ + (log_ifname), \ + (log_con_uuid), \ + "dispatcher: (%u) " \ + _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ + (request_id) \ + _NM_UTILS_MACRO_REST (__VA_ARGS__)) + +#define _NMLOG3_DOMAIN LOGD_DISPATCH +#define _NMLOG3(level, call_id, ...) \ + G_STMT_START { \ + const NMDispatcherCallId *const _call_id = (call_id); \ + \ + _NMLOG2 (level, _call_id->request_id, _call_id->log_ifname, _call_id->log_con_uuid, __VA_ARGS__); \ + } G_STMT_END + +/*****************************************************************************/ + +struct NMDispatcherCallId { + NMDispatcherFunc callback; + gpointer user_data; + const char *log_ifname; + const char *log_con_uuid; + NMDispatcherAction action; + guint idle_id; + guint32 request_id; + char extra_strings[]; +}; + +/*****************************************************************************/ + +/* FIXME(shutdown): on shutdown, we should not run dispatcher scripts synchronously. + * Instead, we should of course still run them asynchronously. + * + * Also, we should wait for all pending requests to complete before exiting the main-loop + * (with a watchdog). If we hit a timeout, we log a warning and quit (but leave the scripts + * running). + * + * Finally, cleanup the global structures. */ +static struct { + GDBusConnection *dbus_connection; + GHashTable *requests; + guint request_id_counter; +} gl; + +/*****************************************************************************/ + +static NMDispatcherCallId * +dispatcher_call_id_new (guint32 request_id, + NMDispatcherAction action, + NMDispatcherFunc callback, + gpointer user_data, + const char *log_ifname, + const char *log_con_uuid) +{ + NMDispatcherCallId *call_id; + gsize l_log_ifname; + gsize l_log_con_uuid; + char *extra_strings; + + l_log_ifname = log_ifname ? (strlen (log_ifname) + 1) : 0u; + l_log_con_uuid = log_con_uuid ? (strlen (log_con_uuid) + 1) : 0u; + + call_id = g_malloc (sizeof (NMDispatcherCallId) + l_log_ifname + l_log_con_uuid); + + call_id->action = action; + call_id->request_id = request_id; + call_id->callback = callback; + call_id->user_data = user_data; + call_id->idle_id = 0; + + extra_strings = &call_id->extra_strings[0]; + + if (log_ifname) { + call_id->log_ifname = extra_strings; + memcpy (extra_strings, log_ifname, l_log_ifname); + extra_strings += l_log_ifname; + } else + call_id->log_ifname = NULL; + + if (log_con_uuid) { + call_id->log_con_uuid = extra_strings; + memcpy (extra_strings, log_con_uuid, l_log_con_uuid); + } else + call_id->log_con_uuid = NULL; + + return call_id; +} + +static void +dispatcher_call_id_free (NMDispatcherCallId *call_id) +{ + nm_clear_g_source (&call_id->idle_id); + g_free (call_id); +} + +/*****************************************************************************/ + + +static void +_init_dispatcher (void) +{ + if (G_UNLIKELY (gl.requests == NULL)) { + gl.requests = g_hash_table_new (nm_direct_hash, NULL); + gl.dbus_connection = nm_g_object_ref (NM_MAIN_DBUS_CONNECTION_GET); + + if (!gl.dbus_connection) + _LOGD ("No D-Bus connection to talk with NetworkManager-dispatcher service"); + } +} + +/*****************************************************************************/ static void dump_proxy_to_props (NMProxyConfig *proxy, GVariantBuilder *builder) @@ -287,39 +400,6 @@ fill_vpn_props (NMProxyConfig *proxy_config, dump_ip6_to_props (ip6_config, ip6_builder); } -typedef struct { - NMDispatcherAction action; - guint request_id; - NMDispatcherFunc callback; - gpointer user_data; - guint idle_id; -} DispatchInfo; - -static void -dispatcher_info_free (DispatchInfo *info) -{ - if (info->idle_id) - g_source_remove (info->idle_id); - g_free (info); -} - -static void -_ensure_requests (void) -{ - if (G_UNLIKELY (requests == NULL)) { - requests = g_hash_table_new_full (nm_direct_hash, - NULL, - NULL, - (GDestroyNotify) dispatcher_info_free); - } -} - -static void -dispatcher_info_cleanup (DispatchInfo *info) -{ - g_hash_table_remove (requests, GUINT_TO_POINTER (info->request_id)); -} - static const char * dispatch_result_to_string (DispatchResult result) { @@ -339,63 +419,71 @@ dispatch_result_to_string (DispatchResult result) } static void -dispatcher_results_process (guint request_id, NMDispatcherAction action, GVariantIter *results) +dispatcher_results_process (guint32 request_id, + const char *log_ifname, + const char *log_con_uuid, + GVariant *v_results) { + nm_auto_free_variant_iter GVariantIter *results = NULL; const char *script, *err; guint32 result; - g_return_if_fail (results != NULL); + g_variant_get (v_results, "(a(sus))", &results); if (g_variant_iter_n_children (results) == 0) { - _LOGD ("(%u) succeeded but no scripts invoked", request_id); + _LOG2D (request_id, log_ifname, log_con_uuid, "succeeded but no scripts invoked"); return; } while (g_variant_iter_next (results, "(&su&s)", &script, &result, &err)) { if (result == DISPATCH_RESULT_SUCCESS) { - _LOGD ("(%u) %s succeeded", request_id, script); + _LOG2D (request_id, log_ifname, log_con_uuid, "%s succeeded", script); } else { - _LOGW ("(%u) %s failed (%s): %s", - request_id, - script, - dispatch_result_to_string (result), - err); + _LOG2W (request_id, + log_ifname, + log_con_uuid, + "%s failed (%s): %s", + script, + dispatch_result_to_string (result), + err); } } } static void -dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) +dispatcher_done_cb (GObject *source, GAsyncResult *result, gpointer user_data) { - DispatchInfo *info = user_data; - GVariant *ret; - GVariantIter *results; - GError *error = NULL; - - ret = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), result, - G_VARIANT_TYPE ("(a(sus))"), - &error); - if (ret) { - g_variant_get (ret, "(a(sus))", &results); - dispatcher_results_process (info->request_id, info->action, results); - g_variant_iter_free (results); - g_variant_unref (ret); - } else { + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + NMDispatcherCallId *call_id = user_data; + + nm_assert ((gpointer) source == gl.dbus_connection); + + ret = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), + result, + &error); + if (!ret) { if (_nm_dbus_error_has_name (error, "org.freedesktop.systemd1.LoadFailed")) { g_dbus_error_strip_remote_error (error); - _LOGW ("(%u) failed to call dispatcher scripts: %s", - info->request_id, error->message); + _LOG3W (call_id, "failed to call dispatcher scripts: %s", + error->message); } else { - _LOGD ("(%u) failed to call dispatcher scripts: %s", - info->request_id, error->message); + _LOG3D (call_id, "failed to call dispatcher scripts: %s", + error->message); } - g_clear_error (&error); + } else { + dispatcher_results_process (call_id->request_id, + call_id->log_ifname, + call_id->log_con_uuid, + ret); } - if (info->callback) - info->callback (info->request_id, info->user_data); + g_hash_table_remove (gl.requests, call_id); - dispatcher_info_cleanup (info); + if (call_id->callback) + call_id->callback (call_id, call_id->user_data); + + dispatcher_call_id_free (call_id); } static const char *action_table[] = { @@ -416,8 +504,9 @@ static const char *action_table[] = { static const char * action_to_string (NMDispatcherAction action) { - g_assert ((gsize) action < G_N_ELEMENTS (action_table)); - return action_table[action]; + if (G_UNLIKELY ((gsize) action >= G_N_ELEMENTS (action_table))) + g_return_val_if_reached (NULL); + return action_table[(gsize) action]; } static gboolean @@ -434,7 +523,7 @@ _dispatcher_call (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { GVariant *connection_dict; GVariantBuilder connection_props; @@ -442,47 +531,57 @@ _dispatcher_call (NMDispatcherAction action, GVariantBuilder device_proxy_props; GVariantBuilder device_ip4_props; GVariantBuilder device_ip6_props; - GVariant *device_dhcp4_props = NULL; - GVariant *device_dhcp6_props = NULL; + gs_unref_variant GVariant *parameters_floating = NULL; + gs_unref_variant GVariant *device_dhcp4_props = NULL; + gs_unref_variant GVariant *device_dhcp6_props = NULL; GVariantBuilder vpn_proxy_props; GVariantBuilder vpn_ip4_props; GVariantBuilder vpn_ip6_props; - DispatchInfo *info = NULL; - gboolean success = FALSE; - GError *error = NULL; - static guint request_counter = 0; - guint reqid = ++request_counter; + NMDispatcherCallId *call_id; + guint request_id; const char *connectivity_state_string = "UNKNOWN"; + const char *log_ifname; + const char *log_con_uuid; - if (!dispatcher_proxy) - return FALSE; + g_return_val_if_fail (!blocking || (!callback && !user_data), FALSE); + + NM_SET_OUT (out_call_id, NULL); - /* Wrapping protection */ - if (G_UNLIKELY (!reqid)) - reqid = ++request_counter; + _init_dispatcher (); + + if (!gl.dbus_connection) + return FALSE; - g_assert (!blocking || (!callback && !user_data)); + log_ifname = device ? nm_device_get_iface (device) : NULL; + log_con_uuid = settings_connection ? nm_settings_connection_get_uuid (settings_connection) : NULL; - _ensure_requests (); + request_id = ++gl.request_id_counter; + if (G_UNLIKELY (!request_id)) + request_id = ++gl.request_id_counter; /* All actions except 'hostname' and 'connectivity-change' require a device */ if ( action == NM_DISPATCHER_ACTION_HOSTNAME || action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) { - _LOGD ("(%u) dispatching action '%s'%s", - reqid, action_to_string (action), - blocking + _LOG2D (request_id, + log_ifname, + log_con_uuid, + "dispatching action '%s'%s", + action_to_string (action), + blocking ? " (blocking)" : (callback ? " (with callback)" : "")); } else { g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); - _LOGD ("(%u) (%s) dispatching action '%s'%s", - reqid, - vpn_iface ?: nm_device_get_iface(device), - action_to_string (action), - blocking - ? " (blocking)" - : (callback ? " (with callback)" : "")); + _LOG2D (request_id, + log_ifname, + log_con_uuid, + "(%s) dispatching action '%s'%s", + vpn_iface ?: nm_device_get_iface (device), + action_to_string (action), + blocking + ? " (blocking)" + : (callback ? " (with callback)" : "")); } if (applied_connection) @@ -542,90 +641,72 @@ _dispatcher_call (NMDispatcherAction action, } } - if (!device_dhcp4_props) - device_dhcp4_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); - if (!device_dhcp6_props) - device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0)); - connectivity_state_string = nm_connectivity_state_to_string (connectivity_state); + parameters_floating = g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", + action_to_string (action), + connection_dict, + &connection_props, + &device_props, + &device_proxy_props, + &device_ip4_props, + &device_ip6_props, + device_dhcp4_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0), + device_dhcp6_props ?: g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0), + connectivity_state_string, + vpn_iface ?: "", + &vpn_proxy_props, + &vpn_ip4_props, + &vpn_ip6_props, + nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)); + /* Send the action to the dispatcher */ if (blocking) { - GVariant *ret; - GVariantIter *results; - - ret = _nm_dbus_proxy_call_sync (dispatcher_proxy, "Action", - g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", - action_to_string (action), - connection_dict, - &connection_props, - &device_props, - &device_proxy_props, - &device_ip4_props, - &device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, - connectivity_state_string, - vpn_iface ?: "", - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)), - G_VARIANT_TYPE ("(a(sus))"), - G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, - NULL, &error); - if (ret) { - g_variant_get (ret, "(a(sus))", &results); - dispatcher_results_process (reqid, action, results); - g_variant_iter_free (results); - g_variant_unref (ret); - success = TRUE; - } else { + gs_unref_variant GVariant *ret = NULL; + gs_free_error GError *error = NULL; + + ret = g_dbus_connection_call_sync (gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_INTERFACE, + "Action", + g_steal_pointer (¶meters_floating), + G_VARIANT_TYPE ("(a(sus))"), + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + &error); + if (!ret) { g_dbus_error_strip_remote_error (error); - _LOGW ("(%u) failed: %s", reqid, error->message); - g_clear_error (&error); - success = FALSE; + _LOG2W (request_id, log_ifname, log_con_uuid, "failed: %s", error->message); + return FALSE; } - } else { - info = g_malloc0 (sizeof (*info)); - info->action = action; - info->request_id = reqid; - info->callback = callback; - info->user_data = user_data; - g_dbus_proxy_call (dispatcher_proxy, "Action", - g_variant_new ("(s@a{sa{sv}}a{sv}a{sv}a{sv}a{sv}a{sv}@a{sv}@a{sv}ssa{sv}a{sv}a{sv}b)", - action_to_string (action), - connection_dict, - &connection_props, - &device_props, - &device_proxy_props, - &device_ip4_props, - &device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, - connectivity_state_string, - vpn_iface ?: "", - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - nm_logging_enabled (LOGL_DEBUG, LOGD_DISPATCH)), - G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, - NULL, dispatcher_done_cb, info); - success = TRUE; + dispatcher_results_process (request_id, log_ifname, log_con_uuid, ret); + return TRUE; } - g_variant_unref (device_dhcp4_props); - g_variant_unref (device_dhcp6_props); - - if (success && info) { - /* Track the request in case of cancelation */ - g_hash_table_insert (requests, GUINT_TO_POINTER (info->request_id), info); - if (out_call_id) - *out_call_id = info->request_id; - } else if (out_call_id) - *out_call_id = 0; - - return success; + call_id = dispatcher_call_id_new (request_id, + action, + callback, + user_data, + log_ifname, + log_con_uuid); + + g_dbus_connection_call (gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_INTERFACE, + "Action", + g_steal_pointer (¶meters_floating), + G_VARIANT_TYPE ("(a(sus))"), + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + dispatcher_done_cb, + call_id); + g_hash_table_add (gl.requests, call_id); + NM_SET_OUT (out_call_id, call_id); + return TRUE; } /** @@ -642,7 +723,7 @@ _dispatcher_call (NMDispatcherAction action, gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE, NULL, NULL, NULL, FALSE, @@ -673,7 +754,7 @@ nm_dispatcher_call_device (NMDispatcherAction action, NMActRequest *act_request, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { nm_assert (NM_IS_DEVICE (device)); if (!act_request) { @@ -757,7 +838,7 @@ nm_dispatcher_call_vpn (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (action, FALSE, parent_device, @@ -821,7 +902,7 @@ gboolean nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id) + NMDispatcherCallId **out_call_id) { return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE, NULL, NULL, NULL, FALSE, @@ -831,40 +912,16 @@ nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state, } void -nm_dispatcher_call_cancel (guint call_id) +nm_dispatcher_call_cancel (NMDispatcherCallId *call_id) { - DispatchInfo *info; - - _ensure_requests (); + if ( !call_id + || g_hash_table_lookup (gl.requests, call_id) != call_id + || !call_id->callback) + g_return_if_reached (); /* Canceling just means the callback doesn't get called, so set the * DispatcherInfo's callback to NULL. */ - info = g_hash_table_lookup (requests, GUINT_TO_POINTER (call_id)); - g_return_if_fail (info); - - if (info && info->callback) { - _LOGD ("(%u) cancelling dispatcher callback action", call_id); - info->callback = NULL; - } + _LOG3D (call_id, "cancelling dispatcher callback action"); + call_id->callback = NULL; } - -void -nm_dispatcher_init (void) -{ - GError *error = NULL; - - dispatcher_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, - NULL, - NM_DISPATCHER_DBUS_SERVICE, - NM_DISPATCHER_DBUS_PATH, - NM_DISPATCHER_DBUS_INTERFACE, - NULL, &error); - if (!dispatcher_proxy) { - _LOGE ("could not get dispatcher proxy! %s", error->message); - g_clear_error (&error); - } -} - diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h index 1cdeeb882a..3c79f3bbfb 100644 --- a/src/nm-dispatcher.h +++ b/src/nm-dispatcher.h @@ -39,18 +39,20 @@ typedef enum { NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE } NMDispatcherAction; -typedef void (*NMDispatcherFunc) (guint call_id, gpointer user_data); +typedef struct NMDispatcherCallId NMDispatcherCallId; + +typedef void (*NMDispatcherFunc) (NMDispatcherCallId *call_id, gpointer user_data); gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_device (NMDispatcherAction action, NMDevice *device, NMActRequest *act_request, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action, NMDevice *device, @@ -66,7 +68,7 @@ gboolean nm_dispatcher_call_vpn (NMDispatcherAction action, NMIP6Config *vpn_ip6_config, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); + NMDispatcherCallId **out_call_id); gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action, NMSettingsConnection *settings_connection, @@ -80,10 +82,8 @@ gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action, gboolean nm_dispatcher_call_connectivity (NMConnectivityState state, NMDispatcherFunc callback, gpointer user_data, - guint *out_call_id); - -void nm_dispatcher_call_cancel (guint call_id); + NMDispatcherCallId **out_call_id); -void nm_dispatcher_init (void); +void nm_dispatcher_call_cancel (NMDispatcherCallId *call_id); #endif /* __NM_DISPATCHER_H__ */ diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 2c4f33359f..99bcd02712 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -103,7 +103,7 @@ typedef struct { char *username; VpnState vpn_state; - guint dispatcher_id; + NMDispatcherCallId *dispatcher_id; NMActiveConnectionStateReason failure_reason; NMVpnServiceState service_state; @@ -418,22 +418,28 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) } static void -dispatcher_pre_down_done (guint call_id, gpointer user_data) +dispatcher_pre_down_done (NMDispatcherCallId *call_id, gpointer user_data) { NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->dispatcher_id = 0; + nm_assert (call_id); + nm_assert (priv->dispatcher_id == call_id); + + priv->dispatcher_id = NULL; _set_vpn_state (self, STATE_DISCONNECTED, NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE); } static void -dispatcher_pre_up_done (guint call_id, gpointer user_data) +dispatcher_pre_up_done (NMDispatcherCallId *call_id, gpointer user_data) { NMVpnConnection *self = NM_VPN_CONNECTION (user_data); NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - priv->dispatcher_id = 0; + nm_assert (call_id); + nm_assert (priv->dispatcher_id == call_id); + + priv->dispatcher_id = NULL; _set_vpn_state (self, STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE); } @@ -442,10 +448,8 @@ dispatcher_cleanup (NMVpnConnection *self) { NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); - if (priv->dispatcher_id) { - nm_dispatcher_call_cancel (priv->dispatcher_id); - priv->dispatcher_id = 0; - } + if (priv->dispatcher_id) + nm_dispatcher_call_cancel (g_steal_pointer (&priv->dispatcher_id)); } static void |