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