From 85563b7fc7ec2cd21e38debb9b28db342e2e8e7c Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sun, 21 Apr 2019 21:09:51 +0200 Subject: New upstream version 1.18.0 --- clients/cli/connections.c | 256 +- clients/cli/devices.c | 1 + clients/cli/general.c | 4 +- clients/cli/meson.build | 2 +- clients/cli/settings.c | 261 +- clients/cli/settings.h | 13 +- clients/cli/utils.c | 3 +- clients/common/meson.build | 4 +- clients/common/nm-client-utils.c | 4 + clients/common/nm-client-utils.h | 8 +- clients/common/nm-meta-setting-access.c | 16 + clients/common/nm-meta-setting-access.h | 2 + clients/common/nm-meta-setting-desc.c | 4128 +++++++++----------- clients/common/nm-meta-setting-desc.h | 83 +- clients/common/nm-secret-agent-simple.c | 49 +- clients/common/nm-vpn-helpers.c | 67 +- clients/common/settings-docs.h | 4 + clients/common/settings-docs.h.in | 4 + clients/common/tests/meson.build | 2 +- clients/meson.build | 2 +- .../test-client.check-on-disk/test_001.expected | 92 +- .../test-client.check-on-disk/test_002.expected | 52 +- .../test-client.check-on-disk/test_003.expected | 1474 +++---- .../test-client.check-on-disk/test_004.expected | 3542 +++++++++++------ clients/tests/test-client.py | 4 + clients/tui/meson.build | 3 +- clients/tui/nmt-editor.c | 4 + clients/tui/nmt-mac-entry.c | 2 +- 28 files changed, 5444 insertions(+), 4642 deletions(-) (limited to 'clients') diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 6db44f87..6ee3b49f 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -1276,12 +1276,18 @@ static void update_secrets_in_connection (NMRemoteConnection *remote, NMConnection *local) { GetSecretsData data = { 0, }; + GType setting_type; int i; data.local = local; data.loop = g_main_loop_new (NULL, FALSE); for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { + setting_type = nm_meta_setting_infos[i].get_setting_gtype(); + if (!nm_connection_get_setting (NM_CONNECTION (remote), setting_type)) + continue; + if (!nm_meta_setting_info_editor_has_secrets (nm_meta_setting_info_editor_find_by_gtype (setting_type))) + continue; data.setting_name = nm_meta_setting_infos[i].setting_name; nm_remote_connection_get_secrets_async (remote, nm_meta_setting_infos[i].setting_name, @@ -1522,15 +1528,13 @@ split_required_fields_for_con_show (const char *input, char **active_flds, GError **error) { - char **fields, **iter; - char *dot; - GString *str1, *str2; - gboolean found; + gs_free const char **fields = NULL; + const char *const*iter; + nm_auto_free_gstring GString *str1 = NULL; + nm_auto_free_gstring GString *str2 = NULL; gboolean group_profile = FALSE; gboolean group_active = FALSE; - gboolean success = TRUE; - gboolean is_all, is_common; - int i; + gboolean do_free; if (!input) { *profile_flds = NULL; @@ -1541,25 +1545,30 @@ split_required_fields_for_con_show (const char *input, str1 = g_string_new (NULL); str2 = g_string_new (NULL); - /* Split supplied fields string */ - fields = g_strsplit_set (input, ",", -1); + fields = nm_utils_strsplit_set_with_empty (input, ","); for (iter = fields; iter && *iter; iter++) { - g_strstrip (*iter); - dot = strchr (*iter, '.'); + char *s_mutable = (char *) (*iter); + char *dot; + gboolean is_all; + gboolean is_common; + gboolean found; + int i; + + g_strstrip (s_mutable); + dot = strchr (s_mutable, '.'); if (dot) *dot = '\0'; - is_all = !dot && strcasecmp (*iter, "all") == 0; - is_common = !dot && strcasecmp (*iter, "common") == 0; + is_all = !dot && strcasecmp (s_mutable, "all") == 0; + is_common = !dot && strcasecmp (s_mutable, "common") == 0; found = FALSE; - for (i = 0; i < _NM_META_SETTING_TYPE_NUM; i++) { if ( is_all || is_common - || !strcasecmp (*iter, nm_meta_setting_infos[i].setting_name)) { + || !strcasecmp (s_mutable, nm_meta_setting_infos[i].setting_name)) { if (dot) *dot = '.'; - g_string_append (str1, *iter); + g_string_append (str1, s_mutable); g_string_append_c (str1, ','); found = TRUE; break; @@ -1567,12 +1576,13 @@ split_required_fields_for_con_show (const char *input, } if (found) continue; + for (i = 0; nmc_fields_con_active_details_groups[i]; i++) { if ( is_all || is_common - || !strcasecmp (*iter, nmc_fields_con_active_details_groups[i]->name)) { + || !strcasecmp (s_mutable, nmc_fields_con_active_details_groups[i]->name)) { if (dot) *dot = '.'; - g_string_append (str2, *iter); + g_string_append (str2, s_mutable); g_string_append_c (str2, ','); found = TRUE; break; @@ -1581,55 +1591,49 @@ split_required_fields_for_con_show (const char *input, if (!found) { if (dot) *dot = '.'; - if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_PROFILE)) + if (!strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_PROFILE)) group_profile = TRUE; - else if (!strcasecmp (*iter, CON_SHOW_DETAIL_GROUP_ACTIVE)) + else if (!strcasecmp (s_mutable, CON_SHOW_DETAIL_GROUP_ACTIVE)) group_active = TRUE; else { - char *allowed1 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), NULL); - char *allowed2 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, NULL); + gs_free char *allowed1 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nm_meta_setting_infos_editor_p (), NULL); + gs_free char *allowed2 = nm_meta_abstract_infos_get_names_str ((const NMMetaAbstractInfo *const*) nmc_fields_con_active_details_groups, NULL); + g_set_error (error, NMCLI_ERROR, 0, _("invalid field '%s'; allowed fields: %s and %s, or %s,%s"), - *iter, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); - g_free (allowed1); - g_free (allowed2); - success = FALSE; - break; + s_mutable, allowed1, allowed2, CON_SHOW_DETAIL_GROUP_PROFILE, CON_SHOW_DETAIL_GROUP_ACTIVE); + return FALSE; } } } - if (fields) - g_strfreev (fields); /* Handle pseudo groups: profile, active */ - if (success && group_profile) { + if (group_profile) { if (str1->len > 0) { g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), CON_SHOW_DETAIL_GROUP_PROFILE); - success = FALSE; - } else - g_string_assign (str1, "all,"); + return FALSE; + } + g_string_assign (str1, "all,"); } - if (success && group_active) { + if (group_active) { if (str2->len > 0) { g_set_error (error, NMCLI_ERROR, 0, _("'%s' has to be alone"), CON_SHOW_DETAIL_GROUP_ACTIVE); - success = FALSE; - } else - g_string_assign (str2, "all,"); + return FALSE; + } + g_string_assign (str2, "all,"); } - if (success) { - if (str1->len > 0) - g_string_truncate (str1, str1->len - 1); - if (str2->len > 0) - g_string_truncate (str2, str2->len - 1); - *profile_flds = g_string_free (str1, str1->len == 0); - *active_flds = g_string_free (str2, str2->len == 0); - } else { - g_string_free (str1, TRUE); - g_string_free (str2, TRUE); - } - return success; + if (str1->len > 0) + g_string_truncate (str1, str1->len - 1); + if (str2->len > 0) + g_string_truncate (str2, str2->len - 1); + + do_free = (str1->len == 0); + *profile_flds = g_string_free (g_steal_pointer (&str1), do_free); + do_free = (str2->len == 0); + *active_flds = g_string_free (g_steal_pointer (&str2), do_free); + return TRUE; } typedef enum { @@ -1870,7 +1874,7 @@ parse_preferred_connection_order (const char *order, GError **error) gboolean inverse, unique; int i; - strv = nm_utils_strsplit_set (order, ":", FALSE); + strv = nm_utils_strsplit_set (order, ":"); if (!strv) { g_set_error (error, NMCLI_ERROR, 0, _("incorrect string '%s' of '--order' option"), order); @@ -2674,7 +2678,7 @@ parse_passwords (const char *passwd_file, GError **error) return NULL; } - strv = nm_utils_strsplit_set (contents, "\r\n", FALSE); + strv = nm_utils_strsplit_set (contents, "\r\n"); for (iter = strv; *iter; iter++) { gs_free char *iter_s = g_strdup (*iter); @@ -3959,11 +3963,12 @@ set_property (NMClient *client, char modifier, GError **error) { - gs_free char *property_name = NULL, *value_free = NULL; + gs_free char *property_name = NULL; + gs_free_error GError *local = NULL; NMSetting *setting; - GError *local = NULL; - g_assert (setting_name && setting_name[0]); + nm_assert (setting_name && setting_name[0]); + nm_assert (NM_IN_SET (modifier, '\0', '+', '-')); setting = nm_connection_get_setting_by_name (connection, setting_name); if (!setting) { @@ -3977,48 +3982,26 @@ set_property (NMClient *client, g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, _("Error: invalid property '%s': %s."), property, local->message); - g_clear_error (&local); return FALSE; } - if (modifier != '-') { - /* Set/add value */ - if (modifier != '+') { - /* We allow the existing property value to be passed as parameter, - * so make a copy if we are going to free it. - */ - value = value_free = g_strdup (value); - nmc_setting_reset_property (setting, property_name, NULL); - } - if (!nmc_setting_set_property (client, setting, property_name, value, &local)) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: failed to modify %s.%s: %s."), - setting_name, property, local->message); - g_clear_error (&local); - return FALSE; - } - } else { - /* Remove value - * - either empty: remove whole value - * - or specified by index <0-n>: remove item at the index - * - or option name: remove item with the option name - */ - if (value) { - unsigned long idx; - - if (nmc_string_to_uint (value, TRUE, 0, G_MAXUINT32, &idx)) - nmc_setting_remove_property_option (setting, property_name, NULL, idx, &local); - else - nmc_setting_remove_property_option (setting, property_name, value, 0, &local); - if (local) { - g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, - _("Error: failed to remove a value from %s.%s: %s."), - setting_name, property, local->message); - g_clear_error (&local); - return FALSE; - } - } else - nmc_setting_reset_property (setting, property_name, NULL); + if (!nmc_setting_set_property (client, + setting, + property_name, + ( (modifier == '-' && !value) + ? '\0' + : modifier), + value, + &local)) { + g_set_error (error, NMCLI_ERROR, NMC_RESULT_ERROR_USER_INPUT, + _("Error: failed to %s %s.%s: %s."), + ( modifier != '-' + ? "modify" + : "remove a value from"), + setting_name, + property, + local->message); + return FALSE; } /* Don't ask for this property in interactive mode. */ @@ -6904,7 +6887,6 @@ property_edit_submenu (NmCli *nmc, gs_free char *cmd_property_user = NULL; gs_free char *cmd_property_arg = NULL; gs_free char *prop_val_user = NULL; - nm_auto_unset_gvalue GValue prop_g_value = G_VALUE_INIT; gboolean removed; gboolean dirty; @@ -6954,24 +6936,17 @@ property_edit_submenu (NmCli *nmc, } else prop_val_user = g_strdup (cmd_property_arg); - /* nmc_setting_set_property() only adds new value, thus we have to - * remove the original value and save it for error cases. - */ - if (cmdsub == NMC_EDITOR_SUB_CMD_SET) { - nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); - nmc_property_set_default_value (curr_setting, prop_name); - } - - set_result = nmc_setting_set_property (nmc->client, curr_setting, prop_name, prop_val_user, &tmp_err); + set_result = nmc_setting_set_property (nmc->client, + curr_setting, + prop_name, + (cmdsub == NMC_EDITOR_SUB_CMD_SET) + ? '\0' + : '+', + prop_val_user, + &tmp_err); if (!set_result) { g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); - if (cmdsub == NMC_EDITOR_SUB_CMD_SET) { - /* Block change signals and restore original value */ - g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); - nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); - g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); - } } break; @@ -6982,41 +6957,23 @@ property_edit_submenu (NmCli *nmc, _("Edit '%s' value: "), prop_name); - nmc_property_get_gvalue (curr_setting, prop_name, &prop_g_value); - nmc_property_set_default_value (curr_setting, prop_name); - - if (!nmc_setting_set_property (nmc->client, curr_setting, prop_name, prop_val_user, &tmp_err)) { + if (!nmc_setting_set_property (nmc->client, curr_setting, prop_name, '\0', prop_val_user, &tmp_err)) { g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); - g_signal_handlers_block_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); - nmc_property_set_gvalue (curr_setting, prop_name, &prop_g_value); - g_signal_handlers_unblock_matched (curr_setting, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL); } break; case NMC_EDITOR_SUB_CMD_REMOVE: - if (cmd_property_arg) { - unsigned long val_int = G_MAXUINT32; - gs_free char *option = NULL; - - if (!nmc_string_to_uint (cmd_property_arg, TRUE, 0, G_MAXUINT32, &val_int)) { - option = g_strdup (cmd_property_arg); - g_strstrip (option); - } - - if (!nmc_setting_remove_property_option (curr_setting, prop_name, - option, - (guint32) val_int, - &tmp_err)) { - g_print (_("Error: %s\n"), tmp_err->message); - g_clear_error (&tmp_err); - } - } else { - if (!nmc_setting_reset_property (curr_setting, prop_name, &tmp_err)) { - g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, - tmp_err->message); - g_clear_error (&tmp_err); - } + if (!nmc_setting_set_property (nmc->client, + curr_setting, + prop_name, + ( cmd_property_arg + ? '-' + : '\0'), + cmd_property_arg, + &tmp_err)) { + g_print (_("Error: %s\n"), tmp_err->message); + g_clear_error (&tmp_err); } break; @@ -7367,8 +7324,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t _("Enter '%s' value: "), prop_name); - /* Set property value */ - if (!nmc_setting_set_property (nmc->client, menu_ctx.curr_setting, prop_name, prop_val_user, &tmp_err)) { + if (!nmc_setting_set_property (nmc->client, menu_ctx.curr_setting, prop_name, '+', prop_val_user, &tmp_err)) { g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); } @@ -7428,8 +7384,13 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t prop_name); } - /* Set property value */ - if (!nmc_setting_set_property (nmc->client, ss, prop_name, cmd_arg_v, &tmp_err)) { + /* setting a value in edit mode "appends". That seems unexpected behavior. */ + if (!nmc_setting_set_property (nmc->client, + ss, + prop_name, + cmd_arg_v ? '+' : '\0', + cmd_arg_v, + &tmp_err)) { g_print (_("Error: failed to set '%s' property: %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); @@ -7526,8 +7487,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t if (!prop_name) break; - /* Delete property value */ - if (!nmc_setting_reset_property (menu_ctx.curr_setting, prop_name, &tmp_err)) { + if (!nmc_setting_set_property (nmc->client, menu_ctx.curr_setting, prop_name, '\0', NULL, &tmp_err)) { g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); @@ -7577,8 +7537,7 @@ editor_menu_main (NmCli *nmc, NMConnection *connection, const char *connection_t prop_name = is_property_valid (ss, cmd_arg_p, &tmp_err); if (prop_name) { - /* Delete property value */ - if (!nmc_setting_reset_property (ss, prop_name, &tmp_err)) { + if (!nmc_setting_set_property (nmc->client, ss, prop_name, '\0', NULL, &tmp_err)) { g_print (_("Error: failed to remove value of '%s': %s\n"), prop_name, tmp_err->message); g_clear_error (&tmp_err); @@ -8148,6 +8107,9 @@ editor_init_existing_connection (NMConnection *connection) NMSettingWireless *s_wireless; NMSettingConnection *s_con; + /* FIXME: this approach of connecting handlers to do something is fundamentally + * flawed. See the comment in nmc_setting_ip6_connect_handlers(). */ + s_ip4 = nm_connection_get_setting_ip4_config (connection); s_ip6 = nm_connection_get_setting_ip6_config (connection); s_proxy = nm_connection_get_setting_proxy (connection); @@ -8585,7 +8547,7 @@ delete_cb (GObject *con, GAsyncResult *result, gpointer user_data) if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; g_string_printf (info->nmc->return_text, _("Error: not all connections deleted.")); - g_printerr (_("Error: Connection deletion failed: %s"), + g_printerr (_("Error: Connection deletion failed: %s\n"), error->message); g_error_free (error); info->nmc->return_value = NMC_RESULT_ERROR_CON_DEL; diff --git a/clients/cli/devices.c b/clients/cli/devices.c index 04a8c988..ad3a44c4 100644 --- a/clients/cli/devices.c +++ b/clients/cli/devices.c @@ -3888,6 +3888,7 @@ do_device_wifi_hotspot (NmCli *nmc, int argc, char **argv) info->nmc = nmc; info->device = device; info->hotspot = TRUE; + info->create = TRUE; nm_client_add_and_activate_connection_async (nmc->client, connection, diff --git a/clients/cli/general.c b/clients/cli/general.c index 2c22bdc9..d713426b 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -23,7 +23,7 @@ #include -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-client-utils.h" @@ -1350,7 +1350,7 @@ do_overview (NmCli *nmc, int argc, char **argv) g_print (_("Use \"nmcli device show\" to get complete information about known devices and\n" "\"nmcli connection show\" to get an overview on active connection profiles.\n" "\n" - "Consult nmcli(1) and nmcli-examples(5) manual pages for complete usage details.\n")); + "Consult nmcli(1) and nmcli-examples(7) manual pages for complete usage details.\n")); return NMC_RESULT_SUCCESS; } diff --git a/clients/cli/meson.build b/clients/cli/meson.build index 11fe1cd1..396466a2 100644 --- a/clients/cli/meson.build +++ b/clients/cli/meson.build @@ -22,7 +22,7 @@ deps = [ libnm_dep, libnmc_base_dep, libnmc_dep, - nm_core_dep, + libnm_core_dep, readline_dep, ] diff --git a/clients/cli/settings.c b/clients/cli/settings.c index a04c8eb6..2446cb08 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -24,7 +24,7 @@ #include #include -#include "nm-common-macros.h" +#include "nm-libnm-core-intern/nm-common-macros.h" #include "nm-client-utils.h" #include "nm-vpn-helpers.h" @@ -89,7 +89,7 @@ ipv4_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_dat static void ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { - static GValue value = G_VALUE_INIT; + static GPtrArray *old_value = NULL; static gboolean answered = FALSE; static gboolean answer = FALSE; @@ -103,17 +103,17 @@ ipv4_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) answer = get_answer ("ipv4.addresses", NULL); } if (answer) { - if (G_IS_VALUE (&value)) - g_value_unset (&value); - nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + nm_clear_pointer (&old_value, g_ptr_array_unref); + g_object_get (object, NM_SETTING_IP_CONFIG_ADDRESSES, &old_value, NULL); g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); } } } else { answered = FALSE; - if (G_IS_VALUE (&value)) { - nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); - g_value_unset (&value); + if (old_value) { + gs_unref_ptrarray GPtrArray *v = g_steal_pointer (&old_value); + + g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, v, NULL); } } @@ -142,6 +142,25 @@ ipv6_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_dat } } else { answered = FALSE; + /* FIXME: editor_init_existing_connection() and registering handlers is not the + * right approach. + * + * This only happens to work because in nmcli's edit mode + * tends to append addresses -- instead of setting them. + * If we would change that (to behavior I'd expect), we'd get: + * + * nmcli> set ipv6.addresses fc01::1:5/68 + * Do you also want to set 'ipv6.method' to 'manual'? [yes]: y + * nmcli> set ipv6.addresses fc01::1:6/68 + * Do you also want to set 'ipv6.method' to 'manual'? [yes]: + * + * That's because nmc_setting_set_property() calls set_fcn(). With modifier '\0' + * (set), it would first clear all addresses before adding the address. Thereby + * emitting multiple property changed signals. + * + * That can be avoided by freezing/thawing the signals, but this solution + * here is ugly in general. + */ if (!g_strcmp0 (nm_setting_ip_config_get_method (NM_SETTING_IP_CONFIG (object)), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) g_object_set (object, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); } @@ -152,7 +171,7 @@ ipv6_addresses_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_dat static void ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) { - static GValue value = G_VALUE_INIT; + static GPtrArray *old_value = NULL; static gboolean answered = FALSE; static gboolean answer = FALSE; @@ -166,17 +185,17 @@ ipv6_method_changed_cb (GObject *object, GParamSpec *pspec, gpointer user_data) answer = get_answer ("ipv6.addresses", NULL); } if (answer) { - if (G_IS_VALUE (&value)) - g_value_unset (&value); - nmc_property_get_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); + nm_clear_pointer (&old_value, g_ptr_array_unref); + g_object_get (object, NM_SETTING_IP_CONFIG_ADDRESSES, &old_value, NULL); g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, NULL, NULL); } } } else { answered = FALSE; - if (G_IS_VALUE (&value)) { - nmc_property_set_gvalue (NM_SETTING (object), NM_SETTING_IP_CONFIG_ADDRESSES, &value); - g_value_unset (&value); + if (old_value) { + gs_unref_ptrarray GPtrArray *v = g_steal_pointer (&old_value); + + g_object_set (object, NM_SETTING_IP_CONFIG_ADDRESSES, v, NULL); } } @@ -319,7 +338,7 @@ _set_fcn_precheck_connection_secondaries (NMClient *client, char **iter; gboolean modified = FALSE; - strv0 = nm_utils_strsplit_set (value, " \t,", FALSE); + strv0 = nm_utils_strsplit_set (value, " \t,"); if (!strv0) return TRUE; @@ -509,151 +528,72 @@ nmc_setting_get_property_parsable (NMSetting *setting, const char *prop, GError return get_property_val (setting, prop, NM_META_ACCESSOR_GET_TYPE_PARSABLE, TRUE, error); } -static gboolean -_set_fcn_call (const NMMetaPropertyInfo *property_info, - NMSetting *setting, - const char *value, - GError **error) -{ - return property_info->property_type->set_fcn (property_info, - nmc_meta_environment, - nmc_meta_environment_arg, - setting, - value, - error); -} - -/* - * Generic function for setting property value. - * - * Sets property=value in setting by calling specialized functions. - * If value is NULL then default property value is set. - * - * Returns: TRUE on success; FALSE on failure and sets error - */ gboolean -nmc_setting_set_property (NMClient *client, NMSetting *setting, const char *prop, const char *value, GError **error) +nmc_setting_set_property (NMClient *client, + NMSetting *setting, + const char *prop, + char modifier, + const char *value, + GError **error) { const NMMetaPropertyInfo *property_info; + gs_free char *value_to_free = NULL; + gboolean success; g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (NM_IN_SET (modifier, '\0', '-', '+'), FALSE); - if ((property_info = nm_meta_property_info_find_by_setting (setting, prop))) { - - if (!value) { - /* No value argument sets default value */ - nmc_property_set_default_value (setting, prop); - return TRUE; - } - - if (property_info->property_type->set_fcn) { - switch (property_info->setting_info->general->meta_type) { - case NM_META_SETTING_TYPE_CONNECTION: - if (nm_streq (property_info->property_name, NM_SETTING_CONNECTION_SECONDARIES)) { - gs_free char *value_coerced = NULL; - - if (!_set_fcn_precheck_connection_secondaries (client, value, &value_coerced, error)) - return FALSE; + if (!(property_info = nm_meta_property_info_find_by_setting (setting, prop))) + goto out_fail_read_only; + if (!property_info->property_type->set_fcn) + goto out_fail_read_only; - return _set_fcn_call (property_info, - setting, - value_coerced ?: value, - error); - } - break; - default: - break; - } - return _set_fcn_call (property_info, - setting, - value, - error); - } + if ( NM_IN_SET (modifier, '+', '-') + && !value) { + /* nothing to do. */ + return TRUE; } - g_set_error_literal (error, 1, 0, _("the property can't be changed")); - return FALSE; -} - -void -nmc_property_set_default_value (NMSetting *setting, const char *prop) -{ - GValue value = G_VALUE_INIT; - GParamSpec *param_spec; - - param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); - if (param_spec) { - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); - g_param_value_set_default (param_spec, &value); - g_object_set_property (G_OBJECT (setting), prop, &value); + if ( modifier == '-' + && !property_info->property_type->set_supports_remove) { + /* The property is a plain property. It does not support '-'. + * + * Maybe we should fail, but just return silently. */ + return TRUE; } -} - -/* - * Generic function for resetting (single value) properties. - * - * The function resets the property value to the default one. It respects - * nmcli restrictions for changing properties. So if 'set_func' is NULL, - * resetting the value is denied. - * - * Returns: TRUE on success; FALSE on failure and sets error - */ -gboolean -nmc_setting_reset_property (NMSetting *setting, const char *prop, GError **error) -{ - const NMMetaPropertyInfo *property_info; - - g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - if ((property_info = nm_meta_property_info_find_by_setting (setting, prop))) { - if (property_info->property_type->set_fcn) { - nmc_property_set_default_value (setting, prop); - return TRUE; + if (value) { + switch (property_info->setting_info->general->meta_type) { + case NM_META_SETTING_TYPE_CONNECTION: + if (nm_streq (property_info->property_name, NM_SETTING_CONNECTION_SECONDARIES)) { + if (!_set_fcn_precheck_connection_secondaries (client, value, &value_to_free, error)) + return FALSE; + if (value_to_free) + value = value_to_free; + } + break; + default: + break; } } - g_set_error_literal (error, 1, 0, _("the property can't be changed")); + g_object_freeze_notify (G_OBJECT (setting)); + success = property_info->property_type->set_fcn (property_info, + nmc_meta_environment, + nmc_meta_environment_arg, + setting, + modifier, + value, + error); + g_object_thaw_notify (G_OBJECT (setting)); + return success; + +out_fail_read_only: + nm_utils_error_set (error, NM_UTILS_ERROR_UNKNOWN, _("the property can't be changed")); return FALSE; } -/* - * Generic function for removing items for collection-type properties. - * - * If 'option' is not NULL, it tries to remove it, otherwise 'idx' is used. - * For single-value properties (not having specialized remove function) this - * function does nothing and just returns TRUE. - * - * Returns: TRUE on success; FALSE on failure and sets error - */ -gboolean -nmc_setting_remove_property_option (NMSetting *setting, - const char *prop, - const char *option, - guint32 idx, - GError **error) -{ - const NMMetaPropertyInfo *property_info; - - g_return_val_if_fail (NM_IS_SETTING (setting), FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - if ((property_info = nm_meta_property_info_find_by_setting (setting, prop))) { - if (property_info->property_type->remove_fcn) { - return property_info->property_type->remove_fcn (property_info, - nmc_meta_environment, - nmc_meta_environment_arg, - setting, - option, - idx, - error); - } - } - - return TRUE; -} - /* * Get valid property names for a setting. * @@ -749,41 +689,6 @@ nmc_setting_get_property_desc (NMSetting *setting, const char *prop) nmcli_desc ?: ""); } -/* - * Gets setting:prop property value and returns it in 'value'. - * Caller is responsible for freeing the GValue resources using g_value_unset() - */ -gboolean -nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value) -{ - GParamSpec *param_spec; - - param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); - if (param_spec) { - memset (value, 0, sizeof (GValue)); - g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (param_spec)); - g_object_get_property (G_OBJECT (setting), prop, value); - return TRUE; - } - return FALSE; -} - -/* - * Sets setting:prop property value from 'value'. - */ -gboolean -nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value) -{ - GParamSpec *param_spec; - - param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), prop); - if (param_spec && G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (param_spec)) { - g_object_set_property (G_OBJECT (setting), prop, value); - return TRUE; - } - return FALSE; -} - /*****************************************************************************/ gboolean diff --git a/clients/cli/settings.h b/clients/cli/settings.h index 4e7e38df..1ff93685 100644 --- a/clients/cli/settings.h +++ b/clients/cli/settings.h @@ -45,20 +45,9 @@ char *nmc_setting_get_property_parsable (NMSetting *setting, gboolean nmc_setting_set_property (NMClient *client, NMSetting *setting, const char *prop, + char modifier, const char *val, GError **error); -gboolean nmc_setting_reset_property (NMSetting *setting, - const char *prop, - GError **error); -gboolean nmc_setting_remove_property_option (NMSetting *setting, - const char *prop, - const char *option, - guint32 idx, - GError **error); -void nmc_property_set_default_value (NMSetting *setting, const char *prop); - -gboolean nmc_property_get_gvalue (NMSetting *setting, const char *prop, GValue *value); -gboolean nmc_property_set_gvalue (NMSetting *setting, const char *prop, GValue *value); gboolean setting_details (const NmcConfig *nmc_config, NMSetting *setting, const char *one_prop); diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 0e74ba88..a8b81279 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -508,7 +508,8 @@ nmc_string_to_arg_array (const char *line, const char *delim, gboolean unquote, gs_free const char **arr0 = NULL; char **arr; - arr0 = nm_utils_strsplit_set (line ?: "", delim ?: " \t", FALSE); + arr0 = nm_utils_strsplit_set (line ?: "", + delim ?: " \t"); if (!arr0) arr = g_new0 (char *, 1); else diff --git a/clients/common/meson.build b/clients/common/meson.build index b4b6bcac..fed0f3bf 100644 --- a/clients/common/meson.build +++ b/clients/common/meson.build @@ -4,7 +4,7 @@ nm_polkit_listener = files('nm-polkit-listener.c') deps = [ libnm_dep, - nm_core_dep, + shared_nm_libnm_core_aux_dep, ] cflags = clients_cflags + [ @@ -55,7 +55,7 @@ libnmc = static_library( sources: files( 'nm-meta-setting-access.c', 'nm-meta-setting-desc.c', - ) + shared_nm_meta_setting_c + shared_nm_ethtool_utils_c + [settings_docs_source], + ) + shared_nm_meta_setting_c + [settings_docs_source], dependencies: deps, c_args: cflags, link_with: libnmc_base, diff --git a/clients/common/nm-client-utils.c b/clients/common/nm-client-utils.c index 1241131a..0a155dae 100644 --- a/clients/common/nm-client-utils.c +++ b/clients/common/nm-client-utils.c @@ -82,6 +82,10 @@ nmc_string_to_uint_base (const char *str, char *end; unsigned long int tmp; + if (!str || !str[0]) + return FALSE; + + /* FIXME: don't use this function, replace by _nm_utils_ascii_str_to_int64() */ errno = 0; tmp = strtoul (str, &end, base); if (errno || *end != '\0' || (range_check && (tmp < min || tmp > max))) { diff --git a/clients/common/nm-client-utils.h b/clients/common/nm-client-utils.h index a5bc05fa..08a39f03 100644 --- a/clients/common/nm-client-utils.h +++ b/clients/common/nm-client-utils.h @@ -23,13 +23,7 @@ #include "nm-meta-setting.h" #include "nm-active-connection.h" #include "nm-device.h" - - -#define nm_auto_unref_ip_address nm_auto (_nm_ip_address_unref) -NM_AUTO_DEFINE_FCN0 (NMIPAddress *, _nm_ip_address_unref, nm_ip_address_unref) - -#define nm_auto_unref_wgpeer nm_auto (_nm_auto_unref_wgpeer) -NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref) +#include "nm-libnm-core-intern/nm-libnm-core-utils.h" const NMObject **nmc_objects_sort_by_path (const NMObject *const*objs, gssize len); diff --git a/clients/common/nm-meta-setting-access.c b/clients/common/nm-meta-setting-access.c index bd4064de..8399f29d 100644 --- a/clients/common/nm-meta-setting-access.c +++ b/clients/common/nm-meta-setting-access.c @@ -101,6 +101,22 @@ nm_meta_setting_info_editor_get_property_info (const NMMetaSettingInfoEditor *se return NULL; } +gboolean +nm_meta_setting_info_editor_has_secrets (const NMMetaSettingInfoEditor *setting_info) +{ + guint i; + + if (!setting_info) + return FALSE; + + for (i = 0; i < setting_info->properties_num; i++) { + if (setting_info->properties[i]->is_secret) + return TRUE; + } + + return FALSE; +} + const NMMetaPropertyInfo * nm_meta_property_info_find_by_name (const char *setting_name, const char *property_name) { diff --git a/clients/common/nm-meta-setting-access.h b/clients/common/nm-meta-setting-access.h index 9898cc5a..ec1c2ba0 100644 --- a/clients/common/nm-meta-setting-access.h +++ b/clients/common/nm-meta-setting-access.h @@ -39,6 +39,8 @@ const NMMetaPropertyInfo *nm_meta_property_info_find_by_name (const char *settin const NMMetaPropertyInfo *nm_meta_property_info_find_by_setting (NMSetting *setting, const char *property_name); +gboolean nm_meta_setting_info_editor_has_secrets (const NMMetaSettingInfoEditor *setting_info); + /*****************************************************************************/ const NMMetaSettingInfoEditor *const*nm_meta_setting_infos_editor_p (void); diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index 6e35228a..698ded86 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -24,8 +24,10 @@ #include #include -#include "nm-common-macros.h" -#include "nm-utils/nm-enum-utils.h" +#include "nm-libnm-core-intern/nm-common-macros.h" +#include "nm-glib-aux/nm-enum-utils.h" +#include "nm-glib-aux/nm-secret-utils.h" +#include "nm-libnm-core-intern/nm-libnm-core-utils.h" #include "nm-vpn-helpers.h" #include "nm-client-utils.h" @@ -74,6 +76,196 @@ _gtype_property_get_gtype (GType gtype, const char *property_name) /*****************************************************************************/ +static int +_int64_cmp_desc (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + NM_CMP_DIRECT (*((const gint64 *) b), *((const gint64 *) a)); + return 0; +} + +static gint64 * +_value_str_as_index_list (const char *value, gsize *out_len) +{ + gs_free char *str_clone_free = NULL; + gboolean str_cloned = FALSE; + char *str; + gsize i, j; + gsize n_alloc; + gsize len; + gs_free gint64 *arr = NULL; + + *out_len = 0; + + if (!value) + return NULL; + + str = (char *) value; + n_alloc = 0; + len = 0; + while (TRUE) { + gint64 i64; + const char *s; + gsize good; + + good = strcspn (str, ","NM_ASCII_SPACES); + if (good == 0) { + if (str[0] == '\0') + break; + str++; + continue; + } + if (str[good] == '\0') { + s = str; + str += good; + } else { + if (!str_cloned) { + str_cloned = TRUE; + str = nm_strndup_a (200, str, strlen (str), &str_clone_free); + } + s = str; + str[good] = '\0'; + str += good + 1; + } + + i64 = _nm_utils_ascii_str_to_int64 (s, 10, 0, G_MAXINT64, -1); + if (i64 == -1) + return NULL; + + if (len >= n_alloc) { + if (n_alloc > 0) { + n_alloc = n_alloc * 2; + arr = g_realloc (arr, n_alloc * sizeof (gint64)); + } else { + n_alloc = 4; + arr = g_new (gint64, n_alloc); + } + } + arr[len++] = i64; + } + + if (len > 1) { + /* sort the list of indexes descendingly, and drop duplicates. */ + g_qsort_with_data (arr, + len, + sizeof (gint64), + _int64_cmp_desc, + NULL); + j = 1; + for (i = 1; i < len; i++) { + nm_assert (arr[i - 1] >= arr[i]); + if (arr[i - 1] > arr[i]) + arr[j++] = arr[i]; + } + len = j; + } + + *out_len = len; + return g_steal_pointer (&arr); +} + +#define ESCAPED_TOKENS_WITH_SPACES_DELIMTER ' ' +#define ESCAPED_TOKENS_WITH_SPACES_DELIMTERS NM_ASCII_SPACES"," + +#define ESCAPED_TOKENS_DELIMITER ',' +#define ESCAPED_TOKENS_DELIMITERS "," + +typedef enum { + VALUE_STRSPLIT_MODE_OBJLIST, + VALUE_STRSPLIT_MODE_MULTILIST, + VALUE_STRSPLIT_MODE_ESCAPED_TOKENS, + VALUE_STRSPLIT_MODE_ESCAPED_TOKENS_WITH_SPACES, +} ValueStrsplitMode; + +static const char ** +_value_strsplit (const char *value, + ValueStrsplitMode split_mode, + gsize *out_len) +{ + gs_free const char **strv = NULL; + gsize i; + gsize len; + + /* FIXME: some modes should support backslash escaping. + * In particular, to distingish from _value_str_as_index_list(), which + * does not accept '\\'. */ + + /* note that all modes remove empty tokens (",", "a,,b", ",,"). */ + switch (split_mode) { + case VALUE_STRSPLIT_MODE_OBJLIST: + strv = nm_utils_strsplit_set (value, ESCAPED_TOKENS_DELIMITERS); + break; + case VALUE_STRSPLIT_MODE_MULTILIST: + strv = nm_utils_strsplit_set (value, ESCAPED_TOKENS_WITH_SPACES_DELIMTERS); + break; + case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS: + strv = nm_utils_escaped_tokens_split (value, ESCAPED_TOKENS_DELIMITERS); + NM_SET_OUT (out_len, NM_PTRARRAY_LEN (strv)); + return g_steal_pointer (&strv); + case VALUE_STRSPLIT_MODE_ESCAPED_TOKENS_WITH_SPACES: + strv = nm_utils_escaped_tokens_split (value, ESCAPED_TOKENS_WITH_SPACES_DELIMTERS); + NM_SET_OUT (out_len, NM_PTRARRAY_LEN (strv)); + return g_steal_pointer (&strv); + default: + nm_assert_not_reached (); + break; + } + + NM_SET_OUT (out_len, 0); + + if (!strv) + return NULL; + + len = 0; + for (i = 0; strv[i]; i++) { + const char *s = strv[i]; + + s = nm_str_skip_leading_spaces (s); + if (s[0] == '\0') + continue; + + g_strchomp ((char *) s); + strv[len++] = s; + } + strv[len] = NULL; + + NM_SET_OUT (out_len, len); + return g_steal_pointer (&strv); +} + +static gboolean +_value_strsplit_assert_unsplitable (const char *str) +{ +#if NM_MORE_ASSERTS > 5 + gs_free const char **strv_test = NULL; + gsize j, l; + + /* Assert that we cannot split the token and that it + * has no unescaped delimiters. */ + + strv_test = _value_strsplit (str, + VALUE_STRSPLIT_MODE_ESCAPED_TOKENS, + NULL); + nm_assert (NM_PTRARRAY_LEN (strv_test) == 1); + + for (j = 0; str[j] != '\0'; ) { + if (str[j] == '\\') { + j++; + nm_assert (str[j] != '\0'); + } else + nm_assert (!NM_IN_SET (str[j], '\0', ',')); + j++; + } + l = j; + nm_assert ( !g_ascii_isspace (str[l - 1]) + || ( l >= 2 + && str[l - 2] == '\\')); +#endif + + return TRUE; +} + static NMIPAddress * _parse_ip_address (int family, const char *address, GError **error) { @@ -123,20 +315,20 @@ _parse_ip_route (int family, gint64 metric = -1; guint i; gs_free const char **routev = NULL; - gs_free char *str_clean = NULL; + gs_free char *str_clean_free = NULL; + const char *str_clean; gs_free char *dest_clone = NULL; const char *dest; const char *plen; gs_unref_hashtable GHashTable *attrs = NULL; - GHashTable *tmp_attrs; #define ROUTE_SYNTAX _("The valid syntax is: 'ip[/prefix] [next-hop] [metric] [attribute=val]... [,ip[/prefix] ...]'") nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); nm_assert (str); nm_assert (!error || !*error); - str_clean = g_strstrip (g_strdup (str)); - routev = nm_utils_strsplit_set (str_clean, " \t", FALSE); + str_clean = nm_strstrip_avoid_copy_a (300, str, &str_clean_free); + routev = nm_utils_strsplit_set (str_clean, " \t"); if (!routev) { g_set_error (error, 1, 0, "'%s' is not valid. %s", @@ -182,6 +374,7 @@ _parse_ip_route (int family, GHashTableIter iter; char *iter_key; GVariant *iter_value; + gs_unref_hashtable GHashTable *tmp_attrs = NULL; tmp_attrs = nm_utils_parse_variant_attributes (routev[i], ' ', '=', FALSE, nm_ip_route_get_variant_attribute_spec(), @@ -207,13 +400,11 @@ _parse_ip_route (int family, if (!nm_ip_route_attribute_validate (iter_key, iter_value, family, NULL, error)) { g_prefix_error (error, "%s: ", iter_key); - g_hash_table_unref (tmp_attrs); return NULL; } g_hash_table_insert (attrs, iter_key, iter_value); g_hash_table_iter_steal (&iter); } - g_hash_table_unref (tmp_attrs); } else { g_set_error (error, 1, 0, "%s", ROUTE_SYNTAX); return NULL; @@ -298,7 +489,8 @@ _parse_team_link_watcher (const char *str, GError **error) { gs_free const char **watcherv = NULL; - gs_free char *str_clean = NULL; + gs_free char *str_clean_free = NULL; + const char *str_clean; guint i; gs_free const char *name = NULL; int val1 = 0, val2 = 0, val3 = 3, val4 = -1; @@ -309,8 +501,8 @@ _parse_team_link_watcher (const char *str, nm_assert (str); nm_assert (!error || !*error); - str_clean = g_strstrip (g_strdup (str)); - watcherv = nm_utils_strsplit_set (str_clean, " \t", FALSE); + str_clean = nm_strstrip_avoid_copy_a (300, str, &str_clean_free); + watcherv = nm_utils_strsplit_set (str_clean, " \t"); if (!watcherv) { g_set_error (error, 1, 0, "'%s' is not valid", str); return NULL; @@ -319,7 +511,7 @@ _parse_team_link_watcher (const char *str, for (i = 0; watcherv[i]; i++) { gs_free const char **pair = NULL; - pair = nm_utils_strsplit_set (watcherv[i], "=", FALSE); + pair = nm_utils_strsplit_set (watcherv[i], "="); if (!pair) { g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], "properties should be specified as 'key=value'"); @@ -397,62 +589,6 @@ _parse_team_link_watcher (const char *str, #define MAX_SKB_PRIO G_MAXUINT32 #define MAX_8021P_PRIO 7 /* Max 802.1p priority */ -/* - * Parse VLAN priority mappings from the following format: 2:1,3:4,7:3 - * and verify if the priority numbers are valid - * - * Return: string array with split maps, or NULL on error - * Caller is responsible for freeing the array. - */ -static char ** -_parse_vlan_priority_maps (const char *priority_map, - NMVlanPriorityMap map_type, - GError **error) -{ - char **mapping = NULL, **iter; - unsigned long from, to, from_max, to_max; - - g_return_val_if_fail (priority_map != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (map_type == NM_VLAN_INGRESS_MAP) { - from_max = MAX_8021P_PRIO; - to_max = MAX_SKB_PRIO; - } else { - from_max = MAX_SKB_PRIO; - to_max = MAX_8021P_PRIO; - } - - mapping = g_strsplit (priority_map, ",", 0); - for (iter = mapping; iter && *iter; iter++) { - char *left, *right; - - left = g_strstrip (*iter); - right = strchr (left, ':'); - if (!right) { - g_set_error (error, 1, 0, _("invalid priority map '%s'"), *iter); - g_strfreev (mapping); - return NULL; - } - *right++ = '\0'; - - if (!nmc_string_to_uint (left, TRUE, 0, from_max, &from)) { - g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), - left, from_max); - g_strfreev (mapping); - return NULL; - } - if (!nmc_string_to_uint (right, TRUE, 0, to_max, &to)) { - g_set_error (error, 1, 0, _("priority '%s' is not valid (<0-%ld>)"), - right, to_max); - g_strfreev (mapping); - return NULL; - } - *(right-1) = ':'; /* Put back ':' */ - } - return mapping; -} - /* * nmc_proxy_check_script: * @script: file name with PAC script, or raw PAC Script data @@ -646,10 +782,10 @@ _env_warn_fcn (const NMMetaEnvironment *environment, const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, NMMetaAccessorGetType get_type, NMMetaAccessorGetFlags get_flags, NMMetaAccessorGetOutFlags *out_flags, gboolean *out_is_default, gpointer *out_to_free #define ARGS_SET_FCN \ - const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, GError **error + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, char modifier, const char *value, GError **error #define ARGS_REMOVE_FCN \ - const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, guint32 idx, GError **error + const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, NMSetting *setting, const char *value, GError **error #define ARGS_COMPLETE_FCN \ const NMMetaPropertyInfo *property_info, const NMMetaEnvironment *environment, gpointer environment_user_data, const NMMetaOperationContext *operation_context, const char *text, char ***out_to_free @@ -660,6 +796,46 @@ _env_warn_fcn (const NMMetaEnvironment *environment, #define ARGS_SETTING_INIT_FCN \ const NMMetaSettingInfoEditor *setting_info, NMSetting *setting, NMMetaAccessorSettingInitType init_type +static gboolean +_SET_FCN_DO_RESET_DEFAULT (const NMMetaPropertyInfo *property_info, char modifier, const char *value) +{ + nm_assert (property_info); + nm_assert (!property_info->property_type->set_supports_remove); + nm_assert (NM_IN_SET (modifier, '\0', '+')); + nm_assert (value || modifier == '\0'); + + return value == NULL; +} + +static gboolean +_SET_FCN_DO_RESET_DEFAULT_WITH_SUPPORTS_REMOVE (const NMMetaPropertyInfo *property_info, char modifier, const char *value) +{ + nm_assert (property_info); + nm_assert (property_info->property_type->set_supports_remove); + nm_assert (NM_IN_SET (modifier, '\0', '+', '-')); + nm_assert (value || modifier == '\0'); + + return value == NULL; +} + +static gboolean +_SET_FCN_DO_SET_ALL (char modifier, const char *value) +{ + nm_assert (NM_IN_SET (modifier, '\0', '+', '-')); + nm_assert (value); + + return modifier == '\0'; +} + +static gboolean +_SET_FCN_DO_REMOVE (char modifier, const char *value) +{ + nm_assert (NM_IN_SET (modifier, '\0', '+', '-')); + nm_assert (value); + + return modifier == '-'; +} + #define RETURN_UNSUPPORTED_GET_TYPE() \ G_STMT_START { \ if (!NM_IN_SET (get_type, \ @@ -677,7 +853,7 @@ _env_warn_fcn (const NMMetaEnvironment *environment, } G_STMT_END static gboolean -property_is_default (NMSetting *setting, const char *prop_name) +_gobject_property_is_default (NMSetting *setting, const char *prop_name) { nm_auto_unset_gvalue GValue v = G_VALUE_INIT; GParamSpec *pspec; @@ -703,32 +879,30 @@ property_is_default (NMSetting *setting, const char *prop_name) return g_param_value_defaults (pspec, &v); } -static gconstpointer -_get_fcn_nmc_with_default (ARGS_GET_FCN) +static gboolean +_gobject_property_reset (NMSetting *setting, + const char *prop_name, + gboolean reset_default) { - const char *s; - char *s_full; - GValue val = G_VALUE_INIT; - - RETURN_UNSUPPORTED_GET_TYPE (); - NM_SET_OUT (out_is_default, property_is_default (setting, property_info->property_name)); + nm_auto_unset_gvalue GValue v = G_VALUE_INIT; + GParamSpec *pspec; - if (property_info->property_typ_data->subtype.get_with_default.fcn (setting)) { - if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) - return _("(default)"); - return ""; - } + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), + prop_name); + if (!G_IS_PARAM_SPEC (pspec)) + g_return_val_if_reached (FALSE); - g_value_init (&val, G_TYPE_STRING); - g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); - s = g_value_get_string (&val); - if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) - s_full = s ? g_strdup_printf ("\"%s\"", s) : g_strdup (""); - else - s_full = g_strdup (s && *s ? s : " "); - g_value_unset (&val); + g_value_init (&v, pspec->value_type); + if (reset_default) + g_param_value_defaults (pspec, &v); + g_object_set_property (G_OBJECT (setting), prop_name, &v); + return TRUE; +} - RETURN_STR_TO_FREE (s_full); +static gboolean +_gobject_property_reset_default (NMSetting *setting, const char *prop_name) +{ + return _gobject_property_reset (setting, prop_name, TRUE); } static gconstpointer @@ -738,13 +912,20 @@ _get_fcn_gobject_impl (const NMMetaPropertyInfo *property_info, gboolean *out_is_default, gpointer *out_to_free) { - char *s; - const char *s_c; + const char *cstr; GType gtype_prop; nm_auto_unset_gvalue GValue val = G_VALUE_INIT; RETURN_UNSUPPORTED_GET_TYPE (); - NM_SET_OUT (out_is_default, property_is_default (setting, property_info->property_name)); + NM_SET_OUT (out_is_default, _gobject_property_is_default (setting, property_info->property_name)); + + if ( property_info->property_typ_data + && property_info->property_typ_data->is_default_fcn + && property_info->property_typ_data->is_default_fcn (setting)) { + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) + return _("(default)"); + return ""; + } gtype_prop = _gobject_property_get_gtype (G_OBJECT (setting), property_info->property_name); @@ -755,15 +936,35 @@ _get_fcn_gobject_impl (const NMMetaPropertyInfo *property_info, g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); b = g_value_get_boolean (&val); if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) - s_c = b ? _("yes") : _("no"); + cstr = b ? _("yes") : _("no"); else - s_c = b ? "yes" : "no"; - return s_c; + cstr = b ? "yes" : "no"; + return cstr; } else { + char *str; + + /* Note that we register certain transform functions in nmc_value_transforms_register(). + * This makes G_TYPE_STRV working. + * + * FIXME: that is particularly ugly because it's non-obvious which code relies + * on nmc_value_transforms_register(). Also, nmc_value_transforms_register() is + * in clients/cli, while we are here in clients/common. */ g_value_init (&val, G_TYPE_STRING); g_object_get_property (G_OBJECT (setting), property_info->property_name, &val); - s = g_value_dup_string (&val); - RETURN_STR_TO_FREE (s); + cstr = g_value_get_string (&val); + + if ( property_info->property_typ_data + && property_info->property_typ_data->is_default_fcn) { + if (get_type == NM_META_ACCESSOR_GET_TYPE_PRETTY) { + str = cstr + ? g_strdup_printf ("\"%s\"", cstr) + : g_strdup (""); + } else + str = g_strdup (cstr && cstr[0] ? cstr : " "); + } else + str = cstr ? g_strdup (cstr) : NULL; + + RETURN_STR_TO_FREE (str); } } @@ -842,10 +1043,9 @@ _get_fcn_gobject_int (ARGS_GET_FCN) for (; value_infos->nick; value_infos++) { if ( ( is_uint64 && value_infos->value.u64 == v.u64) || (!is_uint64 && value_infos->value.i64 == v.i64)) { - char *old_str = return_str; + gs_free char *old_str = return_str; return_str = g_strdup_printf ("%s (%s)", old_str, value_infos->nick); - g_free (old_str); break; } } @@ -1033,6 +1233,9 @@ _set_fcn_gobject_string (ARGS_SET_FCN) { gs_free char *to_free = NULL; + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + if (property_info->property_typ_data) { if (property_info->property_typ_data->subtype.gobject_string.validate_fcn) { value = property_info->property_typ_data->subtype.gobject_string.validate_fcn (value, &to_free, error); @@ -1055,6 +1258,9 @@ _set_fcn_gobject_bool (ARGS_SET_FCN) { gboolean val_bool; + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + if (!nmc_string_to_bool (value, &val_bool, error)) return FALSE; @@ -1076,6 +1282,9 @@ _set_fcn_gobject_int (ARGS_SET_FCN) guint base = 10; const NMMetaUtilsIntValueInfo *value_infos; + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), property_info->property_name); if (!G_IS_PARAM_SPEC (pspec)) g_return_val_if_reached (FALSE); @@ -1085,14 +1294,10 @@ _set_fcn_gobject_int (ARGS_SET_FCN) if (property_info->property_typ_data) { if ( value && (value_infos = property_info->property_typ_data->subtype.gobject_int.value_infos)) { - gs_free char *vv_stripped = NULL; - const char *vv = nm_str_skip_leading_spaces (value); - - if (vv[0] && g_ascii_isspace (vv[strlen (vv) - 1])) { - vv_stripped = g_strstrip (g_strdup (vv)); - vv = vv_stripped; - } + gs_free char *vv_free = NULL; + const char *vv; + vv = nm_strstrip_avoid_copy_a (300, value, &vv_free); for (; value_infos->nick; value_infos++) { if (nm_streq (value_infos->nick, vv)) { v = value_infos->value; @@ -1216,7 +1421,10 @@ _set_fcn_gobject_mtu (ARGS_SET_FCN) const GParamSpec *pspec; gint64 v; - if (nm_streq0 (value, "auto")) + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + + if (nm_streq (value, "auto")) value = "0"; pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (G_OBJECT (setting)), @@ -1254,6 +1462,9 @@ _set_fcn_gobject_mac (ARGS_SET_FCN) NMMetaPropertyTypeMacMode mode; gboolean valid; + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + if (property_info->property_typ_data) mode = property_info->property_typ_data->subtype.mac.mode; else @@ -1289,6 +1500,9 @@ _set_fcn_gobject_enum (ARGS_SET_FCN) gboolean is_flags; int v; + if (_SET_FCN_DO_RESET_DEFAULT (property_info, modifier, value)) + return _gobject_property_reset_default (setting, property_info->property_name); + if (property_info->property_typ_data) { if (property_info->property_typ_data->subtype.gobject_enum.get_gtype) { gtype = property_info->property_typ_data->subtype.gobject_enum.get_gtype (); @@ -1558,25 +1772,6 @@ vlan_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type) return g_string_free (flag_str, FALSE); } -static char * -vlan_priorities_to_string (NMSettingVlan *s_vlan, NMVlanPriorityMap map) -{ - GString *priorities; - int i; - - priorities = g_string_new (NULL); - for (i = 0; i < nm_setting_vlan_get_num_priorities (s_vlan, map); i++) { - guint32 from, to; - - if (nm_setting_vlan_get_priority (s_vlan, map, i, &from, &to)) - g_string_append_printf (priorities, "%d:%d,", from, to); - } - if (priorities->len) - g_string_truncate (priorities, priorities->len-1); /* chop off trailing ',' */ - - return g_string_free (priorities, FALSE); -} - static char * secret_flags_to_string (guint32 flags, NMMetaAccessorGetType get_type) { @@ -1619,166 +1814,204 @@ vpn_data_item (const char *key, const char *value, gpointer user_data) g_string_append_printf (ret_str, "%s = %s", key, value); } -#define DEFINE_SETTER_STR_LIST_MULTI(def_func, s_macro, set_func) \ - static gboolean \ - def_func (NMSetting *setting, \ - const char *prop, \ - const char *value, \ - const char **valid_strv, \ - GError **error) \ - { \ - gs_free const char **strv = NULL; \ - gsize i; \ - const char *item; \ - nm_assert (!error || !*error); \ - strv = nm_utils_strsplit_set (value, " \t,", FALSE); \ - if (strv) { \ - for (i = 0; strv[i]; i++) { \ - if (!(item = nmc_string_is_valid (strv[i], valid_strv, error))) { \ - return FALSE; \ - } \ - set_func (s_macro (setting), item); \ - } \ - } \ - return TRUE; \ +static const char * +_multilist_do_validate (const NMMetaPropertyInfo *property_info, + NMSetting *setting, + const char *item, + GError **error) +{ + if (property_info->property_typ_data->values_static) { + nm_assert (!property_info->property_typ_data->subtype.multilist.validate_fcn); + return nmc_string_is_valid (item, + (const char **) property_info->property_typ_data->values_static, + error); + } + if (property_info->property_typ_data->subtype.multilist.validate_fcn) { + return property_info->property_typ_data->subtype.multilist.validate_fcn (item, + error); + } + if (property_info->property_typ_data->subtype.multilist.validate2_fcn) { + return property_info->property_typ_data->subtype.multilist.validate2_fcn (setting, + item, + error); } -#define DEFINE_SETTER_OPTIONS(def_func, s_macro, s_type, add_func, valid_func1, valid_func2) \ - static gboolean \ - def_func (ARGS_SET_FCN) \ - { \ - gs_free const char **strv = NULL; \ - const char **iter; \ - const char **(*valid_func1_p) (s_type *) = valid_func1; \ - const char * (*valid_func2_p) (const char *, const char *, GError **) = valid_func2; \ - const char *opt_name, *opt_val; \ - \ - nm_assert (!error || !*error); \ - \ - strv = nm_utils_strsplit_set (value, ",", FALSE); \ - for (iter = strv; iter && *iter; iter++) { \ - gs_free char *left_clone = g_strstrip (g_strdup (*iter)); \ - char *left = left_clone; \ - char *right = strchr (left, '='); \ - if (!right) { \ - g_set_error (error, 1, 0, _("'%s' is not valid; use