[dconf: 2/3] bin: Add an option to ignore changes to locked keys during load



commit c963f9eec8d72d6e17099d456330cd988294c0af
Author: Tomasz Miąsko <tomasz miasko gmail com>
Date:   Sat Feb 9 00:00:00 2019 +0000

    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 @@
     <cmdsynopsis>
       <command>dconf</command>
       <arg choice="plain">load</arg>
+      <arg choice="opt">-f</arg>
       <arg choice="plain"><replaceable>DIR</replaceable></arg>
     </cmdsynopsis>
     <cmdsynopsis>
@@ -180,7 +181,13 @@
       <varlistentry>
         <term><option>load</option></term>
 
-        <listitem><para>Populate a subpath from stdin. The expected format is the same as produced by 
<option>dump</option>.</para></listitem>
+        <listitem>
+          <para>
+            Populate a subpath from stdin. The expected format is the same as produced by 
<option>dump</option>.
+            Attempting to change non-writable keys cancels the load command.
+            To ignore changes to non-writable keys instead, use <option>-f</option>.
+          </para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
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.
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]