From e0bbaf6a397aea4980437088ad11f6402c504d73 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 9 Aug 2018 17:53:59 +0200 Subject: [PATCH 1/6] shared: add space escape functions --- libnm-core/tests/test-general.c | 49 ++++++++++++++++++++++++++ shared/nm-utils/nm-shared-utils.c | 57 +++++++++++++++++++++++++++++++ shared/nm-utils/nm-shared-utils.h | 3 ++ 3 files changed, 109 insertions(+) diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index db1d1c1447..16bb5d7113 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -7074,6 +7074,53 @@ test_ethtool_offload (void) g_assert_cmpstr (d->optname, ==, NM_ETHTOOL_OPTNAME_FEATURE_RXHASH); } +static void +test_nm_utils_escape_spaces (void) +{ + char *to_free; + + g_assert_cmpstr (_nm_utils_escape_spaces (NULL, &to_free), ==, NULL); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces ("", &to_free), ==, ""); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces (" ", &to_free), ==, "\\ "); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces ("\t ", &to_free), ==, "\\\t\\ "); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces ("abc", &to_free), ==, "abc"); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces ("abc def", &to_free), ==, "abc\\ def"); + g_free (to_free); + + g_assert_cmpstr (_nm_utils_escape_spaces ("abc\tdef", &to_free), ==, "abc\\\tdef"); + g_free (to_free); +} + +static void +test_nm_utils_unescape_spaces (void) +{ +#define CHECK_STR(in, out) \ + G_STMT_START { \ + gs_free char *str = g_strdup (in); \ + \ + g_assert_cmpstr (_nm_utils_unescape_spaces (str), ==, out); \ + } G_STMT_END + + CHECK_STR ("\\a", "\\a"); + CHECK_STR ("foobar", "foobar"); + CHECK_STR ("foo bar", "foo bar"); + CHECK_STR ("foo\\ bar", "foo bar"); + CHECK_STR ("foo\\", "foo\\"); + CHECK_STR ("\\\\\t", "\\\t"); + +#undef CHECK_STR +} + /*****************************************************************************/ NMTST_DEFINE (); @@ -7224,6 +7271,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx); g_test_add_func ("/core/general/_nm_utils_validate_json", test_nm_utils_check_valid_json); g_test_add_func ("/core/general/_nm_utils_team_config_equal", test_nm_utils_team_config_equal); + g_test_add_func ("/core/general/_nm_utils_escape_spaces", test_nm_utils_escape_spaces); + g_test_add_func ("/core/general/_nm_utils_unescape_spaces", test_nm_utils_unescape_spaces); g_test_add_func ("/core/general/test_nm_utils_enum", test_nm_utils_enum); g_test_add_func ("/core/general/nm-set-out", test_nm_set_out); g_test_add_func ("/core/general/route_attributes/parse", test_route_attributes_parse); diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 34c93c8108..4e2cdbb98a 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -1702,3 +1702,60 @@ _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...) g_slice_free1 (((gsize) nargs) * sizeof (gconstpointer), user_data); } + +/*****************************************************************************/ + +#define IS_SPACE(c) NM_IN_SET ((c), ' ', '\t') + +const char * +_nm_utils_escape_spaces (const char *str, char **to_free) +{ + const char *ptr = str; + char *ret, *r; + + *to_free = NULL; + + if (!str) + return NULL; + + while (TRUE) { + if (!*ptr) + return str; + if (IS_SPACE (*ptr)) + break; + ptr++; + } + + ptr = str; + ret = g_new (char, strlen (str) * 2 + 1); + r = ret; + *to_free = ret; + while (*ptr) { + if (IS_SPACE (*ptr)) + *r++ = '\\'; + *r++ = *ptr++; + } + *r = '\0'; + + return ret; +} + +char * +_nm_utils_unescape_spaces (char *str) +{ + guint i, j = 0; + + if (!str) + return NULL; + + for (i = 0; str[i]; i++) { + if (str[i] == '\\' && IS_SPACE (str[i+1])) + i++; + str[j++] = str[i]; + } + str[j] = '\0'; + + return str; +} + +#undef IS_SPACE diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index 6ee77b989f..ef7225e1bb 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -718,4 +718,7 @@ void _nm_utils_user_data_unpack (gpointer user_data, int nargs, ...); /*****************************************************************************/ +const char *_nm_utils_escape_spaces (const char *str, char **to_free); +char *_nm_utils_unescape_spaces (char *str); + #endif /* __NM_SHARED_UTILS_H__ */ From 593f6efeae597fe05f6a1c879d042c5e924d645e Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 9 Aug 2018 09:34:01 +0200 Subject: [PATCH 2/6] libnm-core: remove wrong annotation in NMSettingIPConfig --- libnm-core/nm-setting-ip-config.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 5af9b64041..5d53184075 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -1691,8 +1691,6 @@ nm_setting_ip_config_remove_dns_search (NMSettingIPConfig *setting, int idx) * Removes the DNS search domain @dns_search. * * Returns: %TRUE if the DNS search domain was found and removed; %FALSE if it was not. - * - * Since 0.9.10 **/ gboolean nm_setting_ip_config_remove_dns_search_by_value (NMSettingIPConfig *setting, From 6a51d393b27c71ca3492d1af2dd92d7e77fc0f10 Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 9 Aug 2018 09:28:30 +0200 Subject: [PATCH 3/6] shared: add @allow_escaping argument to @nm_utils_strsplit_set --- clients/cli/connections.c | 4 +- clients/cli/settings.c | 2 +- clients/cli/utils.c | 2 +- clients/common/nm-meta-setting-desc.c | 56 +++++++++---------- libnm-core/nm-utils.c | 2 +- libnm-core/tests/test-general.c | 34 ++++++----- shared/nm-utils/nm-shared-utils.c | 40 +++++++++---- shared/nm-utils/nm-shared-utils.h | 2 +- .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 50 ++++++++--------- 9 files changed, 108 insertions(+), 84 deletions(-) diff --git a/clients/cli/connections.c b/clients/cli/connections.c index 5361e11fb1..f4d19ee39c 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -1807,7 +1807,7 @@ parse_preferred_connection_order (const char *order, GError **error) gboolean inverse, unique; int i; - strv = nm_utils_strsplit_set (order, ":"); + strv = nm_utils_strsplit_set (order, ":", FALSE); if (!strv) { g_set_error (error, NMCLI_ERROR, 0, _("incorrect string '%s' of '--order' option"), order); @@ -2573,7 +2573,7 @@ parse_passwords (const char *passwd_file, GError **error) return NULL; } - strv = nm_utils_strsplit_set (contents, "\r\n"); + strv = nm_utils_strsplit_set (contents, "\r\n", FALSE); for (iter = strv; *iter; iter++) { gs_free char *iter_s = g_strdup (*iter); diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 2c6baeb426..2d231e1cf3 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -318,7 +318,7 @@ _set_fcn_precheck_connection_secondaries (const char *value, char **iter; gboolean modified = FALSE; - strv0 = nm_utils_strsplit_set (value, " \t,"); + strv0 = nm_utils_strsplit_set (value, " \t,", FALSE); if (!strv0) return TRUE; diff --git a/clients/cli/utils.c b/clients/cli/utils.c index 8cbfe9f2fa..e21c108d22 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -509,7 +509,7 @@ 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"); + arr0 = nm_utils_strsplit_set (line ?: "", delim ?: " \t", FALSE); if (!arr0) arr = g_new0 (char *, 1); else diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index c4b0414575..c041b10ec7 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -137,7 +137,7 @@ _parse_ip_route (int family, nm_assert (!error || !*error); str_clean = g_strstrip (g_strdup (str)); - routev = nm_utils_strsplit_set (str_clean, " \t"); + routev = nm_utils_strsplit_set (str_clean, " \t", FALSE); if (!routev) { g_set_error (error, 1, 0, "'%s' is not valid. %s", @@ -313,7 +313,7 @@ _parse_team_link_watcher (const char *str, nm_assert (!error || !*error); str_clean = g_strstrip (g_strdup (str)); - watcherv = nm_utils_strsplit_set (str_clean, " \t"); + watcherv = nm_utils_strsplit_set (str_clean, " \t", FALSE); if (!watcherv) { g_set_error (error, 1, 0, "'%s' is not valid", str); return NULL; @@ -322,7 +322,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], "="); + pair = nm_utils_strsplit_set (watcherv[i], "=", FALSE); if (!pair) { g_set_error (error, 1, 0, "'%s' is not valid: %s", watcherv[i], "properties should be specified as 'key=value'"); @@ -1589,7 +1589,7 @@ vpn_data_item (const char *key, const char *value, gpointer user_data) gsize i; \ const char *item; \ nm_assert (!error || !*error); \ - strv = nm_utils_strsplit_set (value, " \t,"); \ + 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))) { \ @@ -1613,7 +1613,7 @@ vpn_data_item (const char *key, const char *value, gpointer user_data) \ nm_assert (!error || !*error); \ \ - strv = nm_utils_strsplit_set (value, ","); \ + 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; \ @@ -1700,7 +1700,7 @@ vpn_data_item (const char *key, const char *value, gpointer user_data) \ nm_assert (!error || !*error); \ \ - strv = nm_utils_strsplit_set (value, " \t,"); \ + strv = nm_utils_strsplit_set (value, " \t,", FALSE); \ for (iter = strv; strv && *iter; iter++) { \ if (!nm_utils_hwaddr_aton (*iter, buf, ETH_ALEN)) { \ g_set_error (error, 1, 0, _("'%s' is not a valid MAC"), *iter); \ @@ -1918,7 +1918,7 @@ nmc_property_set_bytes (NMSetting *setting, const char *prop, const char *value, goto done; /* Otherwise, consider the following format: AA b 0xCc D */ - strv = nm_utils_strsplit_set (val_strip, " \t"); + strv = nm_utils_strsplit_set (val_strip, " \t", FALSE); array = g_byte_array_sized_new (NM_PTRARRAY_LEN (strv)); for (iter = strv; iter && *iter; iter++) { int v; @@ -2138,7 +2138,7 @@ _get_fcn_802_1x_phase2_private_key (ARGS_GET_FCN) \ nm_assert (error == NULL || *error == NULL); \ \ - strv = nm_utils_strsplit_set (value, " \t,"); \ + strv = nm_utils_strsplit_set (value, " \t,", FALSE); \ if (strv) { \ for (i = 0; strv[i]; i++) \ set_func (NM_SETTING_802_1X (setting), strv[i++]); \ @@ -2530,7 +2530,7 @@ _set_fcn_connection_permissions (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (!verify_string_list (strv, property_info->property_name, permissions_valid, error)) return FALSE; @@ -2655,7 +2655,7 @@ _set_fcn_connection_secondaries (ARGS_SET_FCN) gs_free const char **strv = NULL; const char *const*iter; - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (strv) { for (iter = strv; *iter; iter++) nm_setting_connection_add_secondary (NM_SETTING_CONNECTION (setting), *iter); @@ -2861,7 +2861,7 @@ _set_fcn_dcb_flags (ARGS_SET_FCN) const char *const*iter; /* Check for individual flag numbers */ - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); for (iter = strv; iter && *iter; iter++) { t = _nm_utils_ascii_str_to_int64 (*iter, 0, 0, DCB_ALL_FLAGS, -1); @@ -3264,7 +3264,7 @@ _set_fcn_ip4_config_dns (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); for (iter = strv; iter && *iter; iter++) { gs_free char *addr = g_strstrip (g_strdup (*iter)); @@ -3309,7 +3309,7 @@ _set_fcn_ip4_config_dns_search (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (!verify_string_list (strv, property_info->property_name, nmc_util_is_domain, error)) return FALSE; @@ -3349,7 +3349,7 @@ _set_fcn_ip4_config_dns_options (ARGS_SET_FCN) nm_assert (!error || !*error); nm_setting_ip_config_clear_dns_options (NM_SETTING_IP_CONFIG (setting), TRUE); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (strv) { for (i = 0; strv[i]; i++) nm_setting_ip_config_add_dns_option (NM_SETTING_IP_CONFIG (setting), strv[i]); @@ -3384,7 +3384,7 @@ _set_fcn_ip4_config_addresses (ARGS_SET_FCN) const char *const*iter; NMIPAddress *ip4addr; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; *iter; iter++) { ip4addr = _parse_ip_address (AF_INET, *iter, error); if (!ip4addr) @@ -3444,7 +3444,7 @@ _set_fcn_ip4_config_routes (ARGS_SET_FCN) const char *const*iter; NMIPRoute *ip4route; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; *iter; iter++) { ip4route = _parse_ip_route (AF_INET, *iter, error); if (!ip4route) @@ -3508,7 +3508,7 @@ _set_fcn_ip6_config_dns (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); for (iter = strv; iter && *iter; iter++) { gs_free char *addr = g_strstrip (g_strdup (*iter)); @@ -3553,7 +3553,7 @@ _set_fcn_ip6_config_dns_search (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (!verify_string_list (strv, property_info->property_name, nmc_util_is_domain, error)) return FALSE; @@ -3593,7 +3593,7 @@ _set_fcn_ip6_config_dns_options (ARGS_SET_FCN) nm_assert (!error || !*error); nm_setting_ip_config_clear_dns_options (NM_SETTING_IP_CONFIG (setting), TRUE); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); if (strv) { for (i = 0; strv[i]; i++) nm_setting_ip_config_add_dns_option (NM_SETTING_IP_CONFIG (setting), strv[i]); @@ -3635,7 +3635,7 @@ _set_fcn_ip6_config_addresses (ARGS_SET_FCN) const char *const*iter; NMIPAddress *ip6addr; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { ip6addr = _parse_ip_address (AF_INET6, *iter, error); if (!ip6addr) @@ -3695,7 +3695,7 @@ _set_fcn_ip6_config_routes (ARGS_SET_FCN) const char *const*iter; NMIPRoute *ip6route; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { ip6route = _parse_ip_route (AF_INET6, *iter, error); if (!ip6route) @@ -3844,7 +3844,7 @@ _set_fcn_sriov_vfs (ARGS_SET_FCN) NMSriovVF *vf; GError *local = NULL; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { vf = nm_utils_sriov_vf_from_str (*iter, &local); if (!vf) { @@ -3866,7 +3866,7 @@ _set_fcn_tc_config_qdiscs (ARGS_SET_FCN) NMTCQdisc *tc_qdisc; GError *local = NULL; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { tc_qdisc = nm_utils_tc_qdisc_from_str (*iter, &local); if (!tc_qdisc) { @@ -3971,7 +3971,7 @@ _set_fcn_tc_config_tfilters (ARGS_SET_FCN) NMTCTfilter *tc_tfilter; GError *local = NULL; - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { tc_tfilter = nm_utils_tc_tfilter_from_str (*iter, &local); if (!tc_tfilter) { @@ -4044,7 +4044,7 @@ _set_fcn_team_runner_tx_hash (ARGS_SET_FCN) nm_assert (!error || !*error); - strv = nm_utils_strsplit_set (value, " \t,"); + strv = nm_utils_strsplit_set (value, " \t,", FALSE); for (iter = strv; strv && *iter; iter++) { if (!_is_valid_team_runner_tx_hash_element (*iter, error)) return FALSE; @@ -4116,7 +4116,7 @@ _set_fcn_team_link_watchers (ARGS_SET_FCN) NMTeamLinkWatcher *watcher; nm_setting_team_clear_link_watchers (NM_SETTING_TEAM (setting)); - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { watcher = _parse_team_link_watcher (*iter, error); if (!watcher) @@ -4190,7 +4190,7 @@ _set_fcn_team_port_link_watchers (ARGS_SET_FCN) NMTeamLinkWatcher *watcher; nm_setting_team_port_clear_link_watchers (NM_SETTING_TEAM_PORT (setting)); - strv = nm_utils_strsplit_set (value, ","); + strv = nm_utils_strsplit_set (value, ",", FALSE); for (iter = strv; strv && *iter; iter++) { watcher = _parse_team_link_watcher (*iter, error); if (!watcher) @@ -4470,7 +4470,7 @@ _set_fcn_wired_s390_subchannels (ARGS_SET_FCN) gs_free const char **strv = NULL; gsize len; - strv = nm_utils_strsplit_set (value, " ,\t"); + strv = nm_utils_strsplit_set (value, " ,\t", FALSE); len = NM_PTRARRAY_LEN (strv); if (len != 2 && len != 3) { g_set_error (error, 1, 0, _("'%s' is not valid; 2 or 3 strings should be provided"), diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index f481f11fe5..eb002d4811 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -2613,7 +2613,7 @@ _nm_sriov_vf_parse_vlans (NMSriovVF *vf, const char *str, GError **error) gs_free const char **vlans = NULL; guint i; - vlans = nm_utils_strsplit_set (str, ";"); + vlans = nm_utils_strsplit_set (str, ";", FALSE); if (!vlans) { g_set_error_literal (error, NM_CONNECTION_ERROR, diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 16bb5d7113..ad40ee947f 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -238,7 +238,7 @@ test_nm_g_slice_free_fcn (void) /*****************************************************************************/ static void -_do_test_nm_utils_strsplit_set (const char *str, ...) +_do_test_nm_utils_strsplit_set (gboolean escape, const char *str, ...) { gs_unref_ptrarray GPtrArray *args_array = g_ptr_array_new (); const char *const*args; @@ -255,7 +255,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...) args = (const char *const*) args_array->pdata; - words = nm_utils_strsplit_set (str, " \t\n"); + words = nm_utils_strsplit_set (str, " \t\n", escape); if (!args[0]) { g_assert (!words); @@ -268,7 +268,7 @@ _do_test_nm_utils_strsplit_set (const char *str, ...) g_assert (args[i]); g_assert (words[i]); g_assert (args[i][0]); - g_assert (NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n'))); + g_assert (escape || NM_STRCHAR_ALL (args[i], ch, !NM_IN_SET (ch, ' ', '\t', '\n'))); g_assert_cmpstr (args[i], ==, words[i]); } } @@ -279,21 +279,29 @@ _do_test_nm_utils_strsplit_set (const char *str, ...) static void test_nm_utils_strsplit_set (void) { - do_test_nm_utils_strsplit_set (NULL); - do_test_nm_utils_strsplit_set (""); - do_test_nm_utils_strsplit_set ("\t"); - do_test_nm_utils_strsplit_set (" \t\n"); - do_test_nm_utils_strsplit_set ("a", "a"); - do_test_nm_utils_strsplit_set ("a b", "a", "b"); - do_test_nm_utils_strsplit_set ("a\rb", "a\rb"); - do_test_nm_utils_strsplit_set (" a\rb ", "a\rb"); - do_test_nm_utils_strsplit_set (" a bbbd afds ere", "a", "bbbd", "afds", "ere"); - do_test_nm_utils_strsplit_set ("1 2 3 4 5 6 7 8 9 0 " + do_test_nm_utils_strsplit_set (FALSE, NULL); + do_test_nm_utils_strsplit_set (FALSE, ""); + do_test_nm_utils_strsplit_set (FALSE, "\t"); + do_test_nm_utils_strsplit_set (FALSE, " \t\n"); + do_test_nm_utils_strsplit_set (FALSE, "a", "a"); + do_test_nm_utils_strsplit_set (FALSE, "a b", "a", "b"); + do_test_nm_utils_strsplit_set (FALSE, "a\rb", "a\rb"); + do_test_nm_utils_strsplit_set (FALSE, " a\rb ", "a\rb"); + do_test_nm_utils_strsplit_set (FALSE, " a bbbd afds ere", "a", "bbbd", "afds", "ere"); + do_test_nm_utils_strsplit_set (FALSE, + "1 2 3 4 5 6 7 8 9 0 " "1 2 3 4 5 6 7 8 9 0 " "1 2 3 4 5 6 7 8 9 0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"); + do_test_nm_utils_strsplit_set (TRUE, "\\", "\\"); + do_test_nm_utils_strsplit_set (TRUE, "\\ ", "\\ "); + do_test_nm_utils_strsplit_set (TRUE, "\\\\", "\\\\"); + do_test_nm_utils_strsplit_set (TRUE, "\\\t", "\\\t"); + do_test_nm_utils_strsplit_set (TRUE, "foo\\", "foo\\"); + do_test_nm_utils_strsplit_set (TRUE, "bar foo\\", "bar", "foo\\"); + do_test_nm_utils_strsplit_set (TRUE, "\\ a b\\ \\ c", "\\ a", "b\\ \\ ", "c"); } /*****************************************************************************/ diff --git a/shared/nm-utils/nm-shared-utils.c b/shared/nm-utils/nm-shared-utils.c index 4e2cdbb98a..96b5f2acab 100644 --- a/shared/nm-utils/nm-shared-utils.c +++ b/shared/nm-utils/nm-shared-utils.c @@ -656,6 +656,7 @@ comp_l: * @str: the string to split. * @delimiters: the set of delimiters. If %NULL, defaults to " \t\n", * like bash's $IFS. + * @allow_escaping: whether delimiters can be escaped by a backslash * * This is a replacement for g_strsplit_set() which avoids copying * each word once (the entire strv array), but instead copies it once @@ -664,6 +665,10 @@ comp_l: * Another difference from g_strsplit_set() is that this never returns * empty words. Multiple delimiters are combined and treated as one. * + * If @allow_escaping is %TRUE, delimiters prefixed by a backslash are + * not treated as a separator. Such delimiters and their escape + * character are copied to the current word without unescaping them. + * * Returns: %NULL if @str is %NULL or contains only delimiters. * Otherwise, a %NULL terminated strv array containing non-empty * words, split at the delimiter characters (delimiter characters @@ -673,7 +678,7 @@ comp_l: * but free everything with g_free(). */ const char ** -nm_utils_strsplit_set (const char *str, const char *delimiters) +nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping) { const char **ptr, **ptr0; gsize alloc_size, plen, i; @@ -681,6 +686,7 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) char *s0; char *s; guint8 delimiters_table[256]; + gboolean escaped = FALSE; if (!str) return NULL; @@ -692,13 +698,23 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) for (i = 0; delimiters[i]; i++) delimiters_table[(guint8) delimiters[i]] = 1; -#define _is_delimiter(ch, delimiters_table) \ - ((delimiters_table)[(guint8) (ch)] != 0) +#define _is_delimiter(ch, delimiters_table, allow_esc, esc) \ + ((delimiters_table)[(guint8) (ch)] != 0 && (!allow_esc || !esc)) + +#define next_char(p, esc) \ + G_STMT_START { \ + if (esc) \ + esc = FALSE; \ + else \ + esc = p[0] == '\\'; \ + p++; \ + } G_STMT_END /* skip initial delimiters, and return of the remaining string is * empty. */ - while (_is_delimiter (str[0], delimiters_table)) - str++; + while (_is_delimiter (str[0], delimiters_table, allow_escaping, escaped)) + next_char (str, escaped); + if (!str[0]) return NULL; @@ -730,20 +746,20 @@ nm_utils_strsplit_set (const char *str, const char *delimiters) ptr[plen++] = s; - nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table)); + nm_assert (s[0] && !_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)); while (TRUE) { - s++; - if (_is_delimiter (s[0], delimiters_table)) + next_char (s, escaped); + if (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) break; if (s[0] == '\0') goto done; } s[0] = '\0'; - s++; - while (_is_delimiter (s[0], delimiters_table)) - s++; + next_char (s, escaped); + while (_is_delimiter (s[0], delimiters_table, allow_escaping, escaped)) + next_char (s, escaped); if (s[0] == '\0') break; } @@ -1602,7 +1618,7 @@ nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid) state = p[0]; - tokens = nm_utils_strsplit_set (p, " "); + tokens = nm_utils_strsplit_set (p, " ", FALSE); if (NM_PTRARRAY_LEN (tokens) < 20) goto fail; diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index ef7225e1bb..265d2ded36 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -208,7 +208,7 @@ int nm_utils_dbus_path_cmp (const char *dbus_path_a, const char *dbus_path_b); /*****************************************************************************/ -const char **nm_utils_strsplit_set (const char *str, const char *delimiters); +const char **nm_utils_strsplit_set (const char *str, const char *delimiters, gboolean allow_escaping); gssize nm_utils_strv_find_first (char **list, gssize len, const char *needle); diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 804139e41d..87cb63eca7 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -254,7 +254,7 @@ make_connection_setting (const char *file, if (v) { gs_free const char **items = NULL; - items = nm_utils_strsplit_set (v, " "); + items = nm_utils_strsplit_set (v, " ", FALSE); for (iter = items; iter && *iter; iter++) { if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL)) PARSE_WARNING ("invalid USERS item '%s'", *iter); @@ -270,7 +270,7 @@ make_connection_setting (const char *file, if (v) { gs_free const char **items = NULL; - items = nm_utils_strsplit_set (v, " \t"); + items = nm_utils_strsplit_set (v, " \t", FALSE); for (iter = items; iter && *iter; iter++) { if (!nm_setting_connection_add_secondary (s_con, *iter)) PARSE_WARNING ("secondary connection UUID '%s' already added", *iter); @@ -679,7 +679,7 @@ parse_route_line (const char *line, * Maybe later we want to support some form of quotation here. * Which of course, would be incompatible with initscripts. */ - words_free = nm_utils_strsplit_set (line, " \t\n"); + words_free = nm_utils_strsplit_set (line, " \t\n", FALSE); words = words_free ?: NM_PTRARRAY_EMPTY (const char *); @@ -1115,7 +1115,7 @@ parse_dns_options (NMSettingIPConfig *ip_config, const char *value) if (!nm_setting_ip_config_has_dns_options (ip_config)) nm_setting_ip_config_clear_dns_options (ip_config, TRUE); - options = nm_utils_strsplit_set (value, " "); + options = nm_utils_strsplit_set (value, " ", FALSE); if (options) { for (item = options; *item; item++) { if (!nm_setting_ip_config_add_dns_option (ip_config, *item)) @@ -1485,7 +1485,7 @@ make_ip4_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " "); + searches = nm_utils_strsplit_set (v, " ", FALSE); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) @@ -1546,7 +1546,7 @@ make_ip4_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " "); + searches = nm_utils_strsplit_set (v, " ", FALSE); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (s_ip4, *item)) @@ -1862,7 +1862,7 @@ make_ip6_setting (shvarFile *ifcfg, ipv6addr_secondaries ?: "", NULL); - list = nm_utils_strsplit_set (value, " "); + list = nm_utils_strsplit_set (value, " ", FALSE); for (iter = list, i = 0; iter && *iter; iter++, i++) { NMIPAddress *addr = NULL; @@ -1955,7 +1955,7 @@ make_ip6_setting (shvarFile *ifcfg, if (v) { gs_free const char **searches = NULL; - searches = nm_utils_strsplit_set (v, " "); + searches = nm_utils_strsplit_set (v, " ", FALSE); if (searches) { for (iter = searches; *iter; iter++) { if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter)) @@ -2305,7 +2305,7 @@ read_dcb_percent_array (shvarFile *ifcfg, return TRUE; } - split = nm_utils_strsplit_set (val, ","); + split = nm_utils_strsplit_set (val, ",", FALSE); if (NM_PTRARRAY_LEN (split) != 8) { PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val); g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, @@ -2741,7 +2741,7 @@ fill_wpa_ciphers (shvarFile *ifcfg, if (!p) return TRUE; - list = nm_utils_strsplit_set (p, " "); + list = nm_utils_strsplit_set (p, " ", FALSE); for (iter = list; iter && *iter; iter++, i++) { /* Ad-Hoc configurations cannot have pairwise ciphers, and can only * have one group cipher. Ignore any additional group ciphers and @@ -3147,7 +3147,7 @@ eap_peap_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (v, " "); + list = nm_utils_strsplit_set (v, " ", FALSE); iter = list; if (iter) { if (NM_IN_STRSET (*iter, "MSCHAPV2", @@ -3225,7 +3225,7 @@ eap_ttls_reader (const char *eap_method, inner_auth = g_ascii_strdown (v, -1); /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (inner_auth, " "); + list = nm_utils_strsplit_set (inner_auth, " ", FALSE); iter = list; if (iter) { if (NM_IN_STRSET (*iter, "mschapv2", @@ -3286,7 +3286,7 @@ eap_fast_reader (const char *eap_method, if (fast_provisioning) { gs_free const char **list1 = NULL; - list1 = nm_utils_strsplit_set (fast_provisioning, " \t"); + list1 = nm_utils_strsplit_set (fast_provisioning, " \t", FALSE); for (iter = list1; iter && *iter; iter++) { if (strcmp (*iter, "allow-unauth") == 0) allow_unauth = TRUE; @@ -3320,7 +3320,7 @@ eap_fast_reader (const char *eap_method, } /* Handle options for the inner auth method */ - list = nm_utils_strsplit_set (inner_auth, " "); + list = nm_utils_strsplit_set (inner_auth, " ", FALSE); iter = list; if (iter) { if ( !strcmp (*iter, "MSCHAPV2") @@ -3400,7 +3400,7 @@ read_8021x_list_value (shvarFile *ifcfg, if (!v) return; - strv = nm_utils_strsplit_set (v, " \t"); + strv = nm_utils_strsplit_set (v, " \t", FALSE); if (strv) g_object_set (setting, prop_name, strv, NULL); } @@ -3429,7 +3429,7 @@ fill_8021x (shvarFile *ifcfg, return NULL; } - list = nm_utils_strsplit_set (v, " "); + list = nm_utils_strsplit_set (v, " ", FALSE); s_8021x = (NMSetting8021x *) nm_setting_802_1x_new (); @@ -3738,7 +3738,7 @@ transform_hwaddr_blacklist (const char *blacklist) const char **strv; gsize i, j; - strv = nm_utils_strsplit_set (blacklist, " \t"); + strv = nm_utils_strsplit_set (blacklist, " \t", FALSE); if (!strv) return NULL; for (i = 0, j = 0; strv[j]; j++) { @@ -4059,7 +4059,7 @@ parse_ethtool_option (const char *value, gs_free const char **words = NULL; guint i; - words = nm_utils_strsplit_set (value, NULL); + words = nm_utils_strsplit_set (value, NULL, FALSE); if (!words) return; @@ -4252,7 +4252,7 @@ parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection) gs_free const char **opts = NULL; const char *const *iter; - opts = nm_utils_strsplit_set (ethtool_opts, ";"); + opts = nm_utils_strsplit_set (ethtool_opts, ";", FALSE); for (iter = opts; iter && iter[0]; iter++) { /* in case of repeated wol_passwords, parse_ethtool_option() * will do the right thing and clear wol_password before resetting. */ @@ -4348,7 +4348,7 @@ make_wired_setting (shvarFile *ifcfg, gs_free const char **chans = NULL; guint32 num_chans; - chans = nm_utils_strsplit_set (value, ","); + chans = nm_utils_strsplit_set (value, ",", FALSE); num_chans = NM_PTRARRAY_LEN (chans); if (num_chans < 2 || num_chans > 3) { PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)", @@ -4671,7 +4671,7 @@ make_bond_setting (shvarFile *ifcfg, gs_free const char **items = NULL; const char *const *iter; - items = nm_utils_strsplit_set (v, " "); + items = nm_utils_strsplit_set (v, " ", FALSE); for (iter = items; iter && *iter; iter++) { gs_strfreev char **keys = NULL; const char *key, *val; @@ -4949,7 +4949,7 @@ handle_bridging_opts (NMSetting *setting, gs_free const char **items = NULL; const char *const *iter; - items = nm_utils_strsplit_set (value, " "); + items = nm_utils_strsplit_set (value, " ", FALSE); for (iter = items; iter && *iter; iter++) { gs_strfreev char **keys = NULL; const char *key, *val; @@ -5169,7 +5169,7 @@ parse_prio_map_list (NMSettingVlan *s_vlan, v = svGetValueStr (ifcfg, key, &value); if (!v) return; - list = nm_utils_strsplit_set (v, ","); + list = nm_utils_strsplit_set (v, ",", FALSE); for (iter = list; iter && *iter; iter++) { if (!strchr (*iter, ':')) @@ -5274,7 +5274,7 @@ make_vlan_setting (shvarFile *ifcfg, gs_free const char **strv = NULL; const char *const *ptr; - strv = nm_utils_strsplit_set (v, ", "); + strv = nm_utils_strsplit_set (v, ", ", FALSE); for (ptr = strv; ptr && *ptr; ptr++) { if (nm_streq (*ptr, "GVRP") && gvrp == -1) vlan_flags |= NM_VLAN_FLAG_GVRP; @@ -5416,7 +5416,7 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6) gs_free const char **searches = NULL; const char *const *item; - searches = nm_utils_strsplit_set (v, " "); + searches = nm_utils_strsplit_set (v, " ", FALSE); if (searches) { for (item = searches; *item; item++) { if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item)) From 9b9dce9486a8d7a5ddaca6b79614564d506396aa Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Tue, 7 Aug 2018 15:52:56 +0200 Subject: [PATCH 4/6] all: add 'match' setting Add a new 'match' setting containing properties to match a connection to devices. At the moment only the interface-name property is present and, contrary to connection.interface-name, it allows the use of wildcards. --- Makefile.am | 2 + clients/cli/connections.c | 1 + clients/common/nm-meta-setting-desc.c | 78 +++++++ clients/common/settings-docs.h.in | 1 + docs/libnm/libnm-docs.xml | 1 + libnm-core/meson.build | 2 + libnm-core/nm-core-internal.h | 1 + libnm-core/nm-core-types.h | 1 + libnm-core/nm-setting-match.c | 295 ++++++++++++++++++++++++++ libnm-core/nm-setting-match.h | 63 ++++++ libnm/NetworkManager.h | 1 + libnm/libnm.ver | 8 + shared/nm-meta-setting.c | 7 + shared/nm-meta-setting.h | 1 + 14 files changed, 462 insertions(+) create mode 100644 libnm-core/nm-setting-match.c create mode 100644 libnm-core/nm-setting-match.h diff --git a/Makefile.am b/Makefile.am index fe156f9284..3dec548c17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -463,6 +463,7 @@ libnm_core_lib_h_pub_real = \ libnm-core/nm-setting-ip6-config.h \ libnm-core/nm-setting-macsec.h \ libnm-core/nm-setting-macvlan.h \ + libnm-core/nm-setting-match.h \ libnm-core/nm-setting-olpc-mesh.h \ libnm-core/nm-setting-ovs-bridge.h \ libnm-core/nm-setting-ovs-interface.h \ @@ -536,6 +537,7 @@ libnm_core_lib_c_settings_real = \ libnm-core/nm-setting-ip6-config.c \ libnm-core/nm-setting-macsec.c \ libnm-core/nm-setting-macvlan.c \ + libnm-core/nm-setting-match.c \ libnm-core/nm-setting-olpc-mesh.c \ libnm-core/nm-setting-ovs-bridge.c \ libnm-core/nm-setting-ovs-interface.c \ diff --git a/clients/cli/connections.c b/clients/cli/connections.c index f4d19ee39c..3808b86dd2 100644 --- a/clients/cli/connections.c +++ b/clients/cli/connections.c @@ -734,6 +734,7 @@ const NmcMetaGenericInfo *const metagen_con_active_vpn[_NMC_GENERIC_INFO_TYPE_CO /*****************************************************************************/ #define NMC_FIELDS_SETTINGS_NAMES_ALL NM_SETTING_CONNECTION_SETTING_NAME","\ + NM_SETTING_MATCH_SETTING_NAME","\ NM_SETTING_WIRED_SETTING_NAME","\ NM_SETTING_802_1X_SETTING_NAME","\ NM_SETTING_WIRELESS_SETTING_NAME","\ diff --git a/clients/common/nm-meta-setting-desc.c b/clients/common/nm-meta-setting-desc.c index c041b10ec7..29eb98a775 100644 --- a/clients/common/nm-meta-setting-desc.c +++ b/clients/common/nm-meta-setting-desc.c @@ -3730,6 +3730,68 @@ DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_ipv6_config_routes, nm_setting_ip_config_remove_route, _validate_and_remove_ipv6_route) +static gconstpointer +_get_fcn_match_interface_name (ARGS_GET_FCN) +{ + NMSettingMatch *s_match = NM_SETTING_MATCH (setting); + GString *str = NULL; + guint i, num; + + RETURN_UNSUPPORTED_GET_TYPE (); + + num = nm_setting_match_get_num_interface_names (s_match); + for (i = 0; i < num; i++) { + const char *name; + gs_free char *to_free = NULL; + + if (i == 0) + str = g_string_new (""); + else + g_string_append_c (str, ' '); + name = nm_setting_match_get_interface_name (s_match, i); + g_string_append (str, _nm_utils_escape_spaces (name, &to_free)); + } + RETURN_STR_TO_FREE (g_string_free (str, FALSE)); +} + +static gboolean +_set_fcn_match_interface_name (ARGS_SET_FCN) +{ + gs_free const char **strv = NULL; + gsize i; + + nm_assert (!error || !*error); + + strv = nm_utils_strsplit_set (value, " \t", TRUE); + if (strv) { + for (i = 0; strv[i]; i++) { + nm_setting_match_add_interface_name (NM_SETTING_MATCH (setting), + _nm_utils_unescape_spaces ((char *) strv[i])); + } + } + return TRUE; +} + +static gboolean +_validate_and_remove_match_interface_name (NMSettingMatch *setting, + const char *interface_name, + GError **error) +{ + gboolean ret; + + ret = nm_setting_match_remove_interface_name_by_value (setting, interface_name); + if (!ret) + g_set_error (error, 1, 0, + _("the property doesn't contain interface name '%s'"), + interface_name); + return ret; +} +DEFINE_REMOVER_INDEX_OR_VALUE (_remove_fcn_match_interface_name, + NM_SETTING_MATCH, + nm_setting_match_get_num_interface_names, + nm_setting_match_remove_interface_name, + _validate_and_remove_match_interface_name) + static gconstpointer _get_fcn_olpc_mesh_ssid (ARGS_GET_FCN) { @@ -6532,6 +6594,19 @@ static const NMMetaPropertyInfo *const property_infos_MACVLAN[] = { NULL }; +#undef _CURRENT_NM_META_SETTING_TYPE +#define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_MATCH +static const NMMetaPropertyInfo *const property_infos_MATCH[] = { + PROPERTY_INFO_WITH_DESC (NM_SETTING_MATCH_INTERFACE_NAME, + .property_type = DEFINE_PROPERTY_TYPE ( + .get_fcn = _get_fcn_match_interface_name, + .set_fcn = _set_fcn_match_interface_name, + .remove_fcn = _remove_fcn_match_interface_name, + ), + ), + NULL +}; + #undef _CURRENT_NM_META_SETTING_TYPE #define _CURRENT_NM_META_SETTING_TYPE NM_META_SETTING_TYPE_OLPC_MESH static const NMMetaPropertyInfo *const property_infos_OLPC_MESH[] = { @@ -7900,6 +7975,7 @@ _setting_init_fcn_wireless (ARGS_SETTING_INIT_FCN) #define SETTING_PRETTY_NAME_IP_TUNNEL N_("IP-tunnel settings") #define SETTING_PRETTY_NAME_MACSEC N_("MACsec connection") #define SETTING_PRETTY_NAME_MACVLAN N_("macvlan connection") +#define SETTING_PRETTY_NAME_MATCH N_("Match") #define SETTING_PRETTY_NAME_OLPC_MESH N_("OLPC Mesh connection") #define SETTING_PRETTY_NAME_OVS_BRIDGE N_("OpenVSwitch bridge settings") #define SETTING_PRETTY_NAME_OVS_INTERFACE N_("OpenVSwitch interface settings") @@ -8066,6 +8142,7 @@ const NMMetaSettingInfoEditor nm_meta_setting_infos_editor[] = { NM_META_SETTING_VALID_PART_ITEM (ETHTOOL, FALSE), ), ), + SETTING_INFO (MATCH), SETTING_INFO (OLPC_MESH, .alias = "olpc-mesh", .valid_parts = NM_META_SETTING_VALID_PARTS ( @@ -8209,6 +8286,7 @@ const NMMetaSettingValidPartItem *const nm_meta_setting_info_valid_parts_default /*****************************************************************************/ static const NMMetaSettingValidPartItem *const valid_settings_noslave[] = { + NM_META_SETTING_VALID_PART_ITEM (MATCH, FALSE), NM_META_SETTING_VALID_PART_ITEM (IP4_CONFIG, FALSE), NM_META_SETTING_VALID_PART_ITEM (IP6_CONFIG, FALSE), NM_META_SETTING_VALID_PART_ITEM (TC_CONFIG, FALSE), diff --git a/clients/common/settings-docs.h.in b/clients/common/settings-docs.h.in index d0814cd38e..b80da262cb 100644 --- a/clients/common/settings-docs.h.in +++ b/clients/common/settings-docs.h.in @@ -249,6 +249,7 @@ #define DESCRIBE_DOC_NM_SETTING_MACVLAN_PARENT N_("If given, specifies the parent interface name or parent connection UUID from which this MAC-VLAN interface should be created. If this property is not specified, the connection must contain an \"802-3-ethernet\" setting with a \"mac-address\" property.") #define DESCRIBE_DOC_NM_SETTING_MACVLAN_PROMISCUOUS N_("Whether the interface should be put in promiscuous mode.") #define DESCRIBE_DOC_NM_SETTING_MACVLAN_TAP N_("Whether the interface should be a MACVTAP.") +#define DESCRIBE_DOC_NM_SETTING_MATCH_INTERFACE_NAME N_("A list of interface names to match. Each element is a shell wildcard pattern. When an element is prefixed with exclamation mark (!) the condition is inverted. A candidate interface name is considered matching when both these conditions are satisfied: (a) any of the elements not prefixed with '!' matches or there aren't such elements; (b) none of the elements prefixed with '!' match.") #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_FAIL_MODE N_("The bridge failure mode. One of \"secure\", \"standalone\" or empty.") #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE N_("Enable or disable multicast snooping.") #define DESCRIBE_DOC_NM_SETTING_OVS_BRIDGE_RSTP_ENABLE N_("Enable or disable RSTP.") diff --git a/docs/libnm/libnm-docs.xml b/docs/libnm/libnm-docs.xml index 94ef406a1d..8de396c45b 100644 --- a/docs/libnm/libnm-docs.xml +++ b/docs/libnm/libnm-docs.xml @@ -212,6 +212,7 @@ print ("NetworkManager version " + client.get_version())]]> + diff --git a/libnm-core/meson.build b/libnm-core/meson.build index 0a0406dfab..ab570b6b47 100644 --- a/libnm-core/meson.build +++ b/libnm-core/meson.build @@ -25,6 +25,7 @@ libnm_core_headers = files( 'nm-setting-ip6-config.h', 'nm-setting-macsec.h', 'nm-setting-macvlan.h', + 'nm-setting-match.h', 'nm-setting-olpc-mesh.h', 'nm-setting-ovs-bridge.h', 'nm-setting-ovs-interface.h', @@ -79,6 +80,7 @@ libnm_core_settings_sources = files( 'nm-setting-ip6-config.c', 'nm-setting-macsec.c', 'nm-setting-macvlan.c', + 'nm-setting-match.c', 'nm-setting-olpc-mesh.c', 'nm-setting-ovs-bridge.c', 'nm-setting-ovs-interface.c', diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 0c999c8c84..337018485d 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -57,6 +57,7 @@ #include "nm-setting-ip6-config.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" +#include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-interface.h" diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h index 4282fdfe5c..89d99579e5 100644 --- a/libnm-core/nm-core-types.h +++ b/libnm-core/nm-core-types.h @@ -51,6 +51,7 @@ typedef struct _NMSettingIP4Config NMSettingIP4Config; typedef struct _NMSettingIP6Config NMSettingIP6Config; typedef struct _NMSettingMacsec NMSettingMacsec; typedef struct _NMSettingMacvlan NMSettingMacvlan; +typedef struct _NMSettingMatch NMSettingMatch; typedef struct _NMSettingOlpcMesh NMSettingOlpcMesh; typedef struct _NMSettingOvsBridge NMSettingOvsBridge; typedef struct _NMSettingOvsInterface NMSettingOvsInterface; diff --git a/libnm-core/nm-setting-match.c b/libnm-core/nm-setting-match.c new file mode 100644 index 0000000000..0964c64471 --- /dev/null +++ b/libnm-core/nm-setting-match.c @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + * + * Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-setting-match.h" +#include "nm-setting-private.h" +#include "nm-utils-private.h" + +/** + * SECTION:nm-setting-match + * @short_description: Properties to match a connection with a device. + * @include: nm-setting-match.h + **/ + +/** + * NMSettingMatch: + * + * Match settings. + * + * Since: 1.14 + */ +struct _NMSettingMatch { + NMSetting parent; + GPtrArray *interface_name; +}; + +struct _NMSettingMatchClass { + NMSettingClass parent; +}; + +G_DEFINE_TYPE (NMSettingMatch, nm_setting_match, NM_TYPE_SETTING) + +NM_GOBJECT_PROPERTIES_DEFINE (NMSettingMatch, + PROP_INTERFACE_NAME, +); + +/*****************************************************************************/ + +/** + * nm_setting_match_get_num_interface_names: + * @setting: the #NMSettingMatch + * + * Returns: the number of configured interface names + * + * Since: 1.14 + **/ +guint +nm_setting_match_get_num_interface_names (NMSettingMatch *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), 0); + + return setting->interface_name->len; +} + +/** + * nm_setting_match_get_interface_name: + * @setting: the #NMSettingMatch + * @idx: index number of the DNS search domain to return + * + * Returns: the interface name at index @idx + * + * Since: 1.14 + **/ +const char * +nm_setting_match_get_interface_name (NMSettingMatch *setting, int idx) +{ + g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), NULL); + + g_return_val_if_fail (idx >= 0 && idx < setting->interface_name->len, NULL); + + return setting->interface_name->pdata[idx]; +} + +/** + * nm_setting_match_add_interface_name: + * @setting: the #NMSettingMatch + * @interface_name: the interface name to add + * + * Adds a new interface name to the setting. + * + * Since: 1.14 + **/ +void +nm_setting_match_add_interface_name (NMSettingMatch *setting, + const char *interface_name) +{ + g_return_if_fail (NM_IS_SETTING_MATCH (setting)); + g_return_if_fail (interface_name != NULL); + g_return_if_fail (interface_name[0] != '\0'); + + g_ptr_array_add (setting->interface_name, g_strdup (interface_name)); + _notify (setting, PROP_INTERFACE_NAME); +} + +/** + * nm_setting_match_remove_interface_name: + * @setting: the #NMSettingMatch + * @idx: index number of the interface name + * + * Removes the interface name at index @idx. + * + * Since: 1.14 + **/ +void +nm_setting_match_remove_interface_name (NMSettingMatch *setting, int idx) +{ + g_return_if_fail (NM_IS_SETTING_MATCH (setting)); + + g_return_if_fail (idx >= 0 && idx < setting->interface_name->len); + + g_ptr_array_remove_index (setting->interface_name, idx); + _notify (setting, PROP_INTERFACE_NAME); +} + +/** + * nm_setting_match_remove_interface_name_by_value: + * @setting: the #NMSettingMatch + * @interface_name: the interface name to remove + * + * Removes @interface_name. + * + * Returns: %TRUE if the interface name was found and removed; %FALSE if it was not. + * + * Since: 1.14 + **/ +gboolean +nm_setting_match_remove_interface_name_by_value (NMSettingMatch *setting, + const char *interface_name) +{ + guint i; + + g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), FALSE); + g_return_val_if_fail (interface_name != NULL, FALSE); + g_return_val_if_fail (interface_name[0] != '\0', FALSE); + + for (i = 0; i < setting->interface_name->len; i++) { + if (nm_streq (interface_name, setting->interface_name->pdata[i])) { + g_ptr_array_remove_index (setting->interface_name, i); + _notify (setting, PROP_INTERFACE_NAME); + return TRUE; + } + } + return FALSE; +} + +/** + * nm_setting_match_clear_interface_names: + * @setting: the #NMSettingMatch + * + * Removes all configured interface names. + * + * Since: 1.14 + **/ +void +nm_setting_match_clear_interface_names (NMSettingMatch *setting) +{ + g_return_if_fail (NM_IS_SETTING_MATCH (setting)); + + if (setting->interface_name->len != 0) { + g_ptr_array_set_size (setting->interface_name, 0); + _notify (setting, PROP_INTERFACE_NAME); + } +} + +/** + * nm_setting_match_get_interface_names: + * @setting: the #NMSettingMatch + * + * Returns all the interface names. + * + * Returns: (transfer none): the configured interface names. + * + * Since: 1.14 + **/ +const char *const * +nm_setting_match_get_interface_names (NMSettingMatch *setting, guint *length) +{ + g_return_val_if_fail (NM_IS_SETTING_MATCH (setting), NULL); + g_return_val_if_fail (length, NULL); + + NM_SET_OUT (length, setting->interface_name->len); + return (const char *const *) setting->interface_name->pdata; +} + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMSettingMatch *self = NM_SETTING_MATCH (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (self->interface_name)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + NMSettingMatch *self = NM_SETTING_MATCH (object); + + switch (prop_id) { + case PROP_INTERFACE_NAME: + g_ptr_array_unref (self->interface_name); + self->interface_name = _nm_utils_strv_to_ptrarray (g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nm_setting_match_init (NMSettingMatch *setting) +{ + setting->interface_name = g_ptr_array_new_with_free_func (g_free); +} + +/** + * nm_setting_match_new: + * + * Creates a new #NMSettingMatch object with default values. + * + * Returns: (transfer full): the new empty #NMSettingMatch object + * + * Since: 1.14 + **/ +NMSetting * +nm_setting_match_new (void) +{ + return (NMSetting *) g_object_new (NM_TYPE_SETTING_MATCH, NULL); +} + +static void +finalize (GObject *object) +{ + NMSettingMatch *self = NM_SETTING_MATCH (object); + + g_ptr_array_unref (self->interface_name); + + G_OBJECT_CLASS (nm_setting_match_parent_class)->finalize (object); +} + +static void +nm_setting_match_class_init (NMSettingMatchClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMSettingClass *setting_class = NM_SETTING_CLASS (klass); + + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + + /** + * NMSettingMatch:interface-name + * + * A list of interface names to match. Each element is a shell wildcard + * pattern. When an element is prefixed with exclamation mark (!) the + * condition is inverted. + * + * A candidate interface name is considered matching when both these + * conditions are satisfied: (a) any of the elements not prefixed with '!' + * matches or there aren't such elements; (b) none of the elements + * prefixed with '!' match. + * + * Since: 1.14 + **/ + obj_properties[PROP_INTERFACE_NAME] = + g_param_spec_boxed (NM_SETTING_MATCH_INTERFACE_NAME, "", "", + G_TYPE_STRV, + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); + + _nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_MATCH); +} diff --git a/libnm-core/nm-setting-match.h b/libnm-core/nm-setting-match.h new file mode 100644 index 0000000000..a39feca2f1 --- /dev/null +++ b/libnm-core/nm-setting-match.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + * + * Copyright 2018 Red Hat, Inc. + */ + +#ifndef NM_SETTING_MATCH_H +#define NM_SETTING_MATCH_H + +#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION) +#error "Only can be included directly." +#endif + +#include "nm-setting.h" + +G_BEGIN_DECLS + +#define NM_TYPE_SETTING_MATCH (nm_setting_match_get_type ()) +#define NM_SETTING_MATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_MATCH, NMSettingMatch)) +#define NM_SETTING_MATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_MATCH, NMSettingMatchClass)) +#define NM_IS_SETTING_MATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_MATCH)) +#define NM_IS_SETTING_MATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_MATCH)) +#define NM_SETTING_MATCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_MATCH, NMSettingMatchClass)) + +#define NM_SETTING_MATCH_SETTING_NAME "match" + +#define NM_SETTING_MATCH_INTERFACE_NAME "interface-name" + +typedef struct _NMSettingMatchClass NMSettingMatchClass; + + +NM_AVAILABLE_IN_1_14 +GType nm_setting_match_get_type (void); +NM_AVAILABLE_IN_1_14 +NMSetting *nm_setting_match_new (void); + +NM_AVAILABLE_IN_1_14 +guint nm_setting_match_get_num_interface_names (NMSettingMatch *setting); +NM_AVAILABLE_IN_1_14 +const char *nm_setting_match_get_interface_name (NMSettingMatch *setting, int idx); +NM_AVAILABLE_IN_1_14 +void nm_setting_match_remove_interface_name (NMSettingMatch *setting, int idx); +NM_AVAILABLE_IN_1_14 +gboolean nm_setting_match_remove_interface_name_by_value (NMSettingMatch *setting, + const char *interface_name); +NM_AVAILABLE_IN_1_14 +void nm_setting_match_add_interface_name (NMSettingMatch *setting, + const char *interface_name); +NM_AVAILABLE_IN_1_14 +void nm_setting_match_clear_interface_names (NMSettingMatch *setting); +NM_AVAILABLE_IN_1_14 +const char *const *nm_setting_match_get_interface_names (NMSettingMatch *setting, guint *length); +G_END_DECLS + +#endif /* NM_SETTING_MATCH_H */ diff --git a/libnm/NetworkManager.h b/libnm/NetworkManager.h index a4af9b0d8e..759a413187 100644 --- a/libnm/NetworkManager.h +++ b/libnm/NetworkManager.h @@ -82,6 +82,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" +#include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-interface.h" diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 042453daa4..77ac0eed50 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1399,6 +1399,14 @@ global: nm_setting_ethtool_get_type; nm_setting_ethtool_new; nm_setting_ethtool_set_feature; + nm_setting_match_add_interface_name; + nm_setting_match_clear_interface_names; + nm_setting_match_get_interface_name; + nm_setting_match_get_interface_names; + nm_setting_match_get_num_interface_names; + nm_setting_match_get_type; + nm_setting_match_remove_interface_name; + nm_setting_match_remove_interface_name_by_value; nm_setting_sriov_add_vf; nm_setting_sriov_clear_vfs; nm_setting_sriov_get_autoprobe_drivers; diff --git a/shared/nm-meta-setting.c b/shared/nm-meta-setting.c index c565e55f38..3e79747fba 100644 --- a/shared/nm-meta-setting.c +++ b/shared/nm-meta-setting.c @@ -44,6 +44,7 @@ #include "nm-setting-ip-tunnel.h" #include "nm-setting-macsec.h" #include "nm-setting-macvlan.h" +#include "nm-setting-match.h" #include "nm-setting-olpc-mesh.h" #include "nm-setting-ovs-bridge.h" #include "nm-setting-ovs-interface.h" @@ -268,6 +269,12 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = { .setting_name = NM_SETTING_MACVLAN_SETTING_NAME, .get_setting_gtype = nm_setting_macvlan_get_type, }, + [NM_META_SETTING_TYPE_MATCH] = { + .meta_type = NM_META_SETTING_TYPE_MATCH, + .setting_priority = NM_SETTING_PRIORITY_AUX, + .setting_name = NM_SETTING_MATCH_SETTING_NAME, + .get_setting_gtype = nm_setting_match_get_type, + }, [NM_META_SETTING_TYPE_OLPC_MESH] = { .meta_type = NM_META_SETTING_TYPE_OLPC_MESH, .setting_priority = NM_SETTING_PRIORITY_HW_BASE, diff --git a/shared/nm-meta-setting.h b/shared/nm-meta-setting.h index c76a4b7008..26c29beaa7 100644 --- a/shared/nm-meta-setting.h +++ b/shared/nm-meta-setting.h @@ -127,6 +127,7 @@ typedef enum { NM_META_SETTING_TYPE_IP6_CONFIG, NM_META_SETTING_TYPE_MACSEC, NM_META_SETTING_TYPE_MACVLAN, + NM_META_SETTING_TYPE_MATCH, NM_META_SETTING_TYPE_OVS_BRIDGE, NM_META_SETTING_TYPE_OVS_INTERFACE, NM_META_SETTING_TYPE_OVS_PATCH, From d47e0beb7df88e6b11952ece2e50a1541055adab Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Fri, 10 Aug 2018 17:31:59 +0200 Subject: [PATCH 5/6] ifcfg-rh: add support for 'match' setting --- Makefile.am | 1 + .../plugins/ifcfg-rh/nms-ifcfg-rh-reader.c | 32 +++++++++- .../plugins/ifcfg-rh/nms-ifcfg-rh-writer.c | 35 +++++++++++ .../ifcfg-Test_Write_Wired_match.cexpected | 11 ++++ .../plugins/ifcfg-rh/tests/test-ifcfg-rh.c | 62 +++++++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected diff --git a/Makefile.am b/Makefile.am index 3dec548c17..a423b1aae6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2254,6 +2254,7 @@ EXTRA_DIST += \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected \ + src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected \ src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-netmask-1 \ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index 87cb63eca7..616e5f8ba0 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -1218,6 +1218,32 @@ make_user_setting (shvarFile *ifcfg) : NULL; } +static NMSetting * +make_match_setting (shvarFile *ifcfg) +{ + NMSettingMatch *s_match = NULL; + gs_free const char **strv = NULL; + gs_free char *value = NULL; + const char *v; + gsize i; + + v = svGetValueStr (ifcfg, "MATCH_INTERFACE_NAME", &value); + if (!v) + return NULL; + + strv = nm_utils_strsplit_set (v, " \t", TRUE); + if (strv) { + for (i = 0; strv[i]; i++) { + if (!s_match) + s_match = (NMSettingMatch *) nm_setting_match_new (); + nm_setting_match_add_interface_name (s_match, + _nm_utils_unescape_spaces ((char *) strv[i])); + } + } + + return (NMSetting *) s_match; +} + static NMSetting * make_proxy_setting (shvarFile *ifcfg) { @@ -5441,7 +5467,7 @@ connection_from_file_full (const char *filename, gs_free char *type = NULL; char *devtype, *bootproto; NMSetting *s_ip4, *s_ip6, *s_tc, *s_proxy, *s_port, *s_dcb = NULL, *s_user; - NMSetting *s_sriov; + NMSetting *s_sriov, *s_match; const char *ifcfg_name = NULL; gboolean has_ip4_defroute = FALSE; gboolean has_complex_routes_v4; @@ -5721,6 +5747,10 @@ connection_from_file_full (const char *filename, if (s_user) nm_connection_add_setting (connection, s_user); + s_match = make_match_setting (parsed); + if (s_match) + nm_connection_add_setting (connection, s_match); + /* Bridge port? */ s_port = make_bridge_port_setting (parsed); if (s_port) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index a2abe995a8..74e8497578 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -2285,6 +2285,38 @@ write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) return TRUE; } +static gboolean +write_match_setting (NMConnection *connection, shvarFile *ifcfg, GError **error) +{ + NMSettingMatch *s_match; + nm_auto_free_gstring GString *str = NULL; + guint i, num; + + svUnsetValue (ifcfg, "MATCH_INTERFACE_NAME"); + + s_match = (NMSettingMatch *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MATCH); + if (!s_match) + return TRUE; + + num = nm_setting_match_get_num_interface_names (s_match); + for (i = 0; i < num; i++) { + gs_free char *to_free = NULL; + const char *name; + + if (i == 0) + str = g_string_new (""); + else + g_string_append_c (str, ' '); + name = nm_setting_match_get_interface_name (s_match, i); + g_string_append (str, _nm_utils_escape_spaces (name, &to_free)); + } + + if (str) + svSetValueStr (ifcfg, "MATCH_INTERFACE_NAME", str->str); + + return TRUE; +} + static void write_res_options (shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var) { @@ -3030,6 +3062,9 @@ do_write_construct (NMConnection *connection, if (!write_user_setting (connection, ifcfg, error)) return FALSE; + if (!write_match_setting (connection, ifcfg, error)) + return FALSE; + write_sriov_setting (connection, ifcfg); if (!write_tc_setting (connection, ifcfg, error)) diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected new file mode 100644 index 0000000000..f0cd36cb13 --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected @@ -0,0 +1,11 @@ +TYPE=Ethernet +PROXY_METHOD=none +BROWSER_ONLY=no +MATCH_INTERFACE_NAME="ens* eth\\ 1? !veth*" +BOOTPROTO=dhcp +DEFROUTE=yes +IPV4_FAILURE_FATAL=no +IPV6INIT=no +NAME="Test Write Wired with Match setting" +UUID=${UUID} +ONBOOT=yes diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index fc42937d0e..99e2beb748 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -4434,6 +4434,67 @@ test_write_wired_dhcp (void) nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); } +static void +test_write_wired_match (void) +{ + nmtst_auto_unlinkfile char *testfile = NULL; + gs_unref_object NMConnection *connection = NULL; + gs_unref_object NMConnection *reread = NULL; + NMSettingConnection *s_con; + NMSettingWired *s_wired; + NMSettingMatch *s_match; + NMSettingIPConfig *s_ip4; + NMSettingIPConfig *s_ip6; + + connection = nm_simple_connection_new (); + + /* Connection setting */ + s_con = (NMSettingConnection *) nm_setting_connection_new (); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + g_object_set (s_con, + NM_SETTING_CONNECTION_ID, "Test Write Wired with Match setting", + NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (), + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NULL); + + /* Wired setting */ + s_wired = (NMSettingWired *) nm_setting_wired_new (); + nm_connection_add_setting (connection, NM_SETTING (s_wired)); + + /* IP4 setting */ + s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip4)); + + g_object_set (s_ip4, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NULL); + + /* IP6 setting */ + s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new (); + nm_connection_add_setting (connection, NM_SETTING (s_ip6)); + + g_object_set (s_ip6, + NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, + NULL); + + /* Match setting */ + s_match = (NMSettingMatch *) nm_setting_match_new (); + nm_setting_match_add_interface_name (s_match, "ens*"); + nm_setting_match_add_interface_name (s_match, "eth 1?"); + nm_setting_match_add_interface_name (s_match, "!veth*"); + nm_connection_add_setting (connection, NM_SETTING (s_match)); + + nmtst_assert_connection_verifies (connection); + _writer_new_connec_exp (connection, + TEST_SCRATCH_DIR, + TEST_IFCFG_DIR"/ifcfg-Test_Write_Wired_match.cexpected", + &testfile); + reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL); + nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); +} + static void test_write_wired_dhcp_plus_ip (void) { @@ -10072,6 +10133,7 @@ int main (int argc, char **argv) g_test_add_func (TPATH "wired/write/dhcp", test_write_wired_dhcp); g_test_add_func (TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip); g_test_add_func (TPATH "wired/write/dhcp-8021x-peap-mschapv2", test_write_wired_dhcp_8021x_peap_mschapv2); + g_test_add_func (TPATH "wired/write/match", test_write_wired_match); #define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \ nmtst_add_test_func (testpath, test_write_wired_8021x_tls, GINT_TO_POINTER (scheme), GINT_TO_POINTER (flags)) From 81978e36ba13a88166afaa5bfd3f01e9c4aba80a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Thu, 9 Aug 2018 09:29:51 +0200 Subject: [PATCH 6/6] device: support match.interface-name Add support for matching a connection with the new match.interface-name property. --- src/devices/nm-device.c | 19 +++++++++++++++---- src/nm-core-utils.c | 28 ++++++++++++++++++++++++++++ src/nm-core-utils.h | 4 ++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 908a0b5359..f2a9f3fa52 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -5511,6 +5511,9 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError ** gs_free_error GError *local = NULL; gs_free char *conn_iface = NULL; NMDeviceClass *klass; + const char *const *patterns; + NMSettingMatch *s_match; + guint num_patterns; klass = NM_DEVICE_GET_CLASS (self); if (klass->connection_type_check_compatible) { @@ -5534,15 +5537,23 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError ** "cannot get interface name due to %s", local->message); return FALSE; } - return TRUE; - } - - if (!nm_streq0 (conn_iface, device_iface)) { + } else if (!nm_streq0 (conn_iface, device_iface)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "mismatching interface name"); return FALSE; } + s_match = (NMSettingMatch *) nm_connection_get_setting (connection, + NM_TYPE_SETTING_MATCH); + if (s_match) { + patterns = nm_setting_match_get_interface_names (s_match, &num_patterns); + if (!nm_wildcard_match_check (device_iface, patterns, num_patterns)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not satisfy match.interface-name property"); + return FALSE; + } + } + return TRUE; } diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 51caf8bdfc..9aab919ebf 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -1730,6 +1731,33 @@ nm_match_spec_join (GSList *specs) return g_string_free (str, FALSE); } +gboolean +nm_wildcard_match_check (const char *str, + const char *const *patterns, + guint num_patterns) +{ + guint i, neg = 0; + + for (i = 0; i < num_patterns; i++) { + if (patterns[i][0] == '!') { + neg++; + if (!fnmatch (patterns[i] + 1, str, 0)) + return FALSE; + } + } + + if (neg == num_patterns) + return TRUE; + + for (i = 0; i < num_patterns; i++) { + if ( patterns[i][0] != '!' + && !fnmatch (patterns[i], str, 0)) + return TRUE; + } + + return FALSE; +} + /*****************************************************************************/ char * diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h index fe429a72e9..53c72e16f9 100644 --- a/src/nm-core-utils.h +++ b/src/nm-core-utils.h @@ -215,6 +215,10 @@ NMMatchSpecMatchType nm_match_spec_config (const GSList *specs, GSList *nm_match_spec_split (const char *value); char *nm_match_spec_join (GSList *specs); +gboolean nm_wildcard_match_check (const char *str, + const char *const *patterns, + guint num_patterns); + /*****************************************************************************/ const char *nm_utils_get_ip_config_method (NMConnection *connection,