diff --git a/src/nmcli/connections.c b/src/nmcli/connections.c index 6ce590d50..ed7a7b62d 100644 --- a/src/nmcli/connections.c +++ b/src/nmcli/connections.c @@ -5229,19 +5229,101 @@ get_value(const char **value, return TRUE; } -gboolean -nmc_process_connection_properties(NmCli *nmc, - NMConnection *connection, - int *argc, - const char *const **argv, - gboolean allow_setting_removal, - GError **error) +static int +_copy_connection_properties(const char ***dst, + const char *const *src, + gboolean invert_match, + const char *const *options_list_match) { + const char *option; + gboolean match; + int count = 0; + + while (*src) { + option = (**src == '+' || **src == '-') ? *src + 1 : *src; + match = _nm_g_strv_contains(options_list_match, option); + match = invert_match ? !match : match; + if (match) { + *((*dst)++) = src[0]; + *((*dst)++) = src[1]; + count += 2; + } + src++; + if (*src) /* Might be the NULL termination, already */ + src++; + } + + return count; +} + +gboolean +nmc_process_connection_properties(NmCli *nmc, + NMConnection *connection, + int argc, + const char *const *argv, + gboolean allow_setting_removal, + GError **error) +{ + gs_free const char **to_free = NULL; + + if (argc == 0) + return TRUE; + /* First check if we have a port-type, as this would mean we will not * have ip properties but possibly others, port-type specific. + * Then check connection.type and connection.controller, as port-type might + * be deduced from them. + * Don't reorder if we are doing CLI argument completion, as it might give + * unexpected results */ + if (!nmc->complete) { + const char **dst; + + dst = to_free = g_new(const char *, argc + 1); + + argc = _copy_connection_properties( + &dst, + argv, + FALSE, + NM_MAKE_STRV(NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_PORT_TYPE, + "port-type", /* alias */ + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_SLAVE_TYPE, + "slave-type" /* alias */)); + argc += _copy_connection_properties(&dst, + argv, + FALSE, + NM_MAKE_STRV(NM_SETTING_CONNECTION_SETTING_NAME + "." NM_SETTING_CONNECTION_TYPE, + "type" /* alias */)); + argc += _copy_connection_properties( + &dst, + argv, + FALSE, + NM_MAKE_STRV(NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_CONTROLLER, + "controller", /* alias */ + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_MASTER, + "master" /* alias */)); + argc += _copy_connection_properties( + &dst, + argv, + TRUE, + NM_MAKE_STRV(NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_PORT_TYPE, + "port-type", + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_SLAVE_TYPE, + "slave-type", + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_TYPE, + "type", + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_CONTROLLER, + "controller", + NM_SETTING_CONNECTION_SETTING_NAME "." NM_SETTING_CONNECTION_MASTER, + "master")); + + *dst = NULL; /* NULL terminated as expected by get_value() */ + argv = to_free; + } + /* Go through arguments and set properties */ - do { + while (argc) { const NMMetaSettingValidPartItem *const *type_settings; const NMMetaSettingValidPartItem *const *port_settings; NMMetaAccessorModifier modifier; @@ -5260,19 +5342,10 @@ nmc_process_connection_properties(NmCli *nmc, ensure_settings(connection, port_settings); ensure_settings(connection, type_settings); - if (*argc <= 0) { - g_set_error_literal(error, - NMCLI_ERROR, - NMC_RESULT_ERROR_USER_INPUT, - _("Error: . argument is missing.")); - return FALSE; - } - nm_assert(argv); nm_assert(*argv); - nm_assert(**argv); - option_orig = **argv; + option_orig = *argv; switch (option_orig[0]) { case '+': @@ -5294,15 +5367,15 @@ nmc_process_connection_properties(NmCli *nmc, NMSetting *ss; const char *setting_name; - (*argc)--; - (*argv)++; + argc--; + argv++; - if (*argc == 1 && nmc->complete) { + if (argc == 1 && nmc->complete) { complete_existing_setting(nmc, connection, value); - return TRUE; + break; } - if (!*argc) { + if (!argc) { g_set_error_literal(error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, @@ -5310,9 +5383,9 @@ nmc_process_connection_properties(NmCli *nmc, return FALSE; } - setting_name = **argv; - (*argc)--; - (*argv)++; + setting_name = *argv; + argc--; + argv++; ss = is_setting_valid(connection, type_settings, port_settings, setting_name); if (!ss) { @@ -5342,7 +5415,7 @@ nmc_process_connection_properties(NmCli *nmc, /* This seems like a . (such as "connection.id" or "bond.mode"), * optionally prefixed with "+| or "-". */ - if (*argc == 1 && nmc->complete) + if (argc == 1 && nmc->complete) complete_property_name(nmc, connection, modifier, option_sett, option_prop); option_sett_expanded = @@ -5358,14 +5431,14 @@ nmc_process_connection_properties(NmCli *nmc, return FALSE; } - (*argc)--; - (*argv)++; - if (!get_value(&value, argc, argv, option_orig, error)) + argc--; + argv++; + if (!get_value(&value, &argc, &argv, option_orig, error)) return FALSE; - if (!*argc && nmc->complete) { + if (!argc && nmc->complete) { complete_property(nmc, option_sett, option_prop, value ?: "", connection); - return TRUE; + break; } if (!set_property(nmc->client, @@ -5447,7 +5520,7 @@ nmc_process_connection_properties(NmCli *nmc, } if (!chosen) { - if (*argc == 1 && nmc->complete) { + if (argc == 1 && nmc->complete) { if (allow_setting_removal && g_str_has_prefix("remove", option)) nmc_print("remove\n"); complete_property_name(nmc, connection, modifier, option, NULL); @@ -5460,21 +5533,20 @@ nmc_process_connection_properties(NmCli *nmc, return FALSE; } - if (*argc == 1 && nmc->complete) + if (argc == 1 && nmc->complete) complete_property_name(nmc, connection, modifier, option, NULL); - (*argc)--; - (*argv)++; - if (!get_value(&value, argc, argv, option_orig, error)) + argc--; + argv++; + if (!get_value(&value, &argc, &argv, option_orig, error)) return FALSE; - if (!*argc && nmc->complete) + if (!argc && nmc->complete) complete_option(nmc, chosen, value ?: "", connection); if (!set_option(nmc, connection, chosen, value, TRUE, error)) return FALSE; - - } while (*argc); + } return TRUE; } @@ -5911,6 +5983,7 @@ nmc_add_connection(NmCli *nmc, NMConnection *connection, gboolean temporary) static void do_connection_add(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const *argv) { + gs_unref_ptrarray GPtrArray *props; gs_unref_object NMConnection *connection = NULL; NMSettingConnection *s_con; gs_free_error GError *error = NULL; @@ -5929,16 +6002,21 @@ do_connection_add(const NMCCommand *cmd, NmCli *nmc, int argc, const char *const s_con = (NMSettingConnection *) nm_setting_connection_new(); nm_connection_add_setting(connection, NM_SETTING(s_con)); -read_properties: - g_clear_error(&error); - /* Get the arguments from the command line if any */ - if (argc && !nmc_process_connection_properties(nmc, connection, &argc, &argv, FALSE, &error)) { - if (nm_streq0(*argv, "--") && !seen_dash_dash) { + props = g_ptr_array_new_full(sizeof(const char *) * (argc + 1), NULL); + + while (argc) { + if (nm_streq0(*argv, "--")) { /* This is for compatibility with older nmcli that required * options and properties to be separated with "--" */ - seen_dash_dash = TRUE; - next_arg(nmc, &argc, &argv, NULL); - goto read_properties; + if (seen_dash_dash) { + g_string_printf(nmc->return_text, + _("Error: argument '--' can only be passed once")); + nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; + goto finish; + } else { + seen_dash_dash = TRUE; + next_arg(nmc, &argc, &argv, NULL); + } } else if (nm_streq0(*argv, "save")) { /* It would be better if "save" was a separate argument and not * mixed with properties, but there's not much we can do about it now. */ @@ -5951,16 +6029,31 @@ read_properties: nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } - g_clear_error(&error); if (!nmc_string_to_bool(*argv, &save_bool, &error)) { g_string_printf(nmc->return_text, _("Error: 'save': %s."), error->message); nmc->return_value = NMC_RESULT_ERROR_USER_INPUT; goto finish; } next_arg(nmc, &argc, &argv, NULL); - goto read_properties; + } else { + g_ptr_array_add(props, (gpointer) *argv); + argc--; + argv++; + if (argc > 0) { + g_ptr_array_add(props, (gpointer) *argv); + argc--; + argv++; + } } + } + g_ptr_array_add(props, NULL); /* Must be NULL terminated */ + if (!nmc_process_connection_properties(nmc, + connection, + props->len - 1, + (const char *const *) props->pdata, + FALSE, + &error)) { g_string_assign(nmc->return_text, error->message); nmc->return_value = error->code; goto finish; @@ -9251,7 +9344,7 @@ do_connection_modify(const NMCCommand *cmd, NmCli *nmc, int argc, const char *co /* Don't insist on having argument if we're running in offline mode. */ if (!nmc->nmc_config.offline || argc > 0) { - if (!nmc_process_connection_properties(nmc, connection, &argc, &argv, TRUE, &error)) { + if (!nmc_process_connection_properties(nmc, connection, argc, argv, TRUE, &error)) { g_string_assign(nmc->return_text, error->message); nmc->return_value = error->code; return; diff --git a/src/nmcli/connections.h b/src/nmcli/connections.h index 48c981704..a343ca256 100644 --- a/src/nmcli/connections.h +++ b/src/nmcli/connections.h @@ -12,12 +12,12 @@ void nmc_monitor_connections(NmCli *nmc); const char *nmc_connection_check_deprecated(NMConnection *c); -gboolean nmc_process_connection_properties(NmCli *nmc, - NMConnection *connection, - int *argc, - const char *const **argv, - gboolean allow_remove_setting, - GError **error); +gboolean nmc_process_connection_properties(NmCli *nmc, + NMConnection *connection, + int argc, + const char *const *args, + gboolean allow_remove_setting, + GError **error); NMMetaColor nmc_active_connection_state_to_color(NMActiveConnection *ac); diff --git a/src/nmcli/devices.c b/src/nmcli/devices.c index b308e4406..9a0181138 100644 --- a/src/nmcli/devices.c +++ b/src/nmcli/devices.c @@ -2596,7 +2596,7 @@ modify_get_applied_cb(GObject *object, GAsyncResult *result, gpointer user_data) argc = info->argc; argv = (const char *const *) info->argv; - if (!nmc_process_connection_properties(info->nmc, connection, &argc, &argv, TRUE, &error)) { + if (!nmc_process_connection_properties(info->nmc, connection, argc, argv, TRUE, &error)) { g_string_assign(nmc->return_text, error->message); nmc->return_value = error->code; quit();