diff --git a/introspection/org.freedesktop.NetworkManager.Device.xml b/introspection/org.freedesktop.NetworkManager.Device.xml index fe30fb884c..970cb611c8 100644 --- a/introspection/org.freedesktop.NetworkManager.Device.xml +++ b/introspection/org.freedesktop.NetworkManager.Device.xml @@ -397,10 +397,12 @@ diff --git a/po/POTFILES.in b/po/POTFILES.in index b66ca45263..605683df42 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,6 +5,7 @@ src/core/NetworkManagerUtils.c src/core/devices/adsl/nm-device-adsl.c src/core/devices/bluetooth/nm-bluez-manager.c src/core/devices/bluetooth/nm-device-bt.c +src/core/devices/nm-device.c src/core/devices/nm-device-6lowpan.c src/core/devices/nm-device-bond.c src/core/devices/nm-device-bridge.c diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c index 8c444baa5e..c3cff1a77b 100644 --- a/src/core/devices/nm-device.c +++ b/src/core/devices/nm-device.c @@ -14943,15 +14943,71 @@ typedef struct { NMDeviceManagedFlags managed_flags; } SetManagedData; +/** + * set_managed: + * @self: the device + * @managed: the new managed state to set. + * @flags: flags to select different behaviors like storing to disk. + * @error: return location for a #GError, or %NULL + * + * Sets the managed state of the device. If the %NM_DEVICE_MANAGED_FLAGS_TO_DISK + * flag is set, the new state will be persisted to disk so it will be restored + * after a reboot. If the %NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK flag is set, the + * managed flag will be cleared from disk. + * + * Returns: %TRUE if the managed state was set successfully, %FALSE otherwise. + */ static gboolean set_managed(NMDevice *self, gboolean managed, NMDeviceManagedFlags flags, GError **error) { - gboolean old; + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE(self); + gboolean managed_old; + NMTernary managed_to_disk, managed_to_disk_old; - g_object_get(self, NM_DEVICE_MANAGED, &old, NULL); - g_object_set(self, NM_DEVICE_MANAGED, managed, NULL); + if (flags & NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK) + flags |= NM_DEVICE_MANAGED_FLAGS_TO_DISK; + + if (flags & NM_DEVICE_MANAGED_FLAGS_TO_DISK) { + managed_to_disk = flags & NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK ? NM_TERNARY_DEFAULT : managed; + + managed_to_disk_old = nm_config_get_device_managed(nm_manager_get_config(priv->manager), + nm_device_get_iface(self)); + + if (!nm_config_set_device_managed(nm_manager_get_config(priv->manager), + self, + managed_to_disk, + flags & NM_DEVICE_MANAGED_FLAGS_BY_MAC, + error)) + return FALSE; + + /* Update the unmanaged flags after the change on disk */ + nm_device_set_unmanaged_by_user_conf(self); + + if (!(flags & NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK) + && !!nm_device_get_unmanaged_flags(self, NM_UNMANAGED_USER_CONF) != !managed) { + /* We failed to make the new state effective on disk. Maybe the new config + * collides with other config. Try to revert and return error. Otherwise, + * we would set the runtime state correctly, but get an unexpected state + * after a reboot. */ + nm_config_set_device_managed(nm_manager_get_config(priv->manager), + self, + managed_to_disk_old, + flags & NM_DEVICE_MANAGED_FLAGS_BY_MAC, + error); + g_set_error(error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_FAILED, + _("failed to persist 'managed=%d' on disk, other configurations may be " + "overriding it"), + managed); + return FALSE; + } + } + + g_object_get(self, NM_DEVICE_MANAGED, &managed_old, NULL); + g_object_set(self, NM_DEVICE_MANAGED, !!managed, NULL); g_object_get(self, NM_DEVICE_MANAGED, &managed, NULL); - if (old != managed) + if (managed_old != managed) _notify(self, PROP_MANAGED); return TRUE; diff --git a/src/core/nm-config.c b/src/core/nm-config.c index 472d783053..2df97e599f 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -2076,6 +2076,121 @@ nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled) g_key_file_unref(keyfile); } +/*****************************************************************************/ + +/** + * nm_config_get_device_managed: + * @self: the NMConfig instance + * @ifname: the interface name + * + * Returns: the current managed state of the device in the intern keyfile. If it + * is not set or it's invalid, returns %NM_TERNARY_DEFAULT. + */ +NMTernary +nm_config_get_device_managed(NMConfig *self, const char *ifname) +{ + NMConfigPrivate *priv; + const GKeyFile *keyfile = NULL; + gs_free char *group = NULL; + + g_return_val_if_fail(NM_IS_CONFIG(self), FALSE); + g_return_val_if_fail(NM_CONFIG_GET_PRIVATE(self)->config_data, FALSE); + g_return_val_if_fail(ifname, FALSE); + + priv = NM_CONFIG_GET_PRIVATE(self); + keyfile = _nm_config_data_get_keyfile_intern(priv->config_data); + group = g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-%s", ifname); + + if (!keyfile) + return NM_TERNARY_DEFAULT; + + return (NMTernary) nm_config_keyfile_get_boolean(keyfile, + group, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + NM_TERNARY_DEFAULT); +} + +/** + * nm_config_set_device_managed: + * @self: the NMConfig instance + * @device: the NMDevice instance associated with this config change + * @ifname: the interface name + * @hwaddr: the hardware address + * @managed: the managed state to set + * @by_mac: if %TRUE, match by MAC address, otherwise by interface name. This is + * only used when @managed = TRUE. + * @error: return location for a #GError, or %NULL + * + * Sets the managed state of the device to the intern keyfile. Here we store the + * configuration received via the D-Bus API. Configurations from other config + * files are still in place and may have higher precedence. + * + * Prior to setting the new state, the existing configuration is removed. If + * @managed is set to %NM_TERNARY_DEFAULT, we only do the removal of the previous + * configuration. + */ +gboolean +nm_config_set_device_managed(NMConfig *self, + NMDevice *device, + NMTernary managed, + gboolean by_mac, + GError **error) +{ + NMConfigPrivate *priv; + g_autoptr(GKeyFile) keyfile = NULL; + gs_free char *group_by_name = NULL; + gs_free char *group_by_mac = NULL; + gs_free char *match_value = NULL; + gboolean changed = FALSE; + const char *ifname = nm_device_get_iface(device); + const char *hwaddr = nm_device_get_permanent_hw_address(device); + + g_return_val_if_fail(NM_IS_CONFIG(self), FALSE); + g_return_val_if_fail(NM_CONFIG_GET_PRIVATE(self)->config_data, FALSE); + g_return_val_if_fail(ifname && hwaddr, FALSE); + + priv = NM_CONFIG_GET_PRIVATE(self); + keyfile = nm_config_data_clone_keyfile_intern(priv->config_data); + group_by_name = g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-%s", ifname); + group_by_mac = + g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE ".by-mac.%s", hwaddr); + + /* Remove existing configs. Search them by group name [.intern.device-*] or [.intern.device.by-mac.*]. In + * the intern file, 'device' sections are only used for this purpose, so we won't remove + * any other device's config. */ + if (g_key_file_remove_group(keyfile, group_by_name, NULL) + || g_key_file_remove_group(keyfile, group_by_mac, NULL)) + changed = TRUE; + + /* If the new state is not explicitly TRUE of FALSE, we only remove the configs */ + if (managed == NM_TERNARY_DEFAULT) + goto done; + + /* Set new values */ + if (by_mac) + match_value = g_strdup_printf("mac:%s", hwaddr); + else + match_value = g_strdup_printf("interface-name:=%s", ifname); + + g_key_file_set_value(keyfile, + by_mac ? group_by_mac : group_by_name, + NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, + match_value); + g_key_file_set_value(keyfile, + by_mac ? group_by_mac : group_by_name, + NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED, + managed ? "1" : "0"); + changed = TRUE; + +done: + if (changed) + nm_config_set_values(self, keyfile, TRUE, FALSE); + + return TRUE; +} + +/*****************************************************************************/ + /** * nm_config_set_values: * @self: the NMConfig instance diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 5518184ce3..2a3b9f4759 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -142,6 +142,13 @@ gboolean nm_config_set_global_dns(NMConfig *self, NMGlobalDnsConfig *global_dns, void nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled); +NMTernary nm_config_get_device_managed(NMConfig *self, const char *ifname); +gboolean nm_config_set_device_managed(NMConfig *self, + NMDevice *device, + NMTernary managed, + gboolean by_mac, + GError **error); + /* internal defines ... */ extern guint _nm_config_match_nm_version; extern char *_nm_config_match_env; diff --git a/src/libnm-base/nm-config-base.h b/src/libnm-base/nm-config-base.h index e8d46ddc09..07e3fe3ccc 100644 --- a/src/libnm-base/nm-config-base.h +++ b/src/libnm-base/nm-config-base.h @@ -90,4 +90,7 @@ #define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN \ NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN +#define NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE \ + NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE + #endif /* __NM_CONFIG_BASE_H__ */ diff --git a/src/libnm-core-public/nm-dbus-interface.h b/src/libnm-core-public/nm-dbus-interface.h index 53efc12e92..f5842c5fc7 100644 --- a/src/libnm-core-public/nm-dbus-interface.h +++ b/src/libnm-core-public/nm-dbus-interface.h @@ -1251,13 +1251,24 @@ typedef enum /*< flags >*/ { /** * NMDeviceManagedFlags: * @NM_DEVICE_MANAGED_FLAGS_NONE: no flag set. + * @NM_DEVICE_MANAGED_FLAGS_TO_DISK: to also persist the device managed state to disk. + * @NM_DEVICE_MANAGED_FLAGS_BY_MAC: to match the device by MAC address, not by name. + * This option only makes sense together with %NM_DEVICE_MANAGED_FLAGS_TO_DISK. + * @NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK: to clear the managed flag from disk. + * If set, the other flags and the 'managed' argument are ignored. + * @NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE: to set the administrative state of the + * device to up if the managed state is TRUE, and down if the managed state is FALSE. + * If the flag is not set, the administrative state is not changed. * * Flags for the SetManaged() D-Bus call of a device and nm_device_set_managed_async(). * * Since: 1.58 */ typedef enum /*< flags >*/ { - NM_DEVICE_MANAGED_FLAGS_NONE = 0, + NM_DEVICE_MANAGED_FLAGS_NONE = 0, + NM_DEVICE_MANAGED_FLAGS_TO_DISK = 0x1, + NM_DEVICE_MANAGED_FLAGS_BY_MAC = 0x2, + NM_DEVICE_MANAGED_FLAGS_CLEAR_DISK = 0x4, } NMDeviceManagedFlags; /**