diff --git a/src/nm-manager.c b/src/nm-manager.c index 2586fada4e..b748131008 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -467,39 +467,6 @@ nm_manager_update_state (NMManager *manager) } } -static void -ignore_cb (NMSettingsConnection *connection, GError *error, gpointer user_data) -{ -} - -static void -update_active_connection_timestamp (NMManager *manager, NMDevice *device) -{ - NMActRequest *req; - NMConnection *connection; - NMSettingConnection *s_con; - NMManagerPrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (device)); - - priv = NM_MANAGER_GET_PRIVATE (manager); - req = nm_device_get_act_request (device); - if (!req) - return; - - connection = nm_act_request_get_connection (req); - g_assert (connection); - - s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); - g_assert (s_con); - g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), NULL); - - if (nm_setting_connection_get_read_only (s_con)) - return; - - nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (connection), ignore_cb, NULL); -} - static void manager_device_state_changed (NMDevice *device, NMDeviceState new_state, @@ -523,8 +490,14 @@ manager_device_state_changed (NMDevice *device, nm_manager_update_state (manager); - if (new_state == NM_DEVICE_STATE_ACTIVATED) - update_active_connection_timestamp (manager, device); + if (new_state == NM_DEVICE_STATE_ACTIVATED) { + NMActRequest *req; + + req = nm_device_get_act_request (device); + if (req) + nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (nm_act_request_get_connection (req)), + (guint64) time (NULL)); + } } /* Removes a device from a device list; returns the start of the new device list */ @@ -3347,7 +3320,8 @@ periodic_update_active_connection_timestamps (gpointer user_data) req = nm_manager_get_act_request_by_path (manager, active_path, &device); if (device && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) - update_active_connection_timestamp (manager, device); + nm_settings_connection_update_timestamp (NM_SETTINGS_CONNECTION (nm_act_request_get_connection (req)), + (guint64) time (NULL)); } return TRUE; diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c index 4c059df862..6d3923ee66 100644 --- a/src/settings/nm-settings-connection.c +++ b/src/settings/nm-settings-connection.c @@ -38,6 +38,8 @@ #include "nm-marshal.h" #include "nm-agent-manager.h" +#define SETTINGS_TIMESTAMPS_FILE LOCALSTATEDIR"/lib/NetworkManager/timestamps" + static void impl_settings_connection_get_settings (NMSettingsConnection *connection, DBusGMethodInvocation *context); @@ -88,6 +90,8 @@ typedef struct { NMSessionMonitor *session_monitor; guint session_changed_id; + + guint64 timestamp; /* Up-to-date timestamp of connection use */ } NMSettingsConnectionPrivate; /**************************************************************/ @@ -330,6 +334,34 @@ commit_changes (NMSettingsConnection *connection, g_object_unref (connection); } +static void +remove_timestamp_from_db (NMSettingsConnection *connection) +{ + GKeyFile *timestamps_file; + + timestamps_file = g_key_file_new (); + if (g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + const char *connection_uuid; + char *data; + gsize len; + GError *error = NULL; + + connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + + g_key_file_remove_key (timestamps_file, "timestamps", connection_uuid, NULL); + data = g_key_file_to_data (timestamps_file, &len, &error); + if (data) { + g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error); + g_free (data); + } + if (error) { + nm_log_warn (LOGD_SETTINGS, "error writing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); + g_error_free (error); + } + } + g_key_file_free (timestamps_file); +} + static void do_delete (NMSettingsConnection *connection, NMSettingsConnectionDeleteFunc callback, @@ -346,6 +378,9 @@ do_delete (NMSettingsConnection *connection, nm_connection_clear_secrets (for_agents); nm_agent_manager_delete_secrets (priv->agent_mgr, for_agents, FALSE, 0); + /* Remove timestamp from timestamps database file */ + remove_timestamp_from_db (connection); + /* Signal the connection is removed and deleted */ g_signal_emit (connection, signals[REMOVED], 0); callback (connection, NULL, user_data); @@ -865,15 +900,35 @@ get_settings_auth_cb (NMSettingsConnection *self, dbus_g_method_return_error (context, error); else { GHashTable *settings; + NMConnection *dupl_con; + NMSettingConnection *s_con; + guint64 timestamp; + + dupl_con = nm_connection_duplicate (NM_CONNECTION (self)); + g_assert (dupl_con); + + /* Timestamp is not updated in connection's 'timestamp' property, + * because it would force updating the connection and in turn + * writing to /etc periodically, which we want to avoid. Rather real + * timestamps are kept track of in a private variable. So, substitute + * timestamp property with the real one here before returning the settings. + */ + timestamp = nm_settings_connection_get_timestamp (self); + if (timestamp) { + s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (dupl_con), NM_TYPE_SETTING_CONNECTION)); + g_assert (s_con); + g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL); + } /* Secrets should *never* be returned by the GetSettings method, they * get returned by the GetSecrets method which can be better * protected against leakage of secrets to unprivileged callers. */ - settings = nm_connection_to_hash (NM_CONNECTION (self), NM_SETTING_HASH_FLAG_NO_SECRETS); + settings = nm_connection_to_hash (NM_CONNECTION (dupl_con), NM_SETTING_HASH_FLAG_NO_SECRETS); g_assert (settings); dbus_g_method_return (context, settings); g_hash_table_destroy (settings); + g_object_unref (dupl_con); } } @@ -1194,6 +1249,99 @@ nm_settings_connection_signal_remove (NMSettingsConnection *self) g_signal_emit_by_name (self, "unregister"); } +/** + * nm_settings_connection_get_timestamp: + * @connection: the #NMSettingsConnection + * + * Returns current connection's timestamp. + * + * Returns: timestamp of the last connection use (0 when it's not used) + **/ +guint64 +nm_settings_connection_get_timestamp (NMSettingsConnection *connection) +{ + g_return_val_if_fail (NM_SETTINGS_CONNECTION (connection), 0); + + return NM_SETTINGS_CONNECTION_GET_PRIVATE (connection)->timestamp; +} + +/** + * nm_settings_connection_update_timestamp: + * @connection: the #NMSettingsConnection + * @timestamp: timestamp to set into the connection and to store into + * the timestamps database + * + * Updates the connection and timestamps database with the provided timestamp. + **/ +void +nm_settings_connection_update_timestamp (NMSettingsConnection *connection, guint64 timestamp) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + const char *connection_uuid; + GKeyFile *timestamps_file; + char *data; + gsize len; + GError *error = NULL; + + /* Update timestamp in private storage */ + priv->timestamp = timestamp; + + /* Save timestamp to timestamps database file */ + timestamps_file = g_key_file_new (); + if (!g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, &error)) { + if (!(error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT)) + nm_log_warn (LOGD_SETTINGS, "error parsing timestamps file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); + g_clear_error (&error); + } + + connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + g_key_file_set_uint64 (timestamps_file, "timestamps", connection_uuid, timestamp); + + data = g_key_file_to_data (timestamps_file, &len, &error); + if (data) { + g_file_set_contents (SETTINGS_TIMESTAMPS_FILE, data, len, &error); + g_free (data); + } + if (error) { + nm_log_warn (LOGD_SETTINGS, "error saving timestamp to file '%s': %s", SETTINGS_TIMESTAMPS_FILE, error->message); + g_error_free (error); + } + g_key_file_free (timestamps_file); +} + +/** + * nm_settings_connection_read_and_fill_timestamp: + * @connection: the #NMSettingsConnection + * + * Retrieves timestamp of the connection's last usage from database file and + * stores it into the connection private data. + **/ +void +nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection) +{ + NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (connection); + const char *connection_uuid; + guint64 timestamp; + GKeyFile *timestamps_file; + GError *err = NULL; + + /* Get timestamp from database file */ + timestamps_file = g_key_file_new (); + g_key_file_load_from_file (timestamps_file, SETTINGS_TIMESTAMPS_FILE, G_KEY_FILE_KEEP_COMMENTS, NULL); + connection_uuid = nm_connection_get_uuid (NM_CONNECTION (connection)); + timestamp = g_key_file_get_uint64 (timestamps_file, "timestamps", connection_uuid, &err); + + /* Update connection's timestamp */ + if (!err) + priv->timestamp = timestamp; + else { + nm_log_dbg (LOGD_SETTINGS, "failed to read connection timestamp for '%s': (%d) %s", + connection_uuid, err->code, err->message); + g_clear_error (&err); + } + g_key_file_free (timestamps_file); +} + /**************************************************************/ static void diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h index 190923c9fb..f68a942511 100644 --- a/src/settings/nm-settings-connection.h +++ b/src/settings/nm-settings-connection.h @@ -116,6 +116,12 @@ void nm_settings_connection_recheck_visibility (NMSettingsConnection *self); void nm_settings_connection_signal_remove (NMSettingsConnection *self); +guint64 nm_settings_connection_get_timestamp (NMSettingsConnection *connection); + +void nm_settings_connection_update_timestamp (NMSettingsConnection *connection, guint64 timestamp); + +void nm_settings_connection_read_and_fill_timestamp (NMSettingsConnection *connection); + G_END_DECLS #endif /* NM_SETTINGS_CONNECTION_H */ diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index 8359c2fe51..7aadadf0ff 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -19,7 +19,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * (C) Copyright 2007 - 2010 Red Hat, Inc. + * (C) Copyright 2007 - 2011 Red Hat, Inc. * (C) Copyright 2008 Novell, Inc. */ @@ -227,6 +227,7 @@ connection_sort (gconstpointer pa, gconstpointer pb) NMSettingConnection *con_a; NMConnection *b = NM_CONNECTION (pb); NMSettingConnection *con_b; + guint64 ts_a, ts_b; con_a = (NMSettingConnection *) nm_connection_get_setting (a, NM_TYPE_SETTING_CONNECTION); g_assert (con_a); @@ -239,9 +240,11 @@ connection_sort (gconstpointer pa, gconstpointer pb) return 1; } - if (nm_setting_connection_get_timestamp (con_a) > nm_setting_connection_get_timestamp (con_b)) + ts_a = nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa)); + ts_b = nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb)); + if (ts_a > ts_b) return -1; - else if (nm_setting_connection_get_timestamp (con_a) == nm_setting_connection_get_timestamp (con_b)) + else if (ts_a == ts_b) return 0; return 1; } @@ -692,6 +695,9 @@ claim_connection (NMSettings *self, return; } + /* Read timestamp from look-aside file and put it into the connection's data */ + nm_settings_connection_read_and_fill_timestamp (connection); + /* Ensure it's initial visibility is up-to-date */ nm_settings_connection_recheck_visibility (connection);