summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2015-07-02 15:54:11 +0200
committerThomas Haller <thaller@redhat.com>2015-07-03 09:24:05 +0200
commitcf15f2a4a82f085d67f54f49ab9355d0d40c0969 (patch)
treed5650d2e0d3cf0e05fdbed1e4d9b4875c51b7d61
parent53dcdf8516e105a02c578cc911abf6a4910bdfa8 (diff)
parent500f590033858f565f35d553b21c822dc378efd2 (diff)
downloadNetworkManager-th/nm-1-0.tar.gz
config: merge branch 'th/nm-config-intern-bgo750558' (early part)th/nm-1-0
Early part of fixes and improvements to NMConfig. This is mostly refactoring and adding of new utility functions. But it also fixes the way how to parse configuration options from "NetworkManager.conf" keyfile. So this brings behavioral changes in the way how we parse the configuration. But unless the user had unusual configurations (whitespaces, backslash escapes), there should be no visible changes. https://bugzilla.gnome.org/show_bug.cgi?id=750558 (cherry picked from commit 65753dbc133f77ccd5531288aab9a9e80da3b786)
-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
}