manager: export DNS global configuration D-Bus property
authorBeniamino Galvani <bgalvani@redhat.com>
Fri, 3 Jul 2015 09:06:39 +0000 (11:06 +0200)
committerBeniamino Galvani <bgalvani@redhat.com>
Thu, 1 Oct 2015 07:05:08 +0000 (09:05 +0200)
introspection/nm-manager.xml
policy/org.freedesktop.NetworkManager.policy.in.in
src/nm-auth-utils.h
src/nm-config-data.c
src/nm-config-data.h
src/nm-config.c
src/nm-config.h
src/nm-manager.c
src/nm-manager.h

index 8548072..88f477d 100644 (file)
       </tp:docstring>
     </property>
 
+    <property name="GlobalDnsConfiguration" type="a{sv}" access="readwrite">
+      <tp:docstring>
+        Dictionary of global DNS settings where the key is one of
+        "searches", "options" and "domains".  The values for the
+        "searches" and "options" keys are string arrays describing the
+        list of search domains and resolver options, respectively.
+        The value of the "domains" key is a second-level dictionary,
+        where each key is a domain name, and each key's value is a
+        third-level dictionary with the keys "servers" and
+        "options". "servers" is a string array of DNS servers,
+        "options" is a string array of domain-specific options.
+      </tp:docstring>
+    </property>
+
     <signal name="PropertiesChanged">
       <tp:docstring>
         NetworkManager's properties changed.
index daefd80..4df6d7e 100644 (file)
     </defaults>
   </action>
 
+  <action id="org.freedesktop.NetworkManager.settings.modify.global-dns">
+    <_description>Modify persistent global DNS configuration</_description>
+    <_message>System policy prevents modification of the persistent global DNS configuration</_message>
+    <defaults>
+      <allow_any>auth_admin_keep</allow_any>
+      <allow_inactive>auth_admin_keep</allow_inactive>
+      <allow_active>auth_admin_keep</allow_active>
+    </defaults>
+  </action>
+
 </policyconfig>
 
index c6cad52..6eb0fda 100644 (file)
@@ -35,6 +35,7 @@
 #define NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM     "org.freedesktop.NetworkManager.settings.modify.system"
 #define NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN        "org.freedesktop.NetworkManager.settings.modify.own"
 #define NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME   "org.freedesktop.NetworkManager.settings.modify.hostname"
+#define NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS "org.freedesktop.NetworkManager.settings.modify.global-dns"
 
 
 typedef struct NMAuthChain NMAuthChain;
index cfca59a..1d0d362 100644 (file)
@@ -812,6 +812,188 @@ load_global_dns (GKeyFile *keyfile, gboolean internal)
        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)
 {
index 41a7cd5..4c0be72 100644 (file)
@@ -153,6 +153,9 @@ gboolean nm_global_dns_config_is_empty (const NMGlobalDnsConfig *dns);
 void nm_global_dns_config_update_checksum (const NMGlobalDnsConfig *dns, GChecksum *sum);
 void nm_global_dns_config_free (NMGlobalDnsConfig *conf);
 
+NMGlobalDnsConfig *nm_global_dns_config_from_dbus (const GValue *value, GError **error);
+void nm_global_dns_config_to_dbus (const NMGlobalDnsConfig *dns_config, GValue *value);
+
 /* private accessors */
 GKeyFile *_nm_config_data_get_keyfile (const NMConfigData *self);
 GKeyFile *_nm_config_data_get_keyfile_user (const NMConfigData *self);
index bf5811d..9edfac1 100644 (file)
@@ -1431,6 +1431,73 @@ nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *group, con
 
 /************************************************************************/
 
+gboolean
+nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error)
+{
+       NMConfigPrivate *priv;
+       GKeyFile *keyfile;
+       char **groups;
+       const NMGlobalDnsConfig *old_global_dns;
+       guint i;
+
+       g_return_val_if_fail (NM_IS_CONFIG (self), FALSE);
+
+       priv = NM_CONFIG_GET_PRIVATE (self);
+       g_return_val_if_fail (priv->config_data, FALSE);
+
+       old_global_dns = nm_config_data_get_global_dns_config (priv->config_data);
+       if (old_global_dns && !nm_global_dns_config_is_internal (old_global_dns)) {
+               g_set_error_literal (error, 1, 0,
+                                    "Global DNS configuration already set via configuration file");
+               return FALSE;
+       }
+
+       keyfile = nm_config_data_clone_keyfile_intern (priv->config_data);
+
+       /* Remove existing groups */
+       g_key_file_remove_group (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NULL);
+       groups = g_key_file_get_groups (keyfile, NULL);
+       for (i = 0; groups[i]; i++) {
+               if (g_str_has_prefix (groups[i], NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN))
+                       g_key_file_remove_group (keyfile, groups[i], NULL);
+       }
+       g_strfreev (groups);
+
+       /* An empty configuration removes everything from internal configuration file */
+       if (nm_global_dns_config_is_empty (global_dns))
+               goto done;
+
+       /* Set new values */
+       g_key_file_set_string (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS, NM_CONFIG_KEYFILE_KEY_GLOBAL_DNS_ENABLE, "yes");
+
+       nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS,
+                                          "searches", nm_global_dns_config_get_searches (global_dns),
+                                          -1);
+
+       nm_config_keyfile_set_string_list (keyfile, NM_CONFIG_KEYFILE_GROUP_INTERN_GLOBAL_DNS,
+                                          "options", nm_global_dns_config_get_options (global_dns),
+                                          -1);
+
+       for (i = 0; i < nm_global_dns_config_get_num_domains (global_dns); i++) {
+               NMGlobalDnsDomain *domain = nm_global_dns_config_get_domain (global_dns, i);
+               gs_free char *group_name;
+
+               group_name = g_strdup_printf (NM_CONFIG_KEYFILE_GROUPPREFIX_INTERN_GLOBAL_DNS_DOMAIN "%s",
+                                             nm_global_dns_domain_get_name (domain));
+
+               nm_config_keyfile_set_string_list (keyfile, group_name, "servers",
+                                                  nm_global_dns_domain_get_servers (domain), -1);
+               nm_config_keyfile_set_string_list (keyfile, group_name, "options",
+                                                  nm_global_dns_domain_get_options (domain), -1);
+       }
+
+done:
+       nm_config_set_values (self, keyfile, TRUE, FALSE);
+       g_key_file_unref (keyfile);
+
+       return TRUE;
+}
+
 /**
  * nm_config_set_values:
  * @self: the NMConfig instance
index c52b3dc..0e96c3e 100644 (file)
@@ -147,6 +147,8 @@ GSList *nm_config_get_device_match_spec (const GKeyFile *keyfile, const char *gr
 
 void _nm_config_sort_groups (char **groups, gsize ngroups);
 
+gboolean nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns, GError **error);
+
 G_END_DECLS
 
 #endif /* __NETWORKMANAGER_CONFIG_H__ */
index 107039f..1253f99 100644 (file)
@@ -181,6 +181,7 @@ enum {
        PROP_ACTIVATING_CONNECTION,
        PROP_DEVICES,
        PROP_METERED,
+       PROP_GLOBAL_DNS_CONFIGURATION,
 
        /* Not exported */
        PROP_HOSTNAME,
@@ -424,6 +425,9 @@ _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeF
                      NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data),
                      NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data),
                      NULL);
+
+       if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
+               g_object_notify (G_OBJECT (self), NM_MANAGER_GLOBAL_DNS_CONFIGURATION);
 }
 
 /************************************************************************/
@@ -4430,7 +4434,6 @@ typedef struct {
        char *audit_prop_value;
        GType interface_type;
        const char *glib_propname;
-       gboolean set_enable;
 } PropertyFilterData;
 
 static void
@@ -4453,9 +4456,12 @@ prop_set_auth_done_cb (NMAuthChain *chain,
        PropertyFilterData *pfd = user_data;
        NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (pfd->self);
        NMAuthCallResult result;
-       GDBusMessage *reply;
+       GDBusMessage *reply = NULL;
        const char *error_message;
        NMExportedObject *object;
+       const NMGlobalDnsConfig *global_dns;
+       gs_unref_variant GVariant *value = NULL;
+       GVariant *args;
 
        priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
        result = nm_auth_chain_get_result (chain, pfd->permission);
@@ -4485,9 +4491,29 @@ prop_set_auth_done_cb (NMAuthChain *chain,
                goto done;
        }
 
-       /* ... but set the property on the @object itself. It would be correct to set the property
-        * on the skeleton interface, but as it is now, the result is the same. */
-       g_object_set (object, pfd->glib_propname, pfd->set_enable, NULL);
+       args = g_dbus_message_get_body (pfd->message);
+       g_variant_get (args, "(&s&sv)", NULL, NULL, &value);
+       g_assert (pfd->glib_propname);
+
+       if (!strcmp (pfd->glib_propname, NM_MANAGER_GLOBAL_DNS_CONFIGURATION)) {
+               g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("a{sv}")));
+               global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
+
+               if (global_dns && !nm_global_dns_config_is_internal (global_dns)) {
+                       reply = g_dbus_message_new_method_error (pfd->message,
+                                                                NM_PERM_DENIED_ERROR,
+                                                                (error_message = "Global DNS configuration already set via configuration file"));
+                       goto done;
+               }
+               /* ... but set the property on the @object itself. It would be correct to set the property
+                * on the skeleton interface, but as it is now, the result is the same. */
+               g_object_set (object, pfd->glib_propname, value, NULL);
+       } else {
+               g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
+               /* the same here */
+               g_object_set (object, pfd->glib_propname, g_variant_get_boolean (value), NULL);
+       }
+
        reply = g_dbus_message_new_method_reply (pfd->message);
        g_dbus_message_set_body (reply, g_variant_new_tuple (NULL, 0));
        error_message = NULL;
@@ -4552,14 +4578,15 @@ prop_filter (GDBusConnection *connection,
              gpointer user_data)
 {
        gs_unref_object NMManager *self = NULL;
-       GVariant *args, *value = NULL;
+       GVariant *args;
        const char *propiface = NULL;
        const char *propname = NULL;
        const char *glib_propname = NULL, *permission = NULL;
        const char *audit_op = NULL;
-       gboolean set_enable;
        GType interface_type = G_TYPE_INVALID;
        PropertyFilterData *pfd;
+       const GVariantType *expected_type = G_VARIANT_TYPE_BOOLEAN;
+       gs_unref_variant GVariant *value = NULL;
 
        self = g_weak_ref_get (user_data);
        if (!self)
@@ -4576,17 +4603,10 @@ prop_filter (GDBusConnection *connection,
            || g_strcmp0 (g_dbus_message_get_member (message), "Set") != 0)
                return message;
 
-       /* Only filter calls with correct arguments (all filtered properties are boolean) */
        args = g_dbus_message_get_body (message);
        if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("(ssv)")))
                return message;
        g_variant_get (args, "(&s&sv)", &propiface, &propname, &value);
-       if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
-               g_variant_unref (value);
-               return message;
-       }
-       set_enable = g_variant_get_boolean (value);
-       g_variant_unref (value);
 
        /* Only filter calls to filtered properties, on existing objects */
        if (!strcmp (propiface, NM_DBUS_INTERFACE)) {
@@ -4602,6 +4622,11 @@ prop_filter (GDBusConnection *connection,
                        glib_propname = NM_MANAGER_WIMAX_ENABLED;
                        permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIMAX;
                        audit_op = NM_AUDIT_OP_RADIO_CONTROL;
+               } else if (!strcmp (propname, "GlobalDnsConfiguration")) {
+                       glib_propname = NM_MANAGER_GLOBAL_DNS_CONFIGURATION;
+                       permission = NM_AUTH_PERMISSION_SETTINGS_MODIFY_GLOBAL_DNS;
+                       audit_op = NM_AUDIT_OP_NET_CONTROL;
+                       expected_type = G_VARIANT_TYPE ("a{sv}");
                } else
                        return message;
                interface_type = NMDBUS_TYPE_MANAGER_SKELETON;
@@ -4620,6 +4645,9 @@ prop_filter (GDBusConnection *connection,
        } else
                return message;
 
+       if (!g_variant_is_of_type (value, expected_type))
+               return message;
+
        /* This filter function is called from a gdbus worker thread which we can't
         * make other D-Bus calls from. In particular, we cannot call
         * org.freedesktop.DBus.GetConnectionUnixUser to find the remote UID.
@@ -4632,9 +4660,13 @@ prop_filter (GDBusConnection *connection,
        pfd->permission = permission;
        pfd->interface_type = interface_type;
        pfd->glib_propname = glib_propname;
-       pfd->set_enable = set_enable;
        pfd->audit_op = audit_op;
-       pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname, pfd->set_enable);
+       if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) {
+               pfd->audit_prop_value = g_strdup_printf ("%s:%d", pfd->glib_propname,
+                                                        g_variant_get_boolean (value));
+       } else
+               pfd->audit_prop_value = g_strdup (pfd->glib_propname);
+
        g_idle_add (do_set_property_check, pfd);
 
        return NULL;
@@ -5028,6 +5060,8 @@ get_property (GObject *object, guint prop_id,
 {
        NMManager *self = NM_MANAGER (object);
        NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+       NMConfigData *config_data;
+       const NMGlobalDnsConfig *dns_config;
        const char *type;
 
        switch (prop_id) {
@@ -5097,6 +5131,11 @@ get_property (GObject *object, guint prop_id,
        case PROP_METERED:
                g_value_set_uint (value, priv->metered);
                break;
+       case PROP_GLOBAL_DNS_CONFIGURATION:
+               config_data = nm_config_get_data (priv->config);
+               dns_config = nm_config_data_get_global_dns_config (config_data);
+               nm_global_dns_config_to_dbus (dns_config, value);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -5109,6 +5148,8 @@ set_property (GObject *object, guint prop_id,
 {
        NMManager *self = NM_MANAGER (object);
        NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+       NMGlobalDnsConfig *dns_config;
+       GError *error = NULL;
 
        switch (prop_id) {
        case PROP_NETWORKING_ENABLED:
@@ -5128,6 +5169,18 @@ set_property (GObject *object, guint prop_id,
        case PROP_WIMAX_ENABLED:
                /* WIMAX is depreacted. This does nothing. */
                break;
+       case PROP_GLOBAL_DNS_CONFIGURATION:
+               dns_config = nm_global_dns_config_from_dbus (value, &error);
+               if (!error)
+                       nm_config_set_global_dns (priv->config, dns_config, &error);
+
+               nm_global_dns_config_free (dns_config);
+
+               if (error) {
+                       nm_log_dbg (LOGD_CORE, "set global DNS failed with error: %s", error->message);
+                       g_error_free (error);
+               }
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -5395,6 +5448,21 @@ nm_manager_class_init (NMManagerClass *manager_class)
                                    G_PARAM_READABLE |
                                    G_PARAM_STATIC_STRINGS));
 
+       /**
+        * NMManager:global-dns-configuration:
+        *
+        * The global DNS configuration.
+        *
+        * Since: 1.2
+        **/
+       g_object_class_install_property
+               (object_class, PROP_GLOBAL_DNS_CONFIGURATION,
+                g_param_spec_variant (NM_MANAGER_GLOBAL_DNS_CONFIGURATION, "", "",
+                                      G_VARIANT_TYPE ("a{sv}"),
+                                      NULL,
+                                      G_PARAM_READWRITE |
+                                      G_PARAM_STATIC_STRINGS));
+
        /* signals */
        signals[DEVICE_ADDED] =
                g_signal_new ("device-added",
index 39123f8..7807f45 100644 (file)
@@ -49,6 +49,7 @@
 #define NM_MANAGER_ACTIVATING_CONNECTION "activating-connection"
 #define NM_MANAGER_DEVICES "devices"
 #define NM_MANAGER_METERED "metered"
+#define NM_MANAGER_GLOBAL_DNS_CONFIGURATION "global-dns-configuration"
 
 /* Not exported */
 #define NM_MANAGER_HOSTNAME "hostname"