From 86ffc0877523cb9d4f2a67c00650f1fbddd3183e Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 19:59:40 +0200 Subject: [PATCH 01/11] cli/connections: only do completion for the last argument Completing the property when we stop parsing due to error is not the right thing to do. --- clients/cli/connections.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 834a4e463..caf151fc5 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -4255,8 +4255,6 @@ read_connection_properties (NmCli *nmc, option = **argv; if (!option) { - if (nmc->complete) - complete_property_name (nmc, connection, '\0', "", NULL); g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: . argument is missing.")); return FALSE; @@ -4275,10 +4273,11 @@ read_connection_properties (NmCli *nmc, if (modifier) setting++; + if (*argc == 1 && nmc->complete) + complete_property_name (nmc, connection, modifier, setting, strv[1]); + setting_name = check_valid_name (setting, type_settings, slv_settings, &local); if (!setting_name) { - if (nmc->complete) - complete_property_name (nmc, connection, modifier, setting, strv[1]); g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: invalid or not allowed setting '%s': %s."), setting, local->message); @@ -4287,11 +4286,8 @@ read_connection_properties (NmCli *nmc, } next_arg (argc, argv); - if (!get_value (&value, argc, argv, option, error)) { - if (nmc->complete) - complete_property_name (nmc, connection, modifier, setting, strv[1]); + if (!get_value (&value, argc, argv, option, error)) return FALSE; - } if (!*argc && nmc->complete) complete_property (setting, strv[1], value ? value : ""); @@ -4318,19 +4314,19 @@ read_connection_properties (NmCli *nmc, if (!chosen) { if (modifier) option++; - if (nmc->complete) + if (*argc == 1 && nmc->complete) complete_property_name (nmc, connection, modifier, option, NULL); g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: invalid . '%s'."), option); return FALSE; } + if (*argc == 1 && nmc->complete) + complete_property_name (nmc, connection, modifier, option, NULL); + next_arg (argc, argv); - if (!get_value (&value, argc, argv, option, error)) { - if (nmc->complete) - complete_property_name (nmc, connection, modifier, option, NULL); + if (!get_value (&value, argc, argv, option, error)) return FALSE; - } if (!*argc && nmc->complete) complete_option (chosen, value ? value : ""); From 56804f4a3f218cce3edda7939455ef9d60ec5096 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 18:49:39 +0200 Subject: [PATCH 02/11] cli/trivial: rename some functions for consistency Some functions that take device lists use plural form in name. --- clients/cli/devices.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index ffe7072e7..953b007fc 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -1446,7 +1446,7 @@ error: } static NMCResultCode -do_devices_show (NmCli *nmc, int argc, char **argv) +do_device_show (NmCli *nmc, int argc, char **argv) { NMDevice **devices = NULL; NMDevice *device = NULL; @@ -2030,7 +2030,7 @@ disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) } static NMCResultCode -do_device_disconnect (NmCli *nmc, int argc, char **argv) +do_devices_disconnect (NmCli *nmc, int argc, char **argv) { NMDevice *device; DeviceCbInfo *info = NULL; @@ -2097,7 +2097,7 @@ delete_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) } static NMCResultCode -do_device_delete (NmCli *nmc, int argc, char **argv) +do_devices_delete (NmCli *nmc, int argc, char **argv) { NMDevice *device; DeviceCbInfo *info = NULL; @@ -2315,7 +2315,7 @@ device_removed (NMClient *client, NMDevice *device, NmCli *nmc) } static NMCResultCode -do_device_monitor (NmCli *nmc, int argc, char **argv) +do_devices_monitor (NmCli *nmc, int argc, char **argv) { if (argc == 0) { /* No devices specified. Monitor all. */ @@ -3758,7 +3758,7 @@ do_devices (NmCli *nmc, int argc, char **argv) } if (!nmc->mode_specified) nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ - nmc->return_value = do_devices_show (nmc, argc-1, argv+1); + nmc->return_value = do_device_show (nmc, argc-1, argv+1); } else if (matches (*argv, "connect") == 0) { if (nmc_arg_is_help (*(argv+1))) { @@ -3779,14 +3779,14 @@ do_devices (NmCli *nmc, int argc, char **argv) usage_device_disconnect (); goto usage_exit; } - nmc->return_value = do_device_disconnect (nmc, argc-1, argv+1); + nmc->return_value = do_devices_disconnect (nmc, argc-1, argv+1); } else if (matches (*argv, "delete") == 0) { if (nmc_arg_is_help (*(argv+1))) { usage_device_delete (); goto usage_exit; } - nmc->return_value = do_device_delete (nmc, argc-1, argv+1); + nmc->return_value = do_devices_delete (nmc, argc-1, argv+1); } else if (matches (*argv, "set") == 0) { if (nmc_arg_is_help (*(argv+1))) { @@ -3800,7 +3800,7 @@ do_devices (NmCli *nmc, int argc, char **argv) usage_device_monitor (); goto usage_exit; } - nmc->return_value = do_device_monitor (nmc, argc-1, argv+1); + nmc->return_value = do_devices_monitor (nmc, argc-1, argv+1); } else if (matches (*argv, "wifi") == 0) { if (nmc_arg_is_help (*(argv+1))) { @@ -3842,5 +3842,5 @@ opt_error: void monitor_devices (NmCli *nmc) { - do_device_monitor (nmc, 0, NULL); + do_devices_monitor (nmc, 0, NULL); } From ef8d696252bd36a9d495cad1e2527a311aa2bdf5 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 17:45:59 +0200 Subject: [PATCH 03/11] cli: move the final completion check after the main loop exit For "nmcli d modify" we'll need to do the completion from async handlers. This seems to be the most reasonable place to ignore the errors. --- clients/cli/connections.c | 6 ------ clients/cli/nmcli.c | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index caf151fc5..bbe8b78b3 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -4758,9 +4758,6 @@ finish: if (connection) g_object_unref (connection); - /* shell completion - be sure to exit with success without printing errors */ - if (nmc->complete) - nmc->return_value = NMC_RESULT_SUCCESS; return nmc->return_value; } @@ -7818,9 +7815,6 @@ do_connection_modify (NmCli *nmc, nmc->should_wait++; finish: - /* shell completion - be sure to exit with success without printing errors */ - if (nmc->complete) - nmc->return_value = NMC_RESULT_SUCCESS; return nmc->return_value; } diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 84bb4a23d..a203e407c 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -640,6 +640,9 @@ main (int argc, char *argv[]) loop = g_main_loop_new (NULL, FALSE); /* create main loop */ g_main_loop_run (loop); /* run main loop */ + if (nm_cli.complete) + nm_cli.return_value = NMC_RESULT_SUCCESS; + /* Print result descripting text */ if (nm_cli.return_value != NMC_RESULT_SUCCESS) { g_printerr ("%s\n", nm_cli.return_text->str); From a30224f6af4e9aaaf34c4bc77552f7f3566e413f Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 22 Jun 2016 17:44:39 +0200 Subject: [PATCH 04/11] cli: use should_wait consistently It's a semaphore, not a boolean. --- clients/cli/connections.c | 4 ++-- clients/cli/devices.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index bbe8b78b3..12c2294d1 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -7958,7 +7958,7 @@ do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) clone_connection_cb, info); - nmc->should_wait = TRUE; + nmc->should_wait++; finish: if (new_connection) g_object_unref (new_connection); @@ -8360,7 +8360,7 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) add_connection_cb, info); - nmc->should_wait = TRUE; + nmc->should_wait++; finish: if (connection) g_object_unref (connection); diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 953b007fc..dc8efc80b 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -1984,7 +1984,7 @@ do_device_reapply (NmCli *nmc, int argc, char **argv) } nmc->nowait_flag = (nmc->timeout == 0); - nmc->should_wait = TRUE; + nmc->should_wait++; info = g_slice_new0 (DeviceCbInfo); info->nmc = nmc; @@ -3325,7 +3325,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) /* Activate the connection now */ nmc->nowait_flag = (nmc->timeout == 0); - nmc->should_wait = TRUE; + nmc->should_wait++; info = g_malloc0 (sizeof (AddAndActivateInfo)); info->nmc = nmc; From 1a88eac02a513ed85f11a3c046d2ca0d7663e507 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 12:18:52 +0200 Subject: [PATCH 05/11] cli: split out do_cmd() --- clients/cli/common.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ clients/cli/common.h | 8 +++++++ clients/cli/nmcli.c | 38 +++++++++---------------------- 3 files changed, 71 insertions(+), 28 deletions(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index 0888f0630..0267d6f67 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -1389,3 +1389,56 @@ nmc_parse_lldp_capabilities (guint value) return g_string_free (str, FALSE); } + +/** + * nmc_do_cmd: + * @nmc: Client instance + * @cmds: Command table + * @cmd: Command + * @argc: Argument count + * @argv: Arguments vector + * + * Picks the right callback to handle command from the command table. + * If --help argument follows and the usage callback is specified for the command + * it calls the usage callback. + * + * The command table is terminated with a %NULL command. The terminating + * entry's handlers are called if the command is empty. + * + * Returns: a nmcli return code + */ +NMCResultCode +nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char **argv) +{ + const NMCCommand *c; + + for (c = cmds; c->cmd; ++c) { + if (cmd && matches (cmd, c->cmd) == 0) + break; + } + + if (c->cmd) { + /* A valid command was specified. */ + if (c->usage && nmc_arg_is_help (*(argv+1))) + c->usage (); + else + nmc->return_value = c->func (nmc, argc-1, argv+1); + } else if (cmd) { + /* Not a known command. */ + if (nmc_arg_is_help (cmd) && c->usage) { + c->usage (); + } else { + g_string_printf (nmc->return_text, _("Error: argument '%s' not understood. Try passing --help instead."), cmd); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + } else if (c->func) { + /* No command, run the default handler. */ + nmc->return_value = c->func (nmc, argc-1, argv+1); + } else { + /* No command and no default handler. */ + g_string_printf (nmc->return_text, _("Error: missing argument. Try passing --help.")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + } + + return nmc->return_value; +} diff --git a/clients/cli/common.h b/clients/cli/common.h index 26459e4ba..79cd81a07 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -73,4 +73,12 @@ int nmc_rl_set_deftext (void); char *nmc_parse_lldp_capabilities (guint value); +typedef struct { + const char *cmd; + NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); + void (*usage) (void); +} NMCCommand; + +NMCResultCode nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *argv0, int argc, char **argv); + #endif /* NMC_COMMON_H */ diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index a203e407c..b921a27f0 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -114,36 +114,18 @@ do_help (NmCli *nmc, int argc, char **argv) return NMC_RESULT_SUCCESS; } -static const struct cmd { - const char *cmd; - NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); -} nmcli_cmds[] = { - { "general", do_general }, - { "monitor", do_monitor }, - { "networking", do_networking }, - { "radio", do_radio }, - { "connection", do_connections }, - { "device", do_devices }, - { "agent", do_agent }, - { "help", do_help }, +static const NMCCommand nmcli_cmds[] = { + { "general", do_general, NULL }, + { "monitor", do_monitor, NULL }, + { "networking", do_networking, NULL }, + { "radio", do_radio, NULL }, + { "connection", do_connections, NULL }, + { "device", do_devices, NULL }, + { "agent", do_agent, NULL }, + { "help", do_help, NULL }, { 0 } }; -static NMCResultCode -do_cmd (NmCli *nmc, const char *argv0, int argc, char **argv) -{ - const struct cmd *c; - - for (c = nmcli_cmds; c->cmd; ++c) { - if (matches (argv0, c->cmd) == 0) - return c->func (nmc, argc-1, argv+1); - } - - g_string_printf (nmc->return_text, _("Error: Object '%s' is unknown, try 'nmcli help'."), argv0); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; -} - static NMCResultCode parse_command_line (NmCli *nmc, int argc, char **argv) { @@ -301,7 +283,7 @@ parse_command_line (NmCli *nmc, int argc, char **argv) if (argc > 1) { /* Now run the requested command */ - return do_cmd (nmc, argv[1], argc-1, argv+1); + return nmc_do_cmd (nmc, nmcli_cmds, argv[1], argc-1, argv+1); } usage (base); From 5182ab3d5d40a6024fc0b2f04c13425fa2f01d0a Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 12:19:10 +0200 Subject: [PATCH 06/11] cli/device: use nmc_do_cmd() --- clients/cli/devices.c | 160 ++++++++++++------------------------------ 1 file changed, 45 insertions(+), 115 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index dc8efc80b..59e10aa94 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -1401,6 +1401,12 @@ do_devices_status (NmCli *nmc, int argc, char **argv) NmcOutputField *tmpl, *arr; size_t tmpl_len; + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; + } + while (argc > 0) { g_printerr (_("Unknown parameter: %s\n"), *argv); argc--; @@ -1421,8 +1427,7 @@ do_devices_status (NmCli *nmc, int argc, char **argv) if (error) { g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message); g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } /* Add headers */ @@ -1440,9 +1445,6 @@ do_devices_status (NmCli *nmc, int argc, char **argv) g_free (devices); return NMC_RESULT_SUCCESS; - -error: - return nmc->return_value; } static NMCResultCode @@ -1454,6 +1456,9 @@ do_device_show (NmCli *nmc, int argc, char **argv) int i; gboolean ret; + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ + if (argc == 1) ifname = *argv; else if (argc > 1) { @@ -3447,6 +3452,14 @@ error: static NMCResultCode do_device_wifi (NmCli *nmc, int argc, char **argv) { + GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; + } + if (argc == 0) nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); else if (argc > 0) { @@ -3630,6 +3643,17 @@ error: static NMCResultCode do_device_lldp (NmCli *nmc, int argc, char **argv) { + GError *error = NULL; + + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + g_error_free (error); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (!nmc->mode_specified) + nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */ + if (argc == 0) nmc->return_value = do_device_lldp_list (nmc, argc, argv); else if (matches (*argv, "list") == 0) @@ -3711,11 +3735,23 @@ nmcli_device_tab_completion (const char *text, int start, int end) return match_array; } +static const NMCCommand device_cmds[] = { + {"status", do_devices_status, usage_device_status }, + {"show", do_device_show, usage_device_show }, + {"connect", do_device_connect, usage_device_connect }, + {"reapply", do_device_reapply, usage_device_reapply }, + {"disconnect", do_devices_disconnect, usage_device_disconnect }, + {"delete", do_devices_delete, usage_device_delete }, + {"set", do_device_set, usage_device_set }, + {"monitor", do_devices_monitor, usage_device_monitor }, + {"wifi", do_device_wifi, usage_device_wifi }, + {"lldp", do_device_lldp, usage_device_lldp }, + {NULL, do_devices_status, usage }, +}; + NMCResultCode do_devices (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); @@ -3727,116 +3763,10 @@ do_devices (NmCli *nmc, int argc, char **argv) /* Check whether NetworkManager is running */ if (!nm_client_get_nm_running (nmc->client)) { g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; - return nmc->return_value; + return NMC_RESULT_ERROR_NM_NOT_RUNNING; } - if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_devices_status (nmc, 0, NULL); - } - - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage (); - goto usage_exit; - } - else if (matches (*argv, "status") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_status (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_devices_status (nmc, argc-1, argv+1); - } - else if (matches (*argv, "show") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_show (); - goto usage_exit; - } - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ - nmc->return_value = do_device_show (nmc, argc-1, argv+1); - } - else if (matches (*argv, "connect") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_connect (); - goto usage_exit; - } - 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 (); - goto usage_exit; - } - nmc->return_value = do_devices_disconnect (nmc, argc-1, argv+1); - } - else if (matches (*argv, "delete") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_delete (); - goto usage_exit; - } - nmc->return_value = do_devices_delete (nmc, argc-1, argv+1); - } - else if (matches (*argv, "set") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_set (); - goto usage_exit; - } - nmc->return_value = do_device_set (nmc, argc-1, argv+1); - } - else if (matches (*argv, "monitor") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_monitor (); - goto usage_exit; - } - nmc->return_value = do_devices_monitor (nmc, argc-1, argv+1); - } - else if (matches (*argv, "wifi") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_wifi (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_device_wifi (nmc, argc-1, argv+1); - } - else if (matches (*argv, "lldp") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_device_lldp (); - goto usage_exit; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - if (!nmc->mode_specified) - nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */ - nmc->return_value = do_device_lldp (nmc, argc-1, argv+1); - } - else { - usage (); - g_string_printf (nmc->return_text, _("Error: 'dev' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - -usage_exit: - return nmc->return_value; - -opt_error: - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - return nmc->return_value; + return nmc_do_cmd (nmc, device_cmds, *argv, argc, argv); } void From 55d349bc3486d63162c1351f53b8e29823040df8 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 22 Jun 2016 18:33:54 +0200 Subject: [PATCH 07/11] cli/trivial: rename device_list() to get_device_list() It will look nicer when we have get_device(). --- clients/cli/devices.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 59e10aa94..03d4ec9eb 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -516,7 +516,7 @@ get_devices_sorted (NMClient *client) } static GSList * -device_list (NmCli *nmc, int argc, char **argv) +get_device_list (NmCli *nmc, int argc, char **argv) { int arg_num = argc; char **arg_arr = NULL; @@ -2045,7 +2045,7 @@ do_devices_disconnect (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 10; - queue = device_list (nmc, argc, argv); + queue = get_device_list (nmc, argc, argv); if (!queue) goto error; queue = g_slist_reverse (queue); @@ -2112,7 +2112,7 @@ do_devices_delete (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 10; - queue = device_list (nmc, argc, argv); + queue = get_device_list (nmc, argc, argv); if (!queue) goto error; queue = g_slist_reverse (queue); @@ -2335,7 +2335,7 @@ do_devices_monitor (NmCli *nmc, int argc, char **argv) g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc); } else { /* Monitor just the specified devices. */ - GSList *queue = device_list (nmc, argc, argv); + GSList *queue = get_device_list (nmc, argc, argv); GSList *iter; if (!queue) From 59bc820e66ea2e2f8081b84e911310b09f15cf08 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 27 Jun 2016 11:54:25 +0200 Subject: [PATCH 08/11] cli/device: split get_device() Parsing a single device name from the command line is generally useful. Remove the open coded versions in reapply, connect & status. --- clients/cli/devices.c | 179 +++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 108 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 03d4ec9eb..37a2b882b 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -572,6 +572,43 @@ error: return queue; } +static NMDevice * +get_device (NmCli *nmc, int *argc, char ***argv, GError **error) +{ + gs_free NMDevice **devices = NULL; + gs_free char *ifname_ask = NULL; + const char *ifname = NULL; + int i; + + if (*argc == 0) { + if (nmc->ask) + ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); + + if (!ifname_ask) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("No interface specified.")); + return NULL; + } + } else { + ifname = **argv; + next_arg (argc, argv); + } + + devices = get_devices_sorted (nmc->client); + for (i = 0; devices[i]; i++) { + NMDevice *candidate = devices[i]; + const char *dev_iface = nm_device_get_iface (candidate); + + if (!g_strcmp0 (dev_iface, ifname)) + return candidate; + } + + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("Device '%s' not found."), ifname); + + return NULL; +} + static int compare_aps (gconstpointer a, gconstpointer b, gpointer user_data) { @@ -1450,54 +1487,42 @@ do_devices_status (NmCli *nmc, int argc, char **argv) static NMCResultCode do_device_show (NmCli *nmc, int argc, char **argv) { - NMDevice **devices = NULL; - NMDevice *device = NULL; - const char *ifname = NULL; - int i; - gboolean ret; + gs_free_error GError *error = NULL; if (!nmc->mode_specified) nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */ - if (argc == 1) - ifname = *argv; - else if (argc > 1) { - g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *(argv+1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } + if (argc) { + NMDevice *device; - devices = get_devices_sorted (nmc->client); - - if (ifname) { - /* Interface specified; show details only for the device */ - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) - device = candidate; - } + device = get_device (nmc, &argc, &argv, &error); if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } + + if (argc) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); + return NMC_RESULT_ERROR_USER_INPUT; + } + show_device_info (device, nmc); } else { + NMDevice **devices = get_devices_sorted (nmc->client); + int i; + /* Show details for all devices */ for (i = 0; devices[i]; i++) { nmc_empty_output_fields (nmc); - ret = show_device_info (devices[i], nmc); - if (!ret) + if (!show_device_info (devices[i], nmc)) break; if (devices[i + 1]) g_print ("\n"); /* Empty line */ } + + g_free (devices); } -error: - g_free (devices); return nmc->return_value; } @@ -1742,58 +1767,26 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data) static NMCResultCode do_device_connect (NmCli *nmc, int argc, char **argv) { - NMDevice **devices; NMDevice *device = NULL; - const char *ifname = NULL; - char *ifname_ask = NULL; - int i; AddAndActivateInfo *info; + gs_free_error GError *error = NULL; /* Set default timeout for connect operation. */ if (nmc->timeout == -1) nmc->timeout = 90; - if (argc == 0) { - if (nmc->ask) - ifname = ifname_ask = nmc_readline (PROMPT_INTERFACE); - - if (!ifname_ask) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - } else { - ifname = *argv; + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } - if (!ifname) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - - if (next_arg (&argc, &argv) == 0) { + if (*argv) { g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto error; } - devices = get_devices_sorted (nmc->client); - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) - device = candidate; - } - g_free (devices); - - if (!device) { - g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; - } - /* * Use nowait_flag instead of should_wait, because exiting has to be postponed * till connect_device_cb() is called, giving NM time to check our permissions. @@ -1826,8 +1819,6 @@ do_device_connect (NmCli *nmc, int argc, char **argv) progress_id = g_timeout_add (120, progress_cb, device); error: - g_free (ifname_ask); - return nmc->return_value; } @@ -1941,51 +1932,23 @@ reapply_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) static NMCResultCode do_device_reapply (NmCli *nmc, int argc, char **argv) { - gs_free NMDevice **devices = NULL; - NMDevice *device = NULL; + NMDevice *device; DeviceCbInfo *info = NULL; - char **arg_ptr = argv; - int arg_num = argc; - int i; - gs_free char *device_name_free = NULL; - const char *device_name = NULL; + gs_free_error GError *error = NULL; /* Set default timeout for reapply operation. */ if (nmc->timeout == -1) nmc->timeout = 10; - if (argc == 0) { - if (nmc->ask) { - device_name_free = nmc_readline (PROMPT_INTERFACE); - device_name = device_name_free; - } - if (!device_name) { - g_string_printf (nmc->return_text, _("Error: No interface specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; - } - } else if (argc == 1) { - device_name = arg_ptr[0]; - next_arg (&arg_num, &arg_ptr); - } else { - next_arg (&arg_num, &arg_ptr); - g_string_printf (nmc->return_text, _("Error: unsupported argument '%s'."), *arg_ptr); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; - } - - devices = get_devices_sorted (nmc->client); - for (i = 0; devices[i]; i++) { - if (!g_strcmp0 (nm_device_get_iface (devices[i]), device_name)) { - device = devices[i]; - break; - } - } - + device = get_device (nmc, &argc, &argv, &error); if (!device) { - g_string_printf (nmc->return_text, _("Error: device '%s' not found."), device_name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - return nmc->return_value; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; + } + + if (argc) { + g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv); + return NMC_RESULT_ERROR_USER_INPUT; } nmc->nowait_flag = (nmc->timeout == 0); From c054b871f744ff15fd15e7db180467a84535c1fd Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 23 Jun 2016 10:49:20 +0200 Subject: [PATCH 09/11] cli/device: add device name completion Useful with connect, disconnect, delete, monitor, show and reapply. --- clients/cli/devices.c | 98 ++++++++++++++++++++++++++++++++----------- clients/cli/nmcli.c | 2 +- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 37a2b882b..d4aa76e9b 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -515,6 +515,19 @@ get_devices_sorted (NMClient *client) return sorted; } +static void +complete_device (NMDevice **devices, const char *prefix) +{ + int i; + + for (i = 0; devices[i]; i++) { + const char *iface = nm_device_get_iface (devices[i]); + + if (g_str_has_prefix (iface, prefix)) + g_print ("%s\n", iface); + } +} + static GSList * get_device_list (NmCli *nmc, int argc, char **argv) { @@ -542,6 +555,9 @@ get_device_list (NmCli *nmc, int argc, char **argv) devices = get_devices_sorted (nmc->client); while (arg_num > 0) { + if (arg_num == 1 && nmc->complete) + complete_device (devices, *arg_ptr); + device = NULL; for (i = 0; devices[i]; i++) { if (!g_strcmp0 (nm_device_get_iface (devices[i]), *arg_ptr)) { @@ -556,7 +572,8 @@ get_device_list (NmCli *nmc, int argc, char **argv) else g_printerr (_("Warning: argument '%s' is duplicated.\n"), *arg_ptr); } else { - g_printerr (_("Error: Device '%s' not found.\n"), *arg_ptr); + if (!nmc->complete) + 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; } @@ -596,17 +613,19 @@ get_device (NmCli *nmc, int *argc, char ***argv, GError **error) devices = get_devices_sorted (nmc->client); for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); - - if (!g_strcmp0 (dev_iface, ifname)) - return candidate; + if (!g_strcmp0 (nm_device_get_iface (devices[i]), ifname)) + break; } - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, - _("Device '%s' not found."), ifname); + if (nmc->complete && !*argc) + complete_device (devices, ifname); - return NULL; + if (devices[i] == NULL) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("Device '%s' not found."), ifname); + } + + return devices[i]; } static int @@ -1438,6 +1457,10 @@ do_devices_status (NmCli *nmc, int argc, char **argv) NmcOutputField *tmpl, *arr; size_t tmpl_len; + /* Nothing to complete */ + if (nmc->complete) + return nmc->return_value; + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); g_error_free (error); @@ -1506,11 +1529,17 @@ do_device_show (NmCli *nmc, int argc, char **argv) return NMC_RESULT_ERROR_USER_INPUT; } + if (nmc->complete) + return nmc->return_value; + show_device_info (device, nmc); } else { NMDevice **devices = get_devices_sorted (nmc->client); int i; + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + /* Show details for all devices */ for (i = 0; devices[i]; i++) { nmc_empty_output_fields (nmc); @@ -1783,10 +1812,12 @@ do_device_connect (NmCli *nmc, int argc, char **argv) if (*argv) { g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } + if (nmc->complete) + return nmc->return_value; + /* * Use nowait_flag instead of should_wait, because exiting has to be postponed * till connect_device_cb() is called, giving NM time to check our permissions. @@ -1818,7 +1849,6 @@ do_device_connect (NmCli *nmc, int argc, char **argv) if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, device); -error: return nmc->return_value; } @@ -1951,6 +1981,9 @@ do_device_reapply (NmCli *nmc, int argc, char **argv) return NMC_RESULT_ERROR_USER_INPUT; } + if (nmc->complete) + return nmc->return_value; + nmc->nowait_flag = (nmc->timeout == 0); nmc->should_wait++; @@ -2010,7 +2043,9 @@ do_devices_disconnect (NmCli *nmc, int argc, char **argv) queue = get_device_list (nmc, argc, argv); if (!queue) - goto error; + return nmc->return_value; + if (nmc->complete) + goto out; queue = g_slist_reverse (queue); info = g_slice_new0 (DeviceCbInfo); @@ -2036,7 +2071,7 @@ do_devices_disconnect (NmCli *nmc, int argc, char **argv) nm_device_disconnect_async (device, NULL, disconnect_device_cb, info); } -error: +out: g_slist_free (queue); return nmc->return_value; } @@ -2077,7 +2112,9 @@ do_devices_delete (NmCli *nmc, int argc, char **argv) queue = get_device_list (nmc, argc, argv); if (!queue) - goto error; + return nmc->return_value; + if (nmc->complete) + goto out; queue = g_slist_reverse (queue); info = g_slice_new0 (DeviceCbInfo); @@ -2100,7 +2137,7 @@ do_devices_delete (NmCli *nmc, int argc, char **argv) nm_device_delete_async (device, NULL, delete_device_cb, info); } -error: +out: g_slist_free (queue); return nmc->return_value; } @@ -2122,6 +2159,10 @@ do_device_set (NmCli *nmc, int argc, char **argv) [DEV_SET_MANAGED] = { -1 }, }; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (argc >= 1 && g_strcmp0 (*argv, "ifname") == 0) { argc--; argv++; @@ -2285,6 +2326,12 @@ device_removed (NMClient *client, NMDevice *device, NmCli *nmc) static NMCResultCode do_devices_monitor (NmCli *nmc, int argc, char **argv) { + GSList *queue = get_device_list (nmc, argc, argv); + GSList *iter; + + if (nmc->complete) + return nmc->return_value; + if (argc == 0) { /* No devices specified. Monitor all. */ const GPtrArray *devices = nm_client_get_devices (nmc->client); @@ -2297,21 +2344,14 @@ do_devices_monitor (NmCli *nmc, int argc, char **argv) nmc->should_wait++; g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc); } else { - /* Monitor just the specified devices. */ - GSList *queue = get_device_list (nmc, argc, argv); - GSList *iter; - - if (!queue) - return nmc->return_value; - + /* Monitor the specified devices. */ for (iter = queue; iter; iter = g_slist_next (iter)) device_watch (nmc, NM_DEVICE (iter->data)); g_slist_free (queue); } g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc); - - return NMC_RESULT_SUCCESS; + return nmc->return_value; } static void @@ -3417,6 +3457,10 @@ do_device_wifi (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); g_error_free (error); @@ -3608,6 +3652,10 @@ do_device_lldp (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); g_error_free (error); diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index b921a27f0..26ef3422c 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -142,7 +142,7 @@ parse_command_line (NmCli *nmc, int argc, char **argv) * autocompletion mode (so we should just quit and don't print anything). * This would help us to ensure shell autocompletion after NM package downgrade * if we ever will enable --complete-args for other commands */ - if ((argc == 2) || !nm_streq0 (argv[2], "connection")) + if ((argc == 2) || !(nm_streq0 (argv[2], "connection") || nm_streq0 (argv[2], "device"))) return nmc->return_value; nmc->complete = TRUE; argv[1] = argv[0]; From a0bb8cd6cb3fdeaf2a1259c5127278443e0f4f1b Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 22 Jun 2016 17:35:16 +0200 Subject: [PATCH 10/11] cli/connection: export read_connection_properties() It makes sense to modify the applied connection from the device object. --- clients/cli/connections.c | 16 ++++++++-------- clients/cli/connections.h | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 12c2294d1..433d5563f 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -4225,12 +4225,12 @@ get_value (const char **value, int *argc, char ***argv, const char *option, GErr return TRUE; } -static gboolean -read_connection_properties (NmCli *nmc, - NMConnection *connection, - int *argc, - char ***argv, - GError **error) +gboolean +nmc_read_connection_properties (NmCli *nmc, + NMConnection *connection, + int *argc, + char ***argv, + GError **error) { const char *option; const char *value = NULL; @@ -4645,7 +4645,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv) read_properties: /* Get the arguments from the command line if any */ - if (argc && !read_connection_properties (nmc, connection, &argc, &argv, &error)) { + if (argc && !nmc_read_connection_properties (nmc, connection, &argc, &argv, &error)) { if (g_strcmp0 (*argv, "--") == 0 && !seen_dash_dash) { /* This is for compatibility with older nmcli that required * options and properties to be separated with "--" */ @@ -7801,7 +7801,7 @@ do_connection_modify (NmCli *nmc, next_arg (&argc, &argv); - if (!read_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, &error)) { + if (!nmc_read_connection_properties (nmc, NM_CONNECTION (rc), &argc, &argv, &error)) { g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); diff --git a/clients/cli/connections.h b/clients/cli/connections.h index c158b6348..189d968a4 100644 --- a/clients/cli/connections.h +++ b/clients/cli/connections.h @@ -26,4 +26,12 @@ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv); void monitor_connections (NmCli *nmc); +gboolean +nmc_read_connection_properties (NmCli *nmc, + NMConnection *connection, + int *argc, + char ***argv, + GError **error); + + #endif /* NMC_CONNECTIONS_H */ From 8b4494598d04b6754a69463a4dc7274fdff8763f Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Wed, 22 Jun 2016 18:25:48 +0200 Subject: [PATCH 11/11] cli/device: add modify command It modifies the applied connection using the Reapply API. --- clients/cli/devices.c | 124 +++++++++++++++++++++++++++++++++++ clients/cli/nmcli-completion | 10 ++- man/nmcli.xml | 52 +++++++++++++-- 3 files changed, 181 insertions(+), 5 deletions(-) diff --git a/clients/cli/devices.c b/clients/cli/devices.c index d4aa76e9b..774a2254d 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -30,6 +30,7 @@ #include "utils.h" #include "common.h" #include "devices.h" +#include "connections.h" /* define some prompts */ #define PROMPT_INTERFACE _("Interface: ") @@ -358,6 +359,26 @@ usage_device_reapply (void) "made since it was last applied.\n\n")); } +static void +usage_device_modify (void) +{ + g_printerr (_("Usage: nmcli connection modify { ARGUMENTS | --help }\n" + "\n" + "ARGUMENTS := ([+|-]. )+\n" + "\n" + "Modify one or more properties currently active on the device without modifying\n" + "the connection profile. The changes have immediate effect. For multi-valued\n" + "properties you can use optional '+' or '-' prefix to the property name.\n" + "The '+' sign allows appending items instead of overwriting the whole value.\n" + "The '-' sign allows removing selected items instead of the whole value.\n" + "\n" + "Examples:\n" + "nmcli dev mod em1 ipv4.method manual ipv4.addr \"192.168.1.2/24, 10.10.1.5/8\"\n" + "nmcli dev mod em1 +ipv4.dns 8.8.4.4\n" + "nmcli dev mod em1 -ipv4.dns 1\n" + "nmcli dev mod em1 -ipv6.addr \"abbe::cafe/56\"\n")); +} + static void usage_device_disconnect (void) { @@ -1997,6 +2018,108 @@ do_device_reapply (NmCli *nmc, int argc, char **argv) return nmc->return_value; } +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} ModifyInfo; + +static void +modify_reapply_cb (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + ModifyInfo *info = user_data; + NmCli *nmc = info->nmc; + GError *error = NULL; + + if (!nm_device_reapply_finish (device, result, &error)) { + g_string_printf (nmc->return_text, _("Error: Reapplying connection to device '%s' (%s) failed: %s"), + 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; + } 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)); + } + + g_slice_free (ModifyInfo, info); + quit (); +} + +static void +modify_get_applied_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NMDevice *device = NM_DEVICE (object); + ModifyInfo *info = user_data; + NmCli *nmc = info->nmc; + gs_free_error GError *error = NULL; + NMConnection *connection; + guint64 version_id; + + connection = nm_device_get_applied_connection_finish (device, + result, + &version_id, + &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: Reading applied connection from device '%s' (%s) failed: %s"), + nm_device_get_iface (device), + nm_object_get_path (NM_OBJECT (device)), + error->message); + nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; + g_slice_free (ModifyInfo, info); + quit (); + return; + } + + if (!nmc_read_connection_properties (info->nmc, connection, &info->argc, &info->argv, &error)) { + g_string_assign (nmc->return_text, error->message); + nmc->return_value = error->code; + g_slice_free (ModifyInfo, info); + quit (); + return; + } + + if (nmc->complete) + quit (); + else + nm_device_reapply_async (device, connection, version_id, 0, NULL, modify_reapply_cb, info); +} + +static NMCResultCode +do_device_modify (NmCli *nmc, int argc, char **argv) +{ + NMDevice *device = NULL; + ModifyInfo *info = NULL; + gs_free_error GError *error = NULL; + + device = get_device (nmc, &argc, &argv, &error); + if (!device) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; + } + + if (nmc->timeout == -1) + nmc->timeout = 10; + + nmc->nowait_flag = (nmc->timeout == 0); + nmc->should_wait++; + + info = g_slice_new0 (ModifyInfo); + info->nmc = nmc; + info->argc = argc; + info->argv = argv; + + nm_device_get_applied_connection_async (device, 0, NULL, modify_get_applied_cb, info); + + return nmc->return_value; +} + static void disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -3757,6 +3880,7 @@ static const NMCCommand device_cmds[] = { {"monitor", do_devices_monitor, usage_device_monitor }, {"wifi", do_device_wifi, usage_device_wifi }, {"lldp", do_device_lldp, usage_device_lldp }, + {"modify", do_device_modify, usage_device_modify }, {NULL, do_devices_status, usage }, }; diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index bb47f2d6a..e78ab85c8 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -1184,7 +1184,7 @@ _nmcli() ;; d|de|dev|devi|devic|device) if [[ ${#words[@]} -eq 2 ]]; then - _nmcli_compl_COMMAND "$command" status show connect reapply disconnect delete monitor wifi set lldp + _nmcli_compl_COMMAND "$command" status show connect reapply modify disconnect delete monitor wifi set lldp elif [[ ${#words[@]} -gt 2 ]]; then case "$command" in s|st|sta|stat|statu|status) @@ -1199,6 +1199,14 @@ _nmcli() _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" fi ;; + mod|modi|modif|modify) + if [[ ${#words[@]} -eq 3 ]]; then + _nmcli_compl_COMMAND_nl "${words[2]}" "$(nmcli --complete-args device modify "" 2>/dev/null)" + else + _nmcli_array_delete_at words 0 1 + _nmcli_list_nl "$(nmcli --complete-args device modify "${words[@]}" 2>/dev/null)" + fi + ;; 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/man/nmcli.xml b/man/nmcli.xml index a1f0062cd..ff8e63e69 100644 --- a/man/nmcli.xml +++ b/man/nmcli.xml @@ -1163,6 +1163,7 @@ set connect reapply + modify disconnect delete monitor @@ -1254,6 +1255,33 @@ + + + modify + + + option value + [+|-]setting.property value + + + + + + Modify the settings currently active on the device. + + This command lets you do temporary changes to a configuration active on + a particular device. The changes are not preserved in the connection profile. + + See nm-settings5 + for the list of available properties. Please note that some + properties can't be changed on an already connected device. + + You can also use the aliases described in + section. The syntax is + the same as of the nmcli connection modify command. + + + disconnect @@ -1599,10 +1627,10 @@ Property Aliases - Apart from the property-value pairs, connection - add and connection modify also accept short forms - of some properties. They exist for convenience and compatiblity with older - versions of nmcli that could not accept the raw + Apart from the property-value pairs, connection add, + connection modify and device modify also + accept short forms of some properties. They exist for convenience and compatiblity + with older versions of nmcli that could not accept the raw properties. The overview of the aliases is below. An actual connection type is used to @@ -2228,6 +2256,22 @@ It's equivalent of using +ipv6.addresses syntax. + + nmcli dev modify em1 ipv4.method shared + + starts IPv4 connection sharing using em1 device. The sharing will be active + until the device is disconnected. + + + + + nmcli dev modify em1 ipv6.address 2001:db8::a:bad:c0de + + temporarily adds an IP address to a device. The address will be removed + when the same connection is activated again. + + + nmcli connection add type ethernet autoconnect no ifname eth0