diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2016-08-01 16:11:35 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2016-08-01 16:11:35 +0200 |
commit | 2956fd5fcb26635c42ad3fa738d06ffb722acfa1 (patch) | |
tree | b7c51eb32100db114cdaa0d47bec4225623fa249 | |
parent | 570e8ee29684f16b4306f1eb94a1b756608ddd3b (diff) | |
parent | 8cc6ce7e44d574cc8baf0dbb3737f6ada060ae99 (diff) | |
download | NetworkManager-2956fd5fcb26635c42ad3fa738d06ffb722acfa1.tar.gz |
merge: branch 'lr/completion-2'
https://bugzilla.gnome.org/show_bug.cgi?id=768737
-rw-r--r-- | clients/cli/agent.c | 53 | ||||
-rw-r--r-- | clients/cli/common.c | 53 | ||||
-rw-r--r-- | clients/cli/common.h | 8 | ||||
-rw-r--r-- | clients/cli/connections.c | 1018 | ||||
-rw-r--r-- | clients/cli/connections.h | 6 | ||||
-rw-r--r-- | clients/cli/devices.c | 482 | ||||
-rw-r--r-- | clients/cli/devices.h | 20 | ||||
-rw-r--r-- | clients/cli/general.c | 423 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 219 | ||||
-rw-r--r-- | clients/cli/nmcli.h | 5 | ||||
-rw-r--r-- | clients/cli/settings.c | 6 | ||||
-rw-r--r-- | clients/cli/settings.h | 30 | ||||
-rw-r--r-- | man/nmcli.xml | 24 |
13 files changed, 1254 insertions, 1093 deletions
diff --git a/clients/cli/agent.c b/clients/cli/agent.c index b8a56d2381..bc837c722f 100644 --- a/clients/cli/agent.c +++ b/clients/cli/agent.c @@ -138,6 +138,9 @@ secrets_requested (NMSecretAgentSimple *agent, static NMCResultCode do_agent_secret (NmCli *nmc, int argc, char **argv) { + if (nmc->complete) + return nmc->return_value; + /* Create secret agent */ nmc->secret_agent = nm_secret_agent_simple_new ("nmcli-agent"); if (nmc->secret_agent) { @@ -160,6 +163,9 @@ do_agent_polkit (NmCli *nmc, int argc, char **argv) { GError *error = NULL; + if (nmc->complete) + return nmc->return_value; + /* Initialize polkit agent */ if (!nmc_polkit_agent_init (nmc, TRUE, &error)) { g_dbus_error_strip_remote_error (error); @@ -182,6 +188,9 @@ do_agent_all (NmCli *nmc, int argc, char **argv) { NMCResultCode secret_res; + if (nmc->complete) + return nmc->return_value; + /* Run both secret and polkit agent */ secret_res = do_agent_secret (nmc, argc, argv); if (secret_res != NMC_RESULT_SUCCESS) @@ -195,6 +204,14 @@ do_agent_all (NmCli *nmc, int argc, char **argv) return nmc->return_value; } +static const NMCCommand agent_cmds[] = { + {"secret", do_agent_secret, usage_agent_secret }, + {"polkit", do_agent_polkit, usage_agent_polkit }, + {"all", do_agent_all, usage_agent_all }, + {NULL, do_agent_all, usage } +}; + + NMCResultCode do_agent (NmCli *nmc, int argc, char **argv) { @@ -208,39 +225,5 @@ do_agent (NmCli *nmc, int argc, char **argv) return nmc->return_value; } - if (argc == 0) { - nmc->return_value = do_agent_all (nmc, 0, NULL); - } - - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage (); - goto usage_exit; - } else if (matches (*argv, "secret") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_secret (); - goto usage_exit; - } - nmc->return_value = do_agent_secret (nmc, argc-1, argv+1); - } else if (matches (*argv, "polkit") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_polkit (); - goto usage_exit; - } - nmc->return_value = do_agent_polkit (nmc, argc-1, argv+1); - } else if (matches (*argv, "all") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_agent_all (); - goto usage_exit; - } - nmc->return_value = do_agent_all (nmc, argc-1, argv+1); - } else { - usage (); - g_string_printf (nmc->return_text, _("Error: 'agent' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - -usage_exit: - return nmc->return_value; + return nmc_do_cmd (nmc, agent_cmds, *argv, argc, argv); } diff --git a/clients/cli/common.c b/clients/cli/common.c index 7ac0a7625d..8b4a6aede5 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -872,6 +872,7 @@ nmc_team_check_config (const char *config, char **out_config, GError **error) * @filter_val: connection to find (connection name, UUID or path) * @start: where to start in @list. The location is updated so that the function * can be called multiple times (for connections with the same name). + * @complete: print possible completions * * Find a connection in @list according to @filter_val. @filter_type determines * what property is used for comparison. When @filter_type is NULL, compare @@ -885,7 +886,8 @@ NMConnection * nmc_find_connection (const GPtrArray *connections, const char *filter_type, const char *filter_val, - int *start) + int *start, + gboolean complete) { NMConnection *connection; NMConnection *found = NULL; @@ -907,20 +909,36 @@ nmc_find_connection (const GPtrArray *connections, * type. If 'path' filter type is specified, comparison against * numeric index (in addition to the whole path) is allowed. */ - if ( ( (!filter_type || strcmp (filter_type, "id") == 0) - && strcmp (filter_val, id) == 0) - || ( (!filter_type || strcmp (filter_type, "uuid") == 0) - && strcmp (filter_val, uuid) == 0) - || ( (!filter_type || strcmp (filter_type, "path") == 0) - && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)))) { - if (!start) - return connection; - if (found) { - *start = i; - return found; - } - found = connection; + if (!filter_type || strcmp (filter_type, "id") == 0) { + if (complete) + nmc_complete_strings (filter_val, id, NULL); + if (strcmp (filter_val, id) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "uuid") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, uuid, NULL); + if (strcmp (filter_val, uuid) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "path") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL); + if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)) + goto found; } + + continue; +found: + if (!start) + return connection; + if (found) { + *start = i; + return found; + } + found = connection; } if (start) @@ -1096,7 +1114,7 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, p = strrchr (path, '/'); if (p) *p = '\0'; - connection = nmc_find_connection (nmc->connections, "path", path, NULL); + connection = nmc_find_connection (nmc->connections, "path", path, NULL, FALSE); g_free (path); } @@ -1416,6 +1434,9 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char { const NMCCommand *c; + if (argc == 0 && nmc->complete) + return nmc->return_value; + if (argc == 1 && nmc->complete) { for (c = cmds; c->cmd; ++c) { if (!*cmd || matches (cmd, c->cmd) == 0) @@ -1445,7 +1466,7 @@ nmc_do_cmd (NmCli *nmc, const NMCCommand cmds[], const char *cmd, int argc, char } } else if (c->func) { /* No command, run the default handler. */ - nmc->return_value = c->func (nmc, argc-1, argv+1); + nmc->return_value = c->func (nmc, argc, argv); } else { /* No command and no default handler. */ g_string_printf (nmc->return_text, _("Error: missing argument. Try passing --help.")); diff --git a/clients/cli/common.h b/clients/cli/common.h index 3910683dc5..2450cdb950 100644 --- a/clients/cli/common.h +++ b/clients/cli/common.h @@ -48,7 +48,8 @@ gboolean nmc_team_check_config (const char *config, char **out_config, GError ** NMConnection *nmc_find_connection (const GPtrArray *connections, const char *filter_type, const char *filter_val, - int *start); + int *start, + gboolean complete); void nmc_secrets_requested (NMSecretAgentSimple *agent, const char *request_id, @@ -85,4 +86,9 @@ void nmc_complete_strings (const char *prefix, ...) G_GNUC_NULL_TERMINATED; void nmc_complete_bool (const char *prefix); +extern NmcOutputField nmc_fields_ip4_config[]; +extern NmcOutputField nmc_fields_dhcp4_config[]; +extern NmcOutputField nmc_fields_ip6_config[]; +extern NmcOutputField nmc_fields_dhcp6_config[]; + #endif /* NMC_COMMON_H */ diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 10b7df77f2..53e57e4eed 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -33,6 +33,7 @@ #include "common.h" #include "settings.h" #include "connections.h" +#include "devices.h" #include "nm-secret-agent-simple.h" #include "polkit-agent.h" #include "nm-vpn-helpers.h" @@ -114,7 +115,7 @@ struct _OptionInfo { #define BASE_PROMPT "nmcli> " /* Available fields for 'connection show' */ -static NmcOutputField nmc_fields_con_show[] = { +NmcOutputField nmc_fields_con_show[] = { {"NAME", N_("NAME")}, /* 0 */ {"UUID", N_("UUID")}, /* 1 */ {"TYPE", N_("TYPE")}, /* 2 */ @@ -137,39 +138,8 @@ static NmcOutputField nmc_fields_con_show[] = { /* Helper macro to define fields */ #define SETTING_FIELD(setting, props) { setting, N_(setting), 0, props, NULL, FALSE, FALSE, 0 } -/* defined in settings.c */ -extern NmcOutputField nmc_fields_setting_connection[]; -extern NmcOutputField nmc_fields_setting_wired[]; -extern NmcOutputField nmc_fields_setting_8021X[]; -extern NmcOutputField nmc_fields_setting_wireless[]; -extern NmcOutputField nmc_fields_setting_wireless_security[]; -extern NmcOutputField nmc_fields_setting_ip4_config[]; -extern NmcOutputField nmc_fields_setting_ip6_config[]; -extern NmcOutputField nmc_fields_setting_serial[]; -extern NmcOutputField nmc_fields_setting_ppp[]; -extern NmcOutputField nmc_fields_setting_pppoe[]; -extern NmcOutputField nmc_fields_setting_adsl[]; -extern NmcOutputField nmc_fields_setting_gsm[]; -extern NmcOutputField nmc_fields_setting_cdma[]; -extern NmcOutputField nmc_fields_setting_bluetooth[]; -extern NmcOutputField nmc_fields_setting_olpc_mesh[]; -extern NmcOutputField nmc_fields_setting_vpn[]; -extern NmcOutputField nmc_fields_setting_wimax[]; -extern NmcOutputField nmc_fields_setting_infiniband[]; -extern NmcOutputField nmc_fields_setting_bond[]; -extern NmcOutputField nmc_fields_setting_vlan[]; -extern NmcOutputField nmc_fields_setting_bridge[]; -extern NmcOutputField nmc_fields_setting_bridge_port[]; -extern NmcOutputField nmc_fields_setting_team[]; -extern NmcOutputField nmc_fields_setting_team_port[]; -extern NmcOutputField nmc_fields_setting_dcb[]; -extern NmcOutputField nmc_fields_setting_tun[]; -extern NmcOutputField nmc_fields_setting_ip_tunnel[]; -extern NmcOutputField nmc_fields_setting_macvlan[]; -extern NmcOutputField nmc_fields_setting_vxlan[]; - /* Available settings for 'connection show <con>' - profile part */ -static NmcOutputField nmc_fields_settings_names[] = { +NmcOutputField nmc_fields_settings_names[] = { SETTING_FIELD (NM_SETTING_CONNECTION_SETTING_NAME, nmc_fields_setting_connection + 1), /* 0 */ SETTING_FIELD (NM_SETTING_WIRED_SETTING_NAME, nmc_fields_setting_wired + 1), /* 1 */ SETTING_FIELD (NM_SETTING_802_1X_SETTING_NAME, nmc_fields_setting_8021X + 1), /* 2 */ @@ -233,7 +203,7 @@ static NmcOutputField nmc_fields_settings_names[] = { /* Active connection data */ /* Available fields for GENERAL group */ -static NmcOutputField nmc_fields_con_active_details_general[] = { +NmcOutputField nmc_fields_con_active_details_general[] = { {"GROUP", N_("GROUP")}, /* 0 */ {"NAME", N_("NAME")}, /* 1 */ {"UUID", N_("UUID")}, /* 2 */ @@ -255,7 +225,7 @@ static NmcOutputField nmc_fields_con_active_details_general[] = { /* IP group is handled by common.c */ /* Available fields for VPN group */ -static NmcOutputField nmc_fields_con_active_details_vpn[] = { +NmcOutputField nmc_fields_con_active_details_vpn[] = { {"GROUP", N_("GROUP")}, /* 0 */ {"TYPE", N_("TYPE")}, /* 1 */ {"USERNAME", N_("USERNAME")}, /* 2 */ @@ -274,7 +244,7 @@ extern NmcOutputField nmc_fields_dhcp4_config[]; extern NmcOutputField nmc_fields_dhcp6_config[]; /* Available fields for 'connection show <con>' - active part */ -static NmcOutputField nmc_fields_con_active_details_groups[] = { +NmcOutputField nmc_fields_con_active_details_groups[] = { {"GENERAL", N_("GENERAL"), 0, nmc_fields_con_active_details_general + 1}, /* 0 */ {"IP4", N_("IP4"), 0, nmc_fields_ip4_config + 1 }, /* 1 */ {"DHCP4", N_("DHCP4"), 0, nmc_fields_dhcp4_config + 1 }, /* 2 */ @@ -613,42 +583,6 @@ usage_connection_export (void) "The data are directed to standard output or to a file if a name is given.\n\n")); } -static gboolean -usage_connection_second_level (const char *cmd) -{ - gboolean ret = TRUE; - - if (matches (cmd, "show") == 0) - usage_connection_show (); - else if (matches (cmd, "up") == 0) - usage_connection_up (); - else if (matches (cmd, "down") == 0) - usage_connection_down (); - else if (matches (cmd, "add") == 0) - usage_connection_add (); - else if (matches (cmd, "modify") == 0) - usage_connection_modify (); - else if (matches (cmd, "clone") == 0) - usage_connection_clone (); - else if (matches (cmd, "edit") == 0) - usage_connection_edit (); - else if (matches (cmd, "delete") == 0) - usage_connection_delete (); - else if (matches (cmd, "monitor") == 0) - usage_connection_monitor (); - else if (matches (cmd, "reload") == 0) - usage_connection_reload (); - else if (matches (cmd, "load") == 0) - usage_connection_load (); - else if (matches (cmd, "import") == 0) - usage_connection_import (); - else if (matches (cmd, "export") == 0) - usage_connection_export (); - else - ret = FALSE; - return ret; -} - /* quit main loop */ static void quit (void) @@ -864,7 +798,8 @@ find_active_connection (const GPtrArray *active_cons, const GPtrArray *cons, const char *filter_type, const char *filter_val, - int *idx) + int *idx, + gboolean complete) { int i; int start = (idx && *idx > 0) ? *idx : 0; @@ -891,22 +826,43 @@ find_active_connection (const GPtrArray *active_cons, * type. If 'path' or 'apath' filter types are specified, comparison * against numeric index (in addition to the whole path) is allowed. */ - if ( ( (!filter_type || strcmp (filter_type, "id") == 0) - && strcmp (filter_val, id) == 0) - || ( (!filter_type || strcmp (filter_type, "uuid") == 0) - && strcmp (filter_val, uuid) == 0) - || ( (!filter_type || strcmp (filter_type, "path") == 0) - && (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0))) - || ( (!filter_type || strcmp (filter_type, "apath") == 0) - && (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)))) { - if (!idx) - return candidate; - if (found) { - *idx = i; - return found; - } - found = candidate; + if (!filter_type || strcmp (filter_type, "id") == 0) { + if (complete) + nmc_complete_strings (filter_val, id, NULL); + if (strcmp (filter_val, id) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "uuid") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, uuid, NULL); + if (strcmp (filter_val, uuid) == 0) + goto found; + } + + if (!filter_type || strcmp (filter_type, "path") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, path, filter_type ? path_num : NULL, NULL); + if (g_strcmp0 (filter_val, path) == 0 || (filter_type && g_strcmp0 (filter_val, path_num) == 0)) + goto found; } + + if (!filter_type || strcmp (filter_type, "apath") == 0) { + if (complete && (filter_type || *filter_val)) + nmc_complete_strings (filter_val, a_path, filter_type ? a_path_num : NULL, NULL); + if (g_strcmp0 (filter_val, a_path) == 0 || (filter_type && g_strcmp0 (filter_val, a_path_num) == 0)) + goto found; + } + + continue; +found: + if (!idx) + return candidate; + if (found) { + *idx = i; + return found; + } + found = candidate; } if (idx) @@ -1649,17 +1605,146 @@ get_invisible_active_connections (NmCli *nmc) return invisibles; } +static GArray * +parse_preferred_connection_order (const char *order, GError **error) +{ + char **strv, **iter; + const char *str; + GArray *order_arr; + NmcSortOrder val; + gboolean inverse, unique; + int i; + + strv = nmc_strsplit_set (order, ":", -1); + if (!strv || !*strv) { + g_set_error (error, NMCLI_ERROR, 0, + _("incorrect string '%s' of '--order' option"), order); + g_strfreev (strv); + return NULL; + } + + order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4); + for (iter = strv; iter && *iter; iter++) { + str = *iter; + inverse = FALSE; + if (str[0] == '-') + inverse = TRUE; + if (str[0] == '+' || str[0] == '-') + str++; + + if (matches (str, "active") == 0) + val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE; + else if (matches (str, "name") == 0) + val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME; + else if (matches (str, "type") == 0) + val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE; + else if (matches (str, "path") == 0) + val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH; + else { + g_array_unref (order_arr); + order_arr = NULL; + g_set_error (error, NMCLI_ERROR, 0, + _("incorrect item '%s' in '--order' option"), *iter); + break; + } + /* Check for duplicates and ignore them. */ + unique = TRUE; + for (i = 0; i < order_arr->len; i++) { + if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) { + unique = FALSE; + break; + } + } + + /* Value is ok and unique, add it to the array */ + if (unique) + g_array_append_val (order_arr, val); + } + + g_strfreev (strv); + return order_arr; +} + +static NMConnection * +get_connection (NmCli *nmc, int *argc, char ***argv, int *pos, GError **error) +{ + NMConnection *connection = NULL; + const char *selector = NULL; + + if (*argc == 0) { + g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("No connection specified")); + return NULL; + } + + if (*argc == 1 && nmc->complete) + nmc_complete_strings (**argv, "id", "uuid", "path", NULL); + + if ( strcmp (**argv, "id") == 0 + || strcmp (**argv, "uuid") == 0 + || strcmp (**argv, "path") == 0) { + selector = **argv; + if (next_arg (argc, argv) != 0) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("%s argument is missing"), selector); + return NULL; + } + } + + connection = nmc_find_connection (nmc->connections, selector, **argv, pos, + *argc == 1 && nmc->complete); + if (!connection) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, + _("unknown connection '%s'"), **argv); + } + + next_arg (argc, argv); + + return connection; +} + static NMCResultCode -do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, - const GArray *order, int argc, char **argv) +do_connections_show (NmCli *nmc, int argc, char **argv) { GError *err = NULL; char *profile_flds = NULL, *active_flds = NULL; GPtrArray *invisibles, *sorted_cons; + gboolean active_only = FALSE; + gboolean show_secrets = FALSE; + GArray *order = NULL; + int i; - /* Not (yet?) supported */ - if (nmc->complete) - goto finish; + /* check connection show options [--active] [--show-secrets] */ + for (i = 0; i < 3; i++) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "--active", "--show-secrets", + "--order", NULL); + } + + if (!active_only && nmc_arg_is_option (*argv, "active")) { + active_only = TRUE; + next_arg (&argc, &argv); + } else if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) { + /* --show-secrets is deprecated in favour of global --show-secrets */ + /* Keep it here for backwards compatibility */ + show_secrets = TRUE; + next_arg (&argc, &argv); + } else if (!order && nmc_arg_is_option (*argv, "order")) { + if (next_arg (&argc, &argv) != 0) { + g_set_error_literal (&err, NMCLI_ERROR, 0, + _("'--order' argument is missing")); + goto finish; + } + /* TODO: complete --order */ + order = parse_preferred_connection_order (*argv, &err); + if (err) + goto finish; + next_arg (&argc, &argv); + } else { + break; + } + } + show_secrets = nmc->show_secrets || show_secrets; if (argc == 0) { char *fields_str; @@ -1667,7 +1752,9 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, char *fields_common = NMC_FIELDS_CON_SHOW_COMMON; NmcOutputField *tmpl, *arr; size_t tmpl_len; - int i; + + if (nmc->complete) + goto finish; if (!nmc->required_fields || strcasecmp (nmc->required_fields, "common") == 0) fields_str = fields_common; @@ -1727,6 +1814,9 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, NMActiveConnection *acon = NULL; const char *selector = NULL; + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "id", "uuid", "path", "apath", NULL); + if ( strcmp (*argv, "id") == 0 || strcmp (*argv, "uuid") == 0 || strcmp (*argv, "path") == 0 @@ -1739,10 +1829,13 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, } } - /* Find connection by id, uuid, path or apath */ - con = nmc_find_connection (nmc->connections, selector, *argv, &pos); - if (!con) { - acon = find_active_connection (active_cons, nmc->connections, selector, *argv, NULL); + /* Try to find connection by id, uuid or path first */ + con = nmc_find_connection (nmc->connections, selector, *argv, &pos, + argc == 1 && nmc->complete); + if (!con && (!selector || strcmp (selector, "apath") == 0)) { + /* Try apath too */ + acon = find_active_connection (active_cons, nmc->connections, "apath", *argv, NULL, + argc == 1 && nmc->complete); if (acon) con = NM_CONNECTION (nm_active_connection_get_connection (acon)); } @@ -1767,6 +1860,11 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, continue; } + if (nmc->complete) { + next_arg (&argc, &argv); + continue; + } + /* Show an empty line between connections */ if (new_line) g_print ("\n"); @@ -1813,6 +1911,8 @@ finish: } g_free (profile_flds); g_free (active_flds); + if (order) + g_array_unref (order); return nmc->return_value; } @@ -2456,14 +2556,12 @@ do_connection_up (NmCli *nmc, int argc, char **argv) const char *ap = NULL; const char *nsp = NULL; const char *pwds = NULL; - GError *error = NULL; - const char *selector = NULL; - const char *name = NULL; - char *line = NULL; + gs_free_error GError *error = NULL; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; /* * Set default timeout for connection activation. * Activation can take quite a long time, use 90 seconds. @@ -2471,66 +2569,63 @@ do_connection_up (NmCli *nmc, int argc, char **argv) if (nmc->timeout == -1) nmc->timeout = 90; - if (argc == 0) { - if (nmc->ask) { - line = nmc_readline (PROMPT_CONNECTION); - name = line ? line : ""; - } - } else if (strcmp (*argv, "ifname") != 0) { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 0 && nmc->ask) { + char *line; - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; - } - name = *argv; - } - name = *argv; - next_arg (&argc, &argv); + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; } - if (name) { - connection = nmc_find_connection (nmc->connections, selector, name, NULL); + if (argc > 0 && strcmp (*argv, "ifname") != 0) { + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); if (!connection) { - g_string_printf (nmc->return_text, _("Error: Connection '%s' does not exist."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + return error->code; } } while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "ap", "passwd-file", NULL); + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_device (nmc->client, ifname, ap != NULL); } else if (strcmp (*argv, "ap") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ap = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_bssid (nmc->client, ifname, ap); } else if (strcmp (*argv, "passwd-file") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } + if (argc == 1 && nmc->complete) + nmc->return_value = NMC_RESULT_COMPLETE_FILE; + pwds = *argv; } - else { + else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2538,6 +2633,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv) argv++; } + if (nmc->complete) + return nmc->return_value; + /* Use nowait_flag instead of should_wait because exiting has to be postponed till * active_connection_state_cb() is called. That gives NM time to check our permissions * and we can follow activation progress. @@ -2548,18 +2646,14 @@ do_connection_up (NmCli *nmc, int argc, char **argv) if (!nmc_activate_connection (nmc, connection, ifname, ap, nsp, pwds, activate_connection_cb, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = error->code; - g_clear_error (&error); nmc->should_wait--; - goto error; + return error->code; } /* Start progress indication */ if (nmc->print_output == NMC_PRINT_PRETTY) progress_id = g_timeout_add (120, progress_cb, _("preparing")); -error: - g_free (line); return nmc->return_value; } @@ -2653,14 +2747,13 @@ do_connection_down (NmCli *nmc, int argc, char **argv) int arg_num = argc; int idx = 0; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - if (nmc->timeout == -1) nmc->timeout = 10; if (argc == 0) { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { char *line = nmc_readline (PROMPT_ACTIVE_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); @@ -2670,7 +2763,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (arg_num == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } @@ -2679,6 +2772,9 @@ do_connection_down (NmCli *nmc, int argc, char **argv) while (arg_num > 0) { const char *selector = NULL; + if (arg_num == 1) + nmc_complete_strings (*arg_ptr, "id", "uuid", "path", "apath", NULL); + if ( strcmp (*arg_ptr, "id") == 0 || strcmp (*arg_ptr, "uuid") == 0 || strcmp (*arg_ptr, "path") == 0 @@ -2688,11 +2784,12 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (next_arg (&arg_num, &arg_ptr) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } - active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx); + active = find_active_connection (active_cons, nmc->connections, selector, *arg_ptr, &idx, + arg_num == 1 && nmc->complete); if (active) { /* Check if the connection is unique. */ /* Calling down for the same connection repeatedly would result in @@ -2700,7 +2797,8 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (!g_slist_find (queue, active)) queue = g_slist_prepend (queue, g_object_ref (active)); } else { - g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr); + if (!nmc->complete) + g_printerr (_("Error: '%s' is not an active connection.\n"), *arg_ptr); g_string_printf (nmc->return_text, _("Error: not all active connections found.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; } @@ -2712,7 +2810,10 @@ do_connection_down (NmCli *nmc, int argc, char **argv) if (!queue) { g_string_printf (nmc->return_text, _("Error: no active connection provided.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; + } else if (nmc->complete) { + g_slist_free (queue); + goto finish; } queue = g_slist_reverse (queue); @@ -2738,7 +2839,7 @@ do_connection_down (NmCli *nmc, int argc, char **argv) nm_client_deactivate_connection (nmc->client, active, NULL, NULL); } -error: +finish: g_strfreev (arg_arr); return nmc->return_value; } @@ -4813,7 +4914,7 @@ uuid_display_hook (char **array, int len, int max_len) char *tmp; const char *id; for (i = 1; i <= len; i++) { - con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL); + con = nmc_find_connection (nmc_tab_completion.nmc->connections, "uuid", array[i], NULL, FALSE); id = con ? nm_connection_get_id (con) : NULL; if (id) { tmp = g_strdup_printf ("%s (%s)", array[i], id); @@ -7575,7 +7676,7 @@ editor_init_existing_connection (NMConnection *connection) } static NMCResultCode -do_connection_edit (NmCli *nmc, int argc, char **argv) +do_connection_edit_func (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMSettingConnection *s_con; @@ -7600,6 +7701,10 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) {"path", TRUE, &con_path, FALSE}, {NULL} }; + /* TODO: complete uuid, path or id */ + if (nmc->complete) + return nmc->return_value; + nmc->return_value = NMC_RESULT_SUCCESS; if (argc == 1) @@ -7643,7 +7748,7 @@ do_connection_edit (NmCli *nmc, int argc, char **argv) /* Existing connection */ NMConnection *found_con; - found_con = nmc_find_connection (nmc->connections, selector, con, NULL); + found_con = nmc_find_connection (nmc->connections, selector, con, NULL, FALSE); if (!found_con) { g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), con); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; @@ -7756,6 +7861,46 @@ error: return nmc->return_value; } +typedef struct { + NmCli *nmc; + int argc; + char **argv; +} NmcEditorThreadData; + +static GThread *editor_thread; +static NmcEditorThreadData editor_thread_data; + +/* + * We need to run do_connection_edit_func() in a thread so that + * glib main loop is not blocked and could receive and process D-Bus + * return messages. + */ +static gpointer +connection_editor_thread_func (gpointer data) +{ + NmcEditorThreadData *td = (NmcEditorThreadData *) data; + + /* run editor for editing/adding connections */ + td->nmc->return_value = do_connection_edit_func (td->nmc, td->argc, td->argv); + + /* quit glib main loop now that we are done with this thread */ + quit (); + + return NULL; +} + +static NMCResultCode +do_connection_edit (NmCli *nmc, int argc, char **argv) +{ + nmc->should_wait++; + editor_thread_data.nmc = nmc; + editor_thread_data.argc = argc; + editor_thread_data.argv = argv; + editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); + g_thread_unref (editor_thread); + + return nmc->return_value; +} static void modify_connection_cb (GObject *connection, @@ -7784,58 +7929,33 @@ modify_connection_cb (GObject *connection, static NMCResultCode do_connection_modify (NmCli *nmc, - gboolean temporary, int argc, char **argv) { NMConnection *connection = NULL; NMRemoteConnection *rc = NULL; - const char *name; - const char *selector = NULL; GError *error = NULL; + gboolean temporary = FALSE; - if (argc == 0) { - /* - * TODO(?) complete "uuid", "path", "id" or connection name. - * (if we ever will move this here from shell completion script) - if (nmc->complete) { - quit (); - */ - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { - - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - /* - * TODO(?): complete uuid, path or id - if (nmc->complete) { - quit (); - } - */ - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + if (argc && nmc_arg_is_option (*argv, "temporary")) { + if (nmc->complete) goto finish; - } - name = *argv; + temporary = TRUE; + next_arg (&argc, &argv); } - name = *argv; - connection = nmc_find_connection (nmc->connections, selector, name, NULL); + connection = get_connection (nmc, &argc, &argv, NULL, &error); if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } + rc = nm_client_get_connection_by_uuid (nmc->client, nm_connection_get_uuid (connection)); if (!rc) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); + g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), + nm_connection_get_uuid (connection)); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; } @@ -7900,74 +8020,64 @@ clone_connection_cb (GObject *client, } static NMCResultCode -do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) +do_connection_clone (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; NMConnection *new_connection = NULL; NMSettingConnection *s_con; CloneConnectionInfo *info; - const char *name; const char *new_name; - char *name_ask = NULL; char *new_name_ask = NULL; - const char *selector = NULL; char *uuid; + gboolean temporary = FALSE; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; + GError *error = NULL; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "temporary", NULL); - if (argc == 0) { - if (nmc->ask) { - name = name_ask = nmc_readline (PROMPT_CONNECTION); - new_name = new_name_ask = nmc_readline (_("New connection name: ")); - } else { - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } else { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 0 && nmc->ask) { + char *line; - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - name = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: <new name> argument is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - new_name = *argv; - if (next_arg (&argc, &argv) == 0) { - g_string_printf (nmc->return_text, _("Error: unexpected extra argument '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; + } else if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); } - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } - if (!new_name) { + + if (nmc->complete) + goto finish; + + if (next_arg (&argc, &argv) == 0) + new_name = *argv; + else if (nmc->ask) + new_name = new_name_ask = nmc_readline (_("New connection name: ")); + else { g_string_printf (nmc->return_text, _("Error: <new name> argument is missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } - connection = nmc_find_connection (nmc->connections, selector, name, NULL); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + if (next_arg (argc_ptr, argv_ptr) == 0) { + g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } @@ -8003,7 +8113,6 @@ do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) finish: if (new_connection) g_object_unref (new_connection); - g_free (name_ask); g_free (new_name_ask); return nmc->return_value; @@ -8039,43 +8148,32 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) int arg_num = argc; GString *invalid_cons = NULL; int pos = 0; - - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; + GError *error = NULL; if (nmc->timeout == -1) nmc->timeout = 10; if (argc == 0) { if (nmc->ask) { - char *line = nmc_readline (PROMPT_CONNECTIONS); + char *line; + + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + + line = nmc_readline ("%s: ", PROMPT_CONNECTIONS); nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); g_free (line); arg_ptr = arg_arr; } if (arg_num == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; } } while (arg_num > 0) { - const char *selector = NULL; - - if ( strcmp (*arg_ptr, "id") == 0 - || strcmp (*arg_ptr, "uuid") == 0 - || strcmp (*arg_ptr, "path") == 0) { - selector = *arg_ptr; - if (next_arg (&arg_num, &arg_ptr) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - - connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos); + connection = get_connection (nmc, &arg_num, &arg_ptr, &pos, &error); if (connection) { /* Check if the connection is unique. */ /* Calling delete for the same connection repeatedly would result in @@ -8083,9 +8181,15 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) if (!g_slist_find (queue, connection)) queue = g_slist_prepend (queue, g_object_ref (connection)); } else { - g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr); - g_string_printf (nmc->return_text, _("Error: not all active connections found.")); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + if (!nmc->complete) + g_printerr (_("Error: %s.\n"), error->message); + g_string_printf (nmc->return_text, _("Error: not all connections found.")); + nmc->return_value = error->code; + g_clear_error (&error); + + if (nmc->return_value != NMC_RESULT_ERROR_NOT_FOUND) + goto finish; + if (!invalid_cons) invalid_cons = g_string_new (NULL); g_string_append_printf (invalid_cons, "'%s', ", *arg_ptr); @@ -8097,9 +8201,12 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) } if (!queue) { - g_string_printf (nmc->return_text, _("Error: no connection provided.")); + g_string_printf (nmc->return_text, _("Error: No connection specified.")); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; goto finish; + } else if (nmc->complete) { + g_slist_free (queue); + goto finish; } queue = g_slist_reverse (queue); @@ -8176,15 +8283,15 @@ connection_removed (NMClient *client, NMRemoteConnection *con, NmCli *nmc) static NMCResultCode do_connection_monitor (NmCli *nmc, int argc, char **argv) { - - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; + GError *error = NULL; if (argc == 0) { /* No connections specified. Monitor all. */ int i; + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + nmc->connections = nm_client_get_connections (nmc->client); for (i = 0; i < nmc->connections->len; i++) connection_watch (nmc, g_ptr_array_index (nmc->connections, i)); @@ -8195,38 +8302,29 @@ do_connection_monitor (NmCli *nmc, int argc, char **argv) } else { /* Look up the specified connections and watch them. */ NMConnection *connection; - char **arg_ptr = argv; - int arg_num = argc; int pos = 0; do { - const char *selector = NULL; - - if ( strcmp (*arg_ptr, "id") == 0 - || strcmp (*arg_ptr, "uuid") == 0 - || strcmp (*arg_ptr, "path") == 0) { - selector = *arg_ptr; - if (next_arg (&arg_num, &arg_ptr) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), selector); - return NMC_RESULT_ERROR_USER_INPUT; - } - } - - connection = nmc_find_connection (nmc->connections, selector, *arg_ptr, &pos); - if (connection) { - connection_watch (nmc, connection); - } else { - g_printerr (_("Error: unknown connection '%s'\n"), *arg_ptr); + connection = get_connection (nmc, &argc, &argv, &pos, &error); + if (!connection) { + if (!nmc->complete) + g_printerr (_("Error: %s.\n"), error->message); g_string_printf (nmc->return_text, _("Error: not all connections found.")); - return NMC_RESULT_ERROR_NOT_FOUND; + return error->code; } - /* Take next argument (if there's no other connection of the same name) */ if (!pos) - next_arg (&arg_num, &arg_ptr); - } while (arg_num > 0); + next_arg (&argc, &argv); + + if (nmc->complete) + continue; + + connection_watch (nmc, connection); + } while (argc > 0); } + if (nmc->complete) + return nmc->return_value; g_signal_connect (nmc->client, NM_CLIENT_CONNECTION_REMOVED, G_CALLBACK (connection_removed), nmc); return NMC_RESULT_SUCCESS; @@ -8237,7 +8335,6 @@ do_connection_reload (NmCli *nmc, int argc, char **argv) { GError *error = NULL; - /* Not (yet?) supported */ if (nmc->complete) return nmc->return_value; @@ -8258,16 +8355,14 @@ do_connection_load (NmCli *nmc, int argc, char **argv) char **filenames, **failures = NULL; int i; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - if (argc == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - return nmc->return_value; + return NMC_RESULT_ERROR_USER_INPUT; } + if (nmc->complete) + return NMC_RESULT_COMPLETE_FILE; + filenames = g_new (char *, argc + 1); for (i = 0; i < argc; i++) filenames[i] = argv[i]; @@ -8296,7 +8391,7 @@ do_connection_load (NmCli *nmc, int argc, char **argv) #define PROMPT_IMPORT_FILE N_("File to import: ") static NMCResultCode -do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) +do_connection_import (NmCli *nmc, int argc, char **argv) { GError *error = NULL; const char *type = NULL, *filename = NULL; @@ -8305,12 +8400,12 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) NMConnection *connection = NULL; NMVpnEditorPlugin *plugin; gs_free char *service_type = NULL; - - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; + gboolean temporary = FALSE; if (argc == 0) { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { type_ask = nmc_readline (gettext (PROMPT_IMPORT_TYPE)); filename_ask = nmc_readline (gettext (PROMPT_IMPORT_FILE)); @@ -8324,6 +8419,13 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) } while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "temporary", "type", "file", NULL); + if (nmc_arg_is_option (*argv, "temporary")) { + temporary = TRUE; + next_arg (&argc, &argv); + } + if (strcmp (*argv, "type") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); @@ -8341,6 +8443,8 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } + if (argc == 1 && nmc->complete) + nmc->return_value = NMC_RESULT_COMPLETE_FILE; if (!filename) filename = *argv; else @@ -8355,6 +8459,9 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) argv++; } + if (nmc->complete) + goto finish; + if (!type) { g_string_printf (nmc->return_text, _("Error: 'type' argument is required.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -8415,64 +8522,50 @@ static NMCResultCode do_connection_export (NmCli *nmc, int argc, char **argv) { NMConnection *connection = NULL; - const char *name; const char *out_name = NULL; char *name_ask = NULL; char *out_name_ask = NULL; const char *path = NULL; - const char *selector = NULL; const char *type = NULL; NMVpnEditorPlugin *plugin; GError *error = NULL; char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX"; + char **arg_arr = NULL; + int arg_num; + char ***argv_ptr = &argv; + int *argc_ptr = &argc; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - - if (argc == 0) { - if (nmc->ask) { - name_ask = nmc_readline (PROMPT_VPN_CONNECTION); - name = name_ask = name_ask ? g_strstrip (name_ask) : NULL; - out_name = out_name_ask = nmc_readline (_("Output file name: ")); - } else { - g_string_printf (nmc->return_text, _("Error: No arguments provided.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } else { - if ( strcmp (*argv, "id") == 0 - || strcmp (*argv, "uuid") == 0 - || strcmp (*argv, "path") == 0) { + if (argc == 0 && nmc->ask) { + char *line; - selector = *argv; - if (next_arg (&argc, &argv) != 0) { - g_string_printf (nmc->return_text, _("Error: %s argument is missing."), - selector); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - } - name = *argv; - if (next_arg (&argc, &argv) == 0) - out_name = *argv; + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); - if (next_arg (&argc, &argv) == 0) { - g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } + line = nmc_readline ("%s: ", PROMPT_VPN_CONNECTION); + nmc_string_to_arg_array (line, NULL, TRUE, &arg_arr, &arg_num); + g_free (line); + argv_ptr = &arg_arr; + argc_ptr = &arg_num; } - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID is missing.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + connection = get_connection (nmc, argc_ptr, argv_ptr, NULL, &error); + if (!connection) { + g_string_printf (nmc->return_text, _("Error: %s."), error->message); + nmc->return_value = error->code; goto finish; } - connection = nmc_find_connection (nmc->connections, selector, name, NULL); - if (!connection) { - g_string_printf (nmc->return_text, _("Error: Unknown connection '%s'."), name); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; + + if (nmc->complete) + return nmc->return_value; + + if (next_arg (&argc, &argv) == 0) + out_name = *argv; + else if (nmc->ask) + out_name = out_name_ask = nmc_readline (_("Output file name: ")); + + if (next_arg (argc_ptr, argv_ptr) == 0) { + g_string_printf (nmc->return_text, _("Error: unknown extra argument: '%s'."), *argv); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } @@ -8537,35 +8630,6 @@ finish: return nmc->return_value; } - -typedef struct { - NmCli *nmc; - int argc; - char **argv; -} NmcEditorThreadData; - -static GThread *editor_thread; -static NmcEditorThreadData editor_thread_data; - -/* - * We need to run do_connection_edit() in a thread so that - * glib main loop is not blocked and could receive and process D-Bus - * return messages. - */ -static gpointer -connection_editor_thread_func (gpointer data) -{ - NmcEditorThreadData *td = (NmcEditorThreadData *) data; - - /* run editor for editing/adding connections */ - td->nmc->return_value = do_connection_edit (td->nmc, td->argc, td->argv); - - /* quit glib main loop now that we are done with this thread */ - quit (); - - return NULL; -} - static char * gen_func_connection_names (const char *text, int state) { @@ -8652,88 +8716,33 @@ nmcli_con_tab_completion (const char *text, int start, int end) return match_array; } -static GArray * -parse_preferred_connection_order (const char *order, GError **error) -{ - char **strv, **iter; - const char *str; - GArray *order_arr; - NmcSortOrder val; - gboolean inverse, unique; - int i; - - strv = nmc_strsplit_set (order, ":", -1); - if (!strv || !*strv) { - g_set_error (error, NMCLI_ERROR, 0, - _("incorrect string '%s' of '--order' option"), order); - g_strfreev (strv); - return NULL; - } - - order_arr = g_array_sized_new (FALSE, FALSE, sizeof (NmcSortOrder), 4); - for (iter = strv; iter && *iter; iter++) { - str = *iter; - inverse = FALSE; - if (str[0] == '-') - inverse = TRUE; - if (str[0] == '+' || str[0] == '-') - str++; - - if (matches (str, "active") == 0) - val = inverse ? NMC_SORT_ACTIVE_INV : NMC_SORT_ACTIVE; - else if (matches (str, "name") == 0) - val = inverse ? NMC_SORT_NAME_INV : NMC_SORT_NAME; - else if (matches (str, "type") == 0) - val = inverse ? NMC_SORT_TYPE_INV : NMC_SORT_TYPE; - else if (matches (str, "path") == 0) - val = inverse ? NMC_SORT_PATH_INV : NMC_SORT_PATH; - else { - g_array_unref (order_arr); - order_arr = NULL; - g_set_error (error, NMCLI_ERROR, 0, - _("incorrect item '%s' in '--order' option"), *iter); - break; - } - /* Check for duplicates and ignore them. */ - unique = TRUE; - for (i = 0; i < order_arr->len; i++) { - if (abs (g_array_index (order_arr, NmcSortOrder, i)) - abs (val) == 0) { - unique = FALSE; - break; - } - } - - /* Value is ok and unique, add it to the array */ - if (unique) - g_array_append_val (order_arr, val); - } - - g_strfreev (strv); - return order_arr; -} +static const NMCCommand connection_cmds[] = { + {"show", do_connections_show, usage_connection_show }, + {"up", do_connection_up, usage_connection_up }, + {"down", do_connection_down, usage_connection_down }, + {"add", do_connection_add, usage_connection_add }, + {"edit", do_connection_edit, usage_connection_edit }, + {"delete", do_connection_delete, usage_connection_delete }, + {"reload", do_connection_reload, usage_connection_reload }, + {"load", do_connection_load, usage_connection_load }, + {"modify", do_connection_modify, usage_connection_modify }, + {"clone", do_connection_clone, usage_connection_clone }, + {"import", do_connection_import, usage_connection_import }, + {"export", do_connection_export, usage_connection_export }, + {"monitor", do_connection_monitor, usage_connection_monitor }, + {NULL, do_connections_show, usage }, +}; /* Entry point function for connections-related commands: 'nmcli connection' */ NMCResultCode do_connections (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); /* Set completion function for 'nmcli con' */ rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_tab_completion; - /* Exit early on help */ - if (nmc_arg_is_help (*argv)) { - usage (); - return nmc->return_value; - } - if (argc != 0 && nmc_arg_is_help (*(argv+1))) { - if (usage_connection_second_level (*argv)) - return nmc->return_value; - } - /* Get NMClient object early */ nmc->get_client (nmc); @@ -8749,124 +8758,7 @@ do_connections (NmCli *nmc, int argc, char **argv) /* Get the connection list */ nmc->connections = nm_client_get_connections (nmc->client); - /* Now parse the command line and perform the required operation */ - if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) - goto opt_error; - nmc->return_value = do_connections_show (nmc, FALSE, FALSE, NULL, argc, argv); - } else { - if (matches (*argv, "show") == 0) { - gboolean active = FALSE; - gboolean show_secrets = FALSE; - GArray *order = NULL; - int i; - - next_arg (&argc, &argv); - /* check connection show options [--active] [--show-secrets] */ - for (i = 0; i < 3; i++) { - if (!active && nmc_arg_is_option (*argv, "active")) { - active = TRUE; - next_arg (&argc, &argv); - } - /* --show-secrets is deprecated in favour of global --show-secrets */ - /* Keep it here for backwards compatibility */ - if (!show_secrets && nmc_arg_is_option (*argv, "show-secrets")) { - show_secrets = TRUE; - next_arg (&argc, &argv); - } - if (!order && nmc_arg_is_option (*argv, "order")) { - if (next_arg (&argc, &argv) != 0) { - g_set_error_literal (&error, NMCLI_ERROR, 0, - _("'--order' argument is missing")); - goto opt_error; - } - order = parse_preferred_connection_order (*argv, &error); - if (error) - goto opt_error; - next_arg (&argc, &argv); - } - } - show_secrets = nmc->show_secrets || show_secrets; - nmc->return_value = do_connections_show (nmc, active, show_secrets, order, argc, argv); - if (order) - g_array_unref (order); - } else if (matches(*argv, "up") == 0) { - nmc->return_value = do_connection_up (nmc, argc-1, argv+1); - } else if (matches(*argv, "down") == 0) { - nmc->return_value = do_connection_down (nmc, argc-1, argv+1); - } else if (matches(*argv, "add") == 0) { - nmc->return_value = do_connection_add (nmc, argc-1, argv+1); - } else if (matches(*argv, "edit") == 0) { - /* edit with --complete? Should not happen */ - if (nmc->complete) - goto opt_error; - nmc->should_wait++; - editor_thread_data.nmc = nmc; - editor_thread_data.argc = argc - 1; - editor_thread_data.argv = argv + 1; - editor_thread = g_thread_new ("editor-thread", connection_editor_thread_func, &editor_thread_data); - g_thread_unref (editor_thread); - } else if (matches(*argv, "delete") == 0) { - nmc->return_value = do_connection_delete (nmc, argc-1, argv+1); - } else if (matches(*argv, "reload") == 0) { - nmc->return_value = do_connection_reload (nmc, argc-1, argv+1); - } else if (matches(*argv, "load") == 0) { - nmc->return_value = do_connection_load (nmc, argc-1, argv+1); - } else if (matches (*argv, "modify") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - if (nmc->complete) - goto opt_error; - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_modify (nmc, temporary, argc, argv); - } else if (matches (*argv, "clone") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - if (nmc->complete) - goto opt_error; - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_clone (nmc, temporary, argc, argv); - } else if (matches(*argv, "import") == 0) { - gboolean temporary = FALSE; - - next_arg (&argc, &argv); - if (nmc_arg_is_option (*argv, "temporary")) { - if (nmc->complete) - goto opt_error; - temporary = TRUE; - next_arg (&argc, &argv); - } - nmc->return_value = do_connection_import (nmc, temporary, argc, argv); - } else if (matches(*argv, "export") == 0) { - nmc->return_value = do_connection_export (nmc, argc-1, argv+1); - } else if (matches(*argv, "monitor") == 0) { - nmc->return_value = do_connection_monitor (nmc, argc-1, argv+1); - } else { - if (nmc->complete) - goto opt_error; - usage (); - g_string_printf (nmc->return_text, _("Error: '%s' is not valid 'connection' command."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } - - return nmc->return_value; - -opt_error: - if (!nmc->complete) { - 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, connection_cmds, *argv, argc, argv); } void diff --git a/clients/cli/connections.h b/clients/cli/connections.h index cb70a3d52e..c73178e24e 100644 --- a/clients/cli/connections.h +++ b/clients/cli/connections.h @@ -35,4 +35,10 @@ nmc_read_connection_properties (NmCli *nmc, void nmc_active_connection_state_to_color (NMActiveConnectionState state, NmcTermColor *color); +extern NmcOutputField nmc_fields_con_show[]; +extern NmcOutputField nmc_fields_settings_names[]; +extern NmcOutputField nmc_fields_con_active_details_general[]; +extern NmcOutputField nmc_fields_con_active_details_vpn[]; +extern NmcOutputField nmc_fields_con_active_details_groups[]; + #endif /* NMC_CONNECTIONS_H */ diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 7b043fd9ba..afa769f63c 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -37,7 +37,7 @@ #define PROMPT_INTERFACES _("Interface(s): ") /* Available fields for 'device status' */ -static NmcOutputField nmc_fields_dev_status[] = { +NmcOutputField nmc_fields_dev_status[] = { {"DEVICE", N_("DEVICE")}, /* 0 */ {"TYPE", N_("TYPE")}, /* 1 */ {"STATE", N_("STATE")}, /* 2 */ @@ -52,7 +52,7 @@ static NmcOutputField nmc_fields_dev_status[] = { /* Available fields for 'device show' - GENERAL part */ -static NmcOutputField nmc_fields_dev_show_general[] = { +NmcOutputField nmc_fields_dev_show_general[] = { {"NAME", N_("NAME")}, /* 0 */ {"DEVICE", N_("DEVICE")}, /* 1 */ {"TYPE", N_("TYPE")}, /* 2 */ @@ -86,7 +86,7 @@ static NmcOutputField nmc_fields_dev_show_general[] = { #define NMC_FIELDS_DEV_SHOW_GENERAL_COMMON "NAME,DEVICE,TYPE,VENDOR,PRODUCT,DRIVER,HWADDR,STATE" /* Available fields for 'device show' - CONNECTIONS part */ -static NmcOutputField nmc_fields_dev_show_connections[] = { +NmcOutputField nmc_fields_dev_show_connections[] = { {"NAME", N_("NAME")}, /* 0 */ {"AVAILABLE-CONNECTION-PATHS", N_("AVAILABLE-CONNECTION-PATHS")}, /* 1 */ {"AVAILABLE-CONNECTIONS", N_("AVAILABLE-CONNECTIONS")}, /* 2 */ @@ -96,7 +96,7 @@ static NmcOutputField nmc_fields_dev_show_connections[] = { #define NMC_FIELDS_DEV_SHOW_CONNECTIONS_COMMON "AVAILABLE-CONNECTION-PATHS,AVAILABLE-CONNECTIONS" /* Available fields for 'device show' - CAPABILITIES part */ -static NmcOutputField nmc_fields_dev_show_cap[] = { +NmcOutputField nmc_fields_dev_show_cap[] = { {"NAME", N_("NAME")}, /* 0 */ {"CARRIER-DETECT", N_("CARRIER-DETECT")}, /* 1 */ {"SPEED", N_("SPEED")}, /* 2 */ @@ -107,7 +107,7 @@ static NmcOutputField nmc_fields_dev_show_cap[] = { #define NMC_FIELDS_DEV_SHOW_CAP_COMMON "NAME,CARRIER-DETECT,SPEED,IS-SOFTWARE" /* Available fields for 'device show' - wired properties part */ -static NmcOutputField nmc_fields_dev_show_wired_prop[] = { +NmcOutputField nmc_fields_dev_show_wired_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"CARRIER", N_("CARRIER")}, /* 1 */ {"S390-SUBCHANNELS", N_("S390-SUBCHANNELS")}, /* 2 */ @@ -117,7 +117,7 @@ static NmcOutputField nmc_fields_dev_show_wired_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIRED_PROP_COMMON "NAME,CARRIER,S390-SUBCHANNELS" /* Available fields for 'device show' - wireless properties part */ -static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { +NmcOutputField nmc_fields_dev_show_wifi_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"WEP", N_("WEP")}, /* 1 */ {"WPA", N_("WPA")}, /* 2 */ @@ -134,7 +134,7 @@ static NmcOutputField nmc_fields_dev_show_wifi_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIFI_PROP_COMMON "NAME,WEP,WPA,WPA2,TKIP,CCMP,AP,ADHOC" /* Available fields for 'device show' - wimax properties part */ -static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { +NmcOutputField nmc_fields_dev_show_wimax_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"CTR-FREQ", N_("CTR-FREQ")}, /* 1 */ {"RSSI", N_("RSSI")}, /* 2 */ @@ -147,7 +147,7 @@ static NmcOutputField nmc_fields_dev_show_wimax_prop[] = { #define NMC_FIELDS_DEV_SHOW_WIMAX_PROP_COMMON "NAME,CTR-FREQ,RSSI,CINR,TX-POW,BSID" /* Available fields for 'device wifi list' */ -static NmcOutputField nmc_fields_dev_wifi_list[] = { +NmcOutputField nmc_fields_dev_wifi_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"SSID", N_("SSID")}, /* 1 */ {"SSID-HEX", N_("SSID-HEX")}, /* 2 */ @@ -173,7 +173,7 @@ static NmcOutputField nmc_fields_dev_wifi_list[] = { #define NMC_FIELDS_DEV_WIFI_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIFI_LIST_COMMON /* Available fields for 'device wimax list' */ -static NmcOutputField nmc_fields_dev_wimax_list[] = { +NmcOutputField nmc_fields_dev_wimax_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"NSP", N_("NSP")}, /* 1 */ {"SIGNAL", N_("SIGNAL")}, /* 2 */ @@ -188,7 +188,7 @@ static NmcOutputField nmc_fields_dev_wimax_list[] = { #define NMC_FIELDS_DEV_WIMAX_LIST_FOR_DEV_LIST "NAME,"NMC_FIELDS_DEV_WIMAX_LIST_COMMON /* Available fields for 'device show' - BOND, BRIDGE part */ -static NmcOutputField nmc_fields_dev_show_master_prop[] = { +NmcOutputField nmc_fields_dev_show_master_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"SLAVES", N_("SLAVES")}, /* 1 */ {NULL, NULL} @@ -197,7 +197,7 @@ static NmcOutputField nmc_fields_dev_show_master_prop[] = { #define NMC_FIELDS_DEV_SHOW_MASTER_PROP_COMMON "NAME,SLAVES" /* Available fields for 'device show' - TEAM part */ -static NmcOutputField nmc_fields_dev_show_team_prop[] = { +NmcOutputField nmc_fields_dev_show_team_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"SLAVES", N_("SLAVES")}, /* 1 */ {"CONFIG", N_("CONFIG")}, /* 2 */ @@ -207,7 +207,7 @@ static NmcOutputField nmc_fields_dev_show_team_prop[] = { #define NMC_FIELDS_DEV_SHOW_TEAM_PROP_COMMON "NAME,SLAVES,CONFIG" /* Available fields for 'device show' - VLAN part */ -static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { +NmcOutputField nmc_fields_dev_show_vlan_prop[] = { {"NAME", N_("NAME")}, /* 0 */ {"PARENT", N_("PARENT")}, /* 1 */ {"ID", N_("ID")}, /* 2 */ @@ -217,7 +217,7 @@ static NmcOutputField nmc_fields_dev_show_vlan_prop[] = { #define NMC_FIELDS_DEV_SHOW_VLAN_PROP_COMMON "NAME,PARENT,ID" /* Available fields for 'device show' - BLUETOOTH part */ -static NmcOutputField nmc_fields_dev_show_bluetooth[] = { +NmcOutputField nmc_fields_dev_show_bluetooth[] = { {"NAME", N_("NAME")}, /* 0 */ {"CAPABILITIES", N_("CAPABILITIES")}, /* 1 */ {NULL, NULL} @@ -232,7 +232,7 @@ extern NmcOutputField nmc_fields_dhcp4_config[]; extern NmcOutputField nmc_fields_dhcp6_config[]; /* Available sections for 'device show' */ -static NmcOutputField nmc_fields_dev_show_sections[] = { +NmcOutputField nmc_fields_dev_show_sections[] = { {"GENERAL", N_("GENERAL"), 0, nmc_fields_dev_show_general + 1 }, /* 0 */ {"CAPABILITIES", N_("CAPABILITIES"), 0, nmc_fields_dev_show_cap + 1 }, /* 1 */ {"WIFI-PROPERTIES", N_("WIFI-PROPERTIES"), 0, nmc_fields_dev_show_wifi_prop + 1 }, /* 2 */ @@ -258,7 +258,7 @@ static NmcOutputField nmc_fields_dev_show_sections[] = { "GENERAL.CONNECTION,GENERAL.CON-PATH,WIRED-PROPERTIES,IP4,IP6" /* Available fields for 'device lldp' */ -static NmcOutputField nmc_fields_dev_lldp_list[] = { +NmcOutputField nmc_fields_dev_lldp_list[] = { {"NAME", N_("NAME")}, /* 0 */ {"DEVICE", N_("DEVICE")}, /* 1 */ {"CHASSIS-ID", N_("CHASSIS-ID")}, /* 2 */ @@ -538,18 +538,30 @@ nmc_get_devices_sorted (NMClient *client) } static void -complete_device (NMDevice **devices, const char *prefix) +complete_device (NMDevice **devices, const char *prefix, gboolean wifi_only) { int i; for (i = 0; devices[i]; i++) { const char *iface = nm_device_get_iface (devices[i]); + if (wifi_only && !NM_IS_DEVICE_WIFI (devices[i])) + continue; + if (g_str_has_prefix (iface, prefix)) g_print ("%s\n", iface); } } +void +nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only) +{ + gs_free NMDevice **devices = NULL; + + devices = nmc_get_devices_sorted (client); + complete_device (devices, prefix, wifi_only); +} + static GSList * get_device_list (NmCli *nmc, int argc, char **argv) { @@ -578,7 +590,7 @@ get_device_list (NmCli *nmc, int argc, char **argv) devices = nmc_get_devices_sorted (nmc->client); while (arg_num > 0) { if (arg_num == 1 && nmc->complete) - complete_device (devices, *arg_ptr); + complete_device (devices, *arg_ptr, FALSE); device = NULL; for (i = 0; devices[i]; i++) { @@ -640,7 +652,7 @@ get_device (NmCli *nmc, int *argc, char ***argv, GError **error) } if (nmc->complete && !*argc) - complete_device (devices, ifname); + complete_device (devices, ifname, FALSE); if (devices[i] == NULL) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_NOT_FOUND, @@ -2484,6 +2496,118 @@ show_access_point_info (NMDevice *device, NmCli *nmc) g_free (info); } +/* + * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, + * the first Wi-Fi device is returned. 'idx' parameter is updated to the point + * where the function finished so that the function can be called repeatedly + * to get next matching device. + * Returns: found device or NULL + */ +static NMDevice * +find_wifi_device_by_iface (NMDevice **devices, const char *iface, int *idx) +{ + int i; + + for (i = idx ? *idx : 0; devices[i]; i++) { + const char *dev_iface = nm_device_get_iface (devices[i]); + + if (!NM_IS_DEVICE_WIFI (devices[i])) + continue; + + if (iface) { + /* If a iface was specified then use it. */ + if (g_strcmp0 (dev_iface, iface) == 0) + break; + } else { + /* Else return the first Wi-Fi device. */ + break; + } + } + + if (idx) + *idx = i + 1; + return devices[i]; +} + +/* + * Find AP on 'device' according to 'bssid' or 'ssid' parameter. + * Returns: found AP or NULL + */ +static NMAccessPoint * +find_ap_on_device (NMDevice *device, const char *bssid, const char *ssid, gboolean complete) +{ + const GPtrArray *aps; + NMAccessPoint *ap = NULL; + int i; + + g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); + for (i = 0; i < aps->len; i++) { + NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); + + if (bssid) { + /* Parameter is BSSID */ + const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); + + /* Compare BSSIDs */ + if (complete) { + if (g_str_has_prefix (candidate_bssid, bssid)) + g_print ("%s\n", candidate_bssid); + } else if (strcmp (bssid, candidate_bssid) == 0) { + ap = candidate_ap; + break; + } + } + + if (ssid) { + /* Parameter is SSID */ + GBytes *candidate_ssid; + char *ssid_tmp; + + candidate_ssid = nm_access_point_get_ssid (candidate_ap); + if (!candidate_ssid) + continue; + + ssid_tmp = nm_utils_ssid_to_utf8 (g_bytes_get_data (candidate_ssid, NULL), + g_bytes_get_size (candidate_ssid)); + + /* Compare SSIDs */ + if (complete) { + if (g_str_has_prefix (ssid_tmp, ssid)) + g_print ("%s\n", ssid_tmp); + } else if (strcmp (ssid, ssid_tmp) == 0) { + ap = candidate_ap; + g_free (ssid_tmp); + break; + } + g_free (ssid_tmp); + } + } + + return ap; +} + +static void +complete_aps (NMDevice **devices, const char *ifname, + const char *bssid_prefix, const char *ssid_prefix) +{ + int devices_idx = 0; + NMDevice *device; + + while ((device = find_wifi_device_by_iface (devices, ifname, &devices_idx))) + find_ap_on_device (device, bssid_prefix, ssid_prefix, TRUE); +} + +void +nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_prefix) +{ + gs_free NMDevice **devices = NULL; + + devices = nmc_get_devices_sorted (client); + complete_aps (devices, ifname, bssid_prefix, NULL); +} + static NMCResultCode do_device_wifi_list (NmCli *nmc, int argc, char **argv) { @@ -2492,7 +2616,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) NMAccessPoint *ap = NULL; const char *ifname = NULL; const char *bssid_user = NULL; - NMDevice **devices = NULL; + gs_free NMDevice **devices = NULL; const GPtrArray *aps; APInfo *info; int i, j; @@ -2503,27 +2627,29 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) size_t tmpl_len; const char *base_hdr = _("Wi-Fi scan list"); - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; + devices = nmc_get_devices_sorted (nmc->client); while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "bssid", NULL); + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "bssid") == 0 || strcmp (*argv, "hwaddr") == 0) { /* hwaddr is deprecated and will be removed later */ if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } bssid_user = *argv; - } else { + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, bssid_user, NULL); + } else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2545,28 +2671,18 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (error) { g_string_printf (nmc->return_text, _("Error: 'device wifi': %s"), error->message); g_error_free (error); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } - devices = nmc_get_devices_sorted (nmc->client); - if (ifname) { - /* Device specified - list only APs of this interface */ - for (i = 0; devices[i]; i++) { - NMDevice *candidate = devices[i]; - const char *dev_iface = nm_device_get_iface (candidate); + if (nmc->complete) + return nmc->return_value; - if (!g_strcmp0 (dev_iface, ifname)) { - device = candidate; - break; - } - } + if (ifname) { + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { g_string_printf (nmc->return_text, _("Error: Device '%s' not found."), ifname); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } - /* Main header name */ nmc->print_fields.header_name = (char *) construct_header_name (base_hdr, ifname); @@ -2587,8 +2703,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (!ap) { g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } /* Add headers (field names) */ arr = nmc_dup_fields_array (tmpl, tmpl_len, NMC_OF_FLAG_MAIN_HEADER_ADD | NMC_OF_FLAG_FIELD_NAMES); @@ -2619,8 +2734,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) _("Error: Device '%s' is not a Wi-Fi device."), ifname); } - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } } else { gboolean empty_line = FALSE; @@ -2671,8 +2785,7 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) if (!ap) { g_string_printf (nmc->return_text, _("Error: Access point with bssid '%s' not found."), bssid_user); - nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + return NMC_RESULT_ERROR_NOT_FOUND; } } else { for (i = 0; devices[i]; i++) { @@ -2693,101 +2806,9 @@ do_device_wifi_list (NmCli *nmc, int argc, char **argv) } } -error: - g_free (devices); return nmc->return_value; } -/* - * Find a Wi-Fi device with 'iface' in 'devices' array. If 'iface' is NULL, - * the first Wi-Fi device is returned. 'idx' parameter is updated to the point - * where the function finished so that the function can be called repeatedly - * to get next matching device. - * Returns: found device or NULL - */ -static NMDevice * -find_wifi_device_by_iface (const GPtrArray *devices, const char *iface, int *idx) -{ - NMDevice *device = NULL; - int i; - - for (i = *idx; i < devices->len; i++) { - NMDevice *candidate = g_ptr_array_index (devices, i); - const char *dev_iface = nm_device_get_iface (candidate); - - if (!NM_IS_DEVICE_WIFI (candidate)) - continue; - - if (iface) { - /* If a iface was specified then use it. */ - if (g_strcmp0 (dev_iface, iface) == 0) { - device = candidate; - break; - } - } else { - /* Else return the first Wi-Fi device. */ - device = candidate; - break; - } - } - - *idx = i + 1; - return device; -} - -/* - * Find AP on 'device' according to 'bssid' or 'ssid' parameter. - * Returns: found AP or NULL - */ -static NMAccessPoint * -find_ap_on_device (NMDevice *device, GByteArray *bssid, const char *ssid) -{ - const GPtrArray *aps; - NMAccessPoint *ap = NULL; - int i; - - g_return_val_if_fail (NM_IS_DEVICE_WIFI (device), NULL); - g_return_val_if_fail ((bssid && !ssid) || (!bssid && ssid), NULL); - - aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (device)); - for (i = 0; i < aps->len; i++) { - NMAccessPoint *candidate_ap = g_ptr_array_index (aps, i); - - if (ssid) { - /* Parameter is SSID */ - GBytes *candidate_ssid; - - candidate_ssid = nm_access_point_get_ssid (candidate_ap); - if (candidate_ssid) { - char *ssid_tmp = nm_utils_ssid_to_utf8 (g_bytes_get_data (candidate_ssid, NULL), - g_bytes_get_size (candidate_ssid)); - - /* Compare SSIDs */ - if (strcmp (ssid, ssid_tmp) == 0) { - ap = candidate_ap; - g_free (ssid_tmp); - break; - } - g_free (ssid_tmp); - } - } else if (bssid) { - /* Parameter is BSSID */ - const char *candidate_bssid = nm_access_point_get_bssid (candidate_ap); - char *bssid_up = nm_utils_hwaddr_ntoa (bssid->data, bssid->len); - - /* Compare BSSIDs */ - if (strcmp (bssid_up, candidate_bssid) == 0) { - ap = candidate_ap; - g_free (bssid_up); - break; - } - g_free (bssid_up); - } - } - - return ap; -} - static NMCResultCode do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) { @@ -2811,27 +2832,31 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) gboolean wep_passphrase = FALSE; GByteArray *bssid1_arr = NULL; GByteArray *bssid2_arr = NULL; - const GPtrArray *devices; + gs_free NMDevice **devices = NULL; int devices_idx; char *ssid_ask = NULL; char *passwd_ask = NULL; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - /* Set default timeout waiting for operation completion. */ if (nmc->timeout == -1) nmc->timeout = 90; + devices = nmc_get_devices_sorted (nmc->client); + /* Get the first compulsory argument (SSID or BSSID) */ if (argc > 0) { param_user = *argv; bssid1_arr = nm_utils_hwaddr_atoba (param_user, ETH_ALEN); + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, param_user, param_user); + argc--; argv++; } else { + /* nmc_do_cmd() should not call this with argc=0. */ + g_assert (!nmc->complete); + if (nmc->ask) { ssid_ask = nmc_readline (_("SSID or BSSID: ")); param_user = ssid_ask ? ssid_ask : ""; @@ -2840,46 +2865,56 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (!ssid_ask) { g_string_printf (nmc->return_text, _("Error: SSID or BSSID are missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } /* Get the rest of the parameters */ while (argc > 0) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "ifname", "bssid", "password", "wep-key-type", + "name", "private", "hidden", NULL); + } + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } ifname = *argv; + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "bssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } bssid = *argv; + if (argc == 1 && nmc->complete) + complete_aps (devices, NULL, bssid, NULL); bssid2_arr = nm_utils_hwaddr_atoba (bssid, ETH_ALEN); if (!bssid2_arr) { g_string_printf (nmc->return_text, _("Error: bssid argument value '%s' is not a valid BSSID."), bssid); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } else if (strcmp (*argv, "password") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } password = *argv; } else if (strcmp (*argv, "wep-key-type") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "key", "phrase", NULL); if (strcmp (*argv, "key") == 0) wep_passphrase = FALSE; else if (strcmp (*argv, "phrase") == 0) @@ -2889,13 +2924,13 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) _("Error: wep-key-type argument value '%s' is invalid, use 'key' or 'phrase'."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } } else if (strcmp (*argv, "name") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } con_name = *argv; } else if (strcmp (*argv, "private") == 0) { @@ -2903,28 +2938,32 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); if (!nmc_string_to_bool (*argv, &private, &err_tmp)) { g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&err_tmp); - goto error; + goto finish; } } else if (strcmp (*argv, "hidden") == 0) { GError *err_tmp = NULL; if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } + if (argc == 1 && nmc->complete) + nmc_complete_bool (*argv); if (!nmc_string_to_bool (*argv, &hidden, &err_tmp)) { g_string_printf (nmc->return_text, _("Error: %s: %s."), *(argv-1), err_tmp->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; g_clear_error (&err_tmp); - goto error; + goto finish; } - } else { + } else if (!nmc->complete) { g_printerr (_("Unknown parameter: %s\n"), *argv); } @@ -2932,21 +2971,22 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) argv++; } + if (nmc->complete) + goto finish; + /* Verify SSID/BSSID parameters */ if (bssid1_arr && bssid2_arr && memcmp (bssid1_arr->data, bssid2_arr->data, ETH_ALEN)) { g_string_printf (nmc->return_text, _("Error: BSSID to connect to (%s) differs from bssid argument (%s)."), param_user, bssid); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } if (!bssid1_arr && strlen (param_user) > 32) { g_string_printf (nmc->return_text, _("Error: Parameter '%s' is neither SSID nor BSSID."), param_user); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } - devices = nm_client_get_devices (nmc->client); - /* Find a device to activate the connection on */ devices_idx = 0; device = find_wifi_device_by_iface (devices, ifname, &devices_idx); @@ -2957,7 +2997,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + goto finish; } /* For hidden SSID first scan it so that NM learns about the AP */ @@ -2979,18 +3019,20 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) scan_err->message); g_clear_error (&scan_err); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; } } /* Find an AP to connect to */ - ap = find_ap_on_device (device, bssid1_arr, bssid1_arr ? NULL : param_user); + ap = find_ap_on_device (device, bssid1_arr ? param_user : NULL, + bssid1_arr ? NULL : param_user, FALSE); if (!ap && !ifname) { NMDevice *dev; /* AP not found, ifname was not specified, so try finding the AP on another device. */ while ((dev = find_wifi_device_by_iface (devices, NULL, &devices_idx)) != NULL) { - ap = find_ap_on_device (dev, bssid1_arr, bssid1_arr ? NULL : param_user); + ap = find_ap_on_device (dev, bssid1_arr ? param_user : NULL, + bssid1_arr ? NULL : param_user, FALSE); if (ap) { device = dev; break; @@ -3004,7 +3046,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No access point with BSSID '%s' found."), param_user); nmc->return_value = NMC_RESULT_ERROR_NOT_FOUND; - goto error; + goto finish; } /* If there are some connection data from user, create a connection and @@ -3107,7 +3149,7 @@ do_device_wifi_connect_network (NmCli *nmc, int argc, char **argv) add_and_activate_cb, info); -error: +finish: if (bssid1_arr) g_byte_array_free (bssid1_arr, TRUE); if (bssid2_arr) @@ -3260,8 +3302,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) const char *password = NULL; gboolean show_password = FALSE; NMDevice *device = NULL; - int devices_idx; - const GPtrArray *devices; + gs_free NMDevice **devices = NULL; NMDeviceWifiCapabilities caps; NMConnection *connection = NULL; NMSettingConnection *s_con; @@ -3271,66 +3312,65 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) GBytes *ssid_bytes; GError *error = NULL; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - /* Set default timeout waiting for operation completion. */ if (nmc->timeout == -1) nmc->timeout = 60; + devices = nmc_get_devices_sorted (nmc->client); + while (argc > 0) { + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "ifname", "con-name", "ssid", "band", + "channel", "password", NULL); + } + if (strcmp (*argv, "ifname") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ifname = *argv; + if (argc == 1 && nmc->complete) + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "con-name") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } con_name = *argv; } else if (strcmp (*argv, "ssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } ssid = *argv; if (strlen (ssid) > 32) { g_string_printf (nmc->return_text, _("Error: ssid is too long.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } else if (strcmp (*argv, "band") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } band = *argv; + if (argc == 1 && nmc->complete) + nmc_complete_strings (band, "a", "bg", NULL); if (strcmp (band, "a") && strcmp (band, "bg")) { g_string_printf (nmc->return_text, _("Error: band argument value '%s' is invalid; use 'a' or 'bg'."), band); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } else if (strcmp (*argv, "channel") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } channel = *argv; } else if (strcmp (*argv, "password") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } password = *argv; /* --show-password is deprecated in favour of global --show-secrets option */ @@ -3339,8 +3379,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) show_password = TRUE; } else { g_string_printf (nmc->return_text, _("Error: Unknown parameter %s."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } argc--; @@ -3348,6 +3387,9 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) } show_password = nmc->show_secrets || show_password; + if (nmc->complete) + return nmc->return_value; + /* Verify band and channel parameters */ if (!channel) { if (g_strcmp0 (band, "bg") == 0) @@ -3358,30 +3400,25 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) if (channel) { if (!band) { g_string_printf (nmc->return_text, _("Error: channel requires band too.")); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } if ( !nmc_string_to_uint (channel, TRUE, 1, 5825, &channel_int) || !nm_utils_wifi_is_channel_valid (channel_int, band)) { g_string_printf (nmc->return_text, _("Error: channel '%s' not valid for band '%s'."), channel, band); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + return NMC_RESULT_ERROR_USER_INPUT; } } /* Find Wi-Fi device. When no ifname is provided, the first Wi-Fi is used. */ - devices = nm_client_get_devices (nmc->client); - devices_idx = 0; - device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { if (ifname) g_string_printf (nmc->return_text, _("Error: Device '%s' is not a Wi-Fi device."), ifname); else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } /* Check device supported mode */ @@ -3393,8 +3430,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) else { g_string_printf (nmc->return_text, _("Error: Device '%s' supports neither AP nor Ad-Hoc mode."), nm_device_get_iface (device)); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } /* Create a connection with appropriate parameters */ @@ -3427,9 +3463,8 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) if (!set_wireless_security_for_hotspot (s_wsec, wifi_mode, caps, password, show_password, &error)) { g_object_unref (connection); g_string_printf (nmc->return_text, _("Error: Invalid 'password': %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; g_clear_error (&error); - goto error; + return NMC_RESULT_ERROR_UNKNOWN; } s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); @@ -3457,7 +3492,6 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) add_and_activate_cb, info); -error: return nmc->return_value; } @@ -3482,51 +3516,53 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) NMDevice *device; const char *ifname = NULL; GPtrArray *ssids; - const GPtrArray *devices; - int devices_idx; + gs_free NMDevice **devices = NULL; GVariantBuilder builder, array_builder; GVariant *options; const char *ssid; int i; - /* Not (yet?) supported */ - if (nmc->complete) - return nmc->return_value; - ssids = g_ptr_array_new (); + devices = nmc_get_devices_sorted (nmc->client); /* Get the parameters */ while (argc > 0) { + if (argc == 1 && nmc->complete) + nmc_complete_strings (*argv, "ifname", "ssid", NULL); + if (strcmp (*argv, "ifname") == 0) { if (ifname) { g_string_printf (nmc->return_text, _("Error: '%s' cannot repeat."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } ifname = *argv; + if (argc == 1 && nmc->complete) + complete_device (devices, ifname, TRUE); } else if (strcmp (*argv, "ssid") == 0) { if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: %s argument is missing."), *(argv-1)); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto error; + goto finish; } g_ptr_array_add (ssids, *argv); - } else + } else if (!nmc->complete) g_printerr (_("Unknown parameter: %s\n"), *argv); argc--; argv++; } + if (nmc->complete) + goto finish; + /* Find Wi-Fi device to scan on. When no ifname is provided, the first Wi-Fi is used. */ - devices = nm_client_get_devices (nmc->client); - devices_idx = 0; - device = find_wifi_device_by_iface (devices, ifname, &devices_idx); + device = find_wifi_device_by_iface (devices, ifname, NULL); if (!device) { if (ifname) @@ -3534,7 +3570,7 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) else g_string_printf (nmc->return_text, _("Error: No Wi-Fi device found.")); nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto error; + goto finish; } @@ -3557,10 +3593,8 @@ do_device_wifi_rescan (NmCli *nmc, int argc, char **argv) nm_device_wifi_request_scan_async (NM_DEVICE_WIFI (device), NULL, request_rescan_cb, nmc); - g_ptr_array_free (ssids, FALSE); - return nmc->return_value; -error: nmc->should_wait++; +finish: g_ptr_array_free (ssids, FALSE); return nmc->return_value; } diff --git a/clients/cli/devices.h b/clients/cli/devices.h index 01ff4819ea..672944beda 100644 --- a/clients/cli/devices.h +++ b/clients/cli/devices.h @@ -24,10 +24,30 @@ NMCResultCode do_devices (NmCli *nmc, int argc, char **argv); +void nmc_complete_device (NMClient *client, const char *prefix, gboolean wifi_only); + +void nmc_complete_bssid (NMClient *client, const char *ifname, const char *bssid_prefix); + void monitor_devices (NmCli *nmc); NMDevice ** nmc_get_devices_sorted (NMClient *client); void nmc_device_state_to_color (NMDeviceState state, NmcTermColor *color, NmcTermFormat *color_fmt); +extern NmcOutputField nmc_fields_dev_status[]; +extern NmcOutputField nmc_fields_dev_show_general[]; +extern NmcOutputField nmc_fields_dev_show_connections[]; +extern NmcOutputField nmc_fields_dev_show_cap[]; +extern NmcOutputField nmc_fields_dev_show_wired_prop[]; +extern NmcOutputField nmc_fields_dev_show_wifi_prop[]; +extern NmcOutputField nmc_fields_dev_show_wimax_prop[]; +extern NmcOutputField nmc_fields_dev_wifi_list[]; +extern NmcOutputField nmc_fields_dev_wimax_list[]; +extern NmcOutputField nmc_fields_dev_show_master_prop[]; +extern NmcOutputField nmc_fields_dev_show_team_prop[]; +extern NmcOutputField nmc_fields_dev_show_vlan_prop[]; +extern NmcOutputField nmc_fields_dev_show_bluetooth[]; +extern NmcOutputField nmc_fields_dev_show_sections[]; +extern NmcOutputField nmc_fields_dev_lldp_list[]; + #endif /* NMC_DEVICES_H */ diff --git a/clients/cli/general.c b/clients/cli/general.c index 739cdf8536..b919ccb549 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -24,6 +24,7 @@ #include "polkit-agent.h" #include "utils.h" +#include "common.h" #include "general.h" #include "common.h" #include "nm-common-macros.h" @@ -389,6 +390,23 @@ show_nm_status (NmCli *nmc, const char *pretty_header_name, const char *print_fl return TRUE; } +static NMCResultCode +do_general_status (NmCli *nmc, int argc, char **argv) +{ + gs_free_error 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); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + + show_nm_status (nmc, NULL, NULL); + return nmc->return_value; +} + static const char * permission_to_string (NMClientPermission perm) { @@ -493,6 +511,23 @@ show_nm_permissions (NmCli *nmc) return TRUE; } +static NMCResultCode +do_general_permissions (NmCli *nmc, int argc, char **argv) +{ + gs_free_error 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); + return NMC_RESULT_ERROR_USER_INPUT; + } + + if (nmc->complete) + return nmc->return_value; + + show_nm_permissions (nmc); + return nmc->return_value; +} + static gboolean show_general_logging (NmCli *nmc) { @@ -546,6 +581,51 @@ show_general_logging (NmCli *nmc) return TRUE; } +static NMCResultCode +do_general_logging (NmCli *nmc, int argc, char **argv) +{ + gs_free_error GError *error = NULL; + + if (argc == 0) { + 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->complete) + return nmc->return_value; + + show_general_logging (nmc); + } else { + /* arguments provided -> set logging level and domains */ + const char *level = NULL; + const char *domains = NULL; + nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, + {"domains", TRUE, &domains, TRUE}, + {NULL} }; + + /* TODO: nmc_parse_args needs completion */ + if (nmc->complete) + return nmc->return_value; + + if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { + g_string_assign (nmc->return_text, error->message); + return error->code; + } + + nmc->get_client (nmc); /* create NMClient */ + nm_client_set_logging (nmc->client, level, domains, &error); + if (error) { + g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), + error->message); + return NMC_RESULT_ERROR_UNKNOWN; + } + } + + return nmc->return_value; +} + static void save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) { @@ -562,129 +642,55 @@ save_hostname_cb (GObject *object, GAsyncResult *result, gpointer user_data) quit (); } -/* - * Entry point function for general operations 'nmcli general' - */ -NMCResultCode -do_general (NmCli *nmc, int argc, char **argv) +static NMCResultCode +do_general_hostname (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; - - /* Register polkit agent */ - nmc_start_polkit_agent_start_try (nmc); + if (nmc->complete) + return nmc->return_value; if (argc == 0) { - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_status (nmc, NULL, NULL); - } + /* no arguments -> get hostname */ + char *hostname = NULL; + + nmc->get_client (nmc); /* create NMClient */ + g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL); + if (hostname) + g_print ("%s\n", hostname); + g_free (hostname); + } else { + /* hostname provided -> set it */ + const char *hostname = *argv; - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage_general (); - } - else if (matches (*argv, "status") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_status (); - goto finish; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_status (nmc, NULL, NULL); - } - else if (matches (*argv, "hostname") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_hostname (); - goto finish; - } + if (next_arg (&argc, &argv) == 0) + g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); - if (next_arg (&argc, &argv) != 0) { - /* no arguments -> get hostname */ - char *hostname = NULL; + nmc->should_wait++; + nmc->get_client (nmc); /* create NMClient */ + nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); + } - nmc->get_client (nmc); /* create NMClient */ - g_object_get (nmc->client, NM_CLIENT_HOSTNAME, &hostname, NULL); - if (hostname) - g_print ("%s\n", hostname); - g_free (hostname); - } else { - /* hostname provided -> set it */ - const char *hostname = *argv; + return nmc->return_value; - if (next_arg (&argc, &argv) == 0) - g_print ("Warning: ignoring extra garbage after '%s' hostname\n", hostname); +} - nmc->should_wait++; - nmc->get_client (nmc); /* create NMClient */ - nm_client_save_hostname_async (nmc->client, hostname, NULL, save_hostname_cb, nmc); - } - } - else if (matches (*argv, "permissions") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_permissions (); - goto finish; - } - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_nm_permissions (nmc); - } - else if (matches (*argv, "logging") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_general_logging (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no arguments -> get logging level and domains */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - goto finish; - } - show_general_logging (nmc); - } else { - /* arguments provided -> set logging level and domains */ - const char *level = NULL; - const char *domains = NULL; - nmc_arg_t exp_args[] = { {"level", TRUE, &level, TRUE}, - {"domains", TRUE, &domains, TRUE}, - {NULL} }; - - if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { - g_string_assign (nmc->return_text, error->message); - nmc->return_value = error->code; - goto finish; - } +static const NMCCommand general_cmds[] = { + {"status", do_general_status, usage_general_status }, + {"hostname", do_general_hostname, usage_general_hostname }, + {"permissions", do_general_permissions, usage_general_permissions }, + {"logging", do_general_logging, usage_general_logging }, + {NULL, do_general_status, usage_general } +}; - nmc->get_client (nmc); /* create NMClient */ - nm_client_set_logging (nmc->client, level, domains, &error); - if (error) { - g_string_printf (nmc->return_text, _("Error: failed to set logging: %s"), - error->message); - nmc->return_value = NMC_RESULT_ERROR_UNKNOWN; - goto finish; - } - } - } - else { - usage_general (); - g_string_printf (nmc->return_text, _("Error: 'general' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - } - } +/* + * Entry point function for general operations 'nmcli general' + */ +NMCResultCode +do_general (NmCli *nmc, int argc, char **argv) +{ + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); -finish: - if (error) - g_error_free (error); - return nmc->return_value; + return nmc_do_cmd (nmc, general_cmds, *argv, argc, argv); } static gboolean @@ -744,12 +750,28 @@ do_networking (NmCli *nmc, int argc, char **argv) /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); - if (argc == 0) + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; nmc_switch_show (nmc, NMC_FIELDS_NM_NETWORKING, _("Networking")); - else if (argc > 0) { + } else if (argc > 0) { + + if (argc == 1 && nmc->complete) { + nmc_complete_strings (*argv, "connectivity", NULL); + nmc_complete_bool (*argv); + return nmc->return_value; + } + if (nmc_arg_is_help (*argv)) { + if (nmc->complete) + return nmc->return_value; usage_networking (); } else if (matches (*argv, "connectivity") == 0) { + if (nmc->complete) { + if (argc == 2) + nmc_complete_strings (*(argv+1), "check", NULL); + return nmc->return_value; + } if (nmc_arg_is_help (*(argv+1))) { usage_networking_connectivity (); goto finish; @@ -774,6 +796,8 @@ do_networking (NmCli *nmc, int argc, char **argv) nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; } } else if (nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) { + if (nmc->complete) + return nmc->return_value; if (nmc_arg_is_help (*(argv+1))) { if (enable_flag) usage_networking_on (); @@ -785,6 +809,8 @@ do_networking (NmCli *nmc, int argc, char **argv) nmc->get_client (nmc); /* create NMClient */ nm_client_networking_set_enabled (nmc->client, enable_flag, NULL); } else { + if (nmc->complete) + return nmc->return_value; usage_networking (); g_string_printf (nmc->return_text, _("Error: 'networking' command '%s' is not valid."), *argv); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; @@ -796,100 +822,114 @@ finish: return nmc->return_value; } -/* - * Entry point function for radio switch commands 'nmcli radio' - */ -NMCResultCode -do_radio (NmCli *nmc, int argc, char **argv) +static NMCResultCode +do_radio_all (NmCli *nmc, int argc, char **argv) { - GError *error = NULL; gboolean enable_flag; - - /* Register polkit agent */ - nmc_start_polkit_agent_start_try (nmc); + gs_free_error GError *error = NULL; if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show all radio switches */ if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - goto finish; + return NMC_RESULT_ERROR_USER_INPUT; } show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; + } + + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + nm_client_wimax_set_enabled (nmc->client, enable_flag); + nm_client_wwan_set_enabled (nmc->client, enable_flag); } - if (argc > 0) { - if (nmc_arg_is_help (*argv)) { - usage_radio (); - } - else if (matches (*argv, "all") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_all (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show all radio switches */ - if (!nmc_terse_option_check (nmc->print_output, nmc->required_fields, &error)) { - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; - g_error_free (error); - goto finish; - } - show_nm_status (nmc, _("Radio switches"), NMC_FIELDS_NM_STATUS_RADIO); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; + return nmc->return_value; +} - nmc->get_client (nmc); /* create NMClient */ - nm_client_wireless_set_enabled (nmc->client, enable_flag); - nm_client_wimax_set_enabled (nmc->client, enable_flag); - nm_client_wwan_set_enabled (nmc->client, enable_flag); - } - } - else if (matches (*argv, "wifi") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_wifi (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show current WiFi state */ - nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; - - nmc->get_client (nmc); /* create NMClient */ - nm_client_wireless_set_enabled (nmc->client, enable_flag); - } - } - else if (matches (*argv, "wwan") == 0) { - if (nmc_arg_is_help (*(argv+1))) { - usage_radio_wwan (); - goto finish; - } - if (next_arg (&argc, &argv) != 0) { - /* no argument, show current WWAN (mobile broadband) state */ - nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); - } else { - if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) - goto finish; +static NMCResultCode +do_radio_wifi (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; - nmc->get_client (nmc); /* create NMClient */ - nm_client_wwan_set_enabled (nmc->client, enable_flag); - } + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show current WiFi state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WIFI, _("Wi-Fi radio switch")); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; } - else { - usage_radio (); - g_string_printf (nmc->return_text, _("Error: 'radio' command '%s' is not valid."), *argv); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wireless_set_enabled (nmc->client, enable_flag); + } + + return nmc->return_value; +} + +static NMCResultCode +do_radio_wwan (NmCli *nmc, int argc, char **argv) +{ + gboolean enable_flag; + + if (argc == 0) { + if (nmc->complete) + return nmc->return_value; + + /* no argument, show current WWAN (mobile broadband) state */ + nmc_switch_show (nmc, NMC_FIELDS_NM_WWAN, _("WWAN radio switch")); + } else { + if (nmc->complete) { + if (argc == 1) + nmc_complete_bool (*argv); + return nmc->return_value; } + if (!nmc_switch_parse_on_off (nmc, *(argv-1), *argv, &enable_flag)) + return nmc->return_value; + + nmc->get_client (nmc); /* create NMClient */ + nm_client_wwan_set_enabled (nmc->client, enable_flag); } -finish: - quit (); return nmc->return_value; } +static const NMCCommand radio_cmds[] = { + {"all", do_radio_all, usage_radio_all }, + {"wifi", do_radio_wifi, usage_radio_wifi }, + {"wwan", do_radio_wwan, usage_radio_wwan }, + {NULL, do_radio_all, usage_radio } +}; + +/* + * Entry point function for radio switch commands 'nmcli radio' + */ +NMCResultCode +do_radio (NmCli *nmc, int argc, char **argv) +{ + /* Register polkit agent */ + nmc_start_polkit_agent_start_try (nmc); + + return nmc_do_cmd (nmc, radio_cmds, *argv, argc, argv); +} + static void networkmanager_running (NMClient *client, GParamSpec *param, NmCli *nmc) { @@ -1181,6 +1221,9 @@ do_overview (NmCli *nmc, int argc, char **argv) NMCResultCode do_monitor (NmCli *nmc, int argc, char **argv) { + if (nmc->complete) + return nmc->return_value; + if (argc > 0) { if (!nmc_arg_is_help (*argv)) { g_string_printf (nmc->return_text, _("Error: 'monitor' command '%s' is not valid."), *argv); diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 0a8dcb5b74..9bfa05fade 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -41,6 +41,7 @@ #include "devices.h" #include "general.h" #include "agent.h" +#include "settings.h" #if defined(NM_DIST_VERSION) # define NMCLI_VERSION NM_DIST_VERSION @@ -64,6 +65,105 @@ GMainLoop *loop = NULL; static sigset_t signal_set; struct termios termios_orig; +static void +complete_field (GHashTable *h, const char *setting, NmcOutputField field[]) +{ + int i; + + for (i = 0; field[i].name; i++) { + if (setting) + g_hash_table_add (h, g_strdup_printf ("%s.%s", setting, field[i].name)); + else + g_hash_table_add (h, g_strdup (field[i].name)); + } +} + +static void +complete_one (gpointer key, gpointer value, gpointer user_data) +{ + const char *prefix = user_data; + const char *name = key; + const char *last; + + last = strrchr (prefix, ','); + if (last) + last++; + else + last = prefix; + + if ((!*last && !strchr (name, '.')) || matches (last, name) == 0) { + g_print ("%.*s%s%s\n", (int)(last-prefix), prefix, name, + strcmp (last, name) == 0 ? "," : ""); + } +} + +static void +complete_fields (const char *prefix) +{ + + GHashTable *h; + + h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + complete_field (h, NULL, nmc_fields_ip4_config); + complete_field (h, NULL, nmc_fields_dhcp4_config); + complete_field (h, NULL, nmc_fields_ip6_config); + complete_field (h, NULL, nmc_fields_dhcp6_config); + complete_field (h, NULL, nmc_fields_con_show); + complete_field (h, NULL, nmc_fields_settings_names); + complete_field (h, NULL, nmc_fields_con_active_details_general); + complete_field (h, NULL, nmc_fields_con_active_details_vpn); + complete_field (h, NULL, nmc_fields_con_active_details_groups); + complete_field (h, NULL, nmc_fields_dev_status); + complete_field (h, NULL, nmc_fields_dev_show_general); + complete_field (h, NULL, nmc_fields_dev_show_connections); + complete_field (h, NULL, nmc_fields_dev_show_cap); + complete_field (h, NULL, nmc_fields_dev_show_wired_prop); + complete_field (h, NULL, nmc_fields_dev_show_wifi_prop); + complete_field (h, NULL, nmc_fields_dev_show_wimax_prop); + complete_field (h, NULL, nmc_fields_dev_wifi_list); + complete_field (h, NULL, nmc_fields_dev_wimax_list); + complete_field (h, NULL, nmc_fields_dev_show_master_prop); + complete_field (h, NULL, nmc_fields_dev_show_team_prop); + complete_field (h, NULL, nmc_fields_dev_show_vlan_prop); + complete_field (h, NULL, nmc_fields_dev_show_bluetooth); + complete_field (h, NULL, nmc_fields_dev_show_sections); + complete_field (h, NULL, nmc_fields_dev_lldp_list); + + complete_field (h, "connection", nmc_fields_setting_connection); + complete_field (h, "wired", nmc_fields_setting_wired); + complete_field (h, "8021X", nmc_fields_setting_8021X); + complete_field (h, "wireless", nmc_fields_setting_wireless); + complete_field (h, "wireless_security", nmc_fields_setting_wireless_security); + complete_field (h, "ip4-config", nmc_fields_setting_ip4_config); + complete_field (h, "ip6-config", nmc_fields_setting_ip6_config); + complete_field (h, "serial", nmc_fields_setting_serial); + complete_field (h, "ppp", nmc_fields_setting_ppp); + complete_field (h, "pppoe", nmc_fields_setting_pppoe); + complete_field (h, "adsl", nmc_fields_setting_adsl); + complete_field (h, "gsm", nmc_fields_setting_gsm); + complete_field (h, "cdma", nmc_fields_setting_cdma); + complete_field (h, "bluetooth", nmc_fields_setting_bluetooth); + complete_field (h, "olpc-mesh", nmc_fields_setting_olpc_mesh); + complete_field (h, "vpn", nmc_fields_setting_vpn); + complete_field (h, "wimax", nmc_fields_setting_wimax); + complete_field (h, "infiniband", nmc_fields_setting_infiniband); + complete_field (h, "bond", nmc_fields_setting_bond); + complete_field (h, "vlan", nmc_fields_setting_vlan); + complete_field (h, "bridge", nmc_fields_setting_bridge); + complete_field (h, "bridge-port", nmc_fields_setting_bridge_port); + complete_field (h, "team", nmc_fields_setting_team); + complete_field (h, "team0port", nmc_fields_setting_team_port); + complete_field (h, "dcb", nmc_fields_setting_dcb); + complete_field (h, "tun", nmc_fields_setting_tun); + complete_field (h, "ip-tunnel", nmc_fields_setting_ip_tunnel); + complete_field (h, "macvlan", nmc_fields_setting_macvlan); + complete_field (h, "vxlan", nmc_fields_setting_vxlan); + + g_hash_table_foreach (h, complete_one, (gpointer) prefix); + g_hash_table_destroy (h); +} + /* Get an error quark for use with GError */ GQuark @@ -78,9 +178,9 @@ nmcli_error_quark (void) } static void -usage (const char *prog_name) +usage (void) { - g_printerr (_("Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" + g_printerr (_("Usage: nmcli [OPTIONS] OBJECT { COMMAND | help }\n" "\n" "OPTIONS\n" " -t[erse] terse output\n" @@ -103,15 +203,7 @@ usage (const char *prog_name) " d[evice] devices managed by NetworkManager\n" " a[gent] NetworkManager secret agent or polkit agent\n" " m[onitor] monitor NetworkManager changes\n" - "\n"), - prog_name); -} - -static NMCResultCode -do_help (NmCli *nmc, int argc, char **argv) -{ - usage ("nmcli"); - return NMC_RESULT_SUCCESS; + "\n")); } static const NMCCommand nmcli_cmds[] = { @@ -122,8 +214,7 @@ static const NMCCommand nmcli_cmds[] = { { "connection", do_connections, NULL }, { "device", do_devices, NULL }, { "agent", do_agent, NULL }, - { "help", do_help, NULL }, - { NULL, do_overview, NULL }, + { NULL, do_overview, usage } }; static NMCResultCode @@ -137,29 +228,33 @@ parse_command_line (NmCli *nmc, int argc, char **argv) else base++; if (argc > 1 && nm_streq (argv[1], "--complete-args")) { - /* We (currently?) support --complete-args for "connection" command only: - * ignore any other command when this option is enabled as means we are in - * 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") || nm_streq0 (argv[2], "device"))) - return nmc->return_value; nmc->complete = TRUE; argv[1] = argv[0]; argc--; argv++; } + argc--; argv++; + /* parse options */ - while (argc > 1) { - char *opt = argv[1]; - /* '--' ends options */ - if (strcmp (opt, "--") == 0) { - argc--; argv++; - break; - } + while (argc) { + char *opt = argv[0]; if (opt[0] != '-') break; - if (opt[1] == '-') + + if (argc == 1 && nmc->complete) { + nmc_complete_strings (opt, "--terse", "--pretty", "--mode", "--colors", "--escape", + "--fields", "--nocheck", "--ask", "--show-secrets", + "--wait", "--version", "--help", NULL); + } + + if (opt[1] == '-') { opt++; + /* '--' ends options */ + if (opt[1] == '\0') { + argc--; argv++; + break; + } + } + if (matches (opt, "-terse") == 0) { if (nmc->print_output == NMC_PRINT_TERSE) { g_string_printf (nmc->return_text, _("Error: Option '--terse' is specified the second time.")); @@ -188,63 +283,67 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->print_output = NMC_PRINT_PRETTY; } else if (matches (opt, "-mode") == 0) { nmc->mode_specified = TRUE; - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "tabular") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "tabular", "multiline", NULL); + if (matches (argv[0], "tabular") == 0) nmc->multiline_output = FALSE; - else if (matches (argv[1], "multiline") == 0) + else if (matches (argv[0], "multiline") == 0) nmc->multiline_output = TRUE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-colors") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "auto") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "yes", "no", "auto", NULL); + if (matches (argv[0], "auto") == 0) nmc->use_colors = NMC_USE_COLOR_AUTO; - else if (matches (argv[1], "yes") == 0) + else if (matches (argv[0], "yes") == 0) nmc->use_colors = NMC_USE_COLOR_YES; - else if (matches (argv[1], "no") == 0) + else if (matches (argv[0], "no") == 0) nmc->use_colors = NMC_USE_COLOR_NO; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-escape") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (matches (argv[1], "yes") == 0) + if (argc == 1 && nmc->complete) + nmc_complete_strings (argv[0], "yes", "no", NULL); + if (matches (argv[0], "yes") == 0) nmc->escape_values = TRUE; - else if (matches (argv[1], "no") == 0) + else if (matches (argv[0], "no") == 0) nmc->escape_values = FALSE; else { - g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[1], opt); + g_string_printf (nmc->return_text, _("Error: '%s' is not valid argument for '%s' option."), argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } } else if (matches (opt, "-fields") == 0) { - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: fields for '%s' options are missing."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - nmc->required_fields = g_strdup (argv[1]); + if (argc == 1 && nmc->complete) + complete_fields (argv[0]); + nmc->required_fields = g_strdup (argv[0]); } else if (matches (opt, "-nocheck") == 0) { /* ignore for backward compatibility */ } else if (matches (opt, "-ask") == 0) { @@ -253,24 +352,25 @@ parse_command_line (NmCli *nmc, int argc, char **argv) nmc->show_secrets = TRUE; } else if (matches (opt, "-wait") == 0) { unsigned long timeout; - next_arg (&argc, &argv); - if (argc <= 1) { + if (next_arg (&argc, &argv) != 0) { g_string_printf (nmc->return_text, _("Error: missing argument for '%s' option."), opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } - if (!nmc_string_to_uint (argv[1], TRUE, 0, G_MAXINT, &timeout)) { + if (!nmc_string_to_uint (argv[0], TRUE, 0, G_MAXINT, &timeout)) { g_string_printf (nmc->return_text, _("Error: '%s' is not a valid timeout for '%s' option."), - argv[1], opt); + argv[0], opt); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; return nmc->return_value; } nmc->timeout = (int) timeout; } else if (matches (opt, "-version") == 0) { - g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); + if (!nmc->complete) + g_print (_("nmcli tool, version %s\n"), NMCLI_VERSION); return NMC_RESULT_SUCCESS; } else if (matches (opt, "-help") == 0) { - usage (base); + if (!nmc->complete) + usage (); return NMC_RESULT_SUCCESS; } else { g_string_printf (nmc->return_text, _("Error: Option '%s' is unknown, try 'nmcli -help'."), opt); @@ -282,7 +382,7 @@ parse_command_line (NmCli *nmc, int argc, char **argv) } /* Now run the requested command */ - return nmc_do_cmd (nmc, nmcli_cmds, argv[1], argc-1, argv+1); + return nmc_do_cmd (nmc, nmcli_cmds, *argv, argc, argv); } static gboolean nmcli_sigint = FALSE; @@ -617,11 +717,12 @@ 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) { + if (nm_cli.complete) { + /* Remove error statuses from command completion runs. */ + if (nm_cli.return_value < NMC_RESULT_COMPLETE_FILE) + nm_cli.return_value = NMC_RESULT_SUCCESS; + } else if (nm_cli.return_value != NMC_RESULT_SUCCESS) { + /* Print result descripting text */ g_printerr ("%s\n", nm_cli.return_text->str); } diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index c867ad5f9f..a8908685bd 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -63,7 +63,10 @@ typedef enum { NMC_RESULT_ERROR_VERSIONS_MISMATCH = 9, /* Connection/Device/AP not found */ - NMC_RESULT_ERROR_NOT_FOUND = 10 + NMC_RESULT_ERROR_NOT_FOUND = 10, + + /* --complete-args signals a file name may follow */ + NMC_RESULT_COMPLETE_FILE = 65, } NMCResultCode; typedef enum { diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 98d0c8b19d..feed51ce7e 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -3247,8 +3247,7 @@ nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, c continue; if (nm_utils_is_uuid (*iter)) { - con = nmc_find_connection (nm_cli.connections, - "uuid", *iter, NULL); + con = nmc_find_connection (nm_cli.connections, "uuid", *iter, NULL, FALSE); if (!con) g_print (_("Warning: %s is not an UUID of any existing connection profile\n"), *iter); else { @@ -3260,8 +3259,7 @@ nmc_property_connection_set_secondaries (NMSetting *setting, const char *prop, c } } } else { - con = nmc_find_connection (nm_cli.connections, - "id", *iter, NULL); + con = nmc_find_connection (nm_cli.connections, "id", *iter, NULL, FALSE); if (!con) { g_set_error (error, 1, 0, _("'%s' is not a name of any exiting profile"), *iter); g_strfreev (strv); diff --git a/clients/cli/settings.h b/clients/cli/settings.h index bab00ee908..33900510fd 100644 --- a/clients/cli/settings.h +++ b/clients/cli/settings.h @@ -63,4 +63,34 @@ gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue * gboolean setting_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboolean secrets); +extern NmcOutputField nmc_fields_setting_connection[]; +extern NmcOutputField nmc_fields_setting_wired[]; +extern NmcOutputField nmc_fields_setting_8021X[]; +extern NmcOutputField nmc_fields_setting_wireless[]; +extern NmcOutputField nmc_fields_setting_wireless_security[]; +extern NmcOutputField nmc_fields_setting_ip4_config[]; +extern NmcOutputField nmc_fields_setting_ip6_config[]; +extern NmcOutputField nmc_fields_setting_serial[]; +extern NmcOutputField nmc_fields_setting_ppp[]; +extern NmcOutputField nmc_fields_setting_pppoe[]; +extern NmcOutputField nmc_fields_setting_adsl[]; +extern NmcOutputField nmc_fields_setting_gsm[]; +extern NmcOutputField nmc_fields_setting_cdma[]; +extern NmcOutputField nmc_fields_setting_bluetooth[]; +extern NmcOutputField nmc_fields_setting_olpc_mesh[]; +extern NmcOutputField nmc_fields_setting_vpn[]; +extern NmcOutputField nmc_fields_setting_wimax[]; +extern NmcOutputField nmc_fields_setting_infiniband[]; +extern NmcOutputField nmc_fields_setting_bond[]; +extern NmcOutputField nmc_fields_setting_vlan[]; +extern NmcOutputField nmc_fields_setting_bridge[]; +extern NmcOutputField nmc_fields_setting_bridge_port[]; +extern NmcOutputField nmc_fields_setting_team[]; +extern NmcOutputField nmc_fields_setting_team_port[]; +extern NmcOutputField nmc_fields_setting_dcb[]; +extern NmcOutputField nmc_fields_setting_tun[]; +extern NmcOutputField nmc_fields_setting_ip_tunnel[]; +extern NmcOutputField nmc_fields_setting_macvlan[]; +extern NmcOutputField nmc_fields_setting_vxlan[]; + #endif /* NMC_SETTINGS_H */ diff --git a/man/nmcli.xml b/man/nmcli.xml index 0d403202f4..5a936335c0 100644 --- a/man/nmcli.xml +++ b/man/nmcli.xml @@ -278,6 +278,23 @@ <varlistentry> <term><group choice='plain'> + <arg choice='plain'><option>--complete-args</option></arg> + </group></term> + + <listitem> + <para>Instead of conducting the desired action, <command>nmcli</command> + will list possible completions for the last argument. This is useful to implement + argument completion in shell.</para> + + <para>The <link linkend='exit_status'>exit status</link> will indicate success + or return a code 65 to indicate the last argument is a file name.</para> + + <para>NetworkManager ships with command completion support for GNU Bash.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><group choice='plain'> <arg choice='plain'><option>-v</option></arg> <arg choice='plain'><option>--version</option></arg> </group></term> @@ -2089,6 +2106,13 @@ It's equivalent of using <literal>+ipv6.addresses</literal> syntax.</entry> <para>Connection, device, or access point does not exist.</para> </listitem> </varlistentry> + + <varlistentry> + <term><errorcode>65</errorcode></term> + <listitem> + <para>When used with <option>--complete-args</option> option, a file name is expected to follow.</para> + </listitem> + </varlistentry> </variablelist> </refsect1> |