diff options
author | Francesco Giudici <fgiudici@redhat.com> | 2016-06-21 16:30:07 +0200 |
---|---|---|
committer | Francesco Giudici <fgiudici@redhat.com> | 2016-06-21 16:30:07 +0200 |
commit | 68d48fa50769c34cf8f10b90788f5dca3ea60191 (patch) | |
tree | b65780fecbe62b7a11115a8254de3b841e34da9d | |
parent | 072358dad08b4b2206f15e6b0ebf9f096e81c05e (diff) | |
parent | 00b362bcf2052671788ce03d6cafb253904e991b (diff) | |
download | NetworkManager-68d48fa50769c34cf8f10b90788f5dca3ea60191.tar.gz |
nmlci merge branch 'fg/autocomplete_fix_rh1301226_with_LR_patches'
https://bugzilla.gnome.org/show_bug.cgi?id=724860
https://bugzilla.redhat.com/show_bug.cgi?id=1301226
-rw-r--r-- | clients/cli/connections.c | 416 | ||||
-rw-r--r-- | clients/cli/nmcli-completion | 41 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 14 | ||||
-rw-r--r-- | clients/cli/nmcli.h | 1 |
4 files changed, 331 insertions, 141 deletions
diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 506bfe0fd0..c10db7b446 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -1590,6 +1590,10 @@ do_connections_show (NmCli *nmc, gboolean active_only, gboolean show_secrets, char *profile_flds = NULL, *active_flds = NULL; GPtrArray *invisibles, *sorted_cons; + /* Not (yet?) supported */ + if (nmc->complete) + goto finish; + if (argc == 0) { char *fields_str; char *fields_all = NMC_FIELDS_CON_SHOW_ALL; @@ -2358,6 +2362,9 @@ do_connection_up (NmCli *nmc, int argc, char **argv) const char *name = NULL; char *line = NULL; + /* 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. @@ -2547,6 +2554,10 @@ 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; @@ -2900,6 +2911,98 @@ get_valid_settings_array (const char *con_type) return NULL; } +/* get_valid_properties_string: + * @array: base properties for the current connection type + * @array_slv: slave properties (or ipv4/ipv6 ones) for the current connection type + * @modifier: to prepend to each element of the returned list + * @prefix: only properties matching the prefix will be returned + * @postfix: required prefix on the property args; if a empty string is passed, is + * assumed that the @prefix is a shortcut, so it should not be completed + * but left as is (and an additional check for shortcut ambiguity is performed) + * + * Returns a list of properties compatible with the current connection type + * for the shell autocompletion functionality. + * + * Returns: list of property.arg elements + */ +static char * +get_valid_properties_string (const NameItem *array, + const NameItem *array_slv, + char modifier, + const char *prefix, + const char *postfix) +{ + const NameItem *iter = array; + const NmcOutputField *field_iter; + const char *prop_name = NULL; + GString *str; + int i, j; + + g_return_val_if_fail (prefix, NULL); + + str = g_string_sized_new (1024); + + for (i = 0; i < 2; i++, iter = array_slv) { + while (iter && iter->name) { + if ( !(g_str_has_prefix (iter->name, prefix)) + && (!(iter->alias) || !g_str_has_prefix (iter->alias, prefix))) { + iter++; + continue; + } + /* If postix (so prefix is terminated by a dot), check + * that prefix is not ambiguous */ + if (postfix) { + if (prop_name) + return g_string_free (str, TRUE); + prop_name = prefix; + } else { + prop_name = iter->name; + } + + /* Search the array with the arguments of the current property */ + j = 0; + while (!nm_streq0 (iter->name, nmc_fields_settings_names[j].name)) { + g_assert (nmc_fields_settings_names[j].name); + j++; + } + field_iter = nmc_fields_settings_names[j].group; + + j = 0; + while (field_iter[j].name) { + gchar *new; + const char *arg_name = field_iter[j].name; + + /* If required, expand the alias too */ + if (!postfix && iter->alias) { + if (modifier) + g_string_append_c (str, modifier); + new = g_strdup_printf ("%s.%s\n", + iter->alias, + arg_name); + g_string_append (str, new); + g_free (new); + } + + if (postfix && !g_str_has_prefix (arg_name, postfix)) { + j++; + continue; + } + + if (modifier) + g_string_append_c (str, modifier); + new = g_strdup_printf ("%s.%s\n", + prop_name, + arg_name); + g_string_append (str, new); + g_free (new); + j++; + } + iter++; + } + } + return g_string_free (str, FALSE); +} + /* * Check if 'val' is valid string in either array->name or array->alias for * both array parameters (array & array_slv). @@ -4601,8 +4704,36 @@ do_questionnaire_ip_tunnel (char **local, char **parent) } } +static void +complete_property_name (NmCli *nmc, NMConnection *connection, + char modifier, + const gchar *prefix, + const gchar *postfix) +{ + NMSettingConnection *s_con; + const NameItem *valid_settings_main = NULL; + const NameItem *valid_settings_slave = NULL; + const char *connection_type = NULL; + const char *slave_type = NULL; + gs_free char *slv_type = NULL; + gs_free char *word_list = NULL; + + connection_type = nm_connection_get_connection_type (connection); + s_con = nm_connection_get_setting_connection (connection); + if (s_con) + slave_type = nm_setting_connection_get_slave_type (s_con); + slv_type = g_strdup_printf ("%s-slave", slave_type ? slave_type : "no"); + valid_settings_main = get_valid_settings_array (connection_type); + valid_settings_slave = get_valid_settings_array (slv_type); + + word_list = get_valid_properties_string (valid_settings_main, valid_settings_slave, modifier, prefix, postfix); + if (word_list) + g_print ("%s", word_list); +} + static gboolean -read_connection_properties (NMConnection *connection, +read_connection_properties (NmCli *nmc, + NMConnection *connection, int argc, char **argv, GError **error) @@ -4610,13 +4741,12 @@ read_connection_properties (NMConnection *connection, NMSetting *setting; NMSettingConnection *s_con; const char *con_type; - const char *s_dot_p; + const char *s_dot_p = NULL; const char *value; char **strv = NULL; char *slv_type = NULL; const char *setting_name; - gboolean append = FALSE; - gboolean remove = FALSE; + char modifier = '\0'; gboolean success = FALSE; GError *local = NULL; @@ -4644,29 +4774,20 @@ read_connection_properties (NMConnection *connection, next_arg (&argc, &argv); if (!s_dot_p) { + /* XXX: can not happen? */ g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: <setting>.<property> argument is missing.")); goto finish; } - if (!value) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: value for '%s' is missing."), s_dot_p); - goto finish; - } - /* Empty string will reset the value to default */ - if (value[0] == '\0') - value = NULL; - - if (s_dot_p[0] == '+') { - s_dot_p++; - append = TRUE; - } else if (s_dot_p[0] == '-') { - s_dot_p++; - remove = TRUE; - } + if (s_dot_p[0] == '+' || s_dot_p[0] == '-') + modifier = *s_dot_p++; strv = g_strsplit (s_dot_p, ".", 2); if (g_strv_length (strv) != 2) { + if (nmc->complete) { + complete_property_name (nmc, connection, modifier, s_dot_p, NULL); + break; + } g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: invalid <setting>.<property> '%s'."), s_dot_p); goto finish; @@ -4703,9 +4824,18 @@ read_connection_properties (NMConnection *connection, goto finish; } - if (!remove) { + if (!value) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: value for '%s' is missing."), s_dot_p); + goto finish; + } + /* Empty string will reset the value to default */ + if (value[0] == '\0') + value = NULL; + + if (modifier != '-') { /* Set/add value */ - if (!append) + if (modifier != '+') nmc_setting_reset_property (setting, property_name, NULL); if (!nmc_setting_set_property (setting, property_name, value, &local)) { g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -4743,6 +4873,10 @@ read_connection_properties (NMConnection *connection, success = TRUE; finish: + if (!success && nmc->complete) { + complete_property_name (nmc, connection, modifier, strv[0], strv[1]); + success = TRUE; + } if (strv) g_strfreev (strv); g_free (slv_type); @@ -4801,11 +4935,9 @@ complete_slave (NMSettingConnection *s_con, } static gboolean -complete_connection_by_type (NMConnection *connection, +complete_connection_by_type (NmCli *nmc, + NMConnection *connection, const char *con_type, - const GPtrArray *all_connections, - gboolean ask, - gboolean show_secrets, int argc, char **argv, GError **error) @@ -4861,7 +4993,7 @@ complete_connection_by_type (NMConnection *connection, mtu = g_strdup (mtu_c); mac = g_strdup (mac_c); cloned_mac = g_strdup (cloned_mac_c); - if (ask) + if (nmc->ask) do_questionnaire_ethernet (TRUE, &mtu, &mac, &cloned_mac); if (!check_and_convert_mtu (mtu, &mtu_int, error)) @@ -4921,7 +5053,7 @@ cleanup_wired: mode = g_strdup (mode_c); parent = g_strdup (parent_c); p_key = g_strdup (p_key_c); - if (ask) + if (nmc->ask) do_questionnaire_infiniband (&mtu, &mac, &mode, &parent, &p_key); if (!check_and_convert_mtu (mtu, &mtu_int, error)) @@ -4981,7 +5113,7 @@ cleanup_ib: char *cloned_mac = NULL; const char *mode_c = NULL; char *mode = NULL; - nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !nmc->ask}, {"mtu", TRUE, &mtu_c, FALSE}, {"mac", TRUE, &mac_c, FALSE}, {"cloned-mac", TRUE, &cloned_mac_c, FALSE}, @@ -4991,7 +5123,7 @@ cleanup_ib: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!ssid && ask) + if (!ssid && nmc->ask) ssid = ssid_ask = nmc_readline (_("SSID: ")); if (!ssid) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5004,7 +5136,7 @@ cleanup_ib: mac = g_strdup (mac_c); cloned_mac = g_strdup (cloned_mac_c); mode = g_strdup (mode_c); - if (ask) + if (nmc->ask) do_questionnaire_wifi (&mtu, &mac, &cloned_mac, &mode); if (!check_and_convert_mtu (mtu, &mtu_int, error)) @@ -5051,14 +5183,14 @@ cleanup_wifi: char *nsp_name_ask = NULL; const char *mac_c = NULL; char *mac = NULL; - nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !ask}, + nmc_arg_t exp_args[] = { {"nsp", TRUE, &nsp_name, !nmc->ask}, {"mac", TRUE, &mac_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!nsp_name && ask) + if (!nsp_name && nmc->ask) nsp_name = nsp_name_ask = nmc_readline (_("WiMAX NSP name: ")); if (!nsp_name) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5068,7 +5200,7 @@ cleanup_wifi: /* Also ask for all optional arguments if '--ask' is specified. */ mac = g_strdup (mac_c); - if (ask) + if (nmc->ask) do_questionnaire_wimax (&mac); if (!check_mac (mac, ARPHRD_ETHER, "mac", error)) @@ -5103,7 +5235,7 @@ cleanup_wimax: guint32 mtu_int = 0; const char *mac_c = NULL; char *mac = NULL; - nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, + nmc_arg_t exp_args[] = { {"username", TRUE, &username, !nmc->ask}, {"password", TRUE, &password_c, FALSE}, {"service", TRUE, &service_c, FALSE}, {"mtu", TRUE, &mtu_c, FALSE}, @@ -5113,7 +5245,7 @@ cleanup_wimax: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!username && ask) + if (!username && nmc->ask) username = username_ask = nmc_readline (_("PPPoE username: ")); if (!username) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5126,8 +5258,8 @@ cleanup_wimax: service = g_strdup (service_c); mtu = g_strdup (mtu_c); mac = g_strdup (mac_c); - if (ask) - do_questionnaire_pppoe (show_secrets, &password, &service, &mtu, &mac); + if (nmc->ask) + do_questionnaire_pppoe (nmc->show_secrets, &password, &service, &mtu, &mac); if (!check_and_convert_mtu (mtu, &mtu_int, error)) goto cleanup_pppoe; @@ -5177,7 +5309,7 @@ cleanup_pppoe: is_gsm = !strcmp (con_type, NM_SETTING_GSM_SETTING_NAME); if (is_gsm) - gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !ask}; + gsm_args[i++] = (nmc_arg_t) {"apn", TRUE, &apn, !nmc->ask}; gsm_args[i++] = (nmc_arg_t) {"user", TRUE, &user_c, FALSE}; gsm_args[i++] = (nmc_arg_t) {"password", TRUE, &password_c, FALSE}; gsm_args[i++] = (nmc_arg_t) {NULL}; @@ -5185,7 +5317,7 @@ cleanup_pppoe: if (!nmc_parse_args (gsm_args, FALSE, &argc, &argv, error)) return FALSE; - if (!apn && ask && is_gsm) + if (!apn && nmc->ask && is_gsm) apn = apn_ask = nmc_readline (_("APN: ")); if (!apn && is_gsm) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5196,8 +5328,8 @@ cleanup_pppoe: /* Also ask for all optional arguments if '--ask' is specified. */ user = g_strdup (user_c); password = g_strdup (password_c); - if (ask) - do_questionnaire_mobile (show_secrets, &user, &password); + if (nmc->ask) + do_questionnaire_mobile (nmc->show_secrets, &user, &password); if (is_gsm) { g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, NM_SETTING_GSM_SETTING_NAME, NULL); @@ -5239,14 +5371,14 @@ cleanup_mobile: char *addr_ask = NULL; const char *bt_type_c = NULL; char *bt_type = NULL; - nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !ask}, + nmc_arg_t exp_args[] = { {"addr", TRUE, &addr, !nmc->ask}, {"bt-type", TRUE, &bt_type_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!addr && ask) + if (!addr && nmc->ask) addr = addr_ask = nmc_readline (_("Bluetooth device address: ")); if (!addr) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5258,7 +5390,7 @@ cleanup_mobile: /* Also ask for all optional arguments if '--ask' is specified. */ bt_type = g_strdup (bt_type_c); - if (ask) + if (nmc->ask) do_questionnaire_bluetooth (&bt_type); /* Default to 'panu' if bt-type is not provided. */ @@ -5323,8 +5455,8 @@ cleanup_bt: char *mtu = NULL; guint32 mtu_int; gboolean valid_mac = FALSE; - nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, - {"id", TRUE, &vlan_id, !ask}, + nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !nmc->ask}, + {"id", TRUE, &vlan_id, !nmc->ask}, {"flags", TRUE, &flags_c, FALSE}, {"ingress", TRUE, &ingress_c, FALSE}, {"egress", TRUE, &egress_c, FALSE}, @@ -5334,14 +5466,14 @@ cleanup_bt: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!parent && ask) + if (!parent && nmc->ask) parent = parent_ask = nmc_readline (_("VLAN parent device or connection UUID: ")); if (!parent) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: 'dev' is required.")); return FALSE; } - if (!vlan_id && ask) + if (!vlan_id && nmc->ask) vlan_id = vlan_id_ask = nmc_readline (_("VLAN ID <0-4094>: ")); if (!vlan_id) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5371,7 +5503,7 @@ cleanup_bt: flags = g_strdup (flags_c); ingress = g_strdup (ingress_c); egress = g_strdup (egress_c); - if (ask) + if (nmc->ask) do_questionnaire_vlan (&mtu, &flags, &ingress, &egress); if (!check_and_convert_mtu (mtu, &mtu_int, error)) @@ -5466,7 +5598,7 @@ cleanup_vlan: bond_arpinterval = g_strdup (bond_arpinterval_c); bond_arpiptarget = g_strdup (bond_arpiptarget_c); bond_lacp_rate = g_strdup (bond_lacp_rate_c); - if (ask) + if (nmc->ask) do_questionnaire_bond (&bond_mode, &bond_primary, &bond_miimon, &bond_downdelay, &bond_updelay, &bond_arpinterval, &bond_arpiptarget, @@ -5475,7 +5607,7 @@ cleanup_vlan: /* Generate ifname if connection doesn't have one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) { - char *bond_ifname = unique_master_iface_ifname (all_connections, "nm-bond"); + char *bond_ifname = unique_master_iface_ifname (nmc->connections, "nm-bond"); g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, bond_ifname, @@ -5548,7 +5680,7 @@ cleanup_bond: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!complete_slave (s_con, all_connections, NM_SETTING_BOND_SETTING_NAME, master, type, ask, error)) + if (!complete_slave (s_con, nmc->connections, NM_SETTING_BOND_SETTING_NAME, master, type, nmc->ask, error)) return FALSE; /* Change properties in 'connection' setting */ @@ -5573,13 +5705,13 @@ cleanup_bond: /* Also ask for all optional arguments if '--ask' is specified. */ config = g_strdup (config_c); - if (ask) + if (nmc->ask) do_questionnaire_team (&config); /* Generate ifname if conneciton doesn't have one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) { - char *team_ifname = unique_master_iface_ifname (all_connections, "nm-team"); + char *team_ifname = unique_master_iface_ifname (nmc->connections, "nm-team"); g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, team_ifname, @@ -5626,12 +5758,12 @@ cleanup_team: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!complete_slave (s_con, all_connections, NM_SETTING_TEAM_SETTING_NAME, master, type, ask, error)) + if (!complete_slave (s_con, nmc->connections, NM_SETTING_TEAM_SETTING_NAME, master, type, nmc->ask, error)) return FALSE; /* Also ask for all optional arguments if '--ask' is specified. */ config = g_strdup (config_c); - if (ask) + if (nmc->ask) do_questionnaire_team_slave (&config); /* Add 'team-port' setting */ @@ -5706,14 +5838,14 @@ cleanup_team_slave: ageing_time = g_strdup (ageing_time_c); mcast_snoop = g_strdup (mcast_snoop_c); mac = g_strdup (mac_c); - if (ask) + if (nmc->ask) do_questionnaire_bridge (&stp, &priority, &fwd_delay, &hello_time, &max_age, &ageing_time, &mcast_snoop, &mac); /* Generate ifname if conneciton doesn't have one */ ifname = nm_setting_connection_get_interface_name (s_con); if (!ifname) { - char *bridge_ifname = unique_master_iface_ifname (all_connections, "nm-bridge"); + char *bridge_ifname = unique_master_iface_ifname (nmc->connections, "nm-bridge"); g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, bridge_ifname, @@ -5826,7 +5958,7 @@ cleanup_bridge: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!complete_slave (s_con, all_connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, ask, error)) + if (!complete_slave (s_con, nmc->connections, NM_SETTING_BRIDGE_SETTING_NAME, master, type, nmc->ask, error)) return FALSE; /* Add 'bridge-port' setting */ @@ -5838,7 +5970,7 @@ cleanup_bridge: priority = g_strdup (priority_c); path_cost = g_strdup (path_cost_c); hairpin = g_strdup (hairpin_c); - if (ask) + if (nmc->ask) do_questionnaire_bridge_slave (&priority, &path_cost, &hairpin); if (priority) @@ -5890,14 +6022,14 @@ cleanup_bridge_slave: const char *user_c = NULL; char *user = NULL; gs_free char *service_type_free = NULL; - nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !ask}, + nmc_arg_t exp_args[] = { {"vpn-type", TRUE, &vpn_type, !nmc->ask}, {"user", TRUE, &user_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!vpn_type && ask) + if (!vpn_type && nmc->ask) vpn_type = vpn_type_ask = nmc_readline (PROMPT_VPN_TYPE); if (!vpn_type) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5915,7 +6047,7 @@ cleanup_bridge_slave: /* Also ask for all optional arguments if '--ask' is specified. */ user = g_strdup (user_c); - if (ask) + if (nmc->ask) do_questionnaire_vpn (&user); /* Add 'vpn' setting */ @@ -5942,7 +6074,7 @@ cleanup_vpn: unsigned long chan; const char *dhcp_anycast_c = NULL; char *dhcp_anycast = NULL; - nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !ask}, + nmc_arg_t exp_args[] = { {"ssid", TRUE, &ssid, !nmc->ask}, {"channel", TRUE, &channel_c, FALSE}, {"dhcp-anycast", TRUE, &dhcp_anycast_c, FALSE}, {NULL} }; @@ -5950,7 +6082,7 @@ cleanup_vpn: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!ssid && ask) + if (!ssid && nmc->ask) ssid = ssid_ask = nmc_readline (_("SSID: ")); if (!ssid) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5961,7 +6093,7 @@ cleanup_vpn: /* Also ask for all optional arguments if '--ask' is specified. */ channel = g_strdup (channel_c); dhcp_anycast = g_strdup (dhcp_anycast_c); - if (ask) + if (nmc->ask) do_questionnaire_olpc (&channel, &dhcp_anycast); if (channel) { @@ -6008,8 +6140,8 @@ cleanup_olpc: char *password = NULL; const char *encapsulation_c = NULL; char *encapsulation = NULL; - nmc_arg_t exp_args[] = { {"username", TRUE, &username, !ask}, - {"protocol", TRUE, &protocol_c, !ask}, + nmc_arg_t exp_args[] = { {"username", TRUE, &username, !nmc->ask}, + {"protocol", TRUE, &protocol_c, !nmc->ask}, {"password", TRUE, &password_c, FALSE}, {"encapsulation", TRUE, &encapsulation_c, FALSE}, {NULL} }; @@ -6017,7 +6149,7 @@ cleanup_olpc: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!username && ask) + if (!username && nmc->ask) username = username_ask = nmc_readline (_("Username: ")); if (!username) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6026,7 +6158,7 @@ cleanup_olpc: } #define PROMPT_ADSL_PROTO "(" NM_SETTING_ADSL_PROTOCOL_PPPOA "/" NM_SETTING_ADSL_PROTOCOL_PPPOE "/" NM_SETTING_ADSL_PROTOCOL_IPOATM "): " - if (!protocol_c && ask) + if (!protocol_c && nmc->ask) protocol_c = protocol_ask = nmc_readline (_("Protocol %s"), PROMPT_ADSL_PROTO); if (!protocol_c) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6040,8 +6172,8 @@ cleanup_olpc: /* Also ask for all optional arguments if '--ask' is specified. */ password = g_strdup (password_c); encapsulation = g_strdup (encapsulation_c); - if (ask) - do_questionnaire_adsl (show_secrets, &password, &encapsulation); + if (nmc->ask) + do_questionnaire_adsl (nmc->show_secrets, &password, &encapsulation); if (!check_adsl_encapsulation (&encapsulation, error)) goto cleanup_adsl; @@ -6080,15 +6212,15 @@ cleanup_adsl: NMSettingMacvlanMode mode_enum; gboolean valid_mac = FALSE; gboolean tap_bool = FALSE; - nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !ask}, - {"mode", TRUE, &mode, !ask}, + nmc_arg_t exp_args[] = { {"dev", TRUE, &parent, !nmc->ask}, + {"mode", TRUE, &mode, !nmc->ask}, {"tap", TRUE, &tap_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!parent && ask) + if (!parent && nmc->ask) parent = parent_ask = nmc_readline (_("MACVLAN parent device or connection UUID: ")); if (!parent) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6105,7 +6237,7 @@ cleanup_adsl: goto cleanup_macvlan; } - if (!mode && ask) + if (!mode && nmc->ask) mode = mode_ask = nmc_readline (PROMPT_MACVLAN_MODE); if (!mode) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6121,7 +6253,7 @@ cleanup_adsl: /* Also ask for all optional arguments if '--ask' is specified. */ tap = g_strdup (tap_c); - if (ask) + if (nmc->ask) do_questionnaire_macvlan (&tap); if (tap) { @@ -6171,7 +6303,7 @@ cleanup_macvlan: const char *pi_c = NULL, *vnet_hdr_c = NULL, *multi_queue_c = NULL; char *pi = NULL, *vnet_hdr = NULL, *multi_queue = NULL; gboolean pi_bool, vnet_hdr_bool, multi_queue_bool; - nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask}, + nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !nmc->ask}, {"owner", TRUE, &owner_c, FALSE}, {"group", TRUE, &group_c, FALSE}, {"pi", TRUE, &pi_c, FALSE}, @@ -6182,7 +6314,7 @@ cleanup_macvlan: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!mode_c && ask) { + if (!mode_c && nmc->ask) { mode_ask = nmc_readline (_("Mode %s"), PROMPT_TUN_MODE); mode_ask = mode_ask ? mode_ask : g_strdup ("tun"); mode_c = mode_ask; @@ -6206,7 +6338,7 @@ cleanup_macvlan: pi = g_strdup (pi_c); vnet_hdr = g_strdup (vnet_hdr_c); multi_queue = g_strdup (multi_queue_c); - if (ask) + if (nmc->ask) do_questionnaire_tun (&owner, &group, &pi, &vnet_hdr, &multi_queue); if (pi) { @@ -6278,16 +6410,16 @@ cleanup_tun: char *parent = NULL; gboolean success = FALSE; NMIPTunnelMode mode_enum; - nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !ask}, + nmc_arg_t exp_args[] = { {"mode", TRUE, &mode_c, !nmc->ask}, {"local", TRUE, &local_c, FALSE}, - {"remote", TRUE, &remote_c, !ask}, + {"remote", TRUE, &remote_c, !nmc->ask}, {"dev", TRUE, &parent_c, FALSE}, {NULL} }; if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!mode_c && ask) + if (!mode_c && nmc->ask) mode_c = mode_ask = nmc_readline (PROMPT_IP_TUNNEL_MODE); if (!mode_c) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6310,7 +6442,7 @@ cleanup_tun: goto cleanup_tunnel; } - if (!remote_c && ask) + if (!remote_c && nmc->ask) remote_c = remote_ask = nmc_readline (_("Remote endpoint: ")); if (!remote_c) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6328,7 +6460,7 @@ cleanup_tun: local = g_strdup (local_c); parent = g_strdup (parent_c); - if (ask) + if (nmc->ask) do_questionnaire_ip_tunnel (&local, &parent); if ( local @@ -6392,8 +6524,8 @@ cleanup_tunnel: unsigned long int vni; unsigned long sport_min = G_MAXULONG, sport_max = G_MAXULONG; unsigned long dport = G_MAXULONG; - nmc_arg_t exp_args[] = { {"id", TRUE, &id, !ask}, - {"remote", TRUE, &remote, !ask}, + nmc_arg_t exp_args[] = { {"id", TRUE, &id, !nmc->ask}, + {"remote", TRUE, &remote, !nmc->ask}, {"dev", TRUE, &parent_c, FALSE}, {"local", TRUE, &local_c, FALSE}, {"source-port-min", TRUE, &src_port_min_c, FALSE}, @@ -6404,7 +6536,7 @@ cleanup_tunnel: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!id && ask) + if (!id && nmc->ask) id = id_ask = nmc_readline (_("VXLAN ID: ")); if (!id) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6412,7 +6544,7 @@ cleanup_tunnel: goto cleanup_vxlan; } - if (!remote && ask) + if (!remote && nmc->ask) remote = remote_ask = nmc_readline (_("Remote: ")); if (!remote) { g_set_error_literal (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -6432,7 +6564,7 @@ cleanup_tunnel: src_port_max = g_strdup (src_port_max_c); dst_port = g_strdup (dst_port_c); - if (ask) + if (nmc->ask) do_questionnaire_vxlan (&parent, &local, &src_port_min, &src_port_max, &dst_port); if (parent) { @@ -6617,7 +6749,7 @@ cleanup_vxlan: } /* Ask for addresses if '--ask' is specified. */ - if (ask) + if (nmc->ask) do_questionnaire_ip (connection); } @@ -6629,7 +6761,7 @@ cleanup_vxlan: if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, error)) return FALSE; - if (!read_connection_properties (connection, argc, argv, error)) + if (!read_connection_properties (nmc, connection, argc, argv, error)) return FALSE; } @@ -6922,7 +7054,10 @@ do_connection_add (NmCli *nmc, int argc, char **argv) rl_attempted_completion_function = (rl_completion_func_t *) nmcli_con_add_tab_completion; - nmc->return_value = NMC_RESULT_SUCCESS; + if (nmc->complete) { + /* Add support to properties and arg completion */ + return nmc->return_value; + } if (!nmc_parse_args (exp_args, FALSE, &argc, &argv, &error)) { g_string_assign (nmc->return_text, error->message); @@ -7041,11 +7176,9 @@ do_connection_add (NmCli *nmc, int argc, char **argv) g_free (default_name); nm_connection_add_setting (connection, NM_SETTING (s_con)); - if (!complete_connection_by_type (connection, + if (!complete_connection_by_type (nmc, + connection, setting_name, - nmc->connections, - nmc->ask, - nmc->show_secrets, argc, argv, &error)) { @@ -10129,9 +10262,13 @@ do_connection_modify (NmCli *nmc, const char *selector = NULL; GError *error = NULL; - nmc->return_value = NMC_RESULT_SUCCESS; - 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; @@ -10142,6 +10279,12 @@ do_connection_modify (NmCli *nmc, 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; @@ -10150,11 +10293,7 @@ do_connection_modify (NmCli *nmc, name = *argv; } name = *argv; - if (!name) { - g_string_printf (nmc->return_text, _("Error: connection ID 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); @@ -10170,22 +10309,31 @@ do_connection_modify (NmCli *nmc, } if (next_arg (&argc, &argv) != 0) { + if (nmc->complete) { + complete_property_name (nmc, NM_CONNECTION (rc), '\0', "", NULL); + goto finish; + } g_string_printf (nmc->return_text, _("Error: <setting>.<property> argument is missing.")); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } - if (!read_connection_properties (NM_CONNECTION (rc), argc, argv, &error)) { + if (!read_connection_properties (nmc, NM_CONNECTION (rc), argc, argv, &error)) { g_string_assign (nmc->return_text, error->message); nmc->return_value = error->code; g_clear_error (&error); goto finish; } - update_connection (!temporary, rc, modify_connection_cb, nmc); + if (!nmc->complete) { + update_connection (!temporary, rc, modify_connection_cb, nmc); + nmc->should_wait++; + } - nmc->should_wait++; finish: + /* shell completion - be sure to exit with success without printing errors */ + if (nmc->complete) + nmc->return_value = NMC_RESULT_SUCCESS; return nmc->return_value; } @@ -10243,6 +10391,10 @@ do_connection_clone (NmCli *nmc, gboolean temporary, int argc, char **argv) const char *selector = NULL; char *uuid; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (argc == 0) { if (nmc->ask) { name = name_ask = nmc_readline (PROMPT_CONNECTION); @@ -10366,6 +10518,10 @@ do_connection_delete (NmCli *nmc, int argc, char **argv) GString *invalid_cons = NULL; int pos = 0; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (nmc->timeout == -1) nmc->timeout = 10; @@ -10498,6 +10654,11 @@ 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; + if (argc == 0) { /* No connections specified. Monitor all. */ int i; @@ -10554,7 +10715,9 @@ do_connection_reload (NmCli *nmc, int argc, char **argv) { GError *error = NULL; - nmc->return_value = NMC_RESULT_SUCCESS; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; if (!nm_client_reload_connections (nmc->client, NULL, &error)) { g_string_printf (nmc->return_text, _("Error: failed to reload connections: %s."), @@ -10573,7 +10736,9 @@ do_connection_load (NmCli *nmc, int argc, char **argv) char **filenames, **failures = NULL; int i; - nmc->return_value = NMC_RESULT_SUCCESS; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; if (argc == 0) { g_string_printf (nmc->return_text, _("Error: No connection specified.")); @@ -10619,6 +10784,10 @@ do_connection_import (NmCli *nmc, gboolean temporary, int argc, char **argv) NMVpnEditorPlugin *plugin; gs_free char *service_type = NULL; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (argc == 0) { if (nmc->ask) { type_ask = nmc_readline (PROMPT_IMPORT_TYPE); @@ -10735,6 +10904,10 @@ do_connection_export (NmCli *nmc, int argc, char **argv) GError *error = NULL; char tmpfile[] = "/tmp/nmcli-export-temp-XXXXXX"; + /* Not (yet?) supported */ + if (nmc->complete) + return nmc->return_value; + if (argc == 0) { if (nmc->ask) { name_ask = nmc_readline (PROMPT_VPN_CONNECTION); @@ -11044,8 +11217,10 @@ do_connections (NmCli *nmc, int argc, char **argv) /* Check whether NetworkManager is running */ if (!nm_client_get_nm_running (nmc->client)) { - g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); - nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + if (!nmc->complete) { + g_string_printf (nmc->return_text, _("Error: NetworkManager is not running.")); + nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING; + } return nmc->return_value; } @@ -11100,6 +11275,9 @@ do_connections (NmCli *nmc, int argc, char **argv) } 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; @@ -11117,6 +11295,8 @@ do_connections (NmCli *nmc, int argc, char **argv) next_arg (&argc, &argv); if (nmc_arg_is_option (*argv, "temporary")) { + if (nmc->complete) + goto opt_error; temporary = TRUE; next_arg (&argc, &argv); } @@ -11126,6 +11306,8 @@ do_connections (NmCli *nmc, int argc, char **argv) next_arg (&argc, &argv); if (nmc_arg_is_option (*argv, "temporary")) { + if (nmc->complete) + goto opt_error; temporary = TRUE; next_arg (&argc, &argv); } @@ -11135,6 +11317,8 @@ do_connections (NmCli *nmc, int argc, char **argv) next_arg (&argc, &argv); if (nmc_arg_is_option (*argv, "temporary")) { + if (nmc->complete) + goto opt_error; temporary = TRUE; next_arg (&argc, &argv); } @@ -11144,6 +11328,8 @@ do_connections (NmCli *nmc, int argc, char **argv) } 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; @@ -11153,8 +11339,10 @@ do_connections (NmCli *nmc, int argc, char **argv) return nmc->return_value; opt_error: - g_string_printf (nmc->return_text, _("Error: %s."), error->message); - nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + 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; } diff --git a/clients/cli/nmcli-completion b/clients/cli/nmcli-completion index ffab7d8b99..d5bd80de5c 100644 --- a/clients/cli/nmcli-completion +++ b/clients/cli/nmcli-completion @@ -231,14 +231,14 @@ _nmcli_compl_OPTIONS() mode) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "tabular multiline" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; colors) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "yes no auto" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; @@ -249,21 +249,21 @@ _nmcli_compl_OPTIONS() connection 802-3-ethernet 802-1x 802-11-wireless 802-11-wireless-security ipv4 ipv6 serial ppp pppoe gsm cdma bluetooth 802-11-olpc-mesh vpn wimax infiniband bond vlan adsl bridge bridge-port team team-port dcb tun ip-tunnel macvlan vxlan GENERAL IP4 DHCP4 IP6 DHCP6 VPN profile active" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; escape) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "no yes" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; wait) if [[ "${#words[@]}" -eq 2 ]]; then _nmcli_list "" - return 0 + return 0 fi _nmcli_array_delete_at words 0 1 ;; @@ -761,21 +761,14 @@ _nmcli_compl_COMMAND_nl() { _nmcli_compl_PROPERTIES() { - while [[ "${#words[@]}" -gt 0 ]]; do - if [[ ${#words[@]} -le 1 ]]; then - local PREFIX="" - - if [[ "${words[0]:0:1}" == [+-] ]]; then - PREFIX="${words[0]:0:1}" - fi - _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print "'$PREFIX'"$1}')" - return 0 - elif [[ ${#words[@]} -le 2 ]]; then + while [[ ${#words[@]} -gt 0 ]]; do + if [[ ${#words[@]} -eq 1 ]]; then + _nmcli_list_nl "$(nmcli --complete-args connection modify "$@" "${words[@]}" 2>/dev/null)" return 0 fi _nmcli_array_delete_at words 0 1 done - _nmcli_list_nl "$(echo -e 'print\nquit\nyes' |nmcli c edit "$@" 2>/dev/null |awk -F: '/\..*:/ {print $1}')" + return 0 } _nmcli() @@ -802,22 +795,18 @@ _nmcli() cur='' fi - local OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS OPTIONS_MANDATORY COMMAND_ARGS_WAIT_OPTIONS OPTIONS_IP OPTIONS_MANDATORY OPTIONS_NEXT_GROUP OPTIONS_SEP OPTIONS_REPEATABLE - local COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID OPTIONS_MANDATORY_IFNAME HELP_ONLY_AS_FIRST - local COMMAND_CONNECTION_ACTIVE="" + local OPTIONS OPTIONS_UNKNOWN_OPTION OPTIONS_TYPE OPTIONS_TYPED OPTIONS_IP OPTIONS_NEXT_GROUP \ + OPTIONS_SEP OPTIONS_REPEATABLE OPTIONS_MANDATORY OPTIONS_MANDATORY_IFNAME \ + COMMAND_ARGS_WAIT_OPTIONS COMMAND_CONNECTION_TYPE COMMAND_CONNECTION_ID \ + COMMAND_CONNECTION_ACTIVE="" HELP_ONLY_AS_FIRST="" \ + LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) - HELP_ONLY_AS_FIRST= - local LONG_OPTIONS=(terse pretty mode fields colors escape ask show-secrets wait version help) _nmcli_compl_OPTIONS i=$? - if [[ "$HELP_ONLY_AS_FIRST" == '0' ]]; then - # got a --help. No more completion. - return 0 - fi - case $i in 0) + # We have just completed an option or got an --help: terminate. return 0 ;; 1) diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index 7e788654d5..84bb4a23df 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -154,7 +154,18 @@ parse_command_line (NmCli *nmc, int argc, char **argv) base = argv[0]; 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")) + return nmc->return_value; + nmc->complete = TRUE; + argv[1] = argv[0]; + argc--; argv++; + } /* parse options */ while (argc > 1) { char *opt = argv[1]; @@ -545,6 +556,7 @@ nmc_init (NmCli *nmc) nmc->output_data = g_ptr_array_new_full (20, g_free); memset (&nmc->print_fields, '\0', sizeof (NmcPrintFields)); nmc->ask = FALSE; + nmc->complete = FALSE; nmc->show_secrets = FALSE; nmc->use_colors = NMC_USE_COLOR_AUTO; nmc->in_editor = FALSE; diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 34100f0026..c867ad5f9f 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -153,6 +153,7 @@ typedef struct _NmCli { GPtrArray *output_data; /* GPtrArray of arrays of NmcOutputField structs - accumulates data for output */ NmcPrintFields print_fields; /* Structure with field indices to print */ gboolean ask; /* Ask for missing parameters: option '--ask' */ + gboolean complete; /* Autocomplete the command line */ gboolean show_secrets; /* Whether to display secrets (both input and output): option '--show-secrets' */ gboolean in_editor; /* Whether running the editor - nmcli con edit' */ gboolean editor_status_line; /* Whether to display status line in connection editor */ |