core: config: allow to store 'managed' configs to NM-intern

To support setting devices as managed or unmanaged via D-Bus API in a
permanent way, we need a way to store this configuration on disk. Before
this commit, only config files manually edited allowed it. Following
commits will make use of the new functions to store [device-*] sections
into NetworkManager-intern.conf depending on D-Bus method invocations.
This commit is contained in:
Íñigo Huguet 2026-02-24 11:30:15 +01:00
parent febb5355fc
commit 3a0c79b8e3
3 changed files with 188 additions and 0 deletions

View file

@ -2076,6 +2076,181 @@ nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled)
g_key_file_unref(keyfile);
}
/*****************************************************************************/
static gboolean
normalize_hwaddr_for_group_name(const char *hwaddr, char *out, GError **error)
{
guint8 hwaddr_bin[NM_UTILS_HWADDR_LEN_MAX];
gsize hwaddr_bin_len;
if (!_nm_utils_hwaddr_aton(hwaddr, hwaddr_bin, sizeof(hwaddr_bin), &hwaddr_bin_len)) {
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_INVALID_ARGUMENT,
"Invalid MAC address: %s",
hwaddr);
return FALSE;
}
nm_utils_bin2hexstr_full(hwaddr_bin, hwaddr_bin_len, '-', TRUE, out);
return TRUE;
}
/**
* nm_config_get_device_managed:
* @self: the NMConfig instance
* @device: the interface
* @out: (out): the managed state of the device
* @error: return location for a #GError, or %NULL
*
* Returns: TRUE if there were no errors, FALSE otherwise.
*/
gboolean
nm_config_get_device_managed(NMConfig *self, NMDevice *device, NMTernary *out, GError **error)
{
NMConfigPrivate *priv;
const GKeyFile *keyfile = NULL;
gs_free char *group_by_name = NULL;
gs_free char *group_by_mac = NULL;
const char *ifname = nm_device_get_iface(device);
const char *hwaddr = nm_device_get_permanent_hw_address(device);
char mac_group_name[NM_UTILS_HWADDR_LEN_MAX * 3 + 1];
NMTernary val_by_name, val_by_mac;
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(out, FALSE);
g_return_val_if_fail(ifname && hwaddr, FALSE);
if (!normalize_hwaddr_for_group_name(hwaddr, mac_group_name, error))
return FALSE;
priv = NM_CONFIG_GET_PRIVATE(self);
keyfile = _nm_config_data_get_keyfile_intern(priv->config_data);
group_by_name =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", ifname);
group_by_mac =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", mac_group_name);
if (!keyfile) {
*out = NM_TERNARY_DEFAULT;
return TRUE;
}
val_by_name = (NMTernary) nm_config_keyfile_get_boolean(keyfile,
group_by_name,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_TERNARY_DEFAULT);
val_by_mac = (NMTernary) nm_config_keyfile_get_boolean(keyfile,
group_by_mac,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_TERNARY_DEFAULT);
if (val_by_name != NM_TERNARY_DEFAULT && val_by_mac == NM_TERNARY_DEFAULT) {
*out = val_by_name;
return TRUE;
} else if (val_by_mac != NM_TERNARY_DEFAULT && val_by_name == NM_TERNARY_DEFAULT) {
*out = val_by_mac;
return TRUE;
} else if (val_by_name == NM_TERNARY_DEFAULT && val_by_mac == NM_TERNARY_DEFAULT) {
*out = NM_TERNARY_DEFAULT;
return TRUE;
} else if (val_by_name == val_by_mac) {
*out = val_by_name;
return TRUE;
}
g_set_error(error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
"Multiple managed states found for device: %s",
nm_device_get_iface(device));
return FALSE;
}
/**
* nm_config_set_device_managed:
* @self: the NMConfig instance
* @device: the NMDevice instance associated with this config change
* @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;
char *group;
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);
char mac_group_name[NM_UTILS_HWADDR_LEN_MAX * 3 + 1];
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);
if (!normalize_hwaddr_for_group_name(hwaddr, mac_group_name, error))
return 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 "-manage-%s", ifname);
group_by_mac =
g_strdup_printf(NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_DEVICE "-manage-%s", mac_group_name);
/* Remove existing configs. Search them by group name [.intern.device-manage-*]. In
* the intern file, 'device-manage' 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))
changed = TRUE;
if (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) {
group = group_by_mac;
match_value = g_strdup_printf("mac:%s", hwaddr);
} else {
group = group_by_name;
match_value = g_strdup_printf("interface-name:=%s", ifname);
}
g_key_file_set_value(keyfile, group, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE, match_value);
g_key_file_set_value(keyfile, group, 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

View file

@ -142,6 +142,16 @@ gboolean nm_config_set_global_dns(NMConfig *self, NMGlobalDnsConfig *global_dns,
void nm_config_set_connectivity_check_enabled(NMConfig *self, gboolean enabled);
gboolean nm_config_get_device_managed(NMConfig *self,
NMDevice *device,
NMTernary *out_managed,
GError **error);
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;

View file

@ -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__ */