core: be strict about connection argument in D-Bus methods
[NetworkManager.git] / src / settings / nm-settings.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager system settings service
3  *
4  * Søren Sandmann <sandmann@daimi.au.dk>
5  * Dan Williams <dcbw@redhat.com>
6  * Tambet Ingo <tambet@gmail.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 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 #include "nm-default.h"
27
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <gmodule.h>
33 #include <pwd.h>
34
35 #if HAVE_SELINUX
36 #include <selinux/selinux.h>
37 #endif
38
39 #include "nm-dbus-interface.h"
40 #include "nm-connection.h"
41 #include "nm-setting-8021x.h"
42 #include "nm-setting-bluetooth.h"
43 #include "nm-setting-cdma.h"
44 #include "nm-setting-connection.h"
45 #include "nm-setting-gsm.h"
46 #include "nm-setting-ip4-config.h"
47 #include "nm-setting-ip6-config.h"
48 #include "nm-setting-olpc-mesh.h"
49 #include "nm-setting-ppp.h"
50 #include "nm-setting-pppoe.h"
51 #include "nm-setting-serial.h"
52 #include "nm-setting-vpn.h"
53 #include "nm-setting-wired.h"
54 #include "nm-setting-adsl.h"
55 #include "nm-setting-wireless.h"
56 #include "nm-setting-wireless-security.h"
57 #include "nm-setting-bond.h"
58 #include "nm-utils.h"
59 #include "nm-core-internal.h"
60
61 #include "nm-device-ethernet.h"
62 #include "nm-settings.h"
63 #include "nm-settings-connection.h"
64 #include "nm-settings-plugin.h"
65 #include "nm-bus-manager.h"
66 #include "nm-auth-utils.h"
67 #include "nm-auth-subject.h"
68 #include "nm-session-monitor.h"
69 #include "plugins/keyfile/plugin.h"
70 #include "nm-agent-manager.h"
71 #include "nm-connection-provider.h"
72 #include "nm-config.h"
73 #include "nm-audit-manager.h"
74 #include "NetworkManagerUtils.h"
75 #include "nm-dispatcher.h"
76
77 #include "nmdbus-settings.h"
78
79 #define _NMLOG_DOMAIN         LOGD_SETTINGS
80 #define _NMLOG_PREFIX_NAME    "settings"
81 #define _NMLOG(level, ...) \
82     G_STMT_START { \
83         nm_log ((level), _NMLOG_DOMAIN, \
84                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
85                 _NMLOG_PREFIX_NAME": " \
86                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
87     } G_STMT_END
88
89 /* LINKER CRACKROCK */
90 #define EXPORT(sym) void * __export_##sym = &sym;
91
92 #include "nm-inotify-helper.h"
93 EXPORT(nm_inotify_helper_get_type)
94 EXPORT(nm_inotify_helper_get)
95 EXPORT(nm_inotify_helper_add_watch)
96 EXPORT(nm_inotify_helper_remove_watch)
97
98 EXPORT(nm_settings_connection_get_type)
99 EXPORT(nm_settings_connection_replace_settings)
100 EXPORT(nm_settings_connection_replace_and_commit)
101 /* END LINKER CRACKROCK */
102
103 #define HOSTNAMED_SERVICE_NAME      "org.freedesktop.hostname1"
104 #define HOSTNAMED_SERVICE_PATH      "/org/freedesktop/hostname1"
105 #define HOSTNAMED_SERVICE_INTERFACE "org.freedesktop.hostname1"
106
107 #define HOSTNAME_FILE_DEFAULT   "/etc/hostname"
108 #define HOSTNAME_FILE_SUSE      "/etc/HOSTNAME"
109 #define HOSTNAME_FILE_GENTOO    "/etc/conf.d/hostname"
110 #define IFCFG_DIR               SYSCONFDIR "/sysconfig/network"
111 #define CONF_DHCP               IFCFG_DIR "/dhcp"
112
113 #define PLUGIN_MODULE_PATH      "plugin-module-path"
114
115 #if defined(HOSTNAME_PERSIST_SUSE)
116 #define HOSTNAME_FILE           HOSTNAME_FILE_SUSE
117 #elif defined(HOSTNAME_PERSIST_GENTOO)
118 #define HOSTNAME_FILE           HOSTNAME_FILE_GENTOO
119 #else
120 #define HOSTNAME_FILE           HOSTNAME_FILE_DEFAULT
121 #endif
122
123 static void claim_connection (NMSettings *self,
124                               NMSettingsConnection *connection);
125
126 static void unmanaged_specs_changed (NMSettingsPlugin *config, gpointer user_data);
127 static void unrecognized_specs_changed (NMSettingsPlugin *config, gpointer user_data);
128
129 static void connection_provider_iface_init (NMConnectionProviderInterface *cp_iface);
130
131 G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, NM_TYPE_EXPORTED_OBJECT, 0,
132                         G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION_PROVIDER, connection_provider_iface_init))
133
134
135 typedef struct {
136         NMAgentManager *agent_mgr;
137
138         NMConfig *config;
139
140         GSList *auths;
141
142         GSList *plugins;
143         gboolean connections_loaded;
144         GHashTable *connections;
145         GSList *unmanaged_specs;
146         GSList *unrecognized_specs;
147         GSList *get_connections_cache;
148
149         gboolean started;
150         gboolean startup_complete;
151
152         struct {
153                 char *value;
154                 char *file;
155                 GFileMonitor *monitor;
156                 GFileMonitor *dhcp_monitor;
157                 gulong monitor_id;
158                 gulong dhcp_monitor_id;
159                 GDBusProxy *hostnamed_proxy;
160         } hostname;
161 } NMSettingsPrivate;
162
163 #define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate))
164
165 enum {
166         CONNECTION_ADDED,
167         CONNECTION_UPDATED,
168         CONNECTION_UPDATED_BY_USER,
169         CONNECTION_REMOVED,
170         CONNECTION_VISIBILITY_CHANGED,
171         AGENT_REGISTERED,
172
173         NEW_CONNECTION, /* exported, not used internally */
174         LAST_SIGNAL
175 };
176 static guint signals[LAST_SIGNAL] = { 0 };
177
178 enum {
179         PROP_0,
180         PROP_UNMANAGED_SPECS,
181         PROP_HOSTNAME,
182         PROP_CAN_MODIFY,
183         PROP_CONNECTIONS,
184         PROP_STARTUP_COMPLETE,
185
186         LAST_PROP
187 };
188
189 static void
190 check_startup_complete (NMSettings *self)
191 {
192         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
193         GHashTableIter iter;
194         NMSettingsConnection *conn;
195
196         if (priv->startup_complete)
197                 return;
198
199         g_hash_table_iter_init (&iter, priv->connections);
200         while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &conn)) {
201                 if (!nm_settings_connection_get_ready (conn))
202                         return;
203         }
204
205         priv->startup_complete = TRUE;
206         g_object_notify (G_OBJECT (self), NM_SETTINGS_STARTUP_COMPLETE);
207 }
208
209 static void
210 connection_ready_changed (NMSettingsConnection *conn,
211                           GParamSpec *pspec,
212                           gpointer user_data)
213 {
214         NMSettings *self = NM_SETTINGS (user_data);
215
216         if (nm_settings_connection_get_ready (conn))
217                 check_startup_complete (self);
218 }
219
220 static void
221 plugin_connection_added (NMSettingsPlugin *config,
222                          NMSettingsConnection *connection,
223                          gpointer user_data)
224 {
225         claim_connection (NM_SETTINGS (user_data), connection);
226 }
227
228 static void
229 load_connections (NMSettings *self)
230 {
231         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
232         GSList *iter;
233
234         for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
235                 NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
236                 GSList *plugin_connections;
237                 GSList *elt;
238
239                 plugin_connections = nm_settings_plugin_get_connections (plugin);
240
241                 // FIXME: ensure connections from plugins loaded with a lower priority
242                 // get rejected when they conflict with connections from a higher
243                 // priority plugin.
244
245                 for (elt = plugin_connections; elt; elt = g_slist_next (elt))
246                         claim_connection (self, NM_SETTINGS_CONNECTION (elt->data));
247
248                 g_slist_free (plugin_connections);
249
250                 g_signal_connect (plugin, NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
251                                   G_CALLBACK (plugin_connection_added), self);
252                 g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
253                                   G_CALLBACK (unmanaged_specs_changed), self);
254                 g_signal_connect (plugin, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
255                                   G_CALLBACK (unrecognized_specs_changed), self);
256         }
257
258         priv->connections_loaded = TRUE;
259
260         unmanaged_specs_changed (NULL, self);
261         unrecognized_specs_changed (NULL, self);
262 }
263
264 void
265 nm_settings_for_each_connection (NMSettings *self,
266                                  NMSettingsForEachFunc for_each_func,
267                                  gpointer user_data)
268 {
269         NMSettingsPrivate *priv;
270         GHashTableIter iter;
271         gpointer data;
272
273         g_return_if_fail (NM_IS_SETTINGS (self));
274         g_return_if_fail (for_each_func != NULL);
275         
276         priv = NM_SETTINGS_GET_PRIVATE (self);
277
278         g_hash_table_iter_init (&iter, priv->connections);
279         while (g_hash_table_iter_next (&iter, NULL, &data))
280                 for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data);
281 }
282
283 static void
284 impl_settings_list_connections (NMSettings *self,
285                                 GDBusMethodInvocation *context)
286 {
287         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
288         GPtrArray *connections;
289         GHashTableIter iter;
290         gpointer key;
291
292         connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
293         g_hash_table_iter_init (&iter, priv->connections);
294         while (g_hash_table_iter_next (&iter, &key, NULL))
295                 g_ptr_array_add (connections, key);
296         g_ptr_array_add (connections, NULL);
297
298         g_dbus_method_invocation_return_value (context,
299                                                g_variant_new ("(^ao)", connections->pdata));
300         g_ptr_array_unref (connections);
301 }
302
303 NMSettingsConnection *
304 nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid)
305 {
306         NMSettingsPrivate *priv;
307         NMSettingsConnection *candidate;
308         GHashTableIter iter;
309
310         g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
311         g_return_val_if_fail (uuid != NULL, NULL);
312
313         priv = NM_SETTINGS_GET_PRIVATE (self);
314
315         g_hash_table_iter_init (&iter, priv->connections);
316         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
317                 if (g_strcmp0 (uuid, nm_settings_connection_get_uuid (candidate)) == 0)
318                         return candidate;
319         }
320
321         return NULL;
322 }
323
324 static void
325 impl_settings_get_connection_by_uuid (NMSettings *self,
326                                       GDBusMethodInvocation *context,
327                                       const char *uuid)
328 {
329         NMSettingsConnection *connection = NULL;
330         NMAuthSubject *subject = NULL;
331         GError *error = NULL;
332         char *error_desc = NULL;
333
334         connection = nm_settings_get_connection_by_uuid (self, uuid);
335         if (!connection) {
336                 error = g_error_new_literal (NM_SETTINGS_ERROR,
337                                              NM_SETTINGS_ERROR_INVALID_CONNECTION,
338                                              "No connection with the UUID was found.");
339                 goto error;
340         }
341
342         subject = nm_auth_subject_new_unix_process_from_context (context);
343         if (!subject) {
344                 error = g_error_new_literal (NM_SETTINGS_ERROR,
345                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
346                                              "Unable to determine UID of request.");
347                 goto error;
348         }
349
350         if (!nm_auth_is_subject_in_acl (NM_CONNECTION (connection),
351                                         subject,
352                                         &error_desc)) {
353                 error = g_error_new_literal (NM_SETTINGS_ERROR,
354                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
355                                              error_desc);
356                 g_free (error_desc);
357                 goto error;
358         }
359
360         g_clear_object (&subject);
361         g_dbus_method_invocation_return_value (
362                 context,
363                 g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
364         return;
365
366 error:
367         g_assert (error);
368         g_dbus_method_invocation_take_error (context, error);
369         g_clear_object (&subject);
370 }
371
372 static int
373 connection_sort (gconstpointer pa, gconstpointer pb)
374 {
375         NMConnection *a = NM_CONNECTION (pa);
376         NMSettingConnection *con_a;
377         NMConnection *b = NM_CONNECTION (pb);
378         NMSettingConnection *con_b;
379         guint64 ts_a = 0, ts_b = 0;
380         gboolean can_ac_a, can_ac_b;
381
382         con_a = nm_connection_get_setting_connection (a);
383         g_assert (con_a);
384         con_b = nm_connection_get_setting_connection (b);
385         g_assert (con_b);
386
387         can_ac_a = !!nm_setting_connection_get_autoconnect (con_a);
388         can_ac_b = !!nm_setting_connection_get_autoconnect (con_b);
389         if (can_ac_a != can_ac_b)
390                 return can_ac_a ? -1 : 1;
391
392         nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa), &ts_a);
393         nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb), &ts_b);
394         if (ts_a > ts_b)
395                 return -1;
396         else if (ts_a == ts_b)
397                 return 0;
398         return 1;
399 }
400
401 /* Returns a list of NMSettingsConnections.
402  * The list is sorted in the order suitable for auto-connecting, i.e.
403  * first go connections with autoconnect=yes and most recent timestamp.
404  * Caller must free the list with g_slist_free().
405  */
406 GSList *
407 nm_settings_get_connections (NMSettings *self)
408 {
409         GHashTableIter iter;
410         gpointer data = NULL;
411         GSList *list = NULL;
412
413         g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
414
415         g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections);
416         while (g_hash_table_iter_next (&iter, NULL, &data))
417                 list = g_slist_insert_sorted (list, data, connection_sort);
418         return list;
419 }
420
421 NMSettingsConnection *
422 nm_settings_get_connection_by_path (NMSettings *self, const char *path)
423 {
424         NMSettingsPrivate *priv;
425
426         g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
427         g_return_val_if_fail (path != NULL, NULL);
428
429         priv = NM_SETTINGS_GET_PRIVATE (self);
430
431         return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path);
432 }
433
434 gboolean
435 nm_settings_has_connection (NMSettings *self, NMSettingsConnection *connection)
436 {
437         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
438         GHashTableIter iter;
439         gpointer data;
440
441         g_hash_table_iter_init (&iter, priv->connections);
442         while (g_hash_table_iter_next (&iter, NULL, &data))
443                 if (data == connection)
444                         return TRUE;
445
446         return FALSE;
447 }
448
449 const GSList *
450 nm_settings_get_unmanaged_specs (NMSettings *self)
451 {
452         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
453
454         return priv->unmanaged_specs;
455 }
456
457 static NMSettingsPlugin *
458 get_plugin (NMSettings *self, guint32 capability)
459 {
460         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
461         GSList *iter;
462
463         g_return_val_if_fail (self != NULL, NULL);
464
465         /* Do any of the plugins support the given capability? */
466         for (iter = priv->plugins; iter; iter = iter->next) {
467                 NMSettingsPluginCapabilities caps = NM_SETTINGS_PLUGIN_CAP_NONE;
468
469                 g_object_get (G_OBJECT (iter->data), NM_SETTINGS_PLUGIN_CAPABILITIES, &caps, NULL);
470                 if (NM_FLAGS_ALL (caps, capability))
471                         return NM_SETTINGS_PLUGIN (iter->data);
472         }
473
474         return NULL;
475 }
476
477 #if defined(HOSTNAME_PERSIST_GENTOO)
478 static gchar *
479 read_hostname_gentoo (const char *path)
480 {
481         gchar *contents = NULL, *result = NULL, *tmp;
482         gchar **all_lines = NULL;
483         guint line_num, i;
484
485         if (!g_file_get_contents (path, &contents, NULL, NULL))
486                 return NULL;
487         all_lines = g_strsplit (contents, "\n", 0);
488         line_num = g_strv_length (all_lines);
489         for (i = 0; i < line_num; i++) {
490                 g_strstrip (all_lines[i]);
491                 if (all_lines[i][0] == '#' || all_lines[i][0] == '\0')
492                         continue;
493                 if (g_str_has_prefix (all_lines[i], "hostname=")) {
494                         tmp = &all_lines[i][NM_STRLEN ("hostname=")];
495                         result = g_shell_unquote (tmp, NULL);
496                         break;
497                 }
498         }
499         g_strfreev (all_lines);
500         g_free (contents);
501         return result;
502 }
503 #endif
504
505 #if defined(HOSTNAME_PERSIST_SUSE)
506 static gboolean
507 hostname_is_dynamic (void)
508 {
509         GIOChannel *channel;
510         char *str = NULL;
511         gboolean dynamic = FALSE;
512
513         channel = g_io_channel_new_file (CONF_DHCP, "r", NULL);
514         if (!channel)
515                 return dynamic;
516
517         while (g_io_channel_read_line (channel, &str, NULL, NULL, NULL) != G_IO_STATUS_EOF) {
518                 if (str) {
519                         g_strstrip (str);
520                         if (g_str_has_prefix (str, "DHCLIENT_SET_HOSTNAME="))
521                                 dynamic = strcmp (&str[NM_STRLEN ("DHCLIENT_SET_HOSTNAME=")], "\"yes\"") == 0;
522                         g_free (str);
523                 }
524         }
525
526         g_io_channel_shutdown (channel, FALSE, NULL);
527         g_io_channel_unref (channel);
528
529         return dynamic;
530 }
531 #endif
532
533 /* Returns an allocated string which the caller owns and must eventually free */
534 char *
535 nm_settings_get_hostname (NMSettings *self)
536 {
537         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
538         char *hostname = NULL;
539
540         if (!priv->started)
541                 return NULL;
542
543         if (priv->hostname.hostnamed_proxy) {
544                 hostname = g_strdup (priv->hostname.value);
545                 goto out;
546         }
547
548 #if defined(HOSTNAME_PERSIST_GENTOO)
549         hostname = read_hostname_gentoo (priv->hostname.file);
550 #else
551
552 #if defined(HOSTNAME_PERSIST_SUSE)
553         if (priv->hostname.dhcp_monitor_id && hostname_is_dynamic ())
554                 return NULL;
555 #endif
556         if (g_file_get_contents (priv->hostname.file, &hostname, NULL, NULL))
557                 g_strchomp (hostname);
558
559 #endif /* HOSTNAME_PERSIST_GENTOO */
560
561 out:
562         if (hostname && !hostname[0]) {
563                 g_free (hostname);
564                 hostname = NULL;
565         }
566
567         return hostname;
568 }
569
570 static gboolean
571 find_spec (GSList *spec_list, const char *spec)
572 {
573         GSList *iter;
574
575         for (iter = spec_list; iter; iter = g_slist_next (iter)) {
576                 if (!strcmp ((const char *) iter->data, spec))
577                         return TRUE;
578         }
579         return FALSE;
580 }
581
582 static void
583 update_specs (NMSettings *self, GSList **specs_ptr,
584               GSList * (*get_specs_func) (NMSettingsPlugin *))
585 {
586         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
587         GSList *iter;
588
589         g_slist_free_full (*specs_ptr, g_free);
590         *specs_ptr = NULL;
591
592         for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
593                 GSList *specs, *specs_iter;
594
595                 specs = get_specs_func (NM_SETTINGS_PLUGIN (iter->data));
596                 for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) {
597                         if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) {
598                                 *specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data);
599                         } else
600                                 g_free (specs_iter->data);
601                 }
602
603                 g_slist_free (specs);
604         }
605 }
606
607 static void
608 unmanaged_specs_changed (NMSettingsPlugin *config,
609                          gpointer user_data)
610 {
611         NMSettings *self = NM_SETTINGS (user_data);
612         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
613
614         update_specs (self, &priv->unmanaged_specs,
615                       nm_settings_plugin_get_unmanaged_specs);
616         g_object_notify (G_OBJECT (self), NM_SETTINGS_UNMANAGED_SPECS);
617 }
618
619 static void
620 unrecognized_specs_changed (NMSettingsPlugin *config,
621                                gpointer user_data)
622 {
623         NMSettings *self = NM_SETTINGS (user_data);
624         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
625
626         update_specs (self, &priv->unrecognized_specs,
627                       nm_settings_plugin_get_unrecognized_specs);
628 }
629
630 static gboolean
631 add_plugin (NMSettings *self, NMSettingsPlugin *plugin)
632 {
633         NMSettingsPrivate *priv;
634         char *pname = NULL;
635         char *pinfo = NULL;
636         const char *path;
637
638         g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
639         g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (plugin), FALSE);
640
641         priv = NM_SETTINGS_GET_PRIVATE (self);
642
643         if (g_slist_find (priv->plugins, plugin)) {
644                 /* don't add duplicates. */
645                 return FALSE;
646         }
647
648         priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
649         nm_settings_plugin_init (plugin);
650
651         g_object_get (G_OBJECT (plugin),
652                       NM_SETTINGS_PLUGIN_NAME, &pname,
653                       NM_SETTINGS_PLUGIN_INFO, &pinfo,
654                       NULL);
655
656         path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH);
657
658         _LOGI ("loaded plugin %s: %s%s%s%s", pname, pinfo,
659                NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
660         g_free (pname);
661         g_free (pinfo);
662
663         return TRUE;
664 }
665
666 static GObject *
667 find_plugin (GSList *list, const char *pname)
668 {
669         GSList *iter;
670         GObject *obj = NULL;
671
672         g_return_val_if_fail (pname != NULL, NULL);
673
674         for (iter = list; iter && !obj; iter = g_slist_next (iter)) {
675                 NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
676                 char *list_pname = NULL;
677
678                 g_object_get (G_OBJECT (plugin),
679                               NM_SETTINGS_PLUGIN_NAME,
680                               &list_pname,
681                               NULL);
682                 if (list_pname && !strcmp (pname, list_pname))
683                         obj = G_OBJECT (plugin);
684
685                 g_free (list_pname);
686         }
687
688         return obj;
689 }
690
691 static void
692 add_keyfile_plugin (NMSettings *self)
693 {
694         gs_unref_object GObject *keyfile_plugin = NULL;
695
696         keyfile_plugin = nm_settings_keyfile_plugin_new ();
697         g_assert (keyfile_plugin);
698         if (!add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin)))
699                 g_return_if_reached ();
700 }
701
702 static gboolean
703 load_plugins (NMSettings *self, const char **plugins, GError **error)
704 {
705         GSList *list = NULL;
706         const char **iter;
707         gboolean keyfile_added = FALSE;
708         gboolean success = TRUE;
709         gboolean add_ibft = FALSE;
710         gboolean has_no_ibft;
711         gssize idx_no_ibft, idx_ibft;
712
713         idx_ibft    = _nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
714         idx_no_ibft = _nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
715         has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft;
716 #if WITH_SETTINGS_PLUGIN_IBFT
717         add_ibft = idx_no_ibft < 0 && idx_ibft < 0;
718 #endif
719
720         for (iter = plugins; iter && *iter; iter++) {
721                 const char *pname = *iter;
722                 GObject *obj;
723
724                 if (!*pname || strchr (pname, '/')) {
725                         _LOGW ("ignore invalid plugin \"%s\"", pname);
726                         continue;
727                 }
728
729                 if (!strcmp (pname, "ifcfg-suse")) {
730                         _LOGW ("skipping deprecated plugin ifcfg-suse");
731                         continue;
732                 }
733
734                 if (!strcmp (pname, "no-ibft"))
735                         continue;
736                 if (has_no_ibft && !strcmp (pname, "ibft"))
737                         continue;
738
739                 /* keyfile plugin is built-in now */
740                 if (strcmp (pname, "keyfile") == 0) {
741                         if (!keyfile_added) {
742                                 add_keyfile_plugin (self);
743                                 keyfile_added = TRUE;
744                         }
745                         continue;
746                 }
747
748                 if (_nm_utils_strv_find_first ((char **) plugins,
749                                                iter - plugins,
750                                                pname) >= 0) {
751                         /* the plugin is already mentioned in the list previously.
752                          * Don't load a duplicate. */
753                         continue;
754                 }
755
756                 if (find_plugin (list, pname))
757                         continue;
758
759 load_plugin:
760                 {
761                         GModule *plugin;
762                         gs_free char *full_name = NULL;
763                         gs_free char *path = NULL;
764                         GObject * (*factory_func) (void);
765                         struct stat st;
766                         int errsv;
767
768                         full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
769                         path = g_module_build_path (NMPLUGINDIR, full_name);
770
771                         if (stat (path, &st) != 0) {
772                                 errsv = errno;
773                                 _LOGW ("could not load plugin '%s' from file '%s': %s", pname, path, strerror (errsv));
774                                 goto next;
775                         }
776                         if (!S_ISREG (st.st_mode)) {
777                                 _LOGW ("could not load plugin '%s' from file '%s': not a file", pname, path);
778                                 goto next;
779                         }
780                         if (st.st_uid != 0) {
781                                 _LOGW ("could not load plugin '%s' from file '%s': file must be owned by root", pname, path);
782                                 goto next;
783                         }
784                         if (st.st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
785                                 _LOGW ("could not load plugin '%s' from file '%s': invalid file permissions", pname, path);
786                                 goto next;
787                         }
788
789                         plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
790                         if (!plugin) {
791                                 _LOGW ("could not load plugin '%s' from file '%s': %s",
792                                      pname, path, g_module_error ());
793                                 goto next;
794                         }
795
796                         /* errors after this point are fatal, because we loaded the shared library already. */
797
798                         if (!g_module_symbol (plugin, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
799                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
800                                              "Could not find plugin '%s' factory function.",
801                                              pname);
802                                 success = FALSE;
803                                 g_module_close (plugin);
804                                 break;
805                         }
806
807                         obj = (*factory_func) ();
808                         if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) {
809                                 g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
810                                              "Plugin '%s' returned invalid system config object.",
811                                              pname);
812                                 success = FALSE;
813                                 g_module_close (plugin);
814                                 break;
815                         }
816
817                         g_module_make_resident (plugin);
818                         g_object_weak_ref (obj, (GWeakNotify) g_module_close, plugin);
819                         g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
820                         path = NULL;
821                         if (add_plugin (self, NM_SETTINGS_PLUGIN (obj)))
822                                 list = g_slist_append (list, obj);
823                         else
824                                 g_object_unref (obj);
825                 }
826 next:
827                 if (add_ibft && !strcmp (pname, "ifcfg-rh")) {
828                         /* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh".
829                          * Enable "ibft" by default after "ifcfg-rh". */
830                         pname = "ibft";
831                         add_ibft = FALSE;
832                         goto load_plugin;
833                 }
834         }
835
836         /* If keyfile plugin was not among configured plugins, add it as the last one */
837         if (!keyfile_added)
838                 add_keyfile_plugin (self);
839
840         g_slist_free_full (list, g_object_unref);
841
842         return success;
843 }
844
845 static void
846 connection_updated (NMSettingsConnection *connection, gpointer user_data)
847 {
848         /* Re-emit for listeners like NMPolicy */
849         g_signal_emit (NM_SETTINGS (user_data),
850                        signals[CONNECTION_UPDATED],
851                        0,
852                        connection);
853         g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection);
854 }
855
856 static void
857 connection_updated_by_user (NMSettingsConnection *connection, gpointer user_data)
858 {
859         /* Re-emit for listeners like NMPolicy */
860         g_signal_emit (NM_SETTINGS (user_data),
861                        signals[CONNECTION_UPDATED_BY_USER],
862                        0,
863                        connection);
864 }
865
866 static void
867 connection_visibility_changed (NMSettingsConnection *connection,
868                                GParamSpec *pspec,
869                                gpointer user_data)
870 {
871         /* Re-emit for listeners like NMPolicy */
872         g_signal_emit (NM_SETTINGS (user_data),
873                        signals[CONNECTION_VISIBILITY_CHANGED],
874                        0,
875                        connection);
876 }
877
878 static void
879 connection_removed (NMSettingsConnection *connection, gpointer user_data)
880 {
881         NMSettings *self = NM_SETTINGS (user_data);
882         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
883         const char *cpath = nm_connection_get_path (NM_CONNECTION (connection));
884
885         if (!g_hash_table_lookup (priv->connections, cpath))
886                 g_return_if_reached ();
887         g_object_ref (connection);
888
889         /* Disconnect signal handlers, as plugins might still keep references
890          * to the connection (and thus the signal handlers would still be live)
891          * even after NMSettings has dropped all its references.
892          */
893
894         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
895         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
896         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated_by_user), self);
897         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self);
898         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
899         g_object_unref (self);
900
901         /* Forget about the connection internally */
902         g_hash_table_remove (priv->connections, (gpointer) cpath);
903
904         /* Notify D-Bus */
905         g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection);
906
907         /* Re-emit for listeners like NMPolicy */
908         g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_REMOVED, connection);
909         g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
910         if (nm_exported_object_is_exported (NM_EXPORTED_OBJECT (connection)))
911                 nm_exported_object_unexport (NM_EXPORTED_OBJECT (connection));
912
913         check_startup_complete (self);
914
915         g_object_unref (connection);
916 }
917
918 static void
919 secret_agent_registered (NMAgentManager *agent_mgr,
920                          NMSecretAgent *agent,
921                          gpointer user_data)
922 {
923         /* Re-emit for listeners like NMPolicy */
924         g_signal_emit (NM_SETTINGS (user_data),
925                        signals[AGENT_REGISTERED],
926                        0,
927                        agent);
928 }
929
930 #define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
931 #define NM_OPENCONNECT_KEY_GATEWAY "gateway"
932 #define NM_OPENCONNECT_KEY_COOKIE "cookie"
933 #define NM_OPENCONNECT_KEY_GWCERT "gwcert"
934 #define NM_OPENCONNECT_KEY_XMLCONFIG "xmlconfig"
935 #define NM_OPENCONNECT_KEY_LASTHOST "lasthost"
936 #define NM_OPENCONNECT_KEY_AUTOCONNECT "autoconnect"
937 #define NM_OPENCONNECT_KEY_CERTSIGS "certsigs"
938
939 static void
940 openconnect_migrate_hack (NMConnection *connection)
941 {
942         NMSettingVpn *s_vpn;
943         NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NOT_SAVED;
944
945         /* Huge hack.  There were some openconnect changes that needed to happen
946          * pretty late, too late to get into distros.  Migration has already
947          * happened for many people, and their secret flags are wrong.  But we
948          * don't want to requrie re-migration, so we have to fix it up here. Ugh.
949          */
950
951         s_vpn = nm_connection_get_setting_vpn (connection);
952         if (s_vpn == NULL)
953                 return;
954
955         if (g_strcmp0 (nm_setting_vpn_get_service_type (s_vpn), NM_DBUS_SERVICE_OPENCONNECT) == 0) {
956                 /* These are different for every login session, and should not be stored */
957                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GATEWAY, flags, NULL);
958                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_COOKIE, flags, NULL);
959                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_GWCERT, flags, NULL);
960
961                 /* These are purely internal data for the auth-dialog, and should be stored */
962                 flags = 0;
963                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_XMLCONFIG, flags, NULL);
964                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_LASTHOST, flags, NULL);
965                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_AUTOCONNECT, flags, NULL);
966                 nm_setting_set_secret_flags (NM_SETTING (s_vpn), NM_OPENCONNECT_KEY_CERTSIGS, flags, NULL);
967         }
968 }
969
970 static void
971 claim_connection (NMSettings *self, NMSettingsConnection *connection)
972 {
973         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
974         GError *error = NULL;
975         GHashTableIter iter;
976         gpointer data;
977         const char *path;
978         NMSettingsConnection *existing;
979
980         g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
981         g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
982
983         g_hash_table_iter_init (&iter, priv->connections);
984         while (g_hash_table_iter_next (&iter, NULL, &data)) {
985                 /* prevent duplicates */
986                 if (data == connection)
987                         return;
988         }
989
990         if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) {
991                 _LOGW ("plugin provided invalid connection: %s", error->message);
992                 g_error_free (error);
993                 return;
994         }
995
996         existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (connection));
997         if (existing) {
998                 /* Cannot add duplicate connections per UUID. Just return without action and
999                  * log a warning.
1000                  *
1001                  * This means, that plugins must not provide duplicate connections (UUID).
1002                  * In fact, none of the plugins currently would do that.
1003                  *
1004                  * But globaly, over different setting plugins, there could be duplicates
1005                  * without the individual plugins being aware. Don't handle that at all, just
1006                  * error out. That should not happen unless the admin misconfigured the system
1007                  * to create conflicting connections. */
1008                 _LOGW ("plugin provided duplicate connection with UUID %s",
1009                        nm_settings_connection_get_uuid (connection));
1010                 return;
1011         }
1012
1013         /* Read timestamp from look-aside file and put it into the connection's data */
1014         nm_settings_connection_read_and_fill_timestamp (connection);
1015
1016         /* Read seen-bssids from look-aside file and put it into the connection's data */
1017         nm_settings_connection_read_and_fill_seen_bssids (connection);
1018
1019         /* Ensure it's initial visibility is up-to-date */
1020         nm_settings_connection_recheck_visibility (connection);
1021
1022         /* Evil openconnect migration hack */
1023         openconnect_migrate_hack (NM_CONNECTION (connection));
1024
1025         g_object_ref (self);
1026         g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
1027                           G_CALLBACK (connection_removed), self);
1028         g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED,
1029                           G_CALLBACK (connection_updated), self);
1030         g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
1031                           G_CALLBACK (connection_updated_by_user), self);
1032         g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE,
1033                           G_CALLBACK (connection_visibility_changed),
1034                           self);
1035         if (!priv->startup_complete) {
1036                 g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_READY,
1037                                   G_CALLBACK (connection_ready_changed),
1038                                   self);
1039         }
1040
1041         /* Export the connection over D-Bus */
1042         g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL);
1043         path = nm_exported_object_export (NM_EXPORTED_OBJECT (connection));
1044         nm_connection_set_path (NM_CONNECTION (connection), path);
1045
1046         g_hash_table_insert (priv->connections,
1047                              (gpointer) nm_connection_get_path (NM_CONNECTION (connection)),
1048                              g_object_ref (connection));
1049
1050         nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ");
1051
1052         /* Only emit the individual connection-added signal after connections
1053          * have been initially loaded.
1054          */
1055         if (priv->connections_loaded) {
1056                 /* Internal added signal */
1057                 g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
1058                 g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection);
1059                 g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS);
1060
1061                 /* Exported D-Bus signal */
1062                 g_signal_emit (self, signals[NEW_CONNECTION], 0, connection);
1063         }
1064 }
1065
1066 /**
1067  * nm_settings_add_connection:
1068  * @self: the #NMSettings object
1069  * @connection: the source connection to create a new #NMSettingsConnection from
1070  * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
1071  * not save to disk
1072  * @error: on return, a location to store any errors that may occur
1073  *
1074  * Creates a new #NMSettingsConnection for the given source @connection.  
1075  * The returned object is owned by @self and the caller must reference
1076  * the object to continue using it.
1077  *
1078  * Returns: the new #NMSettingsConnection or %NULL
1079  */
1080 NMSettingsConnection *
1081 nm_settings_add_connection (NMSettings *self,
1082                             NMConnection *connection,
1083                             gboolean save_to_disk,
1084                             GError **error)
1085 {
1086         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1087         GSList *iter;
1088         NMSettingsConnection *added = NULL;
1089         GHashTableIter citer;
1090         NMConnection *candidate = NULL;
1091
1092         /* Make sure a connection with this UUID doesn't already exist */
1093         g_hash_table_iter_init (&citer, priv->connections);
1094         while (g_hash_table_iter_next (&citer, NULL, (gpointer *) &candidate)) {
1095                 if (g_strcmp0 (nm_connection_get_uuid (connection),
1096                                nm_connection_get_uuid (candidate)) == 0) {
1097                         g_set_error_literal (error,
1098                                              NM_SETTINGS_ERROR,
1099                                              NM_SETTINGS_ERROR_UUID_EXISTS,
1100                                              "A connection with this UUID already exists.");
1101                         return NULL;
1102                 }
1103         }
1104
1105         /* 1) plugin writes the NMConnection to disk
1106          * 2) plugin creates a new NMSettingsConnection subclass with the settings
1107          *     from the NMConnection and returns it to the settings service
1108          * 3) settings service exports the new NMSettingsConnection subclass
1109          * 4) plugin notices that something on the filesystem has changed
1110          * 5) plugin reads the changes and ignores them because they will
1111          *     contain the same data as the connection it already knows about
1112          */
1113         for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1114                 NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1115                 GError *add_error = NULL;
1116
1117                 added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
1118                 if (added) {
1119                         claim_connection (self, added);
1120                         return added;
1121                 }
1122                 _LOGD ("Failed to add %s/'%s': %s",
1123                        nm_connection_get_uuid (connection),
1124                        nm_connection_get_id (connection),
1125                        add_error->message);
1126                 g_clear_error (&add_error);
1127         }
1128
1129         g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
1130                              "No plugin supported adding this connection");
1131         return NULL;
1132 }
1133
1134 static NMConnection *
1135 _nm_connection_provider_add_connection (NMConnectionProvider *provider,
1136                                         NMConnection *connection,
1137                                         gboolean save_to_disk,
1138                                         GError **error)
1139 {
1140         g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider));
1141         return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error));
1142 }
1143
1144 static gboolean
1145 secrets_filter_cb (NMSetting *setting,
1146                    const char *secret,
1147                    NMSettingSecretFlags flags,
1148                    gpointer user_data)
1149 {
1150         NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
1151
1152         /* Returns TRUE to remove the secret */
1153
1154         /* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
1155         if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
1156             && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
1157                 return FALSE;
1158
1159         /* Otherwise if the secret has at least one of the desired flags keep it */
1160         return (flags & filter_flags) ? FALSE : TRUE;
1161 }
1162
1163 static void
1164 send_agent_owned_secrets (NMSettings *self,
1165                           NMSettingsConnection *connection,
1166                           NMAuthSubject *subject)
1167 {
1168         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1169         NMConnection *for_agent;
1170
1171         /* Dupe the connection so we can clear out non-agent-owned secrets,
1172          * as agent-owned secrets are the only ones we send back to be saved.
1173          * Only send secrets to agents of the same UID that called update too.
1174          */
1175         for_agent = nm_simple_connection_new_clone (NM_CONNECTION (connection));
1176         nm_connection_clear_secrets_with_flags (for_agent,
1177                                                 secrets_filter_cb,
1178                                                 GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
1179         nm_agent_manager_save_secrets (priv->agent_mgr,
1180                                        nm_connection_get_path (NM_CONNECTION (connection)),
1181                                        for_agent,
1182                                        subject);
1183         g_object_unref (for_agent);
1184 }
1185
1186 static void
1187 pk_add_cb (NMAuthChain *chain,
1188            GError *chain_error,
1189            GDBusMethodInvocation *context,
1190            gpointer user_data)
1191 {
1192         NMSettings *self = NM_SETTINGS (user_data);
1193         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1194         NMAuthCallResult result;
1195         GError *error = NULL;
1196         NMConnection *connection = NULL;
1197         NMSettingsConnection *added = NULL;
1198         NMSettingsAddCallback callback;
1199         gpointer callback_data;
1200         NMAuthSubject *subject;
1201         const char *perm;
1202         gboolean save_to_disk;
1203
1204         g_assert (context);
1205
1206         priv->auths = g_slist_remove (priv->auths, chain);
1207
1208         perm = nm_auth_chain_get_data (chain, "perm");
1209         g_assert (perm);
1210         result = nm_auth_chain_get_result (chain, perm);
1211
1212         if (chain_error) {
1213                 error = g_error_new (NM_SETTINGS_ERROR,
1214                                      NM_SETTINGS_ERROR_FAILED,
1215                                      "Error checking authorization: %s",
1216                                      chain_error->message);
1217         } else if (result != NM_AUTH_CALL_RESULT_YES) {
1218                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1219                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1220                                              "Insufficient privileges.");
1221         } else {
1222                 /* Authorized */
1223                 connection = nm_auth_chain_get_data (chain, "connection");
1224                 g_assert (connection);
1225                 save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
1226                 added = nm_settings_add_connection (self, connection, save_to_disk, &error);
1227         }
1228
1229         callback = nm_auth_chain_get_data (chain, "callback");
1230         callback_data = nm_auth_chain_get_data (chain, "callback-data");
1231         subject = nm_auth_chain_get_data (chain, "subject");
1232
1233         callback (self, added, error, context, subject, callback_data);
1234
1235         /* Send agent-owned secrets to the agents */
1236         if (!error && added && nm_settings_has_connection (self, added))
1237                 send_agent_owned_secrets (self, added, subject);
1238
1239         g_clear_error (&error);
1240         nm_auth_chain_unref (chain);
1241 }
1242
1243 /* FIXME: remove if/when kernel supports adhoc wpa */
1244 static gboolean
1245 is_adhoc_wpa (NMConnection *connection)
1246 {
1247         NMSettingWireless *s_wifi;
1248         NMSettingWirelessSecurity *s_wsec;
1249         const char *mode, *key_mgmt;
1250
1251         /* The kernel doesn't support Ad-Hoc WPA connections well at this time,
1252          * and turns them into open networks.  It's been this way since at least
1253          * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
1254          */
1255
1256         s_wifi = nm_connection_get_setting_wireless (connection);
1257         if (!s_wifi)
1258                 return FALSE;
1259
1260         mode = nm_setting_wireless_get_mode (s_wifi);
1261         if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC) != 0)
1262                 return FALSE;
1263
1264         s_wsec = nm_connection_get_setting_wireless_security (connection);
1265         if (!s_wsec)
1266                 return FALSE;
1267
1268         key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
1269         if (g_strcmp0 (key_mgmt, "wpa-none") != 0)
1270                 return FALSE;
1271
1272         return TRUE;
1273 }
1274
1275 void
1276 nm_settings_add_connection_dbus (NMSettings *self,
1277                                  NMConnection *connection,
1278                                  gboolean save_to_disk,
1279                                  GDBusMethodInvocation *context,
1280                                  NMSettingsAddCallback callback,
1281                                  gpointer user_data)
1282 {
1283         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1284         NMSettingConnection *s_con;
1285         NMAuthSubject *subject = NULL;
1286         NMAuthChain *chain;
1287         GError *error = NULL, *tmp_error = NULL;
1288         char *error_desc = NULL;
1289         const char *perm;
1290
1291         g_return_if_fail (connection != NULL);
1292         g_return_if_fail (context != NULL);
1293
1294         /* Connection must be valid, of course */
1295         if (!nm_connection_verify (connection, &tmp_error)) {
1296                 error = g_error_new (NM_SETTINGS_ERROR,
1297                                      NM_SETTINGS_ERROR_INVALID_CONNECTION,
1298                                      "The connection was invalid: %s",
1299                                      tmp_error->message);
1300                 g_error_free (tmp_error);
1301                 goto done;
1302         }
1303
1304         /* The kernel doesn't support Ad-Hoc WPA connections well at this time,
1305          * and turns them into open networks.  It's been this way since at least
1306          * 2.6.30 or so; until that's fixed, disable WPA-protected Ad-Hoc networks.
1307          */
1308         if (is_adhoc_wpa (connection)) {
1309                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1310                                              NM_SETTINGS_ERROR_INVALID_CONNECTION,
1311                                              "WPA Ad-Hoc disabled due to kernel bugs");
1312                 goto done;
1313         }
1314
1315         /* Do any of the plugins support adding? */
1316         if (!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS)) {
1317                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1318                                              NM_SETTINGS_ERROR_NOT_SUPPORTED,
1319                                              "None of the registered plugins support add.");
1320                 goto done;
1321         }
1322
1323         subject = nm_auth_subject_new_unix_process_from_context (context);
1324         if (!subject) {
1325                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1326                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1327                                              "Unable to determine UID of request.");
1328                 goto done;
1329         }
1330
1331         /* Ensure the caller's username exists in the connection's permissions,
1332          * or that the permissions is empty (ie, visible by everyone).
1333          */
1334         if (!nm_auth_is_subject_in_acl (connection,
1335                                         subject,
1336                                         &error_desc)) {
1337                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1338                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1339                                              error_desc);
1340                 g_free (error_desc);
1341                 goto done;
1342         }
1343
1344         /* If the caller is the only user in the connection's permissions, then
1345          * we use the 'modify.own' permission instead of 'modify.system'.  If the
1346          * request affects more than just the caller, require 'modify.system'.
1347          */
1348         s_con = nm_connection_get_setting_connection (connection);
1349         g_assert (s_con);
1350         if (nm_setting_connection_get_num_permissions (s_con) == 1)
1351                 perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
1352         else
1353                 perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM;
1354
1355         /* Validate the user request */
1356         chain = nm_auth_chain_new_subject (subject, context, pk_add_cb, self);
1357         if (!chain) {
1358                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1359                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1360                                              "Unable to authenticate the request.");
1361                 goto done;
1362         }
1363
1364         priv->auths = g_slist_append (priv->auths, chain);
1365         nm_auth_chain_add_call (chain, perm, TRUE);
1366         nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL);
1367         nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref);
1368         nm_auth_chain_set_data (chain, "callback", callback, NULL);
1369         nm_auth_chain_set_data (chain, "callback-data", user_data, NULL);
1370         nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref);
1371         nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL);
1372
1373 done:
1374         if (error)
1375                 callback (self, NULL, error, context, subject, user_data);
1376
1377         g_clear_error (&error);
1378         g_clear_object (&subject);
1379 }
1380
1381 static void
1382 impl_settings_add_connection_add_cb (NMSettings *self,
1383                                      NMSettingsConnection *connection,
1384                                      GError *error,
1385                                      GDBusMethodInvocation *context,
1386                                      NMAuthSubject *subject,
1387                                      gpointer user_data)
1388 {
1389         if (error) {
1390                 g_dbus_method_invocation_return_gerror (context, error);
1391                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, NULL, FALSE, subject, error->message);
1392         } else {
1393                 g_dbus_method_invocation_return_value (
1394                     context,
1395                     g_variant_new ("(o)", nm_connection_get_path (NM_CONNECTION (connection))));
1396                 nm_audit_log_connection_op (NM_AUDIT_OP_CONN_ADD, connection, TRUE,
1397                                             subject, NULL);
1398         }
1399 }
1400
1401 static void
1402 impl_settings_add_connection_helper (NMSettings *self,
1403                                      GDBusMethodInvocation *context,
1404                                      GVariant *settings,
1405                                      gboolean save_to_disk)
1406 {
1407         NMConnection *connection;
1408         GError *error = NULL;
1409
1410         connection = _nm_simple_connection_new_from_dbus (settings,
1411                                                             NM_SETTING_PARSE_FLAGS_STRICT
1412                                                           | NM_SETTING_PARSE_FLAGS_NORMALIZE,
1413                                                           &error);
1414
1415         if (connection) {
1416                 if (!nm_connection_verify_secrets (connection, &error))
1417                         goto failure;
1418
1419                 nm_settings_add_connection_dbus (self,
1420                                                  connection,
1421                                                  save_to_disk,
1422                                                  context,
1423                                                  impl_settings_add_connection_add_cb,
1424                                                  NULL);
1425                 g_object_unref (connection);
1426                 return;
1427         }
1428
1429 failure:
1430         g_assert (error);
1431         g_dbus_method_invocation_take_error (context, error);
1432 }
1433
1434 static void
1435 impl_settings_add_connection (NMSettings *self,
1436                               GDBusMethodInvocation *context,
1437                               GVariant *settings)
1438 {
1439         impl_settings_add_connection_helper (self, context, settings, TRUE);
1440 }
1441
1442 static void
1443 impl_settings_add_connection_unsaved (NMSettings *self,
1444                                       GDBusMethodInvocation *context,
1445                                       GVariant *settings)
1446 {
1447         impl_settings_add_connection_helper (self, context, settings, FALSE);
1448 }
1449
1450 static gboolean
1451 ensure_root (NMBusManager          *dbus_mgr,
1452              GDBusMethodInvocation *context)
1453 {
1454         gulong caller_uid;
1455         GError *error = NULL;
1456
1457         if (!nm_bus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) {
1458                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1459                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1460                                              "Unable to determine request UID.");
1461                 g_dbus_method_invocation_take_error (context, error);
1462                 return FALSE;
1463         }
1464         if (caller_uid != 0) {
1465                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1466                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1467                                              "Permission denied");
1468                 g_dbus_method_invocation_take_error (context, error);
1469                 return FALSE;
1470         }
1471
1472         return TRUE;
1473 }
1474
1475 static void
1476 impl_settings_load_connections (NMSettings *self,
1477                                 GDBusMethodInvocation *context,
1478                                 char **filenames)
1479 {
1480         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1481         GPtrArray *failures;
1482         GSList *iter;
1483         int i;
1484
1485         if (!ensure_root (nm_bus_manager_get (), context))
1486                 return;
1487
1488         failures = g_ptr_array_new ();
1489
1490         for (i = 0; filenames[i]; i++) {
1491                 for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1492                         NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1493
1494                         if (nm_settings_plugin_load_connection (plugin, filenames[i]))
1495                                 break;
1496                 }
1497
1498                 if (!iter) {
1499                         if (!g_path_is_absolute (filenames[i]))
1500                                 _LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
1501                         g_ptr_array_add (failures, (char *) filenames[i]);
1502                 }
1503         }
1504
1505         g_ptr_array_add (failures, NULL);
1506         g_dbus_method_invocation_return_value (
1507                 context,
1508                 g_variant_new ("(b^as)",
1509                                failures->len == 1,
1510                                failures->pdata));
1511         g_ptr_array_unref (failures);
1512 }
1513
1514 static void
1515 impl_settings_reload_connections (NMSettings *self,
1516                                   GDBusMethodInvocation *context)
1517 {
1518         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1519         GSList *iter;
1520
1521         if (!ensure_root (nm_bus_manager_get (), context))
1522                 return;
1523
1524         for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1525                 NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1526
1527                 nm_settings_plugin_reload_connections (plugin);
1528         }
1529
1530         g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE));
1531 }
1532
1533 typedef struct {
1534         char *hostname;
1535         NMSettingsSetHostnameCb cb;
1536         gpointer user_data;
1537 } SetHostnameInfo;
1538
1539 static void
1540 set_transient_hostname_done (GObject *object,
1541                              GAsyncResult *res,
1542                              gpointer user_data)
1543 {
1544         GDBusProxy *proxy = G_DBUS_PROXY (object);
1545         gs_free SetHostnameInfo *info = user_data;
1546         gs_unref_variant GVariant *result = NULL;
1547         gs_free_error GError *error = NULL;
1548
1549         result = g_dbus_proxy_call_finish (proxy, res, &error);
1550
1551         if (error) {
1552                 _LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s",
1553                        info->hostname, error->message);
1554         }
1555
1556         info->cb (info->hostname, !error, info->user_data);
1557         g_free (info->hostname);
1558 }
1559
1560 void
1561 nm_settings_set_transient_hostname (NMSettings *self,
1562                                     const char *hostname,
1563                                     NMSettingsSetHostnameCb cb,
1564                                     gpointer user_data)
1565 {
1566         NMSettingsPrivate *priv;
1567         SetHostnameInfo *info;
1568
1569         g_return_if_fail (NM_IS_SETTINGS (self));
1570         priv = NM_SETTINGS_GET_PRIVATE (self);
1571
1572         if (!priv->hostname.hostnamed_proxy) {
1573                 cb (hostname, FALSE, user_data);
1574                 return;
1575         }
1576
1577         info = g_new0 (SetHostnameInfo, 1);
1578         info->hostname = g_strdup (hostname);
1579         info->cb = cb;
1580         info->user_data = user_data;
1581
1582         g_dbus_proxy_call (priv->hostname.hostnamed_proxy,
1583                            "SetHostname",
1584                            g_variant_new ("(sb)", hostname, FALSE),
1585                            G_DBUS_CALL_FLAGS_NONE,
1586                            -1,
1587                            NULL,
1588                            set_transient_hostname_done,
1589                            info);
1590 }
1591
1592 static gboolean
1593 write_hostname (NMSettingsPrivate *priv, const char *hostname)
1594 {
1595         char *hostname_eol;
1596         gboolean ret;
1597         gs_free_error GError *error = NULL;
1598         const char *file = priv->hostname.file;
1599         gs_free char *link_path = NULL;
1600         gs_unref_variant GVariant *var = NULL;
1601         struct stat file_stat;
1602 #if HAVE_SELINUX
1603         security_context_t se_ctx_prev = NULL, se_ctx = NULL;
1604         mode_t st_mode = 0;
1605 #endif
1606
1607         if (priv->hostname.hostnamed_proxy) {
1608                 var = g_dbus_proxy_call_sync (priv->hostname.hostnamed_proxy,
1609                                               "SetStaticHostname",
1610                                               g_variant_new ("(sb)", hostname, FALSE),
1611                                               G_DBUS_CALL_FLAGS_NONE,
1612                                               -1,
1613                                               NULL,
1614                                               &error);
1615                 if (error)
1616                         _LOGW ("could not set hostname: %s", error->message);
1617
1618                 return !error;
1619         }
1620
1621         /* If the hostname file is a symbolic link, follow it to find where the
1622          * real file is located, otherwise g_file_set_contents will attempt to
1623          * replace the link with a plain file.
1624          */
1625         if (   lstat (file, &file_stat) == 0
1626             && S_ISLNK (file_stat.st_mode)
1627             && (link_path = g_file_read_link (file, NULL)))
1628                 file = link_path;
1629
1630 #if HAVE_SELINUX
1631         /* Get default context for hostname file and set it for fscreate */
1632         if (stat (file, &file_stat) == 0)
1633                 st_mode = file_stat.st_mode;
1634         matchpathcon (file, st_mode, &se_ctx);
1635         matchpathcon_fini ();
1636         getfscreatecon (&se_ctx_prev);
1637         setfscreatecon (se_ctx);
1638 #endif
1639
1640 #if defined (HOSTNAME_PERSIST_GENTOO)
1641         hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n"
1642                                         "hostname=\"%s\"\n", hostname);
1643 #else
1644         hostname_eol = g_strdup_printf ("%s\n", hostname);
1645 #endif
1646
1647         /* FIXME: g_file_set_contents() writes first to a temporary file
1648          * and renames it atomically. We should hack g_file_set_contents()
1649          * to set the SELINUX labels before renaming the file. */
1650         ret = g_file_set_contents (file, hostname_eol, -1, &error);
1651
1652 #if HAVE_SELINUX
1653         /* Restore previous context and cleanup */
1654         setfscreatecon (se_ctx_prev);
1655         freecon (se_ctx);
1656         freecon (se_ctx_prev);
1657 #endif
1658
1659         g_free (hostname_eol);
1660
1661         if (!ret) {
1662                 _LOGW ("could not save hostname to %s: %s", file, error->message);
1663                 return FALSE;
1664         }
1665
1666         return TRUE;
1667 }
1668
1669 static void
1670 pk_hostname_cb (NMAuthChain *chain,
1671                 GError *chain_error,
1672                 GDBusMethodInvocation *context,
1673                 gpointer user_data)
1674 {
1675         NMSettings *self = NM_SETTINGS (user_data);
1676         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1677         NMAuthCallResult result;
1678         GError *error = NULL;
1679         const char *hostname;
1680
1681         g_assert (context);
1682
1683         priv->auths = g_slist_remove (priv->auths, chain);
1684
1685         result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
1686
1687         /* If our NMSettingsConnection is already gone, do nothing */
1688         if (chain_error) {
1689                 error = g_error_new (NM_SETTINGS_ERROR,
1690                                      NM_SETTINGS_ERROR_FAILED,
1691                                      "Error checking authorization: %s",
1692                                      chain_error->message);
1693         } else if (result != NM_AUTH_CALL_RESULT_YES) {
1694                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1695                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1696                                              "Insufficient privileges.");
1697         } else {
1698                 hostname = nm_auth_chain_get_data (chain, "hostname");
1699
1700                 if (!write_hostname (priv, hostname)) {
1701                         error = g_error_new_literal (NM_SETTINGS_ERROR,
1702                                                      NM_SETTINGS_ERROR_FAILED,
1703                                                      "Saving the hostname failed.");
1704                 }
1705         }
1706
1707         if (error)
1708                 g_dbus_method_invocation_take_error (context, error);
1709         else
1710                 g_dbus_method_invocation_return_value (context, NULL);
1711
1712         nm_auth_chain_unref (chain);
1713 }
1714
1715 static gboolean
1716 validate_hostname (const char *hostname)
1717 {
1718         const char *p;
1719         gboolean dot = TRUE;
1720
1721         if (!hostname || !hostname[0])
1722                 return FALSE;
1723
1724         for (p = hostname; *p; p++) {
1725                 if (*p == '.') {
1726                         if (dot)
1727                                 return FALSE;
1728                         dot = TRUE;
1729                 } else {
1730                         if (!g_ascii_isalnum (*p) && (*p != '-') && (*p != '_'))
1731                                 return FALSE;
1732                         dot = FALSE;
1733                 }
1734         }
1735
1736         if (dot)
1737                 return FALSE;
1738
1739         return (p - hostname <= HOST_NAME_MAX);
1740 }
1741
1742 static void
1743 impl_settings_save_hostname (NMSettings *self,
1744                              GDBusMethodInvocation *context,
1745                              const char *hostname)
1746 {
1747         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1748         NMAuthChain *chain;
1749         GError *error = NULL;
1750
1751         /* Minimal validation of the hostname */
1752         if (!validate_hostname (hostname)) {
1753                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1754                                              NM_SETTINGS_ERROR_INVALID_HOSTNAME,
1755                                              "The hostname was too long or contained invalid characters.");
1756                 goto done;
1757         }
1758
1759         chain = nm_auth_chain_new_context (context, pk_hostname_cb, self);
1760         if (!chain) {
1761                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1762                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1763                                              "Unable to authenticate the request.");
1764                 goto done;
1765         }
1766
1767         priv->auths = g_slist_append (priv->auths, chain);
1768         nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE);
1769         nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free);
1770
1771 done:
1772         if (error)
1773                 g_dbus_method_invocation_take_error (context, error);
1774 }
1775
1776 static void
1777 hostname_maybe_changed (NMSettings *settings)
1778 {
1779         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (settings);
1780         char *new_hostname;
1781
1782         new_hostname = nm_settings_get_hostname (settings);
1783
1784         if (   (new_hostname && !priv->hostname.value)
1785             || (!new_hostname && priv->hostname.value)
1786             || (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) {
1787
1788                 _LOGI ("hostname changed from %s%s%s to %s%s%s",
1789                        NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
1790                        NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)"));
1791                 g_free (priv->hostname.value);
1792                 priv->hostname.value = new_hostname;
1793                 g_object_notify (G_OBJECT (settings), NM_SETTINGS_HOSTNAME);
1794         } else
1795                 g_free (new_hostname);
1796 }
1797
1798 static void
1799 hostname_file_changed_cb (GFileMonitor *monitor,
1800                           GFile *file,
1801                           GFile *other_file,
1802                           GFileMonitorEvent event_type,
1803                           gpointer user_data)
1804 {
1805         hostname_maybe_changed (user_data);
1806 }
1807
1808 static gboolean
1809 have_connection_for_device (NMSettings *self, NMDevice *device)
1810 {
1811         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1812         GHashTableIter iter;
1813         gpointer data;
1814         NMSettingConnection *s_con;
1815         NMSettingWired *s_wired;
1816         const char *setting_hwaddr;
1817         const char *device_hwaddr;
1818
1819         g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
1820
1821         device_hwaddr = nm_device_get_hw_address (device);
1822
1823         /* Find a wired connection locked to the given MAC address, if any */
1824         g_hash_table_iter_init (&iter, priv->connections);
1825         while (g_hash_table_iter_next (&iter, NULL, &data)) {
1826                 NMConnection *connection = NM_CONNECTION (data);
1827                 const char *ctype, *iface;
1828
1829                 if (!nm_device_check_connection_compatible (device, connection))
1830                         continue;
1831
1832                 s_con = nm_connection_get_setting_connection (connection);
1833
1834                 iface = nm_setting_connection_get_interface_name (s_con);
1835                 if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
1836                         continue;
1837
1838                 ctype = nm_setting_connection_get_connection_type (s_con);
1839                 if (   strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME)
1840                     && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
1841                         continue;
1842
1843                 s_wired = nm_connection_get_setting_wired (connection);
1844
1845                 if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
1846                         /* No wired setting; therefore the PPPoE connection applies to any device */
1847                         return TRUE;
1848                 }
1849
1850                 g_assert (s_wired != NULL);
1851
1852                 setting_hwaddr = nm_setting_wired_get_mac_address (s_wired);
1853                 if (setting_hwaddr) {
1854                         /* A connection mac-locked to this device */
1855                         if (   device_hwaddr
1856                             && nm_utils_hwaddr_matches (setting_hwaddr, -1, device_hwaddr, -1))
1857                                 return TRUE;
1858                 } else {
1859                         /* A connection that applies to any wired device */
1860                         return TRUE;
1861                 }
1862         }
1863
1864         /* See if there's a known non-NetworkManager configuration for the device */
1865         if (nm_device_spec_match_list (device, priv->unrecognized_specs))
1866                 return TRUE;
1867
1868         return FALSE;
1869 }
1870
1871 #define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection"
1872 #define DEFAULT_WIRED_DEVICE_TAG     "default-wired-device"
1873
1874 static void default_wired_clear_tag (NMSettings *self,
1875                                      NMDevice *device,
1876                                      NMSettingsConnection *connection,
1877                                      gboolean add_to_no_auto_default);
1878
1879 static void
1880 default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
1881 {
1882         NMDevice *device;
1883
1884         /* When the default wired connection is removed (either deleted or saved to
1885          * a new persistent connection by a plugin), write the MAC address of the
1886          * wired device to the config file and don't create a new default wired
1887          * connection for that device again.
1888          */
1889         device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
1890         if (device)
1891                 default_wired_clear_tag (self, device, connection, TRUE);
1892 }
1893
1894 static void
1895 default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, NMSettings *self)
1896 {
1897         NMDevice *device;
1898
1899         /* The connection has been changed by the user, it should no longer be
1900          * considered a default wired connection, and should no longer affect
1901          * the no-auto-default configuration option.
1902          */
1903         device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
1904         if (device)
1905                 default_wired_clear_tag (self, device, connection, FALSE);
1906 }
1907
1908 static void
1909 default_wired_clear_tag (NMSettings *self,
1910                          NMDevice *device,
1911                          NMSettingsConnection *connection,
1912                          gboolean add_to_no_auto_default)
1913 {
1914         g_return_if_fail (NM_IS_SETTINGS (self));
1915         g_return_if_fail (NM_IS_DEVICE (device));
1916         g_return_if_fail (NM_IS_CONNECTION (connection));
1917         g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG));
1918         g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG));
1919
1920         g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL);
1921         g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL);
1922
1923         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
1924         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
1925
1926         if (add_to_no_auto_default)
1927                 nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device);
1928 }
1929
1930 static void
1931 device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
1932 {
1933         NMConnection *connection;
1934         NMSettingsConnection *added;
1935         GError *error = NULL;
1936
1937         if (!nm_device_is_real (device))
1938                 return;
1939
1940         g_signal_handlers_disconnect_by_func (device,
1941                                               G_CALLBACK (device_realized),
1942                                               self);
1943
1944         /* If the device isn't managed or it already has a default wired connection,
1945          * ignore it.
1946          */
1947         if (   !nm_device_get_managed (device, FALSE)
1948             || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)
1949             || have_connection_for_device (self, device))
1950                 return;
1951
1952         connection = nm_device_new_default_connection (device);
1953         if (!connection)
1954                 return;
1955
1956         /* Add the connection */
1957         added = nm_settings_add_connection (self, connection, FALSE, &error);
1958         g_object_unref (connection);
1959
1960         if (!added) {
1961                 _LOGW ("(%s) couldn't create default wired connection: %s",
1962                        nm_device_get_iface (device),
1963                        error->message);
1964                 g_clear_error (&error);
1965                 return;
1966         }
1967
1968         g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
1969         g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
1970
1971         g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
1972                           G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
1973         g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
1974                           G_CALLBACK (default_wired_connection_removed_cb), self);
1975
1976         _LOGI ("(%s): created default wired connection '%s'",
1977                nm_device_get_iface (device),
1978                nm_settings_connection_get_id (added));
1979 }
1980
1981 void
1982 nm_settings_device_added (NMSettings *self, NMDevice *device)
1983 {
1984         if (nm_device_is_real (device))
1985                 device_realized (device, NULL, self);
1986         else {
1987                 g_signal_connect_after (device, "notify::" NM_DEVICE_REAL,
1988                                         G_CALLBACK (device_realized),
1989                                         self);
1990         }
1991 }
1992
1993 void
1994 nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting)
1995 {
1996         NMSettingsConnection *connection;
1997
1998         g_signal_handlers_disconnect_by_func (device,
1999                                               G_CALLBACK (device_realized),
2000                                               self);
2001
2002         connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
2003         if (connection) {
2004                 default_wired_clear_tag (self, device, connection, FALSE);
2005
2006                 /* Don't delete the default wired connection on shutdown, so that it
2007                  * remains up and can be assumed if NM starts again.
2008                  */
2009                 if (quitting == FALSE)
2010                         nm_settings_connection_delete (connection, NULL, NULL);
2011         }
2012 }
2013
2014 /***************************************************************/
2015
2016 /* GCompareFunc helper for sorting "best" connections.
2017  * The function sorts connections in ascending timestamp order.
2018  * That means an older connection (lower timestamp) goes before
2019  * a newer one.
2020  */
2021 gint
2022 nm_settings_sort_connections (gconstpointer a, gconstpointer b)
2023 {
2024         NMSettingsConnection *ac = (NMSettingsConnection *) a;
2025         NMSettingsConnection *bc = (NMSettingsConnection *) b;
2026         guint64 ats = 0, bts = 0;
2027
2028         if (ac == bc)
2029                 return 0;
2030         if (!ac)
2031                 return -1;
2032         if (!bc)
2033                 return 1;
2034
2035         /* In the future we may use connection priorities in addition to timestamps */
2036         nm_settings_connection_get_timestamp (ac, &ats);
2037         nm_settings_connection_get_timestamp (bc, &bts);
2038
2039         if (ats < bts)
2040                 return -1;
2041         else if (ats > bts)
2042                 return 1;
2043         return 0;
2044 }
2045
2046 static GSList *
2047 get_best_connections (NMConnectionProvider *provider,
2048                       guint max_requested,
2049                       const char *ctype1,
2050                       const char *ctype2,
2051                       NMConnectionFilterFunc func,
2052                       gpointer func_data)
2053 {
2054         NMSettings *self = NM_SETTINGS (provider);
2055         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2056         GSList *sorted = NULL;
2057         GHashTableIter iter;
2058         NMSettingsConnection *connection;
2059         guint added = 0;
2060         guint64 oldest = 0;
2061
2062         g_hash_table_iter_init (&iter, priv->connections);
2063         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
2064                 guint64 cur_ts = 0;
2065
2066                 if (ctype1 && !nm_connection_is_type (NM_CONNECTION (connection), ctype1))
2067                         continue;
2068                 if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
2069                         continue;
2070                 if (func && !func (provider, NM_CONNECTION (connection), func_data))
2071                         continue;
2072
2073                 /* Don't bother with a connection that's older than the oldest one in the list */
2074                 if (max_requested && added >= max_requested) {
2075                     nm_settings_connection_get_timestamp (connection, &cur_ts);
2076                     if (cur_ts <= oldest)
2077                                 continue;
2078                 }
2079
2080                 /* List is sorted with oldest first */
2081                 sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
2082                 added++;
2083
2084                 if (max_requested && added > max_requested) {
2085                         /* Over the limit, remove the oldest one */
2086                         sorted = g_slist_delete_link (sorted, sorted);
2087                         added--;
2088                 }
2089
2090                 nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data), &oldest);
2091         }
2092
2093         return g_slist_reverse (sorted);
2094 }
2095
2096 static const GSList *
2097 get_connections (NMConnectionProvider *provider)
2098 {
2099         GSList *list = NULL;
2100         NMSettings *self = NM_SETTINGS (provider);
2101         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2102
2103         list = _nm_utils_hash_values_to_slist (priv->connections);
2104
2105         /* Cache the list every call so we can keep it 'const' for callers */
2106         g_slist_free (priv->get_connections_cache);
2107         priv->get_connections_cache = list;
2108         return list;
2109 }
2110
2111 static NMConnection *
2112 cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
2113 {
2114         return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
2115 }
2116
2117 /***************************************************************/
2118
2119 gboolean
2120 nm_settings_get_startup_complete (NMSettings *self)
2121 {
2122         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2123
2124         return priv->startup_complete;
2125 }
2126
2127 /***************************************************************/
2128
2129 static void
2130 hostnamed_properties_changed (GDBusProxy *proxy,
2131                               GVariant *changed_properties,
2132                               char **invalidated_properties,
2133                               gpointer user_data)
2134 {
2135         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (user_data);
2136         GVariant *v_hostname;
2137         const char *hostname;
2138
2139         v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy,
2140                                                        "StaticHostname");
2141         if (!v_hostname)
2142                 return;
2143
2144         hostname = g_variant_get_string (v_hostname, NULL);
2145
2146         if (g_strcmp0 (priv->hostname.value, hostname) != 0) {
2147                 _LOGI ("hostname changed from %s%s%s to %s%s%s",
2148                        NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
2149                        NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)"));
2150                 g_free (priv->hostname.value);
2151                 priv->hostname.value = g_strdup (hostname);
2152                 g_object_notify (G_OBJECT (user_data), NM_SETTINGS_HOSTNAME);
2153                 nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
2154         }
2155
2156         g_variant_unref (v_hostname);
2157 }
2158
2159 static void
2160 setup_hostname_file_monitors (NMSettings *self)
2161 {
2162         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2163         GFileMonitor *monitor;
2164         GFile *file;
2165
2166         priv->hostname.file = HOSTNAME_FILE;
2167         priv->hostname.value = nm_settings_get_hostname (self);
2168
2169         /* monitor changes to hostname file */
2170         file = g_file_new_for_path (priv->hostname.file);
2171         monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
2172         g_object_unref (file);
2173         if (monitor) {
2174                 priv->hostname.monitor_id = g_signal_connect (monitor, "changed",
2175                                                               G_CALLBACK (hostname_file_changed_cb),
2176                                                               self);
2177                 priv->hostname.monitor = monitor;
2178         }
2179
2180 #if defined (HOSTNAME_PERSIST_SUSE)
2181         /* monitor changes to dhcp file to know whether the hostname is valid */
2182         file = g_file_new_for_path (CONF_DHCP);
2183         monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
2184         g_object_unref (file);
2185         if (monitor) {
2186                 priv->hostname.dhcp_monitor_id = g_signal_connect (monitor, "changed",
2187                                                                    G_CALLBACK (hostname_file_changed_cb),
2188                                                                    self);
2189                 priv->hostname.dhcp_monitor = monitor;
2190         }
2191 #endif
2192
2193         hostname_maybe_changed (self);
2194 }
2195
2196 NMSettings *
2197 nm_settings_new (void)
2198 {
2199         NMSettings *self;
2200         NMSettingsPrivate *priv;
2201
2202         self = g_object_new (NM_TYPE_SETTINGS, NULL);
2203
2204         priv = NM_SETTINGS_GET_PRIVATE (self);
2205
2206         priv->config = nm_config_get ();
2207
2208         nm_exported_object_export (NM_EXPORTED_OBJECT (self));
2209         return self;
2210 }
2211
2212 gboolean
2213 nm_settings_start (NMSettings *self, GError **error)
2214 {
2215         NMSettingsPrivate *priv;
2216         GDBusProxy *proxy;
2217         GVariant *variant;
2218         GError *local_error = NULL;
2219
2220         priv = NM_SETTINGS_GET_PRIVATE (self);
2221
2222         /* Load the plugins; fail if a plugin is not found. */
2223         if (!load_plugins (self, nm_config_get_plugins (priv->config), error)) {
2224                 g_object_unref (self);
2225                 return FALSE;
2226         }
2227
2228         load_connections (self);
2229         check_startup_complete (self);
2230
2231         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL,
2232                                                HOSTNAMED_SERVICE_NAME, HOSTNAMED_SERVICE_PATH,
2233                                                HOSTNAMED_SERVICE_INTERFACE, NULL, &local_error);
2234         if (proxy) {
2235                 variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname");
2236                 if (variant) {
2237                         _LOGI ("hostname: using hostnamed");
2238                         priv->hostname.hostnamed_proxy = proxy;
2239                         g_signal_connect (proxy, "g-properties-changed",
2240                                           G_CALLBACK (hostnamed_properties_changed), self);
2241                         hostnamed_properties_changed (proxy, NULL, NULL, self);
2242                         g_variant_unref (variant);
2243                 } else {
2244                         _LOGI ("hostname: couldn't get property from hostnamed");
2245                         g_object_unref (proxy);
2246                 }
2247         } else {
2248                 _LOGI ("hostname: hostnamed not used as proxy creation failed with: %s",
2249                        local_error->message);
2250                 g_clear_error (&local_error);
2251         }
2252
2253         if (!priv->hostname.hostnamed_proxy)
2254                 setup_hostname_file_monitors (self);
2255
2256         priv->started = TRUE;
2257         g_object_notify (G_OBJECT (self), NM_SETTINGS_HOSTNAME);
2258         return TRUE;
2259 }
2260
2261 static void
2262 connection_provider_iface_init (NMConnectionProviderInterface *cp_iface)
2263 {
2264     cp_iface->get_best_connections = get_best_connections;
2265     cp_iface->get_connections = get_connections;
2266     cp_iface->add_connection = _nm_connection_provider_add_connection;
2267     cp_iface->get_connection_by_uuid = cp_get_connection_by_uuid;
2268 }
2269
2270 static void
2271 nm_settings_init (NMSettings *self)
2272 {
2273         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2274
2275         priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
2276
2277         /* Hold a reference to the agent manager so it stays alive; the only
2278          * other holders are NMSettingsConnection objects which are often
2279          * transient, and we don't want the agent manager to get destroyed and
2280          * recreated often.
2281          */
2282         priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
2283
2284         g_signal_connect (priv->agent_mgr, "agent-registered", G_CALLBACK (secret_agent_registered), self);
2285 }
2286
2287 static void
2288 dispose (GObject *object)
2289 {
2290         NMSettings *self = NM_SETTINGS (object);
2291         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2292
2293         g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref);
2294         priv->auths = NULL;
2295
2296         g_object_unref (priv->agent_mgr);
2297
2298         if (priv->hostname.hostnamed_proxy) {
2299                 g_signal_handlers_disconnect_by_func (priv->hostname.hostnamed_proxy,
2300                                                       G_CALLBACK (hostnamed_properties_changed),
2301                                                       self);
2302                 g_clear_object (&priv->hostname.hostnamed_proxy);
2303         }
2304
2305         if (priv->hostname.monitor) {
2306                 if (priv->hostname.monitor_id)
2307                         g_signal_handler_disconnect (priv->hostname.monitor, priv->hostname.monitor_id);
2308
2309                 g_file_monitor_cancel (priv->hostname.monitor);
2310                 g_clear_object (&priv->hostname.monitor);
2311         }
2312
2313         if (priv->hostname.dhcp_monitor) {
2314                 if (priv->hostname.dhcp_monitor_id)
2315                         g_signal_handler_disconnect (priv->hostname.dhcp_monitor,
2316                                                      priv->hostname.dhcp_monitor_id);
2317
2318                 g_file_monitor_cancel (priv->hostname.dhcp_monitor);
2319                 g_clear_object (&priv->hostname.dhcp_monitor);
2320         }
2321
2322         g_clear_pointer (&priv->hostname.value, g_free);
2323
2324         G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
2325 }
2326
2327 static void
2328 finalize (GObject *object)
2329 {
2330         NMSettings *self = NM_SETTINGS (object);
2331         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2332
2333         g_hash_table_destroy (priv->connections);
2334         g_slist_free (priv->get_connections_cache);
2335
2336         g_slist_free_full (priv->unmanaged_specs, g_free);
2337         g_slist_free_full (priv->unrecognized_specs, g_free);
2338
2339         g_slist_free_full (priv->plugins, g_object_unref);
2340
2341         G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
2342 }
2343
2344 static void
2345 get_property (GObject *object, guint prop_id,
2346               GValue *value, GParamSpec *pspec)
2347 {
2348         NMSettings *self = NM_SETTINGS (object);
2349         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2350         const GSList *specs, *iter;
2351         GHashTableIter citer;
2352         GPtrArray *array;
2353         const char *path;
2354
2355         switch (prop_id) {
2356         case PROP_UNMANAGED_SPECS:
2357                 array = g_ptr_array_new ();
2358                 specs = nm_settings_get_unmanaged_specs (self);
2359                 for (iter = specs; iter; iter = g_slist_next (iter))
2360                         g_ptr_array_add (array, g_strdup (iter->data));
2361                 g_ptr_array_add (array, NULL);
2362                 g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
2363                 break;
2364         case PROP_HOSTNAME:
2365                 g_value_take_string (value, nm_settings_get_hostname (self));
2366
2367                 /* Don't ever pass NULL through D-Bus */
2368                 if (!g_value_get_string (value))
2369                         g_value_set_static_string (value, "");
2370                 break;
2371         case PROP_CAN_MODIFY:
2372                 g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS));
2373                 break;
2374         case PROP_CONNECTIONS:
2375                 array = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
2376                 g_hash_table_iter_init (&citer, priv->connections);
2377                 while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL))
2378                         g_ptr_array_add (array, g_strdup (path));
2379                 g_ptr_array_add (array, NULL);
2380                 g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
2381                 break;
2382         case PROP_STARTUP_COMPLETE:
2383                 g_value_set_boolean (value, nm_settings_get_startup_complete (self));
2384                 break;
2385         default:
2386                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2387                 break;
2388         }
2389 }
2390
2391 static void
2392 nm_settings_class_init (NMSettingsClass *class)
2393 {
2394         GObjectClass *object_class = G_OBJECT_CLASS (class);
2395         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
2396
2397         g_type_class_add_private (class, sizeof (NMSettingsPrivate));
2398
2399         exported_object_class->export_path = NM_DBUS_PATH_SETTINGS;
2400
2401         /* virtual methods */
2402         object_class->get_property = get_property;
2403         object_class->dispose = dispose;
2404         object_class->finalize = finalize;
2405
2406         /* properties */
2407
2408         g_object_class_install_property
2409                 (object_class, PROP_UNMANAGED_SPECS,
2410                  g_param_spec_boxed (NM_SETTINGS_UNMANAGED_SPECS, "", "",
2411                                      G_TYPE_STRV,
2412                                      G_PARAM_READABLE |
2413                                      G_PARAM_STATIC_STRINGS));
2414
2415         g_object_class_install_property
2416                 (object_class, PROP_HOSTNAME,
2417                  g_param_spec_string (NM_SETTINGS_HOSTNAME, "", "",
2418                                       NULL,
2419                                       G_PARAM_READABLE |
2420                                       G_PARAM_STATIC_STRINGS));
2421
2422         g_object_class_install_property
2423                 (object_class, PROP_CAN_MODIFY,
2424                  g_param_spec_boolean (NM_SETTINGS_CAN_MODIFY, "", "",
2425                                        FALSE,
2426                                        G_PARAM_READABLE |
2427                                        G_PARAM_STATIC_STRINGS));
2428
2429         g_object_class_install_property
2430                 (object_class, PROP_CONNECTIONS,
2431                  g_param_spec_boxed (NM_SETTINGS_CONNECTIONS, "", "",
2432                                      G_TYPE_STRV,
2433                                      G_PARAM_READABLE |
2434                                      G_PARAM_STATIC_STRINGS));
2435
2436         /* signals */
2437         signals[CONNECTION_ADDED] = 
2438                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
2439                                       G_OBJECT_CLASS_TYPE (object_class),
2440                                       G_SIGNAL_RUN_FIRST,
2441                                       G_STRUCT_OFFSET (NMSettingsClass, connection_added),
2442                                       NULL, NULL,
2443                                       g_cclosure_marshal_VOID__OBJECT,
2444                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2445
2446         signals[CONNECTION_UPDATED] = 
2447                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
2448                                       G_OBJECT_CLASS_TYPE (object_class),
2449                                       G_SIGNAL_RUN_FIRST,
2450                                       G_STRUCT_OFFSET (NMSettingsClass, connection_updated),
2451                                       NULL, NULL,
2452                                       g_cclosure_marshal_VOID__OBJECT,
2453                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2454
2455         signals[CONNECTION_UPDATED_BY_USER] =
2456                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER,
2457                                       G_OBJECT_CLASS_TYPE (object_class),
2458                                       G_SIGNAL_RUN_FIRST,
2459                                       0,
2460                                       NULL, NULL,
2461                                       g_cclosure_marshal_VOID__OBJECT,
2462                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2463
2464         signals[CONNECTION_REMOVED] = 
2465                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
2466                                       G_OBJECT_CLASS_TYPE (object_class),
2467                                       G_SIGNAL_RUN_FIRST,
2468                                       G_STRUCT_OFFSET (NMSettingsClass, connection_removed),
2469                                       NULL, NULL,
2470                                       g_cclosure_marshal_VOID__OBJECT,
2471                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2472
2473         signals[CONNECTION_VISIBILITY_CHANGED] = 
2474                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
2475                                       G_OBJECT_CLASS_TYPE (object_class),
2476                                       G_SIGNAL_RUN_FIRST,
2477                                       G_STRUCT_OFFSET (NMSettingsClass, connection_visibility_changed),
2478                                       NULL, NULL,
2479                                       g_cclosure_marshal_VOID__OBJECT,
2480                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2481
2482         signals[AGENT_REGISTERED] =
2483                 g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED,
2484                               G_OBJECT_CLASS_TYPE (object_class),
2485                               G_SIGNAL_RUN_FIRST,
2486                               G_STRUCT_OFFSET (NMSettingsClass, agent_registered),
2487                               NULL, NULL,
2488                               g_cclosure_marshal_VOID__OBJECT,
2489                               G_TYPE_NONE, 1, NM_TYPE_SECRET_AGENT);
2490
2491
2492         signals[NEW_CONNECTION] = 
2493                         g_signal_new ("new-connection",
2494                                       G_OBJECT_CLASS_TYPE (object_class),
2495                                       G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
2496                                       g_cclosure_marshal_VOID__OBJECT,
2497                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2498
2499         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
2500                                                 NMDBUS_TYPE_SETTINGS_SKELETON,
2501                                                 "ListConnections", impl_settings_list_connections,
2502                                                 "GetConnectionByUuid", impl_settings_get_connection_by_uuid,
2503                                                 "AddConnection", impl_settings_add_connection,
2504                                                 "AddConnectionUnsaved", impl_settings_add_connection_unsaved,
2505                                                 "LoadConnections", impl_settings_load_connections,
2506                                                 "ReloadConnections", impl_settings_reload_connections,
2507                                                 "SaveHostname", impl_settings_save_hostname,
2508                                                 NULL);
2509 }
2510