From ae0940d12e4023f501703ff55f0d123be9da9d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Thu, 12 Feb 2026 15:39:47 +0100 Subject: [PATCH] nmcli: add `managed --permanent yes/no/up/down/reset` Allow to manage or unmanage a device persisting across reboots. If --permanent is not specified, only the runtime managed state is changed, preserving the previous behavior. The --permanent-only option allows to edit only the persistent value, without touching the runtime value. Also add the values up/down. Up means managed=yes and set device's administrative state UP. Down means managed=no and admin state DOWN. Add the value 'reset' too. It reverts managed runtime status to default behaviour. When used with `--permanent` flag, the persisted managed settings is cleared. Co-authored-by: Rahul Rajesh --- man/nmcli.xml | 16 +++++ src/nmcli/devices.c | 158 +++++++++++++++++++++++++++++++++----------- 2 files changed, 134 insertions(+), 40 deletions(-) diff --git a/man/nmcli.xml b/man/nmcli.xml index 5c6d6d3e78..bc569431ff 100644 --- a/man/nmcli.xml +++ b/man/nmcli.xml @@ -1436,15 +1436,31 @@ + + --permanent + --permanent-only + yes no + up + down + reset Set device properties. + + The property accepts a + option to persist the managed state to disk, and not only in runtime. With + only the permanent managed state is set, and not the + runtime managed state. The special values and + can be used to set the administrative state of the device at the same time as the runtime + managed state. The value clears the explicit managed setting, and + with or it also removes + the persisted managed setting. diff --git a/src/nmcli/devices.c b/src/nmcli/devices.c index 15c6686f9c..310baddd67 100644 --- a/src/nmcli/devices.c +++ b/src/nmcli/devices.c @@ -855,7 +855,8 @@ usage(void) "delete | monitor | wifi | lldp }\n\n" " status\n\n" " show []\n\n" - " set [ifname] [autoconnect yes|no] [managed yes|no]\n\n" + " set [ifname] [autoconnect yes|no] [managed [--permanent|--permanent-only] " + "yes|no|up|down|reset]\n\n" " connect \n\n" " reapply \n\n" " modify ([+|-]. )+\n\n" @@ -973,14 +974,15 @@ usage_device_delete(void) static void usage_device_set(void) { - nmc_printerr(_("Usage: nmcli device set { ARGUMENTS | help }\n" - "\n" - "ARGUMENTS := DEVICE { PROPERTY [ PROPERTY ... ] }\n" - "DEVICE := [ifname] \n" - "PROPERTY := { autoconnect { yes | no } |\n" - " { managed { yes | no }\n" - "\n" - "Modify device properties.\n\n")); + nmc_printerr(_( + "Usage: nmcli device set { ARGUMENTS | help }\n" + "\n" + "ARGUMENTS := DEVICE { PROPERTY [ PROPERTY ... ] }\n" + "DEVICE := [ifname] \n" + "PROPERTY := { autoconnect { yes | no } |\n" + " { managed [--permanent | --permanent-only] { yes | no | up | down | reset }\n" + "\n" + "Modify device properties.\n\n")); } static void @@ -2815,17 +2817,18 @@ do_devices_delete(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const static void do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *argv) { -#define DEV_SET_AUTOCONNECT 0 -#define DEV_SET_MANAGED 1 NMDevice *device = NULL; int i; struct { int idx; gboolean value; - } values[2] = { - [DEV_SET_AUTOCONNECT] = {-1}, - [DEV_SET_MANAGED] = {-1}, - }; + } autoconnect_data = {-1}; + struct { + int idx; + NMDeviceManaged value; + guint32 flags; + } managed_data = {-1}; + gs_free_error GError *error = NULL; next_arg(nmc, &argc, &argv, NULL); @@ -2847,49 +2850,120 @@ do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *ar i = 0; do { - gboolean flag; - if (argc == 1 && nmc->complete) nmc_complete_strings(*argv, "managed", "autoconnect"); if (matches(*argv, "managed")) { + NMDeviceManaged val; + guint32 flags = 0; + gboolean val_bool; + gboolean perm = FALSE, perm_only = FALSE; + argc--; argv++; + + if (argc == 1 && nmc->complete) { + nmc_complete_strings(*argv, + "true", + "yes", + "on", + "false", + "no", + "off", + "up", + "down", + "reset", + "--permanent", + "--permanent-only"); + } + + if (managed_data.idx != -1) { + g_string_printf(nmc->return_text, _("Error: 'managed' can only be set once.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return; + } + + while (argc > 0) { + /* --perm matches with --permanent, the most common, + * but --permanent-only requires a exact match */ + if (nm_streq0(*argv, "--permanent-only")) + perm_only = TRUE; + else if (matches(*argv, "--permanent")) + perm = TRUE; + else + break; + argc--; + argv++; + } + + if (perm_only && perm) { + g_string_printf( + nmc->return_text, + _("Error: '--permanent-only' and '--permanent' cannot be used together.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return; + } else if (perm) { + flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME | NM_DEVICE_MANAGED_FLAGS_PERMANENT; + } else if (perm_only) { + flags |= NM_DEVICE_MANAGED_FLAGS_PERMANENT; + } else { + /* If --permanent/--permanent-only are missing, set only the runtime flag, as this + * is how it used to work when these options didn't exist. */ + flags |= NM_DEVICE_MANAGED_FLAGS_RUNTIME; + } + if (!argc) { - g_string_printf(nmc->return_text, - _("Error: '%s' argument is missing."), - *(argv - 1)); + g_string_printf(nmc->return_text, _("Error: 'managed' argument is missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return; } - if (argc == 1 && nmc->complete) - nmc_complete_bool(*argv); - if (!nmc_string_to_bool(*argv, &flag, &error)) { - g_string_printf(nmc->return_text, _("Error: 'managed': %s."), error->message); + + if (matches(*argv, "up") || matches(*argv, "down")) { + flags |= NM_DEVICE_MANAGED_FLAGS_SET_ADMIN_STATE; + val = matches(*argv, "up") ? NM_DEVICE_MANAGED_YES : NM_DEVICE_MANAGED_NO; + } else if (matches(*argv, "reset")) { + val = NM_DEVICE_MANAGED_RESET; + } else if (nmc_string_to_bool(*argv, &val_bool, NULL)) { + val = val_bool ? NM_DEVICE_MANAGED_YES : NM_DEVICE_MANAGED_NO; + } else { + g_string_printf( + nmc->return_text, + _("Error: 'managed': '%s' is not valid, use 'yes/no/up/down/reset'."), + *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return; } - values[DEV_SET_MANAGED].idx = ++i; - values[DEV_SET_MANAGED].value = flag; + + managed_data.idx = i++; + managed_data.value = val; + managed_data.flags = flags; } else if (matches(*argv, "autoconnect")) { + gboolean val; + argc--; argv++; + if (!argc) { - g_string_printf(nmc->return_text, - _("Error: '%s' argument is missing."), - *(argv - 1)); + g_string_printf(nmc->return_text, _("Error: 'autoconnect' argument is missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return; } + if (autoconnect_data.idx != -1) { + g_string_printf(nmc->return_text, _("Error: 'autoconnect' can only be set once.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + return; + } + if (argc == 1 && nmc->complete) nmc_complete_bool(*argv); - if (!nmc_string_to_bool(*argv, &flag, &error)) { + + if (!nmc_string_to_bool(*argv, &val, &error)) { g_string_printf(nmc->return_text, _("Error: 'autoconnect': %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return; } - values[DEV_SET_AUTOCONNECT].idx = ++i; - values[DEV_SET_AUTOCONNECT].value = flag; + autoconnect_data.idx = i++; + autoconnect_data.value = val; } else { g_string_printf(nmc->return_text, _("Error: property '%s' is not known."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -2902,15 +2976,19 @@ do_device_set(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *ar /* when multiple properties are specified, set them in the order as they * are specified on the command line. */ - if (values[DEV_SET_AUTOCONNECT].idx >= 0 && values[DEV_SET_MANAGED].idx >= 0 - && values[DEV_SET_MANAGED].idx < values[DEV_SET_AUTOCONNECT].idx) { - nm_device_set_managed(device, values[DEV_SET_MANAGED].value); - values[DEV_SET_MANAGED].idx = -1; + for (i = 0; i < 2; i++) { + if (autoconnect_data.idx == i) { + nm_device_set_autoconnect(device, autoconnect_data.value); + } + if (managed_data.idx == i) { + nm_device_set_managed_async(device, + managed_data.value, + managed_data.flags, + NULL, + NULL, + NULL); + } } - if (values[DEV_SET_AUTOCONNECT].idx >= 0) - nm_device_set_autoconnect(device, values[DEV_SET_AUTOCONNECT].value); - if (values[DEV_SET_MANAGED].idx >= 0) - nm_device_set_managed(device, values[DEV_SET_MANAGED].value); } static void