summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/nm-glib-compat.h37
-rw-r--r--include/nm-macros-internal.h9
-rw-r--r--include/nm-test-utils.h8
-rw-r--r--libnm-core/nm-core-internal.h11
-rw-r--r--libnm-core/nm-keyfile-internal.h5
-rw-r--r--libnm-core/nm-keyfile-reader.c2
-rw-r--r--libnm-core/nm-keyfile-utils.c135
-rw-r--r--libnm-core/nm-setting-8021x.c12
-rw-r--r--libnm-core/nm-setting-connection.c4
-rw-r--r--libnm-core/nm-setting-wireless-security.c12
-rw-r--r--libnm-core/nm-setting-wireless.c4
-rw-r--r--libnm-core/nm-utils-private.h6
-rw-r--r--libnm-core/nm-utils.c52
-rw-r--r--libnm-core/tests/test-keyfile.c37
-rw-r--r--man/NetworkManager.conf.xml.in11
-rw-r--r--src/NetworkManagerUtils.c135
-rw-r--r--src/NetworkManagerUtils.h1
-rw-r--r--src/devices/nm-device.c8
-rw-r--r--src/main.c5
-rw-r--r--src/nm-config-data.c359
-rw-r--r--src/nm-config-data.h29
-rw-r--r--src/nm-config.c492
-rw-r--r--src/nm-config.h41
-rw-r--r--src/nm-connectivity.c10
-rw-r--r--src/nm-manager.c2
-rw-r--r--src/settings/nm-settings.c8
-rw-r--r--src/settings/plugins/ifnet/net_parser.h1
-rw-r--r--src/settings/plugins/ifnet/plugin.c27
-rw-r--r--src/settings/plugins/ifupdown/plugin.c27
-rw-r--r--src/settings/plugins/keyfile/plugin.c6
-rw-r--r--src/tests/config/NetworkManager.conf44
-rw-r--r--src/tests/config/conf.d/00-overrides.conf46
-rw-r--r--src/tests/config/conf.d/10-more.conf29
-rw-r--r--src/tests/config/conf.d/90-last.conf3
-rw-r--r--src/tests/config/test-config.c88
-rw-r--r--src/tests/test-general.c24
36 files changed, 1333 insertions, 397 deletions
diff --git a/include/nm-glib-compat.h b/include/nm-glib-compat.h
index 741d610319..dc39d2b934 100644
--- a/include/nm-glib-compat.h
+++ b/include/nm-glib-compat.h
@@ -213,4 +213,41 @@ _nm_g_ptr_array_insert (GPtrArray *array,
#endif
+#if !GLIB_CHECK_VERSION (2, 40, 0)
+inline static gboolean
+_g_key_file_save_to_file (GKeyFile *key_file,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *contents;
+ gboolean success;
+ gsize length;
+
+ g_return_val_if_fail (key_file != NULL, FALSE);
+ g_return_val_if_fail (filename != NULL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ contents = g_key_file_to_data (key_file, &length, NULL);
+ g_assert (contents != NULL);
+
+ success = g_file_set_contents (filename, contents, length, error);
+ g_free (contents);
+
+ return success;
+}
+#define g_key_file_save_to_file(key_file, filename, error) \
+ _g_key_file_save_to_file (key_file, filename, error)
+#else
+#define g_key_file_save_to_file(key_file, filename, error) \
+ ({ \
+ gboolean _success; \
+ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ _success = g_key_file_save_to_file (key_file, filename, error); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ _success; \
+ })
+#endif
+
+
#endif /* __NM_GLIB_COMPAT_H__ */
diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h
index 3767a13397..405051f382 100644
--- a/include/nm-macros-internal.h
+++ b/include/nm-macros-internal.h
@@ -236,4 +236,13 @@ nm_clear_g_source (guint *id)
/*****************************************************************************/
+static inline char *
+nm_strstrip (char *str)
+{
+ /* g_strstrip doesn't like NULL. */
+ return str ? g_strstrip (str) : NULL;
+}
+
+/*****************************************************************************/
+
#endif /* __NM_MACROS_INTERNAL_H__ */
diff --git a/include/nm-test-utils.h b/include/nm-test-utils.h
index 0cde9a4b91..4610ee62a6 100644
--- a/include/nm-test-utils.h
+++ b/include/nm-test-utils.h
@@ -123,6 +123,14 @@ nmtst_assert_error (GError *error,
}
}
+inline static void
+_nmtst_assert_success (gboolean success, GError *error, const char *file, int line)
+{
+ if (!success || error)
+ g_error ("(%s:%d) FAILURE success=%d, error=%s", file, line, success, error && error->message ? error->message : "(no error)");
+}
+#define nmtst_assert_success(success, error) _nmtst_assert_success ((success), (error), __FILE__, __LINE__)
+
/*******************************************************************************/
struct __nmtst_internal
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index ab147b9f7a..dd5151e088 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -119,10 +119,21 @@ gboolean _nm_utils_string_in_list (const char *str,
gssize _nm_utils_strv_find_first (char **list, gssize len, const char *needle);
+char **_nm_utils_strv_cleanup (char **strv,
+ gboolean strip_whitespace,
+ gboolean skip_empty,
+ gboolean skip_repeated);
+
char ** _nm_utils_strsplit_set (const char *str,
const char *delimiters,
int max_tokens);
+GSList * _nm_utils_strv_to_slist (char **strv, gboolean deep_copy);
+char ** _nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy);
+
+GPtrArray * _nm_utils_strv_to_ptrarray (char **strv);
+char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray);
+
#define NM_UTILS_UUID_TYPE_LEGACY 0
#define NM_UTILS_UUID_TYPE_VARIANT3 1
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index 90af562cdf..f4bb079637 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -162,5 +162,10 @@ GKeyFile *nm_keyfile_write (NMConnection *connection,
char *nm_keyfile_plugin_kf_get_string (GKeyFile *kf, const char *group, const char *key, GError **error);
void nm_keyfile_plugin_kf_set_string (GKeyFile *kf, const char *group, const char *key, const char *value);
+void _nm_keyfile_copy (GKeyFile *dst, GKeyFile *src);
+gboolean _nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b);
+gboolean _nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order);
+gboolean _nm_keyfile_has_values (GKeyFile *keyfile);
+
#endif /* __NM_KEYFILE_INTERNAL_H__ */
diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c
index df568a8ebd..353804f97c 100644
--- a/libnm-core/nm-keyfile-reader.c
+++ b/libnm-core/nm-keyfile-reader.c
@@ -1577,6 +1577,8 @@ nm_keyfile_read (GKeyFile *keyfile,
info.user_data = user_data;
groups = g_key_file_get_groups (keyfile, &length);
+ if (!groups)
+ length = 0;
for (i = 0; i < length; i++) {
/* Only read out secrets when needed */
if (!strcmp (groups[i], VPN_SECRETS_GROUP)) {
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
index 61b30ab9c8..d04a3d1bef 100644
--- a/libnm-core/nm-keyfile-utils.c
+++ b/libnm-core/nm-keyfile-utils.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
+#include "gsystem-local-alloc.h"
#include "nm-keyfile-utils.h"
#include "nm-keyfile-internal.h"
#include "nm-setting-wired.h"
@@ -204,4 +205,138 @@ nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
return has;
}
+/************************************************************************/
+void
+_nm_keyfile_copy (GKeyFile *dst, GKeyFile *src)
+{
+ gs_strfreev char **groups = NULL;
+ guint g, k;
+
+ groups = g_key_file_get_groups (src, NULL);
+ for (g = 0; groups && groups[g]; g++) {
+ const char *group = groups[g];
+ gs_strfreev char **keys = NULL;
+
+ keys = g_key_file_get_keys (src, group, NULL, NULL);
+ if (!keys)
+ continue;
+
+ for (k = 0; keys[k]; k++) {
+ const char *key = keys[k];
+ gs_free char *value = NULL;
+
+ value = g_key_file_get_value (src, group, key, NULL);
+ if (value)
+ g_key_file_set_value (dst, group, key, value);
+ else
+ g_key_file_remove_key (dst, group, key, NULL);
+ }
+ }
+}
+
+/************************************************************************/
+
+gboolean
+_nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b)
+{
+ gs_strfreev char **groups = NULL;
+ guint i, j;
+
+ if (kf_a == kf_b)
+ return TRUE;
+ if (!kf_a || !kf_b)
+ return FALSE;
+
+ groups = g_key_file_get_groups (kf_a, NULL);
+ for (i = 0; groups && groups[i]; i++) {
+ gs_strfreev char **keys = NULL;
+
+ keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL);
+ if (!keys)
+ continue;
+
+ for (j = 0; keys[j]; j++) {
+ gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL);
+ gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL);
+
+ if (g_strcmp0 (key_a, key_b) != 0)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+static gboolean
+_nm_keyfile_equals_ordered (GKeyFile *kf_a, GKeyFile *kf_b)
+{
+ gs_strfreev char **groups = NULL;
+ gs_strfreev char **groups_b = NULL;
+ guint i, j;
+
+ if (kf_a == kf_b)
+ return TRUE;
+ if (!kf_a || !kf_b)
+ return FALSE;
+
+ groups = g_key_file_get_groups (kf_a, NULL);
+ groups_b = g_key_file_get_groups (kf_b, NULL);
+ if (!groups && !groups_b)
+ return TRUE;
+ if (!groups || !groups_b)
+ return FALSE;
+ for (i = 0; groups[i] && groups_b[i] && !strcmp (groups[i], groups_b[i]); i++)
+ ;
+ if (groups[i] || groups_b[i])
+ return FALSE;
+
+ for (i = 0; groups[i]; i++) {
+ gs_strfreev char **keys = NULL;
+ gs_strfreev char **keys_b = NULL;
+
+ keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL);
+ keys_b = g_key_file_get_keys (kf_b, groups[i], NULL, NULL);
+
+ if ((!keys) != (!keys_b))
+ return FALSE;
+ if (!keys)
+ continue;
+
+ for (j = 0; keys[j] && keys_b[j] && !strcmp (keys[j], keys_b[j]); j++)
+ ;
+ if (keys[j] || keys_b[j])
+ return FALSE;
+
+ for (j = 0; keys[j]; j++) {
+ gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL);
+ gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL);
+
+ if (g_strcmp0 (key_a, key_b) != 0)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+gboolean
+_nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order)
+{
+ if (!consider_order) {
+ return _nm_keyfile_a_contains_all_in_b (kf_a, kf_b)
+ && _nm_keyfile_a_contains_all_in_b (kf_b, kf_a);
+ } else {
+ return _nm_keyfile_equals_ordered (kf_a, kf_b);
+ }
+}
+
+gboolean
+_nm_keyfile_has_values (GKeyFile *keyfile)
+{
+ gs_strfreev char **groups = NULL;
+
+ g_return_val_if_fail (keyfile, FALSE);
+
+ groups = g_key_file_get_groups (keyfile, NULL);
+ return groups && groups[0];
+}
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index 02f9c9039d..e50ad4eae5 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -2888,7 +2888,7 @@ set_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_EAP:
g_slist_free_full (priv->eap, g_free);
- priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->eap = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_IDENTITY:
g_free (priv->identity);
@@ -2922,7 +2922,7 @@ set_property (GObject *object, guint prop_id,
break;
case PROP_ALTSUBJECT_MATCHES:
g_slist_free_full (priv->altsubject_matches, g_free);
- priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_CLIENT_CERT:
if (priv->client_cert)
@@ -2974,7 +2974,7 @@ set_property (GObject *object, guint prop_id,
break;
case PROP_PHASE2_ALTSUBJECT_MATCHES:
g_slist_free_full (priv->phase2_altsubject_matches, g_free);
- priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->phase2_altsubject_matches = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_PHASE2_CLIENT_CERT:
if (priv->phase2_client_cert)
@@ -3060,7 +3060,7 @@ get_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_EAP:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->eap, TRUE));
break;
case PROP_IDENTITY:
g_value_set_string (value, priv->identity);
@@ -3081,7 +3081,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, priv->subject_match);
break;
case PROP_ALTSUBJECT_MATCHES:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->altsubject_matches, TRUE));
break;
case PROP_CLIENT_CERT:
g_value_set_boxed (value, priv->client_cert);
@@ -3111,7 +3111,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, priv->phase2_subject_match);
break;
case PROP_PHASE2_ALTSUBJECT_MATCHES:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->phase2_altsubject_matches, TRUE));
break;
case PROP_PHASE2_CLIENT_CERT:
g_value_set_boxed (value, priv->phase2_client_cert);
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index 6f66e674a0..07b3401d1e 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -1149,7 +1149,7 @@ set_property (GObject *object, guint prop_id,
break;
case PROP_SECONDARIES:
g_slist_free_full (priv->secondaries, g_free);
- priv->secondaries = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->secondaries = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_GATEWAY_PING_TIMEOUT:
priv->gateway_ping_timeout = g_value_get_uint (value);
@@ -1222,7 +1222,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_enum (value, nm_setting_connection_get_autoconnect_slaves (setting));
break;
case PROP_SECONDARIES:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->secondaries));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->secondaries, TRUE));
break;
case PROP_GATEWAY_PING_TIMEOUT:
g_value_set_uint (value, priv->gateway_ping_timeout);
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
index a56a8e9757..6df5b46176 100644
--- a/libnm-core/nm-setting-wireless-security.c
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -1132,15 +1132,15 @@ set_property (GObject *object, guint prop_id,
break;
case PROP_PROTO:
g_slist_free_full (priv->proto, g_free);
- priv->proto = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->proto = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_PAIRWISE:
g_slist_free_full (priv->pairwise, g_free);
- priv->pairwise = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->pairwise = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_GROUP:
g_slist_free_full (priv->group, g_free);
- priv->group = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->group = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_LEAP_USERNAME:
g_free (priv->leap_username);
@@ -1206,13 +1206,13 @@ get_property (GObject *object, guint prop_id,
g_value_set_string (value, priv->auth_alg);
break;
case PROP_PROTO:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->proto));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->proto, TRUE));
break;
case PROP_PAIRWISE:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->pairwise));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->pairwise, TRUE));
break;
case PROP_GROUP:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->group));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->group, TRUE));
break;
case PROP_LEAP_USERNAME:
g_value_set_string (value, priv->leap_username);
diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c
index f8d22c2d00..ce3cf9a7e9 100644
--- a/libnm-core/nm-setting-wireless.c
+++ b/libnm-core/nm-setting-wireless.c
@@ -911,7 +911,7 @@ set_property (GObject *object, guint prop_id,
break;
case PROP_SEEN_BSSIDS:
g_slist_free_full (priv->seen_bssids, g_free);
- priv->seen_bssids = _nm_utils_strv_to_slist (g_value_get_boxed (value));
+ priv->seen_bssids = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
break;
case PROP_HIDDEN:
priv->hidden = g_value_get_boolean (value);
@@ -964,7 +964,7 @@ get_property (GObject *object, guint prop_id,
g_value_set_uint (value, nm_setting_wireless_get_mtu (setting));
break;
case PROP_SEEN_BSSIDS:
- g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->seen_bssids));
+ g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->seen_bssids, TRUE));
break;
case PROP_HIDDEN:
g_value_set_boolean (value, nm_setting_wireless_get_hidden (setting));
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index 3854d67951..ffdc631566 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -40,12 +40,6 @@ GVariant * _nm_utils_bytes_to_dbus (const GValue *prop_value);
void _nm_utils_bytes_from_dbus (GVariant *dbus_value,
GValue *prop_value);
-GSList * _nm_utils_strv_to_slist (char **strv);
-char ** _nm_utils_slist_to_strv (GSList *slist);
-
-GPtrArray * _nm_utils_strv_to_ptrarray (char **strv);
-char ** _nm_utils_ptrarray_to_strv (GPtrArray *ptrarray);
-
char * _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize length);
#endif
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 16561175da..14686ed1fd 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -483,6 +483,36 @@ _nm_utils_strv_find_first (char **list, gssize len, const char *needle)
return -1;
}
+char **
+_nm_utils_strv_cleanup (char **strv,
+ gboolean strip_whitespace,
+ gboolean skip_empty,
+ gboolean skip_repeated)
+{
+ guint i, j;
+
+ if (!strv || !*strv)
+ return strv;
+
+ if (strip_whitespace) {
+ for (i = 0; strv[i]; i++)
+ g_strstrip (strv[i]);
+ }
+ if (!skip_empty && !skip_repeated)
+ return strv;
+ j = 0;
+ for (i = 0; strv[i]; i++) {
+ if ( (skip_empty && !*strv[i])
+ || (skip_repeated && _nm_utils_strv_find_first (strv, j, strv[i]) >= 0))
+ g_free (strv[i]);
+ else
+ strv[j++] = strv[i];
+ }
+ strv[j] = NULL;
+ return strv;
+}
+
+
gboolean
_nm_utils_string_slist_validate (GSList *list, const char **valid_values)
{
@@ -725,21 +755,26 @@ _nm_utils_bytes_from_dbus (GVariant *dbus_value,
}
GSList *
-_nm_utils_strv_to_slist (char **strv)
+_nm_utils_strv_to_slist (char **strv, gboolean deep_copy)
{
int i;
GSList *list = NULL;
if (strv) {
- for (i = 0; strv[i]; i++)
- list = g_slist_prepend (list, g_strdup (strv[i]));
+ if (deep_copy) {
+ for (i = 0; strv[i]; i++)
+ list = g_slist_prepend (list, g_strdup (strv[i]));
+ } else {
+ for (i = 0; strv[i]; i++)
+ list = g_slist_prepend (list, strv[i]);
+ }
}
return g_slist_reverse (list);
}
char **
-_nm_utils_slist_to_strv (GSList *slist)
+_nm_utils_slist_to_strv (GSList *slist, gboolean deep_copy)
{
GSList *iter;
char **strv;
@@ -748,8 +783,13 @@ _nm_utils_slist_to_strv (GSList *slist)
len = g_slist_length (slist);
strv = g_new (char *, len + 1);
- for (i = 0, iter = slist; iter; iter = iter->next, i++)
- strv[i] = g_strdup (iter->data);
+ if (deep_copy) {
+ for (i = 0, iter = slist; iter; iter = iter->next, i++)
+ strv[i] = g_strdup (iter->data);
+ } else {
+ for (i = 0, iter = slist; iter; iter = iter->next, i++)
+ strv[i] = iter->data;
+ }
strv[i] = NULL;
return strv;
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 28d659f985..99f88ac543 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -81,39 +81,6 @@ _keyfile_load_from_data (const char *str)
return keyfile;
}
-static gboolean
-_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b)
-{
- gs_strfreev char **groups = NULL;
- guint i, j;
-
- if (kf_a == kf_b)
- return TRUE;
-
- groups = g_key_file_get_groups (kf_a, NULL);
- for (i = 0; groups && groups[i]; i++) {
- gs_strfreev char **keys = NULL;
-
- keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL);
- if (keys) {
- for (j = 0; keys[j]; j++) {
- gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL);
- gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL);
-
- if (g_strcmp0 (key_a, key_b) != 0)
- return FALSE;
- }
- }
- }
- return TRUE;
-}
-
-static gboolean
-_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b)
-{
- return _keyfile_a_contains_all_in_b (kf_a, kf_b) && _keyfile_a_contains_all_in_b (kf_b, kf_a);
-}
-
static GKeyFile *
_nm_keyfile_write (NMConnection *connection,
NMKeyfileWriteHandler handler,
@@ -185,7 +152,7 @@ _keyfile_convert (NMConnection **con,
c0_k1_c2 = _nm_keyfile_read (c0_k1, keyfile_name, base_dir, read_handler, read_data, FALSE);
c0_k1_c2_k3 = _nm_keyfile_write (c0_k1_c2, write_handler, write_data);
- _keyfile_equals (c0_k1, c0_k1_c2_k3);
+ g_assert (_nm_keyfile_equals (c0_k1, c0_k1_c2_k3, TRUE));
}
if (k0) {
NMSetting8021x *s1, *s2;
@@ -247,7 +214,7 @@ _keyfile_convert (NMConnection **con,
else {
/* finally, if both a keyfile and a connection are given, assert that they are equal
* after a round of conversion. */
- _keyfile_equals (c0_k1, k0_c1_k2);
+ g_assert (_nm_keyfile_equals (c0_k1, k0_c1_k2, TRUE));
nmtst_assert_connection_equals (k0_c1, FALSE, c0_k1_c2, FALSE);
}
}
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index bd187ff57c..40fa49dd7a 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -62,7 +62,8 @@ Copyright 2010 - 2014 Red Hat, Inc.
<para>
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 <xref linkend="device-spec"/>
+ below.
</para>
<para>
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:
<programlisting>
plugins+=another-plugin
+ plugins-=remove-me
</programlisting>
</para>
</refsect1>
@@ -465,7 +467,7 @@ ipv6.ip6-privacy=1
</para>
<para>
- The sections are considered in order of appearance, with the
+ The sections within one file are considered in order of appearance, with the
exception that the <literal>[connection]</literal> section is always
considered last. In the example above, this order is <literal>[connection-wifi-wlan0]</literal>,
<literal>[connection-wlan-other]</literal>, and <literal>[connection]</literal>.
@@ -478,6 +480,11 @@ ipv6.ip6-privacy=1
"[connection-wifi-wlan0]" matches the device, it does not contain that property
and the search continues.
</para>
+ <para>
+ When having different sections in multiple files, sections from files that are read
+ later have higher priority. So within one file the priority of the sections is
+ top-to-bottom. Across multiple files later definitions take precedence.
+ </para>
<para>
<variablelist>
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 6cd937da99..26ec1b92d7 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -1310,11 +1310,32 @@ nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
return match;
}
+/**
+ * nm_match_spec_split:
+ * @value: the string of device specs
+ *
+ * Splits the specs from the string and returns them as individual
+ * entires in a #GSList.
+ *
+ * It does not validate any specs, it basically just does a special
+ * strsplit with ',' or ';' as separators and supporting '\\' as
+ * escape character.
+ *
+ * Leading and trailing spaces of each entry are removed. But the user
+ * can preserve them by specifying "\\s has 2 leading" or "has 2 trailing \\s".
+ *
+ * Specs can have a qualifier like "interface-name:". We still don't strip
+ * any whitespace after the colon, so "interface-name: X" matches an interface
+ * named " X".
+ *
+ * Returns: (transfer-full): the list of device specs.
+ */
GSList *
nm_match_spec_split (const char *value)
{
char *string_value, *p, *q0, *q;
GSList *pieces = NULL;
+ int trailing_ws;
if (!value || !*value)
return NULL;
@@ -1325,7 +1346,13 @@ nm_match_spec_split (const char *value)
string_value = g_new (gchar, strlen (value) + 1);
p = (gchar *) value;
+
+ /* skip over leading whitespace */
+ while (g_ascii_isspace (*p))
+ p++;
+
q0 = q = string_value;
+ trailing_ws = 0;
while (*p) {
if (*p == '\\') {
p++;
@@ -1357,27 +1384,117 @@ nm_match_spec_split (const char *value)
}
break;
}
+ if (*p == '\0')
+ break;
+ p++;
+ trailing_ws = 0;
} else {
*q = *p;
- if (NM_IN_SET (*p, ',', ';')) {
- if (q0 < q)
- pieces = g_slist_prepend (pieces, g_strndup (q0, q - q0));
+ if (*p == '\0')
+ break;
+ if (g_ascii_isspace (*p)) {
+ trailing_ws++;
+ p++;
+ } else if (NM_IN_SET (*p, ',', ';')) {
+ if (q0 < q - trailing_ws)
+ pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
q0 = q + 1;
- }
+ p++;
+ trailing_ws = 0;
+ while (g_ascii_isspace (*p))
+ p++;
+ } else
+ p++;
}
- if (*p == '\0')
- break;
q++;
- p++;
}
*q = '\0';
- if (q0 < q)
- pieces = g_slist_prepend (pieces, g_strndup (q0, q - q0));
+ if (q0 < q - trailing_ws)
+ pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
g_free (string_value);
return g_slist_reverse (pieces);
}
+/**
+ * nm_match_spec_join:
+ * @specs: the device specs to join
+ *
+ * This is based on g_key_file_parse_string_as_value(), analog to
+ * nm_match_spec_split() which is based on g_key_file_parse_value_as_string().
+ *
+ * Returns: (transfer-full): a joined list of device specs that can be
+ * split again with nm_match_spec_split(). Note that
+ * nm_match_spec_split (nm_match_spec_join (specs)) yields the original
+ * result (which is not true the other way around because there are multiple
+ * ways to encode the same joined specs string).
+ */
+char *
+nm_match_spec_join (GSList *specs)
+{
+ const char *p;
+ GString *str;
+
+ str = g_string_new ("");
+
+ for (; specs; specs = specs->next) {
+ p = specs->data;
+
+ if (!p || !*p)
+ continue;
+
+ if (str->len > 0)
+ g_string_append_c (str, ',');
+
+ /* escape leading whitespace */
+ switch (*p) {
+ case ' ':
+ g_string_append (str, "\\s");
+ p++;
+ break;
+ case '\t':
+ g_string_append (str, "\\t");
+ p++;
+ break;
+ }
+
+ for (; *p; p++) {
+ switch (*p) {
+ case '\n':
+ g_string_append (str, "\\n");
+ break;
+ case '\r':
+ g_string_append (str, "\\r");
+ break;
+ case '\\':
+ g_string_append (str, "\\\\");
+ break;
+ case ',':
+ g_string_append (str, "\\,");
+ break;
+ case ';':
+ g_string_append (str, "\\;");
+ break;
+ default:
+ g_string_append_c (str, *p);
+ break;
+ }
+ }
+
+ /* escape trailing whitespaces */
+ switch (str->str[str->len - 1]) {
+ case ' ':
+ g_string_overwrite (str, str->len - 1, "\\s");
+ break;
+ case '\t':
+ g_string_overwrite (str, str->len - 1, "\\t");
+ break;
+ }
+ }
+
+ return g_string_free (str, FALSE);
+}
+
const char *
nm_utils_get_shared_wifi_permission (NMConnection *connection)
{
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index f8e759142e..1864547f56 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -98,6 +98,7 @@ NMMatchSpecMatchType nm_match_spec_hwaddr (const GSList *specs, const char *hwad
NMMatchSpecMatchType nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
NMMatchSpecMatchType nm_match_spec_interface_name (const GSList *specs, const char *interface_name);
GSList *nm_match_spec_split (const char *value);
+char *nm_match_spec_join (GSList *specs);
const char *nm_utils_get_shared_wifi_permission (NMConnection *connection);
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 40802c6e72..f34ed7ef1f 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -784,7 +784,7 @@ _get_ipx_route_metric (NMDevice *self,
/* use the current NMConfigData, which makes this configuration reloadable.
* Note that that means that the route-metric might change between SIGHUP.
* You must cache the returned value if that is a problem. */
- value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
+ value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
is_v4 ? "ipv4.route-metric" : "ipv6.route-metric", self);
if (value) {
route_metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
@@ -2301,7 +2301,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
&& g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
&& !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
&& !priv->slaves
- && !nm_config_data_get_assume_ipv6ll_only (nm_config_get_data (nm_config_get ()), self)) {
+ && !nm_config_data_get_assume_ipv6ll_only (NM_CONFIG_GET_DATA, self)) {
_LOGD (LOGD_DEVICE, "ignoring generated connection (IPv6LL-only and not in master-slave relationship)");
g_object_unref (connection);
connection = NULL;
@@ -4859,7 +4859,7 @@ _ip6_privacy_get (NMDevice *self)
}
}
- value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
+ value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
"ipv6.ip6-privacy", self);
/* 2.) use the default value from the configuration. */
@@ -8359,7 +8359,7 @@ _set_state_full (NMDevice *self,
if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) {
/* We cache the ignore_carrier state to not react on config-reloads while the connection
* is active. But on deactivating, reset the ignore-carrier flag to the current state. */
- priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (nm_config_get ()), self);
+ priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self);
}
if (quitting) {
diff --git a/src/main.c b/src/main.c
index 8a44db31d3..dfc307b5db 100644
--- a/src/main.c
+++ b/src/main.c
@@ -240,8 +240,8 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
N_("Log domains separated by ',': any combination of [%s]"),
"PLATFORM,RFKILL,WIFI" },
{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
- { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_("filename") },
- { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_("/path/to/state.file") },
+ { "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_(NM_DEFAULT_PID_FILE) },
+ { "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_(NM_DEFAULT_SYSTEM_STATE_FILE) },
{ "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL },
{NULL}
};
@@ -423,6 +423,7 @@ main (int argc, char *argv[])
dbus_glib_global_set_disable_legacy_property_access ();
nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
+ nm_config_data_log (nm_config_get_data (config), "CONFIG: ");
nm_log_dbg (LOGD_CORE, "WEXT support is %s",
#if HAVE_WEXT
"enabled"
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index a4effbe0ce..dc20064b19 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -27,7 +27,9 @@
#include "nm-device.h"
#include "gsystem-local-alloc.h"
#include "nm-core-internal.h"
+#include "nm-keyfile-internal.h"
#include "nm-macros-internal.h"
+#include "nm-logging.h"
typedef struct {
char *group_name;
@@ -60,6 +62,7 @@ typedef struct {
struct {
char **arr;
GSList *specs;
+ GSList *specs_config;
} no_auto_default;
GSList *ignore_carrier;
@@ -104,12 +107,55 @@ nm_config_data_get_config_description (const NMConfigData *self)
return NM_CONFIG_DATA_GET_PRIVATE (self)->config_description;
}
+gboolean
+nm_config_data_has_group (const NMConfigData *self, const char *group)
+{
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
+ g_return_val_if_fail (group && *group, FALSE);
+
+ return g_key_file_has_group (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group);
+}
+
char *
-nm_config_data_get_value (const NMConfigData *self, const char *group, const char *key, GError **error)
+nm_config_data_get_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags)
{
- g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL);
+ g_return_val_if_fail (group && *group, NULL);
+ g_return_val_if_fail (key && *key, NULL);
+
+ return nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags);
+}
+
+gboolean
+nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags)
+{
+ gs_free char *value = NULL;
- return g_key_file_get_string (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, error);
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
+ g_return_val_if_fail (group && *group, FALSE);
+ g_return_val_if_fail (key && *key, FALSE);
+
+ value = nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags);
+ return !!value;
+}
+
+gint
+nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, const char *key, gint default_value)
+{
+ char *str;
+ gint value = default_value;
+
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), default_value);
+ g_return_val_if_fail (group && *group, default_value);
+ g_return_val_if_fail (key && *key, default_value);
+
+ /* when parsing the boolean, base it on the raw value from g_key_file_get_value(). */
+ str = g_key_file_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, NULL);
+ if (str) {
+ value = nm_config_parse_boolean (str, default_value);
+ g_free (str);
+ }
+ return value;
}
const char *
@@ -144,12 +190,17 @@ nm_config_data_get_no_auto_default (const NMConfigData *self)
return (const char *const*) NM_CONFIG_DATA_GET_PRIVATE (self)->no_auto_default.arr;
}
-const GSList *
-nm_config_data_get_no_auto_default_list (const NMConfigData *self)
+gboolean
+nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device)
{
- g_return_val_if_fail (self, NULL);
+ NMConfigDataPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE);
+ g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- return NM_CONFIG_DATA_GET_PRIVATE (self)->no_auto_default.specs;
+ priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+ return nm_device_spec_match_list (device, priv->no_auto_default.specs)
+ || nm_device_spec_match_list (device, priv->no_auto_default.specs_config);
}
const char *
@@ -180,6 +231,121 @@ nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *devic
/************************************************************************/
+/**
+ * nm_config_data_get_groups:
+ * @self: the #NMConfigData instance
+ *
+ * Returns: (transfer-full): the list of groups in the configuration. The order
+ * of the section is undefined, as the configuration gets merged from multiple
+ * sources.
+ */
+char **
+nm_config_data_get_groups (const NMConfigData *self)
+{
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL);
+
+ return g_key_file_get_groups (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, NULL);
+}
+
+char **
+nm_config_data_get_keys (const NMConfigData *self, const char *group)
+{
+ g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL);
+ g_return_val_if_fail (group && *group, NULL);
+
+ return g_key_file_get_keys (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, NULL, NULL);
+}
+
+/************************************************************************/
+
+static int
+_nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy)
+{
+ gboolean a_is_connection, b_is_connection;
+ const char *a = *pa;
+ const char *b = *pb;
+
+ /* we sort connection groups before intern groups (to the end). */
+ a_is_connection = a && g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
+ b_is_connection = b && g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
+
+ if (a_is_connection && b_is_connection) {
+ /* if both are connection groups, we want the explicit [connection] group first. */
+ a_is_connection = a[STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0';
+ b_is_connection = b[STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0';
+
+ if (a_is_connection != b_is_connection) {
+ if (a_is_connection)
+ return -1;
+ return 1;
+ }
+ /* the sections are ordered lowest-priority first. Reverse their order. */
+ return pa < pb ? 1 : -1;
+ }
+ if (a_is_connection && !b_is_connection)
+ return 1;
+ if (b_is_connection && !a_is_connection)
+ return -1;
+
+ /* no reordering. */
+ return 0;
+}
+
+void
+nm_config_data_log (const NMConfigData *self, const char *prefix)
+{
+ NMConfigDataPrivate *priv;
+ gs_strfreev char **groups = NULL;
+ gsize ngroups;
+ guint g, k;
+
+ g_return_if_fail (NM_IS_CONFIG_DATA (self));
+
+ if (!nm_logging_enabled (LOGL_DEBUG, LOGD_CORE))
+ return;
+
+ if (!prefix)
+ prefix = "";
+
+#define _LOG(...) _nm_log (LOGL_DEBUG, LOGD_CORE, 0, "%s"_NM_UTILS_MACRO_FIRST(__VA_ARGS__), prefix _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+ priv = NM_CONFIG_DATA_GET_PRIVATE (self);
+
+ groups = g_key_file_get_groups (priv->keyfile, &ngroups);
+ if (!groups)
+ ngroups = 0;
+
+ if (groups && groups[0]) {
+ g_qsort_with_data (groups, ngroups,
+ sizeof (char *),
+ (GCompareDataFunc) _nm_config_data_log_sort,
+ NULL);
+ }
+
+ _LOG ("config-data[%p]: %lu groups", self, (unsigned long) ngroups);
+
+ for (g = 0; g < ngroups; g++) {
+ const char *group = groups[g];
+ gs_strfreev char **keys = NULL;
+
+ _LOG ("");
+ _LOG ("[%s]", group);
+
+ keys = g_key_file_get_keys (priv->keyfile, group, NULL, NULL);
+ for (k = 0; keys && keys[k]; k++) {
+ const char *key = keys[k];
+ gs_free char *value = NULL;
+
+ value = g_key_file_get_value (priv->keyfile, group, key, NULL);
+ _LOG (" %s=%s", key, value);
+ }
+ }
+
+#undef _LOG
+}
+
+/************************************************************************/
+
char *
nm_config_data_get_connection_default (const NMConfigData *self,
const char *property,
@@ -201,7 +367,14 @@ nm_config_data_get_connection_default (const NMConfigData *self,
char *value;
gboolean match;
- value = g_key_file_get_value (priv->keyfile, connection_info->group_name, property, NULL);
+ /* FIXME: Here we use g_key_file_get_string(). This should be in sync with what keyfile-reader
+ * does.
+ *
+ * Unfortunately that is currently not possible because keyfile-reader does the two steps
+ * string_to_value(keyfile_to_string(keyfile)) in one. Optimally, keyfile library would
+ * expose both functions, and we would return here keyfile_to_string(keyfile).
+ * The caller then could convert the string to the proper value via string_to_value(value). */
+ value = g_key_file_get_string (priv->keyfile, connection_info->group_name, property, NULL);
if (!value && !connection_info->stop_match)
continue;
@@ -216,58 +389,59 @@ nm_config_data_get_connection_default (const NMConfigData *self,
return NULL;
}
+static void
+_get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, char *group)
+{
+ /* pass ownership of @group on... */
+ connection_info->group_name = group;
+
+ connection_info->match_device.spec = nm_config_get_device_match_spec (keyfile,
+ group,
+ "match-device",
+ &connection_info->match_device.has);
+ connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, group, "stop-match", FALSE);
+}
+
static ConnectionInfo *
_get_connection_infos (GKeyFile *keyfile)
{
char **groups;
- guint i;
+ gsize i, j, ngroups;
char *connection_tag = NULL;
- GSList *connection_groups = NULL;
ConnectionInfo *connection_infos = NULL;
/* get the list of existing [connection.\+] sections that we consider
- * for nm_config_data_get_connection_default(). Also, get them
- * in the right order. */
- groups = g_key_file_get_groups (keyfile, NULL);
- for (i = 0; groups && groups[i]; i++) {
- if (g_str_has_prefix (groups[i], "connection")) {
- if (strlen (groups[i]) == STRLEN ("connection"))
- connection_tag = groups[i];
- else
- connection_groups = g_slist_prepend (connection_groups, groups[i]);
- } else
- g_free (groups[i]);
+ * for nm_config_data_get_connection_default().
+ *
+ * We expect the sections in their right order, with lowest priority
+ * first. Only exception is the (literal) [connection] section, which
+ * we will always reorder to the end. */
+ groups = g_key_file_get_groups (keyfile, &ngroups);
+ if (!groups)
+ ngroups = 0;
+ else if (ngroups > 0) {
+ for (i = 0, j = 0; i < ngroups; i++) {
+ if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)) {
+ if (groups[i][STRLEN (NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION)] == '\0')
+ connection_tag = groups[i];
+ else
+ groups[j++] = groups[i];
+ } else
+ g_free (groups[i]);
+ }
+ ngroups = j;
}
- g_free (groups);
- if (connection_tag) {
- /* We want the group "connection" checked at last, so that
- * all other "connection.\+" have preference. Those other
- * groups are checked in order of appearance. */
- connection_groups = g_slist_prepend (connection_groups, connection_tag);
+
+ connection_infos = g_new0 (ConnectionInfo, ngroups + 1 + (connection_tag ? 1 : 0));
+ for (i = 0; i < ngroups; i++) {
+ /* pass ownership of @group on... */
+ _get_connection_info_init (&connection_infos[i], keyfile, groups[ngroups - i - 1]);
}
- if (connection_groups) {
- guint len = g_slist_length (connection_groups);
- GSList *iter;
-
- connection_infos = g_new0 (ConnectionInfo, len + 1);
- for (iter = connection_groups; iter; iter = iter->next) {
- ConnectionInfo *connection_info;
- char *value;
-
- nm_assert (len >= 1);
- connection_info = &connection_infos[--len];
- connection_info->group_name = iter->data;
-
- value = g_key_file_get_value (keyfile, iter->data, "match-device", NULL);
- if (value) {
- connection_info->match_device.has = TRUE;
- connection_info->match_device.spec = nm_match_spec_split (value);
- g_free (value);
- }
- connection_info->stop_match = nm_config_keyfile_get_boolean (keyfile, iter->data, "stop-match", FALSE);
- }
- g_slist_free (connection_groups);
+ if (connection_tag) {
+ /* pass ownership of @connection_tag on... */
+ _get_connection_info_init (&connection_infos[i], keyfile, connection_tag);
}
+ g_free (groups);
return connection_infos;
}
@@ -275,30 +449,13 @@ _get_connection_infos (GKeyFile *keyfile)
/************************************************************************/
static gboolean
-_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b)
+_slist_str_equals (GSList *a, GSList *b)
{
- gs_strfreev char **groups = NULL;
- guint i, j;
-
- if (kf_a == kf_b)
- return TRUE;
-
- groups = g_key_file_get_groups (kf_a, NULL);
- for (i = 0; groups && groups[i]; i++) {
- gs_strfreev char **keys = NULL;
-
- keys = g_key_file_get_keys (kf_a, groups[i], NULL, NULL);
- if (keys) {
- for (j = 0; keys[j]; j++) {
- gs_free char *key_a = g_key_file_get_value (kf_a, groups[i], keys[j], NULL);
- gs_free char *key_b = g_key_file_get_value (kf_b, groups[i], keys[j], NULL);
-
- if (g_strcmp0 (key_a, key_b) != 0)
- return FALSE;
- }
- }
+ while (a && b && g_strcmp0 (a->data, b->data) == 0) {
+ a = a->next;
+ b = b->next;
}
- return TRUE;
+ return !a && !b;
}
NMConfigChangeFlags
@@ -306,7 +463,6 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
{
NMConfigChangeFlags changes = NM_CONFIG_CHANGE_NONE;
NMConfigDataPrivate *priv_old, *priv_new;
- GSList *spec_old, *spec_new;
g_return_val_if_fail (NM_IS_CONFIG_DATA (old_data), NM_CONFIG_CHANGE_NONE);
g_return_val_if_fail (NM_IS_CONFIG_DATA (new_data), NM_CONFIG_CHANGE_NONE);
@@ -314,8 +470,7 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
priv_old = NM_CONFIG_DATA_GET_PRIVATE (old_data);
priv_new = NM_CONFIG_DATA_GET_PRIVATE (new_data);
- if ( !_keyfile_a_contains_all_in_b (priv_old->keyfile, priv_new->keyfile)
- || !_keyfile_a_contains_all_in_b (priv_new->keyfile, priv_old->keyfile))
+ if (!_nm_keyfile_equals (priv_old->keyfile, priv_new->keyfile, TRUE))
changes |= NM_CONFIG_CHANGE_VALUES;
if ( g_strcmp0 (nm_config_data_get_config_main_file (old_data), nm_config_data_get_config_main_file (new_data)) != 0
@@ -327,13 +482,8 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data)
|| g_strcmp0 (nm_config_data_get_connectivity_response (old_data), nm_config_data_get_connectivity_response (new_data)))
changes |= NM_CONFIG_CHANGE_CONNECTIVITY;
- spec_old = priv_old->no_auto_default.specs;
- spec_new = priv_new->no_auto_default.specs;
- while (spec_old && spec_new && strcmp (spec_old->data, spec_new->data) == 0) {
- spec_old = spec_old->next;
- spec_new = spec_new->next;
- }
- if (spec_old || spec_new)
+ if ( !_slist_str_equals (priv_old->no_auto_default.specs, priv_new->no_auto_default.specs)
+ || !_slist_str_equals (priv_old->no_auto_default.specs_config, priv_new->no_auto_default.specs_config))
changes |= NM_CONFIG_CHANGE_NO_AUTO_DEFAULT;
if (g_strcmp0 (nm_config_data_get_dns_mode (old_data), nm_config_data_get_dns_mode (new_data)))
@@ -359,9 +509,6 @@ get_property (GObject *object,
case PROP_CONFIG_DESCRIPTION:
g_value_set_string (value, nm_config_data_get_config_description (self));
break;
- case PROP_NO_AUTO_DEFAULT:
- g_value_take_boxed (value, g_strdupv ((char **) nm_config_data_get_no_auto_default (self)));
- break;
case PROP_CONNECTIVITY_URI:
g_value_set_string (value, nm_config_data_get_connectivity_uri (self));
break;
@@ -385,7 +532,6 @@ set_property (GObject *object,
{
NMConfigData *self = NM_CONFIG_DATA (object);
NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self);
- guint i;
/* This type is immutable. All properties are construct only. */
switch (prop_id) {
@@ -401,12 +547,24 @@ set_property (GObject *object,
priv->keyfile = nm_config_create_keyfile ();
break;
case PROP_NO_AUTO_DEFAULT:
- priv->no_auto_default.arr = g_strdupv (g_value_get_boxed (value));
- if (!priv->no_auto_default.arr)
- priv->no_auto_default.arr = g_new0 (char *, 1);
- for (i = 0; priv->no_auto_default.arr[i]; i++)
- priv->no_auto_default.specs = g_slist_prepend (priv->no_auto_default.specs, priv->no_auto_default.arr[i]);
- priv->no_auto_default.specs = g_slist_reverse (priv->no_auto_default.specs);
+ {
+ char **value_arr = g_value_get_boxed (value);
+ guint i, j = 0;
+
+ priv->no_auto_default.arr = g_new (char *, g_strv_length (value_arr) + 1);
+ priv->no_auto_default.specs = NULL;
+
+ for (i = 0; value_arr && value_arr[i]; i++) {
+ if ( *value_arr[i]
+ && nm_utils_hwaddr_valid (value_arr[i], -1)
+ && _nm_utils_strv_find_first (value_arr, i, value_arr[i]) < 0) {
+ priv->no_auto_default.arr[j++] = g_strdup (value_arr[i]);
+ priv->no_auto_default.specs = g_slist_prepend (priv->no_auto_default.specs, g_strdup_printf ("mac:%s", value_arr[i]));
+ }
+ }
+ priv->no_auto_default.arr[j++] = NULL;
+ priv->no_auto_default.specs = g_slist_reverse (priv->no_auto_default.specs);
+ }
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -431,7 +589,8 @@ finalize (GObject *gobject)
g_free (priv->connectivity.uri);
g_free (priv->connectivity.response);
- g_slist_free (priv->no_auto_default.specs);
+ g_slist_free_full (priv->no_auto_default.specs, g_free);
+ g_slist_free_full (priv->no_auto_default.specs_config, g_free);
g_strfreev (priv->no_auto_default.arr);
g_free (priv->dns_mode);
@@ -466,21 +625,23 @@ constructed (GObject *object)
priv->connection_infos = _get_connection_infos (priv->keyfile);
- priv->connectivity.uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL);
- priv->connectivity.response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL);
+ priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL));
+ priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL);
/* On missing config value, fallback to 300. On invalid value, disable connectivity checking by setting
* the interval to zero. */
- interval = g_key_file_get_value (priv->keyfile, "connectivity", "interval", NULL);
+ interval = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", NULL);
priv->connectivity.interval = interval
? _nm_utils_ascii_str_to_int64 (interval, 10, 0, G_MAXUINT, 0)
: NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL;
g_free (interval);
- priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
+ priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL));
+
+ priv->ignore_carrier = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier", NULL);
+ priv->assume_ipv6ll_only = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only", NULL);
- priv->ignore_carrier = nm_config_get_device_match_spec (priv->keyfile, "main", "ignore-carrier");
- priv->assume_ipv6ll_only = nm_config_get_device_match_spec (priv->keyfile, "main", "assume-ipv6ll-only");
+ priv->no_auto_default.specs_config = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL);
G_OBJECT_CLASS (nm_config_data_parent_class)->constructed (object);
}
@@ -575,7 +736,7 @@ nm_config_data_class_init (NMConfigDataClass *config_class)
(object_class, PROP_NO_AUTO_DEFAULT,
g_param_spec_boxed (NM_CONFIG_DATA_NO_AUTO_DEFAULT, "", "",
G_TYPE_STRV,
- G_PARAM_READWRITE |
+ G_PARAM_WRITABLE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index 64ad372b3b..cc7f95e211 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -45,6 +45,23 @@ G_BEGIN_DECLS
#define NM_CONFIG_DATA_NO_AUTO_DEFAULT "no-auto-default"
#define NM_CONFIG_DATA_DNS_MODE "dns"
+typedef enum { /*<flags >*/
+ NM_CONFIG_GET_VALUE_NONE = 0,
+
+ /* use g_key_file_get_value() instead of g_key_file_get_string(). */
+ NM_CONFIG_GET_VALUE_RAW = (1LL << 0),
+
+ /* strip whitespaces */
+ NM_CONFIG_GET_VALUE_STRIP = (1LL << 1),
+
+ /* if the returned string would be the empty word, return NULL. */
+ NM_CONFIG_GET_VALUE_NO_EMPTY = (1LL << 2),
+
+ /* special flag to read device spec. You want to use this before passing the
+ * value to nm_match_spec_split(). */
+ NM_CONFIG_GET_VALUE_TYPE_SPEC = NM_CONFIG_GET_VALUE_RAW,
+} NMConfigGetValueFlags;
+
typedef enum { /*< flags >*/
NM_CONFIG_CHANGE_NONE = 0,
@@ -80,17 +97,22 @@ NMConfigData *nm_config_data_new_update_no_auto_default (const NMConfigData *bas
NMConfigChangeFlags nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data);
+void nm_config_data_log (const NMConfigData *config_data, const char *prefix);
+
const char *nm_config_data_get_config_main_file (const NMConfigData *config_data);
const char *nm_config_data_get_config_description (const NMConfigData *config_data);
-char *nm_config_data_get_value (const NMConfigData *config_data, const char *group, const char *key, GError **error);
+gboolean nm_config_data_has_group (const NMConfigData *self, const char *group);
+gboolean nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags);
+char *nm_config_data_get_value (const NMConfigData *config_data, const char *group, const char *key, NMConfigGetValueFlags flags);
+gint nm_config_data_get_value_boolean (const NMConfigData *self, const char *group, const char *key, gint default_value);
const char *nm_config_data_get_connectivity_uri (const NMConfigData *config_data);
const guint nm_config_data_get_connectivity_interval (const NMConfigData *config_data);
const char *nm_config_data_get_connectivity_response (const NMConfigData *config_data);
const char *const*nm_config_data_get_no_auto_default (const NMConfigData *config_data);
-const GSList * nm_config_data_get_no_auto_default_list (const NMConfigData *config_data);
+gboolean nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device);
const char *nm_config_data_get_dns_mode (const NMConfigData *self);
@@ -101,6 +123,9 @@ char *nm_config_data_get_connection_default (const NMConfigData *self,
const char *property,
NMDevice *device);
+char **nm_config_data_get_groups (const NMConfigData *self);
+char **nm_config_data_get_keys (const NMConfigData *self, const char *group);
+
G_END_DECLS
#endif /* NM_CONFIG_DATA_H */
diff --git a/src/nm-config.c b/src/nm-config.c
index 7520c18e3b..c318eea790 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -37,10 +37,10 @@
#include <gio/gio.h>
#include <glib/gi18n.h>
-#define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf"
-#define NM_DEFAULT_SYSTEM_CONF_DIR NMCONFDIR "/conf.d"
-#define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf"
-#define NM_NO_AUTO_DEFAULT_STATE_FILE NMSTATEDIR "/no-auto-default.state"
+#define DEFAULT_CONFIG_MAIN_FILE NMCONFDIR "/NetworkManager.conf"
+#define DEFAULT_CONFIG_DIR NMCONFDIR "/conf.d"
+#define DEFAULT_CONFIG_MAIN_FILE_OLD NMCONFDIR "/nm-system-settings.conf"
+#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state"
struct NMConfigCmdLineOptions {
char *config_main_file;
@@ -108,39 +108,115 @@ static void _set_config_data (NMConfig *self, NMConfigData *new_data, int signal
/************************************************************************/
-gboolean
+gint
+nm_config_parse_boolean (const char *str,
+ gint default_value)
+{
+ gsize len;
+ char *s = NULL;
+
+ if (!str)
+ return default_value;
+
+ while (str[0] && g_ascii_isspace (str[0]))
+ str++;
+
+ if (!str[0])
+ return default_value;
+
+ len = strlen (str);
+ if (g_ascii_isspace (str[len - 1])) {
+ s = g_strdup (str);
+ g_strchomp (s);
+ str = s;
+ }
+
+ if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
+ default_value = TRUE;
+ else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
+ default_value = FALSE;
+ if (s)
+ g_free (s);
+ return default_value;
+}
+
+gint
nm_config_keyfile_get_boolean (GKeyFile *keyfile,
const char *section,
const char *key,
- gboolean default_value)
+ gint default_value)
{
- gboolean value = default_value;
- char *str;
+ gs_free char *str = NULL;
g_return_val_if_fail (keyfile != NULL, default_value);
g_return_val_if_fail (section != NULL, default_value);
g_return_val_if_fail (key != NULL, default_value);
str = g_key_file_get_value (keyfile, section, key, NULL);
- if (!str)
- return default_value;
+ return nm_config_parse_boolean (str, default_value);
+}
- g_strstrip (str);
- if (str[0]) {
- if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
- value = TRUE;
- else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
- value = FALSE;
- else {
- nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'",
- section, key, str, default_value ? "true" : "false");
- }
+char *
+nm_config_keyfile_get_value (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ NMConfigGetValueFlags flags)
+{
+ char *value;
+
+ if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_RAW))
+ value = g_key_file_get_value (keyfile, section, key, NULL);
+ else
+ value = g_key_file_get_string (keyfile, section, key, NULL);
+
+ if (!value)
+ return NULL;
+
+ if (NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_STRIP))
+ g_strstrip (value);
+
+ if ( NM_FLAGS_HAS (flags, NM_CONFIG_GET_VALUE_NO_EMPTY)
+ && !*value) {
+ g_free (value);
+ return NULL;
}
- g_free (str);
return value;
}
+void
+nm_config_keyfile_set_string_list (GKeyFile *keyfile,
+ const char *group,
+ const char *key,
+ const char *const* strv,
+ gssize len)
+{
+ gsize l;
+ char *new_value;
+
+ if (len < 0)
+ len = strv ? g_strv_length ((char **) strv) : 0;
+
+ g_key_file_set_string_list (keyfile, group, key, strv, len);
+
+ /* g_key_file_set_string_list() appends a trailing separator to the value.
+ * We don't like that, get rid of it. */
+
+ new_value = g_key_file_get_value (keyfile, group, key, NULL);
+ if (!new_value)
+ return;
+
+ l = strlen (new_value);
+ if (l > 0 && new_value[l - 1] == NM_CONFIG_KEYFILE_LIST_SEPARATOR) {
+ /* Maybe we should check that value doesn't end with "\\,", i.e.
+ * with an escaped separator. But the way g_key_file_set_string_list()
+ * is implemented (currently), it always adds a trailing separator. */
+ new_value[l - 1] = '\0';
+ g_key_file_set_value (keyfile, group, key, new_value);
+ }
+ g_free (new_value);
+}
+
/************************************************************************/
NMConfigData *
@@ -227,94 +303,99 @@ nm_config_get_configure_and_quit (NMConfig *config)
/************************************************************************/
static char **
-no_auto_default_merge_from_file (const char *no_auto_default_file, const char *const* no_auto_default)
+no_auto_default_from_file (const char *no_auto_default_file)
{
- GPtrArray *updated;
+ GPtrArray *no_auto_default_new;
char **list;
- int i, j;
+ guint i;
char *data;
- updated = g_ptr_array_new ();
- if (no_auto_default) {
- for (i = 0; no_auto_default[i]; i++)
- g_ptr_array_add (updated, g_strdup (no_auto_default[i]));
- }
+ no_auto_default_new = g_ptr_array_new ();
if ( no_auto_default_file
&& g_file_get_contents (no_auto_default_file, &data, NULL, NULL)) {
list = g_strsplit (data, "\n", -1);
for (i = 0; list[i]; i++) {
- if (!*list[i])
+ if ( *list[i]
+ && nm_utils_hwaddr_valid (list[i], -1)
+ && _nm_utils_strv_find_first (list, i, list[i]) < 0)
+ g_ptr_array_add (no_auto_default_new, list[i]);
+ else
g_free (list[i]);
- else {
- for (j = 0; j < updated->len; j++) {
- if (!strcmp (list[i], updated->pdata[j]))
- break;
- }
- if (j == updated->len)
- g_ptr_array_add (updated, list[i]);
- else
- g_free (list[i]);
- }
}
g_free (list);
g_free (data);
}
- g_ptr_array_add (updated, NULL);
- return (char **) g_ptr_array_free (updated, FALSE);
+ g_ptr_array_add (no_auto_default_new, NULL);
+ return (char **) g_ptr_array_free (no_auto_default_new, FALSE);
+}
+
+static gboolean
+no_auto_default_to_file (const char *no_auto_default_file, const char *const*no_auto_default, GError **error)
+{
+ GString *data;
+ gboolean success;
+ guint i;
+
+ data = g_string_new ("");
+ for (i = 0; no_auto_default && no_auto_default[i]; i++) {
+ g_string_append (data, no_auto_default[i]);
+ g_string_append_c (data, '\n');
+ }
+ success = g_file_set_contents (no_auto_default_file, data->str, data->len, error);
+ g_string_free (data, TRUE);
+ return success;
}
gboolean
nm_config_get_no_auto_default_for_device (NMConfig *self, NMDevice *device)
{
- NMConfigData *config_data;
-
g_return_val_if_fail (NM_IS_CONFIG (self), FALSE);
- g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
- config_data = NM_CONFIG_GET_PRIVATE (self)->config_data;
- return nm_device_spec_match_list (device, nm_config_data_get_no_auto_default_list (config_data));
+ return nm_config_data_get_no_auto_default_for_device (NM_CONFIG_GET_PRIVATE (self)->config_data, device);
}
void
nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
- char *current;
- GString *updated;
GError *error = NULL;
- char **no_auto_default;
NMConfigData *new_data = NULL;
+ const char *hw_address;
+ const char *const*no_auto_default_current;
+ GPtrArray *no_auto_default_new = NULL;
+ guint i;
g_return_if_fail (NM_IS_CONFIG (self));
g_return_if_fail (NM_IS_DEVICE (device));
- if (nm_config_get_no_auto_default_for_device (self, device))
- return;
+ hw_address = nm_device_get_hw_address (device);
+
+ no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data);
- updated = g_string_new (NULL);
- if (g_file_get_contents (priv->no_auto_default_file, &current, NULL, NULL)) {
- g_string_append (updated, current);
- g_free (current);
- if (updated->str[updated->len - 1] != '\n')
- g_string_append_c (updated, '\n');
+ if (_nm_utils_strv_find_first ((char **) no_auto_default_current, -1, hw_address) >= 0) {
+ /* @hw_address is already blocked. We don't have to update our in-memory representation.
+ * Maybe we should write to no_auto_default_file anew, but let's save that too. */
+ return;
}
- g_string_append (updated, nm_device_get_hw_address (device));
- g_string_append_c (updated, '\n');
+ no_auto_default_new = g_ptr_array_new ();
+ for (i = 0; no_auto_default_current && no_auto_default_current[i]; i++)
+ g_ptr_array_add (no_auto_default_new, (char *) no_auto_default_current[i]);
+ g_ptr_array_add (no_auto_default_new, (char *) hw_address);
+ g_ptr_array_add (no_auto_default_new, NULL);
- if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) {
+ if (!no_auto_default_to_file (priv->no_auto_default_file, (const char *const*) no_auto_default_new->pdata, &error)) {
nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s",
error->message);
g_error_free (error);
}
- g_string_free (updated, TRUE);
+ new_data = nm_config_data_new_update_no_auto_default (priv->config_data, (const char *const*) no_auto_default_new->pdata);
- no_auto_default = no_auto_default_merge_from_file (priv->no_auto_default_file, nm_config_data_get_no_auto_default (priv->config_data));
- new_data = nm_config_data_new_update_no_auto_default (priv->config_data, (const char *const*) no_auto_default);
- g_strfreev (no_auto_default);
+ /* unref no_auto_default_set here. Note that _set_config_data() probably invalidates the content of the array. */
+ g_ptr_array_unref (no_auto_default_new);
_set_config_data (self, new_data, 0);
}
@@ -379,16 +460,16 @@ nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
{
GOptionEntry config_options[] = {
- { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_("/path/to/config.file") },
- { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_("/path/to/config/dir") },
- { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, "no-auto-default.state location", NULL },
- { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") },
+ { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_main_file, N_("Config file location"), N_(DEFAULT_CONFIG_MAIN_FILE) },
+ { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) },
+ { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) },
+ { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) },
{ "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL },
/* These three are hidden for now, and should eventually just go away. */
{ "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" },
- { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), "60" },
- { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), N_("Bingo!") },
+ { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli->connectivity_interval, N_("The interval between connectivity checks (in seconds)"), G_STRINGIFY (NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL) },
+ { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli->connectivity_response, N_("The expected start of the response"), N_(NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE) },
{ 0 },
};
@@ -404,10 +485,70 @@ nm_config_create_keyfile ()
GKeyFile *keyfile;
keyfile = g_key_file_new ();
- g_key_file_set_list_separator (keyfile, ',');
+ g_key_file_set_list_separator (keyfile, NM_CONFIG_KEYFILE_LIST_SEPARATOR);
return keyfile;
}
+static int
+_sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
+{
+ const char *a, *b;
+ gboolean a_is_connection, b_is_connection;
+
+ /* basic NULL checking... */
+ if (pa == pb)
+ return 0;
+ if (!pa)
+ return -1;
+ if (!pb)
+ return 1;
+
+ a = *pa;
+ b = *pb;
+
+ a_is_connection = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
+ b_is_connection = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
+
+ if (a_is_connection != b_is_connection) {
+ /* one is a [connection*] entry, the other not. We sort [connection*] entires
+ * after. */
+ if (a_is_connection)
+ return 1;
+ return -1;
+ }
+ if (!a_is_connection) {
+ /* both are non-connection entries. Don't reorder. */
+ return 0;
+ }
+
+ /* both are [connection.\+] entires. Reverse their order.
+ * One of the sections might be literally [connection]. That section
+ * is special and it's order will be fixed later. It doesn't actually
+ * matter here how it compares with [connection.\+] sections. */
+ return pa > pb ? -1 : 1;
+}
+
+static gboolean
+_setting_is_device_spec (const char *group, const char *key)
+{
+#define _IS(group_v, key_v) (strcmp (group, (""group_v)) == 0 && strcmp (key, (""key_v)) == 0)
+ return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default")
+ || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier")
+ || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only")
+ || _IS (NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices")
+ || (g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION) && !strcmp (key, "match-device"));
+}
+
+static gboolean
+_setting_is_string_list (const char *group, const char *key)
+{
+ return _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins")
+ || _IS (NM_CONFIG_KEYFILE_GROUP_MAIN, "debug")
+ || _IS (NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains")
+ || g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST);
+#undef _IS
+}
+
static gboolean
read_config (GKeyFile *keyfile, const char *path, GError **error)
{
@@ -435,36 +576,116 @@ read_config (GKeyFile *keyfile, const char *path, GError **error)
/* Override the current settings with the new ones */
groups = g_key_file_get_groups (kf, &ngroups);
+ if (!groups)
+ ngroups = 0;
+
+ /* Within one file we reverse the order of the '[connection.\+] sections.
+ * Here we merge the current file (@kf) into @keyfile. As we merge multiple
+ * files, earlier sections (with lower priority) will be added first.
+ * But within one file, we want a top-to-bottom order. This means we
+ * must reverse the order within each file.
+ * At the very end, we will revert the order of all sections again and
+ * get thus the right behavior. This final reversing is done in
+ * NMConfigData:_get_connection_infos(). */
+ if (ngroups > 1) {
+ g_qsort_with_data (groups,
+ ngroups,
+ sizeof (char *),
+ (GCompareDataFunc) _sort_groups_cmp,
+ NULL);
+ }
+
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;
-
- 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);
-
- if (old_val && *old_val) {
- char *combined = g_strconcat (old_val, ",", new_val, NULL);
-
- 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);
+ const char *key;
+ char *new_value;
+ char last_char;
+ gsize key_len;
+
+ 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);
+ gboolean is_string_list;
+
+ is_string_list = _setting_is_string_list (group, base_key);
+
+ if ( is_string_list
+ || _setting_is_device_spec (group, base_key)) {
+ gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func (g_free);
+ char **iter_val;
+ gs_strfreev char **old_val = NULL;
+ gs_free char **new_val = NULL;
+
+ if (is_string_list) {
+ old_val = g_key_file_get_string_list (keyfile, group, base_key, NULL, NULL);
+ new_val = g_key_file_get_string_list (kf, group, key, NULL, NULL);
+ } else {
+ gs_free char *old_sval = nm_config_keyfile_get_value (keyfile, group, base_key, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ gs_free char *new_sval = nm_config_keyfile_get_value (kf, group, key, NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ gs_free_slist GSList *old_specs = nm_match_spec_split (old_sval);
+ gs_free_slist GSList *new_specs = nm_match_spec_split (new_sval);
+
+ /* the key is a device spec. This is a special kind of string-list, that
+ * we must split differently. */
+ old_val = _nm_utils_slist_to_strv (old_specs, FALSE);
+ new_val = _nm_utils_slist_to_strv (new_specs, FALSE);
+ }
+
+ /* merge the string lists, by omiting duplicates. */
+
+ 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);
+ }
+
+ if (new->len > 0) {
+ if (is_string_list)
+ nm_config_keyfile_set_string_list (keyfile, group, base_key, (const char *const*) new->pdata, new->len);
+ else {
+ gs_free_slist GSList *specs = NULL;
+ gs_free char *specs_joined = NULL;
+
+ g_ptr_array_add (new, NULL);
+ specs = _nm_utils_strv_to_slist ((char **) new->pdata, FALSE);
+
+ specs_joined = nm_match_spec_join (specs);
+
+ g_key_file_set_value (keyfile, group, base_key, specs_joined);
+ }
+ } else {
+ if (is_string_list)
+ g_key_file_remove_key (keyfile, group, base_key, NULL);
+ else
+ g_key_file_set_value (keyfile, group, base_key, "");
+ }
+ } else {
+ /* For any other settings we don't support extending the option with +/-.
+ * Just drop the key. */
+ }
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);
}
@@ -504,27 +725,27 @@ read_base_config (GKeyFile *keyfile,
*/
/* Try deprecated nm-system-settings.conf first */
- if (read_config (keyfile, NM_OLD_SYSTEM_CONF_FILE, &my_error)) {
- *out_config_main_file = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
+ if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE_OLD, &my_error)) {
+ *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE_OLD);
return TRUE;
}
if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
nm_log_warn (LOGD_CORE, "Old default config file %s invalid: %s\n",
- NM_OLD_SYSTEM_CONF_FILE,
+ DEFAULT_CONFIG_MAIN_FILE_OLD,
my_error->message);
}
g_clear_error (&my_error);
/* Try the standard config file location next */
- if (read_config (keyfile, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) {
- *out_config_main_file = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
+ if (read_config (keyfile, DEFAULT_CONFIG_MAIN_FILE, &my_error)) {
+ *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE);
return TRUE;
}
if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
nm_log_warn (LOGD_CORE, "Default config file %s invalid: %s\n",
- NM_DEFAULT_SYSTEM_CONF_FILE,
+ DEFAULT_CONFIG_MAIN_FILE,
my_error->message);
g_propagate_error (error, my_error);
return FALSE;
@@ -534,9 +755,9 @@ read_base_config (GKeyFile *keyfile,
/* If for some reason no config file exists, use the default
* config file path.
*/
- *out_config_main_file = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
+ *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE);
nm_log_info (LOGD_CORE, "No config file found or given; using %s\n",
- NM_DEFAULT_SYSTEM_CONF_FILE);
+ DEFAULT_CONFIG_MAIN_FILE);
return TRUE;
}
@@ -642,23 +863,23 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
* config files. */
if (cli && cli->plugins && cli->plugins[0])
- g_key_file_set_value (keyfile, "main", "plugins", cli->plugins);
- plugins_tmp = g_key_file_get_string_list (keyfile, "main", "plugins", NULL, NULL);
+ g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", cli->plugins);
+ plugins_tmp = g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL);
if (!plugins_tmp) {
if (STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
- g_key_file_set_value (keyfile, "main", "plugins", CONFIG_PLUGINS_DEFAULT);
+ g_key_file_set_value (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", CONFIG_PLUGINS_DEFAULT);
} else
g_strfreev (plugins_tmp);
if (cli && cli->configure_and_quit)
- g_key_file_set_value (keyfile, "main", "configure-and-quit", "true");
+ g_key_file_set_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", TRUE);
if (cli && cli->connectivity_uri && cli->connectivity_uri[0])
- g_key_file_set_value (keyfile, "connectivity", "uri", cli->connectivity_uri);
+ g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", cli->connectivity_uri);
if (cli && cli->connectivity_interval >= 0)
- g_key_file_set_integer (keyfile, "connectivity", "interval", cli->connectivity_interval);
+ g_key_file_set_integer (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", cli->connectivity_interval);
if (cli && cli->connectivity_response && cli->connectivity_response[0])
- g_key_file_set_value (keyfile, "connectivity", "response", cli->connectivity_response);
+ g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", cli->connectivity_response);
*out_config_main_file = o_config_main_file;
*out_config_description = o_config_description;
@@ -666,11 +887,16 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
}
GSList *
-nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key)
+nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key)
{
gs_free char *value = NULL;
- value = g_key_file_get_string ((GKeyFile *) keyfile, group, key, NULL);
+ /* nm_match_spec_split() already supports full escaping and is basically
+ * a modified version of g_key_file_parse_value_as_string(). So we first read
+ * the raw value (g_key_file_get_value()), and do the parsing ourselves. */
+ value = g_key_file_get_value ((GKeyFile *) keyfile, group, key, NULL);
+ if (out_has_key)
+ *out_has_key = !!value;
return nm_match_spec_split (value);
}
@@ -685,6 +911,7 @@ nm_config_reload (NMConfig *self, int signal)
NMConfigData *new_data = NULL;
char *config_main_file = NULL;
char *config_description = NULL;
+ gs_strfreev char **no_auto_default = NULL;
g_return_if_fail (NM_IS_CONFIG (self));
@@ -710,7 +937,9 @@ nm_config_reload (NMConfig *self, int signal)
_set_config_data (self, NULL, signal);
return;
}
- new_data = nm_config_data_new (config_main_file, config_description, nm_config_data_get_no_auto_default (priv->config_data), keyfile);
+ no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
+
+ new_data = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile);
g_free (config_main_file);
g_free (config_description);
g_key_file_unref (keyfile);
@@ -799,6 +1028,7 @@ _set_config_data (NMConfig *self, NMConfigData *new_data, int signal)
if (new_data) {
nm_log_info (LOGD_CORE, "config: update %s (%s)", nm_config_data_get_config_description (new_data),
(log_str = nm_config_change_flags_to_string (changes)));
+ nm_config_data_log (new_data, "CONFIG: ");
priv->config_data = new_data;
} else if (had_new_data)
nm_log_info (LOGD_CORE, "config: signal %s (no changes from disk)", (log_str = nm_config_change_flags_to_string (changes)));
@@ -840,9 +1070,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
GKeyFile *keyfile;
char *config_main_file = NULL;
char *config_description = NULL;
- char **no_auto_default;
- GSList *no_auto_default_orig_list;
- GPtrArray *no_auto_default_orig;
+ gs_strfreev char **no_auto_default = NULL;
if (priv->config_dir) {
/* Object is already initialized. */
@@ -855,7 +1083,7 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
if (priv->cli.config_dir)
priv->config_dir = g_strdup (priv->cli.config_dir);
else
- priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR);
+ priv->config_dir = g_strdup (DEFAULT_CONFIG_DIR);
keyfile = read_entire_config (&priv->cli,
priv->config_dir,
@@ -870,38 +1098,30 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
if (priv->cli.no_auto_default_file)
priv->no_auto_default_file = g_strdup (priv->cli.no_auto_default_file);
else
- priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE);
+ priv->no_auto_default_file = g_strdup (DEFAULT_NO_AUTO_DEFAULT_FILE);
- priv->plugins = g_key_file_get_string_list (keyfile, "main", "plugins", NULL, NULL);
+ priv->plugins = _nm_utils_strv_cleanup (g_key_file_get_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "plugins", NULL, NULL),
+ TRUE, TRUE, TRUE);
if (!priv->plugins)
priv->plugins = g_new0 (char *, 1);
- priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, "main", "monitor-connection-files", FALSE);
-
- priv->auth_polkit = nm_config_keyfile_get_boolean (keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
+ priv->monitor_connection_files = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "monitor-connection-files", FALSE);
- priv->dhcp_client = g_key_file_get_value (keyfile, "main", "dhcp", NULL);
+ priv->auth_polkit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
- priv->log_level = g_key_file_get_value (keyfile, "logging", "level", NULL);
- priv->log_domains = g_key_file_get_value (keyfile, "logging", "domains", NULL);
+ priv->dhcp_client = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dhcp", NULL));
- priv->debug = g_key_file_get_value (keyfile, "main", "debug", NULL);
+ priv->log_level = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "level", NULL));
+ priv->log_domains = nm_strstrip (g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_LOGGING, "domains", NULL));
- priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, "main", "configure-and-quit", FALSE);
+ priv->debug = g_key_file_get_string (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "debug", NULL);
- no_auto_default_orig_list = nm_config_get_device_match_spec (keyfile, "main", "no-auto-default");
+ priv->configure_and_quit = nm_config_keyfile_get_boolean (keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "configure-and-quit", FALSE);
- no_auto_default_orig = _nm_utils_copy_slist_to_array (no_auto_default_orig_list, NULL, NULL);
- g_ptr_array_add (no_auto_default_orig, NULL);
- no_auto_default = no_auto_default_merge_from_file (priv->no_auto_default_file, (const char *const *) no_auto_default_orig->pdata);
- g_ptr_array_unref (no_auto_default_orig);
-
- g_slist_free_full (no_auto_default_orig_list, g_free);
+ no_auto_default = no_auto_default_from_file (priv->no_auto_default_file);
priv->config_data_orig = nm_config_data_new (config_main_file, config_description, (const char *const*) no_auto_default, keyfile);
- g_strfreev (no_auto_default);
-
priv->config_data = g_object_ref (priv->config_data_orig);
g_free (config_main_file);
diff --git a/src/nm-config.h b/src/nm-config.h
index d4f5e94f3c..5b096f165d 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -44,6 +44,24 @@ G_BEGIN_DECLS
#define NM_CONFIG_SIGNAL_CONFIG_CHANGED "config-changed"
#define NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL 300
+#define NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */
+
+#define NM_CONFIG_KEYFILE_LIST_SEPARATOR ','
+
+#define NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION "connection"
+#define NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST ".test-append-stringlist"
+
+#define NM_CONFIG_KEYFILE_GROUP_MAIN "main"
+#define NM_CONFIG_KEYFILE_GROUP_LOGGING "logging"
+#define NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY "connectivity"
+
+#define NM_CONFIG_KEYFILE_GROUP_KEYFILE "keyfile"
+#define NM_CONFIG_KEYFILE_GROUP_IFUPDOWN "ifupdown"
+#define NM_CONFIG_KEYFILE_GROUP_IFNET "ifnet"
+
+#define NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH "auto_refresh"
+#define NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED "managed"
+#define NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED "managed"
typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
@@ -66,6 +84,10 @@ char *nm_config_change_flags_to_string (NMConfigChangeFlags flags);
NMConfigData *nm_config_get_data (NMConfig *config);
NMConfigData *nm_config_get_data_orig (NMConfig *config);
+
+#define NM_CONFIG_GET_DATA (nm_config_get_data (nm_config_get ()))
+#define NM_CONFIG_GET_DATA_ORIG (nm_config_get_data_orig (nm_config_get ()))
+
const char **nm_config_get_plugins (NMConfig *config);
gboolean nm_config_get_monitor_connection_files (NMConfig *config);
gboolean nm_config_get_auth_polkit (NMConfig *config);
@@ -88,12 +110,23 @@ NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, GError **error);
NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, GError **error);
void nm_config_reload (NMConfig *config, int signal);
+gint nm_config_parse_boolean (const char *str, gint default_value);
+
GKeyFile *nm_config_create_keyfile (void);
-gboolean nm_config_keyfile_get_boolean (GKeyFile *keyfile,
- const char *section,
+gint nm_config_keyfile_get_boolean (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ gint default_value);
+char *nm_config_keyfile_get_value (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ NMConfigGetValueFlags flags);
+void nm_config_keyfile_set_string_list (GKeyFile *keyfile,
+ const char *group,
const char *key,
- gboolean default_value);
-GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key);
+ const char *const* strv,
+ gssize len);
+GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, const char *key, gboolean *out_has_key);
G_END_DECLS
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index f7e7576c22..81ac8f9de4 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -37,8 +37,6 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
#define NM_CONNECTIVITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONNECTIVITY, NMConnectivityPrivate))
-#define DEFAULT_RESPONSE "NetworkManager is online" /* NOT LOCALIZED */
-
#define _LOG_DEFAULT_DOMAIN LOGD_CONCHECK
#define _LOG(level, domain, ...) \
@@ -140,7 +138,7 @@ nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_
NMConnectivityState new_state;
const char *nm_header;
const char *uri = cb_data->uri;
- const char *response = cb_data->response ? cb_data->response : DEFAULT_RESPONSE;
+ const char *response = cb_data->response ? cb_data->response : NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE;
self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
/* it is safe to unref @self here, @simple holds yet another reference. */
@@ -401,7 +399,7 @@ set_property (GObject *object, guint property_id,
case PROP_RESPONSE:
response = g_value_get_string (value);
if (g_strcmp0 (response, priv->response) != 0) {
- /* a response %NULL means, DEFAULT_RESPONSE. Any other response
+ /* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response
* (including "") is accepted. */
g_free (priv->response);
priv->response = g_strdup (response);
@@ -432,7 +430,7 @@ get_property (GObject *object, guint property_id,
if (priv->response)
g_value_set_string (value, priv->response);
else
- g_value_set_static_string (value, DEFAULT_RESPONSE);
+ g_value_set_static_string (value, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE);
break;
case PROP_STATE:
g_value_set_uint (value, priv->state);
@@ -510,7 +508,7 @@ nm_connectivity_class_init (NMConnectivityClass *klass)
g_object_class_install_property
(object_class, PROP_RESPONSE,
g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "",
- DEFAULT_RESPONSE,
+ NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
diff --git a/src/nm-manager.c b/src/nm-manager.c
index e6640ab3c9..080cdb8b24 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -2487,7 +2487,7 @@ should_connect_slaves (NMConnection *connection, NMDevice *device)
goto out;
/* Check configuration default for autoconnect-slaves property */
- value = nm_config_data_get_connection_default (nm_config_get_data (nm_config_get ()),
+ value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
"connection.autoconnect-slaves", device);
if (value)
autoconnect_slaves = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1);
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 9e4c441fbc..cb3b6b4386 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -685,17 +685,13 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
GModule *plugin;
gs_free char *full_name = NULL;
gs_free char *path = NULL;
- gs_free char *pname = NULL;
+ const char *pname;
GObject *obj;
GObject * (*factory_func) (void);
struct stat st;
int errsv;
- pname = g_strdup (*iter);
- g_strstrip (pname);
-
- if (!*pname)
- continue;
+ pname = *iter;
if (!*pname || strchr (pname, '/')) {
LOG (LOGL_WARN, "ignore invalid plugin \"%s\"", pname);
diff --git a/src/settings/plugins/ifnet/net_parser.h b/src/settings/plugins/ifnet/net_parser.h
index 005207adfe..d10979cca6 100644
--- a/src/settings/plugins/ifnet/net_parser.h
+++ b/src/settings/plugins/ifnet/net_parser.h
@@ -25,7 +25,6 @@
#include <glib.h>
#define CONF_NET_FILE SYSCONFDIR "/conf.d/net"
-#define IFNET_KEY_FILE_GROUP "ifnet"
gboolean ifnet_init (gchar * config_file);
void ifnet_destroy (void);
diff --git a/src/settings/plugins/ifnet/plugin.c b/src/settings/plugins/ifnet/plugin.c
index 7f1eb4ff93..5eab31abfd 100644
--- a/src/settings/plugins/ifnet/plugin.c
+++ b/src/settings/plugins/ifnet/plugin.c
@@ -47,7 +47,6 @@
#define IFNET_PLUGIN_INFO "(C) 1999-2010 Gentoo Foundation, Inc. To report bugs please use bugs.gentoo.org with [networkmanager] or [qiaomuf] prefix."
#define IFNET_SYSTEM_HOSTNAME_FILE "/etc/conf.d/hostname"
#define IFNET_MANAGE_WELL_KNOWN_DEFAULT TRUE
-#define IFNET_KEY_FILE_KEY_MANAGED "managed"
typedef struct {
GHashTable *connections; /* uuid::connection */
@@ -121,17 +120,9 @@ write_system_hostname (NMSystemConfigInterface * config,
static gboolean
is_managed_plugin (void)
{
- char *result = NULL;
-
- result = nm_config_data_get_value (nm_config_get_data_orig (nm_config_get ()),
- IFNET_KEY_FILE_GROUP, IFNET_KEY_FILE_KEY_MANAGED,
- NULL);
- if (result) {
- gboolean ret = is_true (result);
- g_free (result);
- return ret;
- }
- return IFNET_MANAGE_WELL_KNOWN_DEFAULT;
+ return nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+ NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_MANAGED,
+ IFNET_MANAGE_WELL_KNOWN_DEFAULT);
}
static void
@@ -246,8 +237,7 @@ reload_connections (NMSystemConfigInterface *config)
SCPluginIfnet *self = SC_PLUGIN_IFNET (config);
SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self);
GList *conn_names = NULL, *n_iter = NULL;
- gboolean auto_refresh = FALSE;
- char *str_auto_refresh;
+ gboolean auto_refresh;
GError *error = NULL;
/* save names for removing unused connections */
@@ -264,12 +254,9 @@ reload_connections (NMSystemConfigInterface *config)
nm_log_info (LOGD_SETTINGS, "Loading connections");
- str_auto_refresh = nm_config_data_get_value (nm_config_get_data_orig (nm_config_get ()),
- IFNET_KEY_FILE_GROUP, "auto_refresh",
- NULL);
- if (str_auto_refresh && is_true (str_auto_refresh))
- auto_refresh = TRUE;
- g_free (str_auto_refresh);
+ auto_refresh = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+ NM_CONFIG_KEYFILE_GROUP_IFNET, NM_CONFIG_KEYFILE_KEY_IFNET_AUTO_REFRESH,
+ FALSE);
new_connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
diff --git a/src/settings/plugins/ifupdown/plugin.c b/src/settings/plugins/ifupdown/plugin.c
index 1cbff29b3d..8652dd8dd8 100644
--- a/src/settings/plugins/ifupdown/plugin.c
+++ b/src/settings/plugins/ifupdown/plugin.c
@@ -63,8 +63,6 @@
#define IFUPDOWN_PLUGIN_INFO "(C) 2008 Canonical Ltd. To report bugs please use the NetworkManager mailing list."
#define IFUPDOWN_SYSTEM_HOSTNAME_FILE "/etc/hostname"
-#define IFUPDOWN_KEY_FILE_GROUP "ifupdown"
-#define IFUPDOWN_KEY_FILE_KEY_MANAGED "managed"
#define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE
/* #define ALWAYS_UNMANAGE TRUE */
@@ -327,8 +325,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
GHashTable *auto_ifaces;
if_block *block = NULL;
NMInotifyHelper *inotify_helper;
- char *value;
- GError *error = NULL;
GList *keys, *iter;
GHashTableIter con_iter;
const char *block_name;
@@ -354,8 +350,6 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
} else
g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
- priv->unmanage_well_known = IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT;
-
inotify_helper = nm_inotify_helper_get ();
priv->inotify_event_id = g_signal_connect (inotify_helper,
"event",
@@ -456,21 +450,10 @@ SCPluginIfupdown_init (NMSystemConfigInterface *config)
g_hash_table_destroy (auto_ifaces);
/* Check the config file to find out whether to manage interfaces */
- value = nm_config_data_get_value (nm_config_get_data_orig (nm_config_get ()),
- IFUPDOWN_KEY_FILE_GROUP, IFUPDOWN_KEY_FILE_KEY_MANAGED,
- &error);
- if (error) {
- nm_log_info (LOGD_SETTINGS, "loading system config file (%s) caused error: %s",
- nm_config_data_get_config_main_file (nm_config_get_data (nm_config_get ())),
- error->message);
- } else {
- gboolean manage_well_known;
- error = NULL;
-
- manage_well_known = !g_strcmp0 (value, "true") || !g_strcmp0 (value, "1");
- priv->unmanage_well_known = !manage_well_known;
- g_free (value);
- }
+ priv->unmanage_well_known = !nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+ NM_CONFIG_KEYFILE_GROUP_IFUPDOWN,
+ NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
+ !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed");
/* Add well-known interfaces */
@@ -630,7 +613,7 @@ sc_plugin_ifupdown_init (SCPluginIfupdown *plugin)
static void
GObject__get_property (GObject *object, guint prop_id,
- GValue *value, GParamSpec *pspec)
+ GValue *value, GParamSpec *pspec)
{
NMSystemConfigInterface *self = NM_SYSTEM_CONFIG_INTERFACE (object);
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index 611192baa5..7f21e71e67 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -591,7 +591,7 @@ get_unmanaged_specs (NMSystemConfigInterface *config)
key_file = nm_config_create_keyfile ();
if (parse_key_file_allow_none (priv, key_file, &error))
- specs = nm_config_get_device_match_spec (key_file, "keyfile", "unmanaged-devices");
+ specs = nm_config_get_device_match_spec (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "unmanaged-devices", NULL);
if (error) {
nm_log_warn (LOGD_SETTINGS, "keyfile: error getting unmanaged specs: %s", error->message);
@@ -617,7 +617,7 @@ plugin_get_hostname (SCPluginKeyfile *plugin)
if (!parse_key_file_allow_none (priv, key_file, &error))
goto out;
- hostname = g_key_file_get_value (key_file, "keyfile", "hostname", NULL);
+ hostname = g_key_file_get_value (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", NULL);
out:
if (error) {
@@ -653,7 +653,7 @@ plugin_set_hostname (SCPluginKeyfile *plugin, const char *hostname)
if (!parse_key_file_allow_none (priv, key_file, &error))
goto out;
- g_key_file_set_string (key_file, "keyfile", "hostname", hostname);
+ g_key_file_set_string (key_file, NM_CONFIG_KEYFILE_GROUP_KEYFILE, "hostname", hostname);
data = g_key_file_to_data (key_file, &len, &error);
if (!data)
diff --git a/src/tests/config/NetworkManager.conf b/src/tests/config/NetworkManager.conf
index 36113661f2..a750c801ee 100644
--- a/src/tests/config/NetworkManager.conf
+++ b/src/tests/config/NetworkManager.conf
@@ -22,6 +22,17 @@ ipv6.ip6_privacy=0
dummy.test1=no
dummy.test2=no
+ord.key00=A-0.0.00
+ord.key01=A-0.0.01
+ord.key02=A-0.0.02
+ord.key03=A-0.0.03
+ord.key04=A-0.0.04
+ord.key05=A-0.0.05
+ord.key06=A-0.0.06
+ord.key07=A-0.0.07
+ord.key08=A-0.0.08
+ord.key09=A-0.0.09
+
[connection.dev51]
match-device=mac:00:00:00:00:00:51
stop-match=yes
@@ -37,3 +48,36 @@ match-device=interface-name:wlan1
# match-wifi is not yet implemented. Just an idea what could be useful.
match-wifi=ssid:*[Ss]tarbucks*|*University*
ipv6.ip6_privacy=2
+
+
+# the following sections are tested for their order across
+# multiple files.
+[connection.ord.0.1]
+ord.key03=A-0.1.03
+ord.key04=A-0.1.04
+ord.key05=A-0.1.05
+ord.key06=A-0.1.06
+ord.key07=A-0.1.07
+ord.key08=A-0.1.08
+ord.key09=A-0.1.09
+ord.ovw01=A-0.1.ovw01
+[connection.ord.0.2]
+ord.key02=A-0.2.02
+ord.key03=A-0.2.03
+ord.key04=A-0.2.04
+ord.key05=A-0.2.05
+ord.key06=A-0.2.06
+ord.key07=A-0.2.07
+ord.key08=A-0.2.08
+ord.key09=A-0.2.09
+[connection.ord.0.3]
+ord.key01=A-0.3.01
+ord.key02=A-0.3.02
+ord.key03=A-0.3.03
+ord.key04=A-0.3.04
+ord.key05=A-0.3.05
+ord.key06=A-0.3.06
+ord.key07=A-0.3.07
+ord.key08=A-0.3.08
+ord.key09=A-0.3.09
+ord.ovw01=A-0.3.ovw01
diff --git a/src/tests/config/conf.d/00-overrides.conf b/src/tests/config/conf.d/00-overrides.conf
index 0aa19d484c..f26ed93b9a 100644
--- a/src/tests/config/conf.d/00-overrides.conf
+++ b/src/tests/config/conf.d/00-overrides.conf
@@ -1,11 +1,57 @@
[main]
dhcp=dhcpcd
+no-auto-default=spec1,spec2
+ignore-carrier=\s space1 \s
+
[logging]
domains=PLATFORM,DNS,WIFI
+[appendable-test]
+non-appendable-key1+=i-will-be-dropped
+non-appendable-key2-=i-will-be-dropped
+
[order]
a=0
b=0
c=0
+
+# the following sections are tested for their order across
+# multiple files.
+[connection.ord.1.1]
+ord.key06=B-1.1.06
+ord.key07=B-1.1.07
+ord.key08=B-1.1.08
+ord.key09=B-1.1.09
+[connection.ord.1.2]
+ord.key05=B-1.2.05
+ord.key06=B-1.2.06
+ord.key07=B-1.2.07
+ord.key08=B-1.2.08
+ord.key09=B-1.2.09
+[connection.ord.1.3]
+ord.key04=B-1.3.04
+ord.key05=B-1.3.05
+ord.key06=B-1.3.06
+ord.key07=B-1.3.07
+ord.key08=B-1.3.08
+ord.key09=B-1.3.09
+
+
+[.test-append-stringlist.1]
+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 b1424a4bc8..eadb7f96f6 100644
--- a/src/tests/config/conf.d/10-more.conf
+++ b/src/tests/config/conf.d/10-more.conf
@@ -1,5 +1,12 @@
[main]
extra=hello
+
+no-auto-default-=spec1
+no-auto-default+=spec3
+
+ignore-carrier+=\sspace2\t
+
+[.test-append-stringlist.0]
new+=something
[connectivity]
@@ -9,3 +16,25 @@ uri=http://example.net
a=10
b=10
+# the following sections are tested for their order across
+# multiple files.
+[connection.ord.2.1]
+ord.key09=C-2.1.09
+[connection.ord.2.2]
+ord.key08=C-2.2.08
+ord.key09=C-2.2.09
+[connection.ord.2.3]
+ord.key07=C-2.3.07
+ord.key08=C-2.3.08
+ord.key09=C-2.3.09
+
+# you can overwrite individual settings in a file loaded
+# previously. But note that this does not bump the priority
+# of the section, i.e. [connection.ord.0.1] still has a pretty
+# low priority and is shadowed by [connection.ord.2.1].
+[connection.ord.0.1]
+ord.ovw01=C-0.1.ovw01
+
+
+[.test-append-stringlist.1]
+val1-=b
diff --git a/src/tests/config/conf.d/90-last.conf b/src/tests/config/conf.d/90-last.conf
index dc1de394f1..7d078788de 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
+
+[.test-append-stringlist.1]
+val1+=c,a
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index 7ce3c1ee7c..8b8028e63a 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -91,7 +91,6 @@ static void
test_config_simple (void)
{
NMConfig *config;
- GError *error = NULL;
const char **plugins;
char *value;
gs_unref_object NMDevice *dev50 = nm_test_device_new ("00:00:00:00:00:50");
@@ -111,25 +110,21 @@ test_config_simple (void)
g_assert_cmpstr (plugins[1], ==, "bar");
g_assert_cmpstr (plugins[2], ==, "baz");
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "extra-key", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "extra-key", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "some value");
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "no-key", &error);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "extra-section", "no-key", NM_CONFIG_GET_VALUE_NONE);
g_assert (!value);
- g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND);
- g_clear_error (&error);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "no-section", "no-key", &error);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "no-section", "no-key", NM_CONFIG_GET_VALUE_NONE);
g_assert (!value);
- g_assert_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND);
- g_clear_error (&error);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection", "ipv6.ip6_privacy", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection", "ipv6.ip6_privacy", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "0");
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection.dev51", "ipv4.route-metric", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "connection.dev51", "ipv4.route-metric", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "51");
g_free (value);
@@ -288,6 +283,7 @@ test_config_confdir (void)
NMConfig *config;
const char **plugins;
char *value;
+ GSList *specs;
config = setup_config (NULL, SRCDIR "/NetworkManager.conf", SRCDIR "/conf.d", NULL);
@@ -306,24 +302,86 @@ test_config_confdir (void)
g_assert_cmpstr (plugins[3], ==, "one");
g_assert_cmpstr (plugins[4], ==, "two");
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "extra", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "extra", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "hello");
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "new", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "no-auto-default", NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ specs = nm_match_spec_split (value);
+ g_free (value);
+ g_assert_cmpint (g_slist_length (specs), ==, 2);
+ g_assert_cmpstr (g_slist_nth_data (specs, 0), ==, "spec2");
+ g_assert_cmpstr (g_slist_nth_data (specs, 1), ==, "spec3");
+ g_slist_free_full (specs, g_free);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "main", "ignore-carrier", NM_CONFIG_GET_VALUE_TYPE_SPEC);
+ specs = nm_match_spec_split (value);
+ g_free (value);
+ g_assert_cmpint (g_slist_length (specs), ==, 2);
+ g_assert_cmpstr (g_slist_nth_data (specs, 0), ==, " space1 ");
+ g_assert_cmpstr (g_slist_nth_data (specs, 1), ==, " space2\t");
+ g_slist_free_full (specs, g_free);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".0", "new", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "something"); /* not ",something" */
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "a", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "a", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "90");
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "b", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "b", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "10");
g_free (value);
- value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "c", NULL);
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), "order", "c", NM_CONFIG_GET_VALUE_NONE);
g_assert_cmpstr (value, ==, "0");
g_free (value);
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1", NM_CONFIG_GET_VALUE_RAW));
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1+", NM_CONFIG_GET_VALUE_RAW));
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key1-", NM_CONFIG_GET_VALUE_RAW));
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2", NM_CONFIG_GET_VALUE_RAW));
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2+", NM_CONFIG_GET_VALUE_RAW));
+ g_assert (!nm_config_data_has_value (nm_config_get_data_orig (config), "appendable-test", "non-appendable-key2-", NM_CONFIG_GET_VALUE_RAW));
+
+#define ASSERT_GET_CONN_DEFAULT(xconfig, xname, xvalue) \
+ G_STMT_START { \
+ gs_free char *_value = nm_config_data_get_connection_default (nm_config_get_data_orig (xconfig), (xname), NULL); \
+ g_assert_cmpstr (_value, ==, (xvalue)); \
+ } G_STMT_END
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key00", "A-0.0.00");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key01", "A-0.3.01");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key02", "A-0.2.02");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key03", "A-0.1.03");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key04", "B-1.3.04");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key05", "B-1.2.05");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key06", "B-1.1.06");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key07", "C-2.3.07");
+ ASSERT_GET_CONN_DEFAULT (config, "ord.key08", "C-2.2.08");
+ 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), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val1", NM_CONFIG_GET_VALUE_NONE);
+ g_assert_cmpstr (value, ==, "a,c");
+ g_free (value);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val2", NM_CONFIG_GET_VALUE_NONE);
+ g_assert_cmpstr (value, ==, "VAL2");
+ g_free (value);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val3", NM_CONFIG_GET_VALUE_NONE);
+ g_assert_cmpstr (value, ==, NULL);
+ g_free (value);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val4", NM_CONFIG_GET_VALUE_NONE);
+ g_assert_cmpstr (value, ==, "vb,vb");
+ g_free (value);
+
+ value = nm_config_data_get_value (nm_config_get_data_orig (config), NM_CONFIG_KEYFILE_GROUPPREFIX_TEST_APPEND_STRINGLIST".1", "val5", NM_CONFIG_GET_VALUE_NONE);
+ g_assert_cmpstr (value, ==, "VAL5");
+ g_free (value);
+
+ nm_config_data_log (nm_config_get_data_orig (config), ">>> TEST: ");
+
g_object_unref (config);
}
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index dae872c86d..de65d50fd9 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -630,12 +630,29 @@ static void
test_match_spec_ifname (const char *spec_str, const char **matches, const char **neg_matches)
{
const char *m;
- GSList *specs, *specs_reverse = NULL;
+ GSList *specs, *specs_reverse = NULL, *specs_resplit, *specs_i, *specs_j;
guint i;
+ gs_free char *specs_joined = NULL;
g_assert (spec_str);
specs = nm_match_spec_split (spec_str);
+
+ /* assert that split(join(specs)) == specs */
+ specs_joined = nm_match_spec_join (specs);
+ specs_resplit = nm_match_spec_split (specs_joined);
+ specs_i = specs;
+ specs_j = specs_resplit;
+ while (specs_i && specs_j && g_strcmp0 (specs_i->data, specs_j->data) == 0) {
+ specs_i = specs_i->next;
+ specs_j = specs_j->next;
+ }
+ g_assert (!specs_i);
+ g_assert (!specs_j);
+ g_slist_free_full (specs_resplit, g_free);
+
+ /* also check the matches in the reverse order. They must yield the same result because
+ * matches are inclusive -- except "except:" which always wins. */
specs_reverse = g_slist_reverse (g_slist_copy (specs));
for (i = 0; matches && matches[i]; i++) {
@@ -702,9 +719,12 @@ test_nm_match_spec_interface_name (void)
test_match_spec_ifname ("interface-name:em\\;1,em\\,2,\\,,\\\\,,em\\\\x",
S ("em;1", "em,2", ",", "\\", "em\\x"),
NULL);
- test_match_spec_ifname (" , interface-name:a, ,",
+ test_match_spec_ifname ("\\s\\s,\\sinterface-name:a,\\s,",
S (" ", " ", " interface-name:a"),
NULL);
+ test_match_spec_ifname (" aa ; bb ; cc\\;dd ;e , ; \t\\t , ",
+ S ("aa", "bb", "cc;dd", "e", "\t"),
+ NULL);
#undef S
}