summary refs log tree commit diff
path: root/src/nm-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nm-config.c')
-rw-r--r--src/nm-config.c675
1 files changed, 675 insertions, 0 deletions
diff --git a/src/nm-config.c b/src/nm-config.c
new file mode 100644
index 00000000..e8cb0864
--- /dev/null
+++ b/src/nm-config.c
@@ -0,0 +1,675 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include "nm-config.h"
+#include "nm-logging.h"
+#include "nm-utils.h"
+#include "nm-glib-compat.h"
+#include "nm-device.h"
+
+#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"
+
+typedef struct {
+	char *nm_conf_path;
+	char *config_dir;
+	char *config_description;
+	char *no_auto_default_file;
+	GKeyFile *keyfile;
+
+	char **plugins;
+	gboolean monitor_connection_files;
+	gboolean auth_polkit;
+	char *dhcp_client;
+	char *dns_mode;
+
+	char *log_level;
+	char *log_domains;
+
+	char *debug;
+
+	char *connectivity_uri;
+	gint connectivity_interval;
+	char *connectivity_response;
+
+	char **no_auto_default;
+	char **ignore_carrier;
+
+	gboolean configure_and_quit;
+} NMConfigPrivate;
+
+static NMConfig *singleton = NULL;
+
+G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
+
+#define NM_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG, NMConfigPrivate))
+
+/************************************************************************/
+
+static gboolean
+_get_bool_value (GKeyFile *keyfile,
+                 const char *section,
+                 const char *key,
+                 gboolean default_value)
+{
+	gboolean value = default_value;
+	char *str;
+
+	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;
+
+	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");
+		}
+	}
+
+	g_free (str);
+	return value;
+}
+
+/************************************************************************/
+
+const char *
+nm_config_get_path (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->nm_conf_path;
+}
+
+const char *
+nm_config_get_description (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->config_description;
+}
+
+const char **
+nm_config_get_plugins (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return (const char **) NM_CONFIG_GET_PRIVATE (config)->plugins;
+}
+
+gboolean
+nm_config_get_monitor_connection_files (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, FALSE);
+
+	return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files;
+}
+
+gboolean
+nm_config_get_auth_polkit (NMConfig *config)
+{
+	g_return_val_if_fail (NM_IS_CONFIG (config), NM_CONFIG_DEFAULT_AUTH_POLKIT);
+
+	return NM_CONFIG_GET_PRIVATE (config)->auth_polkit;
+}
+
+const char *
+nm_config_get_dhcp_client (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->dhcp_client;
+}
+
+const char *
+nm_config_get_dns_mode (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->dns_mode;
+}
+
+const char *
+nm_config_get_log_level (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->log_level;
+}
+
+const char *
+nm_config_get_log_domains (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->log_domains;
+}
+
+const char *
+nm_config_get_debug (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->debug;
+}
+
+const char *
+nm_config_get_connectivity_uri (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->connectivity_uri;
+}
+
+guint
+nm_config_get_connectivity_interval (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, 0);
+
+	/* We store interval as signed internally to track whether it's
+	 * set or not, but report as unsigned to callers.
+	 */
+	return MAX (NM_CONFIG_GET_PRIVATE (config)->connectivity_interval, 0);
+}
+
+const char *
+nm_config_get_connectivity_response (NMConfig *config)
+{
+	g_return_val_if_fail (config != NULL, NULL);
+
+	return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
+}
+
+gboolean
+nm_config_get_configure_and_quit (NMConfig *config)
+{
+	return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit;
+}
+
+char *
+nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+
+	return g_key_file_get_string (priv->keyfile, group, key, error);
+}
+
+gboolean
+nm_config_get_ignore_carrier (NMConfig *config, NMDevice *device)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	GSList *specs = NULL;
+	int i;
+	gboolean match;
+
+	if (!priv->ignore_carrier)
+		return FALSE;
+
+	for (i = 0; priv->ignore_carrier[i]; i++)
+		specs = g_slist_prepend (specs, priv->ignore_carrier[i]);
+
+	match = nm_device_spec_match_list (device, specs);
+
+	g_slist_free (specs);
+	return match;
+}
+
+/************************************************************************/
+
+static void
+merge_no_auto_default_state (NMConfig *config)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	GPtrArray *updated;
+	char **list;
+	int i, j;
+	char *data;
+
+	/* If the config already matches everything, we don't need to do anything else. */
+	if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*"))
+		return;
+
+	updated = g_ptr_array_new ();
+	if (priv->no_auto_default) {
+		for (i = 0; priv->no_auto_default[i]; i++)
+			g_ptr_array_add (updated, priv->no_auto_default[i]);
+		g_free (priv->no_auto_default);
+	}
+
+	if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) {
+		list = g_strsplit (data, "\n", -1);
+		for (i = 0; list[i]; i++) {
+			if (!*list[i])
+				continue;
+			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]);
+		}
+		g_free (list);
+		g_free (data);
+	}
+
+	g_ptr_array_add (updated, NULL);
+	priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE);
+}
+
+gboolean
+nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	GSList *specs = NULL;
+	int i;
+	gboolean match;
+
+	for (i = 0; priv->no_auto_default[i]; i++)
+		specs = g_slist_prepend (specs, priv->no_auto_default[i]);
+
+	match = nm_device_spec_match_list (device, specs);
+
+	g_slist_free (specs);
+	return !match;
+}
+
+void
+nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	char *current;
+	GString *updated;
+	GError *error = NULL;
+
+	if (!nm_config_get_ethernet_can_auto_default (config, device))
+		return;
+
+	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');
+	}
+
+	g_string_append (updated, nm_device_get_hw_address (device));
+	g_string_append_c (updated, '\n');
+
+	if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &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);
+
+	merge_no_auto_default_state (config);
+}
+
+/************************************************************************/
+
+static char *cli_config_path;
+static char *cli_config_dir;
+static char *cli_no_auto_default_file;
+static char *cli_plugins;
+static char *cli_connectivity_uri;
+static int cli_connectivity_interval = -1;
+static char *cli_connectivity_response;
+
+static GOptionEntry config_options[] = {
+	{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, 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") },
+
+	/* 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!") },
+	{NULL}
+};
+GOptionEntry *
+nm_config_get_options (void)
+{
+	return config_options;
+}
+
+/************************************************************************/
+
+static gboolean
+read_config (NMConfig *config, const char *path, GError **error)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	GKeyFile *kf;
+	char **groups, **keys;
+	gsize ngroups, nkeys;
+	int g, k;
+
+	if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) {
+		g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "file %s not found", path);
+		return FALSE;
+	}
+
+	nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path);
+
+	kf = g_key_file_new ();
+	g_key_file_set_list_separator (kf, ',');
+	if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) {
+		g_key_file_free (kf);
+		return FALSE;
+	}
+
+	/* Override the current settings with the new ones */
+	groups = g_key_file_get_groups (kf, &ngroups);
+	for (g = 0; groups[g]; g++) {
+		keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL);
+		if (!keys)
+			continue;
+		for (k = 0; keys[k]; k++) {
+			int len = strlen (keys[k]);
+			if (keys[k][len - 1] == '+') {
+				char *base_key = g_strndup (keys[k], len - 1);
+				const char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL);
+				const 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 (priv->keyfile, groups[g], base_key, combined);
+					g_free (combined);
+				} else
+					g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val);
+
+				g_free (base_key);
+				continue;
+			}
+
+			g_key_file_set_value (priv->keyfile, groups[g], keys[k],
+			                      g_key_file_get_value (kf, groups[g], keys[k], NULL));
+		}
+	}
+	g_key_file_free (kf);
+
+	return TRUE;
+}
+
+static gboolean
+find_base_config (NMConfig *config, GError **error)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+	GError *my_error = NULL;
+
+	/* Try a user-specified config file first */
+	if (cli_config_path) {
+		/* Bad user-specific config file path is a hard error */
+		if (read_config (config, cli_config_path, error)) {
+			priv->nm_conf_path = g_strdup (cli_config_path);
+			return TRUE;
+		} else
+			return FALSE;
+	}
+
+	/* Even though we prefer NetworkManager.conf, we need to check the
+	 * old nm-system-settings.conf first to preserve compat with older
+	 * setups.  In package managed systems dropping a NetworkManager.conf
+	 * onto the system would make NM use it instead of nm-system-settings.conf,
+	 * changing behavior during an upgrade.  We don't want that.
+	 */
+
+	/* Try deprecated nm-system-settings.conf first */
+	if (read_config (config, NM_OLD_SYSTEM_CONF_FILE, &my_error)) {
+		priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
+		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,
+		             my_error->message);
+	}
+	g_clear_error (&my_error);
+
+	/* Try the standard config file location next */
+	if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) {
+		priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_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,
+		             my_error->message);
+		g_propagate_error (error, my_error);
+		return FALSE;
+	}
+	g_clear_error (&my_error);
+
+	/* If for some reason no config file exists, use the default
+	 * config file path.
+	 */
+	priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
+	nm_log_info (LOGD_CORE, "No config file found or given; using %s\n",
+	             NM_DEFAULT_SYSTEM_CONF_FILE);
+	return TRUE;
+}
+
+/************************************************************************/
+
+NMConfig *
+nm_config_get (void)
+{
+	g_assert (singleton);
+	return singleton;
+}
+
+static int
+sort_asciibetically (gconstpointer a, gconstpointer b)
+{
+	const char *s1 = *(const char **)a;
+	const char *s2 = *(const char **)b;
+
+	return strcmp (s1, s2);
+}
+
+/* call this function only once! */
+NMConfig *
+nm_config_new (GError **error)
+{
+	NMConfigPrivate *priv = NULL;
+	GFile *dir;
+	GFileEnumerator *direnum;
+	GFileInfo *info;
+	GPtrArray *confs;
+	const char *name;
+	int i;
+	GString *config_description;
+
+	g_assert (!singleton);
+	singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL));
+	priv = NM_CONFIG_GET_PRIVATE (singleton);
+
+	/* First read the base config file */
+	if (!find_base_config (singleton, error)) {
+		g_object_unref (singleton);
+		singleton = NULL;
+		return NULL;
+	}
+
+	/* Now read the overrides in the config dir */
+	if (cli_config_dir)
+		priv->config_dir = g_strdup (cli_config_dir);
+	else
+		priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR);
+
+	confs = g_ptr_array_new_with_free_func (g_free);
+	config_description = g_string_new (priv->nm_conf_path);
+	dir = g_file_new_for_path (priv->config_dir);
+	direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL);
+	if (direnum) {
+		while ((info = g_file_enumerator_next_file (direnum, NULL, NULL))) {
+			name = g_file_info_get_name (info);
+			if (g_str_has_suffix (name, ".conf")) {
+				g_ptr_array_add (confs, g_build_filename (priv->config_dir, name, NULL));
+				if (confs->len == 1)
+					g_string_append (config_description, " and conf.d: ");
+				else
+					g_string_append (config_description, ", ");
+				g_string_append (config_description, name);
+			}
+			g_object_unref (info);
+		}
+		g_object_unref (direnum);
+	}
+	g_object_unref (dir);
+
+	g_ptr_array_sort (confs, sort_asciibetically);
+	priv->config_description = g_string_free (config_description, FALSE);
+	for (i = 0; i < confs->len; i++) {
+		if (!read_config (singleton, confs->pdata[i], error)) {
+			g_object_unref (singleton);
+			singleton = NULL;
+			break;
+		}
+	}
+	g_ptr_array_unref (confs);
+	if (!singleton)
+		return FALSE;
+
+	/* Handle no-auto-default key and state file */
+	priv->no_auto_default = g_key_file_get_string_list (priv->keyfile, "main", "no-auto-default", NULL, NULL);
+	if (cli_no_auto_default_file)
+		priv->no_auto_default_file = g_strdup (cli_no_auto_default_file);
+	else
+		priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE);
+	merge_no_auto_default_state (singleton);
+
+	/* Now let command-line options override the config files, and fill in priv. */
+	if (cli_plugins && cli_plugins[0])
+		g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins);
+	priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL);
+	if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
+		priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
+
+	priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE);
+
+	priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
+
+	priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
+	priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
+
+	priv->log_level = g_key_file_get_value (priv->keyfile, "logging", "level", NULL);
+	priv->log_domains = g_key_file_get_value (priv->keyfile, "logging", "domains", NULL);
+
+	priv->debug = g_key_file_get_value (priv->keyfile, "main", "debug", NULL);
+
+	if (cli_connectivity_uri && cli_connectivity_uri[0])
+		g_key_file_set_value (priv->keyfile, "connectivity", "uri", cli_connectivity_uri);
+	priv->connectivity_uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL);
+
+	if (cli_connectivity_interval >= 0)
+		g_key_file_set_integer (priv->keyfile, "connectivity", "interval", cli_connectivity_interval);
+	priv->connectivity_interval = g_key_file_get_integer (priv->keyfile, "connectivity", "interval", NULL);
+
+	if (cli_connectivity_response && cli_connectivity_response[0])
+		g_key_file_set_value (priv->keyfile, "connectivity", "response", cli_connectivity_response);
+	priv->connectivity_response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL);
+
+	priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
+
+	priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE);
+
+	return singleton;
+}
+
+static void
+nm_config_init (NMConfig *config)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
+
+	priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT;
+
+	priv->keyfile = g_key_file_new ();
+	g_key_file_set_list_separator (priv->keyfile, ',');
+
+	priv->connectivity_interval = -1;
+}
+
+static void
+finalize (GObject *gobject)
+{
+	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
+
+	g_free (priv->nm_conf_path);
+	g_free (priv->config_dir);
+	g_free (priv->config_description);
+	g_free (priv->no_auto_default_file);
+	g_clear_pointer (&priv->keyfile, g_key_file_unref);
+	g_strfreev (priv->plugins);
+	g_free (priv->dhcp_client);
+	g_free (priv->dns_mode);
+	g_free (priv->log_level);
+	g_free (priv->log_domains);
+	g_free (priv->debug);
+	g_free (priv->connectivity_uri);
+	g_free (priv->connectivity_response);
+	g_strfreev (priv->no_auto_default);
+	g_strfreev (priv->ignore_carrier);
+
+	singleton = NULL;
+
+	g_clear_pointer (&cli_config_path, g_free);
+	g_clear_pointer (&cli_config_dir, g_free);
+	g_clear_pointer (&cli_no_auto_default_file, g_free);
+	g_clear_pointer (&cli_plugins, g_free);
+	g_clear_pointer (&cli_connectivity_uri, g_free);
+	g_clear_pointer (&cli_connectivity_response, g_free);
+
+	G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject);
+}
+
+
+static void
+nm_config_class_init (NMConfigClass *config_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
+
+	g_type_class_add_private (config_class, sizeof (NMConfigPrivate));
+	object_class->finalize = finalize;
+}
+