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