diff options
author | Ryan Lortie <desrt@desrt.ca> | 2012-07-11 12:38:11 -0400 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2012-07-11 12:39:56 -0400 |
commit | 2108640d5a5188873cee73d0c776ab4b4def855d (patch) | |
tree | 56575d62021397d51d6b95c58de26b68395a2483 | |
parent | d25575211d27852df6fd2fbe5eaa9ed985fafbf0 (diff) | |
download | dconf-2108640d5a5188873cee73d0c776ab4b4def855d.tar.gz |
dbus-1/: rewrite against the new engine
Delete the copy of the old engine code that was kept in dbus-1/.
Split the parts that interact with libdbus-1 into a separate file called
dconf-libdbus-1.c and create a static library for it (to facilitate
testing).
Adjust the remaining code (which is now the user-facing DConfDBusClient
API) to the new engine.
-rw-r--r-- | dbus-1/.gitignore | 2 | ||||
-rw-r--r-- | dbus-1/Makefile.am | 29 | ||||
-rw-r--r-- | dbus-1/dconf-dbus-1.c | 632 | ||||
-rw-r--r-- | dbus-1/dconf-engine.c | 769 | ||||
-rw-r--r-- | dbus-1/dconf-engine.h | 144 | ||||
-rw-r--r-- | dbus-1/dconf-libdbus-1.c | 352 | ||||
-rw-r--r-- | dbus-1/dconf-libdbus-1.h | 11 |
7 files changed, 434 insertions, 1505 deletions
diff --git a/dbus-1/.gitignore b/dbus-1/.gitignore index 32072d0..36fad9e 100644 --- a/dbus-1/.gitignore +++ b/dbus-1/.gitignore @@ -1,3 +1,5 @@ +libdconf-libdbus-1.a +libdconf-libdbus-1-shared.a libdconf-dbus-1.so libdconf-dbus-1.so.0 libdconf-dbus-1.so.0.0.0 diff --git a/dbus-1/Makefile.am b/dbus-1/Makefile.am index 74ec56d..7444c17 100644 --- a/dbus-1/Makefile.am +++ b/dbus-1/Makefile.am @@ -1,7 +1,14 @@ include $(top_srcdir)/Makefile.gtester -AM_CFLAGS = -std=c89 -Wall -Wmissing-prototypes -Wwrite-strings -fPIC -DPIC -INCLUDES = -I$(top_srcdir)/common -I$(top_srcdir)/gvdb -I$(top_srcdir)/engine $(dbus_CFLAGS) $(glib_CFLAGS) +noinst_LIBRARIES = libdconf-libdbus-1.a libdconf-libdbus-1-shared.a + +libdconf_libdbus_1_a_CFLAGS = $(dbus_CFLAGS) $(glib_CFLAGS) +libdconf_libdbus_1_a_SOURCES = \ + dconf-libdbus-1.h \ + dconf-libdbus-1.c + +libdconf_libdbus_1_shared_a_CFLAGS = $(libdconf_libdbus_1_a_CFLAGS) -fPIC -DPIC +libdconf_libdbus_1_shared_a_SOURCES = $(libdconf_libdbus_1_a_SOURCES) dconf_dbus_1includedir = $(includedir)/dconf-dbus-1 dconf_dbus_1include_HEADERS = dconf-dbus-1.h @@ -12,15 +19,19 @@ pkgconfig_DATA = dconf-dbus-1.pc shlibdir = $(libdir) shlib_PROGRAMS = libdconf-dbus-1.so.0.0.0 -libdconf_dbus_1_so_0_0_0_LDADD = $(glib_LIBS) $(dbus_LIBS) ../common/libdconf-common-shared.a +libdconf_dbus_1_so_0_0_0_CFLAGS = $(dbus_CFLAGS) $(gio_CFLAGS) -fPIC -DPIC +libdconf_dbus_1_so_0_0_0_LDADD = \ + ../engine/libdconf-engine-shared.a \ + ../common/libdconf-common-hidden.a \ + ./libdconf-libdbus-1-shared.a \ + ../gvdb/libgvdb-shared.a \ + ../shm/libdconf-shm-shared.a \ + $(dbus_LIBS) \ + $(glib_LIBS) libdconf_dbus_1_so_0_0_0_LDFLAGS = -shared -Wl,-soname=libdconf-dbus-1.so.0 -libdconf_dbus_1_so_0_0_0_SOURCES = \ - dconf-engine.h \ - dconf-engine.c \ - ../gvdb/gvdb-reader.c \ - dconf-dbus-1.c +libdconf_dbus_1_so_0_0_0_SOURCES = dconf-dbus-1.c -noinst_DATA = libdconf-dbus-1.so libdconf-dbus-1.so.0 +nodist_noinst_DATA = libdconf-dbus-1.so libdconf-dbus-1.so.0 libdconf-dbus-1.so.0 libdconf-dbus-1.so: libdconf-dbus-1.so.0.0.0 $(AM_V_GEN) ln -fs libdconf-dbus-1.so.0.0.0 $@ diff --git a/dbus-1/dconf-dbus-1.c b/dbus-1/dconf-dbus-1.c index 50dc0f8..bc3aec6 100644 --- a/dbus-1/dconf-dbus-1.c +++ b/dbus-1/dconf-dbus-1.c @@ -19,465 +19,61 @@ #include "dconf-dbus-1.h" -#include <dconf-engine.h> +#include "../engine/dconf-engine.h" #include <string.h> -typedef struct _Outstanding Outstanding; - struct _DConfDBusClient { - DBusConnection *session_bus; - DBusConnection *system_bus; + DConfEngine *engine; GSList *watches; gint ref_count; - - Outstanding *outstanding; - gchar *anti_expose_tag; - - DConfEngine *engine; -}; - -static void -dconf_dbus_client_add_value_to_iter (DBusMessageIter *iter, - GVariant *value) -{ - GVariantClass class; - - class = g_variant_classify (value); - - switch (class) - { - case G_VARIANT_CLASS_BOOLEAN: - { - dbus_bool_t boolean; - - boolean = g_variant_get_boolean (value); - dbus_message_iter_append_basic (iter, 'b', &boolean); - } - break; - - case G_VARIANT_CLASS_BYTE: - case G_VARIANT_CLASS_INT16: - case G_VARIANT_CLASS_UINT16: - case G_VARIANT_CLASS_INT32: - case G_VARIANT_CLASS_UINT32: - case G_VARIANT_CLASS_INT64: - case G_VARIANT_CLASS_UINT64: - case G_VARIANT_CLASS_DOUBLE: - dbus_message_iter_append_basic (iter, class, - g_variant_get_data (value)); - break; - - case G_VARIANT_CLASS_STRING: - case G_VARIANT_CLASS_OBJECT_PATH: - case G_VARIANT_CLASS_SIGNATURE: - { - const gchar *str; - - str = g_variant_get_string (value, NULL); - dbus_message_iter_append_basic (iter, class, &str); - } - break; - - case G_VARIANT_CLASS_ARRAY: - { - const gchar *contained; - DBusMessageIter sub; - gint i, n; - - contained = g_variant_get_type_string (value) + 1; - n = g_variant_n_children (value); - dbus_message_iter_open_container (iter, 'a', contained, &sub); - for (i = 0; i < n; i++) - { - GVariant *child; - - child = g_variant_get_child_value (value, i); - dconf_dbus_client_add_value_to_iter (&sub, child); - g_variant_unref (child); - } - - dbus_message_iter_close_container (iter, &sub); - } - break; - - case G_VARIANT_CLASS_TUPLE: - { - DBusMessageIter sub; - gint i, n; - - n = g_variant_n_children (value); - dbus_message_iter_open_container (iter, 'r', NULL, &sub); - for (i = 0; i < n; i++) - { - GVariant *child; - - child = g_variant_get_child_value (value, i); - dconf_dbus_client_add_value_to_iter (&sub, child); - g_variant_unref (child); - } - - dbus_message_iter_close_container (iter, &sub); - } - break; - - case G_VARIANT_CLASS_DICT_ENTRY: - { - DBusMessageIter sub; - gint i; - - dbus_message_iter_open_container (iter, 'e', NULL, &sub); - for (i = 0; i < 2; i++) - { - GVariant *child; - - child = g_variant_get_child_value (value, i); - dconf_dbus_client_add_value_to_iter (&sub, child); - g_variant_unref (child); - } - - dbus_message_iter_close_container (iter, &sub); - } - break; - - case G_VARIANT_CLASS_VARIANT: - { - DBusMessageIter sub; - GVariant *child; - - child = g_variant_get_variant (value); - dbus_message_iter_open_container (iter, 'v', - g_variant_get_type_string (child), - &sub); - dconf_dbus_client_add_value_to_iter (&sub, child); - dbus_message_iter_close_container (iter, &sub); - g_variant_unref (child); - } - break; - - default: - g_assert_not_reached (); - } -} - -static void -dconf_dbus_client_send (DConfDBusClient *dcdbc, - DConfEngineMessage *dcem, - DBusPendingCallNotifyFunction callback, - gpointer user_data) -{ - DBusConnection *connection; - gint i; - - for (i = 0; i < dcem->n_messages; i++) - { - switch (dcem->bus_types[i]) - { - case 'e': - connection = dcdbc->session_bus; - break; - - case 'y': - connection = dcdbc->system_bus; - break; - - default: - g_assert_not_reached (); - } - - if (connection == NULL && callback != NULL) - callback (NULL, user_data); - - if (connection != NULL) - { - DBusPendingCall *pending; - DBusMessageIter diter; - DBusMessage *message; - GVariantIter giter; - GVariant *child; - - message = dbus_message_new_method_call (dcem->bus_name, - dcem->object_path, - dcem->interface_name, - dcem->method_name); - - dbus_message_iter_init_append (message, &diter); - g_variant_iter_init (&giter, dcem->parameters[i]); - - while ((child = g_variant_iter_next_value (&giter))) - { - dconf_dbus_client_add_value_to_iter (&diter, child); - g_variant_unref (child); - } - - dbus_connection_send_with_reply (connection, message, - &pending, 120000); - dbus_pending_call_set_notify (pending, callback, user_data, NULL); - dbus_message_unref (message); - } - } -} - -static GVariant * -dconf_dbus_client_send_finish (DBusPendingCall *pending) -{ - DBusMessage *message; - GVariant *result; - - if (pending == NULL) - return NULL; - - message = dbus_pending_call_steal_reply (pending); - dbus_pending_call_unref (pending); - - /* We only have to deal with two types of replies: () and (s) */ - if (dbus_message_has_signature (message, "s")) - { - dbus_message_get_args (message, NULL, - DBUS_TYPE_STRING, &result, - DBUS_TYPE_INVALID); - result = g_variant_new ("(s)", result); - } - else - result = g_variant_new ("()"); - - dbus_message_unref (message); - - return result; -} - -struct _Outstanding -{ - Outstanding *next; - - DConfDBusClient *dcdbc; - DConfEngineMessage dcem; - - gchar *set_key; - GVariant *set_value; - GTree *tree; }; -static void -dconf_dbus_client_outstanding_returned (DBusPendingCall *pending, - gpointer user_data) -{ - Outstanding *outstanding = user_data; - DConfDBusClient *dcdbc; - GVariant *reply; - - dcdbc = outstanding->dcdbc; - - /* One way or another we no longer need this hooked into the list. - */ - { - Outstanding **tmp; - - for (tmp = &dcdbc->outstanding; tmp; tmp = &(*tmp)->next) - if (*tmp == outstanding) - { - *tmp = outstanding->next; - break; - } - } - - reply = dconf_dbus_client_send_finish (pending); - - if (reply) - { - /* Success. - * - * We want to ensure that we don't emit an extra change - * notification signal when we see the signal that the service is - * about to send, so store the tag so we know to ignore it when - * the signal comes. - * - * No thread safety issue here since this variable is only - * accessed from the worker thread. - */ - g_free (dcdbc->anti_expose_tag); - - if (g_variant_is_of_type (reply, G_VARIANT_TYPE ("(s)"))) - g_variant_get_child (reply, 0, "s", dcdbc->anti_expose_tag); - - g_variant_unref (reply); - } - else - { - /* An error of some kind. - * - * We already removed the outstanding entry from the list, so the - * unmodified database is now visible to the client. Change - * notify so that they see it. - */ - if (outstanding->set_key) - /* XXX emit */; - else - /* XXX emit */; - } - - dconf_engine_message_destroy (&outstanding->dcem); - dconf_dbus_client_unref (outstanding->dcdbc); - g_free (outstanding->set_key); - - if (outstanding->set_value) - g_variant_unref (outstanding->set_value); - - if (outstanding->tree) - g_tree_unref (outstanding->tree); - - g_slice_free (Outstanding, outstanding); -} - -static void -dconf_dbus_client_queue (DConfDBusClient *dcdbc, - DConfEngineMessage *dcem, - const gchar *set_key, - GVariant *set_value, - GTree *tree) -{ - Outstanding *outstanding; - - outstanding = g_slice_new (Outstanding); - outstanding->dcdbc = dconf_dbus_client_ref (dcdbc); - outstanding->dcem = *dcem; - - outstanding->set_key = g_strdup (set_key); - outstanding->set_value = set_value ? g_variant_ref_sink (set_value) : NULL; - outstanding->tree = tree ? g_tree_ref (tree) : NULL; - - outstanding->next = dcdbc->outstanding; - dcdbc->outstanding = outstanding; - - dconf_dbus_client_send (outstanding->dcdbc, - &outstanding->dcem, - dconf_dbus_client_outstanding_returned, - outstanding); -} - -static gboolean -dconf_dbus_client_scan_outstanding_tree (GTree *tree, - const gchar *key, - gsize key_length, - gpointer *value) -{ - gchar *mykey; - - mykey = g_alloca (key_length + 1); - memcpy (mykey, key, key_length + 1); - - while (!g_tree_lookup_extended (tree, mykey, NULL, value) && - --key_length) - { - while (mykey[key_length - 1] != '/') - key_length--; - - mykey[key_length] = '\0'; - } - - return key_length != 0; -} - -static gboolean -dconf_dbus_client_scan_outstanding (DConfDBusClient *dcdbc, - const gchar *key, - GVariant **value) -{ - gboolean found = FALSE; - Outstanding *node; - gsize length; - - length = strlen (key); - - if G_LIKELY (dcdbc->outstanding == NULL) - return FALSE; - - for (node = dcdbc->outstanding; node; node = node->next) - { - if (node->set_key) - { - if (strcmp (key, node->set_key) == 0) - { - if (node->set_value != NULL) - *value = g_variant_ref (node->set_value); - else - *value = NULL; - - found = TRUE; - break; - } - } - - else - { - gpointer result; - - if (dconf_dbus_client_scan_outstanding_tree (node->tree, key, - length, &result)) - { - if (result) - *value = g_variant_ref (result); - else - *value = NULL; - - found = TRUE; - break; - } - } - } - - return found; -} - -/* Watches are reference counted because they can be held both by the - * list of watches and by the pending watch registration. In the normal - * case, the registration completes before the watch is unsubscribed - * from but it might be the case that the watch is unsubscribed from - * before the AddMatch completes. For that reason, either thing could - * be responsible for freeing the watch structure; we solve that - * ambiguity using a reference count. - * - * We just initially set it to 2, since these are the only two users. - * That way we can skip having the ref() function. - */ typedef struct { - DConfDBusClient *dcdbc; - gchar *name; + gchar *path; DConfDBusNotify notify; gpointer user_data; - guint64 initial_state; - gint ref_count; } Watch; - - -static void -dconf_dbus_emit_change (DConfDBusClient *dcdbc, - const gchar *key) +void +dconf_engine_change_notify (DConfEngine *engine, + const gchar *prefix, + const gchar * const *changes, + const gchar *tag, + gpointer user_data) { + DConfDBusClient *dcdbc = user_data; + gchar **my_changes; + gint n_changes; GSList *iter; + gint i; + + n_changes = g_strv_length ((gchar **) changes); + my_changes = g_new (gchar *, n_changes); + + for (i = 0; i < n_changes; i++) + my_changes[i] = g_strconcat (prefix, changes[i], NULL); + my_changes[i] = NULL; for (iter = dcdbc->watches; iter; iter = iter->next) { Watch *watch = iter->data; - if (g_str_has_prefix (key, watch->name)) - watch->notify (dcdbc, key, watch->user_data); + for (i = 0; i < n_changes; i++) + if (g_str_has_prefix (my_changes[i], watch->path)) + watch->notify (dcdbc, my_changes[i], watch->user_data); } + + g_strfreev (my_changes); } GVariant * dconf_dbus_client_read (DConfDBusClient *dcdbc, const gchar *key) { - GVariant *value; - - if (dconf_dbus_client_scan_outstanding (dcdbc, key, &value)) - return value; - - return dconf_engine_read (dcdbc->engine, key); + return dconf_engine_read (dcdbc->engine, NULL, key); } gboolean @@ -485,102 +81,32 @@ dconf_dbus_client_write (DConfDBusClient *dcdbc, const gchar *key, GVariant *value) { - DConfEngineMessage dcem; + DConfChangeset *changeset; + gboolean success; - if (!dconf_engine_write (dcdbc->engine, key, value, &dcem, NULL)) - return FALSE; + changeset = dconf_changeset_new_write (key, value); + success = dconf_engine_change_fast (dcdbc->engine, changeset, NULL); + dconf_changeset_unref (changeset); - dconf_dbus_client_queue (dcdbc, &dcem, key, value, NULL); - dconf_dbus_emit_change (dcdbc, key); - - return TRUE; + return success; } -static Watch * -watch_new (DConfDBusClient *dcdbc, - const gchar *name, - DConfDBusNotify notify, - gpointer user_data) +void +dconf_dbus_client_subscribe (DConfDBusClient *dcdbc, + const gchar *path, + DConfDBusNotify notify, + gpointer user_data) { Watch *watch; watch = g_slice_new (Watch); - watch->dcdbc = dconf_dbus_client_ref (dcdbc); - watch->user_data = user_data; - watch->name = g_strdup (name); + watch->path = g_strdup (path); watch->notify = notify; - - watch->initial_state = dconf_engine_get_state (dcdbc->engine); - watch->ref_count = 2; + watch->user_data = user_data; dcdbc->watches = g_slist_prepend (dcdbc->watches, watch); - return watch; -} - -static void -watch_unref (Watch *watch) -{ - if (--watch->ref_count == 0) - { - dconf_dbus_client_unref (watch->dcdbc); - g_free (watch->name); - g_slice_free (Watch, watch); - } -} - -static void -add_match_done (DBusPendingCall *pending, - gpointer user_data) -{ - Watch *watch = user_data; - GVariant *reply; - - reply = dconf_dbus_client_send_finish (pending); - - if (reply == NULL) - { - /* This is extremely unlikely to happen and it happens - * asynchronous to the user's call. Since the user doesn't know - * that it happened, we pretend that it didn't (ie: we leave the - * watch structure in the list). - */ - - g_critical ("DBus AddMatch for dconf path '%s'", watch->name); - watch_unref (watch); - - return; - } - - else - g_variant_unref (reply); /* it is just an empty tuple */ - - /* In the normal case we're done. - * - * There is a fleeting chance, however, that the database has changed - * in the meantime. In that case we can only assume that the subject - * of this watch changed in that time period and emit a signal to that - * effect. - */ - if (dconf_engine_get_state (watch->dcdbc->engine) != watch->initial_state) - watch->notify (watch->dcdbc, watch->name, watch->user_data); - - watch_unref (watch); -} - -void -dconf_dbus_client_subscribe (DConfDBusClient *dcdbc, - const gchar *name, - DConfDBusNotify notify, - gpointer user_data) -{ - DConfEngineMessage dcem; - Watch *watch; - - watch = watch_new (dcdbc, name, notify, user_data); - dconf_engine_watch (dcdbc->engine, name, &dcem); - dconf_dbus_client_send (dcdbc, &dcem, add_match_done, watch); - dconf_engine_message_destroy (&dcem); + dconf_engine_watch_fast (dcdbc->engine, path); } void @@ -588,7 +114,6 @@ dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc, DConfDBusNotify notify, gpointer user_data) { - DConfEngineMessage dcem; GSList **ptr; for (ptr = &dcdbc->watches; *ptr; ptr = &(*ptr)->next) @@ -598,12 +123,9 @@ dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc, if (watch->notify == notify && watch->user_data == user_data) { *ptr = g_slist_remove_link (*ptr, *ptr); - - dconf_engine_unwatch (dcdbc->engine, watch->name, &dcem); - dconf_dbus_client_send (dcdbc, &dcem, NULL, NULL); - dconf_engine_message_destroy (&dcem); - watch_unref (watch); - + dconf_engine_unwatch_fast (dcdbc->engine, watch->path); + g_free (watch->path); + g_slice_free (Watch, watch); return; } } @@ -614,54 +136,7 @@ dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc, gboolean dconf_dbus_client_has_pending (DConfDBusClient *dcdbc) { - return dcdbc->outstanding != NULL; -} - -static DBusHandlerResult -dconf_dbus_client_filter (DBusConnection *connection, - DBusMessage *message, - void *user_data) -{ - DConfDBusClient *dcdbc = user_data; - - if (dbus_message_is_signal (message, "ca.desrt.dconf.Writer", "Notify") && - dbus_message_has_signature (message, "sass")) - { - DBusMessageIter iter, sub; - const gchar *path, *tag; - - dbus_message_iter_init (message, &iter); - dbus_message_iter_get_basic (&iter, &path); - dbus_message_iter_next (&iter); - dbus_message_iter_recurse (&iter, &sub); - dbus_message_iter_next (&iter); - dbus_message_iter_get_basic (&iter, &tag); - dbus_message_iter_next (&iter); - - /* Only emit the event if it hasn't been anti-exposed */ - if (dcdbc->anti_expose_tag == NULL || - strcmp (tag, dcdbc->anti_expose_tag) != 0) - { - /* Empty list means that only one key changed */ - if (!dbus_message_iter_get_arg_type (&sub)) - dconf_dbus_emit_change (dcdbc, path); - - while (dbus_message_iter_get_arg_type (&sub) == 's') - { - const gchar *item; - gchar *full; - - dbus_message_iter_get_basic (&sub, &item); - full = g_strconcat (path, item, NULL); - dconf_dbus_emit_change (dcdbc, full); - g_free (full); - - dbus_message_iter_next (&sub); - } - } - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return dconf_engine_has_outstanding (dcdbc->engine); } DConfDBusClient * @@ -677,18 +152,14 @@ dconf_dbus_client_new (const gchar *profile, if (system == NULL) system = dbus_bus_get (DBUS_BUS_SYSTEM, NULL); + dconf_libdbus_1_provide_bus (G_BUS_TYPE_SESSION, session); + dconf_libdbus_1_provide_bus (G_BUS_TYPE_SYSTEM, system); + dcdbc = g_slice_new (DConfDBusClient); - dcdbc->engine = dconf_engine_new (profile); - dcdbc->system_bus = dbus_connection_ref (system); - dcdbc->session_bus = dbus_connection_ref (session); - dcdbc->anti_expose_tag = NULL; - dcdbc->outstanding = NULL; + dcdbc->engine = dconf_engine_new (dcdbc, NULL); dcdbc->watches = NULL; dcdbc->ref_count = 1; - dbus_connection_add_filter (system, dconf_dbus_client_filter, dcdbc, NULL); - dbus_connection_add_filter (session, dconf_dbus_client_filter, dcdbc, NULL); - return dcdbc; } @@ -697,12 +168,7 @@ dconf_dbus_client_unref (DConfDBusClient *dcdbc) { if (--dcdbc->ref_count == 0) { - dbus_connection_remove_filter (dcdbc->session_bus, - dconf_dbus_client_filter, dcdbc); - dbus_connection_remove_filter (dcdbc->system_bus, - dconf_dbus_client_filter, dcdbc); - dbus_connection_unref (dcdbc->session_bus); - dbus_connection_unref (dcdbc->system_bus); + g_return_if_fail (dcdbc->watches == NULL); g_slice_free (DConfDBusClient, dcdbc); } diff --git a/dbus-1/dconf-engine.c b/dbus-1/dconf-engine.c deleted file mode 100644 index f6a1f6a..0000000 --- a/dbus-1/dconf-engine.c +++ /dev/null @@ -1,769 +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> - */ - -#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> - -void -dconf_engine_message_destroy (DConfEngineMessage *dcem) -{ - gint i; - - for (i = 0; dcem->parameters[i]; i++) - g_variant_unref (dcem->parameters[i]); - g_free (dcem->parameters); -} - -void -dconf_engine_message_copy (DConfEngineMessage *orig, - DConfEngineMessage *copy) -{ - gint i, n; - - *copy = *orig; - - for (n = 0; orig->parameters[n]; n++); - copy->parameters = g_new (GVariant *, n + 1); - for (i = 0; i < n; i++) - copy->parameters[i] = g_variant_ref (orig->parameters[i]); - copy->parameters[i] = NULL; -} - -static gchar * -dconf_shmdir_from_environment (void) -{ - gchar *result; - - result = g_build_filename (g_get_user_runtime_dir (), "dconf", NULL); - - if (g_mkdir_with_parents (result, 0700) != 0) - g_critical ("unable to create '%s'; dconf will not work properly.", result); - - return result; -} - -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 = dconf_shmdir_from_environment (); - g_once_init_leave (&initialised, 1); - } - - return session_dir; -} - -struct _DConfEngine -{ - GMutex lock; - guint64 state; - - - GvdbTable **gvdbs; - GvdbTable **lock_tables; - guint8 **shm; - gchar **object_paths; - gchar *bus_types; - gchar **names; - gint n_dbs; -}; - -static void -dconf_engine_setup_user (DConfEngine *engine, - gint i) -{ - /* invariant: we never have user gvdb without shm */ - g_assert ((engine->gvdbs[i] == NULL) >= (engine->shm[i] == NULL)); - - if (engine->names[i]) - { - const gchar *session_dir = dconf_engine_get_session_dir (); - - if (session_dir) - { - gchar *filename; - gint fd; - - filename = g_build_filename (session_dir, - engine->names[i], - NULL); - fd = open (filename, O_RDWR | O_CREAT, 0600); - g_free (filename); - - if (fd >= 0) - { - if (ftruncate (fd, 1) == 0) - { - engine->shm[i] = mmap (NULL, 1, PROT_READ, MAP_SHARED, fd, 0); - - if (engine->shm[i] == MAP_FAILED) - engine->shm[i] = NULL; - } - - close (fd); - } - } - - if (engine->shm[i]) - { - gchar *filename; - - filename = g_build_filename (g_get_user_config_dir (), - "dconf", - engine->names[i], - NULL); - engine->gvdbs[i] = gvdb_table_new (filename, FALSE, NULL); - g_free (filename); - } - } - - g_assert ((engine->gvdbs[i] == NULL) >= (engine->shm[i] == NULL)); -} - -static void -dconf_engine_refresh_user (DConfEngine *engine, - gint i) -{ - g_assert ((engine->gvdbs[i] == NULL) >= (engine->shm[i] == NULL)); - - /* if we failed the first time, fail forever */ - if (engine->shm[i] && *engine->shm[i] == 1) - { - if (engine->gvdbs[i]) - { - gvdb_table_unref (engine->gvdbs[i]); - engine->gvdbs[i] = NULL; - } - - munmap (engine->shm[i], 1); - engine->shm[i] = NULL; - - dconf_engine_setup_user (engine, i); - engine->state++; - } - - g_assert ((engine->gvdbs[i] == NULL) >= (engine->shm[i] == NULL)); -} - -static void -dconf_engine_refresh_system (DConfEngine *engine, - gint i) -{ - if (engine->gvdbs[i] && !gvdb_table_is_valid (engine->gvdbs[i])) - { - if (engine->lock_tables[i]) - { - gvdb_table_unref (engine->lock_tables[i]); - engine->lock_tables[i] = NULL; - } - - gvdb_table_unref (engine->gvdbs[i]); - engine->gvdbs[i] = NULL; - } - - if (engine->gvdbs[i] == NULL) - { - gchar *filename = g_build_filename ("/etc/dconf/db", - engine->names[i], NULL); - engine->gvdbs[i] = gvdb_table_new (filename, TRUE, NULL); - if (engine->gvdbs[i] == NULL) - g_error ("Unable to open '%s', specified in dconf profile\n", - filename); - engine->lock_tables[i] = gvdb_table_get_table (engine->gvdbs[i], - ".locks"); - g_free (filename); - engine->state++; - } -} - -static void -dconf_engine_refresh (DConfEngine *engine) -{ - gint i; - - for (i = 0; i < engine->n_dbs; i++) - if (engine->bus_types[i] == 'e') - dconf_engine_refresh_user (engine, i); - else - dconf_engine_refresh_system (engine, i); -} - -static void -dconf_engine_setup (DConfEngine *engine) -{ - gint i; - - for (i = 0; i < engine->n_dbs; i++) - if (engine->bus_types[i] == 'e') - dconf_engine_setup_user (engine, i); - else - dconf_engine_refresh_system (engine, i); -} - -guint64 -dconf_engine_get_state (DConfEngine *engine) -{ - guint64 state; - - g_mutex_lock (&engine->lock); - - dconf_engine_refresh (engine); - state = engine->state; - - g_mutex_unlock (&engine->lock); - - return state; -} - -static gboolean -dconf_engine_load_profile (const gchar *profile, - gchar **bus_types, - gchar ***names, - gint *n_dbs, - GError **error) -{ - gchar *filename; - gint allocated; - char line[80]; - FILE *f; - - /* DCONF_PROFILE starting with '/' gives an absolute path to a profile */ - if (profile[0] != '/') - filename = g_build_filename ("/etc/dconf/profile", profile, NULL); - else - filename = g_strdup (profile); - - 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; - *bus_types = g_new (gchar, allocated); - *names = g_new (gchar *, allocated); - *n_dbs = 0; - - /* quick and dirty is good enough for now */ - while (fgets (line, sizeof line, f)) - { - const gchar *end; - const gchar *sep; - - 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; - *names = g_renew (gchar *, *names, allocated); - *bus_types = g_renew (gchar, *bus_types, allocated); - } - - sep = strchr (line, ':'); - - if (sep) - { - /* strings MUST be 'user-db' or 'system-db'. we do the check - * this way here merely because it is the fastest. - */ - (*bus_types)[*n_dbs] = (line[0] == 'u') ? 'e' : 'y'; - (*names)[*n_dbs] = g_strndup (sep + 1, end - (sep + 1)); - } - else - { - /* default is for first DB to be user and rest to be system */ - (*bus_types)[*n_dbs] = (*n_dbs == 0) ? 'e' : 'y'; - (*names)[*n_dbs] = g_strndup (line, end - line); - } - - (*n_dbs)++; - } - - *bus_types = g_renew (gchar, *bus_types, *n_dbs); - *names = g_renew (gchar *, *names, *n_dbs); - g_free (filename); - fclose (f); - - return TRUE; -} - -DConfEngine * -dconf_engine_new (const gchar *profile) -{ - DConfEngine *engine; - gint i; - - engine = g_slice_new (DConfEngine); - g_mutex_init (&engine->lock); - - if (profile == NULL) - profile = getenv ("DCONF_PROFILE"); - - if (profile) - { - GError *error = NULL; - - if (!dconf_engine_load_profile (profile, &engine->bus_types, &engine->names, &engine->n_dbs, &error)) - g_error ("Error loading dconf profile '%s': %s\n", - profile, error->message); - } - else - { - if (!dconf_engine_load_profile ("user", &engine->bus_types, &engine->names, &engine->n_dbs, NULL)) - { - engine->names = g_new (gchar *, 1); - engine->names[0] = g_strdup ("user"); - engine->bus_types = g_strdup ("e"); - engine->n_dbs = 1; - } - } - - if (strcmp (engine->names[0], "-") == 0) - { - g_free (engine->names[0]); - engine->names[0] = NULL; - } - - engine->object_paths = g_new (gchar *, engine->n_dbs); - engine->gvdbs = g_new0 (GvdbTable *, engine->n_dbs); - engine->lock_tables = g_new0 (GvdbTable *, engine->n_dbs); - engine->shm = g_new0 (guint8 *, engine->n_dbs); - engine->state = 0; - - for (i = 0; i < engine->n_dbs; i++) - if (engine->names[i]) - engine->object_paths[i] = g_strjoin (NULL, - "/ca/desrt/dconf/Writer/", - engine->names[i], - NULL); - else - engine->object_paths[i] = NULL; - - dconf_engine_setup (engine); - - return engine; -} - -void -dconf_engine_free (DConfEngine *engine) -{ - gint i; - - for (i = 0; i < engine->n_dbs; i++) - { - g_free (engine->object_paths[i]); - g_free (engine->names[i]); - - if (engine->gvdbs[i]) - gvdb_table_unref (engine->gvdbs[i]); - - if (engine->lock_tables[i]) - gvdb_table_unref (engine->lock_tables[i]); - - if (engine->shm[i]) - munmap (engine->shm[i], 1); - } - - - g_mutex_clear (&engine->lock); - - g_free (engine->object_paths); - g_free (engine->bus_types); - g_free (engine->names); - g_free (engine->gvdbs); - g_free (engine->lock_tables); - g_free (engine->shm); - - g_slice_free (DConfEngine, engine); -} - -static GVariant * -dconf_engine_read_internal (DConfEngine *engine, - const gchar *key, - gboolean user, - gboolean system) -{ - GVariant *value = NULL; - gint lowest; - gint limit; - gint i; - - g_mutex_lock (&engine->lock); - - dconf_engine_refresh (engine); - - /* Bound the search space depending on the databases that we are - * interested in. - */ - limit = system ? engine->n_dbs : 1; - lowest = user ? 0 : 1; - - /* We want i equal to the index of the highest database containing a - * lock, or i == lowest if there is no lock. For that reason, we - * don't actually check the lowest database for a lock. That makes - * sense, because even if it had a lock, it would not change our - * search policy (which would be to check the lowest one first). - * - * Note that we intentionally dishonour 'limit' here -- we want to - * ensure that values in the user database are always ignored when - * locks are present. - */ - for (i = MAX (engine->n_dbs - 1, lowest); lowest < i; i--) - if (engine->lock_tables[i] != NULL && - gvdb_table_has_value (engine->lock_tables[i], key)) - break; - - while (i < limit && value == NULL) - { - if (engine->gvdbs[i] != NULL) - value = gvdb_table_get_value (engine->gvdbs[i], key); - i++; - } - - g_mutex_unlock (&engine->lock); - - return value; -} - -GVariant * -dconf_engine_read (DConfEngine *engine, - const gchar *key) -{ - return dconf_engine_read_internal (engine, key, TRUE, TRUE); -} - -GVariant * -dconf_engine_read_default (DConfEngine *engine, - const gchar *key) -{ - return dconf_engine_read_internal (engine, key, FALSE, TRUE); -} - -GVariant * -dconf_engine_read_no_default (DConfEngine *engine, - const gchar *key) -{ - return dconf_engine_read_internal (engine, key, TRUE, FALSE); -} - -static void -dconf_engine_make_match_rule (DConfEngine *engine, - DConfEngineMessage *dcem, - const gchar *name, - const gchar *method_name) -{ - gint i; - - dcem->bus_name = "org.freedesktop.DBus"; - dcem->object_path = "/org/freedesktop/DBus"; - dcem->interface_name = "org.freedesktop.DBus"; - dcem->method_name = method_name; - - dcem->parameters = g_new (GVariant *, engine->n_dbs + 1); - for (i = 0; i < engine->n_dbs; i++) - { - gchar *rule; - - rule = g_strdup_printf ("type='signal'," - "interface='ca.desrt.dconf.Writer'," - "path='%s'," - "arg0path='%s'", - engine->object_paths[i], - name); - dcem->parameters[i] = g_variant_new ("(s)", rule); - g_variant_ref_sink (dcem->parameters[i]); - g_free (rule); - } - dcem->parameters[i] = NULL; - - dcem->bus_types = engine->bus_types; - dcem->n_messages = engine->n_dbs; - dcem->reply_type = G_VARIANT_TYPE_UNIT; -} - -void -dconf_engine_watch (DConfEngine *engine, - const gchar *name, - DConfEngineMessage *dcem) -{ - dconf_engine_make_match_rule (engine, dcem, name, "AddMatch"); -} - -void -dconf_engine_unwatch (DConfEngine *engine, - const gchar *name, - DConfEngineMessage *dcem) -{ - dconf_engine_make_match_rule (engine, dcem, name, "RemoveMatch"); -} - -gboolean -dconf_engine_is_writable (DConfEngine *engine, - const gchar *name) -{ - gboolean writable = TRUE; - - /* Only check if we have more than one database */ - if (engine->n_dbs > 1) - { - gint i; - - g_mutex_lock (&engine->lock); - - dconf_engine_refresh (engine); - - /* Don't check for locks in the top database (i == 0). */ - for (i = engine->n_dbs - 1; 0 < i; i--) - if (engine->lock_tables[i] != NULL && - gvdb_table_has_value (engine->lock_tables[i], name)) - { - writable = FALSE; - break; - } - - g_mutex_unlock (&engine->lock); - } - - return writable; -} - -/* be conservative and fast: false negatives are OK */ -static gboolean -is_dbusable (GVariant *value) -{ - const gchar *type; - - type = g_variant_get_type_string (value); - - /* maybe definitely won't work. - * variant? too lazy to check inside... - */ - if (strchr (type, 'v') || strchr (type, 'm')) - return FALSE; - - /* XXX: we could also check for '{}' not inside an array... - * but i'm not sure we want to support that anyway. - */ - - /* this will avoid any too-deeply-nested limits */ - return strlen (type) < 32; -} - -static GVariant * -fake_maybe (GVariant *value) -{ - GVariantBuilder builder; - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("av")); - - if (value != NULL) - { - if (is_dbusable (value)) - g_variant_builder_add (&builder, "v", value); - - else - { - GVariant *variant; - GVariant *ay; - - variant = g_variant_new_variant (value); - ay = g_variant_new_from_data (G_VARIANT_TYPE_BYTESTRING, - g_variant_get_data (variant), - g_variant_get_size (variant), - TRUE, - (GDestroyNotify) g_variant_unref, - variant); - g_variant_builder_add (&builder, "v", ay); - - g_variant_builder_add (&builder, "v", - g_variant_new_string ("serialised GVariant")); - } - } - - return g_variant_builder_end (&builder); -} - -static void -dconf_engine_dcem (DConfEngine *engine, - DConfEngineMessage *dcem, - const gchar *method_name, - const gchar *format_string, - ...) -{ - va_list ap; - - dcem->bus_name = "ca.desrt.dconf"; - dcem->object_path = engine->object_paths[0]; - dcem->interface_name = "ca.desrt.dconf.Writer"; - dcem->method_name = method_name; - dcem->parameters = g_new (GVariant *, 2); - dcem->n_messages = 1; - - va_start (ap, format_string); - dcem->parameters[0] = g_variant_new_va (format_string, NULL, &ap); - g_variant_ref_sink (dcem->parameters[0]); - dcem->parameters[1] = NULL; - va_end (ap); - - dcem->bus_types = engine->bus_types; - dcem->reply_type = G_VARIANT_TYPE ("(s)"); -} - -gboolean -dconf_engine_write (DConfEngine *engine, - const gchar *name, - GVariant *value, - DConfEngineMessage *dcem, - GError **error) -{ - dconf_engine_dcem (engine, dcem, - "Write", "(s@av)", - name, fake_maybe (value)); - - return TRUE; -} - -gboolean -dconf_engine_write_many (DConfEngine *engine, - const gchar *prefix, - const gchar * const *keys, - GVariant **values, - DConfEngineMessage *dcem, - GError **error) -{ - GVariantBuilder builder; - gsize i; - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sav)")); - - for (i = 0; keys[i]; i++) - g_variant_builder_add (&builder, "(s@av)", - keys[i], fake_maybe (values[i])); - - dconf_engine_dcem (engine, dcem, "WriteMany", "(sa(sav))", prefix, &builder); - - return TRUE; -} - -gchar ** -dconf_engine_list (DConfEngine *engine, - const gchar *dir, - gint *length) -{ - gchar **list; - - g_mutex_lock (&engine->lock); - - dconf_engine_refresh (engine); - - if (engine->gvdbs[0]) - list = gvdb_table_list (engine->gvdbs[0], dir); - else - list = NULL; - - if (list == NULL) - list = g_new0 (char *, 1); - - if (length) - *length = g_strv_length (list); - - g_mutex_unlock (&engine->lock); - - return list; -} - -gboolean -dconf_engine_decode_notify (DConfEngine *engine, - const gchar *anti_expose, - const gchar **path, - const gchar ***rels, - guint bus_type, - const gchar *sender, - const gchar *iface, - const gchar *method, - GVariant *body) -{ - if (strcmp (iface, "ca.desrt.dconf.Writer") || strcmp (method, "Notify")) - return FALSE; - - if (!g_variant_is_of_type (body, G_VARIANT_TYPE ("(sass)"))) - return FALSE; - - if (anti_expose) - { - const gchar *ae; - - g_variant_get_child (body, 2, "&s", &ae); - - if (strcmp (ae, anti_expose) == 0) - return FALSE; - } - - g_variant_get (body, "(&s^a&ss)", path, rels, NULL); - - return TRUE; -} - -gboolean -dconf_engine_decode_writability_notify (const gchar **path, - const gchar *iface, - const gchar *method, - GVariant *body) -{ - if (strcmp (iface, "ca.desrt.dconf.Writer") || - strcmp (method, "WritabilityNotify")) - return FALSE; - - if (!g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)"))) - return FALSE; - - g_variant_get_child (body, 0, "&s", path); - - return TRUE; -} diff --git a/dbus-1/dconf-engine.h b/dbus-1/dconf-engine.h deleted file mode 100644 index 25de43d..0000000 --- a/dbus-1/dconf-engine.h +++ /dev/null @@ -1,144 +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_engine_h__ -#define __dconf_engine_h__ - -#include <glib.h> - -typedef struct _DConfEngine DConfEngine; - -/** - * DConfEngineMessage: - * - * This structure represents a number of DBus method call messages that #DConfEngine would like to send. - * - * #DConfEngine itself is unaware of a particular DBus or main loop implementation. As such, all requests are - * synchronous and non-blocking, but most of them produce a #DConfEngineMessage describing messages that must be - * sent in order for the operation to be completed. - * - * @bus_name, @object_path, @interface_name, @method_name specify the respective header fields of the method - * call. These are always equal for all of the calls contained within a single #DConfEngineMessage. - * - * @reply_type is the expected reply type of the method call. This is also the same for all calls contained - * within a single #DConfEngineMessage. - * - * @n_messages is the number of messages to send. - * - * @bus_types and @parameters are both arrays, of length @n_messages. Each element of @bus_type is the bus type - * to send each method call on and each of @parameters is the body of that call. The reason that there may be - * several messages is that a single dconf "watch" operation may need to send multiple DBus "AddMatch" calls - * (and usually to multiple busses). - * - * Each element in @bus_types is either 'y' for system bus or 'e' for session bus. - * - * A #DConfEngineMessage is always stack-allocated by the caller. It must be cleared using - * dconf_engine_message_destroy() when done. It may be copied using dconf_engine_message_copy(). - */ -typedef struct -{ - const gchar *bus_name; - const gchar *object_path; - const gchar *interface_name; - const gchar *method_name; - - gint n_messages; - GVariant **parameters; - const gchar *bus_types; - - const GVariantType *reply_type; -} DConfEngineMessage; - -G_GNUC_INTERNAL -void dconf_engine_message_copy (DConfEngineMessage *orig, - DConfEngineMessage *copy); -G_GNUC_INTERNAL -void dconf_engine_message_destroy (DConfEngineMessage *message); - -G_GNUC_INTERNAL -DConfEngine * dconf_engine_new (const gchar *profile); -G_GNUC_INTERNAL -DConfEngine * dconf_engine_new_for_db (const gchar *db_name); -G_GNUC_INTERNAL -guint64 dconf_engine_get_state (DConfEngine *engine); - -G_GNUC_INTERNAL -void dconf_engine_free (DConfEngine *engine); - -G_GNUC_INTERNAL -GVariant * dconf_engine_read (DConfEngine *engine, - const gchar *key); -G_GNUC_INTERNAL -GVariant * dconf_engine_read_default (DConfEngine *engine, - const gchar *key); -G_GNUC_INTERNAL -GVariant * dconf_engine_read_no_default (DConfEngine *engine, - const gchar *key); -G_GNUC_INTERNAL -gchar ** dconf_engine_list (DConfEngine *engine, - const gchar *path, - gint *length); - -G_GNUC_INTERNAL -void dconf_engine_get_service_info (DConfEngine *engine, - const gchar **bus_type, - const gchar **destination, - const gchar **object_path); -G_GNUC_INTERNAL -gboolean dconf_engine_is_writable (DConfEngine *engine, - const gchar *name); -G_GNUC_INTERNAL -gboolean dconf_engine_write (DConfEngine *engine, - const gchar *key, - GVariant *value, - DConfEngineMessage *message, - GError **error); -G_GNUC_INTERNAL -gboolean dconf_engine_write_many (DConfEngine *engine, - const gchar *prefix, - const gchar * const *keys, - GVariant **values, - DConfEngineMessage *message, - GError **error); -G_GNUC_INTERNAL -void dconf_engine_watch (DConfEngine *engine, - const gchar *name, - DConfEngineMessage *message); -G_GNUC_INTERNAL -void dconf_engine_unwatch (DConfEngine *engine, - const gchar *name, - DConfEngineMessage *message); -G_GNUC_INTERNAL -gboolean dconf_engine_decode_notify (DConfEngine *engine, - const gchar *anti_expose, - const gchar **prefix, - const gchar ***keys, - guint bus_type, - const gchar *sender, - const gchar *interface, - const gchar *member, - GVariant *body); -G_GNUC_INTERNAL -gboolean dconf_engine_decode_writability_notify (const gchar **path, - const gchar *iface, - const gchar *method, - GVariant *body); -#endif /* __dconf_engine_h__ */ diff --git a/dbus-1/dconf-libdbus-1.c b/dbus-1/dconf-libdbus-1.c new file mode 100644 index 0000000..aedc2cf --- /dev/null +++ b/dbus-1/dconf-libdbus-1.c @@ -0,0 +1,352 @@ +/** + * Copyright © 2010 Canonical Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the licence, or (at + * your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Ryan Lortie <desrt@desrt.ca> + **/ + +#include "dconf-libdbus-1.h" + +#include "../engine/dconf-engine.h" + +#include <string.h> + +static DBusConnection *dconf_libdbus_1_buses[5]; + +struct _DConfDBusClient +{ + DConfEngine *engine; + GSList *watches; + gint ref_count; +}; + +#define DCONF_LIBDBUS_1_ERROR (g_quark_from_static_string("DCONF_LIBDBUS_1_ERROR")) +#define DCONF_LIBDBUS_1_ERROR_FAILED 0 + +static DBusMessage * +dconf_libdbus_1_new_method_call (const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters) +{ + DBusMessageIter dbus_iter; + DBusMessage *message; + GVariantIter iter; + GVariant *child; + + g_variant_ref_sink (parameters); + + message = dbus_message_new_method_call (bus_name, object_path, interface_name, method_name); + dbus_message_iter_init_append (message, &dbus_iter); + g_variant_iter_init (&iter, parameters); + + while ((child = g_variant_iter_next_value (&iter))) + { + if (g_variant_is_of_type (child, G_VARIANT_TYPE_STRING)) + { + const gchar *str; + + str = g_variant_get_string (child, NULL); + dbus_message_iter_append_basic (&dbus_iter, DBUS_TYPE_STRING, &str); + } + + else + { + const guint8 *bytes; + gsize n_elements; + + g_assert (g_variant_is_of_type (child, G_VARIANT_TYPE_BYTESTRING)); + + bytes = g_variant_get_fixed_array (child, &n_elements, sizeof (guint8)); + dbus_message_iter_append_fixed_array (&dbus_iter, DBUS_TYPE_BYTE, &bytes, n_elements); + } + + g_variant_unref (child); + } + + g_variant_unref (parameters); + + return message; +} + +static GVariant * +dconf_libdbus_1_get_message_body (DBusMessage *message, + GError **error) +{ + GVariantBuilder builder; + const gchar *signature; + DBusMessageIter iter; + + /* We support two types: strings and arrays of strings. + * + * It's very simple to detect if the message contains only these + * types: check that the signature contains only the letters "a" and + * "s" and that it does not contain "aa". + */ + signature = dbus_message_get_signature (message); + if (signature[strspn(signature, "as")] != '\0' || strstr (signature, "aa")) + { + g_set_error (error, DCONF_LIBDBUS_1_ERROR, DCONF_LIBDBUS_1_ERROR_FAILED, + "unable to handle message type '(%s)'", signature); + return NULL; + } + + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); + dbus_message_iter_init (message, &iter); + while (dbus_message_iter_get_arg_type (&iter)) + { + const gchar *string; + + if (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) + { + dbus_message_iter_get_basic (&iter, &string); + g_variant_builder_add (&builder, "s", string); + } + else + { + DBusMessageIter sub; + + g_assert (dbus_message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY && + dbus_message_iter_get_element_type (&iter) == DBUS_TYPE_STRING); + + g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY); + dbus_message_iter_recurse (&iter, &sub); + + while (dbus_message_iter_get_arg_type (&sub)) + { + const gchar *string; + dbus_message_iter_get_basic (&sub, &string); + g_variant_builder_add (&builder, "s", string); + dbus_message_iter_next (&sub); + } + + g_variant_builder_close (&builder); + } + dbus_message_iter_next (&iter); + } + + return g_variant_ref_sink (g_variant_builder_end (&builder)); +} + +static GVariant * +dconf_libdbus_1_interpret_result (DBusMessage *result, + const GVariantType *expected_type, + GError **error) +{ + GVariant *reply; + + if (dbus_message_get_type (result) == DBUS_MESSAGE_TYPE_ERROR) + { + const gchar *errstr = "(no message)"; + + dbus_message_get_args (result, NULL, DBUS_TYPE_STRING, &errstr, DBUS_TYPE_INVALID); + g_set_error (error, DCONF_LIBDBUS_1_ERROR, DCONF_LIBDBUS_1_ERROR_FAILED, + "%s: %s", dbus_message_get_error_name (result), errstr); + return NULL; + } + + reply = dconf_libdbus_1_get_message_body (result, error); + + if (reply && expected_type && !g_variant_is_of_type (reply, expected_type)) + { + gchar *expected_string; + + expected_string = g_variant_type_dup_string (expected_type); + g_set_error (error, DCONF_LIBDBUS_1_ERROR, DCONF_LIBDBUS_1_ERROR_FAILED, + "received reply '%s' is not of the expected type %s", + g_variant_get_type_string (reply), expected_string); + g_free (expected_string); + + g_variant_unref (reply); + reply = NULL; + } + + return reply; +} + +static void +dconf_libdbus_1_method_call_done (DBusPendingCall *pending, + gpointer user_data) +{ + DConfEngineCallHandle *handle = user_data; + const GVariantType *expected_type; + DBusMessage *message; + GError *error = NULL; + GVariant *reply; + + if (pending == NULL) + return; + + message = dbus_pending_call_steal_reply (pending); + dbus_pending_call_unref (pending); + + expected_type = dconf_engine_call_handle_get_expected_type (handle); + reply = dconf_libdbus_1_interpret_result (message, expected_type, &error); + dbus_message_unref (message); + + dconf_engine_call_handle_reply (handle, reply, error); + + if (reply) + g_variant_unref (reply); + if (error) + g_error_free (error); +} + +gboolean +dconf_engine_dbus_call_async_func (GBusType bus_type, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + DConfEngineCallHandle *handle, + GError **error) +{ + DBusConnection *connection; + DBusPendingCall *pending; + DBusMessage *message; + + g_assert_cmpint (bus_type, <, G_N_ELEMENTS (dconf_libdbus_1_buses)); + connection = dconf_libdbus_1_buses[bus_type]; + g_assert (connection != NULL); + + message = dconf_libdbus_1_new_method_call (bus_name, object_path, interface_name, method_name, parameters); + dbus_connection_send_with_reply (connection, message, &pending, -1); + dbus_pending_call_set_notify (pending, dconf_libdbus_1_method_call_done, handle, NULL); + dbus_message_unref (message); + + return TRUE; +} + +static void +dconf_libdbus_1_convert_error (DBusError *dbus_error, + GError **error) +{ + g_set_error (error, DCONF_LIBDBUS_1_ERROR, DCONF_LIBDBUS_1_ERROR_FAILED, + "%s: %s", dbus_error->name, dbus_error->message); +} + +GVariant * +dconf_engine_dbus_call_sync_func (GBusType bus_type, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *expected_type, + GError **error) +{ + DBusConnection *connection; + DBusMessage *message; + DBusError dbus_error; + DBusMessage *result; + GVariant *reply; + + g_assert_cmpint (bus_type, <, G_N_ELEMENTS (dconf_libdbus_1_buses)); + connection = dconf_libdbus_1_buses[bus_type]; + g_assert (connection != NULL); + + dbus_error_init (&dbus_error); + message = dconf_libdbus_1_new_method_call (bus_name, object_path, interface_name, method_name, parameters); + result = dbus_connection_send_with_reply_and_block (connection, message, -1, &dbus_error); + dbus_message_unref (message); + + if (result == NULL) + { + dconf_libdbus_1_convert_error (&dbus_error, error); + dbus_error_free (&dbus_error); + return NULL; + } + + reply = dconf_libdbus_1_interpret_result (result, expected_type, error); + dbus_message_unref (result); + + return reply; +} + +static DBusHandlerResult +dconf_libdbus_1_filter (DBusConnection *connection, + DBusMessage *message, + gpointer user_data) +{ + GBusType bus_type = GPOINTER_TO_INT (user_data); + + if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL) + { + const gchar *interface; + + interface = dbus_message_get_interface (message); + + if (interface && g_str_equal (interface, "ca.desrt.dconf.Writer")) + { + GVariant *parameters; + + parameters = dconf_libdbus_1_get_message_body (message, NULL); + + if (parameters != NULL) + { + dconf_engine_handle_dbus_signal (bus_type, + dbus_message_get_sender (message), + dbus_message_get_path (message), + dbus_message_get_member (message), + parameters); + g_variant_unref (parameters); + } + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +void +dconf_libdbus_1_provide_bus (GBusType bus_type, + DBusConnection *connection) +{ + g_assert_cmpint (bus_type, <, G_N_ELEMENTS (dconf_libdbus_1_buses)); + + if (!dconf_libdbus_1_buses[bus_type]) + { + dconf_libdbus_1_buses[bus_type] = dbus_connection_ref (connection); + dbus_connection_add_filter (connection, dconf_libdbus_1_filter, GINT_TO_POINTER (bus_type), NULL); + } +} + +#ifndef PIC +static gboolean +dconf_libdbus_1_check_connection (gpointer user_data) +{ + DBusConnection *connection = user_data; + + dbus_connection_read_write (connection, 0); + dbus_connection_dispatch (connection); + + return G_SOURCE_CONTINUE; +} + +void +dconf_engine_dbus_init_for_testing (void) +{ + DBusConnection *session; + DBusConnection *system; + + dconf_libdbus_1_provide_bus (G_BUS_TYPE_SESSION, session = dbus_bus_get (DBUS_BUS_SESSION, NULL)); + dconf_libdbus_1_provide_bus (G_BUS_TYPE_SYSTEM, system = dbus_bus_get (DBUS_BUS_SYSTEM, NULL)); + + /* "mainloop integration" */ + g_timeout_add (1, dconf_libdbus_1_check_connection, session); + g_timeout_add (1, dconf_libdbus_1_check_connection, system); +} +#endif diff --git a/dbus-1/dconf-libdbus-1.h b/dbus-1/dconf-libdbus-1.h new file mode 100644 index 0000000..201096a --- /dev/null +++ b/dbus-1/dconf-libdbus-1.h @@ -0,0 +1,11 @@ +#ifndef __dconf_libdbus_1_h__ +#define __dconf_libdbus_1_h__ + +#include <dbus/dbus.h> +#include <gio/gio.h> + +G_GNUC_INTERNAL +void dconf_libdbus_1_provide_bus (GBusType bus_type, + DBusConnection *connection); + +#endif /* __dconf_libdbus_1_h__ */ |