diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in index f3011c4f73..40fa49dd7a 100644 --- a/man/NetworkManager.conf.xml.in +++ b/man/NetworkManager.conf.xml.in @@ -62,7 +62,8 @@ Copyright 2010 - 2014 Red Hat, Inc. For keys that take a list of devices as their value, you can specify devices by their MAC addresses or interface names, or - "*" to specify all devices. + "*" to specify all devices. See + below. Minimal system settings configuration file looks like this: @@ -76,6 +77,7 @@ Copyright 2010 - 2014 Red Hat, Inc. append a value to a previously-set list-valued key by doing: plugins+=another-plugin + plugins-=remove-me diff --git a/src/nm-config.c b/src/nm-config.c index 51aff75db2..f018a91819 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -527,35 +527,53 @@ read_config (GKeyFile *keyfile, const char *path, GError **error) } for (g = 0; groups[g]; g++) { - keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL); + const char *group = groups[g]; + + keys = g_key_file_get_keys (kf, group, &nkeys, NULL); if (!keys) continue; for (k = 0; keys[k]; k++) { - int len = strlen (keys[k]); - char *v; + const char *key; + char *new_value; + char last_char; + gsize key_len; - if (keys[k][len - 1] == '+') { - char *base_key = g_strndup (keys[k], len - 1); - char *old_val = g_key_file_get_value (keyfile, groups[g], base_key, NULL); - char *new_val = g_key_file_get_value (kf, groups[g], keys[k], NULL); + key = keys[k]; + g_assert (key && *key); + key_len = strlen (key); + last_char = key[key_len - 1]; + if ( key_len > 1 + && (last_char == '+' || last_char == '-')) { + gs_free char *base_key = g_strndup (key, key_len - 1); + gs_strfreev char **old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, NULL); + gs_free char **new_val = g_key_file_get_string_list (kf, group, key, NULL, NULL); + gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func (g_free); + char **iter_val; - if (old_val && *old_val) { - char *combined = g_strconcat (old_val, ",", new_val, NULL); + for (iter_val = old_val; iter_val && *iter_val; iter_val++) { + if ( last_char != '-' + || _nm_utils_strv_find_first (new_val, -1, *iter_val) < 0) + g_ptr_array_add (new, g_strdup (*iter_val)); + } + for (iter_val = new_val; iter_val && *iter_val; iter_val++) { + /* don't add duplicates. That means an "option=a,b"; "option+=a,c" results in "option=a,b,c" */ + if ( last_char == '+' + && _nm_utils_strv_find_first (old_val, -1, *iter_val) < 0) + g_ptr_array_add (new, *iter_val); + else + g_free (*iter_val); + } - g_key_file_set_value (keyfile, groups[g], base_key, combined); - g_free (combined); - } else - g_key_file_set_value (keyfile, groups[g], base_key, new_val); - - g_free (base_key); - g_free (old_val); - g_free (new_val); + if (new->len > 0) + nm_config_keyfile_set_string_list (keyfile, group, base_key, (const char *const*) new->pdata, new->len); + else + g_key_file_remove_key (keyfile, group, base_key, NULL); continue; } - g_key_file_set_value (keyfile, groups[g], keys[k], - v = g_key_file_get_value (kf, groups[g], keys[k], NULL)); - g_free (v); + new_value = g_key_file_get_value (kf, group, key, NULL); + g_key_file_set_value (keyfile, group, key, new_value); + g_free (new_value); } g_strfreev (keys); } diff --git a/src/tests/config/conf.d/00-overrides.conf b/src/tests/config/conf.d/00-overrides.conf index 0c246a02a4..cb0116edd8 100644 --- a/src/tests/config/conf.d/00-overrides.conf +++ b/src/tests/config/conf.d/00-overrides.conf @@ -31,3 +31,20 @@ ord.key07=B-1.3.07 ord.key08=B-1.3.08 ord.key09=B-1.3.09 + +[append] +val1=a,b + +val2-=VAL2 +val2=VAL2 + +val3=VAL3 +val3-=VAL3 + +val4=VAL4 +val4+=VAL4,va,vb,va,vb +val4-=VAL4,va + +val5=VAL5 +val5-=VAL5 +val5+=VAL5 diff --git a/src/tests/config/conf.d/10-more.conf b/src/tests/config/conf.d/10-more.conf index 08b73ddfdc..a1959c1948 100644 --- a/src/tests/config/conf.d/10-more.conf +++ b/src/tests/config/conf.d/10-more.conf @@ -27,3 +27,7 @@ ord.key09=C-2.3.09 # low priority and is shadowed by [connection.ord.2.1]. [connection.ord.0.1] ord.ovw01=C-0.1.ovw01 + + +[append] +val1-=b diff --git a/src/tests/config/conf.d/90-last.conf b/src/tests/config/conf.d/90-last.conf index dc1de394f1..c75dcc4710 100644 --- a/src/tests/config/conf.d/90-last.conf +++ b/src/tests/config/conf.d/90-last.conf @@ -3,3 +3,6 @@ plugins+=one,two [order] a=90 + +[append] +val1+=c,a diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c index f12752847a..13a2aca9e1 100644 --- a/src/tests/config/test-config.c +++ b/src/tests/config/test-config.c @@ -341,6 +341,26 @@ test_config_confdir (void) ASSERT_GET_CONN_DEFAULT (config, "ord.key09", "C-2.1.09"); ASSERT_GET_CONN_DEFAULT (config, "ord.ovw01", "C-0.1.ovw01"); + value = nm_config_data_get_value (nm_config_get_data_orig (config), "append", "val1", NULL); + g_assert_cmpstr (value, ==, "a,c"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "append", "val2", NULL); + g_assert_cmpstr (value, ==, "VAL2"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "append", "val3", NULL); + g_assert_cmpstr (value, ==, NULL); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "append", "val4", NULL); + g_assert_cmpstr (value, ==, "vb,vb"); + g_free (value); + + value = nm_config_data_get_value (nm_config_get_data_orig (config), "append", "val5", NULL); + g_assert_cmpstr (value, ==, "VAL5"); + g_free (value); + g_object_unref (config); }