settings: convert NMDefaultWiredConnection into a plain NMSettingsConnection (rh #1029464) (bgo #712188)

Changing the default wired connection has always deleted the connection
(thus disconnecting the interface) and re-added it as a settings plugin
connection.  That was always sub-optimal, but until the 'unsaved' connection
stuff landed this summer, we couldn't do anything about that.  Clean
that all up, adding the connection as an unsaved connection right from
the start, which allows changes to the connection without having to
delete and recreate it, thus preventing disconnection of any interface
that is using the connection.

A new signal is added to NMSettingsConnection that is only emitted when
the connection is changed from D-Bus (thus indicating an explicit user-
requested change) since the connection may be modified internally by
NetworkManager.  NM-triggered changes should not result in the connection
no longer being a default-wired connection.

https://bugzilla.gnome.org/show_bug.cgi?id=712188
https://bugzilla.redhat.com/show_bug.cgi?id=1029464
This commit is contained in:
Dan Williams 2013-11-12 14:26:20 -06:00
parent 709bc90b6b
commit 49983db85e
6 changed files with 121 additions and 351 deletions

View file

@ -163,8 +163,6 @@ nm_sources = \
\
settings/nm-agent-manager.c \
settings/nm-agent-manager.h \
settings/nm-default-wired-connection.c \
settings/nm-default-wired-connection.h \
settings/nm-inotify-helper.c \
settings/nm-inotify-helper.h \
settings/nm-secret-agent.c \

View file

@ -1,190 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
* (C) Copyright 2009 - 2011 Red Hat, Inc.
*/
#include "config.h"
#include <netinet/ether.h>
#include <NetworkManager.h>
#include <nm-setting-connection.h>
#include <nm-setting-wired.h>
#include <nm-utils.h>
#include "nm-dbus-glib-types.h"
#include "nm-default-wired-connection.h"
#include "nm-device-ethernet.h"
G_DEFINE_TYPE (NMDefaultWiredConnection, nm_default_wired_connection, NM_TYPE_SETTINGS_CONNECTION)
#define NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionPrivate))
typedef struct {
NMDevice *device;
} NMDefaultWiredConnectionPrivate;
enum {
TRY_UPDATE,
DELETED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
/****************************************************************/
NMDevice *
nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired)
{
g_return_val_if_fail (NM_IS_DEFAULT_WIRED_CONNECTION (wired), NULL);
return NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (wired)->device;
}
static void
commit_changes (NMSettingsConnection *connection,
NMSettingsConnectionCommitFunc callback,
gpointer user_data)
{
NMDefaultWiredConnection *self = NM_DEFAULT_WIRED_CONNECTION (connection);
/* Keep the object alive over try-update since it might get removed
* from the settings service there, but we still need it for the callback.
*/
g_object_ref (connection);
g_signal_emit (self, signals[TRY_UPDATE], 0);
callback (connection, NULL, user_data);
g_object_unref (connection);
}
static void
do_delete (NMSettingsConnection *connection,
NMSettingsConnectionDeleteFunc callback,
gpointer user_data)
{
g_signal_emit (connection, signals[DELETED], 0);
NM_SETTINGS_CONNECTION_CLASS (nm_default_wired_connection_parent_class)->delete (connection,
callback,
user_data);
}
/****************************************************************/
NMDefaultWiredConnection *
nm_default_wired_connection_new (NMDevice *device,
const char *defname,
gboolean read_only)
{
NMDefaultWiredConnection *self;
NMDefaultWiredConnectionPrivate *priv;
NMSetting *setting;
char *uuid;
const guint8 *hw_address;
guint len = 0;
GByteArray *mac;
g_return_val_if_fail (NM_IS_DEVICE_ETHERNET (device), NULL);
g_return_val_if_fail (defname != NULL, NULL);
self = (NMDefaultWiredConnection *) g_object_new (NM_TYPE_DEFAULT_WIRED_CONNECTION, NULL);
priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (self);
priv->device = g_object_ref (device);
setting = nm_setting_connection_new ();
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_READ_ONLY, read_only,
NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
NULL);
g_free (uuid);
nm_connection_add_setting (NM_CONNECTION (self), setting);
/* Lock the connection to the specific device */
hw_address = nm_device_get_hw_address (device, &len);
mac = g_byte_array_sized_new (len);
g_byte_array_append (mac, hw_address, len);
setting = nm_setting_wired_new ();
g_object_set (setting, NM_SETTING_WIRED_MAC_ADDRESS, mac, NULL);
nm_connection_add_setting (NM_CONNECTION (self), setting);
g_byte_array_unref (mac);
return self;
}
static void
nm_default_wired_connection_init (NMDefaultWiredConnection *self)
{
}
static void
dispose (GObject *object)
{
NMDefaultWiredConnectionPrivate *priv = NM_DEFAULT_WIRED_CONNECTION_GET_PRIVATE (object);
g_clear_object (&priv->device);
G_OBJECT_CLASS (nm_default_wired_connection_parent_class)->dispose (object);
}
static void
nm_default_wired_connection_class_init (NMDefaultWiredConnectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMSettingsConnectionClass *settings_class = NM_SETTINGS_CONNECTION_CLASS (klass);
g_type_class_add_private (klass, sizeof (NMDefaultWiredConnectionPrivate));
/* Virtual methods */
object_class->dispose = dispose;
settings_class->commit_changes = commit_changes;
settings_class->delete = do_delete;
/* Signals */
signals[TRY_UPDATE] =
g_signal_new ("try-update",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/* The 'deleted' signal is used to signal intentional deletions (like
* updating or user-requested deletion) rather than using the
* superclass' 'removed' signal, since that signal doesn't have the
* semantics we want; it gets emitted as a side-effect of various operations
* and is meant more for D-Bus clients instead of in-service uses.
*/
signals[DELETED] =
g_signal_new ("deleted",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}

View file

@ -1,55 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager system settings service
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
* (C) Copyright 2009 - 2011 Red Hat, Inc.
*/
#ifndef NM_DEFAULT_WIRED_CONNECTION_H
#define NM_DEFAULT_WIRED_CONNECTION_H
#include "nm-settings-connection.h"
#include "nm-device.h"
G_BEGIN_DECLS
#define NM_TYPE_DEFAULT_WIRED_CONNECTION (nm_default_wired_connection_get_type ())
#define NM_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnection))
#define NM_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
#define NM_IS_DEFAULT_WIRED_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION))
#define NM_IS_DEFAULT_WIRED_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_WIRED_CONNECTION))
#define NM_DEFAULT_WIRED_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_WIRED_CONNECTION, NMDefaultWiredConnectionClass))
typedef struct {
NMSettingsConnection parent;
} NMDefaultWiredConnection;
typedef struct {
NMSettingsConnectionClass parent;
} NMDefaultWiredConnectionClass;
GType nm_default_wired_connection_get_type (void);
NMDefaultWiredConnection *nm_default_wired_connection_new (NMDevice *device,
const char *defname,
gboolean read_only);
NMDevice *nm_default_wired_connection_get_device (NMDefaultWiredConnection *wired);
G_END_DECLS
#endif /* NM_DEFAULT_WIRED_CONNECTION_H */

View file

@ -83,6 +83,7 @@ enum {
enum {
UPDATED,
REMOVED,
DBUS_UPDATED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
@ -1236,6 +1237,8 @@ con_update_cb (NMSettingsConnection *self,
GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
nm_agent_manager_save_secrets (info->agent_mgr, for_agent, TRUE, info->sender_uid);
g_object_unref (for_agent);
g_signal_emit (self, signals[DBUS_UPDATED], 0);
}
update_complete (self, info, error);
@ -2035,6 +2038,8 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
G_PARAM_READABLE));
/* Signals */
/* Emitted when the connection is changed for any reason */
signals[UPDATED] =
g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
G_TYPE_FROM_CLASS (class),
@ -2044,6 +2049,15 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/* Emitted when connection is changed from D-Bus */
signals[DBUS_UPDATED] =
g_signal_new (NM_SETTINGS_CONNECTION_DBUS_UPDATED,
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_FIRST,
0, NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[REMOVED] =
g_signal_new (NM_SETTINGS_CONNECTION_REMOVED,
G_TYPE_FROM_CLASS (class),

View file

@ -41,6 +41,9 @@ G_BEGIN_DECLS
#define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets"
#define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets"
/* Emitted when connection is changed from D-Bus */
#define NM_SETTINGS_CONNECTION_DBUS_UPDATED "dbus-updated"
/* Properties */
#define NM_SETTINGS_CONNECTION_VISIBLE "visible"
#define NM_SETTINGS_CONNECTION_UNSAVED "unsaved"

View file

@ -58,7 +58,6 @@
#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"
@ -878,21 +877,6 @@ claim_connection (NMSettings *self,
}
}
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);
}
}
/**
* nm_settings_add_connection:
* @self: the #NMSettings object
@ -1487,93 +1471,77 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
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,
NMSettings *self)
default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSettings *self)
{
NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
NMDevice *device;
NMSettingConnection *s_con;
device = nm_default_wired_connection_get_device (wired);
g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, NULL);
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.
*/
if (nm_setting_connection_get_read_only (s_con))
return;
/* 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
/* 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.
*/
nm_config_set_ethernet_no_auto_default (priv->config, NM_CONFIG_DEVICE (device));
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_dbus_updated_cb (NMSettingsConnection *connection, NMSettings *self)
{
}
NMDevice *device;
static void
default_wired_try_update (NMDefaultWiredConnection *wired,
NMSettings *self)
{
GError *error = NULL;
const char *id;
NMSettingsConnection *added;
/* Try to move this default wired conneciton to a plugin so that it has
* persistent storage.
/* 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);
}
/* Keep it alive over removal so we can re-add it if we need to */
g_object_ref (wired);
static void
default_wired_clear_tag (NMSettings *self,
NMDevice *device,
NMSettingsConnection *connection,
gboolean add_to_no_auto_default)
{
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 = nm_settings_add_connection (self, NM_CONNECTION (wired), TRUE, &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_dbus_updated_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);
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_ETHERNET (device))
return;
@ -1582,45 +1550,77 @@ nm_settings_device_added (NMSettings *self, NMDevice *device)
* 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;
if (get_plugin (self, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS))
read_only = FALSE;
defname = nm_settings_utils_get_default_wired_name (priv->connections);
wired = nm_default_wired_connection_new (device, defname, read_only);
g_free (defname);
if (!wired)
hw_address = nm_device_get_hw_address (device, &len);
if (!hw_address)
return;
id = nm_connection_get_id (NM_CONNECTION (wired));
g_assert (id);
connection = nm_connection_new ();
g_assert (connection);
setting = nm_setting_connection_new ();
g_assert (setting);
nm_connection_add_setting (connection, setting);
nm_log_info (LOGD_SETTINGS, "Added default wired connection '%s' for %s",
id, nm_device_get_udi (device));
defname = nm_settings_utils_get_default_wired_name (priv->connections);
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);
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);
/* Lock the connection to the device */
setting = nm_setting_wired_new ();
nm_connection_add_setting (connection, setting);
g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_TAG, wired);
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);
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_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
g_signal_connect (added, NM_SETTINGS_CONNECTION_DBUS_UPDATED,
G_CALLBACK (default_wired_connection_dbus_updated_cb), self);
g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
G_CALLBACK (default_wired_connection_removed_cb), self);
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)
{
NMDefaultWiredConnection *connection;
NMSettingsConnection *connection;
if (!NM_IS_DEVICE_ETHERNET (device))
return;
connection = (NMDefaultWiredConnection *) g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_TAG);
if (connection)
remove_default_wired_connection (self, NM_SETTINGS_CONNECTION (connection), TRUE);
connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
if (connection) {
default_wired_clear_tag (self, device, connection, FALSE);
nm_settings_connection_delete (connection, NULL, NULL);
}
}
/***************************************************************/