1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2 of the License, or
7    	 * (at your option) any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2011 Red Hat, Inc.
19   	 * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
20   	 */
21   	
22   	#include <config.h>
23   	#include <string.h>
24   	#include <stdio.h>
25   	
26   	#include "nm-config.h"
27   	#include "nm-logging.h"
28   	#include "nm-utils.h"
29   	#include "nm-glib-compat.h"
30   	
31   	#include <gio/gio.h>
32   	#include <glib/gi18n.h>
33   	
34   	#define NM_DEFAULT_SYSTEM_CONF_FILE    NMCONFDIR "/NetworkManager.conf"
35   	#define NM_DEFAULT_SYSTEM_CONF_DIR     NMCONFDIR "/conf.d"
36   	#define NM_OLD_SYSTEM_CONF_FILE        NMCONFDIR "/nm-system-settings.conf"
37   	#define NM_NO_AUTO_DEFAULT_STATE_FILE  NMSTATEDIR "/no-auto-default.state"
38   	
39   	typedef struct {
40   		char *nm_conf_path;
41   		char *config_dir;
42   		char *config_description;
43   		char *no_auto_default_file;
44   		GKeyFile *keyfile;
45   	
46   		char **plugins;
47   		gboolean monitor_connection_files;
48   		char *dhcp_client;
49   		char *dns_mode;
50   	
51   		char *log_level;
52   		char *log_domains;
53   	
54   		char *connectivity_uri;
55   		gint connectivity_interval;
56   		char *connectivity_response;
57   	
58   		char **no_auto_default;
59   		char **ignore_carrier;
60   	} NMConfigPrivate;
61   	
62   	static NMConfig *singleton = NULL;
63   	
64   	G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
65   	
66   	#define NM_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG, NMConfigPrivate))
67   	
68   	/************************************************************************/
69   	
70   	const char *
71   	nm_config_get_path (NMConfig *config)
72   	{
73   		g_return_val_if_fail (config != NULL, NULL);
74   	
75   		return NM_CONFIG_GET_PRIVATE (config)->nm_conf_path;
76   	}
77   	
78   	const char *
79   	nm_config_get_description (NMConfig *config)
80   	{
81   		g_return_val_if_fail (config != NULL, NULL);
82   	
83   		return NM_CONFIG_GET_PRIVATE (config)->config_description;
84   	}
85   	
86   	const char **
87   	nm_config_get_plugins (NMConfig *config)
88   	{
89   		g_return_val_if_fail (config != NULL, NULL);
90   	
91   		return (const char **) NM_CONFIG_GET_PRIVATE (config)->plugins;
92   	}
93   	
94   	gboolean
95   	nm_config_get_monitor_connection_files (NMConfig *config)
96   	{
97   		g_return_val_if_fail (config != NULL, FALSE);
98   	
99   		return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files;
100  	}
101  	
102  	const char *
103  	nm_config_get_dhcp_client (NMConfig *config)
104  	{
105  		g_return_val_if_fail (config != NULL, NULL);
106  	
107  		return NM_CONFIG_GET_PRIVATE (config)->dhcp_client;
108  	}
109  	
110  	const char *
111  	nm_config_get_dns_mode (NMConfig *config)
112  	{
113  		g_return_val_if_fail (config != NULL, NULL);
114  	
115  		return NM_CONFIG_GET_PRIVATE (config)->dns_mode;
116  	}
117  	
118  	const char *
119  	nm_config_get_log_level (NMConfig *config)
120  	{
121  		g_return_val_if_fail (config != NULL, NULL);
122  	
123  		return NM_CONFIG_GET_PRIVATE (config)->log_level;
124  	}
125  	
126  	const char *
127  	nm_config_get_log_domains (NMConfig *config)
128  	{
129  		g_return_val_if_fail (config != NULL, NULL);
130  	
131  		return NM_CONFIG_GET_PRIVATE (config)->log_domains;
132  	}
133  	
134  	const char *
135  	nm_config_get_connectivity_uri (NMConfig *config)
136  	{
137  		g_return_val_if_fail (config != NULL, NULL);
138  	
139  		return NM_CONFIG_GET_PRIVATE (config)->connectivity_uri;
140  	}
141  	
142  	const guint
143  	nm_config_get_connectivity_interval (NMConfig *config)
144  	{
145  		g_return_val_if_fail (config != NULL, 0);
146  	
147  		/* We store interval as signed internally to track whether it's
148  		 * set or not, but report as unsigned to callers.
149  		 */
(1) Event result_independent_of_operands: "((NMConfigPrivate *)g_type_instance_get_private((GTypeInstance *)config, nm_config_get_type()))->connectivity_interval > 2147483647 /* (gint32)2147483647 */" is always false regardless of the values of its operands. This occurs as the logical first operand of '?:'.
150  		return CLAMP (NM_CONFIG_GET_PRIVATE (config)->connectivity_interval, 0, G_MAXINT32);
151  	}
152  	
153  	const char *
154  	nm_config_get_connectivity_response (NMConfig *config)
155  	{
156  		g_return_val_if_fail (config != NULL, NULL);
157  	
158  		return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
159  	}
160  	
161  	char *
162  	nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
163  	{
164  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
165  	
166  		return g_key_file_get_string (priv->keyfile, group, key, error);
167  	}
168  	
169  	gboolean
170  	nm_config_get_ignore_carrier (NMConfig *config, NMConfigDevice *device)
171  	{
172  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
173  	
174  		return nm_config_device_spec_match_list (device, (const char **) priv->ignore_carrier);
175  	}
176  	
177  	/************************************************************************/
178  	
179  	static void
180  	merge_no_auto_default_state (NMConfig *config)
181  	{
182  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
183  		GPtrArray *updated;
184  		char **list;
185  		int i, j;
186  		char *data;
187  	
188  		/* If the config already matches everything, we don't need to do anything else. */
189  		if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*"))
190  			return;
191  	
192  		updated = g_ptr_array_new ();
193  		if (priv->no_auto_default) {
194  			for (i = 0; priv->no_auto_default[i]; i++)
195  				g_ptr_array_add (updated, priv->no_auto_default[i]);
196  			g_free (priv->no_auto_default);
197  		}
198  	
199  		if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) {
200  			list = g_strsplit (data, "\n", -1);
201  			for (i = 0; list[i]; i++) {
202  				if (!*list[i])
203  					continue;
204  				for (j = 0; j < updated->len; j++) {
205  					if (!strcmp (list[i], updated->pdata[j]))
206  						break;
207  				}
208  				if (j == updated->len)
209  					g_ptr_array_add (updated, list[i]);
210  			}
211  			g_free (list);
212  			g_free (data);
213  		}
214  	
215  		g_ptr_array_add (updated, NULL);
216  		priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE);
217  	}
218  	
219  	gboolean
220  	nm_config_get_ethernet_can_auto_default (NMConfig *config, NMConfigDevice *device)
221  	{
222  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
223  	
224  		return !nm_config_device_spec_match_list (device, (const char **) priv->no_auto_default);
225  	}
226  	
227  	void
228  	nm_config_set_ethernet_no_auto_default (NMConfig *config, NMConfigDevice *device)
229  	{
230  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
231  		char *current;
232  		GString *updated;
233  		GError *error = NULL;
234  	
235  		if (!nm_config_get_ethernet_can_auto_default (config, device))
236  			return;
237  	
238  		updated = g_string_new (NULL);
239  		if (g_file_get_contents (priv->no_auto_default_file, &current, NULL, NULL)) {
240  			g_string_append (updated, current);
241  			g_free (current);
242  			if (updated->str[updated->len - 1] != '\n')
243  				g_string_append_c (updated, '\n');
244  		}
245  	
246  		g_string_append (updated, nm_config_device_get_hwaddr (device));
247  		g_string_append_c (updated, '\n');
248  	
249  		if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) {
250  			nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s",
251  			             error->message);
252  			g_error_free (error);
253  		}
254  	
255  		g_string_free (updated, TRUE);
256  	
257  		merge_no_auto_default_state (config);
258  	}
259  	
260  	/************************************************************************/
261  	
262  	static char *cli_config_path;
263  	static char *cli_config_dir;
264  	static char *cli_no_auto_default_file;
265  	static char *cli_plugins;
266  	static char *cli_connectivity_uri;
267  	static int cli_connectivity_interval = -1;
268  	static char *cli_connectivity_response;
269  	
270  	static GOptionEntry config_options[] = {
271  		{ "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, N_("Config file location"), N_("/path/to/config.file") },
272  		{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_dir, N_("Config directory location"), N_("/path/to/config/dir") },
273  		{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli_no_auto_default_file, "no-auto-default.state location", NULL },
274  		{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli_plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") },
275  	
276  		/* These three are hidden for now, and should eventually just go away. */
277  		{ "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" },
278  		{ "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli_connectivity_interval, N_("The interval between connectivity checks (in seconds)"), "60" },
279  		{ "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_response, N_("The expected start of the response"), N_("Bingo!") },
280  		{NULL}
281  	};
282  	GOptionEntry *
283  	nm_config_get_options (void)
284  	{
285  		return config_options;
286  	}
287  	
288  	/************************************************************************/
289  	
290  	static gboolean
291  	read_config (NMConfig *config, const char *path, GError **error)
292  	{
293  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
294  		GKeyFile *kf;
295  		char **groups, **keys;
296  		gsize ngroups, nkeys;
297  		int g, k;
298  	
299  		if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) {
300  			g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "file %s not found", path);
301  			return FALSE;
302  		}
303  	
304  		nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path);
305  	
306  		kf = g_key_file_new ();
307  		g_key_file_set_list_separator (kf, ',');
308  		if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) {
309  			g_key_file_free (kf);
310  			return FALSE;
311  		}
312  	
313  		/* Override the current settings with the new ones */
314  		groups = g_key_file_get_groups (kf, &ngroups);
315  		for (g = 0; groups[g]; g++) {
316  			keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL);
317  			if (!keys)
318  				continue;
319  			for (k = 0; keys[k]; k++) {
320  				int len = strlen (keys[k]);
321  				if (keys[k][len - 1] == '+') {
322  					char *base_key = g_strndup (keys[k], len - 1);
323  					const char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL);
324  					const char *new_val = g_key_file_get_value (kf, groups[g], keys[k], NULL);
325  	
326  					if (old_val && *old_val) {
327  						char *combined = g_strconcat (old_val, ",", new_val, NULL);
328  	
329  						g_key_file_set_value (priv->keyfile, groups[g], base_key, combined);
330  						g_free (combined);
331  					} else
332  						g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val);
333  	
334  					g_free (base_key);
335  					continue;
336  				}
337  	
338  				g_key_file_set_value (priv->keyfile, groups[g], keys[k],
339  				                      g_key_file_get_value (kf, groups[g], keys[k], NULL));
340  			}
341  		}
342  		g_key_file_free (kf);
343  	
344  		return TRUE;
345  	}
346  	
347  	static gboolean
348  	find_base_config (NMConfig *config, GError **error)
349  	{
350  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
351  		GError *my_error = NULL;
352  	
353  		/* Try a user-specified config file first */
354  		if (cli_config_path) {
355  			/* Bad user-specific config file path is a hard error */
356  			if (read_config (config, cli_config_path, error)) {
357  				priv->nm_conf_path = g_strdup (cli_config_path);
358  				return TRUE;
359  			} else
360  				return FALSE;
361  		}
362  	
363  		/* Even though we prefer NetworkManager.conf, we need to check the
364  		 * old nm-system-settings.conf first to preserve compat with older
365  		 * setups.  In package managed systems dropping a NetworkManager.conf
366  		 * onto the system would make NM use it instead of nm-system-settings.conf,
367  		 * changing behavior during an upgrade.  We don't want that.
368  		 */
369  	
370  		/* Try deprecated nm-system-settings.conf first */
371  		if (read_config (config, NM_OLD_SYSTEM_CONF_FILE, &my_error)) {
372  			priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
373  			return TRUE;
374  		}
375  	
376  		if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
377  			g_warning ("Default config file %s invalid: %s\n",
378  			           NM_OLD_SYSTEM_CONF_FILE,
379  			           my_error->message);
380  		}
381  		g_clear_error (&my_error);
382  	
383  		/* Try the standard config file location next */
384  		if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) {
385  			priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
386  			return TRUE;
387  		}
388  	
389  		if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
390  			g_warning ("Default config file %s invalid: %s\n",
391  			           NM_DEFAULT_SYSTEM_CONF_FILE,
392  			           my_error->message);
393  			g_propagate_error (error, my_error);
394  			return FALSE;
395  		}
396  	
397  		/* If for some reason no config file exists, use the default
398  		 * config file path.
399  		 */
400  		priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
401  		g_warning ("No config file found or given; using %s\n",
402  		           NM_DEFAULT_SYSTEM_CONF_FILE);
403  		return TRUE;
404  	}
405  	
406  	/************************************************************************/
407  	
408  	NMConfig *
409  	nm_config_get (void)
410  	{
411  		g_assert (singleton);
412  		return singleton;
413  	}
414  	
415  	static int
416  	sort_asciibetically (gconstpointer a, gconstpointer b)
417  	{
418  		const char *s1 = *(const char **)a;
419  		const char *s2 = *(const char **)b;
420  	
421  		return strcmp (s1, s2);
422  	}
423  	
424  	/* call this function only once! */
425  	NMConfig *
426  	nm_config_new (GError **error)
427  	{
428  		NMConfigPrivate *priv = NULL;
429  		GFile *dir;
430  		GFileEnumerator *direnum;
431  		GFileInfo *info;
432  		GPtrArray *confs;
433  		const char *name;
434  		char *value;
435  		int i;
436  		GString *config_description;
437  	
438  		g_assert (!singleton);
439  		singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL));
440  		priv = NM_CONFIG_GET_PRIVATE (singleton);
441  	
442  		/* First read the base config file */
443  		if (!find_base_config (singleton, error)) {
444  			g_object_unref (singleton);
445  			singleton = NULL;
446  			return NULL;
447  		}
448  	
449  		/* Now read the overrides in the config dir */
450  		if (cli_config_dir)
451  			priv->config_dir = g_strdup (cli_config_dir);
452  		else
453  			priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR);
454  	
455  		confs = g_ptr_array_new_with_free_func (g_free);
456  		config_description = g_string_new (priv->nm_conf_path);
457  		dir = g_file_new_for_path (priv->config_dir);
458  		direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL);
459  		if (direnum) {
460  			while ((info = g_file_enumerator_next_file (direnum, NULL, NULL))) {
461  				name = g_file_info_get_name (info);
462  				if (g_str_has_suffix (name, ".conf")) {
463  					g_ptr_array_add (confs, g_build_filename (priv->config_dir, name, NULL));
464  					if (confs->len == 1)
465  						g_string_append (config_description, " and conf.d: ");
466  					else
467  						g_string_append (config_description, ", ");
468  					g_string_append (config_description, name);
469  				}
470  				g_object_unref (info);
471  			}
472  			g_object_unref (direnum);
473  		}
474  		g_object_unref (dir);
475  	
476  		g_ptr_array_sort (confs, sort_asciibetically);
477  		priv->config_description = g_string_free (config_description, FALSE);
478  		for (i = 0; i < confs->len; i++) {
479  			if (!read_config (singleton, confs->pdata[i], error)) {
480  				g_object_unref (singleton);
481  				singleton = NULL;
482  				break;
483  			}
484  		}
485  		g_ptr_array_unref (confs);
486  		if (!singleton)
487  			return FALSE;
488  	
489  		/* Handle no-auto-default key and state file */
490  		priv->no_auto_default = g_key_file_get_string_list (priv->keyfile, "main", "no-auto-default", NULL, NULL);
491  		if (cli_no_auto_default_file)
492  			priv->no_auto_default_file = g_strdup (cli_no_auto_default_file);
493  		else
494  			priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE);
495  		merge_no_auto_default_state (singleton);
496  	
497  		/* Now let command-line options override the config files, and fill in priv. */
498  		if (cli_plugins && cli_plugins[0])
499  			g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins);
500  		priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL);
501  	
502  		value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
503  		if (value) {
504  			if (!strcmp (value, "true") || !strcmp (value, "yes") || !strcmp (value, "on"))
505  				priv->monitor_connection_files = TRUE;
506  			else if (!strcmp (value, "false") || !strcmp (value, "no") || !strcmp (value, "off"))
507  				priv->monitor_connection_files = FALSE;
508  			else {
509  				g_warning ("Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
510  				priv->monitor_connection_files = FALSE;
511  			}
512  			g_free (value);
513  		} else
514  			priv->monitor_connection_files = FALSE;
515  	
516  		priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
517  		priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
518  	
519  		priv->log_level = g_key_file_get_value (priv->keyfile, "logging", "level", NULL);
520  		priv->log_domains = g_key_file_get_value (priv->keyfile, "logging", "domains", NULL);
521  	
522  		if (cli_connectivity_uri && cli_connectivity_uri[0])
523  			g_key_file_set_value (priv->keyfile, "connectivity", "uri", cli_connectivity_uri);
524  		priv->connectivity_uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL);
525  	
526  		if (cli_connectivity_interval >= 0)
527  			g_key_file_set_integer (priv->keyfile, "connectivity", "interval", cli_connectivity_interval);
528  		priv->connectivity_interval = g_key_file_get_integer (priv->keyfile, "connectivity", "interval", NULL);
529  	
530  		if (cli_connectivity_response && cli_connectivity_response[0])
531  			g_key_file_set_value (priv->keyfile, "connectivity", "response", cli_connectivity_response);
532  		priv->connectivity_response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL);
533  	
534  		priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
535  	
536  		return singleton;
537  	}
538  	
539  	static void
540  	nm_config_init (NMConfig *config)
541  	{
542  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
543  	
544  		priv->keyfile = g_key_file_new ();
545  		g_key_file_set_list_separator (priv->keyfile, ',');
546  	
547  		priv->connectivity_interval = -1;
548  	}
549  	
550  	static void
551  	finalize (GObject *gobject)
552  	{
553  		NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
554  	
555  		g_free (priv->nm_conf_path);
556  		g_free (priv->config_dir);
557  		g_free (priv->config_description);
558  		g_free (priv->no_auto_default_file);
559  		g_clear_pointer (&priv->keyfile, g_key_file_unref);
560  		g_strfreev (priv->plugins);
561  		g_free (priv->dhcp_client);
562  		g_free (priv->dns_mode);
563  		g_free (priv->log_level);
564  		g_free (priv->log_domains);
565  		g_free (priv->connectivity_uri);
566  		g_free (priv->connectivity_response);
567  		g_strfreev (priv->no_auto_default);
568  		g_strfreev (priv->ignore_carrier);
569  	
570  		singleton = NULL;
571  	
572  		g_clear_pointer (&cli_config_path, g_free);
573  		g_clear_pointer (&cli_config_dir, g_free);
574  		g_clear_pointer (&cli_no_auto_default_file, g_free);
575  		g_clear_pointer (&cli_plugins, g_free);
576  		g_clear_pointer (&cli_connectivity_uri, g_free);
577  		g_clear_pointer (&cli_connectivity_response, g_free);
578  	
579  		G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject);
580  	}
581  	
582  	
583  	static void
584  	nm_config_class_init (NMConfigClass *config_class)
585  	{
586  		GObjectClass *object_class = G_OBJECT_CLASS (config_class);
587  	
588  		g_type_class_add_private (config_class, sizeof (NMConfigPrivate));
589  		object_class->finalize = finalize;
590  	}
591  	
592