settings: track whether connection is saved to disk or not

Use the new NMConnection 'changed' signal to mark connections
as dirty/unsaved, and reset that when they get flushed to disk.
Previously, the 'Updated' signal was emitted only when the
connection was changed and flushed to disk, but now we have
more granular needs, and the signal is emitted whenever the
connection actually *is* changed, regardless of whether its
flushed to disk or not.
This commit is contained in:
Dan Williams 2013-04-11 18:53:54 -05:00
parent 87517ba6df
commit cd5d92705d
8 changed files with 157 additions and 26 deletions

View file

@ -88,6 +88,16 @@
</tp:docstring>
</signal>
<property name="Unsaved" type="b" access="read">
<tp:docstring>
If set, indicates that the in-memory state of the
connection does not match the on-disk state. This flag
will be set when UpdateUnsaved() is called or when any
connection details change, and cleared when the connection
is saved to disk via Save() or from internal operations.
</tp:docstring>
</property>
</interface>
</node>

View file

@ -414,9 +414,12 @@ nm_connection_replace_settings_from_connection (NMConnection *connection,
*/
g_hash_table_remove_all (NM_CONNECTION_GET_PRIVATE (connection)->settings);
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (new_connection)->settings);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting))
nm_connection_add_setting (connection, nm_setting_duplicate (setting));
if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (new_connection)->settings)) {
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (new_connection)->settings);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting))
nm_connection_add_setting (connection, nm_setting_duplicate (setting));
} else
g_signal_emit (connection, signals[CHANGED], 0);
return nm_connection_verify (connection, error);
}

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
* (C) Copyright 2008 - 2012 Red Hat, Inc.
* (C) Copyright 2008 - 2013 Red Hat, Inc.
*/
#include "config.h"
@ -40,6 +40,7 @@
#include "nm-manager-auth.h"
#include "nm-agent-manager.h"
#include "NetworkManagerUtils.h"
#include "nm-properties-changed-signal.h"
#define SETTINGS_TIMESTAMPS_FILE NMSTATEDIR "/timestamps"
#define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
@ -69,6 +70,7 @@ G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_CONNECTION)
enum {
PROP_0 = 0,
PROP_VISIBLE,
PROP_UNSAVED,
};
enum {
@ -86,6 +88,13 @@ typedef struct {
NMSessionMonitor *session_monitor;
guint session_changed_id;
/* TRUE if the connection has not yet been saved to disk,
* or it it contains changes that have not been saved to disk.
*/
gboolean unsaved;
guint updated_idle_id;
GSList *pending_auths; /* List of pending authentication requests */
gboolean visible; /* Is this connection is visible by some session? */
GSList *reqs; /* in-progress secrets requests */
@ -366,12 +375,45 @@ secrets_cleared_cb (NMSettingsConnection *self)
priv->agent_secrets = NULL;
}
static gboolean
emit_updated (NMSettingsConnection *self)
{
NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->updated_idle_id = 0;
g_signal_emit (self, signals[UPDATED], 0);
return FALSE;
}
static void
set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
if (priv->unsaved != now_unsaved) {
priv->unsaved = now_unsaved;
g_object_notify (G_OBJECT (self), NM_SETTINGS_CONNECTION_UNSAVED);
}
}
static void
changed_cb (NMSettingsConnection *self, gpointer user_data)
{
gboolean update_unsaved = !!user_data;
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
if (update_unsaved)
set_unsaved (self, TRUE);
if (priv->updated_idle_id == 0)
priv->updated_idle_id = g_idle_add ((GSourceFunc) emit_updated, self);
}
/* Update the settings of this connection to match that of 'new_connection',
* taking care to make a private copy of secrets.
*/
gboolean
nm_settings_connection_replace_settings (NMSettingsConnection *self,
NMConnection *new_connection,
gboolean update_unsaved,
GError **error)
{
NMSettingsConnectionPrivate *priv;
@ -390,6 +432,11 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
return TRUE;
}
/* Disconnect the changed signal to ensure we don't set Unsaved when
* it's not required.
*/
g_signal_handlers_block_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
if (nm_connection_replace_settings_from_connection (NM_CONNECTION (self),
new_connection,
error)) {
@ -411,7 +458,15 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
}
nm_settings_connection_recheck_visibility (self);
/* Manually emit changed signal since we disconnected the handler, but
* only update Unsaved if the caller wanted us to.
*/
changed_cb (self, GUINT_TO_POINTER (update_unsaved));
}
g_signal_handlers_unblock_by_func (self, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
return success;
}
@ -438,7 +493,7 @@ nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
g_return_if_fail (NM_IS_CONNECTION (new_connection));
if (nm_settings_connection_replace_settings (self, new_connection, &error)) {
if (nm_settings_connection_replace_settings (self, new_connection, TRUE, &error)) {
nm_settings_connection_commit_changes (self, callback ? callback : ignore_cb, user_data);
} else {
if (callback)
@ -447,6 +502,21 @@ nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
}
}
static void
commit_changes (NMSettingsConnection *self,
NMSettingsConnectionCommitFunc callback,
gpointer user_data)
{
/* Subclasses only call this function if the save was successful, so at
* this point the connection is synced to disk and no longer unsaved.
*/
set_unsaved (self, FALSE);
g_object_ref (self);
callback (self, NULL, user_data);
g_object_unref (self);
}
void
nm_settings_connection_commit_changes (NMSettingsConnection *connection,
NMSettingsConnectionCommitFunc callback,
@ -489,17 +559,6 @@ nm_settings_connection_delete (NMSettingsConnection *connection,
}
}
static void
commit_changes (NMSettingsConnection *connection,
NMSettingsConnectionCommitFunc callback,
gpointer user_data)
{
g_object_ref (connection);
g_signal_emit (connection, signals[UPDATED], 0);
callback (connection, NULL, user_data);
g_object_unref (connection);
}
static void
remove_entry_from_db (NMSettingsConnection *connection, const char* db_name)
{
@ -1397,6 +1456,14 @@ nm_settings_connection_signal_remove (NMSettingsConnection *self)
g_signal_emit_by_name (self, "unregister");
}
gboolean
nm_settings_connection_get_unsaved (NMSettingsConnection *self)
{
return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->unsaved;
}
/**************************************************************/
/**
* nm_settings_connection_get_timestamp:
* @connection: the #NMSettingsConnection
@ -1729,7 +1796,8 @@ nm_settings_connection_init (NMSettingsConnection *self)
priv->seen_bssids = g_hash_table_new_full (mac_hash, mac_equal, g_free, g_free);
g_signal_connect (self, "secrets-cleared", G_CALLBACK (secrets_cleared_cb), NULL);
g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (changed_cb), GUINT_TO_POINTER (TRUE));
}
static void
@ -1743,6 +1811,11 @@ dispose (GObject *object)
goto out;
priv->disposed = TRUE;
if (priv->updated_idle_id) {
g_source_remove (priv->updated_idle_id);
priv->updated_idle_id = 0;
}
if (priv->system_secrets)
g_object_unref (priv->system_secrets);
if (priv->agent_secrets)
@ -1776,9 +1849,14 @@ static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (object);
switch (prop_id) {
case PROP_VISIBLE:
g_value_set_boolean (value, NM_SETTINGS_CONNECTION_GET_PRIVATE (object)->visible);
g_value_set_boolean (value, priv->visible);
break;
case PROP_UNSAVED:
g_value_set_boolean (value, priv->unsaved);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -1818,6 +1896,16 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
FALSE,
G_PARAM_READABLE));
g_object_class_install_property
(object_class, PROP_UNSAVED,
g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED,
"Unsaved",
"TRUE when the connection has not yet been saved "
"to permanent storage (eg disk) or when it "
"has been changed but not yet saved.",
FALSE,
G_PARAM_READABLE));
/* Signals */
signals[UPDATED] =
g_signal_new (NM_SETTINGS_CONNECTION_UPDATED,
@ -1847,6 +1935,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class),
&dbus_glib_nm_settings_connection_object_info);
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (class),
&dbus_glib_nm_settings_connection_object_info);
}

View file

@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
* (C) Copyright 2008 - 2011 Red Hat, Inc.
* (C) Copyright 2008 - 2013 Red Hat, Inc.
*/
#ifndef NM_SETTINGS_CONNECTION_H
@ -35,15 +35,17 @@ G_BEGIN_DECLS
#define NM_IS_SETTINGS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_CONNECTION))
#define NM_SETTINGS_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionClass))
/* Signals */
#define NM_SETTINGS_CONNECTION_UPDATED "updated"
#define NM_SETTINGS_CONNECTION_REMOVED "removed"
#define NM_SETTINGS_CONNECTION_GET_SECRETS "get-secrets"
#define NM_SETTINGS_CONNECTION_CANCEL_SECRETS "cancel-secrets"
/* Properties */
#define NM_SETTINGS_CONNECTION_VISIBLE "visible"
#define NM_SETTINGS_CONNECTION_UNSAVED "unsaved"
typedef struct _NMSettingsConnection NMSettingsConnection;
typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
typedef void (*NMSettingsConnectionCommitFunc) (NMSettingsConnection *connection,
@ -82,6 +84,7 @@ void nm_settings_connection_commit_changes (NMSettingsConnection *connection,
gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self,
NMConnection *new_connection,
gboolean update_unsaved,
GError **error);
void nm_settings_connection_replace_and_commit (NMSettingsConnection *self,
@ -122,6 +125,8 @@ gboolean nm_settings_connection_check_permission (NMSettingsConnection *self,
void nm_settings_connection_signal_remove (NMSettingsConnection *self);
gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *connection,
guint64 *out_timestamp);

View file

@ -77,7 +77,10 @@ nm_example_connection_new (const char *full_path,
/* Update our settings with what was read from the file or what got passed
* in as a source NMConnection.
*/
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object), tmp, error)) {
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
tmp,
TRUE,
error)) {
g_object_unref (object);
object = NULL;
goto out;

View file

@ -109,6 +109,7 @@ nm_ifcfg_connection_new (const char *full_path,
char *routefile = NULL;
char *route6file = NULL;
NMInotifyHelper *ih;
gboolean update_unsaved = TRUE;
g_return_val_if_fail (full_path != NULL, NULL);
@ -125,6 +126,9 @@ nm_ifcfg_connection_new (const char *full_path,
ignore_error);
if (!tmp)
return NULL;
/* If we just read the connection from disk, it's clearly not Unsaved */
update_unsaved = FALSE;
}
object = (GObject *) g_object_new (NM_TYPE_IFCFG_CONNECTION,
@ -134,7 +138,10 @@ nm_ifcfg_connection_new (const char *full_path,
goto out;
/* Update our settings with what was read from the file */
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object), tmp, error)) {
if (nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
tmp,
update_unsaved,
error)) {
g_object_unref (object);
object = NULL;
goto out;

View file

@ -62,6 +62,7 @@ nm_ifnet_connection_new (const char *conn_name, NMConnection *source)
NMConnection *tmp;
GObject *object;
GError *error = NULL;
gboolean update_unsaved = TRUE;
g_return_val_if_fail (conn_name != NULL, NULL);
@ -73,11 +74,17 @@ nm_ifnet_connection_new (const char *conn_name, NMConnection *source)
g_error_free (error);
return NULL;
}
/* If we just read the connection from disk, it's clearly not Unsaved */
update_unsaved = FALSE;
}
object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL);
NM_IFNET_CONNECTION_GET_PRIVATE (object)->conn_name = g_strdup (conn_name);
nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object), tmp, NULL);
nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
tmp,
update_unsaved,
NULL);
g_object_unref (tmp);
return NM_IFNET_CONNECTION (object);

View file

@ -49,6 +49,7 @@ nm_keyfile_connection_new (const char *full_path,
NMKeyfileConnectionPrivate *priv;
NMConnection *tmp;
const char *uuid;
gboolean update_unsaved = TRUE;
g_return_val_if_fail (full_path != NULL, NULL);
@ -59,6 +60,9 @@ nm_keyfile_connection_new (const char *full_path,
tmp = nm_keyfile_plugin_connection_from_file (full_path, error);
if (!tmp)
return NULL;
/* If we just read the connection from disk, it's clearly not Unsaved */
update_unsaved = FALSE;
}
object = (GObject *) g_object_new (NM_TYPE_KEYFILE_CONNECTION, NULL);
@ -67,7 +71,10 @@ nm_keyfile_connection_new (const char *full_path,
priv->path = g_strdup (full_path);
/* Update our settings with what was read from the file */
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object), tmp, error)) {
if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
tmp,
update_unsaved,
error)) {
g_object_unref (object);
object = NULL;
goto out;