diff --git a/src/core/nm-dispatcher.c b/src/core/nm-dispatcher.c index 1515efeca9..f2016f14e5 100644 --- a/src/core/nm-dispatcher.c +++ b/src/core/nm-dispatcher.c @@ -57,10 +57,12 @@ struct NMDispatcherCallId { gpointer user_data; const char *log_ifname; const char *log_con_uuid; + GVariant *action_params; gint64 start_at_msec; NMDispatcherAction action; guint idle_id; guint32 request_id; + bool is_action2 : 1; char extra_strings[]; }; @@ -121,6 +123,7 @@ dispatcher_call_id_new(guint32 request_id, call_id->callback = callback; call_id->user_data = user_data; call_id->idle_id = 0; + call_id->is_action2 = TRUE; extra_strings = &call_id->extra_strings[0]; @@ -143,6 +146,7 @@ dispatcher_call_id_new(guint32 request_id, static void dispatcher_call_id_free(NMDispatcherCallId *call_id) { + nm_clear_pointer(&call_id->action_params, g_variant_unref); nm_clear_g_source(&call_id->idle_id); g_free(call_id); } @@ -390,14 +394,18 @@ dispatcher_results_process(guint32 request_id, gint64 now_msec, const char *log_ifname, const char *log_con_uuid, - GVariant *v_results) + GVariant *v_results, + gboolean is_action2) { nm_auto_free_variant_iter GVariantIter *results = NULL; const char *script, *err; guint32 result; gsize n_children; - g_variant_get(v_results, "(a(sus))", &results); + if (is_action2) + g_variant_get(v_results, "(a(susa{sv}))", &results); + else + g_variant_get(v_results, "(a(sus))", &results); n_children = g_variant_iter_n_children(results); @@ -412,7 +420,17 @@ dispatcher_results_process(guint32 request_id, if (n_children == 0) return; - while (g_variant_iter_next(results, "(&su&s)", &script, &result, &err)) { + while (TRUE) { + gs_unref_variant GVariant *options = NULL; + + if (is_action2) { + if (!g_variant_iter_next(results, "(&su&s@a{sv})", &script, &result, &err, &options)) + break; + } else { + if (!g_variant_iter_next(results, "(&su&s)", &script, &result, &err)) + break; + } + if (result == DISPATCH_RESULT_SUCCESS) { _LOG2D(request_id, log_ifname, log_con_uuid, "%s succeeded", script); } else { @@ -440,6 +458,27 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) now_msec = nm_utils_get_monotonic_timestamp_msec(); ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (!ret && call_id->is_action2 + && g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + _LOG3D(call_id, + "dispatcher service does not implement Action2() method, falling back to Action()"); + call_id->is_action2 = FALSE; + g_dbus_connection_call(gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + NM_DISPATCHER_DBUS_PATH, + NM_DISPATCHER_DBUS_INTERFACE, + "Action", + g_steal_pointer(&call_id->action_params), + G_VARIANT_TYPE("(a(sus))"), + G_DBUS_CALL_FLAGS_NONE, + CALL_TIMEOUT, + NULL, + dispatcher_done_cb, + call_id); + return; + } + if (!ret) { NMLogLevel log_level = LOGL_DEBUG; @@ -459,7 +498,8 @@ dispatcher_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) now_msec, call_id->log_ifname, call_id->log_con_uuid, - ret); + ret, + call_id->is_action2); } g_hash_table_remove(gl.requests, call_id); @@ -494,75 +534,29 @@ action_to_string(NMDispatcherAction action) return action_table[(gsize) action]; } -static gboolean -_dispatcher_call(NMDispatcherAction action, - gboolean blocking, - NMDevice *device, - NMSettingsConnection *settings_connection, - NMConnection *applied_connection, - gboolean activation_type_external, - NMConnectivityState connectivity_state, - const char *vpn_iface, - const NML3ConfigData *l3cd, - NMDispatcherFunc callback, - gpointer user_data, - NMDispatcherCallId **out_call_id) +static GVariant * +build_call_parameters(NMDispatcherAction action, + NMDevice *device, + NMSettingsConnection *settings_connection, + NMConnection *applied_connection, + gboolean activation_type_external, + NMConnectivityState connectivity_state, + const char *vpn_iface, + const NML3ConfigData *l3cd, + gboolean is_action2) { + const char *connectivity_state_string = "UNKNOWN"; GVariant *connection_dict; GVariantBuilder connection_props; GVariantBuilder device_props; GVariantBuilder device_proxy_props; GVariantBuilder device_ip4_props; GVariantBuilder device_ip6_props; - gs_unref_variant GVariant *parameters_floating = NULL; - gs_unref_variant GVariant *device_dhcp4_props = NULL; - gs_unref_variant GVariant *device_dhcp6_props = 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; - NMDispatcherCallId *call_id; - guint request_id; - const char *connectivity_state_string = "UNKNOWN"; - const char *log_ifname; - const char *log_con_uuid; - gint64 start_at_msec; - gint64 now_msec; - - g_return_val_if_fail(!blocking || (!callback && !user_data), FALSE); - - NM_SET_OUT(out_call_id, NULL); - - _init_dispatcher(); - - if (!gl.dbus_connection) - return FALSE; - - log_ifname = device ? nm_device_get_iface(device) : NULL; - log_con_uuid = - settings_connection ? nm_settings_connection_get_uuid(settings_connection) : NULL; - - request_id = ++gl.request_id_counter; - if (G_UNLIKELY(!request_id)) - request_id = ++gl.request_id_counter; - - if (!action_need_device(action)) { - _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); - - _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) connection_dict = @@ -621,25 +615,114 @@ _dispatcher_call(NMDispatcherAction action, 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 ?: nm_g_variant_singleton_aLsvI(), - device_dhcp6_props ?: nm_g_variant_singleton_aLsvI(), - connectivity_state_string, - vpn_iface ?: "", - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - nm_logging_enabled(LOGL_DEBUG, LOGD_DISPATCH)); + if (is_action2) { + return 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@a{sv})", + action_to_string(action), + connection_dict, + &connection_props, + &device_props, + &device_proxy_props, + &device_ip4_props, + &device_ip6_props, + device_dhcp4_props ?: nm_g_variant_singleton_aLsvI(), + device_dhcp6_props ?: nm_g_variant_singleton_aLsvI(), + connectivity_state_string, + vpn_iface ?: "", + &vpn_proxy_props, + &vpn_ip4_props, + &vpn_ip6_props, + nm_logging_enabled(LOGL_DEBUG, LOGD_DISPATCH), + nm_g_variant_singleton_aLsvI()); + } - start_at_msec = nm_utils_get_monotonic_timestamp_msec(); + return 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 ?: nm_g_variant_singleton_aLsvI(), + device_dhcp6_props ?: nm_g_variant_singleton_aLsvI(), + connectivity_state_string, + vpn_iface ?: "", + &vpn_proxy_props, + &vpn_ip4_props, + &vpn_ip6_props, + nm_logging_enabled(LOGL_DEBUG, LOGD_DISPATCH)); +} + +static gboolean +_dispatcher_call(NMDispatcherAction action, + gboolean blocking, + NMDevice *device, + NMSettingsConnection *settings_connection, + NMConnection *applied_connection, + gboolean activation_type_external, + NMConnectivityState connectivity_state, + const char *vpn_iface, + const NML3ConfigData *l3cd, + NMDispatcherFunc callback, + gpointer user_data, + NMDispatcherCallId **out_call_id) +{ + NMDispatcherCallId *call_id; + guint request_id; + const char *log_ifname; + const char *log_con_uuid; + gint64 start_at_msec; + gint64 now_msec; + gs_unref_variant GVariant *parameters_floating = NULL; + gboolean is_action2 = TRUE; + + g_return_val_if_fail(!blocking || (!callback && !user_data), FALSE); + + NM_SET_OUT(out_call_id, NULL); + + _init_dispatcher(); + + if (!gl.dbus_connection) + return FALSE; + + log_ifname = device ? nm_device_get_iface(device) : NULL; + log_con_uuid = + settings_connection ? nm_settings_connection_get_uuid(settings_connection) : NULL; + + request_id = ++gl.request_id_counter; + if (G_UNLIKELY(!request_id)) + request_id = ++gl.request_id_counter; + + if (!action_need_device(action)) { + _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); + + _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)" : "")); + } + + parameters_floating = build_call_parameters(action, + device, + settings_connection, + applied_connection, + activation_type_external, + connectivity_state, + vpn_iface, + l3cd, + TRUE); + start_at_msec = nm_utils_get_monotonic_timestamp_msec(); /* Send the action to the dispatcher */ if (blocking) { @@ -650,14 +733,44 @@ _dispatcher_call(NMDispatcherAction action, NM_DISPATCHER_DBUS_SERVICE, NM_DISPATCHER_DBUS_PATH, NM_DISPATCHER_DBUS_INTERFACE, - "Action", + "Action2", g_steal_pointer(¶meters_floating), - G_VARIANT_TYPE("(a(sus))"), + G_VARIANT_TYPE("(a(susa{sv}))"), G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, NULL, &error); + if (!ret && g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + _LOG2D( + request_id, + log_ifname, + log_con_uuid, + "dispatcher service does not implement Action2() method, falling back to Action()"); + g_clear_error(&error); + parameters_floating = build_call_parameters(action, + device, + settings_connection, + applied_connection, + activation_type_external, + connectivity_state, + vpn_iface, + l3cd, + FALSE); + 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); + is_action2 = FALSE; + } + now_msec = nm_utils_get_monotonic_timestamp_msec(); if (!ret) { @@ -676,7 +789,8 @@ _dispatcher_call(NMDispatcherAction action, now_msec, log_ifname, log_con_uuid, - ret); + ret, + is_action2); return TRUE; } @@ -688,13 +802,25 @@ _dispatcher_call(NMDispatcherAction action, log_ifname, log_con_uuid); + /* Since we don't want to cache all the input parameters, already build + * and cache the argument for the Action() method in case Action2() fails. */ + call_id->action_params = build_call_parameters(action, + device, + settings_connection, + applied_connection, + activation_type_external, + connectivity_state, + vpn_iface, + l3cd, + FALSE); + g_dbus_connection_call(gl.dbus_connection, NM_DISPATCHER_DBUS_SERVICE, NM_DISPATCHER_DBUS_PATH, NM_DISPATCHER_DBUS_INTERFACE, - "Action", + "Action2", g_steal_pointer(¶meters_floating), - G_VARIANT_TYPE("(a(sus))"), + G_VARIANT_TYPE("(a(susa{sv}))"), G_DBUS_CALL_FLAGS_NONE, CALL_TIMEOUT, NULL,