diff options
| author | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-07-31 10:51:42 +0200 |
| commit | 2e5fa45ddfbb5cffa1e78221f1cea706e2f298af (patch) | |
| tree | 86f69d36c56de3074280456eddc854a780b8e04b /dispatcher | |
| parent | 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c (diff) | |
New upstream version 1.19.90 upstream/1.19.90
Diffstat (limited to 'dispatcher')
| -rw-r--r-- | dispatcher/meson.build | 2 | ||||
| -rw-r--r-- | dispatcher/nm-dispatcher-utils.c | 46 | ||||
| -rw-r--r-- | dispatcher/nm-dispatcher-utils.h | 1 | ||||
| -rw-r--r-- | dispatcher/nm-dispatcher.c | 718 | ||||
| -rw-r--r-- | dispatcher/tests/meson.build | 7 | ||||
| -rw-r--r-- | dispatcher/tests/test-dispatcher-envp.c | 16 |
6 files changed, 491 insertions, 299 deletions
diff --git a/dispatcher/meson.build b/dispatcher/meson.build index 7bd41935..da9ac7f2 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-utils.c b/dispatcher/nm-dispatcher-utils.c index f56ba38f..d9e7b32b 100644 --- a/dispatcher/nm-dispatcher-utils.c +++ b/dispatcher/nm-dispatcher-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -382,6 +381,8 @@ construct_device_dhcp_items (GPtrArray *items, int addr_family, GVariant *dhcp_c const char *key; GVariant *val; char four_or_six; + gboolean found_unknown_245 = FALSE; + gs_unref_variant GVariant *private_245_val = NULL; if (!dhcp_config) return; @@ -403,10 +404,43 @@ construct_device_dhcp_items (GPtrArray *items, int addr_family, GVariant *dhcp_c four_or_six, ucased, g_variant_get_string (val, NULL)); + + /* MS Azure sends the server endpoint in the dhcp private + * option 245. cloud-init searches the Azure server endpoint + * value looking for the standard dhclient label used for + * that option, which is "unknown_245". + * The 11-dhclient script shipped with Fedora and RHEL dhcp + * package converts our dispatcher environment vars to the + * dhclient ones (new_<some_option>) and calls dhclient hook + * scripts. + * Let's make cloud-init happy and let's duplicate the dhcp + * option 245 with the legacy name of the default dhclient + * label also when using the internal client. + * Note however that the dhclient plugin will have unknown_ + * labels represented as ascii string when possible, falling + * back to hex string otherwise. + * private_ labels instead are always in hex string format. + * This shouldn't affect the MS Azure server endpoint value, + * as it usually belongs to the 240.0.0.0/4 network and so + * is always represented as an hex string. Moreover, cloudinit + * code checks just for an hex value in unknown_245. + */ + if (addr_family == AF_INET) { + if (nm_streq (key, "private_245")) + private_245_val = g_variant_ref (val); + else if (nm_streq (key, "unknown_245")) + found_unknown_245 = true; + } } } g_variant_unref (val); } + + if (private_245_val != NULL && !found_unknown_245) { + _items_add_printf (items, + "DHCP4_UNKNOWN_245=%s", + g_variant_get_string (private_245_val, NULL)); + } } /*****************************************************************************/ @@ -456,12 +490,8 @@ nm_dispatcher_utils_construct_envp (const char *action, goto done; /* Connection properties */ - if (!g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_PATH, "&o", &path)) { - *out_error_message = "Missing or invalid required value " NMD_CONNECTION_PROPS_PATH "!"; - return NULL; - } - - _items_add_key (items, NULL, "CONNECTION_DBUS_PATH", path); + if (g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_PATH, "&o", &path)) + _items_add_key (items, NULL, "CONNECTION_DBUS_PATH", path); if (g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_EXTERNAL, "b", &external) && external) _items_add_str (items, "CONNECTION_EXTERNAL=1"); @@ -535,7 +565,7 @@ nm_dispatcher_utils_construct_envp (const char *action, _items_add_key0 (items, NULL, "DEVICE_IP_IFACE", ip_iface); } - /* Device it's aren't valid if the device isn't activated */ + /* Device items aren't valid if the device isn't activated */ if ( iface && dev_state == NM_DEVICE_STATE_ACTIVATED) { construct_proxy_items (items, device_proxy_props, NULL); diff --git a/dispatcher/nm-dispatcher-utils.h b/dispatcher/nm-dispatcher-utils.h index a6034326..dca9b84f 100644 --- a/dispatcher/nm-dispatcher-utils.h +++ b/dispatcher/nm-dispatcher-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/dispatcher/nm-dispatcher.c b/dispatcher/nm-dispatcher.c index fe6eaa17..311ad8d4 100644 --- a/dispatcher/nm-dispatcher.c +++ b/dispatcher/nm-dispatcher.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -34,74 +33,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 +66,6 @@ typedef struct { } ScriptInfo; struct Request { - Handler *handler; - guint request_id; GDBusMethodInvocation *context; @@ -135,52 +82,86 @@ struct Request { /*****************************************************************************/ -#define __LOG_print(print_cmd, _request, _script, ...) \ +#define __LOG_print(print_cmd, ...) \ + G_STMT_START { \ + 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_print_R(print_cmd, _request, ...) \ 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__)); \ + __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(_request, _script, log_always, print_cmd, ...) \ +#define __LOG_print_S(print_cmd, _request, _script, ...) \ G_STMT_START { \ - const Request *__request = (_request); \ - const ScriptInfo *__script = (_script); \ + __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_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_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_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 +192,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 +221,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 +261,6 @@ complete_request (Request *request) GVariantBuilder results; GVariant *ret; guint i; - Handler *handler = request->handler; nm_assert (request); @@ -301,16 +281,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 +298,6 @@ complete_request (Request *request) static void complete_script (ScriptInfo *script) { - Handler *handler; Request *request; gboolean wait = script->wait; @@ -331,9 +310,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 +323,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 +342,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 +395,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); @@ -466,12 +443,6 @@ check_permissions (struct stat *s, const char **out_error_msg) g_return_val_if_fail (out_error_msg != NULL, FALSE); g_return_val_if_fail (*out_error_msg == NULL, FALSE); - /* Only accept regular files */ - if (!S_ISREG (s->st_mode)) { - *out_error_msg = "not a regular file."; - return FALSE; - } - /* Only accept files owned by root */ if (s->st_uid != 0) { *out_error_msg = "not owned by root."; @@ -539,7 +510,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); @@ -573,59 +544,108 @@ dispatch_one_script (Request *request) return FALSE; } -static GSList * -find_scripts (const char *str_action) +static int +_compare_basenames (gconstpointer a, gconstpointer b) +{ + const char *basename_a = strrchr (a, '/'); + const char *basename_b = strrchr (b, '/'); + int ret; + + nm_assert (basename_a); + nm_assert (basename_b); + + ret = strcmp (++basename_a, ++basename_b); + if (ret) + return ret; + + nm_assert_not_reached (); + return 0; +} + +static void +_find_scripts (Request *request, GHashTable *scripts, const char *base, const char *subdir) { - GDir *dir; const char *filename; - GSList *sorted = NULL; + gs_free char *dirname = NULL; GError *error = NULL; - const char *dirname; - - if ( strcmp (str_action, NMD_ACTION_PRE_UP) == 0 - || strcmp (str_action, NMD_ACTION_VPN_PRE_UP) == 0) - dirname = NMD_SCRIPT_DIR_PRE_UP; - else if ( strcmp (str_action, NMD_ACTION_PRE_DOWN) == 0 - || strcmp (str_action, NMD_ACTION_VPN_PRE_DOWN) == 0) - dirname = NMD_SCRIPT_DIR_PRE_DOWN; - else - dirname = NMD_SCRIPT_DIR_DEFAULT; + GDir *dir; + + 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 NULL; + return; } while ((filename = g_dir_read_name (dir))) { - char *path; + if (!check_filename (filename)) + continue; + + g_hash_table_insert (scripts, + g_strdup (filename), + g_build_filename (dirname, filename, NULL)); + } + + g_dir_close (dir); +} + +static GSList * +find_scripts (Request *request) +{ + gs_unref_hashtable GHashTable *scripts = NULL; + GSList *script_list = NULL; + GHashTableIter iter; + const char *subdir; + char *path; + char *filename; + + if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_UP, + NMD_ACTION_VPN_PRE_UP)) + subdir = "pre-up.d"; + 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 (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)) { struct stat st; + char *link_target; int err; const char *err_msg = NULL; - if (!check_filename (filename)) + link_target = g_file_read_link (path, NULL); + if (g_strcmp0 (link_target, "/dev/null") == 0) { + g_free (link_target); continue; - - path = g_build_filename (dirname, filename, NULL); + } + g_free (link_target); err = stat (path, &st); if (err) - g_warning ("find-scripts: Failed to stat '%s': %d", path, err); - else if (S_ISDIR (st.st_mode)) + _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 */ - sorted = g_slist_insert_sorted (sorted, path, (GCompareFunc) g_strcmp0); - path = NULL; + script_list = g_slist_prepend (script_list, g_strdup (path)); + continue; } - g_free (path); } - g_dir_close (dir); - return sorted; + return g_slist_sort (script_list, _compare_basenames); } static gboolean @@ -636,6 +656,7 @@ script_must_wait (const char *path) gs_free char *real = NULL; char *tmp; + link = g_file_read_link (path, NULL); if (link) { if (!g_path_is_absolute (link)) { @@ -648,35 +669,32 @@ script_must_wait (const char *path) dir = g_path_get_dirname (link); real = realpath (dir, NULL); - - if (real && !strcmp (real, NMD_SCRIPT_DIR_NO_WAIT)) + if (real && !g_str_has_suffix (real, "/no-wait.d")) return FALSE; } 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; @@ -684,33 +702,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; @@ -722,11 +772,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) { @@ -735,18 +785,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); @@ -761,8 +811,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)) { @@ -770,7 +820,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. */ @@ -783,24 +833,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 @@ -809,22 +855,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, @@ -879,42 +982,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; +} + +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 (debug) { + 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. @@ -925,45 +1041,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); - g_queue_free (handler->requests_waiting); - g_object_unref (handler); +done: - if (!debug) + 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)); + + nm_clear_pointer (&gl.requests_waiting, g_queue_free); + + 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 3da3c3e9..9d6a16de 100644 --- a/dispatcher/tests/meson.build +++ b/dispatcher/tests/meson.build @@ -7,9 +7,12 @@ incs = [ exe = executable( test_unit, - test_unit + '.c', + [ test_unit + '.c' ] + [ nmdbus_dispatcher_sources ], include_directories: incs, - dependencies: libnm_core_dep, + dependencies: [ + libnm_core_dep, + libnm_dep, + ], c_args: [ '-DNETWORKMANAGER_COMPILATION_TEST', '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_CLIENT', diff --git a/dispatcher/tests/test-dispatcher-envp.c b/dispatcher/tests/test-dispatcher-envp.c index cdc3e6b2..47ed04ee 100644 --- a/dispatcher/tests/test-dispatcher-envp.c +++ b/dispatcher/tests/test-dispatcher-envp.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,6 +27,8 @@ #include "nm-utils/nm-test-utils.h" +#include "nmdbus-dispatcher.h" + #define TEST_DIR NM_BUILD_SRCDIR"/dispatcher/tests" /*****************************************************************************/ @@ -637,6 +638,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 +665,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 (); } |