summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2012-10-23 10:11:08 +0200
committerRyan Lortie <desrt@desrt.ca>2012-10-23 10:19:32 +0200
commit324df561617b777848b149416f0f6c0c3801b2d8 (patch)
treecf03127a19de5ace0f6d0976975bbe055455a36f
parent004f135e50164dc409bd6de74bc9ee3fe61da3e9 (diff)
downloaddconf-324df561617b777848b149416f0f6c0c3801b2d8.tar.gz
service: rewrite
Rewrite the dconf-service using gdbus-codegen and generally cleaning things up a lot. The DConfWriter class can now be reasonably subclassed to create more complex types of dconf databases (such as ones that are stored in the local runtime dir and synced up with an NFS home directory). Keep a cache of the keys in the database (instead of re-reading it every time we try to make a change). Drop support for the old D-Bus interface (now that we are two stable releases since it was used). Modify the commandline tool for 'dconf blame' to call the new interface.
-rw-r--r--bin/dconf.vala2
-rw-r--r--configure.ac2
-rw-r--r--service/.gitignore2
-rw-r--r--service/Makefile.am18
-rw-r--r--service/ca.desrt.dconf.xml23
-rw-r--r--service/dconf-blame.c189
-rw-r--r--service/dconf-blame.h40
-rw-r--r--service/dconf-interfaces.c94
-rw-r--r--service/dconf-rebuilder.c168
-rw-r--r--service/dconf-rebuilder.h34
-rw-r--r--service/dconf-service.c237
-rw-r--r--service/dconf-service.h36
-rw-r--r--service/dconf-state.c82
-rw-r--r--service/dconf-state.h23
-rw-r--r--service/dconf-writer.c486
-rw-r--r--service/dconf-writer.h31
-rw-r--r--service/main.c (renamed from service/dconf-interfaces.h)17
-rw-r--r--service/service.c608
18 files changed, 971 insertions, 1121 deletions
diff --git a/bin/dconf.vala b/bin/dconf.vala
index 646b275..f07bce3 100644
--- a/bin/dconf.vala
+++ b/bin/dconf.vala
@@ -228,7 +228,7 @@ void dconf_watch (string?[] args) throws Error {
void dconf_blame (string?[] args) throws Error {
var connection = Bus.get_sync (BusType.SESSION, null);
- var reply = connection.call_sync ("ca.desrt.dconf", "/ca/desrt/dconf/Writer", "ca.desrt.dconf.WriterInfo", "Blame",
+ var reply = connection.call_sync ("ca.desrt.dconf", "/ca/desrt/dconf", "ca.desrt.dconf.ServiceInfo", "Blame",
null, new VariantType ("(s)"), DBusCallFlags.NONE, -1, null);
print ("%s", reply.get_child_value (0).get_string (null));
}
diff --git a/configure.ac b/configure.ac
index 1932485..9507aac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ GTK_DOC_CHECK([1.15])
# Dependencies
PKG_CHECK_MODULES(glib, glib-2.0 >= 2.33.3)
-PKG_CHECK_MODULES(gio, gio-2.0)
+PKG_CHECK_MODULES(gio, gio-unix-2.0)
PKG_CHECK_MODULES(dbus, dbus-1)
AC_ARG_ENABLE(editor,
diff --git a/service/.gitignore b/service/.gitignore
index aefa930..f511a9a 100644
--- a/service/.gitignore
+++ b/service/.gitignore
@@ -1,2 +1,4 @@
dconf-service
ca.desrt.dconf.service
+dconf-generated.h
+dconf-generated.c
diff --git a/service/Makefile.am b/service/Makefile.am
index fe3afaa..c2dd6f7 100644
--- a/service/Makefile.am
+++ b/service/Makefile.am
@@ -12,18 +12,22 @@ dconf_service_LDADD = \
$(gio_LIBS)
dconf_service_SOURCES = \
- dconf-interfaces.h \
- dconf-interfaces.c \
- dconf-rebuilder.h \
- dconf-rebuilder.c \
+ $(BUILT_SOURCES) \
+ dconf-blame.c \
+ dconf-blame.h \
+ dconf-service.c \
+ dconf-service.h \
dconf-writer.h \
dconf-writer.c \
- dconf-state.h \
- dconf-state.c \
- service.c
+ main.c
+
+dconf-generated.h dconf-generated.c: ca.desrt.dconf.xml Makefile
+ gdbus-codegen --generate-c-code dconf-generated --c-namespace DConfDBus --interface-prefix=ca.desrt.dconf. $(srcdir)/ca.desrt.dconf.xml
DISTCLEANFILES = ca.desrt.dconf.service
+BUILT_SOURCES = dconf-generated.c dconf-generated.h
+
ca.desrt.dconf.service: Makefile
$(AM_V_GEN) (echo '[D-BUS Service]'; \
echo 'Name=ca.desrt.dconf'; \
diff --git a/service/ca.desrt.dconf.xml b/service/ca.desrt.dconf.xml
new file mode 100644
index 0000000..3273d5d
--- /dev/null
+++ b/service/ca.desrt.dconf.xml
@@ -0,0 +1,23 @@
+<node>
+ <interface name='ca.desrt.dconf.Writer'>
+ <method name='Init'/>
+ <method name='Change'>
+ <arg name='blob' direction='in' type='ay'>
+ <annotation name='org.gtk.GDBus.C.ForceGVariant' value='1'/>
+ </arg>
+ <arg name='tag' direction='out' type='s'/>
+ </method>
+ <signal name='Notify'>
+ <annotation name='org.gtk.GDBus.C.Name' value='NotifySignal'/>
+ <arg name='prefix' direction='out' type='s'/>
+ <arg name='changes' direction='out' type='as'/>
+ <arg name='tag' direction='out' type='s'/>
+ </signal>
+ </interface>
+
+ <interface name='ca.desrt.dconf.ServiceInfo'>
+ <method name='Blame'>
+ <arg name='blame' direction='out' type='s'/>
+ </method>
+ </interface>
+</node>
diff --git a/service/dconf-blame.c b/service/dconf-blame.c
new file mode 100644
index 0000000..920ef08
--- /dev/null
+++ b/service/dconf-blame.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "dconf-blame.h"
+
+#include "dconf-generated.h"
+
+#include <string.h>
+#include <fcntl.h>
+
+typedef DConfDBusServiceInfoSkeletonClass DConfBlameClass;
+struct _DConfBlame
+{
+ DConfDBusServiceInfoSkeleton parent_instance;
+
+ GString *blame_info;
+};
+
+static void dconf_blame_iface_init (DConfDBusServiceInfoIface *iface);
+G_DEFINE_TYPE_WITH_CODE (DConfBlame, dconf_blame, DCONF_DBUS_TYPE_SERVICE_INFO_SKELETON,
+ G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_SERVICE_INFO, dconf_blame_iface_init))
+
+#include "../common/dconf-changeset.h"
+#include "dconf-writer.h"
+
+void
+dconf_blame_record (GDBusMethodInvocation *invocation)
+{
+ DConfBlame *blame = dconf_blame_get ();
+ GError *error = NULL;
+ GVariant *parameters;
+ GVariant *reply;
+ GString *info;
+
+ if (!blame)
+ return;
+
+ if (blame->blame_info->len)
+ g_string_append (blame->blame_info, "\n====================================================================\n");
+
+ info = blame->blame_info;
+
+ g_string_append_printf (info, "Sender: %s\n", g_dbus_method_invocation_get_sender (invocation));
+ g_string_append_printf (info, "Object path: %s\n", g_dbus_method_invocation_get_object_path (invocation));
+ g_string_append_printf (info, "Method: %s\n", g_dbus_method_invocation_get_method_name (invocation));
+
+ if ((parameters = g_dbus_method_invocation_get_parameters (invocation)))
+ {
+ gchar *tmp;
+
+ tmp = g_variant_print (parameters, FALSE);
+ g_string_append_printf (info, "Parameters: %s\n", tmp);
+ g_free (tmp);
+ }
+
+ reply = g_dbus_connection_call_sync (g_dbus_method_invocation_get_connection (invocation),
+ "org.freedesktop.DBus", "/", "org.freedesktop.DBus",
+ "GetConnectionUnixProcessID",
+ g_variant_new ("(s)", g_dbus_method_invocation_get_sender (invocation)),
+ G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+ if (reply != NULL)
+ {
+ guint pid;
+
+ g_variant_get (reply, "(u)", &pid);
+ g_string_append_printf (info, "PID: %u\n", pid);
+ g_variant_unref (reply);
+ }
+ else
+ {
+ g_string_append_printf (info, "Unable to acquire PID: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ {
+ const gchar * const ps_fx[] = { "ps", "fx", NULL };
+ gchar *result_out;
+ gchar *result_err;
+ gint status;
+
+ if (g_spawn_sync (NULL, (gchar **) ps_fx, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ &result_out, &result_err, &status, &error))
+ {
+ g_string_append (info, "\n=== Process table from time of call follows ('ps fx') ===\n");
+ g_string_append (info, result_out);
+ g_string_append (info, result_err);
+ g_string_append_printf (info, "\nps exit status: %u\n", status);
+ }
+ else
+ {
+ g_string_append_printf (info, "\nUnable to spawn 'ps fx': %s\n", error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static gboolean
+dconf_blame_handle_blame (DConfDBusServiceInfo *info,
+ GDBusMethodInvocation *invocation)
+{
+ DConfBlame *blame = DCONF_BLAME (info);
+
+ dconf_blame_record (invocation);
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", blame->blame_info->str));
+
+ return TRUE;
+}
+
+static void
+dconf_blame_init (DConfBlame *blame)
+{
+ blame->blame_info = g_string_new (NULL);
+}
+
+static void
+dconf_blame_class_init (DConfBlameClass *class)
+{
+}
+
+static void
+dconf_blame_iface_init (DConfDBusServiceInfoIface *iface)
+{
+ iface->handle_blame = dconf_blame_handle_blame;
+}
+
+static gboolean
+dconf_blame_enabled (void)
+{
+ gint fd;
+
+ if (getenv ("DCONF_BLAME"))
+ return TRUE;
+
+ fd = open ("/proc/cmdline", O_RDONLY);
+ if (fd != -1)
+ {
+ gchar buffer[1024];
+ gssize s;
+
+ s = read (fd, buffer, sizeof buffer - 1);
+ close (fd);
+
+ if (0 < s && s < sizeof buffer)
+ {
+ buffer[s] = '\0';
+ if (strstr (buffer, "DCONF_BLAME"))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+DConfBlame *
+dconf_blame_get (void)
+{
+ static DConfBlame *blame;
+ static gboolean checked;
+
+ if (!checked)
+ {
+ if (dconf_blame_enabled ())
+ blame = g_object_new (DCONF_TYPE_BLAME, NULL);
+
+ checked = TRUE;
+ }
+
+ return blame;
+}
diff --git a/service/dconf-blame.h b/service/dconf-blame.h
new file mode 100644
index 0000000..07cf85b
--- /dev/null
+++ b/service/dconf-blame.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2010 Codethink Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __dconf_blame_h__
+#define __dconf_blame_h__
+
+typedef struct _DConfBlame DConfBlame;
+
+#include <gio/gio.h>
+
+#define DCONF_TYPE_BLAME (dconf_blame_get_type ())
+#define DCONF_BLAME(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ DCONF_TYPE_BLAME, DConfBlame))
+#define DCONF_IS_BLAME(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ DCONF_TYPE_BLAME))
+#define DCONF_BLAME_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
+ DCONF_TYPE_BLAME, DConfBlameClass))
+
+DConfBlame *dconf_blame_get (void);
+void dconf_blame_record (GDBusMethodInvocation *invocation);
+
+#endif /* __dconf_blame_h__ */
diff --git a/service/dconf-interfaces.c b/service/dconf-interfaces.c
deleted file mode 100644
index 45c6593..0000000
--- a/service/dconf-interfaces.c
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright © 2010 Codethink Limited
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "dconf-interfaces.h"
-
-static const GDBusArgInfo payload_arg = { -1, (gchar *) "payload", (gchar *) "ay" };
-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 tag_arg = { -1, (gchar *) "tag", (gchar *) "s" };
-static const GDBusArgInfo value_arg = { -1, (gchar *) "value", (gchar *) "av" };
-static const GDBusArgInfo values_arg = { -1, (gchar *) "values", (gchar *) "a(sav)" };
-
-static const GDBusArgInfo *change_in[] = { &payload_arg, NULL };
-static const GDBusArgInfo *change_out[] = { &tag_arg, NULL };
-static const GDBusArgInfo *write_in[] = { &name_arg, &value_arg, NULL };
-static const GDBusArgInfo *write_out[] = { &tag_arg, NULL };
-static const GDBusArgInfo *many_in[] = { &path_arg, &values_arg, NULL };
-static const GDBusArgInfo *many_out[] = { &tag_arg, NULL };
-static const GDBusArgInfo *blame_out[] = { &tag_arg, NULL };
-static const GDBusArgInfo *notify_args[] = { &path_arg, &names_arg, &tag_arg, NULL };
-
-static const GDBusMethodInfo change_method = {
- -1, (gchar *) "Change",
- (GDBusArgInfo **) change_in,
- (GDBusArgInfo **) change_out
-};
-
-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 GDBusMethodInfo blame_method = {
- -1, (gchar *) "Blame",
- NULL,
- (GDBusArgInfo **) blame_out
-};
-
-static const GDBusMethodInfo *writer_methods[] = {
- &change_method, &write_method, &writemany_method, NULL
-};
-
-static const GDBusSignalInfo *writer_signals[] = {
- &notify_signal, NULL
-};
-
-static const GDBusMethodInfo *writer_info_methods[] = {
- &blame_method, NULL
-};
-
-const GDBusInterfaceInfo ca_desrt_dconf_Writer = {
- -1, (gchar *) "ca.desrt.dconf.Writer",
- (GDBusMethodInfo **) writer_methods,
- (GDBusSignalInfo **) writer_signals,
- (GDBusPropertyInfo **) NULL
-};
-
-const GDBusInterfaceInfo ca_desrt_dconf_WriterInfo = {
- -1, (gchar *) "ca.desrt.dconf.WriterInfo",
- (GDBusMethodInfo **) writer_info_methods,
- (GDBusSignalInfo **) NULL,
- (GDBusPropertyInfo **) NULL
-};
diff --git a/service/dconf-rebuilder.c b/service/dconf-rebuilder.c
deleted file mode 100644
index 2b79e18..0000000
--- a/service/dconf-rebuilder.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright © 2010 Codethink Limited
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include "dconf-rebuilder.h"
-
-#include <string.h>
-
-#include "../gvdb/gvdb-reader.h"
-#include "../gvdb/gvdb-builder.h"
-#include "../common/dconf-paths.h"
-
-static GvdbItem *
-dconf_rebuilder_get_parent (GHashTable *table,
- const gchar *key)
-{
- GvdbItem *grandparent, *parent;
- gchar *parent_name;
- gint len;
-
- if (g_str_equal (key, "/"))
- return NULL;
-
- len = strlen (key);
- if (key[len - 1] == '/')
- len--;
-
- while (key[len - 1] != '/')
- len--;
-
- parent_name = g_strndup (key, len);
- parent = g_hash_table_lookup (table, parent_name);
-
- if (parent == NULL)
- {
- parent = gvdb_hash_table_insert (table, parent_name);
-
- grandparent = dconf_rebuilder_get_parent (table, parent_name);
-
- if (grandparent != NULL)
- gvdb_item_set_parent (parent, grandparent);
- }
- g_free (parent_name);
-
- return parent;
-}
-
-gboolean
-dconf_rebuilder_rebuild (const gchar *filename,
- const gchar *prefix,
- const gchar * const *keys,
- GVariant * const *values,
- int n_items,
- GError **error)
-{
- GHashTable *table;
- gboolean success;
- GHashTable *new;
- GvdbTable *old;
- gint i;
-
- table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
-
- /* read in the old values */
- if ((old = gvdb_table_new (filename, FALSE, NULL)))
- {
- gchar **names;
- gint n_names;
- gint i;
-
- names = gvdb_table_get_names (old, &n_names);
- for (i = 0; i < n_names; i++)
- {
- if (dconf_is_key (names[i], NULL))
- {
- GVariant *value;
-
- value = gvdb_table_get_value (old, names[i]);
-
- if (value != NULL)
- {
- g_hash_table_insert (table, names[i], value);
- names[i] = NULL;
- }
- }
-
- g_free (names[i]);
- }
-
- gvdb_table_unref (old);
- g_free (names);
- }
-
- /* apply the requested changes */
- for (i = 0; i < n_items; i++)
- {
- gchar *path = g_strconcat (prefix, keys[i], NULL);
-
- /* Check if we are performing a path reset */
- if (g_str_has_suffix (path, "/"))
- {
- GHashTableIter iter;
- gpointer key;
-
- g_assert (values[i] == NULL);
-
- /* A path reset is really a request to delete all keys that
- * has a name starting with the reset path.
- */
- g_hash_table_iter_init (&iter, table);
- while (g_hash_table_iter_next (&iter, &key, NULL))
- if (g_str_has_prefix (key, path))
- g_hash_table_iter_remove (&iter);
- }
-
- if (values[i] != NULL)
- g_hash_table_insert (table, g_strdup (path), g_variant_ref (values[i]));
- else
- g_hash_table_remove (table, path);
-
- g_free (path);
- }
-
- /* convert back to GVDB format */
- {
- GHashTableIter iter;
- gpointer key, value;
-
- new = gvdb_hash_table_new (NULL, NULL);
-
- g_hash_table_iter_init (&iter, table);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- GvdbItem *item;
-
- g_assert (g_hash_table_lookup (new, key) == NULL);
- item = gvdb_hash_table_insert (new, key);
- gvdb_item_set_parent (item, dconf_rebuilder_get_parent (new, key));
- gvdb_item_set_value (item, value);
- }
- }
-
- /* write the new file */
- success = gvdb_table_write_contents (new, filename, FALSE, error);
-
- /* clean up */
- g_hash_table_unref (table);
- g_hash_table_unref (new);
-
- return success;
-}
diff --git a/service/dconf-rebuilder.h b/service/dconf-rebuilder.h
deleted file mode 100644
index 1aa06de..0000000
--- a/service/dconf-rebuilder.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright © 2010 Codethink Limited
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#ifndef __dconf_rebuilder_h__
-#define __dconf_rebuilder_h__
-
-#include <glib.h>
-
-gboolean dconf_rebuilder_rebuild (const gchar *filename,
- const gchar *prefix,
- const gchar *const*keys,
- GVariant *const*values,
- gint n_items,
- GError **error);
-
-#endif /* __dconf_rebuilder_h__ */
diff --git a/service/dconf-service.c b/service/dconf-service.c
new file mode 100644
index 0000000..088a9ba
--- /dev/null
+++ b/service/dconf-service.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "dconf-service.h"
+
+#include "dconf-generated.h"
+#include "dconf-writer.h"
+#include "dconf-blame.h"
+
+#include <string.h>
+#include <fcntl.h>
+
+typedef GApplicationClass DConfServiceClass;
+typedef struct
+{
+ GApplication parent_instance;
+
+ DConfBlame *blame;
+ GHashTable *writers;
+ guint subtree_id;
+} DConfService;
+
+G_DEFINE_TYPE (DConfService, dconf_service, G_TYPE_APPLICATION)
+
+static gboolean
+dconf_service_signalled (gpointer user_data)
+{
+ DConfService *service = user_data;
+
+ g_application_release (G_APPLICATION (service));
+
+ return G_SOURCE_REMOVE;
+}
+
+static gchar **
+dconf_service_subtree_enumerate (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ gpointer user_data)
+{
+ DConfService *service = user_data;
+ GHashTableIter iter;
+ gchar **result;
+ gpointer key;
+ gint n_items;
+ gint i = 0;
+
+ n_items = g_hash_table_size (service->writers);
+ result = g_new (gchar *, n_items + 1);
+
+ g_hash_table_iter_init (&iter, service->writers);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ result[i++] = g_strdup (key);
+ result[i] = NULL;
+
+ g_assert_cmpint (n_items, ==, i);
+
+ return result;
+}
+
+GDBusInterfaceInfo **
+dconf_service_subtree_introspect (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *node,
+ gpointer user_data)
+{
+ GDBusInterfaceInfo **result;
+
+ if (node == NULL)
+ return NULL;
+
+ result = g_new (GDBusInterfaceInfo *, 2);
+ result[0] = dconf_dbus_writer_interface_info ();
+ result[1] = NULL;
+
+ return result;
+}
+
+static gpointer
+dconf_service_get_writer (DConfService *service,
+ GDBusConnection *connection,
+ const gchar *base_path,
+ const gchar *name)
+{
+ GDBusInterfaceSkeleton *writer;
+
+ writer = g_hash_table_lookup (service->writers, name);
+
+ if (writer == NULL)
+ {
+ GError *error = NULL;
+ gchar *object_path;
+
+ writer = dconf_writer_new (name);
+ g_hash_table_insert (service->writers, g_strdup (name), writer);
+ object_path = g_strjoin ("/", base_path, name, NULL);
+ g_dbus_interface_skeleton_export (writer, connection, object_path, &error);
+ g_assert_no_error (error);
+ g_free (object_path);
+ }
+
+ return writer;
+}
+
+const GDBusInterfaceVTable *
+dconf_service_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)
+{
+ DConfService *service = user_data;
+
+ g_assert_cmpstr (interface_name, ==, "ca.desrt.dconf.Writer");
+ g_assert (node != NULL);
+
+ *out_user_data = dconf_service_get_writer (service, connection, object_path, node);
+
+ return g_dbus_interface_skeleton_get_vtable (*out_user_data);
+}
+
+static gboolean
+dconf_service_dbus_register (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path,
+ GError **error)
+{
+ const GDBusSubtreeVTable subtree_vtable = {
+ dconf_service_subtree_enumerate,
+ dconf_service_subtree_introspect,
+ dconf_service_subtree_dispatch
+ };
+ DConfService *service = DCONF_SERVICE (application);
+ GError *local_error = NULL;
+
+ service->blame = dconf_blame_get ();
+ if (service->blame)
+ {
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->blame),
+ connection, object_path, &local_error);
+ g_assert_no_error (local_error);
+ }
+
+ service->subtree_id = g_dbus_connection_register_subtree (connection, "/ca/desrt/dconf/Writer", &subtree_vtable,
+ G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+ g_object_ref (service), g_object_unref, &local_error);
+ g_assert_no_error (local_error);
+
+ return TRUE;
+}
+
+static void
+dconf_service_dbus_unregister (GApplication *application,
+ GDBusConnection *connection,
+ const gchar *object_path)
+{
+ DConfService *service = DCONF_SERVICE (application);
+
+ if (service->blame)
+ {
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (service->blame));
+ g_object_unref (service->blame);
+ service->blame = NULL;
+ }
+
+ g_dbus_connection_unregister_subtree (connection, service->subtree_id);
+ service->subtree_id = 0;
+}
+
+static void
+dconf_service_startup (GApplication *application)
+{
+ DConfService *service = DCONF_SERVICE (application);
+
+ G_APPLICATION_CLASS (dconf_service_parent_class)
+ ->startup (application);
+
+ g_unix_signal_add (SIGTERM, dconf_service_signalled, service);
+ g_unix_signal_add (SIGINT, dconf_service_signalled, service);
+ g_unix_signal_add (SIGHUP, dconf_service_signalled, service);
+
+ g_application_hold (application);
+}
+
+static void
+dconf_service_shutdown (GApplication *application)
+{
+ G_APPLICATION_CLASS (dconf_service_parent_class)
+ ->shutdown (application);
+}
+
+static void
+dconf_service_init (DConfService *service)
+{
+ service->writers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+static void
+dconf_service_class_init (GApplicationClass *class)
+{
+ class->dbus_register = dconf_service_dbus_register;
+ class->dbus_unregister = dconf_service_dbus_unregister;
+ class->startup = dconf_service_startup;
+ class->shutdown = dconf_service_shutdown;
+}
+
+GApplication *
+dconf_service_new (void)
+{
+ g_type_init ();
+
+ return g_object_new (DCONF_TYPE_SERVICE,
+ "application-id", "ca.desrt.dconf",
+ "flags", G_APPLICATION_IS_SERVICE,
+ NULL);
+}
diff --git a/service/dconf-service.h b/service/dconf-service.h
new file mode 100644
index 0000000..221a24f
--- /dev/null
+++ b/service/dconf-service.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2012 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#ifndef __dconf_service_h__
+#define __dconf_service_h__
+
+#include <gio/gio.h>
+
+#define DCONF_TYPE_SERVICE (dconf_service_get_type ())
+#define DCONF_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ DCONF_TYPE_SERVICE, DConfService))
+#define DCONF_IS_SERVICE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ DCONF_TYPE_SERVICE))
+
+GType dconf_service_get_type (void);
+GApplication * dconf_service_new (void);
+
+#endif /* __dconf_service_h__ */
diff --git a/service/dconf-state.c b/service/dconf-state.c
deleted file mode 100644
index dacc175..0000000
--- a/service/dconf-state.c
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "dconf-state.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <errno.h>
-
-static void
-dconf_state_init_session (DConfState *state)
-{
- const gchar *config_dir = g_get_user_config_dir ();
-
- state->db_dir = g_build_filename (config_dir, "dconf", NULL);
-
- if (g_mkdir_with_parents (state->db_dir, 0700) != 0)
- g_error ("Can not create directory '%s': %s", state->db_dir, g_strerror (errno));
-}
-
-static gboolean
-dconf_state_is_blame_mode (void)
-{
- gint fd;
-
- if (getenv ("DCONF_BLAME"))
- return TRUE;
-
- fd = open ("/proc/cmdline", O_RDONLY);
- if (fd != -1)
- {
- gchar buffer[1024];
- gssize s;
-
- s = read (fd, buffer, sizeof buffer - 1);
- close (fd);
-
- if (0 < s && s < sizeof buffer)
- {
- buffer[s] = '\0';
- if (strstr (buffer, "DCONF_BLAME"))
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-void
-dconf_state_init (DConfState *state)
-{
- state->blame_mode = dconf_state_is_blame_mode ();
- state->blame_info = NULL;
- state->is_session = strcmp (g_get_user_name (), "dconf") != 0;
- state->main_loop = g_main_loop_new (NULL, FALSE);
- state->serial = 0;
- state->id = NULL;
-
- if (state->is_session)
- dconf_state_init_session (state);
-}
-
-void
-dconf_state_destroy (DConfState *state)
-{
- g_main_loop_unref (state->main_loop);
-}
-
-void
-dconf_state_set_id (DConfState *state,
- const gchar *id)
-{
- g_assert (state->id == NULL);
- state->id = g_strdup (id);
-}
-
-gchar *
-dconf_state_get_tag (DConfState *state)
-{
- return g_strdup_printf ("%"G_GUINT64_FORMAT"%s",
- state->serial++, state->id);
-}
diff --git a/service/dconf-state.h b/service/dconf-state.h
deleted file mode 100644
index f68ac3e..0000000
--- a/service/dconf-state.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef __dconf_state_h__
-#define __dconf_state_h__
-
-#include <glib.h>
-
-typedef struct
-{
- gboolean blame_mode;
- GString *blame_info;
- gboolean is_session;
- GMainLoop *main_loop;
- guint64 serial;
- gchar *db_dir;
- gchar *id;
-} DConfState;
-
-void dconf_state_init (DConfState *state);
-void dconf_state_set_id (DConfState *state,
- const gchar *id);
-void dconf_state_destroy (DConfState *state);
-gchar * dconf_state_get_tag (DConfState *state);
-
-#endif /* __dconf_state_h__ */
diff --git a/service/dconf-writer.c b/service/dconf-writer.c
index c6cda13..4cf800e 100644
--- a/service/dconf-writer.c
+++ b/service/dconf-writer.c
@@ -21,138 +21,474 @@
#include "dconf-writer.h"
-#include "dconf-rebuilder.h"
-#include "dconf-state.h"
#include "../shm/dconf-shm.h"
#include <stdlib.h>
#include <unistd.h>
+#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
-struct OPAQUE_TYPE__DConfWriter
+#include "dconf-generated.h"
+#include "../common/dconf-changeset.h"
+#include "../gvdb/gvdb-builder.h"
+#include "../gvdb/gvdb-reader.h"
+
+typedef struct
{
- DConfState *state;
+ DConfDBusWriterSkeleton parent_instance;
+ gchar *filename;
+ gboolean native;
gchar *name;
- gchar *path;
-};
+ guint64 tag;
+
+ GHashTable *uncommited_values;
+ GHashTable *commited_values;
+
+ GQueue uncommited_changes;
+ GQueue commited_changes;
+} DConfWriter;
+
+typedef struct
+{
+ DConfChangeset *changeset;
+ gchar *tag;
+} TaggedChange;
+
+typedef struct
+{
+ DConfDBusWriterSkeletonClass parent_instance;
+
+ gboolean (* begin) (DConfWriter *writer,
+ GError **error);
+ void (* change) (DConfWriter *writer,
+ DConfChangeset *changeset,
+ const gchar *tag);
+ gboolean (* commit) (DConfWriter *writer,
+ GError **error);
+ void (* end) (DConfWriter *writer);
+} DConfWriterClass;
+
+
+static void dconf_writer_iface_init (DConfDBusWriterIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (DConfWriter, dconf_writer, DCONF_DBUS_TYPE_WRITER_SKELETON,
+ G_IMPLEMENT_INTERFACE (DCONF_DBUS_TYPE_WRITER, dconf_writer_iface_init))
+
+static GvdbItem *
+dconf_writer_get_parent (GHashTable *table,
+ const gchar *key)
+{
+ GvdbItem *grandparent, *parent;
+ gchar *parent_name;
+ gint len;
+
+ if (g_str_equal (key, "/"))
+ return NULL;
+
+ len = strlen (key);
+ if (key[len - 1] == '/')
+ len--;
+
+ while (key[len - 1] != '/')
+ len--;
+
+ parent_name = g_strndup (key, len);
+ parent = g_hash_table_lookup (table, parent_name);
+
+ if (parent == NULL)
+ {
+ parent = gvdb_hash_table_insert (table, parent_name);
+
+ grandparent = dconf_writer_get_parent (table, parent_name);
+
+ if (grandparent != NULL)
+ gvdb_item_set_parent (parent, grandparent);
+ }
+
+ g_free (parent_name);
+
+ return parent;
+}
+
+static GHashTable *
+dconf_writer_new_value_table (GHashTable *copy_from)
+{
+ GHashTable *table;
+
+ table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+
+ if (copy_from)
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, copy_from);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (table, g_strdup (key), g_variant_ref (value));
+ }
+
+ return table;
+}
+
+static gchar *
+dconf_writer_get_tag (DConfWriter *writer)
+{
+ GDBusConnection *connection;
+
+ connection = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (writer));
+
+ return g_strdup_printf ("%s:%s:%" G_GUINT64_FORMAT,
+ g_dbus_connection_get_unique_name (connection),
+ writer->name, writer->tag++);
+}
-/* Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
- */
static gboolean
-is_valid_dbus_path_element (const gchar *string)
+dconf_writer_real_begin (DConfWriter *writer,
+ GError **error)
{
- gint i;
+ /* If this is the first time, populate the value table with the
+ * existing values.
+ */
+ if (writer->commited_values == NULL)
+ {
+ GError *my_error = NULL;
+ GvdbTable *table;
+ gchar **names;
+ gint n_names;
+ gint i;
+
+ table = gvdb_table_new (writer->filename, FALSE, &my_error);
+
+ /* It is perfectly fine if the file does not exist -- then it's
+ * just empty.
+ */
+ if (g_error_matches (my_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ g_clear_error (&my_error);
+
+ /* Otherwise, we should report errors to prevent ourselves from
+ * overwriting the database in other situations...
+ */
+ if (my_error)
+ {
+ g_propagate_prefixed_error (error, my_error, "Cannot open dconf database: ");
+ return FALSE;
+ }
+
+ /* Only initialise once we know we are in a non-error situation */
+ writer->commited_values = dconf_writer_new_value_table (NULL);
+
+ /* Fill the table up with the initial state */
+ names = gvdb_table_get_names (table, &n_names);
+ for (i = 0; i < n_names; i++)
+ {
+ if (dconf_is_key (names[i], NULL))
+ {
+ GVariant *value;
+
+ value = gvdb_table_get_value (table, names[i]);
+
+ if (value != NULL)
+ {
+ g_hash_table_insert (writer->commited_values, names[i], value);
+ names[i] = NULL;
+ }
+ }
+
+ g_free (names[i]);
+ }
+
+ gvdb_table_unref (table);
+ g_free (names);
+ }
- for (i = 0; string[i]; i++)
- if (!g_ascii_isalnum (string[i]) && string[i] != '_')
- return FALSE;
+ writer->uncommited_values = dconf_writer_new_value_table (writer->commited_values);
return TRUE;
}
-gchar **
-dconf_writer_list_existing (void)
+static void
+dconf_writer_real_change (DConfWriter *writer,
+ DConfChangeset *changeset,
+ const gchar *tag)
+{
+ const gchar *prefix;
+ const gchar * const *keys;
+ GVariant * const *values;
+ int n_items;
+ gint i;
+
+ n_items = dconf_changeset_describe (changeset, &prefix, &keys, &values);
+
+ for (i = 0; i < n_items; i++)
+ {
+ gchar *path = g_strconcat (prefix, keys[i], NULL);
+
+ /* Check if we are performing a path reset */
+ if (g_str_has_suffix (path, "/"))
+ {
+ GHashTableIter iter;
+ gpointer key;
+
+ g_assert (values[i] == NULL);
+
+ /* A path reset is really a request to delete all keys that
+ * have a name starting with the reset path.
+ */
+ g_hash_table_iter_init (&iter, writer->uncommited_values);
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ if (g_str_has_prefix (key, path))
+ g_hash_table_iter_remove (&iter);
+ }
+
+ if (values[i] != NULL)
+ g_hash_table_insert (writer->uncommited_values, g_strdup (path), g_variant_ref (values[i]));
+ else
+ g_hash_table_remove (writer->uncommited_values, path);
+
+ g_free (path);
+ }
+
+ if (tag)
+ {
+ TaggedChange *change;
+
+ change = g_slice_new (TaggedChange);
+ change->changeset = dconf_changeset_ref (changeset);
+ change->tag = g_strdup (tag);
+
+ g_queue_push_tail (&writer->uncommited_changes, change);
+ }
+}
+
+static gboolean
+dconf_writer_real_commit (DConfWriter *writer,
+ GError **error)
{
- GPtrArray *array;
- gchar *path;
- GDir *dir;
+ GHashTableIter iter;
+ gpointer key, value;
+ GHashTable *gvdb;
+ gboolean success;
- path = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
- array = g_ptr_array_new ();
+ gvdb = gvdb_hash_table_new (NULL, NULL);
- if ((dir = g_dir_open (path, 0, NULL)))
+ g_hash_table_iter_init (&iter, writer->uncommited_values);
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
- const gchar *name;
+ GvdbItem *item;
- while ((name = g_dir_read_name (dir)))
- if (is_valid_dbus_path_element (name))
- g_ptr_array_add (array, g_strdup (name));
+ g_assert (g_hash_table_lookup (gvdb, key) == NULL);
+ item = gvdb_hash_table_insert (gvdb, key);
+ gvdb_item_set_parent (item, dconf_writer_get_parent (gvdb, key));
+ gvdb_item_set_value (item, value);
}
- g_ptr_array_add (array, NULL);
- g_free (path);
+ success = gvdb_table_write_contents (gvdb, writer->filename, FALSE, error);
+
+ if (success && writer->native)
+ dconf_shm_flag (writer->name);
+
+ if (writer->commited_values)
+ g_hash_table_unref (writer->commited_values);
+ writer->commited_values = g_hash_table_ref (writer->uncommited_values);
+
+ {
+ GQueue empty_queue = G_QUEUE_INIT;
+
+ g_assert (g_queue_is_empty (&writer->commited_changes));
+ writer->commited_changes = writer->uncommited_changes;
+ writer->uncommited_changes = empty_queue;
+ }
- return (gchar **) g_ptr_array_free (array, FALSE);
+ return success;
+}
+
+static void
+dconf_writer_real_end (DConfWriter *writer)
+{
+ while (!g_queue_is_empty (&writer->uncommited_changes))
+ {
+ TaggedChange *change = g_queue_pop_head (&writer->commited_changes);
+ g_free (change->tag);
+ g_slice_free (TaggedChange, change);
+ }
+
+ while (!g_queue_is_empty (&writer->commited_changes))
+ {
+ TaggedChange *change = g_queue_pop_head (&writer->commited_changes);
+ const gchar *prefix;
+ const gchar * const *paths;
+
+ dconf_changeset_describe (change->changeset, &prefix, &paths, NULL);
+ dconf_dbus_writer_emit_notify_signal (DCONF_DBUS_WRITER (writer), prefix, paths, change->tag);
+ dconf_changeset_unref (change->changeset);
+ g_free (change->tag);
+ g_slice_free (TaggedChange, change);
+ }
+
+ g_clear_pointer (&writer->uncommited_values, g_hash_table_unref);
}
gboolean
-dconf_writer_write (DConfWriter *writer,
- const gchar *name,
- GVariant *value,
+dconf_writer_begin (DConfWriter *writer,
GError **error)
{
- if (!dconf_rebuilder_rebuild (writer->path, "", &name, &value, 1, error))
- return FALSE;
-
- dconf_shm_flag (writer->name);
+ return DCONF_WRITER_GET_CLASS (writer)->begin (writer, error);
+}
- return TRUE;
+void
+dconf_writer_change (DConfWriter *writer,
+ DConfChangeset *changeset,
+ const gchar *tag)
+{
+ DCONF_WRITER_GET_CLASS (writer)->change (writer, changeset, tag);
}
gboolean
-dconf_writer_write_many (DConfWriter *writer,
- const gchar *prefix,
- const gchar * const *keys,
- GVariant * const *values,
- gsize n_items,
- GError **error)
+dconf_writer_commit (DConfWriter *writer,
+ GError **error)
{
- if (!dconf_rebuilder_rebuild (writer->path, prefix, keys,
- values, n_items, error))
- return FALSE;
+ return DCONF_WRITER_GET_CLASS (writer)->commit (writer, error);
+}
+
+void
+dconf_writer_end (DConfWriter *writer)
+{
+ return DCONF_WRITER_GET_CLASS (writer)->end (writer);
+}
+
+static gboolean
+dconf_writer_handle_init (DConfDBusWriter *dbus_writer,
+ GDBusMethodInvocation *invocation)
+{
+ DConfWriter *writer = DCONF_WRITER (dbus_writer);
+ GError *error = NULL;
+
+ dconf_blame_record (invocation);
+
+ dconf_writer_begin (writer, &error) && dconf_writer_commit (writer, &error);
+
+ if (error)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ }
- dconf_shm_flag (writer->name);
+ else
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ dconf_writer_end (writer);
return TRUE;
}
-gboolean
-dconf_writer_change (DConfWriter *writer,
- DConfChangeset *change,
- GError **error)
+static gboolean
+dconf_writer_handle_change (DConfDBusWriter *dbus_writer,
+ GDBusMethodInvocation *invocation,
+ GVariant *blob)
{
- const gchar * const *keys;
- GVariant * const *values;
- const gchar *prefix;
- gint n_items;
+ DConfWriter *writer = DCONF_WRITER (dbus_writer);
+ DConfChangeset *changeset;
+ GError *error = NULL;
+ GVariant *tmp, *args;
+ gchar *tag;
+
+ dconf_blame_record (invocation);
+
+ tmp = g_variant_new_from_data (G_VARIANT_TYPE ("a{smv}"),
+ g_variant_get_data (blob), g_variant_get_size (blob), FALSE,
+ (GDestroyNotify) g_variant_unref, g_variant_ref (blob));
+ g_variant_ref_sink (tmp);
+ args = g_variant_get_normal_form (tmp);
+ g_variant_unref (tmp);
+
+ changeset = dconf_changeset_deserialise (args);
+ g_variant_unref (args);
- n_items = dconf_changeset_describe (change, &prefix, &keys, &values);
+ tag = dconf_writer_get_tag (writer);
- if (!n_items)
- return TRUE;
+ if (!dconf_writer_begin (writer, &error))
+ goto out;
- if (!dconf_rebuilder_rebuild (writer->path, prefix, keys, values, n_items, error))
- return FALSE;
+ dconf_writer_change (writer, changeset, tag);
- dconf_shm_flag (writer->name);
+ if (!dconf_writer_commit (writer, &error))
+ goto out;
+
+out:
+ if (error)
+ {
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ }
+
+ else
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", tag));
+
+ g_free (tag);
+
+ dconf_writer_end (writer);
return TRUE;
}
-const gchar *
-dconf_writer_get_name (DConfWriter *writer)
+static void
+dconf_writer_iface_init (DConfDBusWriterIface *iface)
{
- return writer->name;
+ iface->handle_init = dconf_writer_handle_init;
+ iface->handle_change = dconf_writer_handle_change;
}
-DConfState *
-dconf_writer_get_state (DConfWriter *writer)
+static void
+dconf_writer_init (DConfWriter *writer)
{
- return writer->state;
+ writer->native = TRUE;
}
-DConfWriter *
-dconf_writer_new (DConfState *state,
- const gchar *name)
+static void
+dconf_writer_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
{
- DConfWriter *writer;
+ DConfWriter *writer = DCONF_WRITER (object);
+
+ g_assert_cmpint (prop_id, ==, 1);
- writer = g_slice_new (DConfWriter);
- writer->state = state;
- writer->path = g_build_filename (state->db_dir, name, NULL);
- writer->name = g_strdup (name);
+ g_assert (!writer->name);
+ writer->name = g_value_dup_string (value);
- return writer;
+ if (writer->native)
+ writer->filename = g_build_filename (g_get_user_config_dir (), "dconf", writer->name, NULL);
+ else
+ writer->filename = g_build_filename (g_get_user_runtime_dir (), "dconf", writer->name, NULL);
+}
+
+static void
+dconf_writer_class_init (DConfWriterClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->set_property = dconf_writer_set_property;
+
+ class->begin = dconf_writer_real_begin;
+ class->change = dconf_writer_real_change;
+ class->commit = dconf_writer_real_commit;
+ class->end = dconf_writer_real_end;
+
+ g_object_class_install_property (object_class, 1,
+ g_param_spec_string ("name", "name", "name", NULL,
+ G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_WRITABLE));
+}
+
+const gchar *
+dconf_writer_get_name (DConfWriter *writer)
+{
+ return writer->name;
+}
+
+GDBusInterfaceSkeleton *
+dconf_writer_new (const gchar *name)
+{
+ return g_object_new (DCONF_TYPE_WRITER, "name", name, NULL);
}
diff --git a/service/dconf-writer.h b/service/dconf-writer.h
index 8f391fe..08183e7 100644
--- a/service/dconf-writer.h
+++ b/service/dconf-writer.h
@@ -22,29 +22,16 @@
#ifndef __dconf_writer_h__
#define __dconf_writer_h__
-#include "../common/dconf-changeset.h"
-#include "dconf-state.h"
+#include <gio/gio.h>
-typedef struct OPAQUE_TYPE__DConfWriter DConfWriter;
+#define DCONF_TYPE_WRITER (dconf_writer_get_type ())
+#define DCONF_WRITER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
+ DCONF_TYPE_WRITER, DConfWriter))
+#define DCONF_IS_WRITER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
+ DCONF_TYPE_WRITER))
+#define DCONF_WRITER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
+ DCONF_TYPE_WRITER, DConfWriterClass))
-gchar ** dconf_writer_list_existing (void);
-DConfWriter * dconf_writer_new (DConfState *state,
- const gchar *name);
-DConfState * dconf_writer_get_state (DConfWriter *writer);
-const gchar * dconf_writer_get_name (DConfWriter *writer);
-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);
-
-gboolean dconf_writer_change (DConfWriter *writer,
- DConfChangeset *change,
- GError **error);
+GDBusInterfaceSkeleton *dconf_writer_new (const gchar *filename);
#endif /* __dconf_writer_h__ */
diff --git a/service/dconf-interfaces.h b/service/main.c
index 9f159b1..023abe3 100644
--- a/service/dconf-interfaces.h
+++ b/service/main.c
@@ -19,12 +19,17 @@
* Author: Ryan Lortie <desrt@desrt.ca>
*/
-#ifndef __dconf_interfaces_h__
-#define __dconf_interfaces_h__
+#include "dconf-service.h"
-#include <gio/gio.h>
+int
+main (int argc, char **argv)
+{
+ GApplication *app;
+ gint status;
-extern const GDBusInterfaceInfo ca_desrt_dconf_Writer;
-extern const GDBusInterfaceInfo ca_desrt_dconf_WriterInfo;
+ app = dconf_service_new ();
+ status = g_application_run (app, argc, argv);
+ g_object_unref (app);
-#endif /* __dconf_interfaces_h__ */
+ return status;
+}
diff --git a/service/service.c b/service/service.c
deleted file mode 100644
index 22011d3..0000000
--- a/service/service.c
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright © 2010 Codethink Limited
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Ryan Lortie <desrt@desrt.ca>
- */
-
-#include <glib-unix.h>
-#include <gio/gio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "dconf-interfaces.h"
-#include "../common/dconf-changeset.h"
-#include "dconf-writer.h"
-#include "dconf-state.h"
-
-static void
-emit_notify_signal (GDBusConnection *connection,
- DConfWriter *writer,
- const gchar *tag,
- const gchar *prefix,
- const gchar **keys,
- guint n_keys)
-{
- GVariantBuilder builder;
- GVariant *items;
- gchar *path;
- gchar *obj;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
-
- if (n_keys > 1)
- {
- const gchar *last_reset = NULL;
- gint last_reset_len = 0;
- gint i;
-
- for (i = 0; i < n_keys; i++)
- {
- gint length = strlen (keys[i]);
-
- if (last_reset && length > last_reset_len &&
- memcmp (last_reset, keys[i], last_reset_len) == 0)
- continue;
-
- if (length == 0 || keys[i][length - 1] == '/')
- {
- last_reset_len = length;
- last_reset = keys[i];
- }
- else
- {
- if (last_reset != NULL)
- {
- g_variant_builder_add (&builder, "s", last_reset);
- last_reset = NULL;
- }
-
- g_variant_builder_add (&builder, "s", keys[i]);
- }
- }
- }
-
- items = g_variant_builder_end (&builder);
-
- if (g_variant_n_children (items) == 0)
- path = g_strconcat (prefix, keys[0], NULL);
- else
- path = g_strdup (prefix);
-
- obj = g_strjoin (NULL, "/ca/desrt/dconf/Writer/",
- dconf_writer_get_name (writer), NULL);
- g_dbus_connection_emit_signal (connection, NULL, obj,
- "ca.desrt.dconf.Writer", "Notify",
- g_variant_new ("(s@ass)",
- path, items, tag),
- NULL);
- g_free (path);
- g_free (obj);
-}
-
-static void
-emit_notify_signal_change (GDBusConnection *connection,
- DConfWriter *writer,
- gchar *tag,
- DConfChangeset *change)
-{
- const gchar *path;
- const gchar * const *names;
-
- if (dconf_changeset_describe (change, &path, &names, NULL))
- {
- gchar *obj;
-
- obj = g_strjoin (NULL, "/ca/desrt/dconf/Writer/", dconf_writer_get_name (writer), NULL);
- g_dbus_connection_emit_signal (connection, NULL, obj,
- "ca.desrt.dconf.Writer", "Notify",
- g_variant_new ("(s^ass)", path, names, tag),
- NULL);
- g_free (obj);
- }
-}
-
-static void
-unwrap_maybe_and_variant (GVariant **ptr)
-{
- GVariant *array, *child;
- gsize n_children;
-
- array = *ptr;
- n_children = g_variant_n_children (array);
-
- switch (n_children)
- {
- case 0:
- child = NULL;
- break;
- case 1: default:
- g_variant_get_child (array, 0, "v", &child);
- break;
- case 2:
- {
- GVariant *untrusted;
- GVariant *trusted;
- GVariant *ay;
-
- g_variant_get_child (array, 0, "v", &ay);
- if (!g_variant_is_of_type (ay, G_VARIANT_TYPE_BYTESTRING))
- {
- g_variant_unref (ay);
- child = NULL;
- break;
- }
-
- untrusted = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT,
- g_variant_get_data (ay),
- g_variant_get_size (ay),
- FALSE,
- (GDestroyNotify) g_variant_unref, ay);
- g_variant_ref_sink (untrusted);
- trusted = g_variant_get_normal_form (untrusted);
- g_variant_unref (untrusted);
-
- g_variant_get (trusted, "v", &child);
- }
- }
-
- g_variant_unref (array);
- *ptr = child;
-}
-
-static void
-gather_blame_info (DConfState *state,
- GDBusConnection *connection,
- const gchar *sender,
- const gchar *object_path,
- const gchar *method_name,
- GVariant *parameters)
-{
- GError *error = NULL;
- GVariant *reply;
- GString *info;
-
- if (state->blame_info == NULL)
- state->blame_info = g_string_new (NULL);
- else
- g_string_append (state->blame_info, "\n====================================================================\n");
-
- info = state->blame_info;
-
- g_string_append_printf (info, "Sender: %s\n", sender);
- g_string_append_printf (info, "Object path: %s\n", object_path);
- g_string_append_printf (info, "Method: %s\n", method_name);
-
- if (parameters)
- {
- gchar *tmp;
-
- tmp = g_variant_print (parameters, FALSE);
- g_string_append_printf (info, "Parameters: %s\n", tmp);
- g_free (tmp);
- }
-
- reply = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", "org.freedesktop.DBus",
- "GetConnectionUnixProcessID", g_variant_new ("(s)", sender),
- G_VARIANT_TYPE ("(u)"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
-
- if (reply != NULL)
- {
- guint pid;
-
- g_variant_get (reply, "(u)", &pid);
- g_string_append_printf (info, "PID: %u\n", pid);
- g_variant_unref (reply);
- }
- else
- {
- g_string_append_printf (info, "Unable to acquire PID: %s\n", error->message);
- g_error_free (error);
- }
-
- {
- const gchar * const ps_fx[] = { "ps", "fx", NULL };
- gchar *result_out;
- gchar *result_err;
- gint status;
-
- if (g_spawn_sync (NULL, (gchar **) ps_fx, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
- &result_out, &result_err, &status, &error))
- {
- g_string_append (info, "\n=== Process table from time of call follows ('ps fx') ===\n");
- g_string_append (info, result_out);
- g_string_append (info, result_err);
- g_string_append_printf (info, "\nps exit status: %u\n", status);
- }
- else
- {
- g_string_append_printf (info, "\nUnable to spawn 'ps fx': %s\n", error->message);
- g_error_free (error);
- }
- }
-}
-
-static void
-method_call (GDBusConnection *connection,
- const gchar *sender,
- const gchar *object_path,
- const gchar *interface_name,
- const gchar *method_name,
- GVariant *parameters,
- GDBusMethodInvocation *invocation,
- gpointer user_data)
-{
- DConfWriter *writer = user_data;
- DConfState *state;
-
- state = dconf_writer_get_state (writer);
-
- /* debugging... */
- if G_UNLIKELY (state->blame_mode)
- gather_blame_info (state, connection, sender, object_path, method_name, parameters);
-
- if (strcmp (method_name, "Change") == 0)
- {
- DConfChangeset *change;
- GError *error = NULL;
- GVariant *args;
- GVariant *tmp;
- gchar *tag;
-
- tmp = g_variant_new_from_data (G_VARIANT_TYPE ("a{smv}"),
- g_variant_get_data (parameters), g_variant_get_size (parameters), FALSE,
- (GDestroyNotify) g_variant_unref, g_variant_ref (parameters));
- g_variant_ref_sink (tmp);
- args = g_variant_get_normal_form (tmp);
- g_variant_unref (tmp);
-
- change = dconf_changeset_deserialise (args);
- g_variant_unref (args);
-
- if (!dconf_writer_change (writer, change, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- g_error_free (error);
- return;
- }
-
- tag = dconf_state_get_tag (state);
- g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", tag));
- emit_notify_signal_change (connection, writer, tag, change);
- dconf_changeset_unref (change);
- g_free (tag);
- }
-
- else if (strcmp (method_name, "Write") == 0)
- {
- GError *error = NULL;
- GVariant *keyvalue;
- const gchar *key;
- gsize key_length;
- GVariant *value;
- GVariant *none;
- gchar *path;
- gchar *tag;
-
- g_variant_get (parameters, "(@s@av)", &keyvalue, &value);
- key = g_variant_get_string (keyvalue, &key_length);
- g_variant_unref (keyvalue);
- unwrap_maybe_and_variant (&value);
-
- if (key[0] != '/' || strstr (key, "//"))
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "invalid key: %s", key);
- if (value != NULL)
- g_variant_unref (value);
-
- return;
- }
-
- if (key[key_length - 1] == '/' && value != NULL)
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "can not set value to path");
- if (value != NULL)
- g_variant_unref (value);
- return;
- }
-
- if (!dconf_writer_write (writer, key, value, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- if (value != NULL)
- g_variant_unref (value);
- g_error_free (error);
- return;
- }
-
- tag = dconf_state_get_tag (state);
- g_dbus_method_invocation_return_value (invocation,
- g_variant_new ("(s)", tag));
- none = g_variant_new_array (G_VARIANT_TYPE_STRING, NULL, 0);
- path = g_strjoin (NULL, "/ca/desrt/dconf/Writer/",
- dconf_writer_get_name (writer), NULL);
- g_dbus_connection_emit_signal (connection, NULL, path,
- "ca.desrt.dconf.Writer", "Notify",
- g_variant_new ("(s@ass)",
- key, none, tag),
- NULL);
- if (value != NULL)
- g_variant_unref (value);
- g_free (path);
- g_free (tag);
- }
-
- else if (strcmp (method_name, "WriteMany") == 0)
- {
- GError *error = NULL;
- const gchar *prefix;
- GVariantIter *iter;
- const gchar **keys;
- GVariant **values;
- gsize length;
- gchar *tag;
- gint i = 0;
- gint j;
-
- g_variant_get (parameters, "(&sa(sav))", &prefix, &iter);
- length = g_variant_iter_n_children (iter);
-
- keys = g_new (const gchar *, length + 1);
- values = g_new (GVariant *, length);
- while (g_variant_iter_next (iter, "(&s@av)", &keys[i], &values[i]))
- {
- unwrap_maybe_and_variant (&values[i]);
-
- if (keys[i][0] == '/' || strstr (keys[i], "//") ||
- (i > 0 && !(strcmp (keys[i - 1], keys[i]) < 0)))
- {
- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
- G_DBUS_ERROR_INVALID_ARGS,
- "invalid key list");
-
- for (j = 0; j <= i; j++)
- if (values[j] != NULL)
- g_variant_unref (values[j]);
-
- g_free (values);
- g_free (keys);
-
- return;
- }
-
- i++;
- }
- g_variant_iter_free (iter);
- keys[i] = NULL;
-
- if (!dconf_writer_write_many (writer, prefix, keys, values, i, &error))
- {
- g_dbus_method_invocation_return_gerror (invocation, error);
- g_error_free (error);
- return;
- }
-
- tag = dconf_state_get_tag (state);
- g_dbus_method_invocation_return_value (invocation,
- g_variant_new ("(s)", tag));
- emit_notify_signal (connection, writer, tag, prefix, keys, i);
-
- for (j = 0; j < i; j++)
- if (values[j] != NULL)
- g_variant_unref (values[j]);
-
- g_free (values);
- g_free (keys);
- g_free (tag);
- }
-
- else
- g_assert_not_reached ();
-}
-
-static void
-writer_info_method (GDBusConnection *connection,
- const gchar *sender,
- const gchar *object_path,
- const gchar *interface_name,
- const gchar *method_name,
- GVariant *parameters,
- GDBusMethodInvocation *invocation,
- gpointer user_data)
-{
- DConfState *state = user_data;
-
- /* only record this if it's the first */
- if G_UNLIKELY (state->blame_mode && state->blame_info == NULL)
- gather_blame_info (state, connection, sender, object_path, method_name, parameters);
-
- if (g_str_equal (method_name, "Blame"))
- {
- if (state->blame_info == NULL)
- state->blame_info = g_string_new ("DCONF_BLAME is not in the environment of dconf-service\n");
-
- g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", state->blame_info->str));
- }
-
- else
- g_assert_not_reached ();
-}
-
-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)
-{
- DConfState *state = 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 (state, 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 = {
- writer_info_method, NULL
- };
-
- *out_user_data = state;
- 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)
-{
- /* The root node supports only the info iface */
- if (node == NULL)
- {
- const GDBusInterfaceInfo *interfaces[] = {
- &ca_desrt_dconf_WriterInfo, NULL
- };
-
- return g_memdup (interfaces, sizeof interfaces);
- }
- else
- {
- const GDBusInterfaceInfo *interfaces[] = {
- &ca_desrt_dconf_WriterInfo, &ca_desrt_dconf_Writer, NULL
- };
-
- return g_memdup (interfaces, sizeof interfaces);
- }
-}
-
-static void
-bus_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- static GDBusSubtreeVTable vtable = {
- subtree_enumerate, subtree_introspect, subtree_dispatch
- };
- DConfState *state = user_data;
- GDBusSubtreeFlags flags;
-
- dconf_state_set_id (state, g_dbus_connection_get_unique_name (connection));
-
- flags = G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES;
- g_dbus_connection_register_subtree (connection, "/ca/desrt/dconf/Writer",
- &vtable, flags, state, NULL, NULL);
-}
-
-static void
-name_acquired (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
-}
-
-static void
-name_lost (GDBusConnection *connection,
- const gchar *name,
- gpointer user_data)
-{
- fprintf (stderr, "unable to acquire name: '%s'\n", name);
- exit (1);
-}
-
-static gboolean
-exit_service (gpointer data)
-{
- DConfState *state = data;
-
- g_main_loop_quit (state->main_loop);
-
- return TRUE;
-}
-
-int
-main (void)
-{
- DConfState state;
- GBusType type;
-
- g_type_init ();
-
- dconf_state_init (&state);
-
- if (state.is_session)
- type = G_BUS_TYPE_SESSION;
- else
- type = G_BUS_TYPE_SYSTEM;
-
- g_unix_signal_add (SIGTERM, exit_service, &state);
- g_unix_signal_add (SIGINT, exit_service, &state);
- g_unix_signal_add (SIGHUP, exit_service, &state);
-
- g_bus_own_name (type, "ca.desrt.dconf", G_BUS_NAME_OWNER_FLAGS_NONE,
- bus_acquired, name_acquired, name_lost, &state, NULL);
-
- g_main_loop_run (state.main_loop);
-
- dconf_state_destroy (&state);
-
- return 0;
-}