From c963f9eec8d72d6e17099d456330cd988294c0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 9 Feb 2019 00:00:00 +0000 Subject: bin: Add an option to ignore changes to locked keys during load If load command attempts to change one of non-writable keys, the whole operation fails with an error and no changes are made. Add an `-f` option to the load command that skips non-writable keys and proceeds with remaining changes. Closes issue #1. --- bin/dconf.c | 43 +++++++++++++++++++++++++++++++++++-------- docs/dconf-tool.xml | 9 ++++++++- tests/test-dconf.py | 25 +++++++++++++++++++++++-- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/bin/dconf.c b/bin/dconf.c index 8e3f060..c91f82a 100644 --- a/bin/dconf.c +++ b/bin/dconf.c @@ -578,14 +578,29 @@ keyfile_foreach (GKeyFile *kf, return TRUE; } +typedef struct { + DConfClient *client; + DConfChangeset *changeset; + gboolean force; +} LoadContext; + static void changeset_set (const gchar *path, GVariant *value, gpointer user_data) { - DConfChangeset *changeset = user_data; + LoadContext *ctx = user_data; + + /* When force option is used, ignore changes made to non-writeable keys to + * avoid rejecting the whole changeset. + */ + if (ctx->force && !dconf_client_is_writable (ctx->client, path)) + { + g_fprintf (stderr, "warning: ignored non-writable key '%s'\n", path); + return; + } - dconf_changeset_set (changeset, path, value); + dconf_changeset_set (ctx->changeset, path, value); } static gboolean @@ -593,27 +608,39 @@ dconf_load (const gchar **argv, GError **error) { const gchar *dir; + gint index = 0; + gboolean force = FALSE; g_autoptr(GError) local_error = NULL; g_autoptr(GKeyFile) kf = NULL; g_autoptr(DConfChangeset) changeset = NULL; g_autoptr (DConfClient) client = NULL; - dir = argv[0]; + if (argv[index] != NULL && strcmp (argv[index], "-f") == 0) + { + force = TRUE; + index += 1; + } + + dir = argv[index]; if (!dconf_is_dir (dir, &local_error)) return option_error_propagate (error, &local_error); - if (argv[1] != NULL) + index += 1; + + if (argv[index] != NULL) return option_error_set (error, "too many arguments"); kf = keyfile_from_stdin (error); if (kf == NULL) return FALSE; + client = dconf_client_new (); changeset = dconf_changeset_new (); - if (!keyfile_foreach (kf, dir, changeset_set, changeset, error)) + + LoadContext ctx = { client, changeset, force }; + if (!keyfile_foreach (kf, dir, changeset_set, &ctx, error)) return FALSE; - client = dconf_client_new (); return dconf_client_change_sync (client, changeset, NULL, NULL, error); } @@ -1024,8 +1051,8 @@ static const Command commands[] = { }, { "load", dconf_load, - "Populate a subpath from stdin", - " DIR " + "Populate a subpath from stdin. -f ignore locked keys.", + " [-f] DIR " }, { "blame", dconf_blame, diff --git a/docs/dconf-tool.xml b/docs/dconf-tool.xml index 7093da6..e5f8c24 100644 --- a/docs/dconf-tool.xml +++ b/docs/dconf-tool.xml @@ -77,6 +77,7 @@ dconf load + -f DIR @@ -180,7 +181,13 @@ - Populate a subpath from stdin. The expected format is the same as produced by . + + + Populate a subpath from stdin. The expected format is the same as produced by . + Attempting to change non-writable keys cancels the load command. + To ignore changes to non-writable keys instead, use . + + diff --git a/tests/test-dconf.py b/tests/test-dconf.py index 1e143e2..cf9bb14 100755 --- a/tests/test-dconf.py +++ b/tests/test-dconf.py @@ -62,8 +62,8 @@ def dconf(*args, **kwargs): return subprocess.run(argv, **kwargs) -def dconf_read(key): - return dconf('read', key).stdout.rstrip('\n') +def dconf_read(key, **kwargs): + return dconf('read', key, **kwargs).stdout.rstrip('\n') def dconf_write(key, value): @@ -684,6 +684,7 @@ class DBusTest(unittest.TestCase): - Update configures locks based on files found in "locks" subdirectory. - Locks can be listed with list-locks command. - Locks are enforced during write. + - Load can ignore changes to locked keys using -f option. """ db = os.path.join(self.temporary_dir.name, 'db') @@ -755,6 +756,26 @@ class DBusTest(unittest.TestCase): env=env, stderr=subprocess.PIPE) self.assertRegex(cm.exception.stderr, 'non-writable keys') + keyfile = dedent('''\ + [system/proxy/http] + enabled=false + [org/gnome/desktop] + background='Winter.png' + ''') + + # Load fails to apply changes if some key is locked ... + with self.assertRaises(subprocess.CalledProcessError) as cm: + dconf('load', '/', input=keyfile, env=env, stderr=subprocess.PIPE) + self.assertRegex(cm.exception.stderr, 'non-writable keys') + self.assertEqual('true', dconf_read('/system/proxy/http/enabled', env=env)) + self.assertEqual("'ColdWarm.jpg'", dconf_read('/org/gnome/desktop/background', env=env)) + + # ..., unless invoked with -f option, then it changes unlocked keys. + stderr = dconf('load', '-f', '/', input=keyfile, env=env, stderr=subprocess.PIPE).stderr + self.assertRegex(stderr, 'ignored non-writable key') + self.assertEqual('true', dconf_read('/system/proxy/http/enabled', env=env)) + self.assertEqual("'Winter.png'", dconf_read('/org/gnome/desktop/background', env=env)) + def test_dconf_blame(self): """Blame returns recorded information about write operations. -- cgit v1.2.1