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/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