diff options
Diffstat (limited to 'src/nm-config-data.c')
| -rw-r--r-- | src/nm-config-data.c | 797 |
1 files changed, 775 insertions, 22 deletions
diff --git a/src/nm-config-data.c b/src/nm-config-data.c index ffe00ec2..e2b6c3a6 100644 --- a/src/nm-config-data.c +++ b/src/nm-config-data.c @@ -19,17 +19,18 @@ * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de> */ +#include "config.h" + #include "nm-config-data.h" #include <string.h> #include "nm-config.h" #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" +#include "nm-default.h" typedef struct { char *group_name; @@ -48,6 +49,8 @@ typedef struct { char *config_description; GKeyFile *keyfile; + GKeyFile *keyfile_user; + GKeyFile *keyfile_intern; /* A zero-terminated list of pre-processed information from the * [connection] sections. This is to speed up lookup. */ @@ -69,14 +72,34 @@ typedef struct { GSList *assume_ipv6ll_only; char *dns_mode; + char *rc_manager; + + NMGlobalDnsConfig *global_dns; + + /* mutable field */ + char *value_cached; } NMConfigDataPrivate; +struct _NMGlobalDnsDomain { + char *name; + char **servers; + char **options; +}; + +struct _NMGlobalDnsConfig { + char **searches; + char **options; + GHashTable *domains; + char **domain_list; + gboolean internal; +}; enum { PROP_0, PROP_CONFIG_MAIN_FILE, PROP_CONFIG_DESCRIPTION, - PROP_KEYFILE, + PROP_KEYFILE_USER, + PROP_KEYFILE_INTERN, PROP_CONNECTIVITY_URI, PROP_CONNECTIVITY_INTERVAL, PROP_CONNECTIVITY_RESPONSE, @@ -85,12 +108,21 @@ enum { LAST_PROP }; + G_DEFINE_TYPE (NMConfigData, nm_config_data, G_TYPE_OBJECT) #define NM_CONFIG_DATA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG_DATA, NMConfigDataPrivate)) /************************************************************************/ +#define _HAS_PREFIX(str, prefix) \ + ({ \ + const char *_str = (str); \ + g_str_has_prefix ( _str, ""prefix"") && _str[STRLEN(prefix)] != '\0'; \ + }) + +/************************************************************************/ + const char * nm_config_data_get_config_main_file (const NMConfigData *self) { @@ -126,6 +158,22 @@ nm_config_data_get_value (const NMConfigData *self, const char *group, const cha return nm_config_keyfile_get_value (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, key, flags); } +const char *nm_config_data_get_value_cached (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) +{ + NMConfigDataPrivate *priv; + + 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); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + /* we modify @value_cached. In C++ jargon, the field is mutable. */ + g_free (priv->value_cached); + priv->value_cached = nm_config_keyfile_get_value (priv->keyfile, group, key, flags); + return priv->value_cached; +} + gboolean nm_config_data_has_value (const NMConfigData *self, const char *group, const char *key, NMConfigGetValueFlags flags) { @@ -211,6 +259,14 @@ nm_config_data_get_dns_mode (const NMConfigData *self) return NM_CONFIG_DATA_GET_PRIVATE (self)->dns_mode; } +const char * +nm_config_data_get_rc_manager (const NMConfigData *self) +{ + g_return_val_if_fail (self, NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->rc_manager; +} + gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device) { @@ -229,6 +285,40 @@ nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *devic return nm_device_spec_match_list (device, NM_CONFIG_DATA_GET_PRIVATE (self)->assume_ipv6ll_only); } +GKeyFile * +nm_config_data_clone_keyfile_intern (const NMConfigData *self) +{ + NMConfigDataPrivate *priv; + GKeyFile *keyfile; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + keyfile = nm_config_create_keyfile (); + if (priv->keyfile_intern) + _nm_keyfile_copy (keyfile, priv->keyfile_intern); + return keyfile; +} + +GKeyFile * +_nm_config_data_get_keyfile (const NMConfigData *self) +{ + return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile; +} + +GKeyFile * +_nm_config_data_get_keyfile_intern (const NMConfigData *self) +{ + return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile_intern; +} + +GKeyFile * +_nm_config_data_get_keyfile_user (const NMConfigData *self) +{ + return NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile_user; +} + /************************************************************************/ /** @@ -256,15 +346,128 @@ nm_config_data_get_keys (const NMConfigData *self, const char *group) return g_key_file_get_keys (NM_CONFIG_DATA_GET_PRIVATE (self)->keyfile, group, NULL, NULL); } +/** + * nm_config_data_is_intern_atomic_group: + * @self: + * @group: name of the group to check. + * + * whether a configuration group @group exists and is entirely overwritten + * by internal configuration, i.e. whether it is an atomic group that is + * overwritten. + * + * It doesn't say, that there actually is a user setting that was overwritten. That + * means there could be no corresponding section defined in user configuration + * that required overwriting. + * + * Returns: %TRUE if @group exists and is an atomic group set via internal configuration. + */ +gboolean +nm_config_data_is_intern_atomic_group (const NMConfigData *self, const char *group) +{ + NMConfigDataPrivate *priv; + + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), FALSE); + g_return_val_if_fail (group && *group, FALSE); + + priv = NM_CONFIG_DATA_GET_PRIVATE (self); + + if ( !priv->keyfile_intern + || !g_key_file_has_key (priv->keyfile_intern, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, NULL)) + return FALSE; + + /* we have a .was entry for the section. That means that the section would be overwritten + * from user configuration. But it doesn't mean that the merged configuration contains this + * groups, because the internal setting could hide the user section. + * Only return TRUE, if we actually have such a group in the merged configuration.*/ + return g_key_file_has_group (priv->keyfile, group); +} + +/************************************************************************/ + +static GKeyFile * +_merge_keyfiles (GKeyFile *keyfile_user, GKeyFile *keyfile_intern) +{ + gs_strfreev char **groups = NULL; + guint g, k; + GKeyFile *keyfile; + gsize ngroups; + + keyfile = nm_config_create_keyfile (); + if (keyfile_user) + _nm_keyfile_copy (keyfile, keyfile_user); + if (!keyfile_intern) + return keyfile; + + groups = g_key_file_get_groups (keyfile_intern, &ngroups); + if (!groups) + return keyfile; + + /* we must reverse the order of the connection settings so that we + * have lowest priority last. */ + _nm_config_sort_groups (groups, ngroups); + for (g = 0; groups[g]; g++) { + const char *group = groups[g]; + gs_strfreev char **keys = NULL; + gboolean is_intern, is_atomic = FALSE; + + keys = g_key_file_get_keys (keyfile_intern, group, NULL, NULL); + if (!keys) + continue; + + is_intern = g_str_has_prefix (group, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + if ( !is_intern + && g_key_file_has_key (keyfile_intern, group, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS, NULL)) { + /* the entire section is atomically overwritten by @keyfile_intern. */ + g_key_file_remove_group (keyfile, group, NULL); + is_atomic = TRUE; + } + + for (k = 0; keys[k]; k++) { + const char *key = keys[k]; + gs_free char *value = NULL; + + if (is_atomic && strcmp (key, NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS) == 0) + continue; + + if ( !is_intern && !is_atomic + && _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_WAS)) { + const char *key_base = &key[STRLEN (NM_CONFIG_KEYFILE_KEYPREFIX_WAS)]; + + if (!g_key_file_has_key (keyfile_intern, group, key_base, NULL)) + g_key_file_remove_key (keyfile, group, key_base, NULL); + continue; + } + if (!is_intern && !is_atomic && _HAS_PREFIX (key, NM_CONFIG_KEYFILE_KEYPREFIX_SET)) + continue; + + value = g_key_file_get_value (keyfile_intern, group, key, NULL); + g_key_file_set_value (keyfile, group, key, value); + } + } + return keyfile; +} + /************************************************************************/ static int _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy) { gboolean a_is_connection, b_is_connection; + gboolean a_is_intern, b_is_intern; const char *a = *pa; const char *b = *pb; + /* we sort intern groups to the end. */ + a_is_intern = g_str_has_prefix (a, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + b_is_intern = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN); + + if (a_is_intern && b_is_intern) + return 0; + if (a_is_intern) + return 1; + if (b_is_intern) + return -1; + /* 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); @@ -340,9 +543,12 @@ nm_config_data_log (const NMConfigData *self, for (g = 0; g < ngroups; g++) { const char *group = groups[g]; gs_strfreev char **keys = NULL; + gboolean is_atomic; + + is_atomic = nm_config_data_is_intern_atomic_group (self, group); _LOG (stream, prefix, ""); - _LOG (stream, prefix, "[%s]", group); + _LOG (stream, prefix, "[%s]%s", group, is_atomic && !stream ? " # atomic section" : ""); keys = g_key_file_get_keys (priv->keyfile, group, NULL, NULL); for (k = 0; keys && keys[k]; k++) { @@ -359,6 +565,492 @@ nm_config_data_log (const NMConfigData *self, /************************************************************************/ +const char *const * +nm_global_dns_config_get_searches (const NMGlobalDnsConfig *dns) +{ + g_return_val_if_fail (dns, NULL); + + return (const char *const *) dns->searches; +} + +const char *const * +nm_global_dns_config_get_options (const NMGlobalDnsConfig *dns) +{ + g_return_val_if_fail (dns, NULL); + + return (const char *const *) dns->options; +} + +guint +nm_global_dns_config_get_num_domains (const NMGlobalDnsConfig *dns) +{ + g_return_val_if_fail (dns, 0); + g_return_val_if_fail (dns->domains, 0); + + return g_hash_table_size (dns->domains); +} + +NMGlobalDnsDomain * +nm_global_dns_config_get_domain (const NMGlobalDnsConfig *dns, guint i) +{ + NMGlobalDnsDomain *domain; + + g_return_val_if_fail (dns, NULL); + g_return_val_if_fail (dns->domains, NULL); + g_return_val_if_fail (dns->domain_list, NULL); + g_return_val_if_fail (i < g_strv_length (dns->domain_list), NULL); + + domain = g_hash_table_lookup (dns->domains, dns->domain_list[i]); + g_return_val_if_fail (domain, NULL); + + return domain; +} + +NMGlobalDnsDomain *nm_global_dns_config_lookup_domain (const NMGlobalDnsConfig *dns, const char *name) +{ + g_return_val_if_fail (dns, NULL); + g_return_val_if_fail (dns->domains, NULL); + g_return_val_if_fail (name, NULL); + + return g_hash_table_lookup (dns->domains, name); +} + +const char * +nm_global_dns_domain_get_name (const NMGlobalDnsDomain *domain) +{ + g_return_val_if_fail (domain, NULL); + + return (const char *) domain->name; +} + +const char *const * +nm_global_dns_domain_get_servers (const NMGlobalDnsDomain *domain) +{ + g_return_val_if_fail (domain, NULL); + + return (const char *const *) domain->servers; +} + +const char *const * +nm_global_dns_domain_get_options (const NMGlobalDnsDomain *domain) +{ + g_return_val_if_fail (domain, NULL); + return (const char *const *) domain->options; +} + +gboolean +nm_global_dns_config_is_internal (const NMGlobalDnsConfig *dns) +{ + return dns->internal; +} + +gboolean +nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns) +{ + g_return_val_if_fail (dns, TRUE); + g_return_val_if_fail (dns->domains, TRUE); + + return (!dns->searches || g_strv_length (dns->searches) == 0) + && (!dns->options || g_strv_length (dns->options) == 0) + && g_hash_table_size (dns->domains) == 0; +} + +void +nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum) +{ + NMGlobalDnsDomain *domain; + GList *keys, *key; + guint i; + + g_return_if_fail (dns); + g_return_if_fail (dns->domains); + g_return_if_fail (sum); + + for (i = 0; dns->searches && dns->searches[i]; i++) + g_checksum_update (sum, (guchar *) dns->searches[i], strlen (dns->searches[i])); + for (i = 0; dns->options && dns->options[i]; i++) + g_checksum_update (sum, (guchar *) dns->options[i], strlen (dns->options[i])); + + keys = g_list_sort (g_hash_table_get_keys (dns->domains), (GCompareFunc) strcmp); + for (key = keys; key; key = g_list_next (key)) { + + domain = g_hash_table_lookup (dns->domains, key->data); + g_assert (domain != NULL); + g_checksum_update (sum, (guchar *) domain->name, strlen (domain->name)); + + for (i = 0; domain->servers && domain->servers[i]; i++) + g_checksum_update (sum, (guchar *) domain->servers[i], strlen (domain->servers[i])); + for (i = 0; domain->options && domain->options[i]; i++) + g_checksum_update (sum, (guchar *) domain->options[i], strlen (domain->options[i])); + } + g_list_free (keys); +} + +static void +global_dns_domain_free (NMGlobalDnsDomain *domain) +{ + if (domain) { + g_free (domain->name); + g_strfreev (domain->servers); + g_strfreev (domain->options); + g_free (domain); + } +} + +void +nm_global_dns_config_free (NMGlobalDnsConfig *conf) +{ + if (conf) { + g_strfreev (conf->searches); + g_strfreev (conf->options); + g_free (conf->domain_list); + g_hash_table_unref (conf->domains); + g_free (conf); + } +} + +NMGlobalDnsConfig * +nm_config_data_get_global_dns_config (const NMConfigData *self) +{ + g_return_val_if_fail (NM_IS_CONFIG_DATA (self), NULL); + + return NM_CONFIG_DATA_GET_PRIVATE (self)->global_dns; +} + +static void +global_dns_config_update_domain_list (NMGlobalDnsConfig *dns) +{ + guint length; + + g_free (dns->domain_list); + dns->domain_list = (char **) g_hash_table_get_keys_as_array (dns->domains, &length); +} + +static NMGlobalDnsConfig * +load_global_dns (GKeyFile *keyfile, gboolean internal) +{ + NMGlobalDnsConfig *conf; + char *group, *domain_prefix; + gs_strfreev char **groups = NULL; + int g, i, j, domain_prefix_len; + gboolean default_found = FALSE; + char **strv; + + group = internal + ? NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS + : NM_CONFIG_KEYFILE_GROUP_GLOBAL_DNS; + domain_prefix = internal + ? NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN + : NM_CONFIG_KEYFILE_GROUPPREFIX_GLOBAL_DNS_DOMAIN; + domain_prefix_len = strlen (domain_prefix); + + if (!nm_config_keyfile_has_global_dns_config (keyfile, internal)) + return NULL; + + conf = g_malloc0 (sizeof (NMGlobalDnsConfig)); + conf->domains = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) global_dns_domain_free); + + strv = g_key_file_get_string_list (keyfile, group, "searches", NULL, NULL); + if (strv) + conf->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + + strv = g_key_file_get_string_list (keyfile, group, "options", NULL, NULL); + if (strv) { + _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + for (i = 0, j = 0; strv[i]; i++) { + if (_nm_utils_dns_option_validate (strv[i], NULL, NULL, TRUE, NULL)) + strv[j++] = strv[i]; + else + g_free (strv[i]); + } + strv[j] = NULL; + conf->options = strv; + } + + groups = g_key_file_get_groups (keyfile, NULL); + for (g = 0; groups[g]; g++) { + char *name; + char **servers = NULL, **options = NULL; + NMGlobalDnsDomain *domain; + + if ( !g_str_has_prefix (groups[g], domain_prefix) + || !groups[g][domain_prefix_len]) + continue; + + strv = g_key_file_get_string_list (keyfile, groups[g], "servers", NULL, NULL); + if (strv) { + _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + for (i = 0, j = 0; strv[i]; i++) { + if ( nm_utils_ipaddr_valid (AF_INET, strv[i]) + || nm_utils_ipaddr_valid (AF_INET6, strv[i])) + strv[j++] = strv[i]; + else + g_free (strv[i]); + } + if (j) { + strv[j] = NULL; + servers = strv; + } + else + g_free (strv); + } + + if (!servers) + continue; + + strv = g_key_file_get_string_list (keyfile, groups[g], "options", NULL, NULL); + if (strv) + options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + + name = strdup (&groups[g][domain_prefix_len]); + domain = g_malloc0 (sizeof (NMGlobalDnsDomain)); + domain->name = name; + domain->servers = servers; + domain->options = options; + + g_hash_table_insert (conf->domains, strdup (name), domain); + + if (!strcmp (name, "*")) + default_found = TRUE; + } + + if (!default_found) { + nm_log_dbg (LOGD_CORE, "%s global DNS configuration is missing default domain, ignore it", + internal ? "internal" : "user"); + nm_global_dns_config_free (conf); + return NULL; + } + + conf->internal = internal; + global_dns_config_update_domain_list (conf); + return conf; +} + + +void +nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns, GValue *value) +{ + GVariantBuilder conf_builder, domains_builder, domain_builder; + NMGlobalDnsDomain *domain; + GHashTableIter iter; + + g_variant_builder_init (&conf_builder, G_VARIANT_TYPE ("a{sv}")); + if (!dns) + goto out; + + if (dns->searches) { + g_variant_builder_add (&conf_builder, "{sv}", "searches", + g_variant_new_strv ((const char *const *) dns->searches, -1)); + } + + if (dns->options) { + g_variant_builder_add (&conf_builder, "{sv}", "options", + g_variant_new_strv ((const char *const *) dns->options, -1)); + } + + g_variant_builder_init (&domains_builder, G_VARIANT_TYPE ("a{sv}")); + + g_hash_table_iter_init (&iter, dns->domains); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &domain)) { + + g_variant_builder_init (&domain_builder, G_VARIANT_TYPE ("a{sv}")); + + if (domain->servers) { + g_variant_builder_add (&domain_builder, "{sv}", "servers", + g_variant_new_strv ((const char *const *) domain->servers, -1)); + } + if (domain->options) { + g_variant_builder_add (&domain_builder, "{sv}", "options", + g_variant_new_strv ((const char *const *) domain->options, -1)); + } + + g_variant_builder_add (&domains_builder, "{sv}", domain->name, + g_variant_builder_end (&domain_builder)); + } + + g_variant_builder_add (&conf_builder, "{sv}", "domains", + g_variant_builder_end (&domains_builder)); +out: + g_value_take_variant (value, g_variant_builder_end (&conf_builder)); +} + +static NMGlobalDnsDomain * +global_dns_domain_from_dbus (char *name, GVariant *variant) +{ + NMGlobalDnsDomain *domain; + GVariantIter iter; + char **strv, *key; + GVariant *val; + int i, j; + + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}"))) + return NULL; + + domain = g_malloc0 (sizeof (NMGlobalDnsDomain)); + domain->name = g_strdup (name); + + g_variant_iter_init (&iter, variant); + while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) { + + if ( !g_strcmp0 (key, "servers") + && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) { + strv = g_variant_dup_strv (val, NULL); + _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + for (i = 0, j = 0; strv && strv[i]; i++) { + if ( nm_utils_ipaddr_valid (AF_INET, strv[i]) + || nm_utils_ipaddr_valid (AF_INET6, strv[i])) + strv[j++] = strv[i]; + else + g_free (strv[i]); + } + if (j) { + strv[j] = NULL; + domain->servers = strv; + } else + g_free (strv); + } else if ( !g_strcmp0 (key, "options") + && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) { + strv = g_variant_dup_strv (val, NULL); + domain->options = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + } + + g_variant_unref (val); + } + + /* At least one server is required */ + if (!domain->servers) { + global_dns_domain_free (domain); + return NULL; + } + + return domain; +} + +NMGlobalDnsConfig * +nm_global_dns_config_from_dbus (const GValue *value, GError **error) +{ + NMGlobalDnsConfig *dns_config; + GVariant *variant, *val; + GVariantIter iter; + char **strv, *key; + int i, j; + + if (!G_VALUE_HOLDS_VARIANT (value)) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "invalid value type"); + return NULL; + } + + variant = g_value_get_variant (value); + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("a{sv}"))) { + g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "invalid variant type"); + return NULL; + } + + dns_config = g_malloc0 (sizeof (NMGlobalDnsConfig)); + dns_config->domains = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) global_dns_domain_free); + + g_variant_iter_init (&iter, variant); + while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) { + + if ( !g_strcmp0 (key, "searches") + && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) { + strv = g_variant_dup_strv (val, NULL); + dns_config->searches = _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + } else if ( !g_strcmp0 (key, "options") + && g_variant_is_of_type (val, G_VARIANT_TYPE ("as"))) { + strv = g_variant_dup_strv (val, NULL); + _nm_utils_strv_cleanup (strv, TRUE, TRUE, TRUE); + + for (i = 0, j = 0; strv && strv[i]; i++) { + if (_nm_utils_dns_option_validate (strv[i], NULL, NULL, TRUE, NULL)) + strv[j++] = strv[i]; + else + g_free (strv[i]); + } + + if (strv) + strv[j] = NULL; + + dns_config->options = strv; + } else if ( !g_strcmp0 (key, "domains") + && g_variant_is_of_type (val, G_VARIANT_TYPE ("a{sv}"))) { + NMGlobalDnsDomain *domain; + GVariantIter domain_iter; + GVariant *v; + char *k; + + g_variant_iter_init (&domain_iter, val); + while (g_variant_iter_next (&domain_iter, "{&sv}", &k, &v)) { + if (k) { + domain = global_dns_domain_from_dbus (k, v); + if (domain) + g_hash_table_insert (dns_config->domains, strdup (k), domain); + } + g_variant_unref (v); + } + } + g_variant_unref (val); + } + + /* An empty value is valid and clears the internal configuration */ + if ( !nm_global_dns_config_is_empty (dns_config) + && !nm_global_dns_config_lookup_domain (dns_config, "*")) { + g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED, + "Global DNS configuration is missing the default domain"); + nm_global_dns_config_free (dns_config); + return NULL; + } + + global_dns_config_update_domain_list (dns_config); + return dns_config; +} + +static gboolean +global_dns_equal (NMGlobalDnsConfig *old, NMGlobalDnsConfig *new) +{ + NMGlobalDnsDomain *domain_old, *domain_new; + gpointer key, value_old, value_new; + GHashTableIter iter; + + if (old == new) + return TRUE; + + if (!old || !new) + return FALSE; + + if ( !_nm_utils_strv_equal (old->options, new->options) + || !_nm_utils_strv_equal (old->searches, new->searches)) + return FALSE; + + if ((!old->domains || !new->domains) && old->domains != new->domains) + return FALSE; + + if (g_hash_table_size (old->domains) != g_hash_table_size (new->domains)) + return FALSE; + + g_hash_table_iter_init (&iter, old->domains); + while (g_hash_table_iter_next (&iter, &key, &value_old)) { + value_new = g_hash_table_lookup (new->domains, key); + if (!value_new) + return FALSE; + + domain_old = value_old; + domain_new = value_new; + + if ( !_nm_utils_strv_equal (domain_old->options, domain_new->options) + || !_nm_utils_strv_equal (domain_old->servers, domain_new->servers)) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ + char * nm_config_data_get_connection_default (const NMConfigData *self, const char *property, @@ -408,10 +1100,10 @@ _get_connection_info_init (ConnectionInfo *connection_info, GKeyFile *keyfile, c /* 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->match_device.spec = nm_config_get_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); } @@ -483,8 +1175,11 @@ 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 (!_nm_keyfile_equals (priv_old->keyfile, priv_new->keyfile, TRUE)) - changes |= NM_CONFIG_CHANGE_VALUES; + if (!_nm_keyfile_equals (priv_old->keyfile_user, priv_new->keyfile_user, TRUE)) + changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_USER; + + if (!_nm_keyfile_equals (priv_old->keyfile_intern, priv_new->keyfile_intern, TRUE)) + changes |= NM_CONFIG_CHANGE_VALUES | NM_CONFIG_CHANGE_VALUES_INTERN; if ( g_strcmp0 (nm_config_data_get_config_main_file (old_data), nm_config_data_get_config_main_file (new_data)) != 0 || g_strcmp0 (nm_config_data_get_config_description (old_data), nm_config_data_get_config_description (new_data)) != 0) @@ -502,6 +1197,12 @@ nm_config_data_diff (NMConfigData *old_data, NMConfigData *new_data) if (g_strcmp0 (nm_config_data_get_dns_mode (old_data), nm_config_data_get_dns_mode (new_data))) changes |= NM_CONFIG_CHANGE_DNS_MODE; + if (g_strcmp0 (nm_config_data_get_rc_manager (old_data), nm_config_data_get_rc_manager (new_data))) + changes |= NM_CONFIG_CHANGE_RC_MANAGER; + + if (!global_dns_equal (priv_old->global_dns, priv_new->global_dns)) + changes |= NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG; + return changes; } @@ -554,10 +1255,21 @@ set_property (GObject *object, case PROP_CONFIG_DESCRIPTION: priv->config_description = g_value_dup_string (value); break; - case PROP_KEYFILE: - priv->keyfile = g_value_dup_boxed (value); - if (!priv->keyfile) - priv->keyfile = nm_config_create_keyfile (); + case PROP_KEYFILE_USER: + priv->keyfile_user = g_value_dup_boxed (value); + if ( priv->keyfile_user + && !_nm_keyfile_has_values (priv->keyfile_user)) { + g_key_file_unref (priv->keyfile_user); + priv->keyfile_user = NULL; + } + break; + case PROP_KEYFILE_INTERN: + priv->keyfile_intern = g_value_dup_boxed (value); + if ( priv->keyfile_intern + && !_nm_keyfile_has_values (priv->keyfile_intern)) { + g_key_file_unref (priv->keyfile_intern); + priv->keyfile_intern = NULL; + } break; case PROP_NO_AUTO_DEFAULT: { @@ -607,10 +1319,13 @@ finalize (GObject *gobject) g_strfreev (priv->no_auto_default.arr); g_free (priv->dns_mode); + g_free (priv->rc_manager); g_slist_free_full (priv->ignore_carrier, g_free); g_slist_free_full (priv->assume_ipv6ll_only, g_free); + nm_global_dns_config_free (priv->global_dns); + if (priv->connection_infos) { for (i = 0; priv->connection_infos[i].group_name; i++) { g_free (priv->connection_infos[i].group_name); @@ -620,8 +1335,14 @@ finalize (GObject *gobject) } g_key_file_unref (priv->keyfile); + if (priv->keyfile_user) + g_key_file_unref (priv->keyfile_user); + if (priv->keyfile_intern) + g_key_file_unref (priv->keyfile_intern); G_OBJECT_CLASS (nm_config_data_parent_class)->finalize (gobject); + + g_free (priv->value_cached); } static void @@ -636,6 +1357,8 @@ constructed (GObject *object) NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self); char *interval; + priv->keyfile = _merge_keyfiles (priv->keyfile_user, priv->keyfile_intern); + priv->connection_infos = _get_connection_infos (priv->keyfile); priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL)); @@ -650,11 +1373,16 @@ constructed (GObject *object) g_free (interval); priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL)); + priv->rc_manager = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "rc-manager", NULL)); + + priv->ignore_carrier = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "ignore-carrier", NULL); + priv->assume_ipv6ll_only = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "assume-ipv6ll-only", 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->no_auto_default.specs_config = nm_config_get_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL); - priv->no_auto_default.specs_config = nm_config_get_device_match_spec (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "no-auto-default", NULL); + priv->global_dns = load_global_dns (priv->keyfile_user, FALSE); + if (!priv->global_dns) + priv->global_dns = load_global_dns (priv->keyfile_intern, TRUE); G_OBJECT_CLASS (nm_config_data_parent_class)->constructed (object); } @@ -663,17 +1391,33 @@ NMConfigData * nm_config_data_new (const char *config_main_file, const char *config_description, const char *const*no_auto_default, - GKeyFile *keyfile) + GKeyFile *keyfile_user, + GKeyFile *keyfile_intern) { return g_object_new (NM_TYPE_CONFIG_DATA, NM_CONFIG_DATA_CONFIG_MAIN_FILE, config_main_file, NM_CONFIG_DATA_CONFIG_DESCRIPTION, config_description, - NM_CONFIG_DATA_KEYFILE, keyfile, + NM_CONFIG_DATA_KEYFILE_USER, keyfile_user, + NM_CONFIG_DATA_KEYFILE_INTERN, keyfile_intern, NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default, NULL); } NMConfigData * +nm_config_data_new_update_keyfile_intern (const NMConfigData *base, GKeyFile *keyfile_intern) +{ + NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (base); + + return g_object_new (NM_TYPE_CONFIG_DATA, + NM_CONFIG_DATA_CONFIG_MAIN_FILE, priv->config_main_file, + NM_CONFIG_DATA_CONFIG_DESCRIPTION, priv->config_description, + NM_CONFIG_DATA_KEYFILE_USER, priv->keyfile_user, /* the keyfile is unchanged. It's safe to share it. */ + NM_CONFIG_DATA_KEYFILE_INTERN, keyfile_intern, + NM_CONFIG_DATA_NO_AUTO_DEFAULT, priv->no_auto_default.arr, + NULL); +} + +NMConfigData * nm_config_data_new_update_no_auto_default (const NMConfigData *base, const char *const*no_auto_default) { @@ -682,7 +1426,8 @@ nm_config_data_new_update_no_auto_default (const NMConfigData *base, return g_object_new (NM_TYPE_CONFIG_DATA, NM_CONFIG_DATA_CONFIG_MAIN_FILE, priv->config_main_file, NM_CONFIG_DATA_CONFIG_DESCRIPTION, priv->config_description, - NM_CONFIG_DATA_KEYFILE, priv->keyfile, /* the keyfile is unchanged. It's safe to share it. */ + NM_CONFIG_DATA_KEYFILE_USER, priv->keyfile_user, /* the keyfile is unchanged. It's safe to share it. */ + NM_CONFIG_DATA_KEYFILE_INTERN, priv->keyfile_intern, NM_CONFIG_DATA_NO_AUTO_DEFAULT, no_auto_default, NULL); } @@ -717,8 +1462,16 @@ nm_config_data_class_init (NMConfigDataClass *config_class) G_PARAM_STATIC_STRINGS)); g_object_class_install_property - (object_class, PROP_KEYFILE, - g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE, "", "", + (object_class, PROP_KEYFILE_USER, + g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE_USER, "", "", + G_TYPE_KEY_FILE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (object_class, PROP_KEYFILE_INTERN, + g_param_spec_boxed (NM_CONFIG_DATA_KEYFILE_INTERN, "", "", G_TYPE_KEY_FILE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | |