f0901854807a5b6588b59d73603651c0660fc744
[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, &error);
1411
1412         if (connection) {
1413                 if (!nm_connection_verify_secrets (connection, &error))
1414                         goto failure;
1415
1416                 nm_settings_add_connection_dbus (self,
1417                                                  connection,
1418                                                  save_to_disk,
1419                                                  context,
1420                                                  impl_settings_add_connection_add_cb,
1421                                                  NULL);
1422                 g_object_unref (connection);
1423                 return;
1424         }
1425
1426 failure:
1427         g_assert (error);
1428         g_dbus_method_invocation_take_error (context, error);
1429 }
1430
1431 static void
1432 impl_settings_add_connection (NMSettings *self,
1433                               GDBusMethodInvocation *context,
1434                               GVariant *settings)
1435 {
1436         impl_settings_add_connection_helper (self, context, settings, TRUE);
1437 }
1438
1439 static void
1440 impl_settings_add_connection_unsaved (NMSettings *self,
1441                                       GDBusMethodInvocation *context,
1442                                       GVariant *settings)
1443 {
1444         impl_settings_add_connection_helper (self, context, settings, FALSE);
1445 }
1446
1447 static gboolean
1448 ensure_root (NMBusManager          *dbus_mgr,
1449              GDBusMethodInvocation *context)
1450 {
1451         gulong caller_uid;
1452         GError *error = NULL;
1453
1454         if (!nm_bus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) {
1455                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1456                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1457                                              "Unable to determine request UID.");
1458                 g_dbus_method_invocation_take_error (context, error);
1459                 return FALSE;
1460         }
1461         if (caller_uid != 0) {
1462                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1463                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1464                                              "Permission denied");
1465                 g_dbus_method_invocation_take_error (context, error);
1466                 return FALSE;
1467         }
1468
1469         return TRUE;
1470 }
1471
1472 static void
1473 impl_settings_load_connections (NMSettings *self,
1474                                 GDBusMethodInvocation *context,
1475                                 char **filenames)
1476 {
1477         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1478         GPtrArray *failures;
1479         GSList *iter;
1480         int i;
1481
1482         if (!ensure_root (nm_bus_manager_get (), context))
1483                 return;
1484
1485         failures = g_ptr_array_new ();
1486
1487         for (i = 0; filenames[i]; i++) {
1488                 for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1489                         NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1490
1491                         if (nm_settings_plugin_load_connection (plugin, filenames[i]))
1492                                 break;
1493                 }
1494
1495                 if (!iter) {
1496                         if (!g_path_is_absolute (filenames[i]))
1497                                 _LOGW ("connection filename '%s' is not an absolute path", filenames[i]);
1498                         g_ptr_array_add (failures, (char *) filenames[i]);
1499                 }
1500         }
1501
1502         g_ptr_array_add (failures, NULL);
1503         g_dbus_method_invocation_return_value (
1504                 context,
1505                 g_variant_new ("(b^as)",
1506                                failures->len == 1,
1507                                failures->pdata));
1508         g_ptr_array_unref (failures);
1509 }
1510
1511 static void
1512 impl_settings_reload_connections (NMSettings *self,
1513                                   GDBusMethodInvocation *context)
1514 {
1515         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1516         GSList *iter;
1517
1518         if (!ensure_root (nm_bus_manager_get (), context))
1519                 return;
1520
1521         for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
1522                 NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
1523
1524                 nm_settings_plugin_reload_connections (plugin);
1525         }
1526
1527         g_dbus_method_invocation_return_value (context, g_variant_new ("(b)", TRUE));
1528 }
1529
1530 typedef struct {
1531         char *hostname;
1532         NMSettingsSetHostnameCb cb;
1533         gpointer user_data;
1534 } SetHostnameInfo;
1535
1536 static void
1537 set_transient_hostname_done (GObject *object,
1538                              GAsyncResult *res,
1539                              gpointer user_data)
1540 {
1541         GDBusProxy *proxy = G_DBUS_PROXY (object);
1542         gs_free SetHostnameInfo *info = user_data;
1543         gs_unref_variant GVariant *result = NULL;
1544         gs_free_error GError *error = NULL;
1545
1546         result = g_dbus_proxy_call_finish (proxy, res, &error);
1547
1548         if (error) {
1549                 _LOGW ("couldn't set the system hostname to '%s' using hostnamed: %s",
1550                        info->hostname, error->message);
1551         }
1552
1553         info->cb (info->hostname, !error, info->user_data);
1554         g_free (info->hostname);
1555 }
1556
1557 void
1558 nm_settings_set_transient_hostname (NMSettings *self,
1559                                     const char *hostname,
1560                                     NMSettingsSetHostnameCb cb,
1561                                     gpointer user_data)
1562 {
1563         NMSettingsPrivate *priv;
1564         SetHostnameInfo *info;
1565
1566         g_return_if_fail (NM_IS_SETTINGS (self));
1567         priv = NM_SETTINGS_GET_PRIVATE (self);
1568
1569         if (!priv->hostname.hostnamed_proxy) {
1570                 cb (hostname, FALSE, user_data);
1571                 return;
1572         }
1573
1574         info = g_new0 (SetHostnameInfo, 1);
1575         info->hostname = g_strdup (hostname);
1576         info->cb = cb;
1577         info->user_data = user_data;
1578
1579         g_dbus_proxy_call (priv->hostname.hostnamed_proxy,
1580                            "SetHostname",
1581                            g_variant_new ("(sb)", hostname, FALSE),
1582                            G_DBUS_CALL_FLAGS_NONE,
1583                            -1,
1584                            NULL,
1585                            set_transient_hostname_done,
1586                            info);
1587 }
1588
1589 static gboolean
1590 write_hostname (NMSettingsPrivate *priv, const char *hostname)
1591 {
1592         char *hostname_eol;
1593         gboolean ret;
1594         gs_free_error GError *error = NULL;
1595         const char *file = priv->hostname.file;
1596         gs_free char *link_path = NULL;
1597         gs_unref_variant GVariant *var = NULL;
1598         struct stat file_stat;
1599 #if HAVE_SELINUX
1600         security_context_t se_ctx_prev = NULL, se_ctx = NULL;
1601         mode_t st_mode = 0;
1602 #endif
1603
1604         if (priv->hostname.hostnamed_proxy) {
1605                 var = g_dbus_proxy_call_sync (priv->hostname.hostnamed_proxy,
1606                                               "SetStaticHostname",
1607                                               g_variant_new ("(sb)", hostname, FALSE),
1608                                               G_DBUS_CALL_FLAGS_NONE,
1609                                               -1,
1610                                               NULL,
1611                                               &error);
1612                 if (error)
1613                         _LOGW ("could not set hostname: %s", error->message);
1614
1615                 return !error;
1616         }
1617
1618         /* If the hostname file is a symbolic link, follow it to find where the
1619          * real file is located, otherwise g_file_set_contents will attempt to
1620          * replace the link with a plain file.
1621          */
1622         if (   lstat (file, &file_stat) == 0
1623             && S_ISLNK (file_stat.st_mode)
1624             && (link_path = g_file_read_link (file, NULL)))
1625                 file = link_path;
1626
1627 #if HAVE_SELINUX
1628         /* Get default context for hostname file and set it for fscreate */
1629         if (stat (file, &file_stat) == 0)
1630                 st_mode = file_stat.st_mode;
1631         matchpathcon (file, st_mode, &se_ctx);
1632         matchpathcon_fini ();
1633         getfscreatecon (&se_ctx_prev);
1634         setfscreatecon (se_ctx);
1635 #endif
1636
1637 #if defined (HOSTNAME_PERSIST_GENTOO)
1638         hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n"
1639                                         "hostname=\"%s\"\n", hostname);
1640 #else
1641         hostname_eol = g_strdup_printf ("%s\n", hostname);
1642 #endif
1643
1644         /* FIXME: g_file_set_contents() writes first to a temporary file
1645          * and renames it atomically. We should hack g_file_set_contents()
1646          * to set the SELINUX labels before renaming the file. */
1647         ret = g_file_set_contents (file, hostname_eol, -1, &error);
1648
1649 #if HAVE_SELINUX
1650         /* Restore previous context and cleanup */
1651         setfscreatecon (se_ctx_prev);
1652         freecon (se_ctx);
1653         freecon (se_ctx_prev);
1654 #endif
1655
1656         g_free (hostname_eol);
1657
1658         if (!ret) {
1659                 _LOGW ("could not save hostname to %s: %s", file, error->message);
1660                 return FALSE;
1661         }
1662
1663         return TRUE;
1664 }
1665
1666 static void
1667 pk_hostname_cb (NMAuthChain *chain,
1668                 GError *chain_error,
1669                 GDBusMethodInvocation *context,
1670                 gpointer user_data)
1671 {
1672         NMSettings *self = NM_SETTINGS (user_data);
1673         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1674         NMAuthCallResult result;
1675         GError *error = NULL;
1676         const char *hostname;
1677
1678         g_assert (context);
1679
1680         priv->auths = g_slist_remove (priv->auths, chain);
1681
1682         result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME);
1683
1684         /* If our NMSettingsConnection is already gone, do nothing */
1685         if (chain_error) {
1686                 error = g_error_new (NM_SETTINGS_ERROR,
1687                                      NM_SETTINGS_ERROR_FAILED,
1688                                      "Error checking authorization: %s",
1689                                      chain_error->message);
1690         } else if (result != NM_AUTH_CALL_RESULT_YES) {
1691                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1692                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1693                                              "Insufficient privileges.");
1694         } else {
1695                 hostname = nm_auth_chain_get_data (chain, "hostname");
1696
1697                 if (!write_hostname (priv, hostname)) {
1698                         error = g_error_new_literal (NM_SETTINGS_ERROR,
1699                                                      NM_SETTINGS_ERROR_FAILED,
1700                                                      "Saving the hostname failed.");
1701                 }
1702         }
1703
1704         if (error)
1705                 g_dbus_method_invocation_take_error (context, error);
1706         else
1707                 g_dbus_method_invocation_return_value (context, NULL);
1708
1709         nm_auth_chain_unref (chain);
1710 }
1711
1712 static gboolean
1713 validate_hostname (const char *hostname)
1714 {
1715         const char *p;
1716         gboolean dot = TRUE;
1717
1718         if (!hostname || !hostname[0])
1719                 return FALSE;
1720
1721         for (p = hostname; *p; p++) {
1722                 if (*p == '.') {
1723                         if (dot)
1724                                 return FALSE;
1725                         dot = TRUE;
1726                 } else {
1727                         if (!g_ascii_isalnum (*p) && (*p != '-') && (*p != '_'))
1728                                 return FALSE;
1729                         dot = FALSE;
1730                 }
1731         }
1732
1733         if (dot)
1734                 return FALSE;
1735
1736         return (p - hostname <= HOST_NAME_MAX);
1737 }
1738
1739 static void
1740 impl_settings_save_hostname (NMSettings *self,
1741                              GDBusMethodInvocation *context,
1742                              const char *hostname)
1743 {
1744         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1745         NMAuthChain *chain;
1746         GError *error = NULL;
1747
1748         /* Minimal validation of the hostname */
1749         if (!validate_hostname (hostname)) {
1750                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1751                                              NM_SETTINGS_ERROR_INVALID_HOSTNAME,
1752                                              "The hostname was too long or contained invalid characters.");
1753                 goto done;
1754         }
1755
1756         chain = nm_auth_chain_new_context (context, pk_hostname_cb, self);
1757         if (!chain) {
1758                 error = g_error_new_literal (NM_SETTINGS_ERROR,
1759                                              NM_SETTINGS_ERROR_PERMISSION_DENIED,
1760                                              "Unable to authenticate the request.");
1761                 goto done;
1762         }
1763
1764         priv->auths = g_slist_append (priv->auths, chain);
1765         nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE);
1766         nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free);
1767
1768 done:
1769         if (error)
1770                 g_dbus_method_invocation_take_error (context, error);
1771 }
1772
1773 static void
1774 hostname_maybe_changed (NMSettings *settings)
1775 {
1776         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (settings);
1777         char *new_hostname;
1778
1779         new_hostname = nm_settings_get_hostname (settings);
1780
1781         if (   (new_hostname && !priv->hostname.value)
1782             || (!new_hostname && priv->hostname.value)
1783             || (priv->hostname.value && new_hostname && strcmp (priv->hostname.value, new_hostname))) {
1784
1785                 _LOGI ("hostname changed from %s%s%s to %s%s%s",
1786                        NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
1787                        NM_PRINT_FMT_QUOTED (new_hostname, "\"", new_hostname, "\"", "(none)"));
1788                 g_free (priv->hostname.value);
1789                 priv->hostname.value = new_hostname;
1790                 g_object_notify (G_OBJECT (settings), NM_SETTINGS_HOSTNAME);
1791         } else
1792                 g_free (new_hostname);
1793 }
1794
1795 static void
1796 hostname_file_changed_cb (GFileMonitor *monitor,
1797                           GFile *file,
1798                           GFile *other_file,
1799                           GFileMonitorEvent event_type,
1800                           gpointer user_data)
1801 {
1802         hostname_maybe_changed (user_data);
1803 }
1804
1805 static gboolean
1806 have_connection_for_device (NMSettings *self, NMDevice *device)
1807 {
1808         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
1809         GHashTableIter iter;
1810         gpointer data;
1811         NMSettingConnection *s_con;
1812         NMSettingWired *s_wired;
1813         const char *setting_hwaddr;
1814         const char *device_hwaddr;
1815
1816         g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
1817
1818         device_hwaddr = nm_device_get_hw_address (device);
1819
1820         /* Find a wired connection locked to the given MAC address, if any */
1821         g_hash_table_iter_init (&iter, priv->connections);
1822         while (g_hash_table_iter_next (&iter, NULL, &data)) {
1823                 NMConnection *connection = NM_CONNECTION (data);
1824                 const char *ctype, *iface;
1825
1826                 if (!nm_device_check_connection_compatible (device, connection))
1827                         continue;
1828
1829                 s_con = nm_connection_get_setting_connection (connection);
1830
1831                 iface = nm_setting_connection_get_interface_name (s_con);
1832                 if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
1833                         continue;
1834
1835                 ctype = nm_setting_connection_get_connection_type (s_con);
1836                 if (   strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME)
1837                     && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
1838                         continue;
1839
1840                 s_wired = nm_connection_get_setting_wired (connection);
1841
1842                 if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
1843                         /* No wired setting; therefore the PPPoE connection applies to any device */
1844                         return TRUE;
1845                 }
1846
1847                 g_assert (s_wired != NULL);
1848
1849                 setting_hwaddr = nm_setting_wired_get_mac_address (s_wired);
1850                 if (setting_hwaddr) {
1851                         /* A connection mac-locked to this device */
1852                         if (   device_hwaddr
1853                             && nm_utils_hwaddr_matches (setting_hwaddr, -1, device_hwaddr, -1))
1854                                 return TRUE;
1855                 } else {
1856                         /* A connection that applies to any wired device */
1857                         return TRUE;
1858                 }
1859         }
1860
1861         /* See if there's a known non-NetworkManager configuration for the device */
1862         if (nm_device_spec_match_list (device, priv->unrecognized_specs))
1863                 return TRUE;
1864
1865         return FALSE;
1866 }
1867
1868 #define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection"
1869 #define DEFAULT_WIRED_DEVICE_TAG     "default-wired-device"
1870
1871 static void default_wired_clear_tag (NMSettings *self,
1872                                      NMDevice *device,
1873                                      NMSettingsConnection *connection,
1874                                      gboolean add_to_no_auto_default);
1875
1876 static void
1877 default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
1878 {
1879         NMDevice *device;
1880
1881         /* When the default wired connection is removed (either deleted or saved to
1882          * a new persistent connection by a plugin), write the MAC address of the
1883          * wired device to the config file and don't create a new default wired
1884          * connection for that device again.
1885          */
1886         device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
1887         if (device)
1888                 default_wired_clear_tag (self, device, connection, TRUE);
1889 }
1890
1891 static void
1892 default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, NMSettings *self)
1893 {
1894         NMDevice *device;
1895
1896         /* The connection has been changed by the user, it should no longer be
1897          * considered a default wired connection, and should no longer affect
1898          * the no-auto-default configuration option.
1899          */
1900         device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
1901         if (device)
1902                 default_wired_clear_tag (self, device, connection, FALSE);
1903 }
1904
1905 static void
1906 default_wired_clear_tag (NMSettings *self,
1907                          NMDevice *device,
1908                          NMSettingsConnection *connection,
1909                          gboolean add_to_no_auto_default)
1910 {
1911         g_return_if_fail (NM_IS_SETTINGS (self));
1912         g_return_if_fail (NM_IS_DEVICE (device));
1913         g_return_if_fail (NM_IS_CONNECTION (connection));
1914         g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG));
1915         g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG));
1916
1917         g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL);
1918         g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL);
1919
1920         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
1921         g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
1922
1923         if (add_to_no_auto_default)
1924                 nm_config_set_no_auto_default_for_device (NM_SETTINGS_GET_PRIVATE (self)->config, device);
1925 }
1926
1927 static void
1928 device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
1929 {
1930         NMConnection *connection;
1931         NMSettingsConnection *added;
1932         GError *error = NULL;
1933
1934         if (!nm_device_is_real (device))
1935                 return;
1936
1937         g_signal_handlers_disconnect_by_func (device,
1938                                               G_CALLBACK (device_realized),
1939                                               self);
1940
1941         /* If the device isn't managed or it already has a default wired connection,
1942          * ignore it.
1943          */
1944         if (   !nm_device_get_managed (device, FALSE)
1945             || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)
1946             || have_connection_for_device (self, device))
1947                 return;
1948
1949         connection = nm_device_new_default_connection (device);
1950         if (!connection)
1951                 return;
1952
1953         /* Add the connection */
1954         added = nm_settings_add_connection (self, connection, FALSE, &error);
1955         g_object_unref (connection);
1956
1957         if (!added) {
1958                 _LOGW ("(%s) couldn't create default wired connection: %s",
1959                        nm_device_get_iface (device),
1960                        error->message);
1961                 g_clear_error (&error);
1962                 return;
1963         }
1964
1965         g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
1966         g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
1967
1968         g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_BY_USER,
1969                           G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
1970         g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
1971                           G_CALLBACK (default_wired_connection_removed_cb), self);
1972
1973         _LOGI ("(%s): created default wired connection '%s'",
1974                nm_device_get_iface (device),
1975                nm_settings_connection_get_id (added));
1976 }
1977
1978 void
1979 nm_settings_device_added (NMSettings *self, NMDevice *device)
1980 {
1981         if (nm_device_is_real (device))
1982                 device_realized (device, NULL, self);
1983         else {
1984                 g_signal_connect_after (device, "notify::" NM_DEVICE_REAL,
1985                                         G_CALLBACK (device_realized),
1986                                         self);
1987         }
1988 }
1989
1990 void
1991 nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting)
1992 {
1993         NMSettingsConnection *connection;
1994
1995         g_signal_handlers_disconnect_by_func (device,
1996                                               G_CALLBACK (device_realized),
1997                                               self);
1998
1999         connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
2000         if (connection) {
2001                 default_wired_clear_tag (self, device, connection, FALSE);
2002
2003                 /* Don't delete the default wired connection on shutdown, so that it
2004                  * remains up and can be assumed if NM starts again.
2005                  */
2006                 if (quitting == FALSE)
2007                         nm_settings_connection_delete (connection, NULL, NULL);
2008         }
2009 }
2010
2011 /***************************************************************/
2012
2013 /* GCompareFunc helper for sorting "best" connections.
2014  * The function sorts connections in ascending timestamp order.
2015  * That means an older connection (lower timestamp) goes before
2016  * a newer one.
2017  */
2018 gint
2019 nm_settings_sort_connections (gconstpointer a, gconstpointer b)
2020 {
2021         NMSettingsConnection *ac = (NMSettingsConnection *) a;
2022         NMSettingsConnection *bc = (NMSettingsConnection *) b;
2023         guint64 ats = 0, bts = 0;
2024
2025         if (ac == bc)
2026                 return 0;
2027         if (!ac)
2028                 return -1;
2029         if (!bc)
2030                 return 1;
2031
2032         /* In the future we may use connection priorities in addition to timestamps */
2033         nm_settings_connection_get_timestamp (ac, &ats);
2034         nm_settings_connection_get_timestamp (bc, &bts);
2035
2036         if (ats < bts)
2037                 return -1;
2038         else if (ats > bts)
2039                 return 1;
2040         return 0;
2041 }
2042
2043 static GSList *
2044 get_best_connections (NMConnectionProvider *provider,
2045                       guint max_requested,
2046                       const char *ctype1,
2047                       const char *ctype2,
2048                       NMConnectionFilterFunc func,
2049                       gpointer func_data)
2050 {
2051         NMSettings *self = NM_SETTINGS (provider);
2052         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2053         GSList *sorted = NULL;
2054         GHashTableIter iter;
2055         NMSettingsConnection *connection;
2056         guint added = 0;
2057         guint64 oldest = 0;
2058
2059         g_hash_table_iter_init (&iter, priv->connections);
2060         while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
2061                 guint64 cur_ts = 0;
2062
2063                 if (ctype1 && !nm_connection_is_type (NM_CONNECTION (connection), ctype1))
2064                         continue;
2065                 if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
2066                         continue;
2067                 if (func && !func (provider, NM_CONNECTION (connection), func_data))
2068                         continue;
2069
2070                 /* Don't bother with a connection that's older than the oldest one in the list */
2071                 if (max_requested && added >= max_requested) {
2072                     nm_settings_connection_get_timestamp (connection, &cur_ts);
2073                     if (cur_ts <= oldest)
2074                                 continue;
2075                 }
2076
2077                 /* List is sorted with oldest first */
2078                 sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
2079                 added++;
2080
2081                 if (max_requested && added > max_requested) {
2082                         /* Over the limit, remove the oldest one */
2083                         sorted = g_slist_delete_link (sorted, sorted);
2084                         added--;
2085                 }
2086
2087                 nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data), &oldest);
2088         }
2089
2090         return g_slist_reverse (sorted);
2091 }
2092
2093 static const GSList *
2094 get_connections (NMConnectionProvider *provider)
2095 {
2096         GSList *list = NULL;
2097         NMSettings *self = NM_SETTINGS (provider);
2098         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2099
2100         list = _nm_utils_hash_values_to_slist (priv->connections);
2101
2102         /* Cache the list every call so we can keep it 'const' for callers */
2103         g_slist_free (priv->get_connections_cache);
2104         priv->get_connections_cache = list;
2105         return list;
2106 }
2107
2108 static NMConnection *
2109 cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
2110 {
2111         return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
2112 }
2113
2114 /***************************************************************/
2115
2116 gboolean
2117 nm_settings_get_startup_complete (NMSettings *self)
2118 {
2119         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2120
2121         return priv->startup_complete;
2122 }
2123
2124 /***************************************************************/
2125
2126 static void
2127 hostnamed_properties_changed (GDBusProxy *proxy,
2128                               GVariant *changed_properties,
2129                               char **invalidated_properties,
2130                               gpointer user_data)
2131 {
2132         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (user_data);
2133         GVariant *v_hostname;
2134         const char *hostname;
2135
2136         v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy,
2137                                                        "StaticHostname");
2138         if (!v_hostname)
2139                 return;
2140
2141         hostname = g_variant_get_string (v_hostname, NULL);
2142
2143         if (g_strcmp0 (priv->hostname.value, hostname) != 0) {
2144                 _LOGI ("hostname changed from %s%s%s to %s%s%s",
2145                        NM_PRINT_FMT_QUOTED (priv->hostname.value, "\"", priv->hostname.value, "\"", "(none)"),
2146                        NM_PRINT_FMT_QUOTED (hostname, "\"", hostname, "\"", "(none)"));
2147                 g_free (priv->hostname.value);
2148                 priv->hostname.value = g_strdup (hostname);
2149                 g_object_notify (G_OBJECT (user_data), NM_SETTINGS_HOSTNAME);
2150                 nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
2151         }
2152
2153         g_variant_unref (v_hostname);
2154 }
2155
2156 static void
2157 setup_hostname_file_monitors (NMSettings *self)
2158 {
2159         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2160         GFileMonitor *monitor;
2161         GFile *file;
2162
2163         priv->hostname.file = HOSTNAME_FILE;
2164         priv->hostname.value = nm_settings_get_hostname (self);
2165
2166         /* monitor changes to hostname file */
2167         file = g_file_new_for_path (priv->hostname.file);
2168         monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
2169         g_object_unref (file);
2170         if (monitor) {
2171                 priv->hostname.monitor_id = g_signal_connect (monitor, "changed",
2172                                                               G_CALLBACK (hostname_file_changed_cb),
2173                                                               self);
2174                 priv->hostname.monitor = monitor;
2175         }
2176
2177 #if defined (HOSTNAME_PERSIST_SUSE)
2178         /* monitor changes to dhcp file to know whether the hostname is valid */
2179         file = g_file_new_for_path (CONF_DHCP);
2180         monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
2181         g_object_unref (file);
2182         if (monitor) {
2183                 priv->hostname.dhcp_monitor_id = g_signal_connect (monitor, "changed",
2184                                                                    G_CALLBACK (hostname_file_changed_cb),
2185                                                                    self);
2186                 priv->hostname.dhcp_monitor = monitor;
2187         }
2188 #endif
2189
2190         hostname_maybe_changed (self);
2191 }
2192
2193 NMSettings *
2194 nm_settings_new (void)
2195 {
2196         NMSettings *self;
2197         NMSettingsPrivate *priv;
2198
2199         self = g_object_new (NM_TYPE_SETTINGS, NULL);
2200
2201         priv = NM_SETTINGS_GET_PRIVATE (self);
2202
2203         priv->config = nm_config_get ();
2204
2205         nm_exported_object_export (NM_EXPORTED_OBJECT (self));
2206         return self;
2207 }
2208
2209 gboolean
2210 nm_settings_start (NMSettings *self, GError **error)
2211 {
2212         NMSettingsPrivate *priv;
2213         GDBusProxy *proxy;
2214         GVariant *variant;
2215         GError *local_error = NULL;
2216
2217         priv = NM_SETTINGS_GET_PRIVATE (self);
2218
2219         /* Load the plugins; fail if a plugin is not found. */
2220         if (!load_plugins (self, nm_config_get_plugins (priv->config), error)) {
2221                 g_object_unref (self);
2222                 return FALSE;
2223         }
2224
2225         load_connections (self);
2226         check_startup_complete (self);
2227
2228         proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL,
2229                                                HOSTNAMED_SERVICE_NAME, HOSTNAMED_SERVICE_PATH,
2230                                                HOSTNAMED_SERVICE_INTERFACE, NULL, &local_error);
2231         if (proxy) {
2232                 variant = g_dbus_proxy_get_cached_property (proxy, "StaticHostname");
2233                 if (variant) {
2234                         _LOGI ("hostname: using hostnamed");
2235                         priv->hostname.hostnamed_proxy = proxy;
2236                         g_signal_connect (proxy, "g-properties-changed",
2237                                           G_CALLBACK (hostnamed_properties_changed), self);
2238                         hostnamed_properties_changed (proxy, NULL, NULL, self);
2239                         g_variant_unref (variant);
2240                 } else {
2241                         _LOGI ("hostname: couldn't get property from hostnamed");
2242                         g_object_unref (proxy);
2243                 }
2244         } else {
2245                 _LOGI ("hostname: hostnamed not used as proxy creation failed with: %s",
2246                        local_error->message);
2247                 g_clear_error (&local_error);
2248         }
2249
2250         if (!priv->hostname.hostnamed_proxy)
2251                 setup_hostname_file_monitors (self);
2252
2253         priv->started = TRUE;
2254         g_object_notify (G_OBJECT (self), NM_SETTINGS_HOSTNAME);
2255         return TRUE;
2256 }
2257
2258 static void
2259 connection_provider_iface_init (NMConnectionProviderInterface *cp_iface)
2260 {
2261     cp_iface->get_best_connections = get_best_connections;
2262     cp_iface->get_connections = get_connections;
2263     cp_iface->add_connection = _nm_connection_provider_add_connection;
2264     cp_iface->get_connection_by_uuid = cp_get_connection_by_uuid;
2265 }
2266
2267 static void
2268 nm_settings_init (NMSettings *self)
2269 {
2270         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2271
2272         priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
2273
2274         /* Hold a reference to the agent manager so it stays alive; the only
2275          * other holders are NMSettingsConnection objects which are often
2276          * transient, and we don't want the agent manager to get destroyed and
2277          * recreated often.
2278          */
2279         priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
2280
2281         g_signal_connect (priv->agent_mgr, "agent-registered", G_CALLBACK (secret_agent_registered), self);
2282 }
2283
2284 static void
2285 dispose (GObject *object)
2286 {
2287         NMSettings *self = NM_SETTINGS (object);
2288         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2289
2290         g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref);
2291         priv->auths = NULL;
2292
2293         g_object_unref (priv->agent_mgr);
2294
2295         if (priv->hostname.hostnamed_proxy) {
2296                 g_signal_handlers_disconnect_by_func (priv->hostname.hostnamed_proxy,
2297                                                       G_CALLBACK (hostnamed_properties_changed),
2298                                                       self);
2299                 g_clear_object (&priv->hostname.hostnamed_proxy);
2300         }
2301
2302         if (priv->hostname.monitor) {
2303                 if (priv->hostname.monitor_id)
2304                         g_signal_handler_disconnect (priv->hostname.monitor, priv->hostname.monitor_id);
2305
2306                 g_file_monitor_cancel (priv->hostname.monitor);
2307                 g_clear_object (&priv->hostname.monitor);
2308         }
2309
2310         if (priv->hostname.dhcp_monitor) {
2311                 if (priv->hostname.dhcp_monitor_id)
2312                         g_signal_handler_disconnect (priv->hostname.dhcp_monitor,
2313                                                      priv->hostname.dhcp_monitor_id);
2314
2315                 g_file_monitor_cancel (priv->hostname.dhcp_monitor);
2316                 g_clear_object (&priv->hostname.dhcp_monitor);
2317         }
2318
2319         g_clear_pointer (&priv->hostname.value, g_free);
2320
2321         G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object);
2322 }
2323
2324 static void
2325 finalize (GObject *object)
2326 {
2327         NMSettings *self = NM_SETTINGS (object);
2328         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2329
2330         g_hash_table_destroy (priv->connections);
2331         g_slist_free (priv->get_connections_cache);
2332
2333         g_slist_free_full (priv->unmanaged_specs, g_free);
2334         g_slist_free_full (priv->unrecognized_specs, g_free);
2335
2336         g_slist_free_full (priv->plugins, g_object_unref);
2337
2338         G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object);
2339 }
2340
2341 static void
2342 get_property (GObject *object, guint prop_id,
2343               GValue *value, GParamSpec *pspec)
2344 {
2345         NMSettings *self = NM_SETTINGS (object);
2346         NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
2347         const GSList *specs, *iter;
2348         GHashTableIter citer;
2349         GPtrArray *array;
2350         const char *path;
2351
2352         switch (prop_id) {
2353         case PROP_UNMANAGED_SPECS:
2354                 array = g_ptr_array_new ();
2355                 specs = nm_settings_get_unmanaged_specs (self);
2356                 for (iter = specs; iter; iter = g_slist_next (iter))
2357                         g_ptr_array_add (array, g_strdup (iter->data));
2358                 g_ptr_array_add (array, NULL);
2359                 g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
2360                 break;
2361         case PROP_HOSTNAME:
2362                 g_value_take_string (value, nm_settings_get_hostname (self));
2363
2364                 /* Don't ever pass NULL through D-Bus */
2365                 if (!g_value_get_string (value))
2366                         g_value_set_static_string (value, "");
2367                 break;
2368         case PROP_CAN_MODIFY:
2369                 g_value_set_boolean (value, !!get_plugin (self, NM_SETTINGS_PLUGIN_CAP_MODIFY_CONNECTIONS));
2370                 break;
2371         case PROP_CONNECTIONS:
2372                 array = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1);
2373                 g_hash_table_iter_init (&citer, priv->connections);
2374                 while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL))
2375                         g_ptr_array_add (array, g_strdup (path));
2376                 g_ptr_array_add (array, NULL);
2377                 g_value_take_boxed (value, (char **) g_ptr_array_free (array, FALSE));
2378                 break;
2379         case PROP_STARTUP_COMPLETE:
2380                 g_value_set_boolean (value, nm_settings_get_startup_complete (self));
2381                 break;
2382         default:
2383                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2384                 break;
2385         }
2386 }
2387
2388 static void
2389 nm_settings_class_init (NMSettingsClass *class)
2390 {
2391         GObjectClass *object_class = G_OBJECT_CLASS (class);
2392         NMExportedObjectClass *exported_object_class = NM_EXPORTED_OBJECT_CLASS (class);
2393
2394         g_type_class_add_private (class, sizeof (NMSettingsPrivate));
2395
2396         exported_object_class->export_path = NM_DBUS_PATH_SETTINGS;
2397
2398         /* virtual methods */
2399         object_class->get_property = get_property;
2400         object_class->dispose = dispose;
2401         object_class->finalize = finalize;
2402
2403         /* properties */
2404
2405         g_object_class_install_property
2406                 (object_class, PROP_UNMANAGED_SPECS,
2407                  g_param_spec_boxed (NM_SETTINGS_UNMANAGED_SPECS, "", "",
2408                                      G_TYPE_STRV,
2409                                      G_PARAM_READABLE |
2410                                      G_PARAM_STATIC_STRINGS));
2411
2412         g_object_class_install_property
2413                 (object_class, PROP_HOSTNAME,
2414                  g_param_spec_string (NM_SETTINGS_HOSTNAME, "", "",
2415                                       NULL,
2416                                       G_PARAM_READABLE |
2417                                       G_PARAM_STATIC_STRINGS));
2418
2419         g_object_class_install_property
2420                 (object_class, PROP_CAN_MODIFY,
2421                  g_param_spec_boolean (NM_SETTINGS_CAN_MODIFY, "", "",
2422                                        FALSE,
2423                                        G_PARAM_READABLE |
2424                                        G_PARAM_STATIC_STRINGS));
2425
2426         g_object_class_install_property
2427                 (object_class, PROP_CONNECTIONS,
2428                  g_param_spec_boxed (NM_SETTINGS_CONNECTIONS, "", "",
2429                                      G_TYPE_STRV,
2430                                      G_PARAM_READABLE |
2431                                      G_PARAM_STATIC_STRINGS));
2432
2433         /* signals */
2434         signals[CONNECTION_ADDED] = 
2435                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_ADDED,
2436                                       G_OBJECT_CLASS_TYPE (object_class),
2437                                       G_SIGNAL_RUN_FIRST,
2438                                       G_STRUCT_OFFSET (NMSettingsClass, connection_added),
2439                                       NULL, NULL,
2440                                       g_cclosure_marshal_VOID__OBJECT,
2441                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2442
2443         signals[CONNECTION_UPDATED] = 
2444                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
2445                                       G_OBJECT_CLASS_TYPE (object_class),
2446                                       G_SIGNAL_RUN_FIRST,
2447                                       G_STRUCT_OFFSET (NMSettingsClass, connection_updated),
2448                                       NULL, NULL,
2449                                       g_cclosure_marshal_VOID__OBJECT,
2450                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2451
2452         signals[CONNECTION_UPDATED_BY_USER] =
2453                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER,
2454                                       G_OBJECT_CLASS_TYPE (object_class),
2455                                       G_SIGNAL_RUN_FIRST,
2456                                       0,
2457                                       NULL, NULL,
2458                                       g_cclosure_marshal_VOID__OBJECT,
2459                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2460
2461         signals[CONNECTION_REMOVED] = 
2462                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,
2463                                       G_OBJECT_CLASS_TYPE (object_class),
2464                                       G_SIGNAL_RUN_FIRST,
2465                                       G_STRUCT_OFFSET (NMSettingsClass, connection_removed),
2466                                       NULL, NULL,
2467                                       g_cclosure_marshal_VOID__OBJECT,
2468                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2469
2470         signals[CONNECTION_VISIBILITY_CHANGED] = 
2471                         g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
2472                                       G_OBJECT_CLASS_TYPE (object_class),
2473                                       G_SIGNAL_RUN_FIRST,
2474                                       G_STRUCT_OFFSET (NMSettingsClass, connection_visibility_changed),
2475                                       NULL, NULL,
2476                                       g_cclosure_marshal_VOID__OBJECT,
2477                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2478
2479         signals[AGENT_REGISTERED] =
2480                 g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED,
2481                               G_OBJECT_CLASS_TYPE (object_class),
2482                               G_SIGNAL_RUN_FIRST,
2483                               G_STRUCT_OFFSET (NMSettingsClass, agent_registered),
2484                               NULL, NULL,
2485                               g_cclosure_marshal_VOID__OBJECT,
2486                               G_TYPE_NONE, 1, NM_TYPE_SECRET_AGENT);
2487
2488
2489         signals[NEW_CONNECTION] = 
2490                         g_signal_new ("new-connection",
2491                                       G_OBJECT_CLASS_TYPE (object_class),
2492                                       G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
2493                                       g_cclosure_marshal_VOID__OBJECT,
2494                                       G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
2495
2496         nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (class),
2497                                                 NMDBUS_TYPE_SETTINGS_SKELETON,
2498                                                 "ListConnections", impl_settings_list_connections,
2499                                                 "GetConnectionByUuid", impl_settings_get_connection_by_uuid,
2500                                                 "AddConnection", impl_settings_add_connection,
2501                                                 "AddConnectionUnsaved", impl_settings_add_connection_unsaved,
2502                                                 "LoadConnections", impl_settings_load_connections,
2503                                                 "ReloadConnections", impl_settings_reload_connections,
2504                                                 "SaveHostname", impl_settings_save_hostname,
2505                                                 NULL);
2506 }
2507