nm-settings.c 60.6 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 3 4
/* NetworkManager system settings service
 *
 * Søren Sandmann <sandmann@daimi.au.dk>
5 6
 * Dan Williams <dcbw@redhat.com>
 * Tambet Ingo <tambet@gmail.com>
7 8 9 10 11 12 13 14 15 16 17
 *
 * 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.
 *
18 19 20
 * 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.
21
 *
22
 * (C) Copyright 2007 - 2011 Red Hat, Inc.
23
 * (C) Copyright 2008 Novell, Inc.
24 25
 */

26 27
#include "config.h"

28 29
#include <unistd.h>
#include <string.h>
30
#include <gmodule.h>
31
#include <pwd.h>
32
#include <dbus/dbus.h>
33
#include <dbus/dbus-glib-lowlevel.h>
34

35 36
#include <NetworkManager.h>
#include <nm-connection.h>
37 38 39
#include <nm-setting-8021x.h>
#include <nm-setting-bluetooth.h>
#include <nm-setting-cdma.h>
40
#include <nm-setting-connection.h>
41 42 43 44 45
#include <nm-setting-gsm.h>
#include <nm-setting-ip4-config.h>
#include <nm-setting-ip6-config.h>
#include <nm-setting-olpc-mesh.h>
#include <nm-setting-ppp.h>
46
#include <nm-setting-pppoe.h>
47 48 49
#include <nm-setting-serial.h>
#include <nm-setting-vpn.h>
#include <nm-setting-wired.h>
50
#include <nm-setting-adsl.h>
51 52
#include <nm-setting-wireless.h>
#include <nm-setting-wireless-security.h>
53
#include <nm-setting-bond.h>
54
#include <nm-utils.h>
55

56
#include "nm-device-ethernet.h"
57
#include "nm-dbus-glib-types.h"
58
#include "nm-settings.h"
59
#include "nm-settings-connection.h"
60
#include "nm-settings-error.h"
61
#include "nm-default-wired-connection.h"
Dan Williams's avatar
Dan Williams committed
62
#include "nm-logging.h"
63
#include "nm-dbus-manager.h"
64
#include "nm-manager-auth.h"
65
#include "nm-session-monitor.h"
66
#include "plugins/keyfile/plugin.h"
67
#include "nm-agent-manager.h"
68
#include "nm-settings-utils.h"
69
#include "nm-connection-provider.h"
70
#include "nm-config.h"
71

72 73 74 75 76 77 78 79 80
/* LINKER CRACKROCK */
#define EXPORT(sym) void * __export_##sym = &sym;

#include "nm-inotify-helper.h"
EXPORT(nm_inotify_helper_get_type)
EXPORT(nm_inotify_helper_get)
EXPORT(nm_inotify_helper_add_watch)
EXPORT(nm_inotify_helper_remove_watch)

81 82 83
EXPORT(nm_settings_connection_get_type)
EXPORT(nm_settings_connection_replace_settings)
EXPORT(nm_settings_connection_replace_and_commit)
84 85
/* END LINKER CRACKROCK */

86
static void claim_connection (NMSettings *self,
87
                              NMSettingsConnection *connection,
88
                              gboolean do_export);
89

90
static gboolean impl_settings_list_connections (NMSettings *self,
91 92 93
                                                GPtrArray **connections,
                                                GError **error);

94 95 96 97 98
static gboolean impl_settings_get_connection_by_uuid (NMSettings *self,
                                                      const char *uuid,
                                                      char **out_object_path,
                                                      GError **error);

99
static void impl_settings_add_connection (NMSettings *self,
100 101 102
                                          GHashTable *settings,
                                          DBusGMethodInvocation *context);

103 104 105 106
static void impl_settings_add_connection_unsaved (NMSettings *self,
                                                  GHashTable *settings,
                                                  DBusGMethodInvocation *context);

107
static void impl_settings_save_hostname (NMSettings *self,
108 109
                                         const char *hostname,
                                         DBusGMethodInvocation *context);
110

111
#include "nm-settings-glue.h"
112

113
static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data);
114

115 116 117 118 119 120
static void connection_provider_init (NMConnectionProvider *cp_class);

G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, G_TYPE_OBJECT, 0,
                        G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION_PROVIDER, connection_provider_init))


121
typedef struct {
122
	NMDBusManager *dbus_mgr;
123

124 125
	NMAgentManager *agent_mgr;

126
	NMConfig *config;
127

128
	NMSessionMonitor *session_monitor;
129
	GSList *auths;
130

131 132
	GSList *plugins;
	gboolean connections_loaded;
133
	GHashTable *connections;
134
	GSList *unmanaged_specs;
135
} NMSettingsPrivate;
136

137
#define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate))
138 139 140

enum {
	PROPERTIES_CHANGED,
141 142 143 144 145
	CONNECTION_ADDED,
	CONNECTION_UPDATED,
	CONNECTION_REMOVED,
	CONNECTION_VISIBILITY_CHANGED,
	CONNECTIONS_LOADED,
146
	AGENT_REGISTERED,
147

148
	NEW_CONNECTION, /* exported, not used internally */
149 150 151 152 153 154
	LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };

enum {
	PROP_0,
155
	PROP_UNMANAGED_SPECS,
156 157
	PROP_HOSTNAME,
	PROP_CAN_MODIFY,
158 159 160 161

	LAST_PROP
};

162
static void
163
load_connections (NMSettings *self)
164
{
165
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	GSList *iter;

	if (priv->connections_loaded)
		return;

	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
		GSList *plugin_connections;
		GSList *elt;

		plugin_connections = nm_system_config_interface_get_connections (plugin);

		// FIXME: ensure connections from plugins loaded with a lower priority
		// get rejected when they conflict with connections from a higher
		// priority plugin.

		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
183
			claim_connection (self, NM_SETTINGS_CONNECTION (elt->data), TRUE);
184 185 186 187

		g_slist_free (plugin_connections);
	}

188 189
	priv->connections_loaded = TRUE;

190
	/* FIXME: Bad hack */
191
	unmanaged_specs_changed (NULL, self);
192 193

	g_signal_emit (self, signals[CONNECTIONS_LOADED], 0);
194
	g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTIONS_LOADED);
195 196
}

197
void
198 199 200
nm_settings_for_each_connection (NMSettings *self,
                                 NMSettingsForEachFunc for_each_func,
                                 gpointer user_data)
201
{
202
	NMSettingsPrivate *priv;
203
	GHashTableIter iter;
204
	gpointer data;
205

206
	g_return_if_fail (NM_IS_SETTINGS (self));
207
	g_return_if_fail (for_each_func != NULL);
208
	
209
	priv = NM_SETTINGS_GET_PRIVATE (self);
210

211
	load_connections (self);
212

213
	g_hash_table_iter_init (&iter, priv->connections);
214
	while (g_hash_table_iter_next (&iter, NULL, &data))
215
		for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data);
216 217
}

218
static gboolean
219
impl_settings_list_connections (NMSettings *self,
220 221 222
                                GPtrArray **connections,
                                GError **error)
{
223
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
224
	GHashTableIter iter;
Dan Williams's avatar
Dan Williams committed
225
	gpointer key;
226

227 228
	load_connections (self);

229 230
	*connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
	g_hash_table_iter_init (&iter, priv->connections);
Dan Williams's avatar
Dan Williams committed
231 232
	while (g_hash_table_iter_next (&iter, &key, NULL))
		g_ptr_array_add (*connections, g_strdup ((const char *) key));
233 234 235
	return TRUE;
}

236 237
NMSettingsConnection *
nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
238
{
239 240
	NMSettingsPrivate *priv;
	NMSettingsConnection *candidate;
241
	GHashTableIter iter;
242 243 244 245 246

	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
	g_return_val_if_fail (uuid != NULL, NULL);

	priv = NM_SETTINGS_GET_PRIVATE (self);
247 248 249 250 251

	load_connections (self);

	g_hash_table_iter_init (&iter, priv->connections);
	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
252 253
		if (g_strcmp0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate))) == 0)
			return candidate;
254 255
	}

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
	return NULL;
}

static gboolean
impl_settings_get_connection_by_uuid (NMSettings *self,
                                      const char *uuid,
                                      char **out_object_path,
                                      GError **error)
{
	NMSettingsConnection *connection = NULL;

	connection = nm_settings_get_connection_by_uuid (self, uuid);
	if (connection)
		*out_object_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection)));
	else {
271 272 273 274 275 276
		g_set_error_literal (error,
		                     NM_SETTINGS_ERROR,
		                     NM_SETTINGS_ERROR_INVALID_CONNECTION,
		                     "No connection with the UUID was found.");
	}

277
	return !!connection;
278 279
}

280 281 282 283 284 285 286
static int
connection_sort (gconstpointer pa, gconstpointer pb)
{
	NMConnection *a = NM_CONNECTION (pa);
	NMSettingConnection *con_a;
	NMConnection *b = NM_CONNECTION (pb);
	NMSettingConnection *con_b;
287
	guint64 ts_a = 0, ts_b = 0;
288

289
	con_a = nm_connection_get_setting_connection (a);
290
	g_assert (con_a);
291
	con_b = nm_connection_get_setting_connection (b);
292 293 294 295 296 297 298 299
	g_assert (con_b);

	if (nm_setting_connection_get_autoconnect (con_a) != nm_setting_connection_get_autoconnect (con_b)) {
		if (nm_setting_connection_get_autoconnect (con_a))
			return -1;
		return 1;
	}

300 301
	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa), &ts_a);
	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb), &ts_b);
302
	if (ts_a > ts_b)
303
		return -1;
304
	else if (ts_a == ts_b)
305 306 307 308
		return 0;
	return 1;
}

309
/* Returns a list of NMSettingsConnections.  Caller must free the list with
310
 * g_slist_free().
311 312
 */
GSList *
313
nm_settings_get_connections (NMSettings *self)
314 315
{
	GHashTableIter iter;
316
	gpointer data = NULL;
317 318
	GSList *list = NULL;

319
	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
320

321
	g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections);
322 323
	while (g_hash_table_iter_next (&iter, NULL, &data))
		list = g_slist_insert_sorted (list, data, connection_sort);
324
	return list;
325 326
}

327
NMSettingsConnection *
328
nm_settings_get_connection_by_path (NMSettings *self, const char *path)
329
{
330
	NMSettingsPrivate *priv;
331

332
	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
333 334
	g_return_val_if_fail (path != NULL, NULL);

335
	priv = NM_SETTINGS_GET_PRIVATE (self);
336

337 338
	load_connections (self);

339
	return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path);
340 341
}

342
static void
343
clear_unmanaged_specs (NMSettings *self)
344
{
345
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
346 347 348 349 350 351

	g_slist_foreach (priv->unmanaged_specs, (GFunc) g_free, NULL);
	g_slist_free (priv->unmanaged_specs);
	priv->unmanaged_specs = NULL;
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
static char*
uscore_to_wincaps (const char *uscore)
{
	const char *p;
	GString *str;
	gboolean last_was_uscore;

	last_was_uscore = TRUE;
  
	str = g_string_new (NULL);
	p = uscore;
	while (p && *p) {
		if (*p == '-' || *p == '_')
			last_was_uscore = TRUE;
		else {
			if (last_was_uscore) {
				g_string_append_c (str, g_ascii_toupper (*p));
				last_was_uscore = FALSE;
			} else
				g_string_append_c (str, *p);
		}
		++p;
	}

	return g_string_free (str, FALSE);
}

static void
notify (GObject *object, GParamSpec *pspec)
{
	GValue *value;
	GHashTable *hash;

	value = g_slice_new0 (GValue);
	hash = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);

	g_value_init (value, pspec->value_type);
	g_object_get_property (object, pspec->name, value);
	g_hash_table_insert (hash, uscore_to_wincaps (pspec->name), value);
	g_signal_emit (object, signals[PROPERTIES_CHANGED], 0, hash);
	g_hash_table_destroy (hash);
	g_value_unset (value);
	g_slice_free (GValue, value);
}

397
const GSList *
398
nm_settings_get_unmanaged_specs (NMSettings *self)
399
{
400
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
401

402
	load_connections (self);
403
	return priv->unmanaged_specs;
404 405
}

406
static NMSystemConfigInterface *
407
get_plugin (NMSettings *self, guint32 capability)
408
{
409
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
	GSList *iter;

	g_return_val_if_fail (self != NULL, NULL);

	/* Do any of the plugins support setting the hostname? */
	for (iter = priv->plugins; iter; iter = iter->next) {
		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;

		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
		if (caps & capability)
			return NM_SYSTEM_CONFIG_INTERFACE (iter->data);
	}

	return NULL;
}

426
/* Returns an allocated string which the caller owns and must eventually free */
427
char *
428
nm_settings_get_hostname (NMSettings *self)
429
{
430
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
	GSList *iter;
	char *hostname = NULL;

	/* Hostname returned is the hostname returned from the first plugin
	 * that provides one.
	 */
	for (iter = priv->plugins; iter; iter = iter->next) {
		NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE;

		g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL);
		if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) {
			g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, &hostname, NULL);
			if (hostname && strlen (hostname))
				return hostname;
			g_free (hostname);
		}
	}

449
	return NULL;
450 451
}

452 453
static void
plugin_connection_added (NMSystemConfigInterface *config,
454
                         NMSettingsConnection *connection,
455
                         gpointer user_data)
456
{
457
	claim_connection (NM_SETTINGS (user_data), connection, TRUE);
458 459
}

460
static gboolean
461
find_unmanaged_device (NMSettings *self, const char *needle)
462
{
463
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
464 465 466 467 468 469 470 471 472
	GSList *iter;

	for (iter = priv->unmanaged_specs; iter; iter = g_slist_next (iter)) {
		if (!strcmp ((const char *) iter->data, needle))
			return TRUE;
	}
	return FALSE;
}

473
static void
474 475
unmanaged_specs_changed (NMSystemConfigInterface *config,
                         gpointer user_data)
476
{
477 478
	NMSettings *self = NM_SETTINGS (user_data);
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
479 480
	GSList *iter;

481
	clear_unmanaged_specs (self);
482

483
	/* Ask all the plugins for their unmanaged specs */
484
	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
485 486 487 488 489 490
		GSList *specs, *specs_iter;

		specs = nm_system_config_interface_get_unmanaged_specs (NM_SYSTEM_CONFIG_INTERFACE (iter->data));
		for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) {
			if (!find_unmanaged_device (self, (const char *) specs_iter->data)) {
				priv->unmanaged_specs = g_slist_prepend (priv->unmanaged_specs, specs_iter->data);
491
			} else
492
				g_free (specs_iter->data);
493
		}
494

495
		g_slist_free (specs);
496 497
	}

498
	g_object_notify (G_OBJECT (self), NM_SETTINGS_UNMANAGED_SPECS);
499
}
500

501 502 503 504 505
static void
hostname_changed (NMSystemConfigInterface *config,
                  GParamSpec *pspec,
                  gpointer user_data)
{
506
	g_object_notify (G_OBJECT (user_data), NM_SETTINGS_HOSTNAME);
507 508
}

509
static void
510
add_plugin (NMSettings *self, NMSystemConfigInterface *plugin)
511
{
512
	NMSettingsPrivate *priv;
513 514
	char *pname = NULL;
	char *pinfo = NULL;
515

516
	g_return_if_fail (NM_IS_SETTINGS (self));
517
	g_return_if_fail (NM_IS_SYSTEM_CONFIG_INTERFACE (plugin));
518

519
	priv = NM_SETTINGS_GET_PRIVATE (self);
520

521 522
	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));

523 524
	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED,
	                  G_CALLBACK (plugin_connection_added), self);
525
	g_signal_connect (plugin, "notify::hostname", G_CALLBACK (hostname_changed), self);
526

527
	nm_system_config_interface_init (plugin, NULL);
528 529 530

	g_object_get (G_OBJECT (plugin),
	              NM_SYSTEM_CONFIG_INTERFACE_NAME, &pname,
Dan Williams's avatar
Dan Williams committed
531
	              NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo,
532 533
	              NULL);

534 535 536
	g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED,
	                  G_CALLBACK (unmanaged_specs_changed), self);

537
	nm_log_info (LOGD_SETTINGS, "Loaded plugin %s: %s", pname, pinfo);
538 539 540 541
	g_free (pname);
	g_free (pinfo);
}

542 543 544 545 546 547
static GObject *
find_plugin (GSList *list, const char *pname)
{
	GSList *iter;
	GObject *obj = NULL;

548
	g_return_val_if_fail (pname != NULL, NULL);
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

	for (iter = list; iter && !obj; iter = g_slist_next (iter)) {
		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
		char *list_pname = NULL;

		g_object_get (G_OBJECT (plugin),
		              NM_SYSTEM_CONFIG_INTERFACE_NAME,
		              &list_pname,
		              NULL);
		if (list_pname && !strcmp (pname, list_pname))
			obj = G_OBJECT (plugin);

		g_free (list_pname);
	}

	return obj;
}

567 568 569 570 571
static void
add_keyfile_plugin (NMSettings *self)
{
	GObject *keyfile_plugin;

572
	keyfile_plugin = nm_settings_keyfile_plugin_new ();
573 574 575 576
	g_assert (keyfile_plugin);
	add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (keyfile_plugin));
}

577
static gboolean
578
load_plugins (NMSettings *self, const char **plugins, GError **error)
579 580
{
	GSList *list = NULL;
581
	const char **iter;
582
	gboolean keyfile_added = FALSE;
583 584
	gboolean success = TRUE;

585
	for (iter = plugins; iter && *iter; iter++) {
586 587
		GModule *plugin;
		char *full_name, *path;
588
		const char *pname = *iter;
589
		GObject *obj;
590
		GObject * (*factory_func) (void);
591

592
		/* strip leading spaces */
Dan Winship's avatar
Dan Winship committed
593
		while (g_ascii_isspace (*pname))
594 595
			pname++;

596 597 598 599 600 601 602 603
		/* ifcfg-fedora was renamed ifcfg-rh; handle old configs here */
		if (!strcmp (pname, "ifcfg-fedora"))
			pname = "ifcfg-rh";

		obj = find_plugin (list, pname);
		if (obj)
			continue;

604 605 606 607 608 609 610 611 612
		/* keyfile plugin is built-in now */
		if (strcmp (pname, "keyfile") == 0) {
			if (!keyfile_added) {
				add_keyfile_plugin (self);
				keyfile_added = TRUE;
			}
			continue;
		}

613
		full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
Dan Winship's avatar
Dan Winship committed
614
		path = g_module_build_path (NMPLUGINDIR, full_name);
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637

		plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
		if (!plugin) {
			g_set_error (error, 0, 0,
			             "Could not load plugin '%s': %s",
			             pname, g_module_error ());
			g_free (full_name);
			g_free (path);
			success = FALSE;
			break;
		}

		g_free (full_name);
		g_free (path);

		if (!g_module_symbol (plugin, "nm_system_config_factory", (gpointer) (&factory_func))) {
			g_set_error (error, 0, 0,
			             "Could not find plugin '%s' factory function.",
			             pname);
			success = FALSE;
			break;
		}

638
		obj = (*factory_func) ();
639 640 641 642 643 644 645 646 647 648 649 650 651 652
		if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) {
			g_set_error (error, 0, 0,
			             "Plugin '%s' returned invalid system config object.",
			             pname);
			success = FALSE;
			break;
		}

		g_module_make_resident (plugin);
		g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
		add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (obj));
		list = g_slist_append (list, obj);
	}

653 654 655 656
	/* If keyfile plugin was not among configured plugins, add it as the last one */
	if (!keyfile_added)
		add_keyfile_plugin (self);

657 658 659 660 661 662
	g_slist_foreach (list, (GFunc) g_object_unref, NULL);
	g_slist_free (list);

	return success;
}

663 664 665
#define REMOVED_ID_TAG "removed-id-tag"
#define UPDATED_ID_TAG "updated-id-tag"
#define VISIBLE_ID_TAG "visible-id-tag"
666
#define UNREG_ID_TAG "unreg-id-tag"
667

668
static void
669
connection_removed (NMSettingsConnection *obj, gpointer user_data)
670
{
671 672
	GObject *connection = G_OBJECT (obj);
	guint id;
673

674 675
	g_object_ref (connection);

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
	/* Disconnect signal handlers, as plugins might still keep references
	 * to the connection (and thus the signal handlers would still be live)
	 * even after NMSettings has dropped all its references.
	 */

	id = GPOINTER_TO_UINT (g_object_get_data (connection, REMOVED_ID_TAG));
	if (id)
		g_signal_handler_disconnect (connection, id);

	id = GPOINTER_TO_UINT (g_object_get_data (connection, UPDATED_ID_TAG));
	if (id)
		g_signal_handler_disconnect (connection, id);

	id = GPOINTER_TO_UINT (g_object_get_data (connection, VISIBLE_ID_TAG));
	if (id)
		g_signal_handler_disconnect (connection, id);

693
	/* Forget about the connection internally */
694 695
	g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections,
	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)));
696 697

	/* Re-emit for listeners like NMPolicy */
698
	g_signal_emit (NM_SETTINGS (user_data), signals[CONNECTION_REMOVED], 0, connection);
699
	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
700

701 702 703
	g_object_unref (connection);
}

704 705 706 707 708 709 710 711
static void
connection_unregister (NMSettingsConnection *obj, gpointer user_data)
{
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (user_data);
	GObject *connection = G_OBJECT (obj);
	guint id;

	/* Make sure it's unregistered from the bus now that's removed */
712
	nm_dbus_manager_unregister_object (priv->dbus_mgr, connection);
713 714 715 716 717 718

	id = GPOINTER_TO_UINT (g_object_get_data (connection, UNREG_ID_TAG));
	if (id)
		g_signal_handler_disconnect (connection, id);
}

719
static void
720
connection_updated (NMSettingsConnection *connection, gpointer user_data)
721
{
722
	/* Re-emit for listeners like NMPolicy */
723
	g_signal_emit (NM_SETTINGS (user_data),
724 725 726
	               signals[CONNECTION_UPDATED],
	               0,
	               connection);
727
	g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
728 729 730
}

static void
731
connection_visibility_changed (NMSettingsConnection *connection,
732 733
                               GParamSpec *pspec,
                               gpointer user_data)
734
{
735
	/* Re-emit for listeners like NMPolicy */
736
	g_signal_emit (NM_SETTINGS (user_data),
737 738 739
	               signals[CONNECTION_VISIBILITY_CHANGED],
	               0,
	               connection);
740 741
}

742 743 744 745 746 747 748 749 750 751 752 753
static void
secret_agent_registered (NMAgentManager *agent_mgr,
                         NMSecretAgent *agent,
                         gpointer user_data)
{
	/* Re-emit for listeners like NMPolicy */
	g_signal_emit (NM_SETTINGS (user_data),
	               signals[AGENT_REGISTERED],
	               0,
	               agent);
}

754 755 756 757
#define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
#define NM_OPENCONNECT_KEY_GATEWAY "gateway"
#define NM_OPENCONNECT_KEY_COOKIE "cookie"
#define NM_OPENCONNECT_KEY_GWCERT "gwcert"
758 759 760 761
#define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig"
#define NM_OPENCONNECT_KEY_LASTHOST "lasthost"
#define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect"
#define NM_OPENCONNECT_KEY_CERTSIGS "certsigs"
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779

static void
openconnect_migrate_hack (NMConnection *connection)
{
	NMSettingVPN *s_vpn;
	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NOT_SAVED;

	/* Huge hack.  There were some openconnect changes that needed to happen
	 * pretty late, too late to get into distros.  Migration has already
	 * happened for many people, and their secret flags are wrong.  But we
	 * don't want to requrie re-migration, so we have to fix it up here. Ugh.
	 */

	s_vpn = nm_connection_get_setting_vpn (connection);
	if (s_vpn == NULL)
		return;

	if (g_strcmp0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT) == 0) {
780
		/* These are different for every login session, and should not be stored */
781 782 783
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL);
784 785 786 787 788 789 790

		/* These are purely internal data for the auth-dialog, and should be stored */
		flags = 0;
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL);
		nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL);
791 792 793
	}
}

794
static void
795
claim_connection (NMSettings *self,
796
                  NMSettingsConnection *connection,
797
                  gboolean do_export)
798
{
799
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
800 801 802 803 804
	static guint32 ec_counter = 0;
	GError *error = NULL;
	GHashTableIter iter;
	gpointer data;
	char *path;
805
	guint id;
806

807
	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
808
	g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
809

810 811 812 813 814 815 816 817
	g_hash_table_iter_init (&iter, priv->connections);
	while (g_hash_table_iter_next (&iter, NULL, &data)) {
		/* prevent duplicates */
		if (data == connection)
			return;
	}

	if (!nm_connection_verify (NM_CONNECTION (connection), &error)) {
818
		nm_log_warn (LOGD_SETTINGS, "plugin provided invalid connection: '%s' / '%s' invalid: %d",
819 820 821
		             g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)),
		             error->message, error->code);
		g_error_free (error);
822
		return;
823 824
	}

825 826 827
	/* Read timestamp from look-aside file and put it into the connection's data */
	nm_settings_connection_read_and_fill_timestamp (connection);

828 829 830
	/* Read seen-bssids from look-aside file and put it into the connection's data */
	nm_settings_connection_read_and_fill_seen_bssids (connection);

831
	/* Ensure it's initial visibility is up-to-date */
832
	nm_settings_connection_recheck_visibility (connection);
833

834 835 836
	/* Evil openconnect migration hack */
	openconnect_migrate_hack (NM_CONNECTION (connection));

837
	id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
838 839 840
	                       G_CALLBACK (connection_removed),
	                       self);
	g_object_set_data (G_OBJECT (connection), REMOVED_ID_TAG, GUINT_TO_POINTER (id));
841

842 843 844 845 846
	id = g_signal_connect (connection, "unregister",
	                       G_CALLBACK (connection_unregister),
	                       self);
	g_object_set_data (G_OBJECT (connection), UNREG_ID_TAG, GUINT_TO_POINTER (id));

847
	id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED,
848 849 850
	                       G_CALLBACK (connection_updated),
	                       self);
	g_object_set_data (G_OBJECT (connection), UPDATED_ID_TAG, GUINT_TO_POINTER (id));
851

852
	id = g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE,
853 854 855
	                       G_CALLBACK (connection_visibility_changed),
	                       self);
	g_object_set_data (G_OBJECT (connection), VISIBLE_ID_TAG, GUINT_TO_POINTER (id));
856

857 858 859 860
	/* Export the connection over D-Bus */
	g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
	path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, ec_counter++);
	nm_connection_set_path (NM_CONNECTION (connection), path);
861
	nm_dbus_manager_register_object (priv->dbus_mgr, path, G_OBJECT (connection));
862 863 864 865 866 867 868 869 870 871 872
	g_free (path);

	g_hash_table_insert (priv->connections,
	                     (gpointer) nm_connection_get_path (NM_CONNECTION (connection)),
	                     g_object_ref (connection));

	/* Only emit the individual connection-added signal after connections
	 * have been initially loaded.  While getting the first list of connections
	 * we suppress it, then send the connections-loaded signal after we're all
	 * done to minimize processing.
	 */
873 874
	if (priv->connections_loaded) {
		/* Internal added signal */
875
		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
876
		g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection);
877 878 879 880

		/* Exported D-Bus signal */
		g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
	}
881 882
}

883
static void
884
remove_default_wired_connection (NMSettings *self,
885
                                 NMSettingsConnection *connection,
886
                                 gboolean do_signal)
887
{
888
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
889
	const char *path = nm_connection_get_path (NM_CONNECTION (connection));
890

891
	if (g_hash_table_lookup (priv->connections, path)) {
892 893
		if (do_signal)
			g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_REMOVED);
894
		g_hash_table_remove (priv->connections, path);
895 896 897
	}
}

898
static NMSettingsConnection *
899
add_new_connection (NMSettings *self,
900
                    NMConnection *connection,
901
                    gboolean save_to_disk,
902
                    GError **error)
903
{
904
	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
905
	GSList *iter;
906
	NMSettingsConnection *added = NULL;
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
	GHashTableIter citer;
	NMConnection *candidate = NULL;

	/* Make sure a connection with this UUID doesn't already exist */
	g_hash_table_iter_init (&citer, priv->connections);
	while (g_hash_table_iter_next (&citer, NULL, (gpointer *) &candidate)) {
		if (g_strcmp0 (nm_connection_get_uuid (connection),
		               nm_connection_get_uuid (candidate)) == 0) {
			g_set_error_literal (error,
			                     NM_SETTINGS_ERROR,
			                     NM_SETTINGS_ERROR_UUID_EXISTS,
			                     "A connection with this UUID already exists.");
			return NULL;
		}
	}
922 923

	/* 1) plugin writes the NMConnection to disk
924
	 * 2) plugin creates a new NMSettingsConnection subclass with the settings
925
	 *     from the NMConnection and returns it to the settings service
926
	 * 3) settings service exports the new NMSettingsConnection subclass
927 928 929 930
	 * 4) plugin notices that something on the filesystem has changed
	 * 5) plugin reads the changes and ignores them because they will
	 *     contain the same data as the connection it already knows about
	 */
931 932
	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
		NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
933
		GError *add_error = NULL;
934

935
		added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error);
936 937 938 939
		if (added) {
			claim_connection (self, added, TRUE);
			return added;
		}
940 941 942 943 944
		nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s",
		            nm_connection_get_uuid (connection),
		            nm_connection_get_id (connection),
		            add_error ? add_error->message : "(unknown)");
		g_clear_error (&add_error);
945
	}
946 947 948

	g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_FAILED,
	                     "No plugin supported adding this connection");
949
	return NULL;
950
}
951

952 953 954 955 956 957 958 959 960 961