[seed] Start implementing DBus module



commit 61d504d0d9bfe939a9f2d065d0c1fd1eced9dce8
Author: Robert Carr <racarr svn gnome org>
Date:   Thu May 7 23:27:35 2009 -0400

    Start implementing DBus module
---
 configure.ac                     |   16 +-
 modules/Makefile.am              |    2 +-
 modules/dbus/Makefile.am         |   31 +
 modules/dbus/module.c            |   69 +
 modules/dbus/util/dbus-private.h |   55 +
 modules/dbus/util/dbus-proxy.c   |  666 +++++++++
 modules/dbus/util/dbus-proxy.h   |   74 +
 modules/dbus/util/dbus-signals.c | 1318 +++++++++++++++++
 modules/dbus/util/dbus.c         | 3020 ++++++++++++++++++++++++++++++++++++++
 modules/dbus/util/dbus.h         |  216 +++
 10 files changed, 5465 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 4826ae8..1cb8e45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -153,7 +153,20 @@ AM_CONDITIONAL(BUILD_EXAMPLE_MODULE, test "x$want_example_module" = "xyes")
 AC_SUBST(BUILD_EXAMPLE_MODULE)
 
 dnl ==== dbus? ====
-want_dbus_modules="yes"
+AC_ARG_ENABLE(dbus-module,
+			  AC_HELP_STRING([--enable-dbus-module],
+							 [enable the dbus Seed module. [default=yes]]),
+			  [want_dbus_module=$enableval],[want_dbus_module="yes"])
+
+
+AM_CONDITIONAL(BUILD_DBUS_MODULE, test "x$want_dbus_module" = "xyes")
+AC_SUBST(BUILD_DBUS_MODULE)
+
+if test x"$want_dbus_module" == x"yes" ; then
+	PKG_CHECK_MODULES(DBUS, dbus-1)
+	AC_SUBST(DBUS_CFLAGS)
+	AC_SUBST(DBUS_LDFLAGS)
+fi
 
 dnl ==== os ====
 AC_ARG_ENABLE(os-module,
@@ -304,6 +317,7 @@ modules/readline/Makefile
 modules/Multiprocessing/Makefile
 modules/sandbox/Makefile
 modules/os/Makefile
+modules/dbus/Makefile
 ])
 AC_OUTPUT
 
diff --git a/modules/Makefile.am b/modules/Makefile.am
index f677ce8..66623be 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -1 +1 @@
-SUBDIRS = example sqlite canvas Multiprocessing readline os sandbox
+SUBDIRS = example sqlite canvas Multiprocessing readline os sandbox dbus
diff --git a/modules/dbus/Makefile.am b/modules/dbus/Makefile.am
new file mode 100644
index 0000000..c6f47e9
--- /dev/null
+++ b/modules/dbus/Makefile.am
@@ -0,0 +1,31 @@
+if BUILD_DBUS_MODULE
+
+seedlibdir = ${exec_prefix}/lib/seed
+
+seedlib_LTLIBRARIES = \
+	libdbus.la
+
+libdbus_la_SOURCES = \
+	module.c \
+	util/dbus.c \
+	util/dbus-proxy.c \
+	util/dbus-signals.c
+
+
+libdbus_la_CFLAGS = \
+	-I top_srcdir@/libseed/ \
+	$(GOBJECT_INTROSPECTION_CFLAGS) \
+	$(DBUS_CFLAGS) \
+	$(SEED_DEBUG_CFLAGS) \
+	$(SEED_PROFILE_CFLAGS)
+
+
+libdbus_la_LDFLAGS = \
+	$(GOBJECT_INTROSPECTION_LDFLAGS) \
+	$(SEED_PROFILE_LIBS) \
+	$(DBUS_LDFLAGS)
+
+endif
+
+
+
diff --git a/modules/dbus/module.c b/modules/dbus/module.c
new file mode 100644
index 0000000..a01a496
--- /dev/null
+++ b/modules/dbus/module.c
@@ -0,0 +1,69 @@
+#include <seed.h>
+
+#include "util/dbus.h"
+
+SeedContext ctx;
+SeedContextGroup group;
+
+SeedObject namespace_ref;
+
+static gboolean session_bus_weakref_added = FALSE;
+static DBusConnection *session_bus = NULL;
+static gboolean system_bus_weakref_added = FALSE;
+static DBusConnection *system_bus = NULL;
+
+static SeedValue
+seed_js_dbus_signature_length (SeedContext ctx,
+			       SeedObject function,
+			       SeedObject this_object,
+			       size_t argument_count,
+			       const SeedValue arguments[],
+			       SeedException *exception)
+{
+  const gchar *signature;
+  DBusSignatureIter iter;
+  gint length = 0;
+  
+  if (argument_count < 1) 
+    {
+      seed_make_exception (ctx, exception, "ArgumentError", 
+			   "dbus.signatureLength expected 1 argument, got %d", argument_count);
+      return seed_make_null (ctx);
+    }
+  
+  signature = seed_value_to_string (ctx, arguments[0], exception);
+  
+  if (!dbus_signature_validate (signature, NULL)) {
+    seed_make_exception (ctx, exception, "ArgumentError",
+			 "Invalid signature");
+    return seed_make_null (ctx);
+  }
+  
+  if (*signature == '\0')
+    return seed_value_from_int (ctx, 0, exception);
+  
+  dbus_signature_iter_init (&iter, signature);
+  do {
+    length++;
+  } while (dbus_signature_iter_next (&iter));
+  
+  return seed_value_from_int (ctx, length, exception);
+}
+
+SeedObject
+seed_module_init(SeedEngine * eng)
+{
+  ctx = eng->context;
+  group = eng->group;
+  namespace_ref = seed_make_object (eng->context, NULL, NULL);
+  
+  seed_object_set_property (ctx, namespace_ref, "BUS_SESSION", seed_value_from_int (ctx, DBUS_BUS_SESSION, NULL));
+  seed_object_set_property (ctx, namespace_ref, "BUS_SYSTEM", seed_value_from_int (ctx, DBUS_BUS_SYSTEM, NULL));
+  seed_object_set_property (ctx, namespace_ref, "BUS_STARTER", seed_value_from_int (ctx, DBUS_BUS_STARTER, NULL));
+  
+  seed_create_function(ctx, "signatureLength", 
+		       (SeedFunctionCallback)seed_js_dbus_signature_length,
+		       namespace_ref);
+  
+  return namespace_ref;
+}
diff --git a/modules/dbus/util/dbus-private.h b/modules/dbus/util/dbus-private.h
new file mode 100644
index 0000000..9a31321
--- /dev/null
+++ b/modules/dbus/util/dbus-private.h
@@ -0,0 +1,55 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#ifndef __BIG_UTIL_DBUS_PRIVATE_H__
+#define __BIG_UTIL_DBUS_PRIVATE_H__
+
+#include <glib.h>
+#include <util/dbus.h>
+#include <util/dbus-proxy.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+    DBusBusType bus_type;
+    void *where_connection_was;
+    BigDBusProxy *driver_proxy;
+    GHashTable *json_ifaces;
+    GSList *name_ownership_monitors;
+    GHashTable *name_watches;
+
+    GSList *all_signal_watchers;
+
+    /* These signal watcher tables are maps from a
+     * string to a GSList of BigSignalWatcher,
+     * and they are lazily created if a signal watcher
+     * needs to be looked up by the given key.
+     */
+    GHashTable *signal_watchers_by_unique_sender;
+    GHashTable *signal_watchers_by_path;
+    GHashTable *signal_watchers_by_iface;
+    GHashTable *signal_watchers_by_signal;
+    /* These are matching on well-known name only,
+     * or watching all signals
+     */
+    GSList     *signal_watchers_in_no_table;
+
+} BigDBusInfo;
+
+BigDBusInfo*      _big_dbus_ensure_info                     (DBusConnection *connection);
+void              _big_dbus_dispose_info                    (DBusConnection *connection);
+void              _big_dbus_process_pending_signal_watchers (DBusConnection *connection,
+                                                             BigDBusInfo    *info);
+DBusHandlerResult _big_dbus_signal_watch_filter_message     (DBusConnection *connection,
+                                                             DBusMessage    *message,
+                                                             void           *data);
+void              _big_dbus_set_matching_name_owner_changed (DBusConnection *connection,
+                                                             const char     *bus_name,
+                                                             gboolean        matched);
+void              _big_dbus_ensure_connect_idle             (DBusBusType     bus_type);
+DBusConnection*   _big_dbus_get_weak_ref                    (DBusBusType     which_bus);
+
+
+G_END_DECLS
+
+#endif  /* __BIG_UTIL_DBUS_PRIVATE_H__ */
diff --git a/modules/dbus/util/dbus-proxy.c b/modules/dbus/util/dbus-proxy.c
new file mode 100644
index 0000000..a9a413c
--- /dev/null
+++ b/modules/dbus/util/dbus-proxy.c
@@ -0,0 +1,666 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#include <config.h>
+
+#include "dbus-proxy.h"
+#include "dbus.h"
+#include "log.h"
+#include <dbus/dbus-glib-lowlevel.h>
+#include <stdarg.h>
+
+typedef enum {
+    REPLY_CLOSURE_PLAIN,
+    REPLY_CLOSURE_JSON
+} ReplyClosureType;
+
+typedef struct {
+    BigDBusProxy *proxy;
+    ReplyClosureType type;
+    union {
+        BigDBusProxyReplyFunc     plain;
+        BigDBusProxyJsonReplyFunc json;
+    } func;
+    BigDBusProxyErrorReplyFunc error_func;
+    void *data;
+    /* this is a debug thing; we want to guarantee
+     * we call exactly 1 time either the reply or error
+     * callback.
+     */
+    guint reply_invoked : 1;
+    guint error_invoked : 1;
+} ReplyClosure;
+
+static void     big_dbus_proxy_dispose      (GObject               *object);
+static void     big_dbus_proxy_finalize     (GObject               *object);
+static GObject* big_dbus_proxy_constructor  (GType                  type,
+                                             guint                  n_construct_properties,
+                                             GObjectConstructParam *construct_params);
+static void     big_dbus_proxy_get_property (GObject               *object,
+                                             guint                  prop_id,
+                                             GValue                *value,
+                                             GParamSpec            *pspec);
+static void     big_dbus_proxy_set_property (GObject               *object,
+                                             guint                  prop_id,
+                                             const GValue          *value,
+                                             GParamSpec            *pspec);
+
+struct _BigDBusProxy {
+    GObject parent;
+
+    DBusConnection *connection;
+    char *bus_name;
+    char *object_path;
+    char *iface;
+};
+
+struct _BigDBusProxyClass {
+    GObjectClass parent;
+};
+
+G_DEFINE_TYPE(BigDBusProxy, big_dbus_proxy, G_TYPE_OBJECT);
+
+#if 0
+enum {
+    LAST_SIGNAL
+};
+
+static int signals[LAST_SIGNAL];
+#endif
+
+enum {
+    PROP_0,
+    PROP_CONNECTION,
+    PROP_BUS_NAME,
+    PROP_OBJECT_PATH,
+    PROP_INTERFACE
+};
+
+static void
+big_dbus_proxy_init(BigDBusProxy *proxy)
+{
+
+}
+
+static void
+big_dbus_proxy_class_init(BigDBusProxyClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    object_class->dispose = big_dbus_proxy_dispose;
+    object_class->finalize = big_dbus_proxy_finalize;
+
+    object_class->constructor = big_dbus_proxy_constructor;
+    object_class->get_property = big_dbus_proxy_get_property;
+    object_class->set_property = big_dbus_proxy_set_property;
+
+    g_object_class_install_property(object_class,
+                                    PROP_CONNECTION,
+                                    g_param_spec_boxed("connection",
+                                                       "DBusConnection",
+                                                       "Our connection to the bus",
+                                                       DBUS_TYPE_CONNECTION,
+                                                       G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_BUS_NAME,
+                                    g_param_spec_string("bus-name",
+                                                        "Bus Name",
+                                                        "Name of app on the bus",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_OBJECT_PATH,
+                                    g_param_spec_string("object-path",
+                                                        "Object Path",
+                                                        "Object's dbus path",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+
+    g_object_class_install_property(object_class,
+                                    PROP_INTERFACE,
+                                    g_param_spec_string("interface",
+                                                        "Interface",
+                                                        "Interface to invoke methods on",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+}
+
+static void
+big_dbus_proxy_dispose(GObject *object)
+{
+    BigDBusProxy *proxy;
+
+    proxy = BIG_DBUS_PROXY(object);
+
+    if (proxy->connection) {
+        dbus_connection_unref(proxy->connection);
+        proxy->connection = NULL;
+    }
+
+    if (proxy->bus_name) {
+        g_free(proxy->bus_name);
+        proxy->bus_name = NULL;
+    }
+
+    if (proxy->object_path) {
+        g_free(proxy->object_path);
+        proxy->object_path = NULL;
+    }
+
+    if (proxy->iface) {
+        g_free(proxy->iface);
+        proxy->iface = NULL;
+    }
+
+    G_OBJECT_CLASS(big_dbus_proxy_parent_class)->dispose(object);
+}
+
+static void
+big_dbus_proxy_finalize(GObject *object)
+{
+
+    G_OBJECT_CLASS(big_dbus_proxy_parent_class)->finalize(object);
+}
+
+static GObject*
+big_dbus_proxy_constructor (GType                  type,
+                            guint                  n_construct_properties,
+                            GObjectConstructParam *construct_params)
+{
+    GObject *object;
+    BigDBusProxy *proxy;
+
+    object = (* G_OBJECT_CLASS (big_dbus_proxy_parent_class)->constructor) (type,
+                                                                            n_construct_properties,
+                                                                            construct_params);
+
+    proxy = BIG_DBUS_PROXY(object);
+
+    return object;
+}
+
+static void
+big_dbus_proxy_get_property (GObject     *object,
+                             guint        prop_id,
+                             GValue      *value,
+                             GParamSpec  *pspec)
+{
+    BigDBusProxy *proxy;
+
+    proxy = BIG_DBUS_PROXY (object);
+
+    switch (prop_id) {
+    case PROP_CONNECTION:
+        g_value_set_boxed(value, proxy->connection);
+        break;
+    case PROP_BUS_NAME:
+        g_value_set_string(value, proxy->bus_name);
+        break;
+    case PROP_OBJECT_PATH:
+        g_value_set_string(value, proxy->object_path);
+        break;
+    case PROP_INTERFACE:
+        g_value_set_string(value, proxy->iface);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+big_dbus_proxy_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+    BigDBusProxy *proxy;
+
+    proxy = BIG_DBUS_PROXY (object);
+
+    switch (prop_id) {
+    case PROP_CONNECTION:
+        if (proxy->connection != NULL) {
+            g_warning("Cannot change BigDBusProxy::connection after it's set");
+            return;
+        }
+        proxy->connection = dbus_connection_ref(g_value_get_boxed(value));
+        break;
+    case PROP_BUS_NAME:
+        if (proxy->bus_name != NULL) {
+            g_warning("Cannot change BigDBusProxy::bus-name after it's set");
+            return;
+        }
+        proxy->bus_name = g_value_dup_string(value);
+        break;
+    case PROP_OBJECT_PATH:
+        if (proxy->object_path != NULL) {
+            g_warning("Cannot change BigDBusProxy::object-path after it's set");
+            return;
+        }
+        proxy->object_path = g_value_dup_string(value);
+        break;
+    case PROP_INTERFACE:
+        if (proxy->iface != NULL) {
+            g_warning("Cannot change BigDBusProxy::interface after it's set");
+            return;
+        }
+        proxy->iface = g_value_dup_string(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+/* bus_name can be NULL if not going through a bus, and
+ * iface is allowed to be NULL but likely should not be.
+ */
+BigDBusProxy*
+big_dbus_proxy_new(DBusConnection *connection,
+                   const char     *bus_name,
+                   const char     *object_path,
+                   const char     *iface)
+{
+    BigDBusProxy *proxy;
+
+    g_return_val_if_fail(connection != NULL, NULL);
+    g_return_val_if_fail(object_path != NULL, NULL);
+
+    proxy = g_object_new(BIG_TYPE_DBUS_PROXY,
+                         "connection", connection,
+                         "bus-name", bus_name,
+                         "object-path", object_path,
+                         "interface", iface,
+                         NULL);
+
+    return proxy;
+}
+
+DBusConnection*
+big_dbus_proxy_get_connection(BigDBusProxy *proxy)
+{
+    return proxy->connection;
+}
+
+const char*
+big_dbus_proxy_get_bus_name(BigDBusProxy *proxy)
+{
+    return proxy->bus_name;
+}
+
+DBusMessage*
+big_dbus_proxy_new_method_call(BigDBusProxy *proxy,
+                               const char   *method_name)
+{
+    DBusMessage *message;
+
+    message = dbus_message_new_method_call(proxy->bus_name,
+                                           proxy->object_path,
+                                           proxy->iface,
+                                           method_name);
+    if (message == NULL)
+        g_error("no memory");
+
+    /* We don't want methods to auto-start services...  if a service
+     * needs starting or restarting, we want to do so explicitly so we
+     * can do it in an orderly and predictable way.
+     */
+    dbus_message_set_auto_start(message, FALSE);
+
+    return message;
+}
+
+DBusMessage*
+big_dbus_proxy_new_json_call(BigDBusProxy    *proxy,
+                             const char      *method_name,
+                             DBusMessageIter *arg_iter,
+                             DBusMessageIter *dict_iter)
+{
+    DBusMessage *message;
+
+    message = big_dbus_proxy_new_method_call(proxy, method_name);
+
+    dbus_message_iter_init_append(message, arg_iter);
+    dbus_message_iter_open_container(arg_iter, DBUS_TYPE_ARRAY, "{sv}", dict_iter);
+
+    return message;
+}
+
+static ReplyClosure*
+reply_closure_new(BigDBusProxy              *proxy,
+                  BigDBusProxyReplyFunc      plain_func,
+                  BigDBusProxyJsonReplyFunc  json_func,
+                  BigDBusProxyErrorReplyFunc error_func,
+                  void                      *data)
+{
+    ReplyClosure *c;
+
+    c = g_slice_new0(ReplyClosure);
+
+    c->proxy = g_object_ref(proxy);
+
+    g_assert(!(plain_func && json_func));
+
+    if (plain_func != NULL) {
+        c->type = REPLY_CLOSURE_PLAIN;
+        c->func.plain = plain_func;
+    } else {
+        c->type = REPLY_CLOSURE_JSON;
+        c->func.json = json_func;
+    }
+
+    c->error_func = error_func;
+    c->data = data;
+
+    return c;
+}
+
+static void
+reply_closure_free(ReplyClosure *c)
+{
+    /* call exactly one of these */
+    g_assert(!(c->error_invoked &&
+               c->reply_invoked));
+
+    if (!(c->error_invoked ||
+          c->reply_invoked)) {
+        c->error_invoked = TRUE;
+        if (c->error_func) {
+            (* c->error_func) (c->proxy, DBUS_ERROR_FAILED,
+                               "Pending call was freed (due to dbus_shutdown() probably) before it was ever notified",
+                               c->data);
+        }
+    }
+
+    g_object_unref(c->proxy);
+    g_slice_free(ReplyClosure, c);
+}
+
+static void
+reply_closure_invoke_error(ReplyClosure *c,
+                           DBusMessage  *reply)
+{
+    g_assert(dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR);
+
+    g_assert(!c->reply_invoked);
+    g_assert(!c->error_invoked);
+
+    c->error_invoked = TRUE;
+
+    if (c->error_func) {
+        DBusError derror;
+
+        dbus_error_init(&derror);
+
+        dbus_set_error_from_message(&derror, reply);
+
+        (* c->error_func) (c->proxy, derror.name,
+                           derror.message,
+                           c->data);
+
+        dbus_error_free(&derror);
+    }
+}
+
+static void
+reply_closure_invoke(ReplyClosure *c,
+                     DBusMessage  *reply)
+{
+    if (c->type == REPLY_CLOSURE_PLAIN) {
+        if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+            g_assert(!c->reply_invoked);
+            g_assert(!c->error_invoked);
+
+            c->reply_invoked = TRUE;
+
+            if (c->func.plain != NULL) {
+                (* c->func.plain) (c->proxy,
+                                   reply,
+                                   c->data);
+            }
+        } else if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+            reply_closure_invoke_error(c, reply);
+        } else {
+            g_assert(!c->reply_invoked);
+            g_assert(!c->error_invoked);
+
+            c->error_invoked = TRUE;
+
+            if (c->error_func) {
+                (* c->error_func) (c->proxy, DBUS_ERROR_FAILED,
+                                   "Got weird message type back as a reply",
+                                   c->data);
+            }
+        }
+    } else if (c->type == REPLY_CLOSURE_JSON) {
+        if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+            if (dbus_message_has_signature(reply, "a{sv}")) {
+                g_assert(!c->reply_invoked);
+                g_assert(!c->error_invoked);
+
+                c->reply_invoked = TRUE;
+
+                if (c->func.json) {
+                    DBusMessageIter arg_iter;
+                    DBusMessageIter dict_iter;
+
+                    dbus_message_iter_init(reply, &arg_iter);
+                    dbus_message_iter_recurse(&arg_iter, &dict_iter);
+
+                    (* c->func.json) (c->proxy,
+                                      reply,
+                                      &dict_iter,
+                                      c->data);
+                }
+            } else {
+                g_assert(!c->reply_invoked);
+                g_assert(!c->error_invoked);
+
+                c->error_invoked = TRUE;
+
+                if (c->error_func) {
+                    (* c->error_func) (c->proxy,
+                                       DBUS_ERROR_FAILED,
+                                       "Message we got back did not have the right signature",
+                                       c->data);
+                }
+            }
+        } else if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+            reply_closure_invoke_error(c, reply);
+        } else {
+            g_assert(!c->reply_invoked);
+            g_assert(!c->error_invoked);
+
+            c->error_invoked = TRUE;
+
+            if (c->error_func) {
+                (* c->error_func) (c->proxy, DBUS_ERROR_FAILED,
+                                   "Got weird message type back as a reply",
+                                   c->data);
+            }
+        }
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+
+static gboolean
+failed_to_send_idle(void *data)
+{
+    ReplyClosure *c;
+
+    c = data;
+
+    g_assert(!c->reply_invoked);
+    g_assert(!c->error_invoked);
+
+    c->error_invoked = TRUE;
+
+    if (c->error_func) {
+        (* c->error_func) (c->proxy,
+                           DBUS_ERROR_NO_MEMORY,
+                           "Unable to send method call",
+                           c->data);
+    }
+
+    reply_closure_free(c);
+
+    return FALSE;
+}
+
+
+static void
+pending_call_notify(DBusPendingCall *pending,
+                    void            *user_data)
+{
+    DBusMessage *reply;
+    ReplyClosure *c;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "BigDBusProxy received reply to pending call");
+
+    c = user_data;
+
+    /* reply may be NULL if none received? I think it may never be if
+     * we've already been notified, but be safe here.
+     */
+    reply = dbus_pending_call_steal_reply(pending);
+
+    if (reply) {
+        reply_closure_invoke(c, reply);
+
+        dbus_message_unref(reply);
+    } else {
+        /* I think libdbus won't let this happen, but to be safe... */
+        g_assert(!c->reply_invoked);
+        g_assert(!c->error_invoked);
+
+        c->error_invoked = TRUE;
+
+        if (c->error_func) {
+            (* c->error_func) (c->proxy,
+                               DBUS_ERROR_TIMED_OUT,
+                               "Did not receive a reply or error",
+                               c->data);
+        }
+    }
+
+    /* The closure should be freed along with the pending call */
+}
+
+static void
+pending_call_free_data(void *data)
+{
+    ReplyClosure *c = data;
+    reply_closure_free(c);
+}
+
+static void
+big_dbus_proxy_send_internal(BigDBusProxy              *proxy,
+                             DBusMessage               *message,
+                             BigDBusProxyReplyFunc      plain_func,
+                             BigDBusProxyJsonReplyFunc  json_func,
+                             BigDBusProxyErrorReplyFunc error_func,
+                             void                      *data)
+{
+    ReplyClosure *c;
+    DBusPendingCall *pending;
+
+    if (!(plain_func || json_func || error_func)) {
+        /* Fire and forget! */
+
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "Firing and forgetting dbus proxy call");
+
+        dbus_connection_send(proxy->connection, message, NULL);
+        return;
+    }
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Sending dbus proxy call %s",
+              dbus_message_get_member(message));
+
+    c = reply_closure_new(proxy, plain_func, json_func, error_func, data);
+    pending = NULL;
+    if (!dbus_connection_send_with_reply(proxy->connection, message, &pending, -1) ||
+        pending == NULL) {
+
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "Failed to send call, will report error in idle handler");
+
+        /* Send an error on return to main loop */
+        g_idle_add(failed_to_send_idle, c);
+        return;
+    }
+
+    dbus_pending_call_set_notify(pending, pending_call_notify, c,
+                                 pending_call_free_data);
+
+    dbus_pending_call_unref(pending); /* DBusConnection should still hold a ref until it's completed */
+}
+
+void
+big_dbus_proxy_send(BigDBusProxy              *proxy,
+                    DBusMessage               *message,
+                    BigDBusProxyReplyFunc      reply_func,
+                    BigDBusProxyErrorReplyFunc error_func,
+                    void                      *data)
+{
+    big_dbus_proxy_send_internal(proxy, message, reply_func, NULL, error_func, data);
+}
+
+static void
+append_entries_from_valist(DBusMessageIter *dict_iter,
+                           const char      *first_key,
+                           va_list          args)
+{
+    const char *key;
+    int dbus_type;
+    void *value_p;
+
+    key = first_key;
+    dbus_type = va_arg(args, int);
+    value_p = va_arg(args, void*);
+
+    big_dbus_append_json_entry(dict_iter, key, dbus_type, value_p);
+
+    key = va_arg(args, const char*);
+    while (key != NULL) {
+        dbus_type = va_arg(args, int);
+        value_p = va_arg(args, void*);
+
+        big_dbus_append_json_entry(dict_iter, key, dbus_type, value_p);
+
+        key = va_arg(args, const char*);
+    }
+}
+
+void
+big_dbus_proxy_call_json_async (BigDBusProxy              *proxy,
+                                const char                *method_name,
+                                BigDBusProxyJsonReplyFunc  reply_func,
+                                BigDBusProxyErrorReplyFunc error_func,
+                                void                      *data,
+                                const char                *first_key,
+                                ...)
+{
+    DBusMessageIter arg_iter, dict_iter;
+    DBusMessage *message;
+    va_list args;
+
+    message = big_dbus_proxy_new_json_call(proxy, method_name, &arg_iter, &dict_iter);
+
+    if (first_key != NULL) {
+        va_start(args, first_key);
+        append_entries_from_valist(&dict_iter, first_key, args);
+        va_end(args);
+    }
+
+    dbus_message_iter_close_container(&arg_iter, &dict_iter);
+
+    big_dbus_proxy_send_internal(proxy, message, NULL, reply_func, error_func, data);
+
+    dbus_message_unref(message);
+}
diff --git a/modules/dbus/util/dbus-proxy.h b/modules/dbus/util/dbus-proxy.h
new file mode 100644
index 0000000..6b793e6
--- /dev/null
+++ b/modules/dbus/util/dbus-proxy.h
@@ -0,0 +1,74 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#ifndef __BIG_UTIL_DBUS_PROXY_H__
+#define __BIG_UTIL_DBUS_PROXY_H__
+
+#include <gio/gio.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+
+typedef struct _BigDBusProxy      BigDBusProxy;
+typedef struct _BigDBusProxyClass BigDBusProxyClass;
+
+typedef void (* BigDBusProxyReplyFunc)      (BigDBusProxy    *proxy,
+                                             DBusMessage     *message,
+                                             void            *data);
+typedef void (* BigDBusProxyJsonReplyFunc)  (BigDBusProxy    *proxy,
+                                             DBusMessage     *message,
+                                             DBusMessageIter *return_value_iter,
+                                             void            *data);
+typedef void (* BigDBusProxyErrorReplyFunc) (BigDBusProxy    *proxy,
+                                             const char      *error_name,
+                                             const char      *error_message,
+                                             void            *data);
+
+#define BIG_TYPE_DBUS_PROXY              (big_dbus_proxy_get_type ())
+#define BIG_DBUS_PROXY(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), BIG_TYPE_DBUS_PROXY, BigDBusProxy))
+#define BIG_DBUS_PROXY_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), BIG_TYPE_DBUS_PROXY, BigDBusProxyClass))
+#define BIG_IS_DBUS_PROXY(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), BIG_TYPE_DBUS_PROXY))
+#define BIG_IS_DBUS_PROXY_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), BIG_TYPE_DBUS_PROXY))
+#define BIG_DBUS_PROXY_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), BIG_TYPE_DBUS_PROXY, BigDBusProxyClass))
+
+GType           big_dbus_proxy_get_type      (void) G_GNUC_CONST;
+
+
+BigDBusProxy*   big_dbus_proxy_new             (DBusConnection             *connection,
+                                                const char                 *bus_name,
+                                                const char                 *object_path,
+                                                const char                 *iface);
+DBusConnection* big_dbus_proxy_get_connection  (BigDBusProxy               *proxy);
+const char*     big_dbus_proxy_get_bus_name    (BigDBusProxy               *proxy);
+DBusMessage*    big_dbus_proxy_new_method_call (BigDBusProxy               *proxy,
+                                                const char                 *method_name);
+DBusMessage*    big_dbus_proxy_new_json_call   (BigDBusProxy               *proxy,
+                                                const char                 *method_name,
+                                                DBusMessageIter            *arg_iter,
+                                                DBusMessageIter            *dict_iter);
+void            big_dbus_proxy_send            (BigDBusProxy               *proxy,
+                                                DBusMessage                *message,
+                                                BigDBusProxyReplyFunc       reply_func,
+                                                BigDBusProxyErrorReplyFunc  error_func,
+                                                void                       *data);
+
+/* varargs are like:
+ *
+ *   key1, dbus_type_1, &value_1,
+ *   key2, dbus_type_2, &value_2,
+ *   NULL
+ *
+ * Basic types only (no arrays)
+ */
+void          big_dbus_proxy_call_json_async (BigDBusProxy              *proxy,
+                                              const char                *method_name,
+                                              BigDBusProxyJsonReplyFunc  reply_func,
+                                              BigDBusProxyErrorReplyFunc error_func,
+                                              void                      *data,
+                                              const char                *first_key,
+                                              ...);
+
+G_END_DECLS
+
+#endif  /* __BIG_UTIL_DBUS_PROXY_H__ */
diff --git a/modules/dbus/util/dbus-signals.c b/modules/dbus/util/dbus-signals.c
new file mode 100644
index 0000000..f2e5ba2
--- /dev/null
+++ b/modules/dbus/util/dbus-signals.c
@@ -0,0 +1,1318 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#include <config.h>
+
+#include "dbus-private.h"
+#include "log.h"
+
+#include <string.h>
+
+#define INVALID_SIGNAL_ID (-1)
+
+typedef struct {
+    DBusBusType            bus_type;
+    int                    refcount;
+    char                  *sender;
+    char                  *path;
+    char                  *iface;
+    char                  *name;
+    BigDBusSignalHandler   handler;
+    void                  *data;
+    GDestroyNotify         data_dnotify;
+    int                    id;
+    unsigned int           matching : 1;
+    unsigned int           destroyed : 1;
+} BigSignalWatcher;
+
+static GSList *pending_signal_watchers = NULL;
+
+static void signal_watcher_remove (DBusConnection   *connection,
+                                   BigDBusInfo      *info,
+                                   BigSignalWatcher *watcher);
+
+
+static int global_handler_id = 0;
+
+static BigSignalWatcher*
+signal_watcher_new(DBusBusType                  bus_type,
+                   const char                  *sender,
+                   const char                  *path,
+                   const char                  *iface,
+                   const char                  *name,
+                   BigDBusSignalHandler         handler,
+                   void                        *data,
+                   GDestroyNotify               data_dnotify)
+{
+    BigSignalWatcher *watcher;
+
+    watcher = g_slice_new0(BigSignalWatcher);
+
+    watcher->refcount = 1;
+
+    watcher->bus_type = bus_type;
+    watcher->sender = g_strdup(sender);
+    watcher->path = g_strdup(path);
+    watcher->iface = g_strdup(iface);
+    watcher->name = g_strdup(name);
+    watcher->handler = handler;
+    watcher->id = global_handler_id++;
+    watcher->data = data;
+    watcher->data_dnotify = data_dnotify;
+
+    return watcher;
+}
+
+static void
+signal_watcher_dnotify(BigSignalWatcher *watcher)
+{
+    if (watcher->data_dnotify != NULL) {
+        (* watcher->data_dnotify) (watcher->data);
+        watcher->data_dnotify = NULL;
+    }
+    watcher->destroyed = TRUE;
+}
+
+static void
+signal_watcher_ref(BigSignalWatcher *watcher)
+{
+    watcher->refcount += 1;
+}
+
+static void
+signal_watcher_unref(BigSignalWatcher *watcher)
+{
+    watcher->refcount -= 1;
+
+    if (watcher->refcount == 0) {
+        signal_watcher_dnotify(watcher);
+
+        g_free(watcher->sender);
+        g_free(watcher->path);
+        g_free(watcher->iface);
+        g_free(watcher->name);
+
+        g_slice_free(BigSignalWatcher, watcher);
+    }
+}
+
+static char*
+signal_watcher_build_match_rule(BigSignalWatcher *watcher)
+{
+    GString *s;
+
+    s = g_string_new("type='signal'");
+
+    if (watcher->sender) {
+        g_string_append_printf(s, ",sender='%s'", watcher->sender);
+    }
+
+    if (watcher->path) {
+        g_string_append_printf(s, ",path='%s'", watcher->path);
+    }
+
+    if (watcher->iface) {
+        g_string_append_printf(s, ",interface='%s'", watcher->iface);
+    }
+
+    if (watcher->name) {
+        g_string_append_printf(s, ",member='%s'", watcher->name);
+    }
+
+    return g_string_free(s, FALSE);
+}
+
+
+static GSList*
+signal_watcher_table_lookup(GHashTable *table,
+                            const char *key)
+{
+    if (table == NULL) {
+        return NULL;
+    }
+
+    return g_hash_table_lookup(table, key);
+}
+
+static void
+signal_watcher_list_free(void *data)
+{
+    GSList *l = data;
+    while (l != NULL) {
+        GSList *next = l->next;
+        signal_watcher_unref(l->data);
+        g_slist_free_1(l);
+        l = next;
+    }
+}
+
+static void
+signal_watcher_table_add(GHashTable      **table_p,
+                         const char       *key,
+                         BigSignalWatcher *watcher)
+{
+    GSList *list;
+    char *original_key;
+
+    if (*table_p == NULL) {
+        list = NULL;
+        original_key = g_strdup(key);
+        *table_p = g_hash_table_new_full(g_str_hash,
+                                         g_str_equal,
+                                         g_free,
+                                         signal_watcher_list_free);
+    } else {
+        if (!g_hash_table_lookup_extended(*table_p,
+                                          key,
+                                          (gpointer*)&original_key,
+                                          (gpointer*)&list)) {
+            original_key = g_strdup(key);
+            list = NULL;
+        }
+    }
+
+    list = g_slist_prepend(list, watcher);
+    signal_watcher_ref(watcher);
+
+    g_hash_table_steal(*table_p, key);
+    g_hash_table_insert(*table_p, original_key, list);
+}
+
+static void
+signal_watcher_table_remove(GHashTable       *table,
+                            const char       *key,
+                            BigSignalWatcher *watcher)
+{
+    GSList *list;
+    GSList *l;
+    char *original_key;
+
+    if (table == NULL)
+        return; /* Never lazily-created the table, nothing ever added */
+
+    if (!g_hash_table_lookup_extended(table,
+                                      key,
+                                      (gpointer*)&original_key,
+                                      (gpointer*)&list)) {
+        return;
+    }
+
+    l = g_slist_find(list, watcher);
+    if (!l)
+        return; /* we don't want to unref if we weren't in this table */
+
+    list = g_slist_delete_link(list, l);
+
+    g_hash_table_steal(table, key);
+    if (list != NULL) {
+        g_hash_table_insert(table, original_key, list);
+    } else {
+        g_free(original_key);
+    }
+
+    signal_watcher_unref(watcher);
+}
+
+static void
+signal_emitter_name_appeared(DBusConnection *connection,
+                             const char     *name,
+                             const char     *new_owner_unique_name,
+                             void           *data)
+{
+    /* We don't need to do anything here, we installed a name watch so
+     * we could call big_dbus_get_watched_name_owner() to dispatch
+     * signals, and to get destroy notification on unique names.
+     */
+}
+
+static void
+signal_emitter_name_vanished(DBusConnection *connection,
+                             const char     *name,
+                             const char     *old_owner_unique_name,
+                             void           *data)
+{
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Signal emitter '%s' is now gone",
+              name);
+
+    /* If a watcher is matching on a unique name sender, once the unique
+     * name goes away, the watcher can never see anything so nuke it.
+     */
+    if (*name == ':') {
+        GSList *list;
+        BigDBusInfo *info;
+
+        info = _big_dbus_ensure_info(connection);
+
+        list = signal_watcher_table_lookup(info->signal_watchers_by_unique_sender,
+                                           name);
+
+        if (list == NULL)
+            return;
+
+        /* copy the list since we're about to remove stuff from it
+         * in signal_watcher_remove
+         */
+        list = g_slist_copy(list);
+        while (list != NULL) {
+            signal_watcher_remove(connection, info, list->data);
+            list = g_slist_delete_link(list, list);
+        }
+    }
+}
+
+static BigDBusWatchNameFuncs signal_emitter_name_funcs = {
+    signal_emitter_name_appeared,
+    signal_emitter_name_vanished
+};
+
+static void
+signal_watcher_set_matching(DBusConnection   *connection,
+                            BigSignalWatcher *watcher,
+                            gboolean          matching)
+{
+    char *rule;
+
+    if (watcher->matching == (matching != FALSE)) {
+        return;
+    }
+
+    /* Never add match on a destroyed signal watcher */
+    if (watcher->destroyed && matching)
+        return;
+
+    /* We can't affect match rules if not connected */
+    if (!dbus_connection_get_is_connected(connection)) {
+        return;
+    }
+
+    watcher->matching = matching != FALSE;
+
+    rule = signal_watcher_build_match_rule(watcher);
+
+    if (matching)
+        dbus_bus_add_match(connection,
+                           rule, NULL); /* asking for error would make this block */
+    else
+        dbus_bus_remove_match(connection, rule, NULL);
+
+    g_free(rule);
+
+    if (watcher->sender) {
+        /* If the signal is from a well-known name, we have to add
+         * a name watch to know who owns that name.
+         *
+         * If the signal is from a unique name, we want to destroy
+         * the watcher if the unique name goes away
+         */
+        if (matching) {
+            big_dbus_watch_name(watcher->bus_type,
+                                watcher->sender,
+                                0,
+                                &signal_emitter_name_funcs,
+                                NULL);
+        } else {
+            big_dbus_unwatch_name(watcher->bus_type,
+                                  watcher->sender,
+                                  &signal_emitter_name_funcs,
+                                  NULL);
+        }
+    }
+}
+
+static void
+signal_watcher_add(DBusConnection   *connection,
+                   BigDBusInfo      *info,
+                   BigSignalWatcher *watcher)
+{
+    gboolean in_some_table;
+
+    signal_watcher_set_matching(connection, watcher, TRUE);
+
+    info->all_signal_watchers = g_slist_prepend(info->all_signal_watchers, watcher);
+    signal_watcher_ref(watcher);
+
+    in_some_table = FALSE;
+
+    if (watcher->sender && *(watcher->sender) == ':') {
+        signal_watcher_table_add(&info->signal_watchers_by_unique_sender,
+                                 watcher->sender,
+                                 watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->path) {
+        signal_watcher_table_add(&info->signal_watchers_by_path,
+                                 watcher->path,
+                                 watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->iface) {
+        signal_watcher_table_add(&info->signal_watchers_by_iface,
+                                 watcher->iface,
+                                 watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->name) {
+        signal_watcher_table_add(&info->signal_watchers_by_signal,
+                                 watcher->name,
+                                 watcher);
+        in_some_table = TRUE;
+    }
+
+    if (!in_some_table) {
+        info->signal_watchers_in_no_table =
+            g_slist_prepend(info->signal_watchers_in_no_table,
+                            watcher);
+        signal_watcher_ref(watcher);
+    }
+}
+
+static void
+signal_watcher_remove(DBusConnection   *connection,
+                      BigDBusInfo      *info,
+                      BigSignalWatcher *watcher)
+{
+    gboolean in_some_table;
+
+    signal_watcher_set_matching(connection, watcher, FALSE);
+
+    info->all_signal_watchers = g_slist_remove(info->all_signal_watchers, watcher);
+
+    in_some_table = FALSE;
+
+    if (watcher->sender && *(watcher->sender) == ':') {
+        signal_watcher_table_remove(info->signal_watchers_by_unique_sender,
+                                    watcher->sender,
+                                    watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->path) {
+        signal_watcher_table_remove(info->signal_watchers_by_path,
+                                    watcher->path,
+                                    watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->iface) {
+        signal_watcher_table_remove(info->signal_watchers_by_iface,
+                                    watcher->iface,
+                                    watcher);
+        in_some_table = TRUE;
+    }
+
+    if (watcher->name) {
+        signal_watcher_table_remove(info->signal_watchers_by_signal,
+                                    watcher->name,
+                                    watcher);
+        in_some_table = TRUE;
+    }
+
+    if (!in_some_table) {
+        info->signal_watchers_in_no_table =
+            g_slist_remove(info->signal_watchers_in_no_table,
+                           watcher);
+        signal_watcher_unref(watcher);
+    }
+
+    /* Destroy-notify before dropping last ref for a little more safety
+     * (avoids "resurrection" issues), and to ensure we call the destroy
+     * notifier even if we don't finish finalizing just yet.
+     */
+    signal_watcher_dnotify(watcher);
+
+    signal_watcher_unref(watcher);
+}
+
+/* This is called before we notify the app that the connection is open,
+ * to add match rules. It must add the match rules, but MUST NOT
+ * invoke application callbacks since the "connection opened"
+ * callback needs to be first.
+ */
+void
+_big_dbus_process_pending_signal_watchers(DBusConnection *connection,
+                                          BigDBusInfo    *info)
+{
+    GSList *remaining;
+
+    remaining = NULL;
+    while (pending_signal_watchers) {
+        BigSignalWatcher *watcher = pending_signal_watchers->data;
+        pending_signal_watchers = g_slist_delete_link(pending_signal_watchers,
+                                                      pending_signal_watchers);
+
+        if (watcher->bus_type == info->bus_type) {
+            /* Transfer to the non-pending BigDBusInfo */
+            signal_watcher_add(connection, info, watcher);
+            signal_watcher_unref(watcher);
+        } else {
+            remaining = g_slist_prepend(remaining, watcher);
+        }
+    }
+
+    /* keep the order deterministic by reversing, though I don't know
+     * of a reason it matters.
+     */
+    pending_signal_watchers = g_slist_reverse(remaining);
+}
+
+static void
+signal_watchers_disconnected(DBusConnection *connection,
+                             BigDBusInfo    *info)
+{
+    /* None should be pending on this bus, because at start of
+     * _big_dbus_signal_watch_filter_message() we process all the pending ones.
+     * However there could be stuff in pending_signal_watchers for
+     * another bus. Anyway bottom line we can ignore pending_signal_watchers
+     * in here.
+     */
+    GSList *list;
+    GSList *destroyed;
+
+    /* Build a separate list to destroy to avoid re-entrancy as we are
+     * walking the list
+     */
+    destroyed = NULL;
+    for (list = info->all_signal_watchers;
+         list != NULL;
+         list = list->next) {
+        BigSignalWatcher *watcher = list->data;
+        if (watcher->sender && *(watcher->sender) == ':') {
+            destroyed = g_slist_prepend(destroyed,
+                                        watcher);
+            signal_watcher_ref(watcher);
+        }
+    }
+
+    while (destroyed != NULL) {
+        BigSignalWatcher *watcher = destroyed->data;
+        destroyed = g_slist_delete_link(destroyed, destroyed);
+
+        signal_watcher_remove(connection, info, watcher);
+        signal_watcher_unref(watcher);
+    }
+}
+
+static void
+concat_candidates(GSList    **candidates_p,
+                  GHashTable *table,
+                  const char *key)
+{
+    GSList *list;
+
+    list = signal_watcher_table_lookup(table, key);
+    if (list == NULL)
+        return;
+
+    *candidates_p = g_slist_concat(*candidates_p,
+                                   g_slist_copy(list));
+}
+
+static int
+direct_cmp(gconstpointer a,
+           gconstpointer b)
+{
+    /* gcc dislikes pointer math on void* so cast */
+    return ((const char*)a) - ((const char*)b);
+}
+
+static gboolean
+signal_watcher_watches(BigDBusInfo      *info,
+                       BigSignalWatcher *watcher,
+                       const char       *sender,
+                       const char       *path,
+                       const char       *iface,
+                       const char       *name)
+{
+    if (watcher->path &&
+        strcmp(watcher->path, path) != 0)
+        return FALSE;
+
+    if (watcher->iface &&
+        strcmp(watcher->iface, iface) != 0)
+        return FALSE;
+
+    if (watcher->name &&
+        strcmp(watcher->name, name) != 0)
+        return FALSE;
+
+    /* "sender" from message is always the unique name, but
+     * watcher may or may not be.
+     */
+
+    if (watcher->sender == NULL)
+        return TRUE;
+
+
+    if (* (watcher->sender) == ':') {
+        return strcmp(watcher->sender, sender) == 0;
+    } else {
+        const char *owner;
+
+        owner = big_dbus_get_watched_name_owner(info->bus_type,
+                                                watcher->sender);
+
+        if (owner != NULL &&
+            strcmp(sender, owner) == 0)
+            return TRUE;
+        else
+            return FALSE;
+    }
+}
+
+DBusHandlerResult
+_big_dbus_signal_watch_filter_message(DBusConnection *connection,
+                                      DBusMessage    *message,
+                                      void           *data)
+{
+    /* Two things we're looking for
+     * 1) signals
+     * 2) if the sender of a signal watcher is a unique name,
+     *    we want to destroy notify when it vanishes or
+     *    when the bus disconnects.
+     */
+    BigDBusInfo *info;
+    const char *sender;
+    const char *path;
+    const char *iface;
+    const char *name;
+    GSList *candidates;
+    BigSignalWatcher *previous;
+
+    info = _big_dbus_ensure_info(connection);
+
+    /* Be sure they are all in the lookup tables */
+    _big_dbus_process_pending_signal_watchers(connection, info);
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    sender = dbus_message_get_sender(message);
+    path = dbus_message_get_path(message);
+    iface = dbus_message_get_interface(message);
+    name = dbus_message_get_member(message);
+
+    /* libdbus requires path, iface, name. The bus daemon
+     * will always set a sender but some locally-generated
+     * messages (e.g. disconnected) may not have one.
+     */
+    g_assert(path != NULL);
+    g_assert(iface != NULL);
+    g_assert(name != NULL);
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Signal from %s %s.%s sender %s",
+              path, iface, name, sender ? sender : "(none)");
+
+    candidates = NULL;
+
+    if (sender != NULL) {
+        concat_candidates(&candidates,
+                          info->signal_watchers_by_unique_sender,
+                          sender);
+    }
+    concat_candidates(&candidates,
+                      info->signal_watchers_by_path,
+                      path);
+    concat_candidates(&candidates,
+                      info->signal_watchers_by_iface,
+                      iface);
+    concat_candidates(&candidates,
+                      info->signal_watchers_by_signal,
+                      name);
+    candidates = g_slist_concat(candidates,
+                                g_slist_copy(info->signal_watchers_in_no_table));
+
+    /* Sort so we can find dups */
+    candidates = g_slist_sort(candidates, direct_cmp);
+
+    previous = NULL;
+    while (candidates != NULL) {
+        BigSignalWatcher *watcher;
+
+        watcher = candidates->data;
+        candidates = g_slist_delete_link(candidates, candidates);
+
+        if (previous == watcher)
+            continue; /* watcher was in more than one table */
+
+        previous = watcher;
+
+        if (!signal_watcher_watches(info,
+                                    watcher,
+                                    sender, path, iface, name))
+            continue;
+
+        /* destroyed would happen if e.g. removed while we are going
+         * through here.
+         */
+        if (watcher->destroyed)
+            continue;
+
+        /* Invoke the watcher */
+
+        signal_watcher_ref(watcher);
+
+        (* watcher->handler) (connection,
+                              message,
+                              watcher->data);
+
+        signal_watcher_unref(watcher);
+    }
+
+    /* Note that signal watchers can also listen to the disconnected
+     * signal, so we do our special handling of it last
+     */
+    if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+        big_debug(BIG_DEBUG_UTIL_DBUS, "Disconnected in %s", G_STRFUNC);
+
+        signal_watchers_disconnected(connection, info);
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int
+big_dbus_watch_signal(DBusBusType                  bus_type,
+                      const char                  *sender,
+                      const char                  *path,
+                      const char                  *iface,
+                      const char                  *name,
+                      BigDBusSignalHandler         handler,
+                      void                        *data,
+                      GDestroyNotify               data_dnotify)
+{
+    BigSignalWatcher *watcher;
+    DBusConnection *weak;
+
+    watcher = signal_watcher_new(bus_type, sender, path,
+                                 iface, name, handler,
+                                 data, data_dnotify);
+
+    /* If we're already connected, it's essential to get the
+     * match rule added right away. Otherwise the race-free pattern
+     * is not possible:
+     * 1. Add match rule to monitor state of remote object
+     * 2. Get current state of remote object
+     *
+     * Using the pending_signal_watchers codepath, there's no
+     * notification when the match rule is added so you can't
+     * be sure you get current state *after* that.
+     *
+     * Since we add our match rule here immediately if connected,
+     * then apps can rely on first watching the signal, then
+     * getting current state.
+     *
+     * In the connect idle, we process pending signal watchers
+     * before calling any other app callbacks, so if someone
+     * gets current state on connect, that will be after
+     * all their match rules are added.
+     */
+    weak = _big_dbus_get_weak_ref(bus_type);
+    if (weak != NULL) {
+        signal_watcher_add(weak, _big_dbus_ensure_info(weak), watcher);
+        signal_watcher_unref(watcher);
+    } else {
+        pending_signal_watchers = g_slist_prepend(pending_signal_watchers, watcher);
+        _big_dbus_ensure_connect_idle(bus_type);
+    }
+
+    return watcher->id;
+}
+
+/* Does the watcher match a removal request? */
+static gboolean
+signal_watcher_matches(BigSignalWatcher     *watcher,
+                       DBusBusType           bus_type,
+                       const char           *sender,
+                       const char           *path,
+                       const char           *iface,
+                       const char           *name,
+                       int                   id,
+                       BigDBusSignalHandler  handler,
+                       void                 *data)
+{
+    /* If we have an ID, check that first. If it matches, we are
+     * done
+     */
+    if (id != INVALID_SIGNAL_ID && watcher->id == id)
+        return TRUE;
+
+    /* Start with data, most likely thing to not match */
+    if (watcher->data != data)
+        return FALSE;
+
+    /* Second most likely non-match */
+    if (watcher->handler != handler)
+        return FALSE;
+
+    /* Then third, do the more expensive checks */
+
+    if (watcher->bus_type != bus_type)
+        return FALSE;
+
+    if (g_strcmp0(watcher->sender, sender) != 0)
+        return FALSE;
+
+    if (g_strcmp0(watcher->path, path) != 0)
+        return FALSE;
+
+    if (g_strcmp0(watcher->iface, iface) != 0)
+        return FALSE;
+
+    if (g_strcmp0(watcher->name, name) != 0)
+        return FALSE;
+
+    return TRUE;
+}
+
+static void
+unwatch_signal(DBusBusType                  bus_type,
+               const char                  *sender,
+               const char                  *path,
+               const char                  *iface,
+               const char                  *name,
+               int                          id,
+               BigDBusSignalHandler         handler,
+               void                        *data)
+{
+    GSList *list;
+    DBusConnection *weak;
+    BigDBusInfo *info;
+
+    /* Always remove only ONE watcher (the first one we find) */
+
+    weak = _big_dbus_get_weak_ref(bus_type);
+
+    /* First see if it's still pending */
+    for (list = pending_signal_watchers;
+         list != NULL;
+         list = list->next) {
+        if (signal_watcher_matches(list->data,
+                                   bus_type,
+                                   sender,
+                                   path,
+                                   iface,
+                                   name,
+                                   id,
+                                   handler,
+                                   data)) {
+            BigSignalWatcher *watcher = list->data;
+            pending_signal_watchers = g_slist_remove_link(pending_signal_watchers,
+                                                          list);
+
+            if (weak != NULL)
+                signal_watcher_set_matching(weak, watcher, FALSE);
+
+            signal_watcher_dnotify(watcher); /* destroy even if we don't finalize */
+            signal_watcher_unref(watcher);
+            return;
+        }
+    }
+
+    /* If not pending, and no bus connection, it can't exist */
+    if (weak == NULL) {
+        /* don't warn on nonexistent, since a vanishing bus name could
+         * have nuked it outside the app's control.
+         */
+        return;
+    }
+
+    info = _big_dbus_ensure_info(weak);
+
+    for (list = info->all_signal_watchers;
+         list != NULL;
+         list = list->next) {
+        if (signal_watcher_matches(list->data,
+                                   bus_type,
+                                   sender,
+                                   path,
+                                   iface,
+                                   name,
+                                   id,
+                                   handler,
+                                   data)) {
+            signal_watcher_remove(weak, info, list->data);
+            /* note that "list" node is now invalid */
+            return;
+        }
+    }
+
+    /* don't warn on nonexistent, since a vanishing bus name could
+     * have nuked it outside the app's control. Just do nothing.
+     */
+}
+
+void
+big_dbus_unwatch_signal(DBusBusType                  bus_type,
+                        const char                  *sender,
+                        const char                  *path,
+                        const char                  *iface,
+                        const char                  *name,
+                        BigDBusSignalHandler         handler,
+                        void                        *data)
+{
+    unwatch_signal(bus_type,
+                   sender,
+                   path,
+                   iface,
+                   name,
+                   INVALID_SIGNAL_ID,
+                   handler,
+                   data);
+}
+
+void
+big_dbus_unwatch_signal_by_id(DBusBusType                  bus_type,
+                              int                          id)
+{
+    unwatch_signal(bus_type,
+                   NULL,
+                   NULL,
+                   NULL,
+                   NULL,
+                   id,
+                   (BigDBusSignalHandler)NULL,
+                   NULL);
+}
+
+#if BIG_BUILD_TESTS
+
+#include "dbus-proxy.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+static pid_t test_service_pid = 0;
+static BigDBusProxy *test_service_proxy = NULL;
+
+static GMainLoop *outer_loop = NULL;
+static GMainLoop *inner_loop = NULL;
+
+static int n_running_children = 0;
+
+typedef struct {
+    const char *sender;
+    const char *path;
+    const char *iface;
+    const char *member;
+} SignalWatchTest;
+
+static SignalWatchTest watch_tests[] = {
+    { NULL, NULL, NULL, NULL },
+    { "com.litl.TestService", NULL, NULL, NULL },
+    { NULL, "/com/litl/test/object42", NULL, NULL },
+    { NULL, NULL, "com.litl.TestIface", NULL },
+    { NULL, NULL, NULL, "TheSignal" }
+};
+
+static void do_test_service_child (void);
+
+/* quit when all children are gone */
+static void
+another_child_down(void)
+{
+    g_assert(n_running_children > 0);
+    n_running_children -= 1;
+
+    if (n_running_children == 0) {
+        g_main_loop_quit(outer_loop);
+    }
+}
+
+/* This test function doesn't really test anything, just sets up
+ * for the following one
+ */
+static void
+fork_test_signal_service(void)
+{
+    pid_t child_pid;
+
+    /* it would break to fork after we already connected */
+    g_assert(_big_dbus_get_weak_ref(DBUS_BUS_SESSION) == NULL);
+    g_assert(_big_dbus_get_weak_ref(DBUS_BUS_SYSTEM) == NULL);
+    g_assert(test_service_pid == 0);
+
+    child_pid = fork();
+
+    if (child_pid == -1) {
+        g_error("Failed to fork dbus service");
+    } else if (child_pid > 0) {
+        /* We are the parent */
+        test_service_pid = child_pid;
+        n_running_children += 1;
+
+        return;
+    }
+
+    /* we are the child, set up a service for main test process to talk to */
+
+    do_test_service_child();
+}
+
+static void
+kill_child(void)
+{
+    if (kill(test_service_pid, SIGTERM) < 0) {
+        g_error("Test service was no longer around... it must have failed somehow (%s)",
+                strerror(errno));
+    }
+
+    /* We will quit main loop when we see the child go away */
+}
+
+static int signal_received_count = 0;
+static int destroy_notify_count = 0;
+
+static void
+the_destroy_notifier(void *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "got destroy notification on signal watch");
+    destroy_notify_count += 1;
+}
+
+static void
+the_destroy_notifier_that_quits(void *data)
+{
+    the_destroy_notifier(data);
+    g_main_loop_quit(inner_loop);
+}
+
+static void
+expect_receive_signal_handler(DBusConnection *connection,
+                              DBusMessage    *message,
+                              void           *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "dbus signal watch handler called");
+
+    g_assert(dbus_message_is_signal(message,
+                                    "com.litl.TestIface",
+                                    "TheSignal"));
+
+    signal_received_count += 1;
+
+    g_main_loop_quit(inner_loop);
+}
+
+static void
+test_match_combo(const char *sender,
+                 const char *path,
+                 const char *iface,
+                 const char *member)
+{
+    signal_received_count = 0;
+    destroy_notify_count = 0;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "Watching %s %s %s %s",
+              sender,
+              path,
+              iface,
+              member);
+
+    big_dbus_watch_signal(DBUS_BUS_SESSION,
+                          sender,
+                          path,
+                          iface,
+                          member,
+                          expect_receive_signal_handler,
+                          GINT_TO_POINTER(1),
+                          the_destroy_notifier);
+
+    big_dbus_proxy_call_json_async(test_service_proxy,
+                                   "emitTheSignal",
+                                   NULL,
+                                   NULL,
+                                   NULL,
+                                   NULL);
+    g_main_loop_run(inner_loop);
+
+    g_assert(signal_received_count == 1);
+    g_assert(destroy_notify_count == 0);
+
+    big_dbus_unwatch_signal(DBUS_BUS_SESSION,
+                            sender,
+                            path,
+                            iface,
+                            member,
+                            expect_receive_signal_handler,
+                            GINT_TO_POINTER(1));
+
+    g_assert(destroy_notify_count == 1);
+}
+
+static gboolean
+run_signal_tests_idle(void *data)
+{
+    int i;
+    const char *unique_name;
+
+    for (i = 0; i < (int) G_N_ELEMENTS(watch_tests); ++i) {
+        SignalWatchTest *test = &watch_tests[i];
+
+        test_match_combo(test->sender,
+                         test->path,
+                         test->iface,
+                         test->member);
+    }
+
+    /* Now try on the unique bus name */
+
+    unique_name = big_dbus_proxy_get_bus_name(test_service_proxy);
+
+    test_match_combo(unique_name,
+                     NULL, NULL, NULL);
+
+    /* Now test we get destroy notify when the unique name disappears
+     * on killing the child.
+     */
+    signal_received_count = 0;
+    destroy_notify_count = 0;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "Watching unique name %s",
+              unique_name);
+
+    big_dbus_watch_signal(DBUS_BUS_SESSION,
+                          unique_name,
+                          NULL, NULL, NULL,
+                          expect_receive_signal_handler,
+                          GINT_TO_POINTER(1),
+                          the_destroy_notifier_that_quits);
+
+    /* kill owner of unique_name */
+    kill_child();
+
+    /* wait for destroy notify */
+    g_main_loop_run(inner_loop);
+
+    g_assert(signal_received_count == 0);
+    /* roundabout way to write == 1 that gives more info on fail */
+    g_assert(destroy_notify_count > 0);
+    g_assert(destroy_notify_count < 2);
+
+    big_dbus_unwatch_signal(DBUS_BUS_SESSION,
+                            unique_name,
+                            NULL, NULL, NULL,
+                            expect_receive_signal_handler,
+                            GINT_TO_POINTER(1));
+
+    g_assert(signal_received_count == 0);
+    g_assert(destroy_notify_count == 1);
+
+    /* remove idle */
+    return FALSE;
+}
+
+static void
+on_test_service_appeared(DBusConnection *connection,
+                         const char     *name,
+                         const char     *new_owner_unique_name,
+                         void           *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s appeared",
+              name);
+
+    inner_loop = g_main_loop_new(NULL, FALSE);
+
+    test_service_proxy =
+        big_dbus_proxy_new(connection, new_owner_unique_name,
+                           "/com/litl/test/object42",
+                           "com.litl.TestIface");
+
+    g_idle_add(run_signal_tests_idle, NULL);
+}
+
+static void
+on_test_service_vanished(DBusConnection *connection,
+                         const char     *name,
+                         const char     *old_owner_unique_name,
+                         void           *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s vanished", name);
+
+    another_child_down();
+}
+
+static BigDBusWatchNameFuncs watch_test_service_funcs = {
+    on_test_service_appeared,
+    on_test_service_vanished
+};
+
+void
+bigtest_test_func_util_dbus_signals_client(void)
+{
+    pid_t result;
+    int status;
+
+    /* See comment in dbus.c above the g_test_trap_fork()
+     * there on why we have to do this.
+     */
+    if (!g_test_trap_fork(0, 0)) {
+        /* We are the parent */
+        g_test_trap_assert_passed();
+        return;
+    }
+
+    /* All this code runs in a child process */
+
+    fork_test_signal_service();
+
+    g_type_init();
+
+    /* We rely on the child-forking test functions being called first */
+    g_assert(test_service_pid != 0);
+
+    big_dbus_watch_name(DBUS_BUS_SESSION,
+                        "com.litl.TestService",
+                        0,
+                        &watch_test_service_funcs,
+                        NULL);
+
+    outer_loop = g_main_loop_new(NULL, FALSE);
+
+    g_main_loop_run(outer_loop);
+
+    if (test_service_proxy != NULL)
+        g_object_unref(test_service_proxy);
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "waitpid() for first child");
+
+    result = waitpid(test_service_pid, &status, 0);
+    if (result < 0) {
+        g_error("Failed to waitpid() for forked child: %s", strerror(errno));
+    }
+
+    if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+        g_error("Forked dbus service child exited with error code %d", WEXITSTATUS(status));
+    }
+
+    if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
+        g_error("Forked dbus service child exited on wrong signal number %d", WTERMSIG(status));
+    }
+
+    big_debug(BIG_DEBUG_IN_TESTS, "dbus signals test completed");
+
+    /* We want to kill dbus so the weak refs are NULL to start the
+     * next dbus-related test, which allows those tests
+     * to fork new child processes.
+     */
+    _big_dbus_dispose_info(_big_dbus_get_weak_ref(DBUS_BUS_SESSION));
+    dbus_shutdown();
+
+    big_debug(BIG_DEBUG_IN_TESTS, "dbus shut down");
+
+    /* FIXME this is only here because we've forked */
+    exit(0);
+}
+
+/*
+ * Child service that emits signals
+ */
+
+static gboolean currently_have_test_service = FALSE;
+static GObject *test_service_object = NULL;
+
+static void
+test_service_emit_the_signal(DBusConnection  *connection,
+                             DBusMessage     *message,
+                             DBusMessageIter *in_iter,
+                             DBusMessageIter *out_iter,
+                             void            *data,
+                             DBusError       *error)
+{
+    DBusMessage *signal;
+
+    signal = dbus_message_new_signal("/com/litl/test/object42",
+                                     "com.litl.TestIface",
+                                     "TheSignal");
+    dbus_connection_send(connection, signal, NULL);
+    dbus_message_unref(signal);
+}
+
+static BigDBusJsonMethod test_service_methods[] = {
+    { "emitTheSignal", test_service_emit_the_signal, NULL }
+};
+
+static void
+on_test_service_acquired(DBusConnection *connection,
+                         const char     *name,
+                         void           *data)
+{
+    g_assert(!currently_have_test_service);
+    currently_have_test_service = TRUE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService acquired by child");
+
+    big_dbus_register_json(connection,
+                           "com.litl.TestIface",
+                           test_service_methods,
+                           G_N_ELEMENTS(test_service_methods));
+
+    test_service_object = g_object_new(G_TYPE_OBJECT, NULL);
+
+    big_dbus_register_g_object(connection,
+                               "/com/litl/test/object42",
+                               test_service_object,
+                               "com.litl.TestIface");
+}
+
+static void
+on_test_service_lost(DBusConnection *connection,
+                     const char     *name,
+                     void           *data)
+{
+    g_assert(currently_have_test_service);
+    currently_have_test_service = FALSE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService lost by child");
+
+    big_dbus_unregister_g_object(connection,
+                                 "/com/litl/test/object42");
+
+    big_dbus_unregister_json(connection,
+                             "com.litl.TestIface");
+}
+
+static BigDBusNameOwnerFuncs test_service_funcs = {
+    "com.litl.TestService",
+    DBUS_BUS_SESSION,
+    on_test_service_acquired,
+    on_test_service_lost
+};
+
+static void
+do_test_service_child(void)
+{
+    GMainLoop *loop;
+
+    g_type_init();
+
+    loop = g_main_loop_new(NULL, FALSE);
+
+    big_dbus_acquire_name(DBUS_BUS_SESSION,
+                          &test_service_funcs,
+                          NULL);
+
+    g_main_loop_run(loop);
+
+    /* Don't return to the test program main() */
+    exit(0);
+}
+
+#endif /* BIG_BUILD_TESTS */
diff --git a/modules/dbus/util/dbus.c b/modules/dbus/util/dbus.c
new file mode 100644
index 0000000..ec46679
--- /dev/null
+++ b/modules/dbus/util/dbus.c
@@ -0,0 +1,3020 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#include <config.h>
+
+#include "dbus.h"
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "dbus-private.h"
+#include "dbus-proxy.h"
+#include "log.h"
+#include "glib.h"
+
+typedef struct {
+    const BigDBusConnectFuncs *funcs;
+    void *data;
+    unsigned int opened : 1;
+} ConnectFuncs;
+
+typedef enum {
+    NAME_NOT_REQUESTED,
+    NAME_PRIMARY_OWNER,
+    NAME_IN_QUEUE,
+    NAME_NOT_OWNED
+} NameOwnershipState;
+
+typedef struct {
+    char *name;
+    const BigDBusJsonMethod *methods;
+    int n_methods;
+} BigJsonIface;
+
+typedef struct {
+    DBusBusType bus_type;
+    /* If prev_state != state then we may need to notify */
+    NameOwnershipState prev_state;
+    NameOwnershipState state;
+    const BigDBusNameOwnerFuncs *funcs;
+    void *data;
+    unsigned int id;
+} BigNameOwnershipMonitor;
+
+typedef struct {
+    char *name;
+    char *current_owner;
+    GSList *watchers;
+} BigNameWatch;
+
+typedef struct {
+    BigDBusWatchNameFlags flags;
+    const BigDBusWatchNameFuncs *funcs;
+    void *data;
+    DBusBusType bus_type;
+    BigNameWatch *watch;
+    guint notify_idle;
+    int refcount;
+    guint destroyed : 1;
+} BigNameWatcher;
+
+typedef struct {
+    DBusBusType bus_type;
+    char *name;
+    BigNameWatcher *watcher;
+} BigPendingNameWatcher;
+
+static DBusConnection *session_bus_weak_ref = NULL;
+static GSList *session_bus_weak_refs = NULL;
+static DBusConnection *system_bus_weak_ref = NULL;
+static GSList *system_bus_weak_refs = NULL;
+static guint session_connect_idle_id = 0;
+static guint system_connect_idle_id = 0;
+static GSList *all_connect_funcs = NULL;
+
+static GSList *pending_name_ownership_monitors = NULL;
+static GSList *pending_name_watchers = NULL;
+
+#define BIG_DBUS_NAME_OWNER_MONITOR_INVALID_ID 0
+
+static unsigned int global_monitor_id = 0;
+
+static DBusHandlerResult disconnect_filter_message             (DBusConnection   *connection,
+                                                                DBusMessage      *message,
+                                                                void             *data);
+static DBusHandlerResult name_ownership_monitor_filter_message (DBusConnection   *connection,
+                                                                DBusMessage      *message,
+                                                                void             *data);
+static void              process_name_ownership_monitors       (DBusConnection   *connection,
+                                                                BigDBusInfo      *info);
+static void              name_watch_remove_watcher             (BigNameWatch     *watch,
+                                                                BigNameWatcher   *watcher);
+static DBusHandlerResult name_watch_filter_message             (DBusConnection   *connection,
+                                                                DBusMessage      *message,
+                                                                void             *data);
+static void              process_pending_name_watchers         (DBusConnection   *connection,
+                                                                BigDBusInfo      *info);
+static void              json_iface_free                       (BigJsonIface     *iface);
+static void              info_free                             (BigDBusInfo      *info);
+static gboolean          notify_watcher_name_appeared          (gpointer data);
+
+static dbus_int32_t info_slot = -1;
+BigDBusInfo*
+_big_dbus_ensure_info(DBusConnection *connection)
+{
+    BigDBusInfo *info;
+
+    dbus_connection_allocate_data_slot(&info_slot);
+
+    info = dbus_connection_get_data(connection, info_slot);
+
+    if (info == NULL) {
+        info = g_slice_new0(BigDBusInfo);
+
+        info->where_connection_was = connection;
+
+        if (connection == session_bus_weak_ref)
+            info->bus_type = DBUS_BUS_SESSION;
+        else if (connection == system_bus_weak_ref)
+            info->bus_type = DBUS_BUS_SYSTEM;
+        else
+            g_error("Unknown bus type opened in %s", __FILE__);
+
+        info->json_ifaces = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                  NULL, (GFreeFunc) json_iface_free);
+        info->name_watches = g_hash_table_new(g_str_hash, g_str_equal);
+        dbus_connection_set_data(connection, info_slot, info, (DBusFreeFunction) info_free);
+
+        dbus_connection_add_filter(connection, name_ownership_monitor_filter_message,
+                                   NULL, NULL);
+        dbus_connection_add_filter(connection, name_watch_filter_message,
+                                   NULL, NULL);
+        dbus_connection_add_filter(connection, _big_dbus_signal_watch_filter_message,
+                                   NULL, NULL);
+
+        /* Important: disconnect_filter_message() must be LAST so
+         * it runs last when the disconnect message arrives.
+         */
+        dbus_connection_add_filter(connection, disconnect_filter_message,
+                                   NULL, NULL);
+
+        /* caution, this could get circular if proxy_new() goes back around
+         * and tries to use dbus.c - but we'll fix it when it happens.
+         * Also, this refs the connection ...
+         */
+        info->driver_proxy =
+            big_dbus_proxy_new(connection,
+                               DBUS_SERVICE_DBUS,
+                               DBUS_PATH_DBUS,
+                               DBUS_INTERFACE_DBUS);
+    }
+
+    return info;
+}
+
+void
+_big_dbus_dispose_info(DBusConnection *connection)
+{
+    BigDBusInfo *info;
+
+    if (info_slot < 0)
+        return;
+
+    info = dbus_connection_get_data(connection, info_slot);
+
+    if (info != NULL) {
+
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "Disposing info on connection %p",
+                  connection);
+
+        /* the driver proxy refs the connection, we want
+         * to break that cycle.
+         */
+        g_object_unref(info->driver_proxy);
+        info->driver_proxy = NULL;
+
+        dbus_connection_set_data(connection, info_slot, NULL, NULL);
+
+        dbus_connection_free_data_slot(&info_slot);
+    }
+}
+
+DBusConnection*
+_big_dbus_get_weak_ref(DBusBusType which_bus)
+{
+    if (which_bus == DBUS_BUS_SESSION) {
+        return session_bus_weak_ref;
+    } else if (which_bus == DBUS_BUS_SYSTEM) {
+        return system_bus_weak_ref;
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+static DBusHandlerResult
+disconnect_filter_message(DBusConnection   *connection,
+                          DBusMessage      *message,
+                          void             *data)
+{
+    /* We should be running after all other filters */
+    if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+        big_debug(BIG_DEBUG_UTIL_DBUS, "Disconnected in %s", G_STRFUNC);
+
+        _big_dbus_dispose_info(connection);
+
+        if (session_bus_weak_ref == connection)
+            session_bus_weak_ref = NULL;
+
+        if (system_bus_weak_ref == connection)
+            system_bus_weak_ref = NULL;
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusConnection*
+try_connecting(DBusBusType which_bus)
+{
+
+    DBusGConnection *gconnection;
+    DBusConnection *connection;
+    GError *error;
+
+    connection = _big_dbus_get_weak_ref(which_bus);
+    if (connection != NULL)
+        return connection;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "trying to connect to message bus");
+
+    error = NULL;
+    gconnection = dbus_g_bus_get(which_bus,
+                                 &error);
+    if (gconnection == NULL) {
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "bus connection failed: %s",
+                  error->message);
+        g_error_free(error);
+        return NULL;
+    }
+
+    connection = dbus_g_connection_get_connection(gconnection);
+
+    /* Disable this because all our apps will be well-behaved! */
+    dbus_connection_set_exit_on_disconnect(connection, FALSE);
+
+    if (which_bus == DBUS_BUS_SESSION &&
+        session_bus_weak_ref == NULL) {
+        GSList *l;
+        session_bus_weak_ref = connection;
+        for (l = session_bus_weak_refs; l != NULL; l = l->next) {
+            DBusConnection **connection_p = l->data;
+            *connection_p = session_bus_weak_ref;
+        }
+    } else if (which_bus == DBUS_BUS_SYSTEM &&
+               system_bus_weak_ref == NULL) {
+        GSList *l;
+        system_bus_weak_ref = connection;
+        for (l = system_bus_weak_refs; l != NULL; l = l->next) {
+            DBusConnection **connection_p = l->data;
+            *connection_p = system_bus_weak_ref;
+        }
+    }
+
+    dbus_g_connection_unref(gconnection); /* rely on libdbus holding a ref */
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Successfully connected");
+
+    return connection;
+}
+
+static gboolean
+connect_idle(void *data)
+{
+    GSList *l;
+    DBusConnection *connection;
+    BigDBusInfo *info;
+    DBusBusType bus_type;
+
+    bus_type = GPOINTER_TO_INT(data);
+
+    if (bus_type == DBUS_BUS_SESSION)
+        session_connect_idle_id = 0;
+    else if (bus_type == DBUS_BUS_SYSTEM)
+        system_connect_idle_id = 0;
+    else
+        g_assert_not_reached();
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "connection idle with %d connect listeners to traverse", g_slist_length(all_connect_funcs));
+
+    connection = try_connecting(bus_type);
+    if (connection == NULL) {
+        if (bus_type == DBUS_BUS_SESSION) {
+            g_printerr("Lost connection to session bus, exiting\n");
+            exit(1);
+        } else {
+            /* Here it would theoretically make sense to reinstall the
+             * idle as a timeout or something, but we don't for now,
+             * just wait for something to trigger a reconnect. It is
+             * not a situation that should happen in reality (we won't
+             * restart the system bus without rebooting).
+             */
+        }
+        return FALSE;
+    }
+
+    info = _big_dbus_ensure_info(connection);
+
+    /* We first need to call AddMatch on all signal watchers.
+     * This is so if on connect, the app calls methods to get
+     * the state the signal notifies the app of changes in,
+     * the match rule is added before the "get current state"
+     * methods are called. Otherwise there's a race where
+     * a signal can be missed between a "get current state" method
+     * call reply and the AddMatch.
+     */
+    _big_dbus_process_pending_signal_watchers(connection, info);
+
+    /* We want the app to see notification of connection opening,
+     * THEN other notifications, so notify it's open first.
+     */
+
+    for (l = all_connect_funcs; l != NULL; l = l->next) {
+        ConnectFuncs *f;
+        f = l->data;
+
+        if (!f->opened && f->funcs->which_bus == bus_type) {
+            f->opened = TRUE;
+            (* f->funcs->opened) (connection, f->data);
+        }
+    }
+
+    /* These two invoke application callbacks, unlike
+     * _big_dbus_process_pending_signal_watchers(), so should come after
+     * the above calls to the "connection opened" callbacks.
+     */
+
+    process_name_ownership_monitors(connection, info);
+
+    process_pending_name_watchers(connection, info);
+
+    return FALSE;
+}
+
+void
+_big_dbus_ensure_connect_idle(DBusBusType bus_type)
+{
+    if (bus_type == DBUS_BUS_SESSION) {
+        if (session_connect_idle_id == 0) {
+            session_connect_idle_id = g_idle_add(connect_idle, GINT_TO_POINTER(bus_type));
+        }
+    } else if (bus_type == DBUS_BUS_SYSTEM) {
+        if (system_connect_idle_id == 0) {
+            system_connect_idle_id = g_idle_add(connect_idle, GINT_TO_POINTER(bus_type));
+        }
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+static void
+internal_add_connect_funcs(const BigDBusConnectFuncs *funcs,
+                           void                      *data,
+                           gboolean                   sync_notify)
+{
+    ConnectFuncs *f;
+
+    f = g_slice_new0(ConnectFuncs);
+    f->funcs = funcs;
+    f->data = data;
+    f->opened = FALSE;
+
+    all_connect_funcs = g_slist_prepend(all_connect_funcs, f);
+
+    _big_dbus_ensure_connect_idle(f->funcs->which_bus);
+
+    if (sync_notify) {
+        /* sync_notify means IF we are already connected
+         * (we have a weak ref != NULL) then notify
+         * right away before we return.
+         */
+        DBusConnection *connection;
+
+        connection = _big_dbus_get_weak_ref(f->funcs->which_bus);
+
+        if (connection != NULL && !f->opened) {
+            f->opened = TRUE;
+            (* f->funcs->opened) (connection, f->data);
+        }
+    }
+}
+
+/* this should guarantee that the funcs are only called async, which is why
+ * it does not try_connecting right away; the idea is to defer to inside the
+ * main loop.
+ */
+void
+big_dbus_add_connect_funcs(const BigDBusConnectFuncs *funcs,
+                           void                      *data)
+{
+    internal_add_connect_funcs(funcs, data, FALSE);
+}
+
+/* The sync_notify flavor calls the open notification right away if
+ * we are already connected.
+ */
+void
+big_dbus_add_connect_funcs_sync_notify(const BigDBusConnectFuncs *funcs,
+                                       void                      *data)
+{
+    internal_add_connect_funcs(funcs, data, TRUE);
+}
+
+void
+big_dbus_remove_connect_funcs(const BigDBusConnectFuncs *funcs,
+                              void                      *data)
+{
+    ConnectFuncs *f;
+    GSList *l;
+
+    f = NULL;
+    for (l = all_connect_funcs; l != NULL; l = l->next) {
+        f = l->data;
+
+        if (f->funcs == funcs &&
+            f->data == data)
+            break;
+    }
+
+    if (l == NULL) {
+        g_warning("Could not find functions matching %p %p", funcs, data);
+        return;
+    }
+    g_assert(l->data == f);
+
+    all_connect_funcs = g_slist_delete_link(all_connect_funcs, l);
+    g_slice_free(ConnectFuncs, f);
+}
+
+void
+big_dbus_add_bus_weakref(DBusBusType      which_bus,
+                         DBusConnection **connection_p)
+{
+    if (which_bus == DBUS_BUS_SESSION) {
+        *connection_p = session_bus_weak_ref;
+        session_bus_weak_refs = g_slist_prepend(session_bus_weak_refs, connection_p);
+    } else if (which_bus == DBUS_BUS_SYSTEM) {
+        *connection_p = system_bus_weak_ref;
+        system_bus_weak_refs = g_slist_prepend(system_bus_weak_refs, connection_p);
+    } else {
+        g_assert_not_reached();
+    }
+
+    _big_dbus_ensure_connect_idle(which_bus);
+}
+
+void
+big_dbus_remove_bus_weakref(DBusBusType      which_bus,
+                            DBusConnection **connection_p)
+{
+    if (which_bus == DBUS_BUS_SESSION) {
+        *connection_p = NULL;
+        session_bus_weak_refs = g_slist_remove(session_bus_weak_refs, connection_p);
+    } else if (which_bus == DBUS_BUS_SYSTEM) {
+        *connection_p = NULL;
+        system_bus_weak_refs = g_slist_remove(system_bus_weak_refs, connection_p);
+    } else {
+        g_assert_not_reached();
+    }
+}
+
+void
+big_dbus_try_connecting_now(DBusBusType which_bus)
+{
+    try_connecting(which_bus);
+}
+
+static BigJsonIface*
+json_iface_new(const char *name,
+               const BigDBusJsonMethod *methods,
+               int n_methods)
+{
+    BigJsonIface *iface;
+
+    iface = g_slice_new0(BigJsonIface);
+    iface->name = g_strdup(name);
+    iface->methods = methods;
+    iface->n_methods = n_methods;
+
+    return iface;
+}
+
+static void
+json_iface_free(BigJsonIface *iface)
+{
+    g_free(iface->name);
+    g_slice_free(BigJsonIface, iface);
+}
+
+static BigNameOwnershipMonitor*
+name_ownership_monitor_new(DBusBusType                  bus_type,
+                           const BigDBusNameOwnerFuncs *funcs,
+                           void                        *data)
+{
+    BigNameOwnershipMonitor *monitor;
+
+    monitor = g_slice_new0(BigNameOwnershipMonitor);
+    monitor->bus_type = bus_type;
+    monitor->prev_state = NAME_NOT_REQUESTED;
+    monitor->state = NAME_NOT_REQUESTED;
+    monitor->funcs = funcs;
+    monitor->data = data;
+    monitor->id = ++global_monitor_id;
+
+    return  monitor;
+}
+
+static void
+name_ownership_monitor_free(BigNameOwnershipMonitor *monitor)
+{
+
+    g_slice_free(BigNameOwnershipMonitor, monitor);
+}
+
+static BigNameWatch*
+name_watch_new(const char *name)
+{
+    BigNameWatch *watch;
+
+    watch = g_slice_new0(BigNameWatch);
+    watch->name = g_strdup(name);
+
+    /* For unique names, we assume the owner is itself,
+     * so we default to "exists" and maybe emit "vanished",
+     * while with well-known names we do the opposite.
+     */
+    if (*watch->name == ':') {
+        watch->current_owner = g_strdup(watch->name);
+    }
+
+    return watch;
+}
+
+static void
+name_watch_free(BigNameWatch *watch)
+{
+    g_assert(watch->watchers == NULL);
+
+    g_free(watch->name);
+    g_free(watch->current_owner);
+    g_slice_free(BigNameWatch, watch);
+}
+
+static BigNameWatcher*
+name_watcher_new(BigDBusWatchNameFlags        flags,
+                 const BigDBusWatchNameFuncs *funcs,
+                 void                        *data,
+                 DBusBusType                  bus_type)
+{
+    BigNameWatcher *watcher;
+
+    watcher = g_slice_new0(BigNameWatcher);
+    watcher->flags = flags;
+    watcher->funcs = funcs;
+    watcher->data = data;
+    watcher->bus_type = bus_type;
+    watcher->watch = NULL;
+    watcher->refcount = 1;
+
+    return watcher;
+}
+
+static void
+name_watcher_ref(BigNameWatcher *watcher)
+{
+    watcher->refcount += 1;
+}
+
+static void
+name_watcher_unref(BigNameWatcher *watcher)
+{
+    watcher->refcount -= 1;
+
+    if (watcher->refcount == 0)
+        g_slice_free(BigNameWatcher, watcher);
+}
+
+static void
+info_free(BigDBusInfo *info)
+{
+    void *key;
+    void *value;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Destroy notify invoked on bus connection info for %p",
+              info->where_connection_was);
+
+    if (info->where_connection_was == session_bus_weak_ref)
+        session_bus_weak_ref = NULL;
+
+    if (info->where_connection_was == system_bus_weak_ref)
+        system_bus_weak_ref = NULL;
+
+    /* This could create some strange re-entrancy so do it first.
+     * If we processed a disconnect message, this should have been done
+     * already at that time, but if we were finalized without that,
+     * it may not have been.
+     */
+    if (info->driver_proxy != NULL) {
+        g_object_unref(info->driver_proxy);
+        info->driver_proxy = NULL;
+    }
+
+    while (info->name_ownership_monitors != NULL) {
+        name_ownership_monitor_free(info->name_ownership_monitors->data);
+        info->name_ownership_monitors = g_slist_remove(info->name_ownership_monitors,
+                                                       info->name_ownership_monitors->data);
+    }
+
+    while (big_g_hash_table_steal_one(info->name_watches, &key, &value)) {
+        BigNameWatch *watch = value;
+
+        while (watch->watchers) {
+            name_watch_remove_watcher(watch, watch->watchers->data);
+        }
+
+        name_watch_free(watch);
+    }
+
+    if (info->signal_watchers_by_unique_sender) {
+        g_hash_table_destroy(info->signal_watchers_by_unique_sender);
+    }
+
+    if (info->signal_watchers_by_path) {
+        g_hash_table_destroy(info->signal_watchers_by_path);
+    }
+
+    if (info->signal_watchers_by_iface) {
+        g_hash_table_destroy(info->signal_watchers_by_iface);
+    }
+
+    if (info->signal_watchers_by_signal) {
+        g_hash_table_destroy(info->signal_watchers_by_signal);
+    }
+
+    g_hash_table_destroy(info->name_watches);
+    g_hash_table_destroy(info->json_ifaces);
+    g_slice_free(BigDBusInfo, info);
+}
+
+static DBusHandlerResult
+name_ownership_monitor_filter_message(DBusConnection *connection,
+                                      DBusMessage    *message,
+                                      void           *data)
+{
+    BigDBusInfo *info;
+    gboolean states_changed;
+
+    info = _big_dbus_ensure_info(connection);
+
+    states_changed = FALSE;
+
+    if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameLost") &&
+        dbus_message_has_sender(message, DBUS_SERVICE_DBUS)) {
+        const char *name = NULL;
+        if (dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_STRING, &name,
+                                  DBUS_TYPE_INVALID)) {
+            GSList *l;
+
+            big_debug(BIG_DEBUG_UTIL_DBUS, "Lost name %s", name);
+
+            for (l = info->name_ownership_monitors; l != NULL; l = l->next) {
+                BigNameOwnershipMonitor *monitor;
+
+                monitor = l->data;
+
+                if (monitor->state == NAME_PRIMARY_OWNER &&
+                    strcmp(name, monitor->funcs->name) == 0) {
+                    monitor->prev_state = monitor->state;
+                    monitor->state = NAME_NOT_OWNED;
+                    states_changed = TRUE;
+                    /* keep going, don't break, there may be more matches */
+                }
+            }
+        } else {
+            big_debug(BIG_DEBUG_UTIL_DBUS, "NameLost has wrong arguments???");
+        }
+    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameAcquired") &&
+               dbus_message_has_sender(message, DBUS_SERVICE_DBUS)) {
+        const char *name = NULL;
+        if (dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_STRING, &name,
+                                  DBUS_TYPE_INVALID)) {
+            GSList *l;
+
+            big_debug(BIG_DEBUG_UTIL_DBUS, "Acquired name %s", name);
+
+            for (l = info->name_ownership_monitors; l != NULL; l = l->next) {
+                BigNameOwnershipMonitor *monitor;
+
+                monitor = l->data;
+
+                if (monitor->state != NAME_PRIMARY_OWNER &&
+                    strcmp(name, monitor->funcs->name) == 0) {
+                    monitor->prev_state = monitor->state;
+                    monitor->state = NAME_PRIMARY_OWNER;
+                    states_changed = TRUE;
+                    /* keep going, don't break, there may be more matches */
+                }
+            }
+        } else {
+            big_debug(BIG_DEBUG_UTIL_DBUS, "NameAcquired has wrong arguments???");
+        }
+    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+        GSList *l;
+
+        big_debug(BIG_DEBUG_UTIL_DBUS, "Disconnected in %s", G_STRFUNC);
+
+        for (l = info->name_ownership_monitors; l != NULL; l = l->next) {
+            BigNameOwnershipMonitor *monitor;
+
+            monitor = l->data;
+
+            if (monitor->state != NAME_NOT_REQUESTED) {
+                /* Set things up to re-request the name */
+                monitor->prev_state = monitor->state;
+                monitor->state = NAME_NOT_REQUESTED;
+                states_changed = TRUE;
+            }
+        }
+
+        /* FIXME move the monitors back to the pending list so they'll be found on reconnect */
+    }
+
+    if (states_changed)
+        process_name_ownership_monitors(connection, info);
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+process_name_ownership_monitors(DBusConnection *connection,
+                                BigDBusInfo    *info)
+{
+    GSList *l;
+    gboolean connected;
+    GSList *still_pending;
+
+    /* First pull anything out of pending queue */
+
+    still_pending = NULL;
+    while (pending_name_ownership_monitors != NULL) {
+        BigNameOwnershipMonitor *monitor;
+
+        monitor = pending_name_ownership_monitors->data;
+        pending_name_ownership_monitors =
+            g_slist_remove(pending_name_ownership_monitors,
+                           pending_name_ownership_monitors->data);
+
+        if (monitor->bus_type == info->bus_type) {
+            info->name_ownership_monitors =
+                g_slist_prepend(info->name_ownership_monitors,
+                                monitor);
+        } else {
+            still_pending = g_slist_prepend(still_pending, monitor);
+        }
+    }
+    g_assert(pending_name_ownership_monitors == NULL);
+    pending_name_ownership_monitors = still_pending;
+
+    /* Now send notifications to the app */
+
+    connected = dbus_connection_get_is_connected(connection);
+
+    if (connected) {
+        for (l = info->name_ownership_monitors; l != NULL; l = l->next) {
+            BigNameOwnershipMonitor *monitor;
+
+            monitor = l->data;
+
+            if (monitor->state == NAME_NOT_REQUESTED) {
+                int result;
+                unsigned int flags;
+                DBusError derror;
+
+                flags = DBUS_NAME_FLAG_ALLOW_REPLACEMENT;
+                if (monitor->funcs->type == BIG_DBUS_NAME_SINGLE_INSTANCE)
+                    flags |= DBUS_NAME_FLAG_DO_NOT_QUEUE;
+
+                dbus_error_init(&derror);
+                result = dbus_bus_request_name(connection,
+                                               monitor->funcs->name,
+                                               flags,
+                                               &derror);
+
+                /* log 'error' word only when one occurred */
+                if (derror.message != NULL) {
+                    big_debug(BIG_DEBUG_UTIL_DBUS, "Requested name %s result %d error %s",
+                              monitor->funcs->name, result, derror.message);
+                } else {
+                    big_debug(BIG_DEBUG_UTIL_DBUS, "Requested name %s result %d",
+                              monitor->funcs->name, result);
+                }
+
+                dbus_error_free(&derror);
+
+                /* An important feature of this code is that we always
+                 * transition from NOT_REQUESTED to something else when
+                 * a name monitor is first added, so we always notify
+                 * the app either "acquired" or "lost" and don't
+                 * leave the app in limbo.
+                 *
+                 * This means the app can "get going" when it gets the name
+                 * and exit when it loses it, and that will just work
+                 * since one or the other will always happen on startup.
+                 */
+
+                monitor->prev_state = monitor->state;
+
+                if (result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
+                    result == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) {
+                    monitor->state = NAME_PRIMARY_OWNER;
+                } else if (result == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) {
+                    monitor->state = NAME_IN_QUEUE;
+                } else if (result == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+                    monitor->state = NAME_NOT_OWNED;
+                } else {
+                    /* reply code we don't understand? */
+                    monitor->state = NAME_NOT_OWNED;
+                }
+            }
+        }
+    }
+
+    /* Do notifications with a list copy for extra safety
+     * (for true safety we also need to refcount each monitor
+     * and have a "destroyed" flag)
+     */
+    l = g_slist_copy(info->name_ownership_monitors);
+    while (l != NULL) {
+        BigNameOwnershipMonitor *monitor;
+
+        monitor = l->data;
+        l = g_slist_remove(l, l->data);
+
+        if (monitor->prev_state != monitor->state) {
+            monitor->prev_state = monitor->state;
+
+            if (monitor->state == NAME_PRIMARY_OWNER) {
+                big_debug(BIG_DEBUG_UTIL_DBUS, "Notifying acquired %s",
+                          monitor->funcs->name);
+                (* monitor->funcs->acquired) (connection, monitor->funcs->name, monitor->data);
+            } else if (monitor->state != NAME_PRIMARY_OWNER) {
+                big_debug(BIG_DEBUG_UTIL_DBUS, "Notifying lost %s",
+                          monitor->funcs->name);
+                (* monitor->funcs->lost) (connection, monitor->funcs->name, monitor->data);
+            }
+        }
+    }
+}
+
+unsigned int
+big_dbus_acquire_name (DBusBusType                  bus_type,
+                       const BigDBusNameOwnerFuncs *funcs,
+                       void                        *data)
+{
+    BigNameOwnershipMonitor *monitor;
+
+    monitor = name_ownership_monitor_new(bus_type, funcs, data);
+    pending_name_ownership_monitors = g_slist_prepend(pending_name_ownership_monitors, monitor);
+
+    _big_dbus_ensure_connect_idle(bus_type);
+
+    return monitor->id;
+}
+
+static void
+release_name_internal (DBusBusType                  bus_type,
+                       const BigDBusNameOwnerFuncs *funcs,
+                       void                        *data,
+                       unsigned int                 id)
+{
+    BigDBusInfo *info;
+    GSList *l;
+    BigNameOwnershipMonitor *monitor;
+    DBusConnection *connection;
+
+    connection = _big_dbus_get_weak_ref(bus_type);
+    if (!connection)
+        return;
+
+    info = _big_dbus_ensure_info(connection);
+
+    /* Check first pending list */
+    for (l = pending_name_ownership_monitors; l; l = l->next) {
+        monitor = l->data;
+        /* If the id is valid an matches, we are done */
+        if (monitor->state == NAME_PRIMARY_OWNER &&
+            ((id != BIG_DBUS_NAME_OWNER_MONITOR_INVALID_ID && monitor->id == id) ||
+             (monitor->funcs == funcs &&
+              monitor->data == data))) {
+            dbus_bus_release_name(connection, monitor->funcs->name, NULL);
+            pending_name_ownership_monitors =
+                g_slist_remove(pending_name_ownership_monitors,
+                               monitor);
+            name_ownership_monitor_free(monitor);
+            /* If the monitor was in the pending list it
+             * can't be in the processed list
+             */
+            return;
+        }
+    }
+
+    for (l = info->name_ownership_monitors; l; l = l->next) {
+        monitor = l->data;
+        /* If the id is valid an matches, we are done */
+        if (monitor->state == NAME_PRIMARY_OWNER &&
+            ((id != BIG_DBUS_NAME_OWNER_MONITOR_INVALID_ID && monitor->id == id) ||
+             (monitor->funcs == funcs &&
+              monitor->data == data))) {
+            dbus_bus_release_name(connection, monitor->funcs->name, NULL);
+            info->name_ownership_monitors = g_slist_remove(info->name_ownership_monitors,
+                                                           monitor);
+            name_ownership_monitor_free(monitor);
+            break;
+        }
+    }
+}
+
+void
+big_dbus_release_name_by_id (DBusBusType  bus_type,
+                             unsigned int id)
+{
+    release_name_internal(bus_type, NULL, NULL, id);
+}
+
+void
+big_dbus_release_name (DBusBusType                  bus_type,
+                       const BigDBusNameOwnerFuncs *funcs,
+                       void                        *data)
+{
+    release_name_internal(bus_type, funcs, data,
+                          BIG_DBUS_NAME_OWNER_MONITOR_INVALID_ID);
+}
+
+static void
+notify_name_owner_changed(DBusConnection *connection,
+                          const char     *name,
+                          const char     *new_owner)
+{
+    BigDBusInfo *info;
+    BigNameWatch *watch;
+    GSList *l, *watchers;
+    gchar *old_owner;
+
+    info = _big_dbus_ensure_info(connection);
+
+    if (*new_owner == '\0')
+        new_owner = NULL;
+
+    watch = g_hash_table_lookup(info->name_watches, name);
+
+    if (watch == NULL)
+        return;
+
+    if ((watch->current_owner == new_owner) ||
+        (watch->current_owner && new_owner &&
+         strcmp(watch->current_owner, new_owner) == 0)) {
+        /* No change */
+        return;
+    }
+
+    /* we copy the list before iterating, because the
+     * callbacks may modify it */
+    watchers = g_slist_copy(watch->watchers);
+    g_slist_foreach(watchers, (GFunc)name_watcher_ref, NULL);
+
+    /* copy the old owner in case the watch is removed in
+     * the callbacks */
+    old_owner = g_strdup(watch->current_owner);
+
+    /* vanish the old owner */
+    if (old_owner != NULL) {
+        for (l = watchers;
+             l != NULL;
+             l = l->next) {
+            BigNameWatcher *watcher = l->data;
+
+            if (watcher->notify_idle != 0) {
+                /* Name owner changed before we notified
+                 * the watcher of the initial name. We will notify
+                 * him now of the old name, then that this name
+                 * vanished.
+                 *
+                 * This is better than not sending calling any
+                 * callback, it might for instance trigger destroying
+                 * signal watchers on the unique name.
+                 */
+                g_source_remove(watcher->notify_idle);
+                notify_watcher_name_appeared(watcher);
+            }
+
+            if (!watcher->destroyed) {
+                (* watcher->funcs->vanished) (connection,
+                                              name,
+                                              old_owner,
+                                              watcher->data);
+            }
+        }
+    }
+
+    /* lookup for the watch again, since it might have vanished
+     * if all watchers were removed in the watcher->vanished
+     * callbacks */
+    watch = g_hash_table_lookup(info->name_watches, name);
+
+    if (watch) {
+        g_free(watch->current_owner);
+        watch->current_owner = g_strdup(new_owner);
+    }
+
+    /* appear the new owner */
+    if (new_owner != NULL) {
+        for (l = watchers;
+             l != NULL;
+             l = l->next) {
+            BigNameWatcher *watcher = l->data;
+
+            if (!watcher->destroyed) {
+                (* watcher->funcs->appeared) (connection,
+                                              name,
+                                              new_owner,
+                                              watcher->data);
+            }
+        }
+    }
+
+    /* now destroy our copy */
+    g_slist_foreach(watchers, (GFunc)name_watcher_unref, NULL);
+    g_slist_free(watchers);
+
+    g_free(old_owner);
+}
+
+static DBusHandlerResult
+name_watch_filter_message(DBusConnection *connection,
+                          DBusMessage    *message,
+                          void           *data)
+{
+    BigDBusInfo *info;
+
+    info = _big_dbus_ensure_info(connection);
+
+    if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged") &&
+        dbus_message_has_sender(message, DBUS_SERVICE_DBUS)) {
+        const char *name = NULL;
+        const char *old_owner = NULL;
+        const char *new_owner = NULL;
+        if (dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_STRING, &name,
+                                  DBUS_TYPE_STRING, &old_owner,
+                                  DBUS_TYPE_STRING, &new_owner,
+                                  DBUS_TYPE_INVALID)) {
+            big_debug(BIG_DEBUG_UTIL_DBUS, "NameOwnerChanged %s:   %s -> %s",
+                      name, old_owner, new_owner);
+
+            notify_name_owner_changed(connection, name, new_owner);
+        } else {
+            big_debug(BIG_DEBUG_UTIL_DBUS, "NameOwnerChanged has wrong arguments???");
+        }
+    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+
+        big_debug(BIG_DEBUG_UTIL_DBUS, "Disconnected in %s", G_STRFUNC);
+
+        /* FIXME set all current owners to NULL, and move watches back to the pending
+         * list so they are found on reconnect.
+         */
+    }
+
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+void
+_big_dbus_set_matching_name_owner_changed(DBusConnection *connection,
+                                          const char     *bus_name,
+                                          gboolean        matched)
+{
+    char *s;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "%s NameOwnerChanged on name '%s'",
+              matched ? "Matching" : "No longer matching",
+              bus_name);
+
+    s = g_strdup_printf("type='signal',sender='"
+                        DBUS_SERVICE_DBUS
+                        "',interface='"
+                        DBUS_INTERFACE_DBUS
+                        "',member='"
+                        "NameOwnerChanged"
+                        "',arg0='%s'",
+                        bus_name);
+
+    if (matched)
+        dbus_bus_add_match(connection,
+                           s, NULL); /* asking for error would make this block */
+    else
+        dbus_bus_remove_match(connection, s, NULL);
+
+    g_free(s);
+}
+
+static void
+on_start_service_reply(BigDBusProxy    *proxy,
+                       DBusMessage     *message,
+                       void            *data)
+{
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Got successful reply to service start");
+}
+
+static void
+on_start_service_error(BigDBusProxy    *proxy,
+                       const char      *error_name,
+                       const char      *error_message,
+                       void            *data)
+{
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Got error starting service: %s: %s",
+              error_name, error_message);
+}
+
+void
+big_dbus_start_service(DBusConnection *connection,
+                       const char     *name)
+{
+    DBusMessage *message;
+    dbus_uint32_t flags;
+    BigDBusInfo *info;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Starting service '%s'",
+              name);
+
+    info = _big_dbus_ensure_info(connection);
+
+    message = big_dbus_proxy_new_method_call(info->driver_proxy,
+                                             "StartServiceByName");
+
+    flags = 0;
+    if (dbus_message_append_args(message,
+                                 DBUS_TYPE_STRING, &name,
+                                 DBUS_TYPE_UINT32, &flags,
+                                 DBUS_TYPE_INVALID)) {
+        big_dbus_proxy_send(info->driver_proxy,
+                            message,
+                            on_start_service_reply,
+                            on_start_service_error,
+                            NULL);
+    } else {
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "No memory appending args to StartServiceByName");
+    }
+
+    dbus_message_unref(message);
+}
+
+typedef struct {
+    DBusConnection *connection;
+    char *name;
+    BigDBusWatchNameFlags flags;
+} GetOwnerRequest;
+
+static GetOwnerRequest*
+get_owner_request_new(DBusConnection       *connection,
+                      const char           *name,
+                      BigDBusWatchNameFlags flags)
+{
+    GetOwnerRequest *gor;
+
+    gor = g_slice_new0(GetOwnerRequest);
+    gor->connection = connection;
+    gor->name = g_strdup(name);
+    gor->flags = flags;
+    dbus_connection_ref(connection);
+
+    return gor;
+}
+
+static void
+get_owner_request_free(GetOwnerRequest *gor)
+{
+    dbus_connection_unref(gor->connection);
+    g_free(gor->name);
+    g_slice_free(GetOwnerRequest, gor);
+}
+
+static void
+on_get_owner_reply(DBusPendingCall *pending,
+                   void            *user_data)
+{
+    DBusMessage *reply;
+    GetOwnerRequest *gor;
+
+    gor = user_data;
+
+    reply = dbus_pending_call_steal_reply(pending);
+    if (reply == NULL) {
+        g_warning("NULL reply in on_get_owner_reply?");
+        return;
+    }
+
+    if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_METHOD_RETURN) {
+        const char *current_owner = NULL;
+
+        if (!dbus_message_get_args(reply, NULL,
+                                   DBUS_TYPE_STRING, &current_owner,
+                                   DBUS_TYPE_INVALID)) {
+            big_debug(BIG_DEBUG_UTIL_DBUS,
+                      "GetNameOwner has wrong args '%s'",
+                      dbus_message_get_signature(reply));
+        } else {
+            big_debug(BIG_DEBUG_UTIL_DBUS,
+                      "Got owner '%s' for name '%s'",
+                      current_owner, gor->name);
+            if (current_owner != NULL) {
+                notify_name_owner_changed(gor->connection,
+                                          gor->name,
+                                          current_owner);
+            }
+        }
+    } else if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
+        if (g_str_equal(dbus_message_get_error_name(reply),
+                        DBUS_ERROR_NAME_HAS_NO_OWNER)) {
+            big_debug(BIG_DEBUG_UTIL_DBUS,
+                      "'%s' was not running",
+                      gor->name);
+            if (gor->flags & BIG_DBUS_NAME_START_IF_NOT_FOUND) {
+                big_debug(BIG_DEBUG_UTIL_DBUS,
+                          "  (starting it up)");
+                big_dbus_start_service(gor->connection, gor->name);
+            } else {
+                /* no owner for now, notify app */
+                notify_name_owner_changed(gor->connection,
+                                          gor->name,
+                                          "");
+            }
+        } else {
+            big_debug(BIG_DEBUG_UTIL_DBUS,
+                      "Error getting owner of name '%s': %s",
+                      gor->name,
+                      dbus_message_get_error_name(reply));
+
+            /* Notify no owner for now, ensuring the app
+             * gets advised "appeared" or "vanished",
+             * one or the other.
+             */
+            notify_name_owner_changed(gor->connection,
+                                      gor->name,
+                                      "");
+        }
+    } else {
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "Nonsensical reply type to GetNameOwner");
+    }
+
+    dbus_message_unref(reply);
+}
+
+static void
+request_name_owner(DBusConnection *connection,
+                   BigDBusInfo    *info,
+                   BigNameWatch   *watch)
+{
+    DBusMessage *message;
+    DBusPendingCall *call;
+
+    message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
+                                           DBUS_PATH_DBUS,
+                                           DBUS_INTERFACE_DBUS,
+                                           "GetNameOwner");
+    if (message == NULL)
+        g_error("no memory");
+
+    if (!dbus_message_append_args(message,
+                                  DBUS_TYPE_STRING, &watch->name,
+                                  DBUS_TYPE_INVALID))
+        g_error("no memory");
+
+    call = NULL;
+    dbus_connection_send_with_reply(connection, message, &call, -1);
+    if (call != NULL) {
+        GetOwnerRequest *gor;
+        BigDBusWatchNameFlags flags;
+        GSList *l;
+
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "Sent GetNameOwner for '%s'",
+                  watch->name);
+
+        flags = 0;
+        for (l = watch->watchers;
+             l != NULL;
+             l = l->next) {
+            BigNameWatcher *watcher = l->data;
+
+            if (watcher->flags & BIG_DBUS_NAME_START_IF_NOT_FOUND)
+                flags |= BIG_DBUS_NAME_START_IF_NOT_FOUND;
+        }
+
+        gor = get_owner_request_new(connection, watch->name, flags);
+
+        if (!dbus_pending_call_set_notify(call, on_get_owner_reply,
+                                          gor,
+                                          (DBusFreeFunction) get_owner_request_free))
+            g_error("no memory");
+
+        /* the connection will hold a ref to the pending call */
+        dbus_pending_call_unref(call);
+    } else {
+        big_debug(BIG_DEBUG_UTIL_DBUS,
+                  "GetNameOwner for '%s' not sent, connection disconnected",
+                  watch->name);
+    }
+}
+
+static gboolean
+notify_watcher_name_appeared(gpointer data)
+{
+    BigNameWatcher *watcher;
+    DBusConnection *connection;
+
+    watcher = data;
+    watcher->notify_idle = 0;
+
+    connection = _big_dbus_get_weak_ref(watcher->bus_type);
+
+    if (!connection)
+        return FALSE;
+
+    (* watcher->funcs->appeared) (connection,
+                                  watcher->watch->name,
+                                  watcher->watch->current_owner,
+                                  watcher->data);
+    return FALSE;
+}
+
+static void
+create_watch_for_watcher(DBusConnection *connection,
+                         BigDBusInfo    *info,
+                         const char     *name,
+                         BigNameWatcher *watcher)
+{
+    BigNameWatch *watch;
+
+    watch = g_hash_table_lookup(info->name_watches, name);
+    if (watch == NULL) {
+        watch = name_watch_new(name);
+
+        g_hash_table_replace(info->name_watches, watch->name, watch);
+
+        watch->watchers = g_slist_prepend(watch->watchers, watcher);
+
+        _big_dbus_set_matching_name_owner_changed(connection, watch->name, TRUE);
+
+        request_name_owner(connection, info, watch);
+    } else {
+        watch->watchers = g_slist_prepend(watch->watchers, watcher);
+    }
+    name_watcher_ref(watcher);
+
+    watcher->watch = watch;
+
+}
+
+static void
+process_pending_name_watchers(DBusConnection *connection,
+                              BigDBusInfo    *info)
+{
+    GSList *still_pending;
+
+    still_pending = NULL;
+    while (pending_name_watchers != NULL) {
+        BigPendingNameWatcher *pending;
+        BigNameWatch *watch;
+
+        pending = pending_name_watchers->data;
+        pending_name_watchers = g_slist_remove(pending_name_watchers,
+                                               pending_name_watchers->data);
+
+        if (pending->bus_type != info->bus_type) {
+            still_pending = g_slist_prepend(still_pending, pending);
+            continue;
+        }
+
+        create_watch_for_watcher(connection,
+                                 info,
+                                 pending->name,
+                                 pending->watcher);
+
+        watch = pending->watcher->watch;
+
+        /* If we already know the owner, let the new watcher know */
+        if (watch->current_owner != NULL) {
+            (* pending->watcher->funcs->appeared) (connection,
+                                                   watch->name,
+                                                   watch->current_owner,
+                                                   pending->watcher->data);
+        }
+
+        g_free(pending->name);
+        name_watcher_unref(pending->watcher);
+        g_slice_free(BigPendingNameWatcher, pending);
+    }
+
+    g_assert(pending_name_watchers == NULL);
+    pending_name_watchers = still_pending;
+}
+
+static void
+name_watch_remove_watcher(BigNameWatch     *watch,
+                          BigNameWatcher   *watcher)
+{
+    watch->watchers = g_slist_remove(watch->watchers,
+                                     watcher);
+
+    if (watcher->notify_idle) {
+        g_source_remove(watcher->notify_idle);
+        watcher->notify_idle = 0;
+    }
+
+    watcher->destroyed = TRUE;
+    name_watcher_unref(watcher);
+}
+
+void
+big_dbus_watch_name(DBusBusType                  bus_type,
+                    const char                  *name,
+                    BigDBusWatchNameFlags        flags,
+                    const BigDBusWatchNameFuncs *funcs,
+                    void                        *data)
+{
+    BigNameWatcher *watcher;
+    DBusConnection *connection;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Adding watch on name '%s'",
+              name);
+
+    watcher = name_watcher_new(flags, funcs, data, bus_type);
+
+    connection = _big_dbus_get_weak_ref(bus_type);
+
+    if (connection) {
+        BigDBusInfo *info;
+
+        info = _big_dbus_ensure_info(connection);
+
+        create_watch_for_watcher(connection,
+                                 info,
+                                 name,
+                                 watcher);
+        /* The initial reference is now transferred to the watch */
+        name_watcher_unref(watcher);
+
+        /* If we already know the owner, notify the user in an idle */
+        if (watcher->watch->current_owner) {
+            watcher->notify_idle =
+                g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
+                                notify_watcher_name_appeared,
+                                watcher,
+                                (GDestroyNotify)name_watcher_unref);
+            name_watcher_ref(watcher);
+        }
+
+    } else {
+        BigPendingNameWatcher *pending;
+
+        pending = g_slice_new0(BigPendingNameWatcher);
+
+        pending->bus_type = bus_type;
+        pending->name = g_strdup(name);
+        pending->watcher = watcher;
+
+        pending_name_watchers = g_slist_prepend(pending_name_watchers, pending);
+
+        _big_dbus_ensure_connect_idle(pending->bus_type);
+    }
+}
+
+void
+big_dbus_unwatch_name(DBusBusType                  bus_type,
+                      const char                  *name,
+                      const BigDBusWatchNameFuncs *funcs,
+                      void                        *data)
+{
+    DBusConnection *connection;
+    BigDBusInfo *info;
+    BigNameWatch *watch;
+    GSList *l;
+    BigNameWatcher *watcher;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Removing watch on name '%s'",
+              name);
+
+    connection = _big_dbus_get_weak_ref(bus_type);
+    if (connection == NULL) {
+        /* right now our state is entirely hosed if we disconnect
+         * (we don't move the watchers out of the connection data),
+         * so can't do much here without larger changes to the file
+         */
+        g_warning("Have not implemented disconnect handling");
+        return;
+    }
+
+    info = _big_dbus_ensure_info(connection);
+
+    /* could still be pending */
+    process_pending_name_watchers(connection, info);
+
+    watch = g_hash_table_lookup(info->name_watches, name);
+
+    if (watch == NULL) {
+        g_warning("attempt to unwatch name %s but nobody is watching that",
+                  name);
+        return;
+    }
+
+    watcher = NULL;
+    for (l = watch->watchers; l != NULL; l = l->next) {
+        watcher = l->data;
+
+        if (watcher->funcs == funcs &&
+            watcher->data == data)
+            break;
+    }
+
+    if (l == NULL) {
+        g_warning("Could not find a watch on %s matching %p %p",
+                  name, funcs, data);
+        return;
+    }
+    g_assert(l->data == watcher);
+
+    name_watch_remove_watcher(watch, watcher);
+
+    /* Clear out the watch if it's gone */
+    if (watch->watchers == NULL) {
+        g_hash_table_remove(info->name_watches, watch->name);
+
+        _big_dbus_set_matching_name_owner_changed(connection, watch->name, FALSE);
+
+        name_watch_free(watch);
+    }
+}
+
+const char*
+big_dbus_get_watched_name_owner(DBusBusType   bus_type,
+                                const char   *name)
+{
+    DBusConnection *connection;
+    BigNameWatch *watch;
+    BigDBusInfo *info;
+
+    connection = _big_dbus_get_weak_ref(bus_type);
+    if (connection == NULL) {
+        return NULL;
+    }
+
+    info = _big_dbus_ensure_info(connection);
+
+    /* could still be pending */
+    process_pending_name_watchers(connection, info);
+
+    watch = g_hash_table_lookup(info->name_watches, name);
+    if (watch == NULL) {
+        g_warning("Tried to get owner of '%s' but there is no watch on it",
+                  name);
+        return NULL;
+    }
+
+    return watch->current_owner;
+}
+
+void
+big_dbus_register_json(DBusConnection          *connection,
+                       const char              *iface_name,
+                       const BigDBusJsonMethod *methods,
+                       int                      n_methods)
+{
+    BigDBusInfo *info;
+    BigJsonIface *iface;
+
+    info = _big_dbus_ensure_info(connection);
+
+    iface = json_iface_new(iface_name, methods, n_methods);
+
+    g_hash_table_replace(info->json_ifaces, iface->name, iface);
+}
+
+void
+big_dbus_unregister_json(DBusConnection *connection,
+                         const char     *iface_name)
+{
+    BigDBusInfo *info;
+
+    info = _big_dbus_ensure_info(connection);
+
+    g_hash_table_remove(info->json_ifaces, iface_name);
+}
+
+typedef struct {
+    DBusConnection *connection;
+    GObject *gobj;
+    char *iface_name;
+} BigDBusGObject;
+
+static void
+gobj_path_unregistered(DBusConnection  *connection,
+                       void            *user_data)
+{
+    BigDBusGObject *g;
+
+    g = user_data;
+
+    if (g->gobj) {
+        g_object_remove_weak_pointer(g->gobj, (void**) &g->gobj);
+        g->gobj = NULL;
+    }
+
+    g_free(g->iface_name);
+    g_slice_free(BigDBusGObject, g);
+}
+
+static DBusHandlerResult
+gobj_path_message(DBusConnection  *connection,
+                  DBusMessage     *message,
+                  void            *user_data)
+{
+    BigDBusGObject *g;
+    BigDBusInfo *info;
+    BigJsonIface *iface;
+    const char *message_iface;
+    const char *message_method;
+    DBusError derror;
+    int i;
+    const BigDBusJsonMethod *method;
+    DBusMessageIter arg_iter, dict_iter;
+
+    info = _big_dbus_ensure_info(connection);
+    g = user_data;
+
+    big_debug(BIG_DEBUG_UTIL_DBUS,
+              "Received message to iface %s gobj %p",
+              g->iface_name, g->gobj);
+
+    if (g->gobj == NULL) {
+        /* GObject was destroyed */
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    dbus_error_init(&derror);
+
+    message_iface = dbus_message_get_interface(message);
+
+    /* FIXME implement Introspectable() just to enable dbus debugger */
+
+    if (message_iface != NULL &&
+        strcmp(message_iface, g->iface_name) != 0) {
+
+        dbus_set_error(&derror, DBUS_ERROR_UNKNOWN_METHOD,
+                       "Interface '%s' not implemented by this object, did you mean '%s'?",
+                       message_iface, g->iface_name);
+
+        goto out;
+    }
+
+    iface = g_hash_table_lookup(info->json_ifaces,
+                                g->iface_name);
+    if (iface == NULL) {
+        g_warning("Object registered with iface %s but that iface is not registered",
+                  g->iface_name);
+        dbus_set_error(&derror, DBUS_ERROR_UNKNOWN_METHOD,
+                       "Bug - '%s' is not registered",
+                       g->iface_name);
+        goto out;
+    }
+
+    method = NULL;
+    message_method = dbus_message_get_member(message);
+    for (i = 0; i < iface->n_methods; ++i) {
+        if (strcmp(message_method, iface->methods[i].name) == 0) {
+            method = &iface->methods[i];
+            break;
+        }
+    }
+
+    if (method == NULL) {
+        dbus_set_error(&derror, DBUS_ERROR_UNKNOWN_METHOD,
+                       "Interface '%s' has no method '%s'",
+                       g->iface_name, message_method);
+        goto out;
+    }
+
+    if (!dbus_message_has_signature(message, "a{sv}")) {
+        dbus_set_error(&derror, DBUS_ERROR_INVALID_ARGS,
+                       "Method %s.%s should have 1 argument which is a dictionary",
+                       g->iface_name, message_method);
+        goto out;
+    }
+
+    dbus_message_iter_init(message, &arg_iter);
+    dbus_message_iter_recurse(&arg_iter, &dict_iter);
+
+    if (method->sync_func != NULL) {
+        DBusMessage *reply;
+        DBusMessageIter out_arg_iter, out_dict_iter;
+
+        reply = dbus_message_new_method_return(message);
+        if (reply == NULL) {
+            dbus_set_error(&derror, DBUS_ERROR_NO_MEMORY,
+                           "No memory");
+            goto out;
+        }
+
+        dbus_message_iter_init_append(reply, &out_arg_iter);
+        dbus_message_iter_open_container(&out_arg_iter,
+                                         DBUS_TYPE_ARRAY, "{sv}",
+                                         &out_dict_iter);
+
+        g_object_ref(g->gobj);
+        (* method->sync_func) (connection, message,
+                               &dict_iter, &out_dict_iter,
+                               g->gobj,
+                               &derror);
+        g_object_unref(g->gobj);
+
+        dbus_message_iter_close_container(&out_arg_iter, &out_dict_iter);
+
+        if (!dbus_error_is_set(&derror)) {
+            dbus_connection_send(connection, reply, NULL);
+        }
+        dbus_message_unref(reply);
+
+    } else if (method->async_func != NULL) {
+        g_object_ref(g->gobj);
+        (* method->async_func) (connection, message,
+                                &dict_iter,
+                                g->gobj);
+        g_object_unref(g->gobj);
+    } else {
+        g_warning("Method %s does not have any implementation", method->name);
+    }
+
+ out:
+    if (dbus_error_is_set(&derror)) {
+        DBusMessage *reply;
+
+        reply = dbus_message_new_error(message,
+                                       derror.name,
+                                       derror.message);
+        dbus_error_free(&derror);
+
+        if (reply != NULL) {
+            dbus_connection_send(connection, reply, NULL);
+
+            dbus_message_unref(reply);
+        } else {
+            /* use g_printerr not g_warning since this is NOT a "can
+             * never happen" just a "probably will never happen"
+             */
+            g_printerr("Could not send OOM error\n");
+        }
+    }
+
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusObjectPathVTable gobj_vtable = {
+    gobj_path_unregistered,
+    gobj_path_message,
+    NULL,
+};
+
+/* Note that because of how this works, each object can be registered
+ * at multiple paths but only once per path. Which is sort of bizarre,
+ * but we'll fix it when we need it.
+ */
+void
+big_dbus_register_g_object(DBusConnection *connection,
+                           const char     *path,
+                           GObject        *gobj,
+                           const char     *iface_name)
+{
+    BigDBusGObject *g;
+
+    g = g_slice_new0(BigDBusGObject);
+    g->iface_name = g_strdup(iface_name);
+    g->gobj = gobj;
+
+    if (!dbus_connection_register_object_path(connection, path,
+                                              &gobj_vtable, g)) {
+        g_warning("Failed to register object path %s", path);
+    }
+
+    g_object_add_weak_pointer(g->gobj, (void**) &g->gobj);
+}
+
+void
+big_dbus_unregister_g_object (DBusConnection *connection,
+                              const char     *path)
+{
+    dbus_connection_unregister_object_path(connection, path);
+}
+
+static void
+open_json_entry(DBusMessageIter *dict_iter,
+                const char      *key,
+                const char      *signature,
+                DBusMessageIter *entry_iter,
+                DBusMessageIter *variant_iter)
+{
+    dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, entry_iter);
+
+    dbus_message_iter_append_basic(entry_iter, DBUS_TYPE_STRING, &key);
+
+    dbus_message_iter_open_container(entry_iter, DBUS_TYPE_VARIANT, signature, variant_iter);
+}
+
+static void
+close_json_entry(DBusMessageIter *dict_iter,
+                 DBusMessageIter *entry_iter,
+                 DBusMessageIter *variant_iter)
+{
+    dbus_message_iter_close_container(entry_iter, variant_iter);
+
+    dbus_message_iter_close_container(dict_iter, entry_iter);
+}
+
+static void
+open_json_entry_array(DBusMessageIter *dict_iter,
+                      const char      *key,
+                      int              array_element_type,
+                      DBusMessageIter *entry_iter,
+                      DBusMessageIter *variant_iter,
+                      DBusMessageIter *array_iter)
+{
+    char buf[3];
+    buf[0] = 'a';
+    buf[1] = array_element_type;
+    buf[2] = '\0';
+
+    open_json_entry(dict_iter, key, buf, entry_iter, variant_iter);
+
+    dbus_message_iter_open_container(variant_iter, DBUS_TYPE_ARRAY, &buf[1], array_iter);
+}
+
+static void
+close_json_entry_array(DBusMessageIter *dict_iter,
+                       DBusMessageIter *entry_iter,
+                       DBusMessageIter *variant_iter,
+                       DBusMessageIter *array_iter)
+{
+    dbus_message_iter_close_container(variant_iter, array_iter);
+
+    close_json_entry(dict_iter, entry_iter, variant_iter);
+}
+
+void
+big_dbus_append_json_entry (DBusMessageIter *dict_iter,
+                            const char      *key,
+                            int              dbus_type,
+                            void            *basic_value_p)
+{
+    DBusMessageIter entry_iter, variant_iter;
+    char buf[2];
+
+    buf[0] = dbus_type;
+    buf[1] = '\0';
+
+    open_json_entry(dict_iter, key, buf, &entry_iter, &variant_iter);
+
+    dbus_message_iter_append_basic(&variant_iter, dbus_type, basic_value_p);
+
+    close_json_entry(dict_iter, &entry_iter, &variant_iter);
+}
+
+void
+big_dbus_append_json_entry_STRING (DBusMessageIter *dict_iter,
+                                   const char      *key,
+                                   const char      *value)
+{
+    big_dbus_append_json_entry(dict_iter, key, DBUS_TYPE_STRING, &value);
+}
+
+void
+big_dbus_append_json_entry_INT32 (DBusMessageIter *dict_iter,
+                                  const char      *key,
+                                  dbus_int32_t     value)
+{
+    big_dbus_append_json_entry(dict_iter, key, DBUS_TYPE_INT32, &value);
+}
+
+void
+big_dbus_append_json_entry_DOUBLE (DBusMessageIter *dict_iter,
+                                   const char      *key,
+                                   double           value)
+{
+    big_dbus_append_json_entry(dict_iter, key, DBUS_TYPE_DOUBLE, &value);
+}
+
+void
+big_dbus_append_json_entry_BOOLEAN (DBusMessageIter *dict_iter,
+                                  const char      *key,
+                                  dbus_bool_t      value)
+{
+    big_dbus_append_json_entry(dict_iter, key, DBUS_TYPE_BOOLEAN, &value);
+}
+
+/* when coming from a dynamic language, we don't know what type of array '[]' is supposed to be */
+void
+big_dbus_append_json_entry_EMPTY_ARRAY (DBusMessageIter  *dict_iter,
+                                        const char       *key)
+{
+    DBusMessageIter entry_iter, variant_iter, array_iter;
+
+    /* so just say VARIANT even though there won't be any elements in the array */
+    open_json_entry_array(dict_iter, key, DBUS_TYPE_VARIANT, &entry_iter, &variant_iter, &array_iter);
+
+    close_json_entry_array(dict_iter, &entry_iter, &variant_iter, &array_iter);
+}
+
+void
+big_dbus_append_json_entry_STRING_ARRAY (DBusMessageIter  *dict_iter,
+                                         const char       *key,
+                                         const char      **value)
+{
+    DBusMessageIter entry_iter, variant_iter, array_iter;
+    int i;
+
+    open_json_entry_array(dict_iter, key, DBUS_TYPE_STRING, &entry_iter, &variant_iter, &array_iter);
+
+    for (i = 0; value[i] != NULL; ++i) {
+        dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &value[i]);
+    }
+
+    close_json_entry_array(dict_iter, &entry_iter, &variant_iter, &array_iter);
+}
+
+gboolean
+big_dbus_message_iter_get_gsize(DBusMessageIter  *iter,
+                                gsize            *value_p)
+{
+    switch (dbus_message_iter_get_arg_type(iter)) {
+    case DBUS_TYPE_INT32:
+        {
+            dbus_int32_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v < 0)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_UINT32:
+        {
+            dbus_uint32_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_INT64:
+        {
+            dbus_int64_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v < 0)
+                return FALSE;
+            if (((guint64)v) > G_MAXSIZE)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_UINT64:
+        {
+            dbus_uint64_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v > G_MAXSIZE)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+gboolean
+big_dbus_message_iter_get_gssize(DBusMessageIter  *iter,
+                                 gssize           *value_p)
+{
+    switch (dbus_message_iter_get_arg_type(iter)) {
+    case DBUS_TYPE_INT32:
+        {
+            dbus_int32_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_UINT32:
+        {
+            dbus_uint32_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v > (guint32) G_MAXSSIZE)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_INT64:
+        {
+            dbus_int64_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v > (gint64) G_MAXSSIZE)
+                return FALSE;
+            if (v < (gint64) G_MINSSIZE)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    case DBUS_TYPE_UINT64:
+        {
+            dbus_uint64_t v;
+            dbus_message_iter_get_basic(iter, &v);
+            if (v > (guint64) G_MAXSSIZE)
+                return FALSE;
+            *value_p = v;
+        }
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+#if BIG_BUILD_TESTS
+
+#include "dbus-proxy.h"
+#include "dbus-input-stream.h"
+#include "dbus-output-stream.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+
+static pid_t test_service_pid = 0;
+static BigDBusProxy *test_service_proxy = NULL;
+
+static pid_t test_io_pid = 0;
+static BigDBusProxy *test_io_proxy = NULL;
+
+static GMainLoop *client_loop = NULL;
+
+static int n_running_children = 0;
+
+static BigDBusInputStream  *input_from_io_service;
+static BigDBusOutputStream *output_to_io_service;
+
+static const char stream_data_to_io_service[] = "This is sent from the main test process to the IO service.";
+static const char stream_data_from_io_service[] = "This is sent from the IO service to the main test process. The quick brown fox, etc.";
+
+static void do_test_service_child (void);
+static void do_test_io_child      (void);
+
+/* quit when all children are gone */
+static void
+another_child_down(void)
+{
+    g_assert(n_running_children > 0);
+    n_running_children -= 1;
+
+    if (n_running_children == 0) {
+        g_main_loop_quit(client_loop);
+    }
+}
+
+static const char*
+extract_string_arg(DBusMessageIter *in_iter,
+                   const char      *prop_name,
+                   DBusError       *error)
+{
+    const char *s;
+
+    s = NULL;
+    while (dbus_message_iter_get_arg_type(in_iter) == DBUS_TYPE_DICT_ENTRY) {
+        DBusMessageIter entry_iter, variant_iter;
+        const char *key;
+
+        dbus_message_iter_recurse(in_iter, &entry_iter);
+
+        dbus_message_iter_get_basic(&entry_iter, &key);
+
+        if (strcmp(key, prop_name) == 0) {
+            dbus_message_iter_next(&entry_iter);
+
+            dbus_message_iter_recurse(&entry_iter, &variant_iter);
+            if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+                dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+                               "Value of '%s' prop should be a string",
+                               prop_name);
+                return NULL;
+            }
+
+            dbus_message_iter_get_basic(&variant_iter, &s);
+
+            return s;
+        }
+    }
+
+    dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+                   "No '%s' prop provided", prop_name);
+    return NULL;
+}
+
+static void
+fork_child_test_service(void)
+{
+    pid_t child_pid;
+
+    /* it would break to fork after we already connected */
+    g_assert(session_bus_weak_ref == NULL);
+    g_assert(system_bus_weak_ref == NULL);
+    g_assert(test_service_pid == 0);
+
+    child_pid = fork();
+
+    if (child_pid == -1) {
+        g_error("Failed to fork dbus service");
+    } else if (child_pid > 0) {
+        /* We are the parent */
+        test_service_pid = child_pid;
+        n_running_children += 1;
+
+        return;
+    }
+
+    /* we are the child, set up a service for main test process to talk to */
+
+    do_test_service_child();
+}
+
+/* This test function doesn't really test anything, just sets up
+ * for the following one
+ */
+static void
+fork_child_test_io(void)
+{
+    pid_t child_pid;
+
+    /* it would break to fork after we already connected */
+    g_assert(session_bus_weak_ref == NULL);
+    g_assert(system_bus_weak_ref == NULL);
+    g_assert(test_io_pid == 0);
+
+    child_pid = fork();
+
+    if (child_pid == -1) {
+        g_error("Failed to fork dbus service");
+    } else if (child_pid > 0) {
+        /* We are the parent */
+        test_io_pid = child_pid;
+        n_running_children += 1;
+
+        return;
+    }
+
+    /* we are the child, set up a service for main test process to talk to */
+
+    do_test_io_child();
+}
+
+static void
+on_expected_fnf_error_reply_kill_child(BigDBusProxy    *proxy,
+                                       const char      *error_name,
+                                       const char      *error_message,
+                                       void            *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "got expected error reply to alwaysErrorSync, killing child");
+
+    /* We were expecting an error, good. */
+    if (strcmp(error_name, DBUS_ERROR_FILE_NOT_FOUND) != 0) {
+        g_error("Got error we did not expect %s: %s",
+                error_name, error_message);
+    }
+
+    if (kill(test_service_pid, SIGTERM) < 0) {
+        g_error("Test service was no longer around... it must have failed somehow (%s)",
+                strerror(errno));
+    }
+
+    /* We will quit main loop when we see the child go away */
+}
+
+static void
+on_unexpected_error_reply(BigDBusProxy    *proxy,
+                          const char      *error_name,
+                          const char      *error_message,
+                          void            *data)
+{
+    const char *context_text = data;
+
+    g_error("Got error %s: '%s' context was: %s",
+            error_name, error_message, context_text);
+}
+
+static void
+on_get_always_error_reply(BigDBusProxy    *proxy,
+                          DBusMessage     *message,
+                          DBusMessageIter *return_value_iter,
+                          void            *data)
+{
+    g_error("alwaysError json method supposed to return an error always, not a valid reply");
+}
+
+static void
+on_get_some_stuff_reply(BigDBusProxy    *proxy,
+                        DBusMessage     *message,
+                        DBusMessageIter *return_value_iter,
+                        void            *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "reply received to getSomeStuffSync");
+
+    /* FIXME look at the return value to see if it's what
+     * the test service sends
+     */
+
+    big_dbus_proxy_call_json_async(test_service_proxy,
+                                   "alwaysErrorSync",
+                                   on_get_always_error_reply,
+                                   on_expected_fnf_error_reply_kill_child,
+                                   NULL,
+                                   NULL);
+}
+
+static void
+on_test_service_appeared(DBusConnection *connection,
+                         const char     *name,
+                         const char     *new_owner_unique_name,
+                         void           *data)
+{
+    dbus_int32_t v_INT32;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s appeared",
+              name);
+
+    test_service_proxy =
+        big_dbus_proxy_new(connection, new_owner_unique_name,
+                           "/com/litl/test/object42",
+                           "com.litl.TestIface");
+    v_INT32 = 42;
+    big_dbus_proxy_call_json_async(test_service_proxy,
+                                   "getSomeStuffSync",
+                                   on_get_some_stuff_reply,
+                                   on_unexpected_error_reply,
+                                   "getSomeStuffSync call from on_test_service_appeared",
+                                   "yourNameIs", DBUS_TYPE_STRING, &name,
+                                   "yourUniqueNameIs", DBUS_TYPE_STRING, &new_owner_unique_name,
+                                   "anIntegerIs", DBUS_TYPE_INT32, &v_INT32,
+                                   NULL);
+}
+
+static void
+on_test_service_vanished(DBusConnection *connection,
+                         const char     *name,
+                         const char     *old_owner_unique_name,
+                         void           *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s vanished", name);
+
+    another_child_down();
+}
+
+static BigDBusWatchNameFuncs watch_test_service_funcs = {
+    on_test_service_appeared,
+    on_test_service_vanished
+};
+
+static void
+on_confirm_streams_reply(BigDBusProxy    *proxy,
+                         DBusMessage     *message,
+                         DBusMessageIter *return_value_iter,
+                         void            *data)
+{
+    const char *received;
+
+    received = extract_string_arg(return_value_iter,
+                                  "received",
+                                  NULL);
+    g_assert(received != NULL);
+
+    if (strcmp(received, stream_data_to_io_service) != 0) {
+        g_error("We sent the child process '%s' but it says it got '%s'",
+                stream_data_to_io_service,
+                received);
+    }
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestIO says it got: '%s'", received);
+
+    /* We've exchanged all our streams - time to kill the TestIO
+     * child process
+     */
+    big_debug(BIG_DEBUG_IN_TESTS, "Sending TERM to TestIO child");
+    if (kill(test_io_pid, SIGTERM) < 0) {
+        g_error("Test IO service was no longer around... it must have failed somehow (%s)",
+                strerror(errno));
+    }
+}
+
+static void
+on_setup_streams_reply(BigDBusProxy    *proxy,
+                       DBusMessage     *message,
+                       DBusMessageIter *return_value_iter,
+                       void            *data)
+{
+    const char *stream_path;
+    gsize total;
+    gssize result;
+    gsize read_size;
+    GError *error;
+    GString *str;
+    char buf[10];
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "Got reply to setupStreams");
+
+    stream_path = extract_string_arg(return_value_iter,
+                                     "stream",
+                                     NULL);
+    g_assert(stream_path != NULL);
+
+    output_to_io_service =
+        big_dbus_output_stream_new(big_dbus_proxy_get_connection(proxy),
+                                   dbus_message_get_sender(message),
+                                   stream_path);
+
+    g_assert(input_from_io_service && output_to_io_service);
+
+    /* Write to the output stream */
+
+    total = strlen(stream_data_to_io_service);
+
+    error = NULL;
+    result = g_output_stream_write(G_OUTPUT_STREAM(output_to_io_service),
+                                   stream_data_to_io_service,
+                                   10,
+                                   NULL,
+                                   &error);
+    if (result < 0) {
+        g_error("Error writing to output stream: %s", error->message);
+        g_error_free(error);
+    }
+
+    if (result != 10) {
+        g_error("Wrote %d instead of 10 bytes", (int) result);
+    }
+
+    if (!g_output_stream_write_all(G_OUTPUT_STREAM(output_to_io_service),
+                                   stream_data_to_io_service + 10,
+                                   total - 10,
+                                   NULL, NULL, &error)) {
+        g_error("Error writing all to output stream: %s", error->message);
+        g_error_free(error);
+    }
+
+    /* flush should do nothing here, and is not needed, but
+     * just calling it to test it
+     */
+    if (!g_output_stream_flush(G_OUTPUT_STREAM(output_to_io_service), NULL, &error)) {
+        g_error("Error flushing output stream: %s", error->message);
+        g_error_free(error);
+    }
+
+    if (!g_output_stream_close(G_OUTPUT_STREAM(output_to_io_service), NULL, &error)) {
+        g_error("Error closing output stream: %s", error->message);
+        g_error_free(error);
+    }
+    g_object_unref(output_to_io_service);
+    output_to_io_service = NULL;
+
+    /* Now read from the input stream - in an inefficient way to be sure
+     * we test multiple, partial reads
+     */
+
+    read_size = 1;
+    str = g_string_new(NULL);
+
+    while (TRUE) {
+        /* test get_received() */
+        g_assert(big_dbus_input_stream_get_received(input_from_io_service) <= strlen(stream_data_from_io_service));
+
+        /* This is a blocking read... in production code, you would
+         * want to use the ready-to-read signal instead to avoid
+         * blocking when there is nothing to read.
+         */
+        result = g_input_stream_read(G_INPUT_STREAM(input_from_io_service),
+                                     buf,
+                                     read_size,
+                                     NULL, &error);
+        if (result < 0) {
+            g_error("Error reading %d bytes from input stream: %s",
+                    (int) read_size, error->message);
+            g_error_free(error);
+        }
+
+        if (result == 0) {
+            /* EOF */
+            break;
+        }
+
+        g_string_append_len(str, buf, result);
+
+        if (read_size < sizeof(buf))
+            read_size += 1;
+    }
+
+    if (!g_input_stream_close(G_INPUT_STREAM(input_from_io_service), NULL, &error)) {
+        g_error("Error closing input stream: %s", error->message);
+        g_error_free(error);
+    }
+    g_object_unref(input_from_io_service);
+    input_from_io_service = NULL;
+
+    /* Now make the confirmStreams call
+     */
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "Confirming to com.litl.TestIO we got: '%s'", str->str);
+
+    big_dbus_proxy_call_json_async(test_io_proxy,
+                                   "confirmStreamsData",
+                                   on_confirm_streams_reply,
+                                   on_unexpected_error_reply,
+                                   "confirmStreamsData call from on_setup_streams_reply",
+                                   "received", DBUS_TYPE_STRING, &str->str,
+                                   NULL);
+
+    g_string_free(str, TRUE);
+}
+
+static void
+on_test_io_appeared(DBusConnection *connection,
+                    const char     *name,
+                    const char     *new_owner_unique_name,
+                    void           *data)
+{
+    const char *stream_path;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s appeared",
+              name);
+
+    test_io_proxy =
+        big_dbus_proxy_new(connection, new_owner_unique_name,
+                           "/com/litl/test/object47",
+                           "com.litl.TestIO");
+
+    input_from_io_service =
+        g_object_new(BIG_TYPE_DBUS_INPUT_STREAM, NULL);
+    big_dbus_input_stream_attach(input_from_io_service, connection);
+
+    stream_path = big_dbus_input_stream_get_path(input_from_io_service);
+
+    big_dbus_proxy_call_json_async(test_io_proxy,
+                                   "setupStreams",
+                                   on_setup_streams_reply,
+                                   on_unexpected_error_reply,
+                                   "setupStreams call from on_test_io_appeared",
+                                   "stream", DBUS_TYPE_STRING, &stream_path,
+                                   NULL);
+}
+
+static void
+on_test_io_vanished(DBusConnection *connection,
+                    const char     *name,
+                    const char     *old_owner_unique_name,
+                    void           *data)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "%s vanished", name);
+
+    another_child_down();
+}
+
+static BigDBusWatchNameFuncs watch_test_io_funcs = {
+    on_test_io_appeared,
+    on_test_io_vanished
+};
+
+void
+bigtest_test_func_util_dbus_client(void)
+{
+    pid_t result;
+    int status;
+
+    /* We have to fork() to avoid creating the DBusConnection*
+     * and thus preventing other dbus-using tests from forking
+     * children.  This dbus bug, when the fix makes it into Ubuntu,
+     * should solve the problem:
+     * https://bugs.freedesktop.org/show_bug.cgi?id=15570
+     *
+     * The symptom of that bug is failure to connect to the bus in
+     * dbus-signals.c tests. The symptom of opening a connection
+     * before forking children is the connection FD shared among
+     * multiple processes, i.e. huge badness.
+     */
+    if (!g_test_trap_fork(0, 0)) {
+        /* We are the parent */
+        g_test_trap_assert_passed();
+        return;
+    }
+
+    /* All this stuff runs in the forked child only */
+
+    fork_child_test_service();
+    fork_child_test_io();
+
+    g_type_init();
+
+    g_assert(test_service_pid != 0);
+    g_assert(test_io_pid != 0);
+
+    big_dbus_watch_name(DBUS_BUS_SESSION,
+                        "com.litl.TestService",
+                        0,
+                        &watch_test_service_funcs,
+                        NULL);
+
+    big_dbus_watch_name(DBUS_BUS_SESSION,
+                        "com.litl.TestIO",
+                        0,
+                        &watch_test_io_funcs,
+                        NULL);
+
+    client_loop = g_main_loop_new(NULL, FALSE);
+
+    g_main_loop_run(client_loop);
+
+    if (test_service_proxy != NULL)
+        g_object_unref(test_service_proxy);
+
+    if (test_io_proxy != NULL)
+        g_object_unref(test_io_proxy);
+
+    /* child was killed already, or should have been */
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "waitpid() for first child");
+
+    result = waitpid(test_service_pid, &status, 0);
+    if (result < 0) {
+        g_error("Failed to waitpid() for forked child: %s", strerror(errno));
+    }
+
+    if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+        g_error("Forked dbus service child exited with error code %d", WEXITSTATUS(status));
+    }
+
+    if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
+        g_error("Forked dbus service child exited on wrong signal number %d", WTERMSIG(status));
+    }
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "waitpid() for second child");
+
+    result = waitpid(test_io_pid, &status, 0);
+    if (result < 0) {
+        g_error("Failed to waitpid() for forked child: %s", strerror(errno));
+    }
+
+    if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
+        g_error("Forked dbus service child exited with error code %d", WEXITSTATUS(status));
+    }
+
+    if (WIFSIGNALED(status) && WTERMSIG(status) != SIGTERM) {
+        g_error("Forked dbus service child exited on wrong signal number %d", WTERMSIG(status));
+    }
+
+    big_debug(BIG_DEBUG_IN_TESTS, "dbus client test completed");
+
+    /* We want to kill dbus so the weak refs are NULL to start the
+     * next dbus-related test, which allows those tests
+     * to fork new child processes.
+     */
+    _big_dbus_dispose_info(_big_dbus_get_weak_ref(DBUS_BUS_SESSION));
+    dbus_shutdown();
+
+    big_debug(BIG_DEBUG_IN_TESTS, "dbus shut down");
+
+    /* FIXME this is here only while we need g_test_trap_fork(),
+     * see comment above.
+     */
+    exit(0);
+}
+
+/*
+ * First child service we forked, tests general dbus API
+ */
+
+static gboolean currently_have_test_service = FALSE;
+static GObject *test_service_object = NULL;
+
+static void
+test_service_get_some_stuff_sync(DBusConnection  *connection,
+                                 DBusMessage     *message,
+                                 DBusMessageIter *in_iter,
+                                 DBusMessageIter *out_iter,
+                                 void            *data,
+                                 DBusError       *error)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService got getSomeStuffSync");
+
+    g_assert(G_IS_OBJECT(data));
+
+    big_dbus_append_json_entry_BOOLEAN(out_iter,
+                                       "haveTestService",
+                                       currently_have_test_service);
+}
+
+static void
+test_service_always_error_sync(DBusConnection  *connection,
+                               DBusMessage     *message,
+                               DBusMessageIter *in_iter,
+                               DBusMessageIter *out_iter,
+                               void            *data,
+                               DBusError       *error)
+{
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService got alwaysErrorSync");
+
+    g_assert(G_IS_OBJECT(data));
+
+    dbus_set_error(error, DBUS_ERROR_FILE_NOT_FOUND,
+                   "Did not find some kind of file! Help!");
+}
+
+static BigDBusJsonMethod test_service_methods[] = {
+    { "getSomeStuffSync", test_service_get_some_stuff_sync, NULL },
+    { "alwaysErrorSync",  test_service_always_error_sync, NULL }
+};
+
+static void
+on_test_service_acquired(DBusConnection *connection,
+                         const char     *name,
+                         void           *data)
+{
+    g_assert(!currently_have_test_service);
+    currently_have_test_service = TRUE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService acquired by child");
+
+    big_dbus_register_json(connection,
+                           "com.litl.TestIface",
+                           test_service_methods,
+                           G_N_ELEMENTS(test_service_methods));
+
+    test_service_object = g_object_new(G_TYPE_OBJECT, NULL);
+
+    big_dbus_register_g_object(connection,
+                               "/com/litl/test/object42",
+                               test_service_object,
+                               "com.litl.TestIface");
+}
+
+static void
+on_test_service_lost(DBusConnection *connection,
+                     const char     *name,
+                     void           *data)
+{
+    g_assert(currently_have_test_service);
+    currently_have_test_service = FALSE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestService lost by child");
+
+    big_dbus_unregister_g_object(connection,
+                                 "/com/litl/test/object42");
+
+    big_dbus_unregister_json(connection,
+                             "com.litl.TestIface");
+}
+
+static BigDBusNameOwnerFuncs test_service_funcs = {
+    "com.litl.TestService",
+    BIG_DBUS_NAME_SINGLE_INSTANCE,
+    on_test_service_acquired,
+    on_test_service_lost
+};
+
+static void
+do_test_service_child(void)
+{
+    GMainLoop *loop;
+
+    g_type_init();
+
+    loop = g_main_loop_new(NULL, FALSE);
+
+    big_dbus_acquire_name(DBUS_BUS_SESSION,
+                          &test_service_funcs,
+                          NULL);
+
+    g_main_loop_run(loop);
+
+    /* Don't return to the test program main() */
+    exit(0);
+}
+
+/*
+ * Second child service we forked, tests IO streams
+ */
+
+static gboolean currently_have_test_io = FALSE;
+static GObject *test_io_object = NULL;
+
+static BigDBusInputStream  *io_input_stream = NULL;
+static BigDBusOutputStream *io_output_stream = NULL;
+
+static GString *input_buffer = NULL;
+
+static void
+test_io_confirm_streams_data(DBusConnection  *connection,
+                             DBusMessage     *message,
+                             DBusMessageIter *in_iter,
+                             DBusMessageIter *out_iter,
+                             void            *data,
+                             DBusError       *error)
+{
+    const char *received;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestIO got confirmStreamsData");
+
+    g_assert(G_IS_OBJECT(data));
+
+    received = extract_string_arg(in_iter, "received", error);
+    if (received == NULL) {
+        g_assert(error == NULL || dbus_error_is_set(error));
+        return;
+    }
+
+    if (strcmp(received, stream_data_from_io_service) != 0) {
+        g_error("We sent the main process '%s' but it says it got '%s'",
+                stream_data_from_io_service,
+                received);
+        return;
+    }
+
+    /* We were reading from the main process in the main loop.
+     * As a hack, we'll block in the main loop here to test.
+     * In a real app, never block in the main loop; you would
+     * just plain block, e.g. in g_input_stream_read(), if
+     * you wanted to block. But don't block.
+     */
+    while (io_input_stream != NULL) {
+        g_main_iteration(TRUE);
+    }
+
+    big_dbus_append_json_entry_STRING(out_iter,
+                                      "received",
+                                      input_buffer->str);
+
+    g_string_free(input_buffer, TRUE);
+    input_buffer = NULL;
+}
+
+static void
+on_input_ready(BigDBusInputStream *dbus_stream,
+               void               *data)
+{
+    GInputStream *stream;
+    char buf[3];
+    gssize result;
+    GError *error;
+
+    stream = G_INPUT_STREAM(dbus_stream);
+
+    g_assert(dbus_stream == io_input_stream);
+
+    /* test get_received() */
+    g_assert(big_dbus_input_stream_get_received(dbus_stream) <= strlen(stream_data_to_io_service));
+
+    /* Should not block, since we got the ready-to-read signal */
+    error = NULL;
+    result = g_input_stream_read(G_INPUT_STREAM(io_input_stream),
+                                 buf,
+                                 sizeof(buf),
+                                 NULL,
+                                 &error);
+    if (result < 0) {
+        g_error("Error reading bytes from input stream: %s",
+                error->message);
+        g_error_free(error);
+    }
+
+    if (result == 0) {
+        /* EOF */
+        if (!g_input_stream_close(G_INPUT_STREAM(io_input_stream), NULL, &error)) {
+            g_error("Error closing input stream in child: %s", error->message);
+            g_error_free(error);
+        }
+        g_object_unref(io_input_stream);
+        io_input_stream = NULL;
+
+        return;
+    }
+
+    g_string_append_len(input_buffer, buf, result);
+
+    /* We should automatically get another callback if there's more data or EOF
+     * was not yet reached.
+     */
+}
+
+static void
+test_io_setup_streams(DBusConnection  *connection,
+                      DBusMessage     *message,
+                      DBusMessageIter *in_iter,
+                      DBusMessageIter *out_iter,
+                      void            *data,
+                      DBusError       *error)
+{
+    const char *stream_path;
+    gsize total;
+    gsize remaining;
+    gssize result;
+    GError *gerror;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestIO got setupStreams");
+
+    g_assert(G_IS_OBJECT(data));
+
+    stream_path = extract_string_arg(in_iter, "stream", error);
+
+    if (stream_path == NULL) {
+        g_assert(error == NULL || dbus_error_is_set(error));
+        return;
+    }
+
+    /* Create output stream to write to caller's path */
+    io_output_stream =
+        big_dbus_output_stream_new(connection,
+                                   dbus_message_get_sender(message),
+                                   stream_path);
+
+    /* Create input stream and return its path to caller */
+    io_input_stream =
+        g_object_new(BIG_TYPE_DBUS_INPUT_STREAM,
+                     NULL);
+    big_dbus_input_stream_attach(io_input_stream,
+                                 connection);
+    stream_path = big_dbus_input_stream_get_path(io_input_stream);
+
+    big_dbus_append_json_entry_STRING(out_iter,
+                                      "stream",
+                                      stream_path);
+
+    /* Set up callbacks to read input stream in an async way */
+    input_buffer = g_string_new(NULL);
+
+    g_signal_connect(io_input_stream,
+                     "ready-to-read",
+                     G_CALLBACK(on_input_ready),
+                     NULL);
+
+    /* Write to output stream */
+    gerror = NULL;
+    total = strlen(stream_data_from_io_service);
+    remaining = total;
+    while (remaining > 0) {
+        /* One byte at a time, fun torture test, totally silly in real
+         * code of course
+         */
+        result = g_output_stream_write(G_OUTPUT_STREAM(io_output_stream),
+                                       stream_data_from_io_service + (total - remaining),
+                                       1,
+                                       NULL,
+                                       &gerror);
+        if (result < 0) {
+            g_assert(gerror != NULL);
+            g_error("Error writing to output stream: %s", gerror->message);
+            g_error_free(gerror);
+        }
+
+        if (result != 1) {
+            g_error("Wrote %d instead of 1 bytes", (int) result);
+        }
+
+        remaining -= 1;
+    }
+
+    /* flush should do nothing here, and is not needed, but
+     * just calling it to test it
+     */
+    if (!g_output_stream_flush(G_OUTPUT_STREAM(io_output_stream), NULL, &gerror)) {
+        g_assert(gerror != NULL);
+        g_error("Error flushing output stream: %s", gerror->message);
+        g_error_free(gerror);
+    }
+
+    if (!g_output_stream_close(G_OUTPUT_STREAM(io_output_stream), NULL, &gerror)) {
+        g_assert(gerror != NULL);
+        g_error("Error closing output stream: %s", gerror->message);
+        g_error_free(gerror);
+    }
+    g_object_unref(io_output_stream);
+    io_output_stream = NULL;
+
+
+    /* Now return, and wait for our input stream data to come in from
+     * the main process
+     */
+}
+
+static BigDBusJsonMethod test_io_methods[] = {
+    { "setupStreams", test_io_setup_streams, NULL },
+    { "confirmStreamsData", test_io_confirm_streams_data, NULL }
+};
+
+static void
+on_test_io_acquired(DBusConnection *connection,
+                    const char     *name,
+                    void           *data)
+{
+    g_assert(!currently_have_test_io);
+    currently_have_test_io = TRUE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestIO acquired by child");
+
+    big_dbus_register_json(connection,
+                           "com.litl.TestIO",
+                           test_io_methods,
+                           G_N_ELEMENTS(test_io_methods));
+
+    test_io_object = g_object_new(G_TYPE_OBJECT, NULL);
+
+    big_dbus_register_g_object(connection,
+                               "/com/litl/test/object47",
+                               test_io_object,
+                               "com.litl.TestIO");
+}
+
+static void
+on_test_io_lost(DBusConnection *connection,
+                const char     *name,
+                void           *data)
+{
+    g_assert(currently_have_test_io);
+    currently_have_test_io = FALSE;
+
+    big_debug(BIG_DEBUG_IN_TESTS,
+              "com.litl.TestIO lost by child");
+
+    big_dbus_unregister_g_object(connection,
+                                 "/com/litl/test/object47");
+
+    big_dbus_unregister_json(connection,
+                             "com.litl.TestIO");
+}
+
+static BigDBusNameOwnerFuncs test_io_funcs = {
+    "com.litl.TestIO",
+    BIG_DBUS_NAME_SINGLE_INSTANCE,
+    on_test_io_acquired,
+    on_test_io_lost
+};
+
+static void
+do_test_io_child(void)
+{
+    GMainLoop *loop;
+
+    g_type_init();
+
+    loop = g_main_loop_new(NULL, FALSE);
+
+    big_dbus_acquire_name(DBUS_BUS_SESSION,
+                          &test_io_funcs,
+                          NULL);
+
+    g_main_loop_run(loop);
+
+    /* Don't return to the test program main() */
+    exit(0);
+}
+
+#endif /* BIG_BUILD_TESTS */
diff --git a/modules/dbus/util/dbus.h b/modules/dbus/util/dbus.h
new file mode 100644
index 0000000..5b31c39
--- /dev/null
+++ b/modules/dbus/util/dbus.h
@@ -0,0 +1,216 @@
+/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
+/* Copyright 2008 litl, LLC. All Rights Reserved. */
+
+#ifndef __BIG_UTIL_DBUS_H__
+#define __BIG_UTIL_DBUS_H__
+
+#include <glib-object.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+/* Convenience macro */
+
+#define BIG_DBUS_NAME_FROM_TYPE(type) ((type) == DBUS_BUS_SESSION ? "session" : "system")
+
+/* Error names */
+#define BIG_DBUS_ERROR_STREAM_RECEIVER_CLOSED "com.litl.Error.Stream.ReceiverClosed"
+
+/*
+ * Monitor whether we are connected / not-connected to the bus
+ */
+
+typedef void (* BigDBusConnectionOpenedFunc)   (DBusConnection *connection,
+                                                void           *data);
+typedef void (* BigDBusConnectionClosedFunc)   (DBusConnection *connection,
+                                                void           *data);
+
+typedef struct {
+    DBusBusType which_bus;
+    BigDBusConnectionOpenedFunc opened;
+    BigDBusConnectionClosedFunc closed;
+} BigDBusConnectFuncs;
+
+void big_dbus_add_connect_funcs             (const BigDBusConnectFuncs *funcs,
+                                             void                      *data);
+void big_dbus_remove_connect_funcs          (const BigDBusConnectFuncs *funcs,
+                                             void                      *data);
+void big_dbus_add_connect_funcs_sync_notify (const BigDBusConnectFuncs *funcs,
+                                             void                      *data);
+
+
+void big_dbus_add_bus_weakref    (DBusBusType      bus_type,
+                                  DBusConnection **connection_p);
+void big_dbus_remove_bus_weakref (DBusBusType      bus_type,
+                                  DBusConnection **connection_p);
+
+void big_dbus_try_connecting_now (DBusBusType which_bus);
+
+/*
+ * Own a bus name
+ *
+ */
+
+typedef enum {
+    BIG_DBUS_NAME_SINGLE_INSTANCE,
+    BIG_DBUS_NAME_MANY_INSTANCES
+} BigDBusNameType;
+
+typedef void (* BigDBusNameAcquiredFunc) (DBusConnection *connection,
+                                          const char     *name,
+                                          void           *data);
+typedef void (* BigDBusNameLostFunc)     (DBusConnection *connection,
+                                          const char     *name,
+                                          void           *data);
+
+typedef struct {
+    const char *name;
+    BigDBusNameType type;
+    BigDBusNameAcquiredFunc acquired;
+    BigDBusNameLostFunc lost;
+} BigDBusNameOwnerFuncs;
+
+guint        big_dbus_acquire_name       (DBusBusType                  bus_type,
+                                          const BigDBusNameOwnerFuncs *funcs,
+                                          void                        *data);
+void         big_dbus_release_name       (DBusBusType                  bus_type,
+                                          const BigDBusNameOwnerFuncs *funcs,
+                                          void                        *data);
+void         big_dbus_release_name_by_id (DBusBusType                  bus_type,
+                                          guint                        id);
+
+/*
+ * Keep track of someone else's bus name
+ *
+ */
+
+typedef enum {
+    BIG_DBUS_NAME_START_IF_NOT_FOUND = 0x1
+} BigDBusWatchNameFlags;
+
+typedef void (* BigDBusNameAppearedFunc) (DBusConnection *connection,
+                                          const char     *name,
+                                          const char     *new_owner_unique_name,
+                                          void           *data);
+typedef void (* BigDBusNameVanishedFunc) (DBusConnection *connection,
+                                          const char     *name,
+                                          const char     *old_owner_unique_name,
+                                          void           *data);
+
+typedef struct {
+    BigDBusNameAppearedFunc appeared;
+    BigDBusNameVanishedFunc vanished;
+} BigDBusWatchNameFuncs;
+
+void        big_dbus_watch_name             (DBusBusType                  bus_type,
+                                             const char                  *name,
+                                             BigDBusWatchNameFlags        flags,
+                                             const BigDBusWatchNameFuncs *funcs,
+                                             void                        *data);
+void        big_dbus_unwatch_name           (DBusBusType                  bus_type,
+                                             const char                  *name,
+                                             const BigDBusWatchNameFuncs *funcs,
+                                             void                        *data);
+const char* big_dbus_get_watched_name_owner (DBusBusType                  bus_type,
+                                             const char                  *name);
+
+
+typedef void (* BigDBusSignalHandler) (DBusConnection *connection,
+                                       DBusMessage    *message,
+                                       void           *data);
+int big_dbus_watch_signal          (DBusBusType           bus_type,
+                                    const char           *sender,
+                                    const char           *path,
+                                    const char           *iface,
+                                    const char           *name,
+                                    BigDBusSignalHandler  handler,
+                                    void                 *data,
+                                    GDestroyNotify        data_dnotify);
+void big_dbus_unwatch_signal       (DBusBusType           bus_type,
+                                    const char           *sender,
+                                    const char           *path,
+                                    const char           *iface,
+                                    const char           *name,
+                                    BigDBusSignalHandler  handler,
+                                    void                 *data);
+void big_dbus_unwatch_signal_by_id (DBusBusType           bus_type,
+                                    int                   id);
+
+/* A "json method" is a D-Bus method with signature
+ *      DICT jsonMethodName(DICT)
+ * with the idea that it both takes and returns
+ * a JavaScript-style dictionary. This makes
+ * our JavaScript-to-dbus bindings really simple,
+ * and avoids a lot of futzing with dbus IDL.
+ *
+ * Of course it's completely annoying for someone
+ * using D-Bus in a "normal" way but the idea is just
+ * to use this to communicate within our own app
+ * that happens to consist of multiple processes
+ * and have bits written in JS.
+ */
+typedef void (* BigDBusJsonSyncMethodFunc)  (DBusConnection  *connection,
+                                             DBusMessage     *message,
+                                             DBusMessageIter *in_iter,
+                                             DBusMessageIter *out_iter,
+                                             void            *data,
+                                             DBusError       *error);
+
+typedef void (* BigDBusJsonAsyncMethodFunc) (DBusConnection  *connection,
+                                             DBusMessage     *message,
+                                             DBusMessageIter *in_iter,
+                                             void            *data);
+
+typedef struct {
+    const char *name;
+    /* one of these two but not both should be non-NULL */
+    BigDBusJsonSyncMethodFunc sync_func;
+    BigDBusJsonAsyncMethodFunc async_func;
+} BigDBusJsonMethod;
+
+void big_dbus_register_json       (DBusConnection          *connection,
+                                   const char              *iface_name,
+                                   const BigDBusJsonMethod *methods,
+                                   int                      n_methods);
+void big_dbus_unregister_json     (DBusConnection          *connection,
+                                   const char              *iface_name);
+void big_dbus_register_g_object   (DBusConnection          *connection,
+                                   const char              *path,
+                                   GObject                 *gobj,
+                                   const char              *iface_name);
+void big_dbus_unregister_g_object (DBusConnection          *connection,
+                                   const char              *path);
+
+void big_dbus_append_json_entry              (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              int               dbus_type,
+                                              void             *basic_value_p);
+void big_dbus_append_json_entry_STRING       (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              const char       *value);
+void big_dbus_append_json_entry_INT32        (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              dbus_int32_t      value);
+void big_dbus_append_json_entry_DOUBLE       (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              double            value);
+void big_dbus_append_json_entry_BOOLEAN      (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              dbus_bool_t       value);
+void big_dbus_append_json_entry_EMPTY_ARRAY  (DBusMessageIter  *dict_iter,
+                                              const char       *key);
+void big_dbus_append_json_entry_STRING_ARRAY (DBusMessageIter  *dict_iter,
+                                              const char       *key,
+                                              const char      **value);
+
+gboolean big_dbus_message_iter_get_gsize  (DBusMessageIter  *iter,
+                                           gsize            *value_p);
+gboolean big_dbus_message_iter_get_gssize (DBusMessageIter  *iter,
+                                           gssize           *value_p);
+
+void big_dbus_start_service(DBusConnection *connection,
+                            const char     *name);
+
+G_END_DECLS
+
+#endif  /* __BIG_UTIL_DBUS_H__ */



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