GConfig Plans and Design
note: these are my design notes of december 2006, when I started working on gconfig design. gconfig is not exactly like this anymore, and only the tenets are the important part. EmmanueleBassi
Design tenets
- this goes in between glib/gobject and gtk+
- no more paths
- object persistence
- you can have a section of the database, never the entire database
- reuse the same remote object for the same object path
- you need a schema
you need a schema
- you don't need a schema file
- leave the daemon out of the reads
- leave the client library out of the writes
- have multiple namespaces
- have multiple backends
- leave the schemas out of the database
- validate everything client-side
- think of a gconf migration path that doesn't break API (ABI is fine)
- lockdown policy
Assumptions
configuration object path (like D-Bus paths): namespace:/path/to/object
- preference: attribute of the object at the configuration object path
Low level API
- GConfigBackend: interface for database backends
- multiple backends
- loadable modules
- each backend has the base path it's been assigned by the user via configuration file
- each backend has a set, a get, a list preferences and a list paths method, all accepting paths
- GConfigSource: remote object for database objects
- remote object
- loads the backend needed for the namespace+path
- registers a filter function on the bus
- holds a counter for the number of factories (proxies) it has
- if the counter reaches 0, unrefs the backend
- GConfigFactory: local object for database objects
- bound to a namespace+path
- loads the schema object
- validates data when setting (fails if not valid)
- validates data when getting (fails if not valid, returns the schema default)
- changes notification: GSignal "changed" for every change, "changed::preference" for the changes of "preference"
- multiple factories for the same configuration path map to a single source
- GConfigSchema: shallow object for a schema (from file, buffer or in-code)
- can be created from scratch and assigned to a factory
- can be created from a file or a buffer and assigned to a factory
API usage examples
get two preferences from the applications:/org/gnome/Test configuration object:
GConfigFactory *factory; gchar *foo; gint bar; factory = g_config_get_applications_factory ("/org/gnome/Test", "test.schemas"); g_config_factory_get (factory, "foo", &foo, "bar", &bar, NULL); g_object_unref (factory);
set a preference of the applications:/org/gnome/Test configuration object, with direct error reporting:
if (!g_config_factory_set_preference (factory, "bar", new_bar)) { GError *error; g_config_factory_get_error (factory, &error); g_warning ("Error: %s", error->message); g_error_free (error); }
- set a preference with indirect error reporting:
static void on_error_cb (GConfigFactory *factory, const GError *error, gpointer user_data) { ... } ... g_signal_connect (factory, "error", G_CALLBACK (on_error_cb), NULL); g_config_factory_set (factory, "bar", new_bar, NULL);
- change notification:
static void on_foo_changed_cb (GConfigFactory *factory, const gchar *preference, /* "foo" */ GConfigSchema *schema, gpointer user_data) { gchar *foo; g_config_factory_get (factory, preference, &foo, NULL); } ... g_signal_connect (factory, "changed::foo", G_CALLBACK (on_foo_changed_cb), NULL);
Configuration
gconfig resource file (similar to gtkrc, parsed by GScanner)
- define namespaces, define backends
namespace "applications" { readable = TRUE writable = TRUE backend "file" { base_path "$HOME/.gconfig/applications" } } namespace "system" { readable = TRUE writable = TRUE backend "bdb" { base_path "$HOME/.gconfig/system" } }
- all the defaults are stored inside schemas files
- administrators must override a schema, not a value inside the database
schema "org.gnome.Foo" { # override the file containing the schema path "/etc/gconfig/local/our-foo" } schema "org.gnome.Bar" { # override a preference - a schema for org.gnome.Bar/baz *must* exist preference "baz" { type = "int" value = 42 } }
Schemas
- FIXME
Backends
- current backends: file (keyfile-based), dummy (in-memory table)
Namespaces
- defined by default:
applications - name says it all
system - system stuff, like gtk theme, icon theme, UI prefs, etc.
session - things that must persist for a session, but can be discarded afterwards (tests, preview settings, etc.)
- a user/application can add namespaces inside the gconfig resource file and bind them to a backend
Lockdown policy
- defined per-schema
- API to ask the daemon to lock an object down (granularity: readable, writable, visible)
writable will make any further call for setting a preference for that object fail
readable will make any further call for getting a preference for that object fail
visible will make any further call for getting a factory for that object fail
- how do we decide if a lock down request should be honoured?