merge: branch 'lr/device-modify'

https://bugzilla.gnome.org/show_bug.cgi?id=767999
This commit is contained in:
Lubomir Rintel
2016-06-29 20:28:59 +02:00
8 changed files with 475 additions and 314 deletions

View File

@@ -1389,3 +1389,56 @@ nmc_parse_lldp_capabilities (guint value)
return g_string_free (str, FALSE); 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;
}

View File

@@ -73,4 +73,12 @@ int nmc_rl_set_deftext (void);
char *nmc_parse_lldp_capabilities (guint value); 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 */ #endif /* NMC_COMMON_H */

View File

@@ -4225,12 +4225,12 @@ get_value (const char **value, int *argc, char ***argv, const char *option, GErr
return TRUE; return TRUE;
} }
static gboolean gboolean
read_connection_properties (NmCli *nmc, nmc_read_connection_properties (NmCli *nmc,
NMConnection *connection, NMConnection *connection,
int *argc, int *argc,
char ***argv, char ***argv,
GError **error) GError **error)
{ {
const char *option; const char *option;
const char *value = NULL; const char *value = NULL;
@@ -4255,8 +4255,6 @@ read_connection_properties (NmCli *nmc,
option = **argv; option = **argv;
if (!option) { if (!option) {
if (nmc->complete)
complete_property_name (nmc, connection, '\0', "", NULL);
g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
_("Error: <setting>.<property> argument is missing.")); _("Error: <setting>.<property> argument is missing."));
return FALSE; return FALSE;
@@ -4275,10 +4273,11 @@ read_connection_properties (NmCli *nmc,
if (modifier) if (modifier)
setting++; 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); setting_name = check_valid_name (setting, type_settings, slv_settings, &local);
if (!setting_name) { 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, g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
_("Error: invalid or not allowed setting '%s': %s."), _("Error: invalid or not allowed setting '%s': %s."),
setting, local->message); setting, local->message);
@@ -4287,11 +4286,8 @@ read_connection_properties (NmCli *nmc,
} }
next_arg (argc, argv); next_arg (argc, argv);
if (!get_value (&value, argc, argv, option, error)) { if (!get_value (&value, argc, argv, option, error))
if (nmc->complete)
complete_property_name (nmc, connection, modifier, setting, strv[1]);
return FALSE; return FALSE;
}
if (!*argc && nmc->complete) if (!*argc && nmc->complete)
complete_property (setting, strv[1], value ? value : ""); complete_property (setting, strv[1], value ? value : "");
@@ -4318,19 +4314,19 @@ read_connection_properties (NmCli *nmc,
if (!chosen) { if (!chosen) {
if (modifier) if (modifier)
option++; option++;
if (nmc->complete) if (*argc == 1 && nmc->complete)
complete_property_name (nmc, connection, modifier, option, NULL); complete_property_name (nmc, connection, modifier, option, NULL);
g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT,
_("Error: invalid <setting>.<property> '%s'."), option); _("Error: invalid <setting>.<property> '%s'."), option);
return FALSE; return FALSE;
} }
if (*argc == 1 && nmc->complete)
complete_property_name (nmc, connection, modifier, option, NULL);
next_arg (argc, argv); next_arg (argc, argv);
if (!get_value (&value, argc, argv, option, error)) { if (!get_value (&value, argc, argv, option, error))
if (nmc->complete)
complete_property_name (nmc, connection, modifier, option, NULL);
return FALSE; return FALSE;
}
if (!*argc && nmc->complete) if (!*argc && nmc->complete)
complete_option (chosen, value ? value : ""); complete_option (chosen, value ? value : "");
@@ -4649,7 +4645,7 @@ do_connection_add (NmCli *nmc, int argc, char **argv)
read_properties: read_properties:
/* Get the arguments from the command line if any */ /* 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) { if (g_strcmp0 (*argv, "--") == 0 && !seen_dash_dash) {
/* This is for compatibility with older nmcli that required /* This is for compatibility with older nmcli that required
* options and properties to be separated with "--" */ * options and properties to be separated with "--" */
@@ -4762,9 +4758,6 @@ finish:
if (connection) if (connection)
g_object_unref (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; return nmc->return_value;
} }
@@ -7808,7 +7801,7 @@ do_connection_modify (NmCli *nmc,
next_arg (&argc, &argv); 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); g_string_assign (nmc->return_text, error->message);
nmc->return_value = error->code; nmc->return_value = error->code;
g_clear_error (&error); g_clear_error (&error);
@@ -7822,9 +7815,6 @@ do_connection_modify (NmCli *nmc,
nmc->should_wait++; nmc->should_wait++;
finish: 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; return nmc->return_value;
} }
@@ -7968,7 +7958,7 @@ do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv)
clone_connection_cb, clone_connection_cb,
info); info);
nmc->should_wait = TRUE; nmc->should_wait++;
finish: finish:
if (new_connection) if (new_connection)
g_object_unref (new_connection); g_object_unref (new_connection);
@@ -8370,7 +8360,7 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv)
add_connection_cb, add_connection_cb,
info); info);
nmc->should_wait = TRUE; nmc->should_wait++;
finish: finish:
if (connection) if (connection)
g_object_unref (connection); g_object_unref (connection);

View File

@@ -26,4 +26,12 @@ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv);
void monitor_connections (NmCli *nmc); void monitor_connections (NmCli *nmc);
gboolean
nmc_read_connection_properties (NmCli *nmc,
NMConnection *connection,
int *argc,
char ***argv,
GError **error);
#endif /* NMC_CONNECTIONS_H */ #endif /* NMC_CONNECTIONS_H */

View File

@@ -30,6 +30,7 @@
#include "utils.h" #include "utils.h"
#include "common.h" #include "common.h"
#include "devices.h" #include "devices.h"
#include "connections.h"
/* define some prompts */ /* define some prompts */
#define PROMPT_INTERFACE _("Interface: ") #define PROMPT_INTERFACE _("Interface: ")
@@ -358,6 +359,26 @@ usage_device_reapply (void)
"made since it was last applied.\n\n")); "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 := <ifname> ([+|-]<setting>.<property> <value>)+\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 static void
usage_device_disconnect (void) usage_device_disconnect (void)
{ {
@@ -515,8 +536,21 @@ get_devices_sorted (NMClient *client)
return sorted; 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 * static GSList *
device_list (NmCli *nmc, int argc, char **argv) get_device_list (NmCli *nmc, int argc, char **argv)
{ {
int arg_num = argc; int arg_num = argc;
char **arg_arr = NULL; char **arg_arr = NULL;
@@ -542,6 +576,9 @@ device_list (NmCli *nmc, int argc, char **argv)
devices = get_devices_sorted (nmc->client); devices = get_devices_sorted (nmc->client);
while (arg_num > 0) { while (arg_num > 0) {
if (arg_num == 1 && nmc->complete)
complete_device (devices, *arg_ptr);
device = NULL; device = NULL;
for (i = 0; devices[i]; i++) { for (i = 0; devices[i]; i++) {
if (!g_strcmp0 (nm_device_get_iface (devices[i]), *arg_ptr)) { if (!g_strcmp0 (nm_device_get_iface (devices[i]), *arg_ptr)) {
@@ -556,7 +593,8 @@ device_list (NmCli *nmc, int argc, char **argv)
else else
g_printerr (_("Warning: argument '%s' is duplicated.\n"), *arg_ptr); g_printerr (_("Warning: argument '%s' is duplicated.\n"), *arg_ptr);
} else { } 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.")); g_string_printf (nmc->return_text, _("Error: not all devices found."));
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND;
} }
@@ -572,6 +610,45 @@ error:
return queue; 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++) {
if (!g_strcmp0 (nm_device_get_iface (devices[i]), ifname))
break;
}
if (nmc->complete && !*argc)
complete_device (devices, ifname);
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 static int
compare_aps (gconstpointer a, gconstpointer b, gpointer user_data) compare_aps (gconstpointer a, gconstpointer b, gpointer user_data)
{ {
@@ -1401,6 +1478,16 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
NmcOutputField *tmpl, *arr; NmcOutputField *tmpl, *arr;
size_t tmpl_len; 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);
return NMC_RESULT_ERROR_USER_INPUT;
}
while (argc > 0) { while (argc > 0) {
g_printerr (_("Unknown parameter: %s\n"), *argv); g_printerr (_("Unknown parameter: %s\n"), *argv);
argc--; argc--;
@@ -1421,8 +1508,7 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
if (error) { if (error) {
g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message); g_string_printf (nmc->return_text, _("Error: 'device status': %s"), error->message);
g_error_free (error); g_error_free (error);
nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return NMC_RESULT_ERROR_USER_INPUT;
goto error;
} }
/* Add headers */ /* Add headers */
@@ -1440,59 +1526,53 @@ do_devices_status (NmCli *nmc, int argc, char **argv)
g_free (devices); g_free (devices);
return NMC_RESULT_SUCCESS; return NMC_RESULT_SUCCESS;
error:
return nmc->return_value;
} }
static NMCResultCode static NMCResultCode
do_devices_show (NmCli *nmc, int argc, char **argv) do_device_show (NmCli *nmc, int argc, char **argv)
{ {
NMDevice **devices = NULL; gs_free_error GError *error = NULL;
NMDevice *device = NULL;
const char *ifname = NULL;
int i;
gboolean ret;
if (argc == 1) if (!nmc->mode_specified)
ifname = *argv; nmc->multiline_output = TRUE; /* multiline mode is default for 'device show' */
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;
}
devices = get_devices_sorted (nmc->client); if (argc) {
NMDevice *device;
if (ifname) { device = get_device (nmc, &argc, &argv, &error);
/* 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;
}
if (!device) { if (!device) {
g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); g_string_printf (nmc->return_text, _("Error: %s."), error->message);
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; return error->code;
goto error;
} }
if (argc) {
g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (nmc->complete)
return nmc->return_value;
show_device_info (device, nmc); show_device_info (device, nmc);
} else { } 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 */ /* Show details for all devices */
for (i = 0; devices[i]; i++) { for (i = 0; devices[i]; i++) {
nmc_empty_output_fields (nmc); nmc_empty_output_fields (nmc);
ret = show_device_info (devices[i], nmc); if (!show_device_info (devices[i], nmc))
if (!ret)
break; break;
if (devices[i + 1]) if (devices[i + 1])
g_print ("\n"); /* Empty line */ g_print ("\n"); /* Empty line */
} }
g_free (devices);
} }
error:
g_free (devices);
return nmc->return_value; return nmc->return_value;
} }
@@ -1737,58 +1817,28 @@ connect_device_cb (GObject *client, GAsyncResult *result, gpointer user_data)
static NMCResultCode static NMCResultCode
do_device_connect (NmCli *nmc, int argc, char **argv) do_device_connect (NmCli *nmc, int argc, char **argv)
{ {
NMDevice **devices;
NMDevice *device = NULL; NMDevice *device = NULL;
const char *ifname = NULL;
char *ifname_ask = NULL;
int i;
AddAndActivateInfo *info; AddAndActivateInfo *info;
gs_free_error GError *error = NULL;
/* Set default timeout for connect operation. */ /* Set default timeout for connect operation. */
if (nmc->timeout == -1) if (nmc->timeout == -1)
nmc->timeout = 90; nmc->timeout = 90;
if (argc == 0) { device = get_device (nmc, &argc, &argv, &error);
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;
}
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) {
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) { if (!device) {
g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); g_string_printf (nmc->return_text, _("Error: %s."), error->message);
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; return error->code;
goto error;
} }
if (*argv) {
g_string_printf (nmc->return_text, _("Error: extra argument not allowed: '%s'."), *argv);
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 * 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. * till connect_device_cb() is called, giving NM time to check our permissions.
@@ -1820,9 +1870,6 @@ do_device_connect (NmCli *nmc, int argc, char **argv)
if (nmc->print_output == NMC_PRINT_PRETTY) if (nmc->print_output == NMC_PRINT_PRETTY)
progress_id = g_timeout_add (120, progress_cb, device); progress_id = g_timeout_add (120, progress_cb, device);
error:
g_free (ifname_ask);
return nmc->return_value; return nmc->return_value;
} }
@@ -1936,55 +1983,30 @@ reapply_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
static NMCResultCode static NMCResultCode
do_device_reapply (NmCli *nmc, int argc, char **argv) do_device_reapply (NmCli *nmc, int argc, char **argv)
{ {
gs_free NMDevice **devices = NULL; NMDevice *device;
NMDevice *device = NULL;
DeviceCbInfo *info = NULL; DeviceCbInfo *info = NULL;
char **arg_ptr = argv; gs_free_error GError *error = NULL;
int arg_num = argc;
int i;
gs_free char *device_name_free = NULL;
const char *device_name = NULL;
/* Set default timeout for reapply operation. */ /* Set default timeout for reapply operation. */
if (nmc->timeout == -1) if (nmc->timeout == -1)
nmc->timeout = 10; nmc->timeout = 10;
if (argc == 0) { device = get_device (nmc, &argc, &argv, &error);
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;
}
}
if (!device) { if (!device) {
g_string_printf (nmc->return_text, _("Error: device '%s' not found."), device_name); g_string_printf (nmc->return_text, _("Error: %s."), error->message);
nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; return error->code;
return nmc->return_value;
} }
if (argc) {
g_string_printf (nmc->return_text, _("Error: invalid extra argument '%s'."), *argv);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (nmc->complete)
return nmc->return_value;
nmc->nowait_flag = (nmc->timeout == 0); nmc->nowait_flag = (nmc->timeout == 0);
nmc->should_wait = TRUE; nmc->should_wait++;
info = g_slice_new0 (DeviceCbInfo); info = g_slice_new0 (DeviceCbInfo);
info->nmc = nmc; info->nmc = nmc;
@@ -1996,6 +2018,108 @@ do_device_reapply (NmCli *nmc, int argc, char **argv)
return nmc->return_value; 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 static void
disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data) disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
{ {
@@ -2030,7 +2154,7 @@ disconnect_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
} }
static NMCResultCode static NMCResultCode
do_device_disconnect (NmCli *nmc, int argc, char **argv) do_devices_disconnect (NmCli *nmc, int argc, char **argv)
{ {
NMDevice *device; NMDevice *device;
DeviceCbInfo *info = NULL; DeviceCbInfo *info = NULL;
@@ -2040,9 +2164,11 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv)
if (nmc->timeout == -1) if (nmc->timeout == -1)
nmc->timeout = 10; nmc->timeout = 10;
queue = device_list (nmc, argc, argv); queue = get_device_list (nmc, argc, argv);
if (!queue) if (!queue)
goto error; return nmc->return_value;
if (nmc->complete)
goto out;
queue = g_slist_reverse (queue); queue = g_slist_reverse (queue);
info = g_slice_new0 (DeviceCbInfo); info = g_slice_new0 (DeviceCbInfo);
@@ -2068,7 +2194,7 @@ do_device_disconnect (NmCli *nmc, int argc, char **argv)
nm_device_disconnect_async (device, NULL, disconnect_device_cb, info); nm_device_disconnect_async (device, NULL, disconnect_device_cb, info);
} }
error: out:
g_slist_free (queue); g_slist_free (queue);
return nmc->return_value; return nmc->return_value;
} }
@@ -2097,7 +2223,7 @@ delete_device_cb (GObject *object, GAsyncResult *result, gpointer user_data)
} }
static NMCResultCode static NMCResultCode
do_device_delete (NmCli *nmc, int argc, char **argv) do_devices_delete (NmCli *nmc, int argc, char **argv)
{ {
NMDevice *device; NMDevice *device;
DeviceCbInfo *info = NULL; DeviceCbInfo *info = NULL;
@@ -2107,9 +2233,11 @@ do_device_delete (NmCli *nmc, int argc, char **argv)
if (nmc->timeout == -1) if (nmc->timeout == -1)
nmc->timeout = 10; nmc->timeout = 10;
queue = device_list (nmc, argc, argv); queue = get_device_list (nmc, argc, argv);
if (!queue) if (!queue)
goto error; return nmc->return_value;
if (nmc->complete)
goto out;
queue = g_slist_reverse (queue); queue = g_slist_reverse (queue);
info = g_slice_new0 (DeviceCbInfo); info = g_slice_new0 (DeviceCbInfo);
@@ -2132,7 +2260,7 @@ do_device_delete (NmCli *nmc, int argc, char **argv)
nm_device_delete_async (device, NULL, delete_device_cb, info); nm_device_delete_async (device, NULL, delete_device_cb, info);
} }
error: out:
g_slist_free (queue); g_slist_free (queue);
return nmc->return_value; return nmc->return_value;
} }
@@ -2154,6 +2282,10 @@ do_device_set (NmCli *nmc, int argc, char **argv)
[DEV_SET_MANAGED] = { -1 }, [DEV_SET_MANAGED] = { -1 },
}; };
/* Not (yet?) supported */
if (nmc->complete)
return nmc->return_value;
if (argc >= 1 && g_strcmp0 (*argv, "ifname") == 0) { if (argc >= 1 && g_strcmp0 (*argv, "ifname") == 0) {
argc--; argc--;
argv++; argv++;
@@ -2315,8 +2447,14 @@ device_removed (NMClient *client, NMDevice *device, NmCli *nmc)
} }
static NMCResultCode static NMCResultCode
do_device_monitor (NmCli *nmc, int argc, char **argv) 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) { if (argc == 0) {
/* No devices specified. Monitor all. */ /* No devices specified. Monitor all. */
const GPtrArray *devices = nm_client_get_devices (nmc->client); const GPtrArray *devices = nm_client_get_devices (nmc->client);
@@ -2329,21 +2467,14 @@ do_device_monitor (NmCli *nmc, int argc, char **argv)
nmc->should_wait++; nmc->should_wait++;
g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc); g_signal_connect (nmc->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK (device_added), nmc);
} else { } else {
/* Monitor just the specified devices. */ /* Monitor the specified devices. */
GSList *queue = device_list (nmc, argc, argv);
GSList *iter;
if (!queue)
return nmc->return_value;
for (iter = queue; iter; iter = g_slist_next (iter)) for (iter = queue; iter; iter = g_slist_next (iter))
device_watch (nmc, NM_DEVICE (iter->data)); device_watch (nmc, NM_DEVICE (iter->data));
g_slist_free (queue); g_slist_free (queue);
} }
g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc); g_signal_connect (nmc->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK (device_removed), nmc);
return nmc->return_value;
return NMC_RESULT_SUCCESS;
} }
static void static void
@@ -3325,7 +3456,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv)
/* Activate the connection now */ /* Activate the connection now */
nmc->nowait_flag = (nmc->timeout == 0); nmc->nowait_flag = (nmc->timeout == 0);
nmc->should_wait = TRUE; nmc->should_wait++;
info = g_malloc0 (sizeof (AddAndActivateInfo)); info = g_malloc0 (sizeof (AddAndActivateInfo));
info->nmc = nmc; info->nmc = nmc;
@@ -3447,6 +3578,18 @@ error:
static NMCResultCode static NMCResultCode
do_device_wifi (NmCli *nmc, int argc, char **argv) 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);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (argc == 0) if (argc == 0)
nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1); nmc->return_value = do_device_wifi_list (nmc, argc-1, argv+1);
else if (argc > 0) { else if (argc > 0) {
@@ -3630,6 +3773,21 @@ error:
static NMCResultCode static NMCResultCode
do_device_lldp (NmCli *nmc, int argc, char **argv) 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);
return NMC_RESULT_ERROR_USER_INPUT;
}
if (!nmc->mode_specified)
nmc->multiline_output = TRUE; /* multiline mode is default for 'device lldp' */
if (argc == 0) if (argc == 0)
nmc->return_value = do_device_lldp_list (nmc, argc, argv); nmc->return_value = do_device_lldp_list (nmc, argc, argv);
else if (matches (*argv, "list") == 0) else if (matches (*argv, "list") == 0)
@@ -3711,11 +3869,24 @@ nmcli_device_tab_completion (const char *text, int start, int end)
return match_array; 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 },
{"modify", do_device_modify, usage_device_modify },
{NULL, do_devices_status, usage },
};
NMCResultCode NMCResultCode
do_devices (NmCli *nmc, int argc, char **argv) do_devices (NmCli *nmc, int argc, char **argv)
{ {
GError *error = NULL;
/* Register polkit agent */ /* Register polkit agent */
nmc_start_polkit_agent_start_try (nmc); nmc_start_polkit_agent_start_try (nmc);
@@ -3727,120 +3898,14 @@ do_devices (NmCli *nmc, int argc, char **argv)
/* Check whether NetworkManager is running */ /* Check whether NetworkManager is running */
if (!nm_client_get_nm_running (nmc->client)) { if (!nm_client_get_nm_running (nmc->client)) {
g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; return NMC_RESULT_ERROR_NM_NOT_RUNNING;
return nmc->return_value;
} }
if (argc == 0) { return nmc_do_cmd (nmc, device_cmds, *argv, argc, argv);
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_devices_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_device_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);
}
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_device_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;
} }
void void
monitor_devices (NmCli *nmc) monitor_devices (NmCli *nmc)
{ {
do_device_monitor (nmc, 0, NULL); do_devices_monitor (nmc, 0, NULL);
} }

View File

@@ -1184,7 +1184,7 @@ _nmcli()
;; ;;
d|de|dev|devi|devic|device) d|de|dev|devi|devic|device)
if [[ ${#words[@]} -eq 2 ]]; then 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 elif [[ ${#words[@]} -gt 2 ]]; then
case "$command" in case "$command" in
s|st|sta|stat|statu|status) s|st|sta|stat|statu|status)
@@ -1199,6 +1199,14 @@ _nmcli()
_nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)" _nmcli_compl_COMMAND_nl "${words[2]}" "$(_nmcli_dev_status DEVICE)"
fi 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| \ d|di|dis|disc|disco|discon|disconn|disconne|disconnec|disconnect| \
de|del|dele|delet|delete| \ de|del|dele|delet|delete| \
m|mo|mon|moni|monit|monito|monitor) m|mo|mon|moni|monit|monito|monitor)

View File

@@ -114,36 +114,18 @@ do_help (NmCli *nmc, int argc, char **argv)
return NMC_RESULT_SUCCESS; return NMC_RESULT_SUCCESS;
} }
static const struct cmd { static const NMCCommand nmcli_cmds[] = {
const char *cmd; { "general", do_general, NULL },
NMCResultCode (*func) (NmCli *nmc, int argc, char **argv); { "monitor", do_monitor, NULL },
} nmcli_cmds[] = { { "networking", do_networking, NULL },
{ "general", do_general }, { "radio", do_radio, NULL },
{ "monitor", do_monitor }, { "connection", do_connections, NULL },
{ "networking", do_networking }, { "device", do_devices, NULL },
{ "radio", do_radio }, { "agent", do_agent, NULL },
{ "connection", do_connections }, { "help", do_help, NULL },
{ "device", do_devices },
{ "agent", do_agent },
{ "help", do_help },
{ 0 } { 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 static NMCResultCode
parse_command_line (NmCli *nmc, int argc, char **argv) parse_command_line (NmCli *nmc, int argc, char **argv)
{ {
@@ -160,7 +142,7 @@ parse_command_line (NmCli *nmc, int argc, char **argv)
* autocompletion mode (so we should just quit and don't print anything). * autocompletion mode (so we should just quit and don't print anything).
* This would help us to ensure shell autocompletion after NM package downgrade * This would help us to ensure shell autocompletion after NM package downgrade
* if we ever will enable --complete-args for other commands */ * 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; return nmc->return_value;
nmc->complete = TRUE; nmc->complete = TRUE;
argv[1] = argv[0]; argv[1] = argv[0];
@@ -301,7 +283,7 @@ parse_command_line (NmCli *nmc, int argc, char **argv)
if (argc > 1) { if (argc > 1) {
/* Now run the requested command */ /* 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); usage (base);
@@ -640,6 +622,9 @@ main (int argc, char *argv[])
loop = g_main_loop_new (NULL, FALSE); /* create main loop */ loop = g_main_loop_new (NULL, FALSE); /* create main loop */
g_main_loop_run (loop); /* run 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 */ /* Print result descripting text */
if (nm_cli.return_value != NMC_RESULT_SUCCESS) { if (nm_cli.return_value != NMC_RESULT_SUCCESS) {
g_printerr ("%s\n", nm_cli.return_text->str); g_printerr ("%s\n", nm_cli.return_text->str);

View File

@@ -1163,6 +1163,7 @@
<arg choice='plain'><command>set</command></arg> <arg choice='plain'><command>set</command></arg>
<arg choice='plain'><command>connect</command></arg> <arg choice='plain'><command>connect</command></arg>
<arg choice='plain'><command>reapply</command></arg> <arg choice='plain'><command>reapply</command></arg>
<arg choice='plain'><command>modify</command></arg>
<arg choice='plain'><command>disconnect</command></arg> <arg choice='plain'><command>disconnect</command></arg>
<arg choice='plain'><command>delete</command></arg> <arg choice='plain'><command>delete</command></arg>
<arg choice='plain'><command>monitor</command></arg> <arg choice='plain'><command>monitor</command></arg>
@@ -1254,6 +1255,33 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term>
<command>modify</command>
<arg rep='repeat' choice='plain'>
<group>
<arg choice='plain'><replaceable>option</replaceable> <replaceable>value</replaceable></arg>
<arg choice='plain'>[+|-]<replaceable>setting</replaceable>.<replaceable>property</replaceable> <replaceable>value</replaceable></arg>
</group>
</arg>
</term>
<listitem>
<para>Modify the settings currently active on the device.</para>
<para>This command lets you do temporary changes to a configuration active on
a particular device. The changes are not preserved in the connection profile.</para>
<para>See <citerefentry><refentrytitle>nm-settings</refentrytitle><manvolnum>5</manvolnum>
</citerefentry> for the list of available properties. Please note that some
properties can't be changed on an already connected device.</para>
<para>You can also use the aliases described in
<link linkend='property_aliases' endterm='property_aliases.title' /> section. The syntax is
the same as of the <command>nmcli connection modify</command> command.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term> <term>
<command>disconnect</command> <command>disconnect</command>
@@ -1599,10 +1627,10 @@
<refsect1 id='property_aliases'><title id='property_aliases.title'>Property Aliases</title> <refsect1 id='property_aliases'><title id='property_aliases.title'>Property Aliases</title>
<para>Apart from the property-value pairs, <command>connection <para>Apart from the property-value pairs, <command>connection add</command>,
add</command> and <command>connection modify</command> also accept short forms <command>connection modify</command> and <command>device modify</command> also
of some properties. They exist for convenience and compatiblity with older accept short forms of some properties. They exist for convenience and compatiblity
versions of <command>nmcli</command> that could not accept the raw with older versions of <command>nmcli</command> that could not accept the raw
properties.</para> properties.</para>
<para>The overview of the aliases is below. An actual connection type is used to <para>The overview of the aliases is below. An actual connection type is used to
@@ -2228,6 +2256,22 @@ It's equivalent of using <literal>+ipv6.addresses</literal> syntax.</entry>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><userinput>nmcli dev modify em1 ipv4.method shared</userinput></term>
<listitem>
<para>starts IPv4 connection sharing using em1 device. The sharing will be active
until the device is disconnected.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput>nmcli dev modify em1 ipv6.address 2001:db8::a:bad:c0de</userinput></term>
<listitem>
<para>temporarily adds an IP address to a device. The address will be removed
when the same connection is activated again.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><userinput>nmcli connection add type ethernet autoconnect no ifname eth0</userinput></term> <term><userinput>nmcli connection add type ethernet autoconnect no ifname eth0</userinput></term>
<listitem> <listitem>