| From 6edc7e0cfc672fe4c2331ac8131ea1aa82881b63 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Wed, 19 Dec 2018 19:58:16 -0500 |
| Subject: [PATCH 1/9] settings: Make the keyfile backend parameterless |
| |
| Make it possible to instantiate a keyfile settings backend |
| without specifying parameters, by turning the arguments to |
| the new() function into construct-only properties. If no |
| filename is specified, default to |
| $XDG_CONFIG_HOME/glib-2.0/settings/keyfile |
| |
| gio/gkeyfilesettingsbackend.c | 252 ++++++++++++++++++++++++++------- |
| gio/gsettingsbackendinternal.h | 2 + |
| 2 files changed, 199 insertions(+), 55 deletions(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index a37978e83..e8e9f44a5 100644 |
| |
| |
| @@ -21,6 +21,9 @@ |
| |
| #include "config.h" |
| |
| +#include <glib.h> |
| +#include <glibintl.h> |
| + |
| #include <stdio.h> |
| #include <string.h> |
| |
| @@ -28,7 +31,8 @@ |
| #include "gfileinfo.h" |
| #include "gfilemonitor.h" |
| #include "gsimplepermission.h" |
| -#include "gsettingsbackend.h" |
| +#include "gsettingsbackendinternal.h" |
| +#include "giomodule.h" |
| |
| |
| #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) |
| @@ -41,6 +45,12 @@ |
| |
| typedef GSettingsBackendClass GKeyfileSettingsBackendClass; |
| |
| +enum { |
| + PROP_FILENAME = 1, |
| + PROP_ROOT_PATH, |
| + PROP_ROOT_GROUP |
| +}; |
| + |
| typedef struct |
| { |
| GSettingsBackend parent_instance; |
| @@ -61,7 +71,6 @@ typedef struct |
| GFileMonitor *dir_monitor; |
| } GKeyfileSettingsBackend; |
| |
| -static GType g_keyfile_settings_backend_get_type (void); |
| G_DEFINE_TYPE (GKeyfileSettingsBackend, |
| g_keyfile_settings_backend, |
| G_TYPE_SETTINGS_BACKEND) |
| @@ -287,7 +296,8 @@ g_keyfile_settings_backend_check_one (gpointer key, |
| { |
| WriteManyData *data = user_data; |
| |
| - return data->failed = !path_is_valid (data->kfsb, key); |
| + return data->failed = g_hash_table_contains (data->kfsb->system_locks, key) || |
| + !path_is_valid (data->kfsb, key); |
| } |
| |
| static gboolean |
| @@ -522,25 +532,6 @@ g_keyfile_settings_backend_init (GKeyfileSettingsBackend *kfsb) |
| { |
| } |
| |
| -static void |
| -g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) |
| -{ |
| - GObjectClass *object_class = G_OBJECT_CLASS (class); |
| - |
| - object_class->finalize = g_keyfile_settings_backend_finalize; |
| - |
| - class->read = g_keyfile_settings_backend_read; |
| - class->write = g_keyfile_settings_backend_write; |
| - class->write_tree = g_keyfile_settings_backend_write_tree; |
| - class->reset = g_keyfile_settings_backend_reset; |
| - class->get_writable = g_keyfile_settings_backend_get_writable; |
| - class->get_permission = g_keyfile_settings_backend_get_permission; |
| - /* No need to implement subscribed/unsubscribe: the only point would be to |
| - * stop monitoring the file when there's no GSettings anymore, which is no |
| - * big win. |
| - */ |
| -} |
| - |
| static void |
| file_changed (GFileMonitor *monitor, |
| GFile *file, |
| @@ -567,6 +558,185 @@ dir_changed (GFileMonitor *monitor, |
| g_keyfile_settings_backend_keyfile_writable (kfsb); |
| } |
| |
| +static void |
| +g_keyfile_settings_backend_constructed (GObject *object) |
| +{ |
| + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); |
| + |
| + if (kfsb->file == NULL) |
| + { |
| + char *filename = g_build_filename (g_get_user_config_dir (), |
| + "glib-2.0", "settings", "keyfile", |
| + NULL); |
| + kfsb->file = g_file_new_for_path (filename); |
| + g_free (filename); |
| + } |
| + |
| + if (kfsb->prefix == NULL) |
| + { |
| + kfsb->prefix = g_strdup ("/"); |
| + kfsb->prefix_len = 1; |
| + } |
| + |
| + kfsb->keyfile = g_key_file_new (); |
| + kfsb->permission = g_simple_permission_new (TRUE); |
| + |
| + kfsb->dir = g_file_get_parent (kfsb->file); |
| + g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); |
| + |
| + kfsb->file_monitor = g_file_monitor (kfsb->file, G_FILE_MONITOR_NONE, NULL, NULL); |
| + kfsb->dir_monitor = g_file_monitor (kfsb->dir, G_FILE_MONITOR_NONE, NULL, NULL); |
| + |
| + compute_checksum (kfsb->digest, NULL, 0); |
| + |
| + g_signal_connect (kfsb->file_monitor, "changed", |
| + G_CALLBACK (file_changed), kfsb); |
| + g_signal_connect (kfsb->dir_monitor, "changed", |
| + G_CALLBACK (dir_changed), kfsb); |
| + |
| + g_keyfile_settings_backend_keyfile_writable (kfsb); |
| + g_keyfile_settings_backend_keyfile_reload (kfsb); |
| +} |
| + |
| +static void |
| +g_keyfile_settings_backend_set_property (GObject *object, |
| + guint prop_id, |
| + const GValue *value, |
| + GParamSpec *pspec) |
| +{ |
| + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); |
| + |
| + switch (prop_id) |
| + { |
| + case PROP_FILENAME: |
| + /* Construct only. */ |
| + g_assert (kfsb->file == NULL); |
| + kfsb->file = g_file_new_for_path (g_value_get_string (value)); |
| + break; |
| + |
| + case PROP_ROOT_PATH: |
| + /* Construct only. */ |
| + g_assert (kfsb->prefix == NULL); |
| + kfsb->prefix = g_value_dup_string (value); |
| + if (kfsb->prefix) |
| + kfsb->prefix_len = strlen (kfsb->prefix); |
| + break; |
| + |
| + case PROP_ROOT_GROUP: |
| + /* Construct only. */ |
| + g_assert (kfsb->root_group == NULL); |
| + kfsb->root_group = g_value_dup_string (value); |
| + if (kfsb->root_group) |
| + kfsb->root_group_len = strlen (kfsb->root_group); |
| + break; |
| + |
| + default: |
| + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| + break; |
| + } |
| +} |
| + |
| +static void |
| +g_keyfile_settings_backend_get_property (GObject *object, |
| + guint prop_id, |
| + GValue *value, |
| + GParamSpec *pspec) |
| +{ |
| + GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); |
| + |
| + switch (prop_id) |
| + { |
| + case PROP_FILENAME: |
| + g_value_set_string (value, g_file_peek_path (kfsb->file)); |
| + break; |
| + |
| + case PROP_ROOT_PATH: |
| + g_value_set_string (value, kfsb->prefix); |
| + break; |
| + |
| + case PROP_ROOT_GROUP: |
| + g_value_set_string (value, kfsb->root_group); |
| + break; |
| + |
| + default: |
| + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| + break; |
| + } |
| +} |
| + |
| +static void |
| +g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) |
| +{ |
| + GObjectClass *object_class = G_OBJECT_CLASS (class); |
| + |
| + object_class->finalize = g_keyfile_settings_backend_finalize; |
| + object_class->constructed = g_keyfile_settings_backend_constructed; |
| + object_class->get_property = g_keyfile_settings_backend_get_property; |
| + object_class->set_property = g_keyfile_settings_backend_set_property; |
| + |
| + class->read = g_keyfile_settings_backend_read; |
| + class->write = g_keyfile_settings_backend_write; |
| + class->write_tree = g_keyfile_settings_backend_write_tree; |
| + class->reset = g_keyfile_settings_backend_reset; |
| + class->get_writable = g_keyfile_settings_backend_get_writable; |
| + class->get_permission = g_keyfile_settings_backend_get_permission; |
| + /* No need to implement subscribed/unsubscribe: the only point would be to |
| + * stop monitoring the file when there's no GSettings anymore, which is no |
| + * big win. |
| + */ |
| + |
| + /** |
| + * GKeyfileSettingsBackend:filename: |
| + * |
| + * The location where the settings are stored on disk. |
| + * |
| + * Defaults to `$XDG_CONFIG_HOME/glib-2.0/settings/keyfile`. |
| + */ |
| + g_object_class_install_property (object_class, |
| + PROP_FILENAME, |
| + g_param_spec_string ("filename", |
| + P_("Filename"), |
| + P_("The filename"), |
| + NULL, |
| + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
| + G_PARAM_STATIC_STRINGS)); |
| + |
| + /** |
| + * GKeyfileSettingsBackend:root-path: |
| + * |
| + * All settings read to or written from the backend must fall under the |
| + * path given in @root_path (which must start and end with a slash and |
| + * not contain two consecutive slashes). @root_path may be "/". |
| + * |
| + * Defaults to "/". |
| + */ |
| + g_object_class_install_property (object_class, |
| + PROP_ROOT_PATH, |
| + g_param_spec_string ("root-path", |
| + P_("Root path"), |
| + P_("The root path"), |
| + NULL, |
| + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
| + G_PARAM_STATIC_STRINGS)); |
| + |
| + /** |
| + * GKeyfileSettingsBackend:root-group: |
| + * |
| + * If @root_group is non-%NULL then it specifies the name of the keyfile |
| + * group used for keys that are written directly below the root path. |
| + * |
| + * Defaults to NULL. |
| + */ |
| + g_object_class_install_property (object_class, |
| + PROP_ROOT_GROUP, |
| + g_param_spec_string ("root-group", |
| + P_("Root group"), |
| + P_("The root group"), |
| + NULL, |
| + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
| + G_PARAM_STATIC_STRINGS)); |
| +} |
| + |
| /** |
| * g_keyfile_settings_backend_new: |
| * @filename: the filename of the keyfile |
| @@ -626,43 +796,15 @@ g_keyfile_settings_backend_new (const gchar *filename, |
| const gchar *root_path, |
| const gchar *root_group) |
| { |
| - GKeyfileSettingsBackend *kfsb; |
| - |
| g_return_val_if_fail (filename != NULL, NULL); |
| g_return_val_if_fail (root_path != NULL, NULL); |
| g_return_val_if_fail (g_str_has_prefix (root_path, "/"), NULL); |
| g_return_val_if_fail (g_str_has_suffix (root_path, "/"), NULL); |
| g_return_val_if_fail (strstr (root_path, "//") == NULL, NULL); |
| |
| - kfsb = g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, NULL); |
| - kfsb->keyfile = g_key_file_new (); |
| - kfsb->permission = g_simple_permission_new (TRUE); |
| - |
| - kfsb->file = g_file_new_for_path (filename); |
| - kfsb->dir = g_file_get_parent (kfsb->file); |
| - g_file_make_directory_with_parents (kfsb->dir, NULL, NULL); |
| - |
| - kfsb->file_monitor = g_file_monitor (kfsb->file, 0, NULL, NULL); |
| - kfsb->dir_monitor = g_file_monitor (kfsb->dir, 0, NULL, NULL); |
| - |
| - kfsb->prefix_len = strlen (root_path); |
| - kfsb->prefix = g_strdup (root_path); |
| - |
| - if (root_group) |
| - { |
| - kfsb->root_group_len = strlen (root_group); |
| - kfsb->root_group = g_strdup (root_group); |
| - } |
| - |
| - compute_checksum (kfsb->digest, NULL, 0); |
| - |
| - g_signal_connect (kfsb->file_monitor, "changed", |
| - G_CALLBACK (file_changed), kfsb); |
| - g_signal_connect (kfsb->dir_monitor, "changed", |
| - G_CALLBACK (dir_changed), kfsb); |
| - |
| - g_keyfile_settings_backend_keyfile_writable (kfsb); |
| - g_keyfile_settings_backend_keyfile_reload (kfsb); |
| - |
| - return G_SETTINGS_BACKEND (kfsb); |
| + return G_SETTINGS_BACKEND (g_object_new (G_TYPE_KEYFILE_SETTINGS_BACKEND, |
| + "filename", filename, |
| + "root-path", root_path, |
| + "root-group", root_group, |
| + NULL)); |
| } |
| diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h |
| index 2a76a80bc..9e1d51dba 100644 |
| |
| |
| @@ -87,6 +87,8 @@ GType g_null_settings_backend_get_type (void); |
| |
| GType g_memory_settings_backend_get_type (void); |
| |
| +GType g_keyfile_settings_backend_get_type (void); |
| + |
| #ifdef HAVE_COCOA |
| GType g_nextstep_settings_backend_get_type (void); |
| #endif |
| -- |
| 2.28.0 |
| |
| |
| From cd448d51f610f1e6a701ac8a1146d3b2048427a0 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Wed, 19 Dec 2018 20:03:29 -0500 |
| Subject: [PATCH 2/9] settings: Register the keyfile backend as extension |
| |
| This was not done previously because the backend |
| could not be instantiated without parameters. |
| |
| gio/gkeyfilesettingsbackend.c | 11 +++++++---- |
| 1 file changed, 7 insertions(+), 4 deletions(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index e8e9f44a5..0e9789cde 100644 |
| |
| |
| @@ -32,7 +32,7 @@ |
| #include "gfilemonitor.h" |
| #include "gsimplepermission.h" |
| #include "gsettingsbackendinternal.h" |
| -#include "giomodule.h" |
| +#include "giomodule-priv.h" |
| |
| |
| #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) |
| @@ -71,9 +71,12 @@ typedef struct |
| GFileMonitor *dir_monitor; |
| } GKeyfileSettingsBackend; |
| |
| -G_DEFINE_TYPE (GKeyfileSettingsBackend, |
| - g_keyfile_settings_backend, |
| - G_TYPE_SETTINGS_BACKEND) |
| +G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, |
| + g_keyfile_settings_backend, |
| + G_TYPE_SETTINGS_BACKEND, |
| + _g_io_modules_ensure_extension_points_registered (); |
| + g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, |
| + g_define_type_id, "keyfile", 10)) |
| |
| static void |
| compute_checksum (guint8 *digest, |
| -- |
| 2.28.0 |
| |
| |
| From 8cbdb7dd496f6c2361eb46ca4c0848d7210fe27a Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Mon, 5 Nov 2018 16:07:55 -0500 |
| Subject: [PATCH 3/9] settings: Add support for defaults to keyfile backend |
| |
| Stacked databases and locks are dconf features that allow |
| management software like Fleet Commander to set system-wide |
| defaults and overrides centrally for applications. |
| |
| This patch adds minimal support for the same to the keyfile |
| backend. We look for a keyfile named 'defaults' and a |
| lock-list named 'locks'. |
| |
| Suitable files can be produced from a dconf database with |
| dconf dump and dconf list-locks, respectively. |
| |
| The default location for these files is /etc/glib-2.0/settings/. |
| For test purposes, this can be overwritten with the |
| GSETTINGS_DEFAULTS_DIR environment variable. |
| |
| Writes always go to the per-user keyfile. |
| |
| gio/gkeyfilesettingsbackend.c | 138 ++++++++++++++++++++++++++++++++-- |
| 1 file changed, 132 insertions(+), 6 deletions(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index 0e9789cde..0a4e81511 100644 |
| |
| |
| @@ -29,6 +29,7 @@ |
| |
| #include "gfile.h" |
| #include "gfileinfo.h" |
| +#include "gfileenumerator.h" |
| #include "gfilemonitor.h" |
| #include "gsimplepermission.h" |
| #include "gsettingsbackendinternal.h" |
| @@ -45,11 +46,12 @@ |
| |
| typedef GSettingsBackendClass GKeyfileSettingsBackendClass; |
| |
| -enum { |
| +typedef enum { |
| PROP_FILENAME = 1, |
| PROP_ROOT_PATH, |
| - PROP_ROOT_GROUP |
| -}; |
| + PROP_ROOT_GROUP, |
| + PROP_DEFAULTS_DIR |
| +} GKeyfileSettingsBackendProperty; |
| |
| typedef struct |
| { |
| @@ -58,6 +60,9 @@ typedef struct |
| GKeyFile *keyfile; |
| GPermission *permission; |
| gboolean writable; |
| + char *defaults_dir; |
| + GKeyFile *system_keyfile; |
| + GHashTable *system_locks; /* Used as a set, owning the strings it contains */ |
| |
| gchar *prefix; |
| gint prefix_len; |
| @@ -196,10 +201,19 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, |
| if (convert_path (kfsb, key, &group, &name)) |
| { |
| gchar *str; |
| + gchar *sysstr; |
| |
| g_assert (*name); |
| |
| + sysstr = g_key_file_get_value (kfsb->system_keyfile, group, name, NULL); |
| str = g_key_file_get_value (kfsb->keyfile, group, name, NULL); |
| + if (sysstr && |
| + (g_hash_table_contains (kfsb->system_locks, key) || |
| + str == NULL)) |
| + { |
| + g_free (str); |
| + str = g_steal_pointer (&sysstr); |
| + } |
| |
| if (str) |
| { |
| @@ -207,6 +221,8 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, |
| g_free (str); |
| } |
| |
| + g_free (sysstr); |
| + |
| g_free (group); |
| g_free (name); |
| } |
| @@ -221,6 +237,9 @@ set_to_keyfile (GKeyfileSettingsBackend *kfsb, |
| { |
| gchar *group, *name; |
| |
| + if (g_hash_table_contains (kfsb->system_locks, key)) |
| + return FALSE; |
| + |
| if (convert_path (kfsb, key, &group, &name)) |
| { |
| if (value) |
| @@ -368,7 +387,9 @@ g_keyfile_settings_backend_get_writable (GSettingsBackend *backend, |
| { |
| GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (backend); |
| |
| - return kfsb->writable && path_is_valid (kfsb, name); |
| + return kfsb->writable && |
| + !g_hash_table_contains (kfsb->system_locks, name) && |
| + path_is_valid (kfsb, name); |
| } |
| |
| static GPermission * |
| @@ -514,6 +535,9 @@ g_keyfile_settings_backend_finalize (GObject *object) |
| |
| g_key_file_free (kfsb->keyfile); |
| g_object_unref (kfsb->permission); |
| + g_key_file_unref (kfsb->system_keyfile); |
| + g_hash_table_unref (kfsb->system_locks); |
| + g_free (kfsb->defaults_dir); |
| |
| g_file_monitor_cancel (kfsb->file_monitor); |
| g_object_unref (kfsb->file_monitor); |
| @@ -561,6 +585,75 @@ dir_changed (GFileMonitor *monitor, |
| g_keyfile_settings_backend_keyfile_writable (kfsb); |
| } |
| |
| +static void |
| +load_system_settings (GKeyfileSettingsBackend *kfsb) |
| +{ |
| + GError *error = NULL; |
| + const char *dir = "/etc/glib-2.0/settings"; |
| + char *path; |
| + char *contents; |
| + |
| + kfsb->system_keyfile = g_key_file_new (); |
| + kfsb->system_locks = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
| + |
| + if (kfsb->defaults_dir) |
| + dir = kfsb->defaults_dir; |
| + |
| + path = g_build_filename (dir, "defaults", NULL); |
| + |
| + /* The defaults are in the same keyfile format that we use for the settings. |
| + * It can be produced from a dconf database using: dconf dump |
| + */ |
| + if (!g_key_file_load_from_file (kfsb->system_keyfile, path, G_KEY_FILE_NONE, &error)) |
| + { |
| + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) |
| + g_warning ("Failed to read %s: %s", path, error->message); |
| + g_clear_error (&error); |
| + } |
| + else |
| + g_debug ("Loading default settings from %s", path); |
| + |
| + g_free (path); |
| + |
| + path = g_build_filename (dir, "locks", NULL); |
| + |
| + /* The locks file is a text file containing a list paths to lock, one per line. |
| + * It can be produced from a dconf database using: dconf list-locks |
| + */ |
| + if (!g_file_get_contents (path, &contents, NULL, &error)) |
| + { |
| + if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) |
| + g_warning ("Failed to read %s: %s", path, error->message); |
| + g_clear_error (&error); |
| + } |
| + else |
| + { |
| + char **lines; |
| + gsize i; |
| + |
| + g_debug ("Loading locks from %s", path); |
| + |
| + lines = g_strsplit (contents, "\n", 0); |
| + for (i = 0; lines[i]; i++) |
| + { |
| + char *line = lines[i]; |
| + if (line[0] == '#' || line[0] == '\0') |
| + { |
| + g_free (line); |
| + continue; |
| + } |
| + |
| + g_debug ("Locking key %s", line); |
| + g_hash_table_add (kfsb->system_locks, g_steal_pointer (&line)); |
| + } |
| + |
| + g_free (lines); |
| + } |
| + g_free (contents); |
| + |
| + g_free (path); |
| +} |
| + |
| static void |
| g_keyfile_settings_backend_constructed (GObject *object) |
| { |
| @@ -599,6 +692,8 @@ g_keyfile_settings_backend_constructed (GObject *object) |
| |
| g_keyfile_settings_backend_keyfile_writable (kfsb); |
| g_keyfile_settings_backend_keyfile_reload (kfsb); |
| + |
| + load_system_settings (kfsb); |
| } |
| |
| static void |
| @@ -609,7 +704,7 @@ g_keyfile_settings_backend_set_property (GObject *object, |
| { |
| GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); |
| |
| - switch (prop_id) |
| + switch ((GKeyfileSettingsBackendProperty)prop_id) |
| { |
| case PROP_FILENAME: |
| /* Construct only. */ |
| @@ -633,6 +728,12 @@ g_keyfile_settings_backend_set_property (GObject *object, |
| kfsb->root_group_len = strlen (kfsb->root_group); |
| break; |
| |
| + case PROP_DEFAULTS_DIR: |
| + /* Construct only. */ |
| + g_assert (kfsb->defaults_dir == NULL); |
| + kfsb->defaults_dir = g_value_dup_string (value); |
| + break; |
| + |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| @@ -647,7 +748,7 @@ g_keyfile_settings_backend_get_property (GObject *object, |
| { |
| GKeyfileSettingsBackend *kfsb = G_KEYFILE_SETTINGS_BACKEND (object); |
| |
| - switch (prop_id) |
| + switch ((GKeyfileSettingsBackendProperty)prop_id) |
| { |
| case PROP_FILENAME: |
| g_value_set_string (value, g_file_peek_path (kfsb->file)); |
| @@ -661,6 +762,10 @@ g_keyfile_settings_backend_get_property (GObject *object, |
| g_value_set_string (value, kfsb->root_group); |
| break; |
| |
| + case PROP_DEFAULTS_DIR: |
| + g_value_set_string (value, kfsb->defaults_dir); |
| + break; |
| + |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| @@ -738,6 +843,22 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) |
| NULL, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
| G_PARAM_STATIC_STRINGS)); |
| + |
| + /** |
| + * GKeyfileSettingsBackend:default-dir: |
| + * |
| + * The directory where the system defaults and locks are located. |
| + * |
| + * Defaults to `/etc/glib-2.0/settings`. |
| + */ |
| + g_object_class_install_property (object_class, |
| + PROP_DEFAULTS_DIR, |
| + g_param_spec_string ("defaults-dir", |
| + P_("Default dir"), |
| + P_("Defaults dir"), |
| + NULL, |
| + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | |
| + G_PARAM_STATIC_STRINGS)); |
| } |
| |
| /** |
| @@ -792,6 +913,11 @@ g_keyfile_settings_backend_class_init (GKeyfileSettingsBackendClass *class) |
| * characters in your path names or '=' in your key names you may be in |
| * trouble. |
| * |
| + * The backend reads default values from a keyfile called `defaults` in |
| + * the directory specified by the #GKeyfileSettingsBackend:defaults-dir property, |
| + * and a list of locked keys from a text file with the name `locks` in |
| + * the same location. |
| + * |
| * Returns: (transfer full): a keyfile-backed #GSettingsBackend |
| **/ |
| GSettingsBackend * |
| -- |
| 2.28.0 |
| |
| |
| From 93dceb39fe2de23aeffe40ff02c824393c749270 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Fri, 2 Nov 2018 23:00:49 -0400 |
| Subject: [PATCH 4/9] settings: Prefer the keyfile backend when sandboxed |
| |
| When we are in a sandboxed situation, bump the priority |
| of the keyfile settings backend above the dconf one, |
| so we use a keyfile inside the sandbox instead of requiring |
| holes in the sandbox for dconf. |
| |
| gio/gkeyfilesettingsbackend.c | 9 ++++++++- |
| 1 file changed, 8 insertions(+), 1 deletion(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index 0a4e81511..398cb053a 100644 |
| |
| |
| @@ -34,6 +34,7 @@ |
| #include "gsimplepermission.h" |
| #include "gsettingsbackendinternal.h" |
| #include "giomodule-priv.h" |
| +#include "gportalsupport.h" |
| |
| |
| #define G_TYPE_KEYFILE_SETTINGS_BACKEND (g_keyfile_settings_backend_get_type ()) |
| @@ -76,12 +77,18 @@ typedef struct |
| GFileMonitor *dir_monitor; |
| } GKeyfileSettingsBackend; |
| |
| +#ifdef G_OS_WIN32 |
| +#define EXTENSION_PRIORITY 10 |
| +#else |
| +#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10) |
| +#endif |
| + |
| G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, |
| g_keyfile_settings_backend, |
| G_TYPE_SETTINGS_BACKEND, |
| _g_io_modules_ensure_extension_points_registered (); |
| g_io_extension_point_implement (G_SETTINGS_BACKEND_EXTENSION_POINT_NAME, |
| - g_define_type_id, "keyfile", 10)) |
| + g_define_type_id, "keyfile", EXTENSION_PRIORITY)) |
| |
| static void |
| compute_checksum (guint8 *digest, |
| -- |
| 2.28.0 |
| |
| |
| From 5d0d49750564aa7fd9e7d0d58c08a08847570921 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Mon, 21 Jan 2019 22:55:45 -0500 |
| Subject: [PATCH 5/9] keyfile settings: Accept unquoted strings |
| |
| It is hard for users to remember that strings have to be explicitly |
| quoted in the keyfile. Be lenient and accept strings that lack those |
| quotes. |
| |
| gio/gkeyfilesettingsbackend.c | 19 +++++++++++++++++++ |
| gio/tests/gsettings.c | 17 +++++++++++++++++ |
| 2 files changed, 36 insertions(+) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index 398cb053a..d5796b706 100644 |
| |
| |
| @@ -225,6 +225,25 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, |
| if (str) |
| { |
| return_value = g_variant_parse (type, str, NULL, NULL, NULL); |
| + if (return_value == NULL && |
| + g_variant_type_equal (type, G_VARIANT_TYPE_STRING) && |
| + str[0] != '\"') |
| + { |
| + GString *s = g_string_sized_new (strlen (str) + 2); |
| + char *p = str; |
| + |
| + g_string_append_c (s, '\"'); |
| + while (*p) |
| + { |
| + if (*p == '\"') |
| + g_string_append_c (s, '\\'); |
| + g_string_append_c (s, *p); |
| + p++; |
| + } |
| + g_string_append_c (s, '\"'); |
| + return_value = g_variant_parse (type, s->str, NULL, NULL, NULL); |
| + g_string_free (s, TRUE); |
| + } |
| g_free (str); |
| } |
| |
| diff --git a/gio/tests/gsettings.c b/gio/tests/gsettings.c |
| index 2be4122fe..6eb49f124 100644 |
| |
| |
| @@ -1716,6 +1716,23 @@ test_keyfile (void) |
| g_assert_cmpstr (str, ==, "howdy"); |
| g_free (str); |
| |
| + /* Now check setting a string without quotes */ |
| + called = FALSE; |
| + g_signal_connect (settings, "changed::greeting", G_CALLBACK (key_changed_cb), &called); |
| + |
| + g_key_file_set_string (keyfile, "tests", "greeting", "he\"l🤗uń"); |
| + g_free (data); |
| + data = g_key_file_to_data (keyfile, &len, NULL); |
| + g_file_set_contents ("keyfile/gsettings.store", data, len, &error); |
| + g_assert_no_error (error); |
| + while (!called) |
| + g_main_context_iteration (NULL, FALSE); |
| + g_signal_handlers_disconnect_by_func (settings, key_changed_cb, &called); |
| + |
| + str = g_settings_get_string (settings, "greeting"); |
| + g_assert_cmpstr (str, ==, "he\"l🤗uń"); |
| + g_free (str); |
| + |
| g_settings_set (settings, "farewell", "s", "cheerio"); |
| |
| called = FALSE; |
| -- |
| 2.28.0 |
| |
| |
| From 3765e73f9a2dbbc6863bcffdee85ffb18530c89b Mon Sep 17 00:00:00 2001 |
| From: Philip Withnall <withnall@endlessm.com> |
| Date: Wed, 23 Jan 2019 15:14:58 +0000 |
| Subject: [PATCH 6/9] gkeyfilesettingsbackend: Add a code comment to clarify |
| things |
| |
| Signed-off-by: Philip Withnall <withnall@endlessm.com> |
| |
| gio/gkeyfilesettingsbackend.c | 4 ++++ |
| 1 file changed, 4 insertions(+) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index d5796b706..5ea632305 100644 |
| |
| |
| @@ -225,6 +225,10 @@ get_from_keyfile (GKeyfileSettingsBackend *kfsb, |
| if (str) |
| { |
| return_value = g_variant_parse (type, str, NULL, NULL, NULL); |
| + |
| + /* As a special case, support values of type %G_VARIANT_TYPE_STRING |
| + * not being quoted, since users keep forgetting to do it and then |
| + * getting confused. */ |
| if (return_value == NULL && |
| g_variant_type_equal (type, G_VARIANT_TYPE_STRING) && |
| str[0] != '\"') |
| -- |
| 2.28.0 |
| |
| |
| From 1df628861afe027e8dce5e27f4249059bad6bc60 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Wed, 10 Jul 2019 11:14:03 -0400 |
| Subject: [PATCH 7/9] key file: Handle filename being NULL |
| |
| This happens when we are default-constructed |
| without explicit arguments. |
| |
| Closes: https://gitlab.gnome.org/GNOME/glib/issues/1825 |
| |
| gio/gkeyfilesettingsbackend.c | 3 ++- |
| 1 file changed, 2 insertions(+), 1 deletion(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index 5ea632305..a874af287 100644 |
| |
| |
| @@ -739,7 +739,8 @@ g_keyfile_settings_backend_set_property (GObject *object, |
| case PROP_FILENAME: |
| /* Construct only. */ |
| g_assert (kfsb->file == NULL); |
| - kfsb->file = g_file_new_for_path (g_value_get_string (value)); |
| + if (g_value_get_string (value)) |
| + kfsb->file = g_file_new_for_path (g_value_get_string (value)); |
| break; |
| |
| case PROP_ROOT_PATH: |
| -- |
| 2.28.0 |
| |
| |
| From 9c5d3a6081e5ff419db96d9651bddbfcdb8b1bc6 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Fri, 12 Jul 2019 11:30:30 -0400 |
| Subject: [PATCH] portal: Add a getter for dconf access |
| |
| Add method to find whether the sandbox provides |
| access to dconf. This will be used to tweak |
| the priorities for the keyfile settings backend. |
| |
| gio/gportalsupport.c | 18 ++++++++++++++++++ |
| gio/gportalsupport.h | 1 + |
| 2 files changed, 19 insertions(+) |
| |
| diff --git a/gio/gportalsupport.c b/gio/gportalsupport.c |
| index 2f1e82517..b0a94b360 100644 |
| |
| |
| @@ -23,6 +23,7 @@ |
| static gboolean flatpak_info_read; |
| static gboolean use_portal; |
| static gboolean network_available; |
| +static gboolean dconf_access; |
| |
| static void |
| read_flatpak_info (void) |
| @@ -40,11 +41,13 @@ read_flatpak_info (void) |
| |
| use_portal = TRUE; |
| network_available = FALSE; |
| + dconf_access = FALSE; |
| |
| keyfile = g_key_file_new (); |
| if (g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) |
| { |
| char **shared = NULL; |
| + char *dconf_policy = NULL; |
| |
| shared = g_key_file_get_string_list (keyfile, "Context", "shared", NULL, NULL); |
| if (shared) |
| @@ -52,6 +55,14 @@ read_flatpak_info (void) |
| network_available = g_strv_contains ((const char * const *)shared, "network"); |
| g_strfreev (shared); |
| } |
| + |
| + dconf_policy = g_key_file_get_string (keyfile, "Session Bus Policy", "ca.desrt.dconf", NULL); |
| + if (dconf_policy) |
| + { |
| + if (strcmp (dconf_policy, "talk") == 0) |
| + dconf_access = TRUE; |
| + g_free (dconf_policy); |
| + } |
| } |
| |
| g_key_file_unref (keyfile); |
| @@ -64,6 +75,7 @@ read_flatpak_info (void) |
| if (var && var[0] == '1') |
| use_portal = TRUE; |
| network_available = TRUE; |
| + dconf_access = TRUE; |
| } |
| } |
| |
| @@ -81,3 +93,9 @@ glib_network_available_in_sandbox (void) |
| return network_available; |
| } |
| |
| +gboolean |
| +glib_has_dconf_access_in_sandbox (void) |
| +{ |
| + read_flatpak_info (); |
| + return dconf_access; |
| +} |
| diff --git a/gio/gportalsupport.h b/gio/gportalsupport.h |
| index a331f45d3..746f1fd6b 100644 |
| |
| |
| @@ -24,6 +24,7 @@ G_BEGIN_DECLS |
| |
| gboolean glib_should_use_portal (void); |
| gboolean glib_network_available_in_sandbox (void); |
| +gboolean glib_has_dconf_access_in_sandbox (void); |
| |
| G_END_DECLS |
| |
| -- |
| GitLab |
| |
| |
| From e6461f208931e27b52d82f7d3b5a7ce4fc26c9d7 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Fri, 12 Jul 2019 11:31:37 -0400 |
| Subject: [PATCH 8/9] settings: Tweak priorities for keyfile backend |
| |
| We want to use the keyfile backend in sandboxes, |
| but we want to avoid people losing their existing |
| settings that are stored in dconf. Flatpak does |
| a migration from dconf to keyfile, but only if |
| the app explictly requests it. |
| |
| From an app perspective, there are two steps to |
| the dconf->keyfile migration: |
| 1. Request that flatpak do the migration, by adding |
| the migrate-path key to the metadata |
| 2. Stop adding the 'dconf hole' to the sandbox |
| |
| To keep us from switching to the keyfile backend |
| prematurely, look at whether the app has stopped |
| requesting a 'dconf hole' in the sandbox. |
| |
| gio/gkeyfilesettingsbackend.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| diff --git a/gio/gkeyfilesettingsbackend.c b/gio/gkeyfilesettingsbackend.c |
| index a874af287..f5358818e 100644 |
| |
| |
| @@ -80,7 +80,7 @@ typedef struct |
| #ifdef G_OS_WIN32 |
| #define EXTENSION_PRIORITY 10 |
| #else |
| -#define EXTENSION_PRIORITY (glib_should_use_portal () ? 110 : 10) |
| +#define EXTENSION_PRIORITY (glib_should_use_portal () && !glib_has_dconf_access_in_sandbox () ? 110 : 10) |
| #endif |
| |
| G_DEFINE_TYPE_WITH_CODE (GKeyfileSettingsBackend, |
| -- |
| 2.28.0 |
| |
| |
| From 5f8d787815dc220fa2e288e03cd0cb9497727753 Mon Sep 17 00:00:00 2001 |
| From: Matthias Clasen <mclasen@redhat.com> |
| Date: Mon, 8 Jul 2019 10:31:51 -0400 |
| Subject: [PATCH] Ensure that the keyfile settings backend exists |
| |
| We need to bring the type into existence. |
| |
| Closes: https://gitlab.gnome.org/GNOME/glib/issues/1822 |
| |
| gio/giomodule.c | 1 + |
| 1 file changed, 1 insertion(+) |
| |
| diff --git a/gio/giomodule.c b/gio/giomodule.c |
| index 9bb28985a..1007abdbf 100644 |
| |
| |
| @@ -1211,6 +1211,7 @@ _g_io_modules_ensure_loaded (void) |
| /* Initialize types from built-in "modules" */ |
| g_type_ensure (g_null_settings_backend_get_type ()); |
| g_type_ensure (g_memory_settings_backend_get_type ()); |
| + g_type_ensure (g_keyfile_settings_backend_get_type ()); |
| #if defined(HAVE_INOTIFY_INIT1) |
| g_type_ensure (g_inotify_file_monitor_get_type ()); |
| #endif |
| -- |
| GitLab |