From 33491bc4279481db8ae47213e34a6d695a0e8830 Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Sun, 6 Jul 2014 02:16:10 +0200 Subject: Imported Upstream version 0.9.10.0 --- src/settings/nm-settings.c | 1223 ++++++++++++++++++++++---------------------- 1 file changed, 611 insertions(+), 612 deletions(-) (limited to 'src/settings/nm-settings.c') diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 71009a0c..e90776ab 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -54,13 +53,11 @@ #include #include -#include "../nm-device-ethernet.h" -#include "../nm-device-wired.h" +#include "nm-device-ethernet.h" #include "nm-dbus-glib-types.h" #include "nm-settings.h" #include "nm-settings-connection.h" #include "nm-settings-error.h" -#include "nm-default-wired-connection.h" #include "nm-logging.h" #include "nm-dbus-manager.h" #include "nm-manager-auth.h" @@ -69,8 +66,8 @@ #include "nm-agent-manager.h" #include "nm-settings-utils.h" #include "nm-connection-provider.h" - -#define CONFIG_KEY_NO_AUTO_DEFAULT "no-auto-default" +#include "nm-config.h" +#include "NetworkManagerUtils.h" /* LINKER CRACKROCK */ #define EXPORT(sym) void * __export_##sym = &sym; @@ -94,15 +91,25 @@ static gboolean impl_settings_list_connections (NMSettings *self, GPtrArray **connections, GError **error); -static gboolean impl_settings_get_connection_by_uuid (NMSettings *self, - const char *uuid, - char **out_object_path, - GError **error); +static void impl_settings_get_connection_by_uuid (NMSettings *self, + const char *uuid, + DBusGMethodInvocation *context); static void impl_settings_add_connection (NMSettings *self, GHashTable *settings, DBusGMethodInvocation *context); +static void impl_settings_add_connection_unsaved (NMSettings *self, + GHashTable *settings, + DBusGMethodInvocation *context); + +static void impl_settings_load_connections (NMSettings *self, + char **filenames, + DBusGMethodInvocation *context); + +static void impl_settings_reload_connections (NMSettings *self, + DBusGMethodInvocation *context); + static void impl_settings_save_hostname (NMSettings *self, const char *hostname, DBusGMethodInvocation *context); @@ -110,6 +117,7 @@ static void impl_settings_save_hostname (NMSettings *self, #include "nm-settings-glue.h" static void unmanaged_specs_changed (NMSystemConfigInterface *config, gpointer user_data); +static void unrecognized_specs_changed (NMSystemConfigInterface *config, gpointer user_data); static void connection_provider_init (NMConnectionProvider *cp_class); @@ -119,19 +127,19 @@ G_DEFINE_TYPE_EXTENDED (NMSettings, nm_settings, G_TYPE_OBJECT, 0, typedef struct { NMDBusManager *dbus_mgr; - DBusGConnection *bus; NMAgentManager *agent_mgr; - char *config_file; + NMConfig *config; - NMSessionMonitor *session_monitor; GSList *auths; GSList *plugins; gboolean connections_loaded; GHashTable *connections; GSList *unmanaged_specs; + GSList *unrecognized_specs; + GSList *get_connections_cache; } NMSettingsPrivate; #define NM_SETTINGS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTINGS, NMSettingsPrivate)) @@ -140,9 +148,9 @@ enum { PROPERTIES_CHANGED, CONNECTION_ADDED, CONNECTION_UPDATED, + CONNECTION_UPDATED_BY_USER, CONNECTION_REMOVED, CONNECTION_VISIBILITY_CHANGED, - CONNECTIONS_LOADED, AGENT_REGISTERED, NEW_CONNECTION, /* exported, not used internally */ @@ -155,19 +163,25 @@ enum { PROP_UNMANAGED_SPECS, PROP_HOSTNAME, PROP_CAN_MODIFY, + PROP_CONNECTIONS, LAST_PROP }; +static void +plugin_connection_added (NMSystemConfigInterface *config, + NMSettingsConnection *connection, + gpointer user_data) +{ + claim_connection (NM_SETTINGS (user_data), connection, TRUE); +} + static void load_connections (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; - if (priv->connections_loaded) - return; - for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); GSList *plugin_connections; @@ -183,15 +197,19 @@ load_connections (NMSettings *self) claim_connection (self, NM_SETTINGS_CONNECTION (elt->data), TRUE); g_slist_free (plugin_connections); + + g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, + G_CALLBACK (plugin_connection_added), self); + g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED, + G_CALLBACK (unmanaged_specs_changed), self); + g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNRECOGNIZED_SPECS_CHANGED, + G_CALLBACK (unrecognized_specs_changed), self); } priv->connections_loaded = TRUE; - /* FIXME: Bad hack */ unmanaged_specs_changed (NULL, self); - - g_signal_emit (self, signals[CONNECTIONS_LOADED], 0); - g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTIONS_LOADED); + unrecognized_specs_changed (NULL, self); } void @@ -203,14 +221,11 @@ nm_settings_for_each_connection (NMSettings *self, GHashTableIter iter; gpointer data; - g_return_if_fail (self != NULL); g_return_if_fail (NM_IS_SETTINGS (self)); g_return_if_fail (for_each_func != NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - load_connections (self); - g_hash_table_iter_init (&iter, priv->connections); while (g_hash_table_iter_next (&iter, NULL, &data)) for_each_func (self, NM_SETTINGS_CONNECTION (data), user_data); @@ -225,8 +240,6 @@ impl_settings_list_connections (NMSettings *self, GHashTableIter iter; gpointer key; - load_connections (self); - *connections = g_ptr_array_sized_new (g_hash_table_size (priv->connections) + 1); g_hash_table_iter_init (&iter, priv->connections); while (g_hash_table_iter_next (&iter, &key, NULL)) @@ -241,14 +254,11 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) NMSettingsConnection *candidate; GHashTableIter iter; - g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); g_return_val_if_fail (uuid != NULL, NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - load_connections (self); - g_hash_table_iter_init (&iter, priv->connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) { if (g_strcmp0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate))) == 0) @@ -258,25 +268,52 @@ nm_settings_get_connection_by_uuid (NMSettings *self, const char *uuid) return NULL; } -static gboolean +static void impl_settings_get_connection_by_uuid (NMSettings *self, const char *uuid, - char **out_object_path, - GError **error) + DBusGMethodInvocation *context) { NMSettingsConnection *connection = NULL; + NMAuthSubject *subject = NULL; + GError *error = NULL; + char *error_desc = NULL; connection = nm_settings_get_connection_by_uuid (self, uuid); - if (connection) - *out_object_path = g_strdup (nm_connection_get_path (NM_CONNECTION (connection))); - else { - g_set_error_literal (error, - NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_INVALID_CONNECTION, - "No connection with the UUID was found."); + if (!connection) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_INVALID_CONNECTION, + "No connection with the UUID was found."); + goto error; } - return !!connection; + subject = nm_auth_subject_new_from_context (context); + if (!subject) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine UID of request."); + goto error; + } + + if (!nm_auth_uid_in_acl (NM_CONNECTION (connection), + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), + &error_desc)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + error_desc); + g_free (error_desc); + goto error; + } + + g_clear_object (&subject); + dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection))); + return; + +error: + g_assert (error); + dbus_g_method_return_error (context, error); + g_error_free (error); + g_clear_object (&subject); } static int @@ -308,8 +345,10 @@ connection_sort (gconstpointer pa, gconstpointer pb) return 1; } -/* Returns a list of NMSettingsConnections. Caller must free the list with - * g_slist_free(). +/* Returns a list of NMSettingsConnections. + * The list is sorted in the order suitable for auto-connecting, i.e. + * first go connections with autoconnect=yes and most recent timestamp. + * Caller must free the list with g_slist_free(). */ GSList * nm_settings_get_connections (NMSettings *self) @@ -331,27 +370,14 @@ nm_settings_get_connection_by_path (NMSettings *self, const char *path) { NMSettingsPrivate *priv; - g_return_val_if_fail (self != NULL, NULL); g_return_val_if_fail (NM_IS_SETTINGS (self), NULL); g_return_val_if_fail (path != NULL, NULL); priv = NM_SETTINGS_GET_PRIVATE (self); - load_connections (self); - return (NMSettingsConnection *) g_hash_table_lookup (priv->connections, path); } -static void -clear_unmanaged_specs (NMSettings *self) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - - g_slist_foreach (priv->unmanaged_specs, (GFunc) g_free, NULL); - g_slist_free (priv->unmanaged_specs); - priv->unmanaged_specs = NULL; -} - static char* uscore_to_wincaps (const char *uscore) { @@ -402,7 +428,6 @@ nm_settings_get_unmanaged_specs (NMSettings *self) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - load_connections (self); return priv->unmanaged_specs; } @@ -452,55 +477,66 @@ nm_settings_get_hostname (NMSettings *self) return NULL; } -static void -plugin_connection_added (NMSystemConfigInterface *config, - NMSettingsConnection *connection, - gpointer user_data) -{ - claim_connection (NM_SETTINGS (user_data), connection, TRUE); -} - static gboolean -find_unmanaged_device (NMSettings *self, const char *needle) +find_spec (GSList *spec_list, const char *spec) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; - for (iter = priv->unmanaged_specs; iter; iter = g_slist_next (iter)) { - if (!strcmp ((const char *) iter->data, needle)) + for (iter = spec_list; iter; iter = g_slist_next (iter)) { + if (!strcmp ((const char *) iter->data, spec)) return TRUE; } return FALSE; } static void -unmanaged_specs_changed (NMSystemConfigInterface *config, - gpointer user_data) +update_specs (NMSettings *self, GSList **specs_ptr, + GSList * (*get_specs_func) (NMSystemConfigInterface *)) { - NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; - clear_unmanaged_specs (self); + g_slist_free_full (*specs_ptr, g_free); + *specs_ptr = NULL; - /* Ask all the plugins for their unmanaged specs */ for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { GSList *specs, *specs_iter; - specs = nm_system_config_interface_get_unmanaged_specs (NM_SYSTEM_CONFIG_INTERFACE (iter->data)); + specs = get_specs_func (NM_SYSTEM_CONFIG_INTERFACE (iter->data)); for (specs_iter = specs; specs_iter; specs_iter = specs_iter->next) { - if (!find_unmanaged_device (self, (const char *) specs_iter->data)) { - priv->unmanaged_specs = g_slist_prepend (priv->unmanaged_specs, specs_iter->data); + if (!find_spec (*specs_ptr, (const char *) specs_iter->data)) { + *specs_ptr = g_slist_prepend (*specs_ptr, specs_iter->data); } else g_free (specs_iter->data); } g_slist_free (specs); } +} +static void +unmanaged_specs_changed (NMSystemConfigInterface *config, + gpointer user_data) +{ + NMSettings *self = NM_SETTINGS (user_data); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + + update_specs (self, &priv->unmanaged_specs, + nm_system_config_interface_get_unmanaged_specs); g_object_notify (G_OBJECT (self), NM_SETTINGS_UNMANAGED_SPECS); } +static void +unrecognized_specs_changed (NMSystemConfigInterface *config, + gpointer user_data) +{ + NMSettings *self = NM_SETTINGS (user_data); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + + update_specs (self, &priv->unrecognized_specs, + nm_system_config_interface_get_unrecognized_specs); +} + static void hostname_changed (NMSystemConfigInterface *config, GParamSpec *pspec, @@ -523,8 +559,6 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin) priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin)); - g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, - G_CALLBACK (plugin_connection_added), self); g_signal_connect (plugin, "notify::hostname", G_CALLBACK (hostname_changed), self); nm_system_config_interface_init (plugin, NULL); @@ -534,9 +568,6 @@ add_plugin (NMSettings *self, NMSystemConfigInterface *plugin) NM_SYSTEM_CONFIG_INTERFACE_INFO, &pinfo, NULL); - g_signal_connect (plugin, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED, - G_CALLBACK (unmanaged_specs_changed), self); - nm_log_info (LOGD_SETTINGS, "Loaded plugin %s: %s", pname, pinfo); g_free (pname); g_free (pinfo); @@ -570,10 +601,9 @@ find_plugin (GSList *list, const char *pname) static void add_keyfile_plugin (NMSettings *self) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GObject *keyfile_plugin; - keyfile_plugin = nm_settings_keyfile_plugin_new (priv->config_file); + keyfile_plugin = nm_settings_keyfile_plugin_new (); g_assert (keyfile_plugin); add_plugin (self, NM_SYSTEM_CONFIG_INTERFACE (keyfile_plugin)); } @@ -581,7 +611,6 @@ add_keyfile_plugin (NMSettings *self) static gboolean load_plugins (NMSettings *self, const char **plugins, GError **error) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *list = NULL; const char **iter; gboolean keyfile_added = FALSE; @@ -592,7 +621,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) char *full_name, *path; const char *pname = *iter; GObject *obj; - GObject * (*factory_func) (const char *); + GObject * (*factory_func) (void); /* strip leading spaces */ while (g_ascii_isspace (*pname)) @@ -616,7 +645,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) } full_name = g_strdup_printf ("nm-settings-plugin-%s", pname); - path = g_module_build_path (PLUGINDIR, full_name); + path = g_module_build_path (NMPLUGINDIR, full_name); plugin = g_module_open (path, G_MODULE_BIND_LOCAL); if (!plugin) { @@ -640,7 +669,7 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) break; } - obj = (*factory_func) (priv->config_file); + obj = (*factory_func) (); if (!obj || !NM_IS_SYSTEM_CONFIG_INTERFACE (obj)) { g_set_error (error, 0, 0, "Plugin '%s' returned invalid system config object.", @@ -659,77 +688,30 @@ load_plugins (NMSettings *self, const char **plugins, GError **error) if (!keyfile_added) add_keyfile_plugin (self); - g_slist_foreach (list, (GFunc) g_object_unref, NULL); - g_slist_free (list); + g_slist_free_full (list, g_object_unref); return success; } -#define REMOVED_ID_TAG "removed-id-tag" -#define UPDATED_ID_TAG "updated-id-tag" -#define VISIBLE_ID_TAG "visible-id-tag" -#define UNREG_ID_TAG "unreg-id-tag" - static void -connection_removed (NMSettingsConnection *obj, gpointer user_data) +connection_updated (NMSettingsConnection *connection, gpointer user_data) { - GObject *connection = G_OBJECT (obj); - guint id; - - g_object_ref (connection); - - /* Disconnect signal handlers, as plugins might still keep references - * to the connection (and thus the signal handlers would still be live) - * even after NMSettings has dropped all its references. - */ - - id = GPOINTER_TO_UINT (g_object_get_data (connection, REMOVED_ID_TAG)); - if (id) - g_signal_handler_disconnect (connection, id); - - id = GPOINTER_TO_UINT (g_object_get_data (connection, UPDATED_ID_TAG)); - if (id) - g_signal_handler_disconnect (connection, id); - - id = GPOINTER_TO_UINT (g_object_get_data (connection, VISIBLE_ID_TAG)); - if (id) - g_signal_handler_disconnect (connection, id); - - /* Forget about the connection internally */ - g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections, - (gpointer) nm_connection_get_path (NM_CONNECTION (connection))); - /* Re-emit for listeners like NMPolicy */ - g_signal_emit (NM_SETTINGS (user_data), signals[CONNECTION_REMOVED], 0, connection); - g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_REMOVED, connection); - - g_object_unref (connection); -} - -static void -connection_unregister (NMSettingsConnection *obj, gpointer user_data) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (user_data); - GObject *connection = G_OBJECT (obj); - guint id; - - /* Make sure it's unregistered from the bus now that's removed */ - dbus_g_connection_unregister_g_object (priv->bus, connection); - - id = GPOINTER_TO_UINT (g_object_get_data (connection, UNREG_ID_TAG)); - if (id) - g_signal_handler_disconnect (connection, id); + g_signal_emit (NM_SETTINGS (user_data), + signals[CONNECTION_UPDATED], + 0, + connection); + g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection); } static void -connection_updated (NMSettingsConnection *connection, gpointer user_data) +connection_updated_by_user (NMSettingsConnection *connection, gpointer user_data) { /* Re-emit for listeners like NMPolicy */ g_signal_emit (NM_SETTINGS (user_data), - signals[CONNECTION_UPDATED], + signals[CONNECTION_UPDATED_BY_USER], 0, connection); - g_signal_emit_by_name (NM_SETTINGS (user_data), NM_CP_SIGNAL_CONNECTION_UPDATED, connection); } static void @@ -744,6 +726,37 @@ connection_visibility_changed (NMSettingsConnection *connection, connection); } +static void +connection_removed (NMSettingsConnection *connection, gpointer user_data) +{ + NMSettings *self = NM_SETTINGS (user_data); + + g_object_ref (connection); + + /* Disconnect signal handlers, as plugins might still keep references + * to the connection (and thus the signal handlers would still be live) + * even after NMSettings has dropped all its references. + */ + + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated_by_user), self); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self); + + /* Forget about the connection internally */ + g_hash_table_remove (NM_SETTINGS_GET_PRIVATE (user_data)->connections, + (gpointer) nm_connection_get_path (NM_CONNECTION (connection))); + + /* Notify D-Bus */ + g_signal_emit (self, signals[CONNECTION_REMOVED], 0, connection); + + /* Re-emit for listeners like NMPolicy */ + g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_REMOVED, connection); + g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS); + + g_object_unref (connection); +} + static void secret_agent_registered (NMAgentManager *agent_mgr, NMSecretAgent *agent, @@ -807,7 +820,6 @@ claim_connection (NMSettings *self, GHashTableIter iter; gpointer data; char *path; - guint id; g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection)); g_return_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL); @@ -819,6 +831,8 @@ claim_connection (NMSettings *self, return; } + nm_utils_normalize_connection (NM_CONNECTION (connection), TRUE); + if (!nm_connection_verify (NM_CONNECTION (connection), &error)) { nm_log_warn (LOGD_SETTINGS, "plugin provided invalid connection: '%s' / '%s' invalid: %d", g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)), @@ -839,31 +853,21 @@ claim_connection (NMSettings *self, /* Evil openconnect migration hack */ openconnect_migrate_hack (NM_CONNECTION (connection)); - id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, - G_CALLBACK (connection_removed), - self); - g_object_set_data (G_OBJECT (connection), REMOVED_ID_TAG, GUINT_TO_POINTER (id)); - - id = g_signal_connect (connection, "unregister", - G_CALLBACK (connection_unregister), - self); - g_object_set_data (G_OBJECT (connection), UNREG_ID_TAG, GUINT_TO_POINTER (id)); - - id = g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED, - G_CALLBACK (connection_updated), - self); - g_object_set_data (G_OBJECT (connection), UPDATED_ID_TAG, GUINT_TO_POINTER (id)); - - id = g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE, - G_CALLBACK (connection_visibility_changed), - self); - g_object_set_data (G_OBJECT (connection), VISIBLE_ID_TAG, GUINT_TO_POINTER (id)); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, + G_CALLBACK (connection_removed), self); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED, + G_CALLBACK (connection_updated), self); + g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_BY_USER, + G_CALLBACK (connection_updated_by_user), self); + g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE, + G_CALLBACK (connection_visibility_changed), + self); /* Export the connection over D-Bus */ g_warn_if_fail (nm_connection_get_path (NM_CONNECTION (connection)) == NULL); path = g_strdup_printf ("%s/%u", NM_DBUS_PATH_SETTINGS, ec_counter++); nm_connection_set_path (NM_CONNECTION (connection), path); - dbus_g_connection_register_g_object (priv->bus, path, G_OBJECT (connection)); + nm_dbus_manager_register_object (priv->dbus_mgr, path, G_OBJECT (connection)); g_free (path); g_hash_table_insert (priv->connections, @@ -871,39 +875,38 @@ claim_connection (NMSettings *self, g_object_ref (connection)); /* Only emit the individual connection-added signal after connections - * have been initially loaded. While getting the first list of connections - * we suppress it, then send the connections-loaded signal after we're all - * done to minimize processing. + * have been initially loaded. */ if (priv->connections_loaded) { /* Internal added signal */ g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection); g_signal_emit_by_name (self, NM_CP_SIGNAL_CONNECTION_ADDED, connection); + g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTIONS); /* Exported D-Bus signal */ g_signal_emit (self, signals[NEW_CONNECTION], 0, connection); } } -static void -remove_default_wired_connection (NMSettings *self, - NMSettingsConnection *connection, - gboolean do_signal) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - const char *path = nm_connection_get_path (NM_CONNECTION (connection)); - - if (g_hash_table_lookup (priv->connections, path)) { - if (do_signal) - g_signal_emit_by_name (G_OBJECT (connection), NM_SETTINGS_CONNECTION_REMOVED); - g_hash_table_remove (priv->connections, path); - } -} - -static NMSettingsConnection * -add_new_connection (NMSettings *self, - NMConnection *connection, - GError **error) +/** + * nm_settings_add_connection: + * @self: the #NMSettings object + * @connection: the source connection to create a new #NMSettingsConnection from + * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to + * not save to disk + * @error: on return, a location to store any errors that may occur + * + * Creates a new #NMSettingsConnection for the given source @connection. + * The returned object is owned by @self and the caller must reference + * the object to continue using it. + * + * Returns: the new #NMSettingsConnection or %NULL + */ +NMSettingsConnection * +nm_settings_add_connection (NMSettings *self, + NMConnection *connection, + gboolean save_to_disk, + GError **error) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GSList *iter; @@ -936,24 +939,31 @@ add_new_connection (NMSettings *self, NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); GError *add_error = NULL; - g_clear_error (error); - added = nm_system_config_interface_add_connection (plugin, connection, &add_error); + added = nm_system_config_interface_add_connection (plugin, connection, save_to_disk, &add_error); if (added) { claim_connection (self, added, TRUE); return added; } - g_propagate_error (error, add_error); + nm_log_dbg (LOGD_SETTINGS, "Failed to add %s/'%s': %s", + nm_connection_get_uuid (connection), + nm_connection_get_id (connection), + add_error ? add_error->message : "(unknown)"); + g_clear_error (&add_error); } + + g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_FAILED, + "No plugin supported adding this connection"); return NULL; } static NMConnection * _nm_connection_provider_add_connection (NMConnectionProvider *provider, NMConnection *connection, + gboolean save_to_disk, GError **error) { g_assert (NM_IS_CONNECTION_PROVIDER (provider) && NM_IS_SETTINGS (provider)); - return (NMConnection *) add_new_connection (NM_SETTINGS (provider), connection, error); + return NM_CONNECTION (nm_settings_add_connection (NM_SETTINGS (provider), connection, save_to_disk, error)); } static gboolean @@ -978,7 +988,7 @@ secrets_filter_cb (NMSetting *setting, static void send_agent_owned_secrets (NMSettings *self, NMSettingsConnection *connection, - gulong caller_uid) + NMAuthSubject *subject) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMConnection *for_agent; @@ -991,7 +1001,7 @@ send_agent_owned_secrets (NMSettings *self, nm_connection_clear_secrets_with_flags (for_agent, secrets_filter_cb, GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED)); - nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, TRUE, caller_uid); + nm_agent_manager_save_secrets (priv->agent_mgr, for_agent, subject); g_object_unref (for_agent); } @@ -1004,76 +1014,54 @@ pk_add_cb (NMAuthChain *chain, NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthCallResult result; - GError *error = NULL, *add_error = NULL; + GError *error = NULL; NMConnection *connection; NMSettingsConnection *added = NULL; NMSettingsAddCallback callback; gpointer callback_data; - gulong caller_uid; + NMAuthSubject *subject; const char *perm; + gboolean save_to_disk; + + g_assert (context); priv->auths = g_slist_remove (priv->auths, chain); + perm = nm_auth_chain_get_data (chain, "perm"); + g_assert (perm); + result = nm_auth_chain_get_result (chain, perm); + if (chain_error) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_GENERAL, "Error checking authorization: %s", chain_error->message ? chain_error->message : "(unknown)"); - goto done; - } - - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - result = nm_auth_chain_get_result (chain, perm); - - /* Caller didn't successfully authenticate */ - if (result != NM_AUTH_CALL_RESULT_YES) { + } else if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_NOT_PRIVILEGED, + NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); - goto done; - } - - connection = nm_auth_chain_get_data (chain, "connection"); - g_assert (connection); - added = add_new_connection (self, connection, &add_error); - if (!added) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_ADD_FAILED, - "Saving connection failed: (%d) %s", - add_error ? add_error->code : -1, - (add_error && add_error->message) ? add_error->message : "(unknown)"); - g_error_free (add_error); + } else { + /* Authorized */ + connection = nm_auth_chain_get_data (chain, "connection"); + g_assert (connection); + save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk")); + added = nm_settings_add_connection (self, connection, save_to_disk, &error); } -done: callback = nm_auth_chain_get_data (chain, "callback"); callback_data = nm_auth_chain_get_data (chain, "callback-data"); - caller_uid = nm_auth_chain_get_data_ulong (chain, "caller-uid"); + subject = nm_auth_chain_get_data (chain, "subject"); callback (self, added, error, context, callback_data); /* Send agent-owned secrets to the agents */ if (!error && added) - send_agent_owned_secrets (self, added, caller_uid); + send_agent_owned_secrets (self, added, subject); g_clear_error (&error); nm_auth_chain_unref (chain); } -static void -add_cb (NMSettings *self, - NMSettingsConnection *connection, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - if (error) - dbus_g_method_return_error (context, error); - else - dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection))); -} - /* FIXME: remove if/when kernel supports adhoc wpa */ static gboolean is_adhoc_wpa (NMConnection *connection) @@ -1107,20 +1095,24 @@ is_adhoc_wpa (NMConnection *connection) } void -nm_settings_add_connection (NMSettings *self, +nm_settings_add_connection_dbus (NMSettings *self, NMConnection *connection, + gboolean save_to_disk, DBusGMethodInvocation *context, NMSettingsAddCallback callback, gpointer user_data) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMSettingConnection *s_con; + NMAuthSubject *subject = NULL; NMAuthChain *chain; GError *error = NULL, *tmp_error = NULL; - gulong caller_uid = G_MAXULONG; char *error_desc = NULL; const char *perm; + g_return_if_fail (connection != NULL); + g_return_if_fail (context != NULL); + /* Connection must be valid, of course */ if (!nm_connection_verify (connection, &tmp_error)) { error = g_error_new (NM_SETTINGS_ERROR, @@ -1128,9 +1120,7 @@ nm_settings_add_connection (NMSettings *self, "The connection was invalid: %s", tmp_error ? tmp_error->message : "(unknown)"); g_error_free (tmp_error); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* The kernel doesn't support Ad-Hoc WPA connections well at this time, @@ -1141,9 +1131,7 @@ nm_settings_add_connection (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION, "WPA Ad-Hoc disabled due to kernel bugs"); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* Do any of the plugins support adding? */ @@ -1151,34 +1139,29 @@ nm_settings_add_connection (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_ADD_NOT_SUPPORTED, "None of the registered plugins support add."); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } - /* Get the caller's UID */ - if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &caller_uid, &error_desc)) { - error = g_error_new (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_NOT_PRIVILEGED, - "Unable to determine UID of request: %s.", - error_desc ? error_desc : "(unknown)"); - g_free (error_desc); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + subject = nm_auth_subject_new_from_context (context); + if (!subject) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine UID of request."); + goto done; } /* Ensure the caller's username exists in the connection's permissions, * or that the permissions is empty (ie, visible by everyone). */ - if (!nm_auth_uid_in_acl (connection, priv->session_monitor, caller_uid, &error_desc)) { + if (!nm_auth_uid_in_acl (connection, + nm_session_monitor_get (), + nm_auth_subject_get_uid (subject), + &error_desc)) { error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_PERMISSION_DENIED, error_desc); g_free (error_desc); - callback (self, NULL, error, context, user_data); - g_error_free (error); - return; + goto done; } /* If the caller is the only user in the connection's permissions, then @@ -1192,29 +1175,62 @@ nm_settings_add_connection (NMSettings *self, else perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; - /* Otherwise validate the user request */ - chain = nm_auth_chain_new (context, NULL, pk_add_cb, self); - g_assert (chain); + /* Validate the user request */ + chain = nm_auth_chain_new_subject (subject, context, pk_add_cb, self); + if (!chain) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + goto done; + } + priv->auths = g_slist_append (priv->auths, chain); nm_auth_chain_add_call (chain, perm, TRUE); nm_auth_chain_set_data (chain, "perm", (gpointer) perm, NULL); nm_auth_chain_set_data (chain, "connection", g_object_ref (connection), g_object_unref); nm_auth_chain_set_data (chain, "callback", callback, NULL); nm_auth_chain_set_data (chain, "callback-data", user_data, NULL); - nm_auth_chain_set_data_ulong (chain, "caller-uid", caller_uid); + nm_auth_chain_set_data (chain, "subject", g_object_ref (subject), g_object_unref); + nm_auth_chain_set_data (chain, "save-to-disk", GUINT_TO_POINTER (save_to_disk), NULL); + +done: + if (error) + callback (self, NULL, error, context, user_data); + + g_clear_error (&error); + g_clear_object (&subject); } static void -impl_settings_add_connection (NMSettings *self, - GHashTable *settings, - DBusGMethodInvocation *context) +impl_settings_add_connection_add_cb (NMSettings *self, + NMSettingsConnection *connection, + GError *error, + DBusGMethodInvocation *context, + gpointer user_data) +{ + if (error) + dbus_g_method_return_error (context, error); + else + dbus_g_method_return (context, nm_connection_get_path (NM_CONNECTION (connection))); +} + +static void +impl_settings_add_connection_helper (NMSettings *self, + GHashTable *settings, + gboolean save_to_disk, + DBusGMethodInvocation *context) { NMConnection *connection; GError *error = NULL; connection = nm_connection_new_from_hash (settings, &error); if (connection) { - nm_settings_add_connection (self, connection, context, add_cb, NULL); + nm_settings_add_connection_dbus (self, + connection, + save_to_disk, + context, + impl_settings_add_connection_add_cb, + NULL); g_object_unref (connection); } else { g_assert (error); @@ -1223,6 +1239,103 @@ impl_settings_add_connection (NMSettings *self, } } +static void +impl_settings_add_connection (NMSettings *self, + GHashTable *settings, + DBusGMethodInvocation *context) +{ + impl_settings_add_connection_helper (self, settings, TRUE, context); +} + +static void +impl_settings_add_connection_unsaved (NMSettings *self, + GHashTable *settings, + DBusGMethodInvocation *context) +{ + impl_settings_add_connection_helper (self, settings, FALSE, context); +} + +static gboolean +ensure_root (NMDBusManager *dbus_mgr, + DBusGMethodInvocation *context) +{ + gulong caller_uid; + GError *error = NULL; + + if (!nm_dbus_manager_get_caller_info (dbus_mgr, context, NULL, &caller_uid, NULL)) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to determine request UID."); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + if (caller_uid != 0) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Permission denied"); + dbus_g_method_return_error (context, error); + g_error_free (error); + return FALSE; + } + + return TRUE; +} + +static void +impl_settings_load_connections (NMSettings *self, + char **filenames, + DBusGMethodInvocation *context) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GPtrArray *failures; + GSList *iter; + int i; + + if (!ensure_root (priv->dbus_mgr, context)) + return; + + failures = g_ptr_array_new (); + + for (i = 0; filenames[i]; i++) { + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); + + if (nm_system_config_interface_load_connection (plugin, filenames[i])) + break; + } + + if (!iter) { + if (!g_path_is_absolute (filenames[i])) + nm_log_warn (LOGD_SETTINGS, "Connection filename '%s' is not an absolute path", filenames[i]); + g_ptr_array_add (failures, (char *) filenames[i]); + } + } + + g_ptr_array_add (failures, NULL); + dbus_g_method_return (context, failures->len == 1, failures->pdata); + g_ptr_array_unref (failures); +} + +static void +impl_settings_reload_connections (NMSettings *self, + DBusGMethodInvocation *context) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GSList *iter; + + if (!ensure_root (priv->dbus_mgr, context)) + return; + + for (iter = priv->plugins; iter; iter = g_slist_next (iter)) { + NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data); + + nm_system_config_interface_reload_connections (plugin); + } + + dbus_g_method_return (context, TRUE); +} + static void pk_hostname_cb (NMAuthChain *chain, GError *chain_error, @@ -1232,51 +1345,45 @@ pk_hostname_cb (NMAuthChain *chain, NMSettings *self = NM_SETTINGS (user_data); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); NMAuthCallResult result; - gboolean success = FALSE; GError *error = NULL; GSList *iter; const char *hostname; + g_assert (context); + priv->auths = g_slist_remove (priv->auths, chain); + result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); + /* If our NMSettingsConnection is already gone, do nothing */ if (chain_error) { error = g_error_new (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_GENERAL, "Error checking authorization: %s", chain_error->message ? chain_error->message : "(unknown)"); - goto done; - } - - result = nm_auth_chain_get_result (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME); - - /* Caller didn't successfully authenticate */ - if (result != NM_AUTH_CALL_RESULT_YES) { + } else if (result != NM_AUTH_CALL_RESULT_YES) { error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_NOT_PRIVILEGED, + NM_SETTINGS_ERROR_PERMISSION_DENIED, "Insufficient privileges."); - goto done; - } - - /* Set the hostname in all plugins */ - hostname = nm_auth_chain_get_data (chain, "hostname"); - for (iter = priv->plugins; iter; iter = iter->next) { - NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; - - g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); - if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) { - g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, hostname, NULL); - success = TRUE; + } else { + /* Set the hostname in all plugins */ + hostname = nm_auth_chain_get_data (chain, "hostname"); + for (iter = priv->plugins; iter; iter = iter->next) { + NMSystemConfigInterfaceCapabilities caps = NM_SYSTEM_CONFIG_INTERFACE_CAP_NONE; + + /* error will be cleared if any plugin supports saving the hostname */ + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, + "Saving the hostname failed."); + + g_object_get (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES, &caps, NULL); + if (caps & NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME) { + g_object_set (G_OBJECT (iter->data), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME, hostname, NULL); + g_clear_error (&error); + } } } - if (!success) { - error = g_error_new_literal (NM_SETTINGS_ERROR, - NM_SETTINGS_ERROR_SAVE_HOSTNAME_FAILED, - "Saving the hostname failed."); - } - -done: if (error) dbus_g_method_return_error (context, error); else @@ -1327,9 +1434,7 @@ impl_settings_save_hostname (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_HOSTNAME_INVALID, "The hostname was too long or contained invalid characters."); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto done; } /* Do any of the plugins support setting the hostname? */ @@ -1337,33 +1442,42 @@ impl_settings_save_hostname (NMSettings *self, error = g_error_new_literal (NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_SAVE_HOSTNAME_NOT_SUPPORTED, "None of the registered plugins support setting the hostname."); - dbus_g_method_return_error (context, error); - g_error_free (error); - return; + goto done; + } + + chain = nm_auth_chain_new_context (context, pk_hostname_cb, self); + if (!chain) { + error = g_error_new_literal (NM_SETTINGS_ERROR, + NM_SETTINGS_ERROR_PERMISSION_DENIED, + "Unable to authenticate the request."); + goto done; } - /* Otherwise validate the user request */ - chain = nm_auth_chain_new (context, NULL, pk_hostname_cb, self); - g_assert (chain); priv->auths = g_slist_append (priv->auths, chain); nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SETTINGS_MODIFY_HOSTNAME, TRUE); nm_auth_chain_set_data (chain, "hostname", g_strdup (hostname), g_free); + +done: + if (error) + dbus_g_method_return_error (context, error); + g_clear_error (&error); } static gboolean -have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device) +have_connection_for_device (NMSettings *self, NMDevice *device) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GHashTableIter iter; gpointer data; NMSettingConnection *s_con; NMSettingWired *s_wired; - NMSettingInfiniband *s_infiniband; const GByteArray *setting_mac; - gboolean ret = FALSE; + const guint8 *hwaddr; + guint hwaddr_len = 0; g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE); - g_return_val_if_fail (mac != NULL, FALSE); + + hwaddr = nm_device_get_hw_address (device, &hwaddr_len); /* Find a wired connection locked to the given MAC address, if any */ g_hash_table_iter_init (&iter, priv->connections); @@ -1372,344 +1486,221 @@ have_connection_for_device (NMSettings *self, GByteArray *mac, NMDevice *device) const char *ctype, *iface; s_con = nm_connection_get_setting_connection (connection); - ctype = nm_setting_connection_get_connection_type (s_con); - iface = nm_connection_get_virtual_iface_name (connection); - if (iface) { - if (!strcmp (iface, nm_device_get_iface (device))) { - ret = TRUE; - break; - } else - continue; - } + iface = nm_setting_connection_get_interface_name (s_con); + if (iface && strcmp (iface, nm_device_get_iface (device)) != 0) + continue; + ctype = nm_setting_connection_get_connection_type (s_con); if ( strcmp (ctype, NM_SETTING_WIRED_SETTING_NAME) - && strcmp (ctype, NM_SETTING_INFINIBAND_SETTING_NAME) && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) continue; s_wired = nm_connection_get_setting_wired (connection); - s_infiniband = nm_connection_get_setting_infiniband (connection); - /* No wired setting; therefore the PPPoE connection applies to any device */ if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) { - ret = TRUE; - break; + /* No wired setting; therefore the PPPoE connection applies to any device */ + return TRUE; } - g_assert (s_wired != NULL || s_infiniband != NULL); + g_assert (s_wired != NULL); - setting_mac = s_wired ? - nm_setting_wired_get_mac_address (s_wired) : - nm_setting_infiniband_get_mac_address (s_infiniband); + setting_mac = nm_setting_wired_get_mac_address (s_wired); if (setting_mac) { /* A connection mac-locked to this device */ - if (mac->len == setting_mac->len && - !memcmp (setting_mac->data, mac->data, mac->len)) { - ret = TRUE; - break; - } + if (hwaddr_len == setting_mac->len && + !memcmp (setting_mac->data, hwaddr, hwaddr_len)) + return TRUE; } else { /* A connection that applies to any wired device */ - ret = TRUE; - break; - } - } - - return ret; -} - -/* Search through the list of blacklisted MAC addresses in the config file. */ -static gboolean -is_mac_auto_wired_blacklisted (NMSettings *self, const GByteArray *mac) -{ - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GKeyFile *config; - char **list, **iter; - gboolean found = FALSE; - int hwaddr_type; - - g_return_val_if_fail (mac != NULL, FALSE); - - if (!priv->config_file) - return FALSE; - - config = g_key_file_new (); - if (!config) { - nm_log_warn (LOGD_SETTINGS, "not enough memory to load config file."); - return FALSE; - } - - g_key_file_set_list_separator (config, ','); - if (!g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_NONE, NULL)) - goto out; - - hwaddr_type = nm_utils_hwaddr_type (mac->len); - - list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, NULL, NULL); - for (iter = list; iter && *iter; iter++) { - guint8 *candidate, buffer[NM_UTILS_HWADDR_LEN_MAX]; - - if (strcmp (g_strstrip (*iter), "*") == 0) { - found = TRUE; - break; - } - - candidate = nm_utils_hwaddr_aton (*iter, hwaddr_type, buffer); - if (candidate && !memcmp (mac->data, candidate, mac->len)) { - found = TRUE; - break; + return TRUE; } } - if (list) - g_strfreev (list); + /* See if there's a known non-NetworkManager configuration for the device */ + if (nm_device_spec_match_list (device, priv->unrecognized_specs)) + return TRUE; -out: - g_key_file_free (config); - return found; + return FALSE; } -#define DEFAULT_WIRED_TAG "default-wired" +#define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection" +#define DEFAULT_WIRED_DEVICE_TAG "default-wired-device" + +static void default_wired_clear_tag (NMSettings *self, + NMDevice *device, + NMSettingsConnection *connection, + gboolean add_to_no_auto_default); static void -default_wired_deleted (NMDefaultWiredConnection *wired, - const GByteArray *mac, - NMSettings *self) +default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - NMSettingConnection *s_con; - int hwaddr_type; - char *tmp; - GKeyFile *config; - char **list, **iter, **updated; - gboolean found = FALSE; - gsize len = 0, i; - char *data; - - /* If there was no config file specified, there's nothing to do */ - if (!priv->config_file) - goto cleanup; - - /* When the default wired connection is removed (either deleted or saved - * to a new persistent connection by a plugin), write the MAC address of - * the wired device to the config file and don't create a new default wired - * connection for that device again. - */ + NMDevice *device; - s_con = nm_connection_get_setting_connection (NM_CONNECTION (wired)); - g_assert (s_con); - - /* Ignore removals of read-only connections, since they couldn't have - * been removed by the user. + /* When the default wired connection is removed (either deleted or saved to + * a new persistent connection by a plugin), write the MAC address of the + * wired device to the config file and don't create a new default wired + * connection for that device again. */ - if (nm_setting_connection_get_read_only (s_con)) - goto cleanup; - - config = g_key_file_new (); - if (!config) - goto cleanup; - - if (nm_connection_get_setting_wired (NM_CONNECTION (wired))) - hwaddr_type = ARPHRD_ETHER; - else if (nm_connection_get_setting_infiniband (NM_CONNECTION (wired))) - hwaddr_type = ARPHRD_INFINIBAND; - else - goto cleanup; - - g_key_file_set_list_separator (config, ','); - g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_KEEP_COMMENTS, NULL); - - list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL); - for (iter = list; iter && *iter; iter++) { - guint8 *candidate, buffer[NM_UTILS_HWADDR_LEN_MAX]; - - if (strcmp (g_strstrip (*iter), "*") == 0) { - found = TRUE; - break; - } - - candidate = nm_utils_hwaddr_aton (*iter, hwaddr_type, buffer); - if (candidate && !memcmp (mac->data, candidate, mac->len)) { - found = TRUE; - break; - } - } - - /* Add this device's MAC to the list */ - if (!found) { - tmp = nm_utils_hwaddr_ntoa (mac->data, hwaddr_type); - - /* New list; size + 1 for the new element, + 1 again for ending NULL */ - updated = g_malloc0 (sizeof (char*) * (len + 2)); - - /* Copy original list and add new MAC */ - for (i = 0; list && list[i]; i++) - updated[i] = list[i]; - updated[i++] = tmp; - updated[i] = NULL; - - g_key_file_set_string_list (config, - "main", CONFIG_KEY_NO_AUTO_DEFAULT, - (const char **) updated, - len + 2); - /* g_free() not g_strfreev() since 'updated' isn't a deep-copy */ - g_free (updated); - g_free (tmp); - - data = g_key_file_to_data (config, &len, NULL); - if (data) { - g_file_set_contents (priv->config_file, data, len, NULL); - g_free (data); - } - } - - if (list) - g_strfreev (list); - g_key_file_free (config); - -cleanup: - g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)), - DEFAULT_WIRED_TAG, - NULL); + device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG); + if (device) + default_wired_clear_tag (self, device, connection, TRUE); } static void -delete_cb (NMSettingsConnection *connection, GError *error, gpointer user_data) +default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, NMSettings *self) { + NMDevice *device; + + /* The connection has been changed by the user, it should no longer be + * considered a default wired connection, and should no longer affect + * the no-auto-default configuration option. + */ + device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG); + if (device) + default_wired_clear_tag (self, device, connection, FALSE); } static void -default_wired_try_update (NMDefaultWiredConnection *wired, - NMSettings *self) +default_wired_clear_tag (NMSettings *self, + NMDevice *device, + NMSettingsConnection *connection, + gboolean add_to_no_auto_default) { - GError *error = NULL; - const char *id; - NMSettingsConnection *added; - - /* Try to move this default wired conneciton to a plugin so that it has - * persistent storage. - */ - - /* Keep it alive over removal so we can re-add it if we need to */ - g_object_ref (wired); + g_return_if_fail (NM_IS_SETTINGS (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG)); + g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)); - id = nm_connection_get_id (NM_CONNECTION (wired)); - g_assert (id); + g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL); + g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL); - remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE); - added = add_new_connection (self, NM_CONNECTION (wired), &error); - if (added) { - nm_settings_connection_delete (NM_SETTINGS_CONNECTION (wired), delete_cb, NULL); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self); + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self); - g_object_set_data (G_OBJECT (nm_default_wired_connection_get_device (wired)), - DEFAULT_WIRED_TAG, - NULL); - nm_log_info (LOGD_SETTINGS, "Saved default wired connection '%s' to persistent storage", id); - } else { - nm_log_warn (LOGD_SETTINGS, "couldn't save default wired connection '%s': %d / %s", - id, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); - g_clear_error (&error); - - /* If there was an error, don't destroy the default wired connection, - * but add it back to the system settings service. Connection is already - * exported on the bus, don't export it again, thus do_export == FALSE. - */ - claim_connection (self, NM_SETTINGS_CONNECTION (wired), FALSE); - } - - g_object_unref (wired); + if (add_to_no_auto_default) + nm_config_set_ethernet_no_auto_default (NM_SETTINGS_GET_PRIVATE (self)->config, NM_CONFIG_DEVICE (device)); } void nm_settings_device_added (NMSettings *self, NMDevice *device) { NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GByteArray *mac = NULL; - const guint8 *hwaddr; - guint hwaddr_len = 0; - NMDefaultWiredConnection *wired; - gboolean read_only = TRUE; - const char *id; - char *defname; + NMConnection *connection; + NMSettingsConnection *added; + NMSetting *setting; + GError *error = NULL; + const guint8 *hw_address; + char *defname, *uuid; + guint len = 0; + GByteArray *mac; - if (!NM_IS_DEVICE_WIRED (device)) + if (!NM_IS_DEVICE_ETHERNET (device)) return; /* If the device isn't managed or it already has a default wired connection, * ignore it. */ if ( !nm_device_get_managed (device) - || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG)) + || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG) + || have_connection_for_device (self, device) + || !nm_config_get_ethernet_can_auto_default (priv->config, NM_CONFIG_DEVICE (device))) return; - hwaddr = nm_device_get_hw_address (device, &hwaddr_len); - - mac = g_byte_array_sized_new (hwaddr_len); - g_byte_array_append (mac, hwaddr, hwaddr_len); - - if ( have_connection_for_device (self, mac, device) - || is_mac_auto_wired_blacklisted (self, mac)) - goto ignore; + hw_address = nm_device_get_hw_address (device, &len); + if (!hw_address) + return; - if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)) - read_only = FALSE; + connection = nm_connection_new (); + g_assert (connection); + setting = nm_setting_connection_new (); + g_assert (setting); + nm_connection_add_setting (connection, setting); defname = nm_settings_utils_get_default_wired_name (priv->connections); - wired = nm_default_wired_connection_new (mac, device, defname, read_only); + uuid = nm_utils_uuid_generate (); + g_object_set (setting, + NM_SETTING_CONNECTION_ID, defname, + NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME, + NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), + NULL); + g_free (uuid); g_free (defname); - if (!wired) - goto ignore; - id = nm_connection_get_id (NM_CONNECTION (wired)); - g_assert (id); + /* Lock the connection to the device */ + setting = nm_setting_wired_new (); + nm_connection_add_setting (connection, setting); + + mac = g_byte_array_sized_new (len); + g_byte_array_append (mac, hw_address, len); + g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL); + g_byte_array_unref (mac); + + /* Add the connection */ + added = nm_settings_add_connection (self, connection, FALSE, &error); + g_object_unref (connection); - nm_log_info (LOGD_SETTINGS, "Added default wired connection '%s' for %s", - id, nm_device_get_udi (device)); + if (!added) { + nm_log_warn (LOGD_SETTINGS, "(%s) couldn't create default wired connection: %s", + nm_device_get_iface (device), + (error && error->message) ? error->message : "(unknown)"); + g_clear_error (&error); + return; + } - g_signal_connect (wired, "try-update", (GCallback) default_wired_try_update, self); - g_signal_connect (wired, "deleted", (GCallback) default_wired_deleted, self); - claim_connection (self, NM_SETTINGS_CONNECTION (wired), TRUE); - g_object_unref (wired); + g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device); + g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added); - g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, wired); + g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_BY_USER, + G_CALLBACK (default_wired_connection_updated_by_user_cb), self); + g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED, + G_CALLBACK (default_wired_connection_removed_cb), self); -ignore: - g_byte_array_free (mac, TRUE); + nm_log_info (LOGD_SETTINGS, "(%s): created default wired connection '%s'", + nm_device_get_iface (device), + nm_connection_get_id (NM_CONNECTION (added))); } void -nm_settings_device_removed (NMSettings *self, NMDevice *device) +nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting) { - NMDefaultWiredConnection *connection; + NMSettingsConnection *connection; - if (!NM_IS_DEVICE_WIRED (device)) - return; + connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG); + if (connection) { + default_wired_clear_tag (self, device, connection, FALSE); - connection = (NMDefaultWiredConnection *) g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG); - if (connection) - remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (connection), TRUE); + /* Don't delete the default wired connection on shutdown, so that it + * remains up and can be assumed if NM starts again. + */ + if (quitting == FALSE) + nm_settings_connection_delete (connection, NULL, NULL); + } } /***************************************************************/ -static gint -best_connection_sort (gconstpointer a, gconstpointer b, gpointer user_data) +/* GCompareFunc helper for sorting "best" connections. + * The function sorts connections in ascending timestamp order. + * That means an older connection (lower timestamp) goes before + * a newer one. + */ +gint +nm_settings_sort_connections (gconstpointer a, gconstpointer b) { NMSettingsConnection *ac = (NMSettingsConnection *) a; NMSettingsConnection *bc = (NMSettingsConnection *) b; guint64 ats = 0, bts = 0; - if (!ac && bc) + if (ac == bc) + return 0; + if (!ac) return -1; - else if (ac && !bc) + if (!bc) return 1; - else if (!ac && !bc) - return 0; - - g_assert (ac && bc); /* In the future we may use connection priorities in addition to timestamps */ nm_settings_connection_get_timestamp (ac, &ats); @@ -1757,7 +1748,7 @@ get_best_connections (NMConnectionProvider *provider, } /* List is sorted with oldest first */ - sorted = g_slist_insert_sorted_with_data (sorted, connection, best_connection_sort, NULL); + sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections); added++; if (max_requested && added > max_requested) { @@ -1775,58 +1766,53 @@ get_best_connections (NMConnectionProvider *provider, static const GSList * get_connections (NMConnectionProvider *provider) { - static GSList *list = NULL; + GSList *list = NULL; NMSettings *self = NM_SETTINGS (provider); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); GHashTableIter iter; NMSettingsConnection *connection; - /* Lazily free the list with every call so we can keep it 'const' for callers */ - g_slist_free (list); - list = NULL; - - g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections); + g_hash_table_iter_init (&iter, priv->connections); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) list = g_slist_prepend (list, connection); - return g_slist_reverse (list); + list = g_slist_reverse (list); + + /* Cache the list every call so we can keep it 'const' for callers */ + g_slist_free (priv->get_connections_cache); + priv->get_connections_cache = list; + return list; } -static gboolean -has_connections_loaded (NMConnectionProvider *provider) +static NMConnection * +cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid) { - NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (provider); - - return priv->connections_loaded; + return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid)); } /***************************************************************/ NMSettings * -nm_settings_new (const char *config_file, - const char **plugins, - GError **error) +nm_settings_new (GError **error) { NMSettings *self; NMSettingsPrivate *priv; self = g_object_new (NM_TYPE_SETTINGS, NULL); - if (!self) - return NULL; priv = NM_SETTINGS_GET_PRIVATE (self); - priv->config_file = g_strdup (config_file); + priv->config = nm_config_get (); priv->dbus_mgr = nm_dbus_manager_get (); - priv->bus = nm_dbus_manager_get_connection (priv->dbus_mgr); /* Load the plugins; fail if a plugin is not found. */ - if (!load_plugins (self, plugins, error)) { + if (!load_plugins (self, nm_config_get_plugins (priv->config), error)) { g_object_unref (self); return NULL; } - unmanaged_specs_changed (NULL, self); + load_connections (self); - dbus_g_connection_register_g_object (priv->bus, NM_DBUS_PATH_SETTINGS, G_OBJECT (self)); + nm_dbus_manager_register_object (priv->dbus_mgr, NM_DBUS_PATH_SETTINGS, self); return self; } @@ -1835,8 +1821,8 @@ connection_provider_init (NMConnectionProvider *cp_class) { cp_class->get_best_connections = get_best_connections; cp_class->get_connections = get_connections; - cp_class->has_connections_loaded = has_connections_loaded; cp_class->add_connection = _nm_connection_provider_add_connection; + cp_class->get_connection_by_uuid = cp_get_connection_by_uuid; } static void @@ -1846,8 +1832,6 @@ nm_settings_init (NMSettings *self) priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); - priv->session_monitor = nm_session_monitor_get (); - /* Hold a reference to the agent manager so it stays alive; the only * other holders are NMSettingsConnection objects which are often * transient, and we don't want the agent manager to get destroyed and @@ -1863,15 +1847,12 @@ dispose (GObject *object) { NMSettings *self = NM_SETTINGS (object); NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); - GSList *iter; - for (iter = priv->auths; iter; iter = g_slist_next (iter)) - nm_auth_chain_unref ((NMAuthChain *) iter->data); - g_slist_free (priv->auths); + g_slist_free_full (priv->auths, (GDestroyNotify) nm_auth_chain_unref); + priv->auths = NULL; - g_object_unref (priv->dbus_mgr); + priv->dbus_mgr = NULL; - g_object_unref (priv->session_monitor); g_object_unref (priv->agent_mgr); G_OBJECT_CLASS (nm_settings_parent_class)->dispose (object); @@ -1884,13 +1865,12 @@ finalize (GObject *object) NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); g_hash_table_destroy (priv->connections); + g_slist_free (priv->get_connections_cache); - clear_unmanaged_specs (self); - - g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL); - g_slist_free (priv->plugins); + g_slist_free_full (priv->unmanaged_specs, g_free); + g_slist_free_full (priv->unrecognized_specs, g_free); - g_free (priv->config_file); + g_slist_free_full (priv->plugins, g_object_unref); G_OBJECT_CLASS (nm_settings_parent_class)->finalize (object); } @@ -1900,8 +1880,12 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMSettings *self = NM_SETTINGS (object); + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); const GSList *specs, *iter; GSList *copy = NULL; + GHashTableIter citer; + GPtrArray *array; + const char *path; switch (prop_id) { case PROP_UNMANAGED_SPECS: @@ -1920,6 +1904,13 @@ get_property (GObject *object, guint prop_id, case PROP_CAN_MODIFY: g_value_set_boolean (value, !!get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS)); break; + case PROP_CONNECTIONS: + array = g_ptr_array_sized_new (g_hash_table_size (priv->connections)); + g_hash_table_iter_init (&citer, priv->connections); + while (g_hash_table_iter_next (&citer, (gpointer) &path, NULL)) + g_ptr_array_add (array, g_strdup (path)); + g_value_take_boxed (value, array); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1965,6 +1956,14 @@ nm_settings_class_init (NMSettingsClass *class) FALSE, G_PARAM_READABLE)); + g_object_class_install_property + (object_class, PROP_CONNECTIONS, + g_param_spec_boxed (NM_SETTINGS_CONNECTIONS, + "Connections", + "Connections", + DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, + G_PARAM_READABLE)); + /* signals */ signals[PROPERTIES_CHANGED] = g_signal_new ("properties-changed", @@ -1992,6 +1991,15 @@ nm_settings_class_init (NMSettingsClass *class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); + signals[CONNECTION_UPDATED_BY_USER] = + g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_UPDATED_BY_USER, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + signals[CONNECTION_REMOVED] = g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, G_OBJECT_CLASS_TYPE (object_class), @@ -2010,15 +2018,6 @@ nm_settings_class_init (NMSettingsClass *class) g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); - signals[CONNECTIONS_LOADED] = - g_signal_new (NM_SETTINGS_SIGNAL_CONNECTIONS_LOADED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NMSettingsClass, connections_loaded), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - signals[AGENT_REGISTERED] = g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED, G_OBJECT_CLASS_TYPE (object_class), -- cgit 1.3.0-6-gf8a5