[dconf] Many improvements



commit e7fd6d849feaea45d76cda1eb074c9ea59b85637
Author: Ryan Lortie <desrt desrt ca>
Date:   Thu Jul 15 21:18:55 2010 -0400

    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.

 client/dconf-client.c            |   17 ++-
 configure.ac                     |    2 +-
 engine/dconf-engine.c            |  285 ++++++++++++++++++++++++++++++++++----
 engine/dconf-engine.h            |    9 +-
 gsettings/dconfsettingsbackend.c |   16 ++-
 service/Makefile.am              |    4 +-
 service/dconf-rebuilder.c        |    8 +-
 service/dconf-rebuilder.h        |    4 +-
 service/dconf-writer.c           |  166 ++++++++++++++++++++++
 service/dconf-writer.h           |   18 +++
 service/service.c                |  230 ++++++++++++++++++++++--------
 11 files changed, 656 insertions(+), 103 deletions(-)
---
diff --git a/client/dconf-client.c b/client/dconf-client.c
index fdc33d2..9b44397 100644
--- a/client/dconf-client.c
+++ b/client/dconf-client.c
@@ -241,6 +241,19 @@ dconf_client_class_init (DConfClientClass *class)
   object_class->finalize = dconf_client_finalize;
 }
 
+static GVariant *
+dconf_client_service_func (DConfEngineMessage *dcem)
+{
+  g_assert (dcem->bus_type == 'e');
+
+  return g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION,
+                                                      NULL, NULL),
+                                      dcem->destination, dcem->object_path,
+                                      dcem->interface, dcem->method,
+                                      dcem->body, dcem->reply_type,
+                                      0, -1, NULL, NULL);
+}
+
 /**
  * dconf_client_new:
  * @context: the context string (must by %NULL for now)
@@ -265,7 +278,9 @@ dconf_client_new (const gchar          *context,
 {
   DConfClient *client = g_object_new (DCONF_TYPE_CLIENT, NULL);
 
-  client->engine = dconf_engine_new (context);
+  dconf_engine_set_service_func (dconf_client_service_func);
+
+  client->engine = dconf_engine_new ();
   client->will_write = will_write;
   client->watch_func = watch_func;
   client->user_data = user_data;
diff --git a/configure.ac b/configure.ac
index 45a5f43..1994903 100644
--- a/configure.ac
+++ b/configure.ac
@@ -24,7 +24,7 @@ GOBJECT_INTROSPECTION_CHECK([0.6.7])
 GTK_DOC_CHECK([1.14])
 
 # Dependencies
-PKG_CHECK_MODULES(gio, gio-2.0 >= 2.23.10)
+PKG_CHECK_MODULES(gio, gio-2.0 >= 2.25.12)
 PKG_CHECK_MODULES(gtk, gtk+-2.0)
 PKG_CHECK_MODULES(gee, gee-1.0)
 PKG_CHECK_MODULES(libxml, libxml-2.0)
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
index d8272cf..82fcd17 100644
--- a/engine/dconf-engine.c
+++ b/engine/dconf-engine.c
@@ -1,20 +1,265 @@
 
+#define _XOPEN_SOURCE 600
 #include "dconf-engine.h"
 #include <gvdb-reader.h>
 #include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+static DConfEngineServiceFunc dconf_engine_service_func;
+
+void
+dconf_engine_set_service_func (DConfEngineServiceFunc func)
+{
+  dconf_engine_service_func = func;
+}
+
+static const gchar *
+dconf_engine_get_session_dir (void)
+{
+  static const gchar *session_dir;
+  static gsize initialised;
+
+  if (g_once_init_enter (&initialised))
+    {
+      session_dir = g_strdup (getenv ("DCONF_SESSION_DIR"));
+
+      if (session_dir == NULL)
+        {
+          DConfEngineMessage dcem;
+          GVariant *result;
+
+          dcem.bus_type = 'e';
+          dcem.destination = "ca.desrt.dconf";
+          dcem.object_path = "/ca/desrt/dconf/Writer";
+          dcem.interface = "org.freedesktop.DBus.Properties";
+          dcem.method = "Get";
+          dcem.reply_type = G_VARIANT_TYPE ("(v)");
+          dcem.body = g_variant_new ("(ss)",
+                                     "ca.desrt.dconf.WriterInfo",
+                                     "ShmDirectory");
+
+          result = dconf_engine_service_func (&dcem);
+
+          if (result != NULL)
+            {
+              GVariant *str;
+
+              g_variant_get (result, "(v)", &str);
+
+              if (g_variant_is_of_type (str, G_VARIANT_TYPE_STRING))
+                session_dir = g_variant_get_string (str, NULL);
+              else
+                g_critical ("dconf service sent invalid reply");
+
+              g_variant_unref (result);
+              g_variant_unref (str);
+            }
+          else
+            g_critical ("Unable to contact dconf service");
+        }
+
+      g_once_init_leave (&initialised, 1);
+    }
+
+  return session_dir;
+}
 
 struct _DConfEngine
 {
+  guint8 *shm;
+  GvdbTable *gvdb;
+  gchar *name;
+  gchar *object_path;
   gint ref_count;
 };
 
+static void
+dconf_engine_setup (DConfEngine *engine)
+{
+  /* invariant: we never have gvdb without shm */
+  g_assert ((engine->gvdb == NULL) >= (engine->shm == NULL));
+
+  if (engine->object_path)
+    {
+      const gchar *session_dir = dconf_engine_get_session_dir ();
+
+      if (session_dir)
+        {
+          gchar *filename;
+          gint fd;
+
+          filename = g_build_filename (session_dir,
+                                       engine->name,
+                                       NULL);
+          fd = open (filename, O_RDWR | O_CREAT, 0600);
+          g_free (filename);
+
+          if (fd >= 0)
+            {
+              if (posix_fallocate (fd, 0, 1) == 0)
+                {
+                  engine->shm = mmap (NULL, 1, PROT_READ, MAP_SHARED, fd, 0);
+
+                  if (engine->shm == MAP_FAILED)
+                    engine->shm = NULL;
+                }
+
+              close (fd);
+            }
+        }
+
+      if (engine->shm)
+        {
+          gchar *filename;
+
+          filename = g_build_filename (g_get_user_config_dir (),
+                                       "dconf",
+                                       engine->name,
+                                       NULL);
+          engine->gvdb = gvdb_table_new (filename, FALSE, NULL);
+          g_free (filename);
+        }
+    }
+
+  g_assert ((engine->gvdb == NULL) >= (engine->shm == NULL));
+}
+
+static void
+dconf_engine_refresh (DConfEngine *engine)
+{
+  g_assert ((engine->gvdb == NULL) >= (engine->shm == NULL));
+
+  /* if we failed the first time, fail forever */
+  if (engine->shm && *engine->shm == 1)
+    {
+      if (engine->gvdb)
+        {
+          gvdb_table_unref (engine->gvdb);
+          engine->gvdb = NULL;
+        }
+
+      munmap (engine->shm, 1);
+      engine->shm = NULL;
+
+      dconf_engine_setup (engine);
+    }
+
+  g_assert ((engine->gvdb == NULL) >= (engine->shm == NULL));
+}
+
+
+static gboolean
+dconf_engine_load_profile (const gchar   *profile,
+                           gchar       ***dbs,
+                           gint          *n_dbs,
+                           GError       **error)
+{
+  gchar *filename;
+  gint allocated;
+  char line[80];
+  FILE *f;
+
+  filename = g_build_filename ("/etc/dconf/profiles", profile, NULL);
+  f = fopen (filename, "r");
+
+  if (f == NULL)
+    {
+      gint saved_errno = errno;
+
+      g_set_error (error, G_FILE_ERROR,
+                   g_file_error_from_errno (saved_errno),
+                   "open '%s': %s", filename, g_strerror (saved_errno));
+      g_free (filename);
+      return FALSE;
+    }
+
+  allocated = 4;
+  *dbs = g_new (gchar *, allocated);
+  *n_dbs = 0;
+
+  /* quick and dirty is good enough for now */
+  while (fgets (line, sizeof line, f))
+    {
+      const gchar *end;
+
+      end = strchr (line, '\n');
+
+      if (end == NULL)
+        g_error ("long line in %s", filename);
+
+      if (end == line)
+        continue;
+
+      if (line[0] == '#')
+        continue;
+
+      if (*n_dbs == allocated)
+        {
+          allocated *= 2;
+          *dbs = g_renew (gchar *, *dbs, allocated);
+        }
+
+      (*dbs)[(*n_dbs)++] = g_strndup (line, end - line);
+    }
+
+  *dbs = g_renew (gchar *, *dbs, *n_dbs);
+  g_free (filename);
+
+  return TRUE;
+}
+
 DConfEngine *
-dconf_engine_new (const gchar *context)
+dconf_engine_new (void)
 {
+  const gchar *profile;
   DConfEngine *engine;
+  gchar **dbs;
+  gint n_dbs;
+
+  profile = getenv ("DCONF_PROFILE");
+
+  if (profile)
+    {
+      GError *error = NULL;
+
+      if (!dconf_engine_load_profile (profile, &dbs, &n_dbs, &error))
+        g_error ("Error loading dconf profile '%s': %s\n",
+                 profile, error->message);
+    }
+  else
+    {
+      if (!dconf_engine_load_profile ("user", &dbs, &n_dbs, NULL))
+        {
+          dbs = g_new (gchar *, 1);
+          dbs[0] = g_strdup ("user");
+          n_dbs = 1;
+        }
+    }
 
   engine = g_slice_new (DConfEngine);
   engine->ref_count = 1;
+  engine->gvdb = NULL;
+  engine->shm = NULL;
+
+  if (strcmp (dbs[0], "-") != 0)
+    {
+      engine->name = g_strdup (dbs[0]);
+      engine->object_path = g_strjoin (NULL,
+                                       "/ca/desrt/dconf/Writer/",
+                                       dbs[0], NULL);
+    }
+  else
+    {
+      engine->name = NULL;
+      engine->object_path = NULL;
+    }
+
+  dconf_engine_setup (engine);
 
   return engine;
 }
@@ -38,24 +283,15 @@ dconf_engine_read (DConfEngine   *engine,
                    const gchar   *key,
                    DConfReadType  type)
 {
-  GvdbTable *table;
-  GVariant *value;
-  gchar *filename;
+  GVariant *value = NULL;
 
-  if (type == DCONF_READ_RESET)
-    return NULL;
-
-  filename = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
-  table = gvdb_table_new (filename, FALSE, NULL);
-  g_free (filename);
-
-  if (table)
+  if (type != DCONF_READ_RESET)
     {
-      value = gvdb_table_get_value (table, key);
-      gvdb_table_unref (table);
+      dconf_engine_refresh (engine);
+
+      if (engine->gvdb)
+        value = gvdb_table_get_value (engine->gvdb, key);
     }
-  else
-    value = NULL;
 
   return value;
 }
@@ -71,7 +307,7 @@ dconf_engine_make_match_rule (DConfEngine        *engine,
                           "arg1path='%s'", name);
   dcem->bus_type = 'e';
   dcem->destination = "org.freedesktop.DBus";
-  dcem->object_path = "/";
+  dcem->object_path = engine->object_path;
   dcem->interface = "org.freedesktop.DBus";
   dcem->body = g_variant_ref_sink (g_variant_new ("(s)", rule));
   g_free (rule);
@@ -131,7 +367,7 @@ dconf_engine_dcem (DConfEngine        *engine,
 
   dcem->bus_type = 'e';
   dcem->destination = "ca.desrt.dconf";
-  dcem->object_path = "/";
+  dcem->object_path = engine->object_path;
   dcem->interface = "ca.desrt.dconf.Writer";
   dcem->reply_type = G_VARIANT_TYPE ("(t)");
   dcem->method = method;
@@ -193,22 +429,15 @@ dconf_engine_list (DConfEngine    *engine,
                    DConfResetList *resets,
                    gsize          *length)
 {
-  GvdbTable *table;
-  gchar *filename;
   gchar **list;
 
   /* not yet supported */
   g_assert (resets == NULL);
 
-  filename = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
-  table = gvdb_table_new (filename, FALSE, NULL);
-  g_free (filename);
+  dconf_engine_refresh (engine);
 
-  if (table)
-    {
-      list = gvdb_table_list (table, dir);
-      gvdb_table_unref (table);
-    }
+  if (engine->gvdb)
+    list = gvdb_table_list (engine->gvdb, dir);
   else
     list = NULL;
 
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
index af9547c..08f84b8 100644
--- a/engine/dconf-engine.h
+++ b/engine/dconf-engine.h
@@ -20,10 +20,13 @@ typedef struct
 
 
 
-typedef GVariant *    (*DConfEngineServiceFunc)                         (DConfEngine             *engine,
-                                                                         DConfEngineMessage      *message);
+typedef GVariant *    (*DConfEngineServiceFunc)                         (DConfEngineMessage      *message);
+
+void                    dconf_engine_set_service_func                   (DConfEngineServiceFunc   func);
+DConfEngine *           dconf_engine_new                                (void);
+DConfEngine *           dconf_engine_new_for_db                         (DConfEngineServiceFunc  *service,
+                                                                         const gchar             *db_name);
 
-DConfEngine *           dconf_engine_new                                (const gchar             *context);
 void                    dconf_engine_unref                              (DConfEngine             *engine);
 DConfEngine *           dconf_engine_ref                                (DConfEngine             *engine);
 
diff --git a/gsettings/dconfsettingsbackend.c b/gsettings/dconfsettingsbackend.c
index 3daecc7..66c0881 100644
--- a/gsettings/dconfsettingsbackend.c
+++ b/gsettings/dconfsettingsbackend.c
@@ -594,10 +594,24 @@ dconf_settings_backend_sync (GSettingsBackend *backend)
   g_static_mutex_unlock (&dcsb->lock);
 }
 
+static GVariant *
+dconf_settings_backend_service_func (DConfEngineMessage *dcem)
+{
+  g_assert (dcem->bus_type == 'e');
+
+  return g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION,
+                                                      NULL, NULL),
+                                      dcem->destination, dcem->object_path,
+                                      dcem->interface, dcem->method,
+                                      dcem->body, dcem->reply_type,
+                                      0, -1, NULL, NULL);
+}
+
 static void
 dconf_settings_backend_init (DConfSettingsBackend *dcsb)
 {
-  dcsb->engine = dconf_engine_new (NULL);
+  dconf_engine_set_service_func (dconf_settings_backend_service_func);
+  dcsb->engine = dconf_engine_new ();
 }
 
 static void
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;
 }



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