Commit 83d86779 authored by Jiří Klimeš's avatar Jiří Klimeš

core: timestamp handling - don't touch /etc when updating timestamps (bgo #637825)

NM updates timestamp for active connections every 5 min. We don't
want to touch files in /etc due to this. This commit solves that
by not updating timestamp in the connection's property. Rather it
updates the timestamp internally. All timestamps are also kept track
of in /var/lib/NetworkManager/timestamps file.
When settings are requested via D-Bus GetSettings(), the proper
timestamp is put in the connection setting before returning.
parent 580ee0fc
......@@ -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;
......
......@@ -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
......
......@@ -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 */
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment