summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2010-07-15 21:18:55 -0400
committerRyan Lortie <desrt@desrt.ca>2010-07-15 21:30:53 -0400
commite7fd6d849feaea45d76cda1eb074c9ea59b85637 (patch)
treeee014c5ab816989bf19c730f16373359453bb2e0 /service
parentc37c49a0c25505e0136158b56aba3703db0fa828 (diff)
downloaddconf-e7fd6d849feaea45d76cda1eb074c9ea59b85637.tar.gz
Many improvements
- support the notion of 'profiles' This is how we will configure layering of databases in dconf. It's not wired-up yet (except to choose the name of the user database). - support multiple writers in the service Using the (very freshly API-broken) GDBus subtree support. Introspection returns the names of existing databases, but any object path can be used to create a new database. - support the start of the 'shm' file Used to prevent the reader from reopening the gvdb every time. dconf reads now involve zero system calls in the usual case. The server is queried on startup for the location of the shm files. By default, this is in ~/.cache/dconf/ for now. This won't work properly on NFS, but it's a start.
Diffstat (limited to 'service')
-rw-r--r--service/Makefile.am4
-rw-r--r--service/dconf-rebuilder.c8
-rw-r--r--service/dconf-rebuilder.h4
-rw-r--r--service/dconf-writer.c166
-rw-r--r--service/dconf-writer.h18
-rw-r--r--service/service.c230
6 files changed, 361 insertions, 69 deletions
diff --git a/service/Makefile.am b/service/Makefile.am
index dbd509e..ceb9e7d 100644
--- a/service/Makefile.am
+++ b/service/Makefile.am
@@ -1,4 +1,4 @@
-AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb
+AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb -Wall -Wmissing-prototypes -Wwrite-strings
libexec_PROGRAMS = dconf-service
@@ -12,6 +12,8 @@ dconf_service_SOURCES = \
dconf-rebuilder.h \
dconf-rebuilder.h \
dconf-rebuilder.c \
+ dconf-writer.h \
+ dconf-writer.c \
service.c
DISTCLEANFILES = ca.desrt.dconf.service
diff --git a/service/dconf-rebuilder.c b/service/dconf-rebuilder.c
index 9584ac5..5cc502f 100644
--- a/service/dconf-rebuilder.c
+++ b/service/dconf-rebuilder.c
@@ -32,8 +32,8 @@ typedef struct
gint prefix_len;
GHashTable *table;
- const gchar **keys;
- GVariant **values;
+ const gchar *const*keys;
+ GVariant *const*values;
gint n_items;
gint index;
@@ -181,8 +181,8 @@ dconf_rebuilder_walk_close (gpointer user_data)
gboolean
dconf_rebuilder_rebuild (const gchar *filename,
const gchar *prefix,
- const gchar **keys,
- GVariant **values,
+ const gchar *const*keys,
+ GVariant *const*values,
int n_items,
GError **error)
{
diff --git a/service/dconf-rebuilder.h b/service/dconf-rebuilder.h
index 4c675db..57f6974 100644
--- a/service/dconf-rebuilder.h
+++ b/service/dconf-rebuilder.h
@@ -23,7 +23,7 @@
gboolean dconf_rebuilder_rebuild (const gchar *filename,
const gchar *prefix,
- const gchar **keys,
- GVariant **values,
+ const gchar *const*keys,
+ GVariant *const*values,
gint n_items,
GError **error);
diff --git a/service/dconf-writer.c b/service/dconf-writer.c
new file mode 100644
index 0000000..a165ed5
--- /dev/null
+++ b/service/dconf-writer.c
@@ -0,0 +1,166 @@
+#include "dconf-writer.h"
+
+#include "dconf-rebuilder.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+
+static const gchar *dconf_writer_shm_dir;
+static const gchar *dconf_writer_db_dir;
+
+struct OPAQUE_TYPE__DConfWriter
+{
+ gchar *path;
+ gchar *shm;
+};
+
+const gchar *
+dconf_writer_get_shm_dir (void)
+{
+ return dconf_writer_shm_dir;
+}
+
+/* Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
+ */
+static gboolean
+is_valid_dbus_path_element (const gchar *string)
+{
+ gint i;
+
+ for (i = 0; string[i]; i++)
+ if (!g_ascii_isalnum (string[i]) && string[i] != '_')
+ return FALSE;
+
+ return TRUE;
+}
+
+gchar **
+dconf_writer_list_existing (void)
+{
+ GPtrArray *array;
+ GDir *dir;
+
+ array = g_ptr_array_new ();
+
+ if ((dir = g_dir_open ("/home/desrt/.config/dconf", 0, NULL)))
+ {
+ const gchar *name;
+
+ while ((name = g_dir_read_name (dir)))
+ if (is_valid_dbus_path_element (name))
+ g_ptr_array_add (array, g_strdup (name));
+ }
+
+ g_ptr_array_add (array, NULL);
+
+ return (gchar **) g_ptr_array_free (array, FALSE);
+}
+
+static void
+dconf_writer_touch_shm (DConfWriter *writer)
+{
+ gchar one = 1;
+ gint fd;
+
+ fd = open (writer->shm, O_WRONLY);
+ write (fd, &one, sizeof one);
+ close (fd);
+
+ unlink (writer->shm);
+}
+
+gboolean
+dconf_writer_write (DConfWriter *writer,
+ const gchar *name,
+ GVariant *value,
+ GError **error)
+{
+ if (!dconf_rebuilder_rebuild (writer->path, "", &name, &value, 1, error))
+ return FALSE;
+
+ dconf_writer_touch_shm (writer);
+
+ return TRUE;
+}
+
+gboolean
+dconf_writer_write_many (DConfWriter *writer,
+ const gchar *prefix,
+ const gchar * const *keys,
+ GVariant * const *values,
+ gsize n_items,
+ GError **error)
+{
+ if (!dconf_rebuilder_rebuild (writer->path, prefix, keys,
+ values, n_items, error))
+ return FALSE;
+
+ dconf_writer_touch_shm (writer);
+
+ return TRUE;
+}
+
+DConfWriter *
+dconf_writer_new (const gchar *name)
+{
+ DConfWriter *writer;
+
+ writer = g_slice_new (DConfWriter);
+ writer->path = g_build_filename (dconf_writer_db_dir, name, NULL);
+ writer->shm = g_build_filename (dconf_writer_shm_dir, name, NULL);
+
+ return writer;
+}
+
+void
+dconf_writer_init (void)
+{
+ const gchar *config_dir = g_get_user_config_dir ();
+ const gchar *cache_dir = g_get_user_cache_dir ();
+
+ dconf_writer_db_dir = g_build_filename (config_dir, "dconf", NULL);
+
+ if (g_mkdir_with_parents (dconf_writer_db_dir, 0700))
+ {
+ /* XXX remove this after a while... */
+ if (errno == ENOTDIR)
+ {
+ gchar *tmp, *final;
+
+ g_message ("Attempting to migrate ~/.config/dconf "
+ "to ~/.config/dconf/user");
+
+ tmp = g_build_filename (config_dir, "dconf-user.db", NULL);
+
+ if (rename (dconf_writer_db_dir, tmp))
+ g_error ("Can not rename '%s' to '%s': %s",
+ dconf_writer_db_dir, tmp, g_strerror (errno));
+
+ if (g_mkdir_with_parents (dconf_writer_db_dir, 0700))
+ g_error ("Can not create directory '%s': %s",
+ dconf_writer_db_dir, g_strerror (errno));
+
+ final = g_build_filename (dconf_writer_db_dir, "user", NULL);
+
+ if (rename (tmp, final))
+ g_error ("Can not rename '%s' to '%s': %s",
+ tmp, final, g_strerror (errno));
+
+ g_message ("Successful.");
+
+ g_free (final);
+ g_free (tmp);
+ }
+ else
+ g_error ("Can not create directory '%s': %s",
+ dconf_writer_db_dir, g_strerror (errno));
+ }
+
+ dconf_writer_shm_dir = g_build_filename (cache_dir, "dconf", NULL);
+
+ if (g_mkdir_with_parents (dconf_writer_shm_dir, 0700))
+ g_error ("Can not create directory '%s': %s",
+ dconf_writer_shm_dir, g_strerror (errno));
+}
diff --git a/service/dconf-writer.h b/service/dconf-writer.h
new file mode 100644
index 0000000..55611b1
--- /dev/null
+++ b/service/dconf-writer.h
@@ -0,0 +1,18 @@
+#include <glib.h>
+
+typedef struct OPAQUE_TYPE__DConfWriter DConfWriter;
+
+const gchar * dconf_writer_get_shm_dir (void);
+gchar ** dconf_writer_list_existing (void);
+void dconf_writer_init (void);
+DConfWriter * dconf_writer_new (const gchar *name);
+gboolean dconf_writer_write (DConfWriter *writer,
+ const gchar *name,
+ GVariant *value,
+ GError **error);
+gboolean dconf_writer_write_many (DConfWriter *writer,
+ const gchar *prefix,
+ const gchar * const *keys,
+ GVariant * const *values,
+ gsize n_items,
+ GError **error);
diff --git a/service/service.c b/service/service.c
index 2954093..a9d9806 100644
--- a/service/service.c
+++ b/service/service.c
@@ -23,45 +23,9 @@
#include <string.h>
#include <stdio.h>
-#include "dconf-rebuilder.h"
+#include "dconf-writer.h"
-static const GDBusArgInfo name_arg = { -1, "name", "s" };
-static const GDBusArgInfo names_arg = { -1, "names", "as" };
-static const GDBusArgInfo serial_arg = { -1, "serial", "t" };
-static const GDBusArgInfo locked_arg = { -1, "locked", "b" };
-static const GDBusArgInfo value_arg = { -1, "value", "av" };
-static const GDBusArgInfo values_arg = { -1, "values", "a(sav)" };
-
-static const GDBusArgInfo *write_inargs[] = { &name_arg, &value_arg, NULL };
-static const GDBusArgInfo *write_outargs[] = { &serial_arg, NULL };
-
-static const GDBusArgInfo *merge_inargs[] = { &name_arg, &values_arg, NULL };
-static const GDBusArgInfo *merge_outargs[] = { &serial_arg, NULL };
-
-static const GDBusArgInfo *gsd_inargs[] = { NULL };
-static const GDBusArgInfo *gsd_outargs[] = { &name_arg, NULL };
-
-static const GDBusMethodInfo write_method = {
- -1, "Write", (gpointer) write_inargs, (gpointer) write_outargs };
-static const GDBusMethodInfo merge_method = {
- -1, "Merge", (gpointer) merge_inargs, (gpointer) merge_outargs };
-static const GDBusMethodInfo gsd_method = {
- -1, "GetSessionDir", (gpointer) gsd_inargs, (gpointer) gsd_outargs };
-
-static const GDBusMethodInfo *writer_methods[] = {
- &write_method, &merge_method, &gsd_method, NULL
-};
-
-static const GDBusInterfaceInfo writer_interface = {
- -1, "ca.desrt.dconf.Writer", (gpointer) writer_methods
-};
-
-typedef struct
-{
- GMainLoop *loop;
- guint64 serial;
- gchar *path;
-} DConfWriter;
+static guint64 dconf_service_serial;
static void
emit_notify_signal (GDBusConnection *connection,
@@ -123,7 +87,7 @@ emit_notify_signal (GDBusConnection *connection,
g_free (path);
}
-static GVariant *
+static void
unwrap_maybe (GVariant **ptr)
{
GVariant *array, *child;
@@ -186,15 +150,14 @@ method_call (GDBusConnection *connection,
return;
}
- if (!dconf_rebuilder_rebuild (writer->path, "", &key,
- &value, 1, &error))
+ if (!dconf_writer_write (writer, key, value, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
return;
}
- serial = writer->serial++;
+ serial = dconf_service_serial++;
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(t)", serial));
none = g_variant_new_array (G_VARIANT_TYPE_STRING, NULL, 0);
@@ -247,15 +210,14 @@ method_call (GDBusConnection *connection,
g_variant_iter_free (iter);
keys[i] = NULL;
- if (!dconf_rebuilder_rebuild (writer->path, prefix, keys,
- values, i, &error))
+ if (!dconf_writer_write_many (writer, prefix, keys, values, i, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
return;
}
- serial = writer->serial++;
+ serial = dconf_service_serial++;
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(t)", serial));
@@ -272,17 +234,168 @@ method_call (GDBusConnection *connection,
g_assert_not_reached ();
}
+static GVariant *
+writer_info_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ return g_variant_new_string (dconf_writer_get_shm_dir ());
+}
+
+static const GDBusInterfaceVTable *
+subtree_dispatch (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *node,
+ gpointer *out_user_data,
+ gpointer user_data)
+{
+ if (strcmp (interface_name, "ca.desrt.dconf.Writer") == 0)
+ {
+ static const GDBusInterfaceVTable vtable = {
+ method_call, NULL, NULL
+ };
+ static GHashTable *writer_table;
+ DConfWriter *writer;
+
+ if (node == NULL)
+ return NULL;
+
+ if G_UNLIKELY (writer_table == NULL)
+ writer_table = g_hash_table_new (g_str_hash, g_str_equal);
+
+ writer = g_hash_table_lookup (writer_table, node);
+
+ if G_UNLIKELY (writer == NULL)
+ {
+ writer = dconf_writer_new (node);
+ g_hash_table_insert (writer_table, g_strdup (node), writer);
+ }
+
+ *out_user_data = writer;
+
+ return &vtable;
+ }
+
+ else if (strcmp (interface_name, "ca.desrt.dconf.WriterInfo") == 0)
+ {
+ static const GDBusInterfaceVTable vtable = {
+ NULL, writer_info_get_property, NULL
+ };
+
+ *out_user_data = NULL;
+ return &vtable;
+ }
+
+ else
+ return NULL;
+}
+
+static gchar **
+subtree_enumerate (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ gpointer user_data)
+{
+ return dconf_writer_list_existing ();
+}
+
+static GDBusInterfaceInfo **
+subtree_introspect (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *node,
+ gpointer user_data)
+{
+ static const GDBusArgInfo name_arg = { -1, (gchar *) "name", (gchar *) "s" };
+ static const GDBusArgInfo path_arg = { -1, (gchar *) "path", (gchar *) "s" };
+ static const GDBusArgInfo names_arg = { -1, (gchar *) "names", (gchar *) "as" };
+ static const GDBusArgInfo serial_arg = { -1, (gchar *) "serial", (gchar *) "t" };
+ static const GDBusArgInfo value_arg = { -1, (gchar *) "value", (gchar *) "av" };
+ static const GDBusArgInfo values_arg = { -1, (gchar *) "values", (gchar *) "a(sav)" };
+
+ static const GDBusArgInfo *write_in[] = { &name_arg, &value_arg, NULL };
+ static const GDBusArgInfo *write_out[] = { &serial_arg, NULL };
+ static const GDBusArgInfo *many_in[] = { &path_arg, &values_arg, NULL };
+ static const GDBusArgInfo *many_out[] = { &serial_arg, NULL };
+ static const GDBusArgInfo *notify_args[] = { &path_arg, &names_arg, NULL };
+
+ static const GDBusMethodInfo write_method = {
+ -1, (gchar *) "Write",
+ (GDBusArgInfo **) write_in,
+ (GDBusArgInfo **) write_out
+ };
+ static const GDBusMethodInfo writemany_method = {
+ -1, (gchar *) "WriteMany",
+ (GDBusArgInfo **) many_in,
+ (GDBusArgInfo **) many_out
+ };
+ static const GDBusSignalInfo notify_signal = {
+ -1, (gchar *) "Notify",
+ (GDBusArgInfo **) notify_args
+ };
+ static const GDBusPropertyInfo shmdir_property = {
+ -1, (gchar *) "ShmDirectory", (gchar *) "s", G_DBUS_PROPERTY_INFO_FLAGS_READABLE
+ };
+ static const GDBusMethodInfo *writer_methods[] = {
+ &write_method, &writemany_method, NULL
+ };
+ static const GDBusSignalInfo *writer_signals[] = {
+ &notify_signal, NULL
+ };
+ static const GDBusPropertyInfo *writer_info_properties[] = {
+ &shmdir_property, NULL
+ };
+ static const GDBusInterfaceInfo writer_interface = {
+ -1, (gchar *) "ca.desrt.dconf.Writer",
+ (GDBusMethodInfo **) writer_methods,
+ (GDBusSignalInfo **) writer_signals,
+ (GDBusPropertyInfo **) NULL
+ };
+ static const GDBusInterfaceInfo writer_info_interface = {
+ -1, (gchar *) "ca.desrt.dconf.WriterInfo",
+ (GDBusMethodInfo **) NULL,
+ (GDBusSignalInfo **) NULL,
+ (GDBusPropertyInfo **) writer_info_properties
+ };
+
+ /* The root node supports only the info iface */
+ if (node == NULL)
+ {
+ const GDBusInterfaceInfo *interfaces[] = {
+ &writer_info_interface, NULL
+ };
+
+ return g_memdup (interfaces, sizeof interfaces);
+ }
+ else
+ {
+ const GDBusInterfaceInfo *interfaces[] = {
+ &writer_info_interface, &writer_interface, NULL
+ };
+
+ return g_memdup (interfaces, sizeof interfaces);
+ }
+}
+
static void
bus_acquired (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- static const GDBusInterfaceVTable interface_vtable = { method_call };
- DConfWriter *writer = user_data;
-
- g_dbus_connection_register_object (connection, "/",
- &writer_interface, &interface_vtable,
- writer, NULL, NULL);
+ static GDBusSubtreeVTable vtable = {
+ subtree_enumerate, subtree_introspect, subtree_dispatch
+ };
+ GDBusSubtreeFlags flags;
+
+ flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
+ g_dbus_connection_register_subtree (connection, "/ca/desrt/dconf/Writer",
+ &vtable, flags, NULL, NULL, NULL);
}
static void
@@ -297,27 +410,20 @@ name_lost (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- DConfWriter *writer = user_data;
-
- g_critical ("unable to acquire name: '%s'", name);
-
- g_main_loop_quit (writer->loop);
+ g_error ("unable to acquire name: '%s'", name);
}
int
main (void)
{
- DConfWriter writer = { };
-
g_type_init ();
- writer.loop = g_main_loop_new (NULL, FALSE);
- writer.path = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
+ dconf_writer_init ();
g_bus_own_name (G_BUS_TYPE_SESSION, "ca.desrt.dconf", 0,
- bus_acquired, name_acquired, name_lost, &writer, NULL);
+ bus_acquired, name_acquired, name_lost, NULL, NULL);
- g_main_loop_run (writer.loop);
+ g_main_loop_run (g_main_loop_new (NULL, FALSE));
return 0;
}