diff options
Diffstat (limited to 'dbus-1/dconf-dbus-1.c')
-rw-r--r-- | dbus-1/dconf-dbus-1.c | 633 |
1 files changed, 50 insertions, 583 deletions
diff --git a/dbus-1/dconf-dbus-1.c b/dbus-1/dconf-dbus-1.c index 50dc0f8..bbc8b77 100644 --- a/dbus-1/dconf-dbus-1.c +++ b/dbus-1/dconf-dbus-1.c @@ -19,465 +19,62 @@ #include "dconf-dbus-1.h" -#include <dconf-engine.h> +#include "../engine/dconf-engine.h" +#include "dconf-libdbus-1.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 +82,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 +115,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 +124,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 +137,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 +153,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 +169,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); } |