diff --git a/ChangeLog b/ChangeLog index 062e1f695..dffe1a1e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2008-08-26 Dan Williams + + * system-settings/plugins/keyfile/nm-keyfile-connection.c + - (update): Update filename of the connection if the connection id + was changed + + * system-settings/plugins/keyfile/plugin.c + - (dir_changed): first pass at handling connection renames correctly + + * system-settings/plugins/keyfile/writer.c + system-settings/plugins/keyfile/writer.h + - (write_connection): replace '/' with '*' when writing out the filename + from the connection id + 2008-08-26 Dan Williams Add connection UUIDs, since connection names can be changed, and since diff --git a/system-settings/plugins/keyfile/nm-keyfile-connection.c b/system-settings/plugins/keyfile/nm-keyfile-connection.c index 1999050eb..2e8f5a879 100644 --- a/system-settings/plugins/keyfile/nm-keyfile-connection.c +++ b/system-settings/plugins/keyfile/nm-keyfile-connection.c @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ #include #include @@ -31,8 +31,8 @@ nm_keyfile_connection_new (const char *filename) g_return_val_if_fail (filename != NULL, NULL); return (NMKeyfileConnection *) g_object_new (NM_TYPE_KEYFILE_CONNECTION, - NM_KEYFILE_CONNECTION_FILENAME, filename, - NULL); + NM_KEYFILE_CONNECTION_FILENAME, filename, + NULL); } const char * @@ -51,14 +51,26 @@ get_settings (NMExportedConnection *exported) static gboolean update (NMExportedConnection *exported, - GHashTable *new_settings, - GError **error) + GHashTable *new_settings, + GError **error) { + NMKeyfileConnectionPrivate *priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (exported); gboolean success; success = NM_EXPORTED_CONNECTION_CLASS (nm_keyfile_connection_parent_class)->update (exported, new_settings, error); - if (success) - success = write_connection (nm_exported_connection_get_connection (exported), error); + if (success) { + NMConnection *connection; + char *filename = NULL; + + connection = nm_exported_connection_get_connection (exported); + success = write_connection (connection, &filename, error); + if (success && filename && strcmp (priv->filename, filename)) { + /* Update the filename if it changed */ + g_free (priv->filename); + priv->filename = filename; + } else + g_free (filename); + } return success; } @@ -116,7 +128,7 @@ constructor (GType type, GError *error = NULL; s_con->uuid = nm_utils_uuid_generate (); - if (!write_connection (wrapped, &error)) { + if (!write_connection (wrapped, NULL, &error)) { g_warning ("Couldn't update connection %s with a UUID: (%d) %s", s_con->id, error ? error->code : 0, error ? error->message : "unknown"); diff --git a/system-settings/plugins/keyfile/plugin.c b/system-settings/plugins/keyfile/plugin.c index eaf780e83..ff9cadb4f 100644 --- a/system-settings/plugins/keyfile/plugin.c +++ b/system-settings/plugins/keyfile/plugin.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -42,25 +43,10 @@ typedef struct { gboolean disposed; } SCPluginKeyfilePrivate; -static NMKeyfileConnection * -read_one_connection (NMSystemConfigInterface *config, const char *filename) -{ - SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (config); - NMKeyfileConnection *connection; - - connection = nm_keyfile_connection_new (filename); - if (connection) { - g_hash_table_insert (priv->hash, - (gpointer) nm_keyfile_connection_get_filename (connection), - g_object_ref (connection)); - } - - return connection; -} - static void read_connections (NMSystemConfigInterface *config) { + SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (config); GDir *dir; GError *err = NULL; @@ -69,10 +55,16 @@ read_connections (NMSystemConfigInterface *config) const char *item; while ((item = g_dir_read_name (dir))) { + NMKeyfileConnection *connection; char *full_path; full_path = g_build_filename (KEYFILE_DIR, item, NULL); - read_one_connection (config, full_path); + connection = nm_keyfile_connection_new (full_path); + if (connection) { + g_hash_table_insert (priv->hash, + (gpointer) nm_keyfile_connection_get_filename (connection), + connection); + } g_free (full_path); } @@ -83,6 +75,45 @@ read_connections (NMSystemConfigInterface *config) } } +typedef struct { + const char *uuid; + NMKeyfileConnection *found; +} FindByUUIDInfo; + +static void +find_by_uuid (gpointer key, gpointer data, gpointer user_data) +{ + NMKeyfileConnection *keyfile = NM_KEYFILE_CONNECTION (key); + FindByUUIDInfo *info = user_data; + NMConnection *connection; + NMSettingConnection *s_con; + + if (info->found) + return; + + connection = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (keyfile)); + s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION); + if (s_con && s_con->uuid) { + if (!strcmp (info->uuid, s_con->uuid)) + info->found = keyfile; + } +} + +static gboolean +update_connection_settings (NMExportedConnection *orig, + NMExportedConnection *new, + GError **error) +{ + GHashTable *settings; + gboolean success; + + settings = nm_connection_to_hash (nm_exported_connection_get_connection (new)); + success = nm_exported_connection_update (orig, settings, error); + g_hash_table_destroy (settings); + + return success; +} + /* Monitoring */ static void @@ -103,8 +134,11 @@ dir_changed (GFileMonitor *monitor, switch (event_type) { case G_FILE_MONITOR_EVENT_DELETED: if (connection) { + /* Removing from the hash table should drop the last reference */ + g_object_ref (connection); g_hash_table_remove (priv->hash, name); nm_exported_connection_signal_removed (NM_EXPORTED_CONNECTION (connection)); + g_object_unref (connection); } break; case G_FILE_MONITOR_EVENT_CREATED: @@ -115,18 +149,75 @@ dir_changed (GFileMonitor *monitor, tmp = (NMExportedConnection *) nm_keyfile_connection_new (name); if (tmp) { - GHashTable *settings; + GError *error = NULL; - settings = nm_connection_to_hash (nm_exported_connection_get_connection (tmp)); - nm_exported_connection_update (NM_EXPORTED_CONNECTION (connection), settings, NULL); - g_hash_table_destroy (settings); + if (!update_connection_settings (NM_EXPORTED_CONNECTION (connection), tmp, &error)) { + g_warning ("%s: couldn't update connection settings: (%d) %s", + __func__, error ? error->code : 0, + error ? error->message : "unknown"); + g_error_free (error); + } g_object_unref (tmp); } } else { /* New */ - connection = read_one_connection (config, name); - if (connection) - g_signal_emit_by_name (config, "connection-added", connection); + connection = nm_keyfile_connection_new (name); + if (connection) { + NMConnection *tmp; + NMSettingConnection *s_con; + NMKeyfileConnection *found = NULL; + + /* Connection renames will show up as different files but with + * the same UUID. Try to find the original connection. + */ + tmp = nm_exported_connection_get_connection (NM_EXPORTED_CONNECTION (connection)); + s_con = (NMSettingConnection *) nm_connection_get_setting (tmp, NM_TYPE_SETTING_CONNECTION); + if (s_con && s_con->uuid) { + FindByUUIDInfo info = { .found = NULL, .uuid = s_con->uuid }; + + g_hash_table_foreach (priv->hash, find_by_uuid, &info); + found = info.found; + } + + /* A connection rename is treated just like an update except + * there's a bit more housekeeping with the hash table. + */ + if (found) { + const char *old_filename = nm_keyfile_connection_get_filename (connection); + GError *error = NULL; + + /* Removing from the hash table should drop the last reference, + * but of course we want to keep the connection around. + */ + g_object_ref (found); + g_hash_table_remove (priv->hash, old_filename); + + /* Updating settings should update the NMKeyfileConnection's + * filename property too. + */ + if (!update_connection_settings (NM_EXPORTED_CONNECTION (found), + NM_EXPORTED_CONNECTION (connection), + &error)) { + g_warning ("%s: couldn't update connection settings: (%d) %s", + __func__, error ? error->code : 0, + error ? error->message : "unknown"); + g_error_free (error); + } + + /* Re-insert the connection back into the hash with the new filename */ + g_hash_table_insert (priv->hash, + (gpointer) nm_keyfile_connection_get_filename (found), + found); + + /* Get rid of the temporary connection */ + g_object_unref (connection); + } else { + g_hash_table_insert (priv->hash, + (gpointer) nm_keyfile_connection_get_filename (connection), + connection); + g_signal_emit_by_name (config, "connection-added", connection); + } + } } break; default: @@ -186,7 +277,7 @@ add_connection (NMSystemConfigInterface *config, NMConnection *connection, GError **error) { - return write_connection (connection, error); + return write_connection (connection, NULL, error); } /* GObject */ diff --git a/system-settings/plugins/keyfile/writer.c b/system-settings/plugins/keyfile/writer.c index ee229f622..235fe81f3 100644 --- a/system-settings/plugins/keyfile/writer.c +++ b/system-settings/plugins/keyfile/writer.c @@ -263,15 +263,38 @@ write_setting_value (NMSetting *setting, } } +char * +writer_id_to_filename (const char *id) +{ + char *filename, *f; + const char *i = id; + + f = filename = g_malloc0 (strlen (id) + 1); + + /* Convert '/' to '*' */ + while (*i) { + if (*i == '/') + *f++ = '*'; + else + *f++ = *i; + i++; + } + + return filename; +} + gboolean -write_connection (NMConnection *connection, GError **error) +write_connection (NMConnection *connection, char **out_path, GError **error) { NMSettingConnection *s_con; GKeyFile *key_file; char *data; gsize len; gboolean success = FALSE; - GError *err = NULL; + char *filename, *path; + + if (out_path) + g_return_val_if_fail (*out_path == NULL, FALSE); s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION)); if (!s_con) @@ -279,30 +302,28 @@ write_connection (NMConnection *connection, GError **error) key_file = g_key_file_new (); nm_connection_for_each_setting_value (connection, write_setting_value, key_file); - data = g_key_file_to_data (key_file, &len, &err); + data = g_key_file_to_data (key_file, &len, error); + if (!data) + goto out; - if (!err) { - char *filename; + filename = writer_id_to_filename (s_con->id); + path = g_build_filename (KEYFILE_DIR, filename, NULL); + g_free (filename); - filename = g_build_filename (KEYFILE_DIR, s_con->id, NULL); - g_file_set_contents (filename, data, len, &err); - chmod (filename, S_IRUSR | S_IWUSR); - if (chown (filename, 0, 0) < 0) { - g_warning ("Error chowning '%s': %d", filename, errno); - unlink (filename); - } else - success = TRUE; - - g_free (filename); - } - - if (err) { - g_warning ("Error while saving connection: %s", err->message); - g_error_free (err); + g_file_set_contents (path, data, len, error); + chmod (path, S_IRUSR | S_IWUSR); + if (chown (path, 0, 0) < 0) { + g_warning ("Error chowning '%s': %d", path, errno); + unlink (path); + } else { + if (out_path) + *out_path = g_strdup (path); + success = TRUE; } + g_free (path); +out: g_free (data); g_key_file_free (key_file); - return success; } diff --git a/system-settings/plugins/keyfile/writer.h b/system-settings/plugins/keyfile/writer.h index 38a20d4e4..c19e8ae94 100644 --- a/system-settings/plugins/keyfile/writer.h +++ b/system-settings/plugins/keyfile/writer.h @@ -6,6 +6,8 @@ #include #include -gboolean write_connection (NMConnection *connection, GError **error); +gboolean write_connection (NMConnection *connection, char **out_path, GError **error); + +char *writer_id_to_filename (const char *id); #endif /* _KEYFILE_PLUGIN_WRITER_H */