diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 640157db32..c1df7fee49 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -282,11 +282,12 @@ static void usage (void) { g_printerr (_("Usage: nmcli device { COMMAND | help }\n\n" - "COMMAND := { status | show | connect | disconnect | delete | monitor | wifi | lldp }\n\n" + "COMMAND := { status | show | connect | reapply | disconnect | delete | monitor | wifi | lldp }\n\n" " status\n\n" " show []\n\n" " set [ifname] [autoconnect yes|no] [managed yes|no]\n\n" " connect \n\n" + " reapply ...\n\n" " disconnect ...\n\n" " delete ...\n\n" " monitor ...\n\n" @@ -337,6 +338,17 @@ usage_device_connect (void) "It will also consider connections that are not set to auto-connect.\n\n")); } +static void +usage_device_reapply (void) +{ + g_printerr (_("Usage: nmcli device reapply { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := ...\n" + "\n" + "Attempts to update device with changes to the currently active connection\n" + "made since it was last applied.\n\n")); +} + static void usage_device_disconnect (void) { @@ -1813,6 +1825,116 @@ device_cb_info_finish (DeviceCbInfo *info, NMDevice *device) quit (); } +static void +reapply_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + DeviceCbInfo *info = (DeviceCbInfo *) user_data; + NmCli *nmc = info->nmc; + GError *error = NULL; + + if (!nm_device_reapply_finish (device, result, &error)) { + g_string_printf (nmc->return_text, _("Error: not all connections reapplied.")); + g_printerr (_("Error: Reapplying connection to device '%s' (%s) failed: %s\n"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + g_error_free (error); + nmc->return_value = NMC_RESULT_ERROR_DEV_DISCONNECT; + device_cb_info_finish (info, device); + } else { + if (nmc->print_output == NMC_PRINT_PRETTY) + nmc_terminal_erase_line (); + g_print (_("Connection successfully reapplied to device '%s'.\n"), + nm_device_get_iface (device)); + device_cb_info_finish (info, device); + } +} + +static NMCResultCode +do_device_reapply (NmCli *nmc, int argc, char **argv) +{ + NMDevice **devices; + NMDevice *device; + DeviceCbInfo *info = NULL; + GSList *queue = NULL, *iter; + char **arg_arr = NULL; + char **arg_ptr = argv; + int arg_num = argc; + int i; + + /* Set default timeout for reapply operation. */ + if (nmc->timeout == -1) + nmc->timeout = 10; + + if (argc == 0) { + if (nmc->ask) { + char *line = nmc_readline (PROMPT_INTERFACES); + nmc_string_to_arg_array (line, NULL, FALSE, &arg_arr, &arg_num); + g_free (line); + arg_ptr = arg_arr; + } + if (arg_num == 0) { + g_string_printf (nmc->return_text, _("Error: No interface specified.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto error; + } + } + + devices = get_devices_sorted (nmc->client); + while (arg_num > 0) { + device = NULL; + for (i = 0; devices[i]; i++) { + if (!g_strcmp0 (nm_device_get_iface (devices[i]), *arg_ptr)) { + device = devices[i]; + break; + } + } + + if (device) { + if (!g_slist_find (queue, device)) + queue = g_slist_prepend (queue, device); + else + g_printerr (_("Warning: argument '%s' is duplicated.\n"), *arg_ptr); + } else { + g_printerr (_("Error: Device '%s' not found.\n"), *arg_ptr); + g_string_printf (nmc->return_text, _("Error: not all devices found.")); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + } + + /* Take next argument */ + next_arg (&arg_num, &arg_ptr); + } + g_free (devices); + + if (!queue) { + g_string_printf (nmc->return_text, _("Error: no valid device provided.")); + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + goto error; + } + queue = g_slist_reverse (queue); + + info = g_slice_new0 (DeviceCbInfo); + info->nmc = nmc; + + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait = TRUE; + + for (iter = queue; iter; iter = g_slist_next (iter)) { + device = iter->data; + + info->queue = g_slist_prepend (info->queue, g_object_ref (device)); + + /* Now reapply the connection to the device */ + nm_device_reapply_async (device, NULL, 0, NULL, reapply_device_cb, info); + } + +error: + g_strfreev (arg_arr); + g_slist_free (queue); + return nmc->return_value; +} + static void disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -3593,6 +3715,13 @@ do_devices (NmCli *nmc, int argc, char **argv) } nmc->return_value = do_device_connect (nmc, argc-1, argv+1); } + else if (matches (*argv, "reapply") == 0) { + if (nmc_arg_is_help (*(argv+1))) { + usage_device_reapply (); + goto usage_exit; + } + nmc->return_value = do_device_reapply (nmc, argc-1, argv+1); + } else if (matches (*argv, "disconnect") == 0) { if (nmc_arg_is_help (*(argv+1))) { usage_device_disconnect (); diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index a336dd5274..f8c0298e4a 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -1419,7 +1419,7 @@ _nmcli() ;; d|de|dev|devi|devic|device) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status show connect disconnect delete monitor wifi set lldp + _nmcli_compl_COMMAND "$command" status show connect reapply disconnect delete monitor wifi set lldp elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|st|sta|stat|statu|status) @@ -1433,6 +1433,7 @@ _nmcli() _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" fi ;; + r|re|rea|reap|reapp|reappl|reapply| \ d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \ de|del|dele|delet|delete| \ m|mo|mon|moni|monit|monito|monitor) diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml index cf04ac82f8..2930cf067e 100644 --- a/introspection/nm-device.xml +++ b/introspection/nm-device.xml @@ -169,6 +169,27 @@ + + + + The effective connection settings and properties to use. If empty, the connection + settings from the connection that is active on the device will be used. + + + + + Flags which would modify the behavior of the Reapply call. + There are no flags defined currently and the users should use the value of 0. + + + + Attempts to update the configuration of a device without deactivating it. + You can either modify the configuration by passing the desired setup via "connection" + argument or just omit the argument to bring it in sync with the connection that + has been activated but could have been modified since. + + + Disconnects a device and prevents the device from automatically activating further connections without user intervention. diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 4636e9acec..8b78822829 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1037,4 +1037,7 @@ global: nm_vpn_service_plugin_set_ip4_config; nm_vpn_service_plugin_set_ip6_config; nm_vpn_service_plugin_set_login_banner; + nm_device_reapply; + nm_device_reapply_async; + nm_device_reapply_finish; } libnm_1_0_0; diff --git a/libnm/nm-device.c b/libnm/nm-device.c index 8efd0e68da..1e14658d28 100644 --- a/libnm/nm-device.c +++ b/libnm/nm-device.c @@ -2164,6 +2164,128 @@ nm_device_is_software (NMDevice *device) return !!(NM_DEVICE_GET_PRIVATE (device)->capabilities & NM_DEVICE_CAP_IS_SOFTWARE); } +/** + * nm_device_reapply: + * @device: a #NMDevice + * @connection: the #NMConnection to replace the applied settings with or %NULL to reuse existing + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @error: location for a #GError, or %NULL + * + * Attempts to update device with changes to the currently active connection + * made since it was last applied. + * + * Returns: %TRUE on success, %FALSE on error, in which case @error will be set. + **/ +gboolean +nm_device_reapply (NMDevice *device, + NMConnection *connection, + guint flags, + GCancellable *cancellable, + GError **error) +{ + GVariant *dict = NULL; + gboolean ret; + + g_return_val_if_fail (NM_IS_DEVICE (device), FALSE); + + if (connection) + dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); + if (!dict) + dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0); + + + ret = nmdbus_device_call_reapply_sync (NM_DEVICE_GET_PRIVATE (device)->proxy, + dict, flags, cancellable, error); + if (error && *error) + g_dbus_error_strip_remote_error (*error); + return ret; +} + +static void +device_reapply_cb (GObject *proxy, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + GError *error = NULL; + + if (nmdbus_device_call_reapply_finish (NMDBUS_DEVICE (proxy), result, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else { + g_dbus_error_strip_remote_error (error); + g_simple_async_result_take_error (simple, error); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * nm_device_reapply_async: + * @device: a #NMDevice + * @connection: the #NMConnection to replace the applied settings with or %NULL to reuse existing + * @flags: always set this to zero + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to be called when the reapply operation completes + * @user_data: caller-specific data passed to @callback + * + * Asynchronously begins an attempt to update device with changes to the + * currently active connection made since it was last applied. + **/ +void +nm_device_reapply_async (NMDevice *device, + NMConnection *connection, + guint flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GVariant *dict = NULL; + GSimpleAsyncResult *simple; + + g_return_if_fail (NM_IS_DEVICE (device)); + + if (connection) + dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); + if (!dict) + dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0); + + simple = g_simple_async_result_new (G_OBJECT (device), callback, user_data, + nm_device_reapply_async); + + nmdbus_device_call_reapply (NM_DEVICE_GET_PRIVATE (device)->proxy, + dict, flags, cancellable, + device_reapply_cb, simple); +} + +/** + * nm_device_reapply_finish: + * @device: a #NMDevice + * @result: the result passed to the #GAsyncReadyCallback + * @error: location for a #GError, or %NULL + * + * Gets the result of a call to nm_device_reapply_async(). + * + * Returns: %TRUE on success, %FALSE on error, in which case @error + * will be set. + **/ +gboolean +nm_device_reapply_finish (NMDevice *device, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (device), nm_device_reapply_async), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return g_simple_async_result_get_op_res_gboolean (simple); +} + /** * nm_device_disconnect: * @device: a #NMDevice diff --git a/libnm/nm-device.h b/libnm/nm-device.h index 45ee3b2100..c9d619fc6c 100644 --- a/libnm/nm-device.h +++ b/libnm/nm-device.h @@ -138,6 +138,21 @@ GPtrArray * nm_device_get_lldp_neighbors (NMDevice *device); char ** nm_device_disambiguate_names (NMDevice **devices, int num_devices); +gboolean nm_device_reapply (NMDevice *device, + NMConnection *connection, + guint flags, + GCancellable *cancellable, + GError **error); +void nm_device_reapply_async (NMDevice *device, + NMConnection *connection, + guint flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nm_device_reapply_finish (NMDevice *device, + GAsyncResult *result, + GError **error); + gboolean nm_device_disconnect (NMDevice *device, GCancellable *cancellable, GError **error); diff --git a/man/nmcli.1.in b/man/nmcli.1.in index c3bb7afdfa..fbd53c4945 100644 --- a/man/nmcli.1.in +++ b/man/nmcli.1.in @@ -892,7 +892,7 @@ provided, the VPN configuration data will be printed to standard output. .B device - show and manage network interfaces .br .TP -.SS \fICOMMAND\fP := { status | show | set | connect | disconnect | delete | monitor | wifi | lldp } +.SS \fICOMMAND\fP := { status | show | set | connect | reapply | disconnect | delete | monitor | wifi | lldp } .sp .RS .TP @@ -920,6 +920,11 @@ will be activated. It will also consider connections that are not set to auto co .br If '--wait' option is not specified, the default timeout will be 90 seconds. .TP +.B reapply +.br +Attempt to update device with changes to the currently active connection +made since it was last applied. +.TP .B disconnect ... .br Disconnect a device and prevent the device from automatically activating further diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index a4b611b5f9..0e5993a79c 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6528,7 +6528,7 @@ activate_stage5_ip4_config_commit (NMDevice *self) } static void -nm_device_queued_ip_config_change_clear (NMDevice *self) +queued_ip4_config_change_clear (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -6537,6 +6537,13 @@ nm_device_queued_ip_config_change_clear (NMDevice *self) g_source_remove (priv->queued_ip4_config_id); priv->queued_ip4_config_id = 0; } +} + +static void +queued_ip6_config_change_clear (NMDevice *self) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + if (priv->queued_ip6_config_id) { _LOGD (LOGD_DEVICE, "clearing queued IP6 config change"); g_source_remove (priv->queued_ip6_config_id); @@ -6556,7 +6563,7 @@ nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *conf if (config) priv->dev_ip4_config = g_object_ref (config); - nm_device_queued_ip_config_change_clear (self); + queued_ip4_config_change_clear (self); activation_source_schedule (self, activate_stage5_ip4_config_commit, AF_INET); } @@ -6795,6 +6802,301 @@ delete_on_deactivate_check_and_schedule (NMDevice *self, int ifindex) ifindex, data->idle_add_id); } +static void +_cleanup_ip4_pre (NMDevice *self, CleanupType cleanup_type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->ip4_state = IP_NONE; + queued_ip4_config_change_clear (self); + + dhcp4_cleanup (self, cleanup_type, FALSE); + arp_cleanup (self); + dnsmasq_cleanup (self); + ipv4ll_cleanup (self); +} + +static void +_cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + + priv->ip6_state = IP_NONE; + queued_ip6_config_change_clear (self); + + dhcp6_cleanup (self, cleanup_type, FALSE); + linklocal6_cleanup (self); + addrconf6_cleanup (self); +} + +G_GNUC_NULL_TERMINATED +static gboolean +_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, GError **error, ...) +{ + va_list ap; + const char *key; + guint found_keys = 0; + +#if NM_MORE_ASSERTS > 10 + /* Assert that the keys are unique. */ + { + gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (!g_hash_table_add (check_dups, (char *) key)) + nm_assert (FALSE); + } + va_end (ap); + nm_assert (g_hash_table_size (check_dups) > 0); + } +#endif + + if (!hash || g_hash_table_size (hash) == 0) + return TRUE; + + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (g_hash_table_contains (hash, key)) + found_keys++; + } + va_end (ap); + + if (found_keys != g_hash_table_size (hash)) { + GHashTableIter iter; + const char *k = NULL; + const char *first_invalid_key = NULL; + + if (!error) + return FALSE; + + g_hash_table_iter_init (&iter, hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) { + va_start (ap, error); + while ((key = va_arg (ap, const char *))) { + if (!strcmp (key, k)) { + first_invalid_key = k; + break; + } + } + va_end (ap); + if (first_invalid_key) + break; + } + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION, + "Can't reapply changes to '%s%s%s' setting", + setting_name ? : "", + setting_name ? "." : "", + first_invalid_key ? : ""); + g_return_val_if_fail (first_invalid_key, FALSE); + return FALSE; + } + + return TRUE; +} + +/* reapply_connection: + * @connection: the new connection settings to be applied or %NULL to reapply + * the current settings connection + * @error: the error if %FALSE is returned + * + * Change configuration of an already configured device if possible. + * Updates the device's applied connection upon success. + * + * Return: %FALSE if the new configuration can not be reapplied. + */ +static gboolean +reapply_connection (NMDevice *self, + NMConnection *connection, + GError **error) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMConnection *applied = nm_device_get_applied_connection (self); + gs_unref_object NMConnection *applied_clone = NULL; + gs_unref_hashtable GHashTable *diffs = NULL; + NMConnection *con_old, *con_new; + NMSettingIPConfig *s_ip4_old, *s_ip4_new; + NMSettingIPConfig *s_ip6_old, *s_ip6_new; + + if (priv->state != NM_DEVICE_STATE_ACTIVATED) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Device is not activated"); + return FALSE; + } + + nm_connection_diff (connection, + applied, + NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP | + NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS, + &diffs); + + /************************************************************************** + * check for unsupported changes and reject to reapply + *************************************************************************/ + if (!_hash_check_invalid_keys (diffs, NULL, error, + NM_SETTING_IP4_CONFIG_SETTING_NAME, + NM_SETTING_IP6_CONFIG_SETTING_NAME, + NM_SETTING_CONNECTION_SETTING_NAME, + NULL)) + return FALSE; + + if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL, + NM_SETTING_CONNECTION_SETTING_NAME, + error, + NM_SETTING_CONNECTION_ZONE, + NM_SETTING_CONNECTION_METERED, + NULL)) + return FALSE; + + _LOGD (LOGD_DEVICE, "reapply"); + + /************************************************************************** + * Update applied connection + *************************************************************************/ + + if (diffs) { + con_old = applied_clone = nm_simple_connection_new_clone (applied); + con_new = applied; + nm_connection_replace_settings_from_connection (applied, connection); + } else + con_old = con_new = applied; + + s_ip4_new = nm_connection_get_setting_ip4_config (con_new); + s_ip4_old = nm_connection_get_setting_ip4_config (con_old); + s_ip6_new = nm_connection_get_setting_ip6_config (con_new); + s_ip6_old = nm_connection_get_setting_ip6_config (con_old); + + /************************************************************************** + * Reapply changes + *************************************************************************/ + + nm_device_update_firewall_zone (self); + nm_device_update_metered (self); + + if (priv->ip4_state != IP_NONE) { + g_clear_object (&priv->con_ip4_config); + priv->con_ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self)); + nm_ip4_config_merge_setting (priv->con_ip4_config, + s_ip4_new, + nm_device_get_ip4_route_metric (self)); + + if (strcmp (nm_setting_ip_config_get_method (s_ip4_new), + nm_setting_ip_config_get_method (s_ip4_old))) { + _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); + priv->ip4_state = IP_WAIT; + if (!nm_device_activate_stage3_ip4_start (self)) + _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); + } else + ip4_config_merge_and_apply (self, NULL, TRUE, NULL); + } + + if (priv->ip6_state != IP_NONE) { + g_clear_object (&priv->con_ip6_config); + priv->con_ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self)); + nm_ip6_config_merge_setting (priv->con_ip6_config, + s_ip6_new, + nm_device_get_ip6_route_metric (self)); + + if (strcmp (nm_setting_ip_config_get_method (s_ip6_new), + nm_setting_ip_config_get_method (s_ip6_old))) { + _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); + priv->ip6_state = IP_WAIT; + if (!nm_device_activate_stage3_ip6_start (self)) + _LOGW (LOGD_IP6, "Failed to apply IPv6 configuration"); + } else + ip6_config_merge_and_apply (self, TRUE, NULL); + } + + return TRUE; +} + +static void +reapply_cb (NMDevice *self, + GDBusMethodInvocation *context, + NMAuthSubject *subject, + GError *error, + gpointer user_data) +{ + gs_unref_object NMConnection *connection = NM_CONNECTION (user_data); + GError *local = NULL; + + if (error) { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message); + g_dbus_method_invocation_return_gerror (context, error); + return; + } + + if (!reapply_connection (self, + connection ? : (NMConnection *) nm_device_get_settings_connection (self), + &local)) { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message); + g_dbus_method_invocation_take_error (context, local); + local = NULL; + } else { + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, subject, NULL); + g_dbus_method_invocation_return_value (context, NULL); + } +} + +static void +impl_device_reapply (NMDevice *self, + GDBusMethodInvocation *context, + GVariant *settings, + guint flags) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMSettingsConnection *settings_connection; + NMConnection *connection = NULL; + GError *error = NULL; + + /* No flags supported as of now. */ + if (flags != 0) { + error = g_error_new_literal (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Invalid flags specified"); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + + if (priv->state != NM_DEVICE_STATE_ACTIVATED) { + error = g_error_new_literal (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "Device is not activated"); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + + settings_connection = nm_device_get_settings_connection (self); + g_return_if_fail (settings_connection); + + if (settings && g_variant_n_children (settings)) { + /* New settings specified inline. */ + connection = nm_simple_connection_new_from_dbus (settings, &error); + if (!connection) { + g_prefix_error (&error, "The settings specified are invalid: "); + nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message); + g_dbus_method_invocation_take_error (context, error); + return; + } + nm_connection_clear_secrets (connection); + } + + /* Ask the manager to authenticate this request for us */ + g_signal_emit (self, signals[AUTH_REQUEST], 0, + context, + nm_device_get_applied_connection (self), + NM_AUTH_PERMISSION_NETWORK_CONTROL, + TRUE, + reapply_cb, + connection); +} + static void disconnect_cb (NMDevice *self, GDBusMethodInvocation *context, @@ -8939,23 +9241,6 @@ nm_device_has_pending_action (NMDevice *self) /***********************************************************/ -static void -_cleanup_ip_pre (NMDevice *self, CleanupType cleanup_type) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - priv->ip4_state = priv->ip6_state = IP_NONE; - nm_device_queued_ip_config_change_clear (self); - - dhcp4_cleanup (self, cleanup_type, FALSE); - arp_cleanup (self); - dhcp6_cleanup (self, cleanup_type, FALSE); - linklocal6_cleanup (self); - addrconf6_cleanup (self); - dnsmasq_cleanup (self); - ipv4ll_cleanup (self); -} - static void _cancel_activation (NMDevice *self) { @@ -8997,7 +9282,8 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type) /* Clear any queued transitions */ nm_device_queued_state_clear (self); - _cleanup_ip_pre (self, cleanup_type); + _cleanup_ip4_pre (self, cleanup_type); + _cleanup_ip6_pre (self, cleanup_type); } static void @@ -9536,7 +9822,8 @@ _set_state_full (NMDevice *self, /* Clean up any half-done IP operations if the device's layer2 * finds out it needs authentication during IP config. */ - _cleanup_ip_pre (self, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE); + _cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE); } break; default: @@ -10905,6 +11192,7 @@ nm_device_class_init (NMDeviceClass *klass) nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), NMDBUS_TYPE_DEVICE_SKELETON, + "Reapply", impl_device_reapply, "Disconnect", impl_device_disconnect, "Delete", impl_device_delete, NULL); diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c index de4e56c254..007e4321e7 100644 --- a/src/nm-audit-manager.c +++ b/src/nm-audit-manager.c @@ -165,15 +165,27 @@ nm_audit_log (NMAuditManager *self, GPtrArray *fields, const char *file, static void _audit_log_helper (NMAuditManager *self, GPtrArray *fields, const char *file, guint line, const char *func, const char *op, gboolean result, - NMAuthSubject *subject, const char *reason) + gpointer subject_context, const char *reason) { AuditField op_field = { }, pid_field = { }, uid_field = { }; AuditField result_field = { }, reason_field = { }; gulong pid, uid; + NMAuthSubject *subject = NULL; + gs_unref_object NMAuthSubject *subject_free = NULL; _audit_field_init_string (&op_field, "op", op, FALSE, BACKEND_ALL); g_ptr_array_insert (fields, 0, &op_field); + if (subject_context) { + if (NM_IS_AUTH_SUBJECT (subject_context)) + subject = subject_context; + else if (G_IS_DBUS_METHOD_INVOCATION (subject_context)) { + GDBusMethodInvocation *context = subject_context; + + subject = subject_free = nm_auth_subject_new_unix_process_from_context (context); + } else + g_warn_if_reached (); + } if (subject && nm_auth_subject_is_unix_process (subject)) { pid = nm_auth_subject_get_unix_process_pid (subject); uid = nm_auth_subject_get_unix_process_uid (subject); @@ -215,7 +227,7 @@ nm_audit_manager_audit_enabled (NMAuditManager *self) void _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, NMSettingsConnection *connection, - gboolean result, NMAuthSubject *subject, const char *reason) + gboolean result, gpointer subject_context, const char *reason) { gs_unref_ptrarray GPtrArray *fields = NULL; AuditField uuid_field = { }, name_field = { }; @@ -234,13 +246,13 @@ _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, gui g_ptr_array_add (fields, &name_field); } - _audit_log_helper (self, fields, file, line, func, op, result, subject, reason); + _audit_log_helper (self, fields, file, line, func, op, result, subject_context, reason); } void _nm_audit_manager_log_control_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, const char *arg, - gboolean result, NMAuthSubject *subject, + gboolean result, gpointer subject_context, const char *reason) { gs_unref_ptrarray GPtrArray *fields = NULL; @@ -254,13 +266,13 @@ _nm_audit_manager_log_control_op (NMAuditManager *self, const char *file, guint _audit_field_init_string (&arg_field, "arg", arg, TRUE, BACKEND_ALL); g_ptr_array_add (fields, &arg_field); - _audit_log_helper (self, fields, file, line, func, op, result, subject, reason); + _audit_log_helper (self, fields, file, line, func, op, result, subject_context, reason); } void _nm_audit_manager_log_device_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, NMDevice *device, - gboolean result, NMAuthSubject *subject, + gboolean result, gpointer subject_context, const char *reason) { gs_unref_ptrarray GPtrArray *fields = NULL; @@ -282,7 +294,7 @@ _nm_audit_manager_log_device_op (NMAuditManager *self, const char *file, guint l g_ptr_array_add (fields, &ifindex_field); } - _audit_log_helper (self, fields, file, line, func, op, result, subject, reason); + _audit_log_helper (self, fields, file, line, func, op, result, subject_context, reason); } #if HAVE_LIBAUDIT diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h index 78333c8bed..ed53be6921 100644 --- a/src/nm-audit-manager.h +++ b/src/nm-audit-manager.h @@ -61,53 +61,54 @@ typedef struct { #define NM_AUDIT_OP_DEVICE_DISCONNECT "device-disconnect" #define NM_AUDIT_OP_DEVICE_DELETE "device-delete" #define NM_AUDIT_OP_DEVICE_MANAGED "device-managed" +#define NM_AUDIT_OP_DEVICE_REAPPLY "device-reapply" GType nm_audit_manager_get_type (void); NMAuditManager *nm_audit_manager_get (void); gboolean nm_audit_manager_audit_enabled (NMAuditManager *self); -#define nm_audit_log_connection_op(op, connection, result, subject, reason) \ +#define nm_audit_log_connection_op(op, connection, result, subject_context, reason) \ G_STMT_START { \ NMAuditManager *_audit = nm_audit_manager_get (); \ \ if (nm_audit_manager_audit_enabled (_audit)) { \ _nm_audit_manager_log_connection_op (_audit, __FILE__, __LINE__, G_STRFUNC, \ - (op), (connection), (result), (subject), \ + (op), (connection), (result), (subject_context), \ (reason)); \ } \ } G_STMT_END -#define nm_audit_log_control_op(op, arg, result, subject, reason) \ +#define nm_audit_log_control_op(op, arg, result, subject_context, reason) \ G_STMT_START { \ NMAuditManager *_audit = nm_audit_manager_get (); \ \ if (nm_audit_manager_audit_enabled (_audit)) { \ _nm_audit_manager_log_control_op (_audit, __FILE__, __LINE__, G_STRFUNC, \ - (op), (arg), (result), (subject), (reason)); \ + (op), (arg), (result), (subject_context), (reason)); \ } \ } G_STMT_END -#define nm_audit_log_device_op(op, device, result, subject, reason) \ +#define nm_audit_log_device_op(op, device, result, subject_context, reason) \ G_STMT_START { \ NMAuditManager *_audit = nm_audit_manager_get (); \ \ if (nm_audit_manager_audit_enabled (_audit)) { \ _nm_audit_manager_log_device_op (_audit, __FILE__, __LINE__, G_STRFUNC, \ - (op), (device), (result), (subject), (reason)); \ + (op), (device), (result), (subject_context), (reason)); \ } \ } G_STMT_END void _nm_audit_manager_log_connection_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, NMSettingsConnection *connection, - gboolean result, NMAuthSubject *subject, const char *reason); + gboolean result, gpointer subject_context, const char *reason); void _nm_audit_manager_log_control_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, const char *arg, - gboolean result, NMAuthSubject *subject, const char *reason); + gboolean result, gpointer subject_context, const char *reason); void _nm_audit_manager_log_device_op (NMAuditManager *self, const char *file, guint line, const char *func, const char *op, NMDevice *device, - gboolean result, NMAuthSubject *subject, const char *reason); + gboolean result, gpointer subject_context, const char *reason); G_END_DECLS #endif /* __NM_AUDIT_MANAGER_H__ */