[kdbus] Make SipHash function as a private utility function to kdbus
[platform/upstream/glib.git] / gio / gkdbus.c
index cb614c2..10becde 100644 (file)
 #include "glibintl.h"
 #include "gunixfdmessage.h"
 
-#define KDBUS_TIMEOUT_NS 2000000000LU
-#define KDBUS_POOL_SIZE (16 * 1024LU * 1024LU)
-#define KDBUS_ALIGN8(l) (((l) + 7) & ~7)
-#define KDBUS_ALIGN8_PTR(p) ((void*) (uintptr_t)(p))
+#define KDBUS_MSG_MAX_SIZE         8192
+#define KDBUS_TIMEOUT_NS           2000000000LU
+#define KDBUS_POOL_SIZE            (16 * 1024LU * 1024LU)
+#define KDBUS_ALIGN8(l)            (((l) + 7) & ~7)
+#define KDBUS_ALIGN8_PTR(p)        ((void*) (uintptr_t)(p))
 
 #define KDBUS_ITEM_HEADER_SIZE G_STRUCT_OFFSET(struct kdbus_item, data)
 #define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
         for (item = (head)->first;                               \
              (guint8 *)(item) < (guint8 *)(head) + (head)->size; \
              item = KDBUS_ITEM_NEXT(item))
+#define KDBUS_FOREACH(iter, first, _size)                             \
+        for (iter = (first);                                          \
+             ((guint8 *)(iter) < (guint8 *)(first) + (_size)) &&      \
+               ((guint8 *)(iter) >= (guint8 *)(first));               \
+             iter = (void*)(((guint8 *)iter) + KDBUS_ALIGN8((iter)->size)))
 
 #define g_alloca0(x) memset(g_alloca(x), '\0', (x))
 
@@ -81,7 +87,37 @@ struct dbus_fixed_header {
 #define DBUS_EXTENDED_HEADER_TYPE  ((const GVariantType *) "a{tv}")
 #define DBUS_MESSAGE_TYPE          ((const GVariantType *) "((yyyyut)a{tv}v)")
 
-#define KDBUS_MSG_MAX_SIZE         8192
+/*
+ * Macros for SipHash algorithm
+ */
+
+#define ROTL(x,b) (guint64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
+
+#define U32TO8_LE(p, v)         \
+    (p)[0] = (guint8)((v)      ); (p)[1] = (guint8)((v) >>  8); \
+    (p)[2] = (guint8)((v) >> 16); (p)[3] = (guint8)((v) >> 24);
+
+#define U64TO8_LE(p, v)         \
+  U32TO8_LE((p),     (guint32)((v)      ));   \
+  U32TO8_LE((p) + 4, (guint32)((v) >> 32));
+
+#define U8TO64_LE(p) \
+  (((guint64)((p)[0])      ) | \
+   ((guint64)((p)[1]) <<  8) | \
+   ((guint64)((p)[2]) << 16) | \
+   ((guint64)((p)[3]) << 24) | \
+   ((guint64)((p)[4]) << 32) | \
+   ((guint64)((p)[5]) << 40) | \
+   ((guint64)((p)[6]) << 48) | \
+   ((guint64)((p)[7]) << 56))
+
+#define SIPROUND            \
+  do {              \
+    v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
+    v2 += v3; v3=ROTL(v3,16); v3 ^= v2;     \
+    v0 += v3; v3=ROTL(v3,21); v3 ^= v0;     \
+    v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
+  } while(0)
 
 typedef enum
 {
@@ -120,6 +156,12 @@ struct _GKDBusWorker
   guint              timed_out : 1;
 
   guchar             bus_id[16];
+
+  GDBusCapabilityFlags                     capabilities;
+  GDBusWorkerMessageReceivedCallback       message_received_callback;
+  GDBusWorkerMessageAboutToBeSentCallback  message_about_to_be_sent_callback;
+  GDBusWorkerDisconnectedCallback          disconnected_callback;
+  gpointer                                 user_data;
 };
 
 static gssize _g_kdbus_receive (GKDBusWorker  *kdbus,
@@ -141,6 +183,341 @@ const guint8 hash_keys[8][16] =
   {0xf2,0x77,0xe9,0x6f,0x93,0xb5,0x4e,0x71,0x9a,0x0c,0x34,0x88,0x39,0x25,0xbf,0x35}
 };
 
+enum {
+  MATCH_ELEMENT_TYPE,
+  MATCH_ELEMENT_SENDER,
+  MATCH_ELEMENT_INTERFACE,
+  MATCH_ELEMENT_MEMBER,
+  MATCH_ELEMENT_PATH,
+  MATCH_ELEMENT_PATH_NAMESPACE,
+  MATCH_ELEMENT_DESTINATION,
+  MATCH_ELEMENT_ARG0NAMESPACE,
+  MATCH_ELEMENT_EAVESDROP,
+  MATCH_ELEMENT_ARGN,
+  MATCH_ELEMENT_ARGNPATH,
+};
+
+/* MatchElement struct */
+typedef struct {
+  guint16 type;
+  guint16 arg;
+  char *value;
+} MatchElement;
+
+/* Match struct */
+typedef struct {
+  int n_elements;
+  MatchElement *elements;
+} Match;
+
+
+/**
+ * SipHash algorithm
+ *
+ */
+static void
+_g_siphash24 (guint8         out[8],
+              const void    *_in,
+              gsize          inlen,
+              const guint8   k[16])
+{
+  /* "somepseudorandomlygeneratedbytes" */
+  guint64 v0 = 0x736f6d6570736575ULL;
+  guint64 v1 = 0x646f72616e646f6dULL;
+  guint64 v2 = 0x6c7967656e657261ULL;
+  guint64 v3 = 0x7465646279746573ULL;
+  guint64 b;
+  guint64 k0 = U8TO64_LE (k);
+  guint64 k1 = U8TO64_LE (k + 8);
+  guint64 m;
+  const guint8 *in = _in;
+  const guint8 *end = in + inlen - (inlen % sizeof(guint64));
+  const int left = inlen & 7;
+  b = ((guint64) inlen) << 56;
+  v3 ^= k1;
+  v2 ^= k0;
+  v1 ^= k1;
+  v0 ^= k0;
+
+  for (; in != end; in += 8)
+    {
+      m = U8TO64_LE (in);
+      v3 ^= m;
+      SIPROUND;
+      SIPROUND;
+      v0 ^= m;
+    }
+
+  switch (left)
+    {
+      case 7: b |= ((guint64) in[6]) << 48;
+      case 6: b |= ((guint64) in[5]) << 40;
+      case 5: b |= ((guint64) in[4]) << 32;
+      case 4: b |= ((guint64) in[3]) << 24;
+      case 3: b |= ((guint64) in[2]) << 16;
+      case 2: b |= ((guint64) in[1]) <<  8;
+      case 1: b |= ((guint64) in[0]); break;
+      case 0: break;
+    }
+
+  v3 ^= b;
+  SIPROUND;
+  SIPROUND;
+  v0 ^= b;
+
+  v2 ^= 0xff;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  b = v0 ^ v1 ^ v2  ^ v3;
+  U64TO8_LE (out, b);
+}
+
+
+/**
+ * is_key()
+ *
+ */
+static gboolean
+is_key (const char *key_start, const char *key_end, char *value)
+{
+  gsize len = strlen (value);
+
+  if (len != key_end - key_start)
+    return FALSE;
+
+  return strncmp (key_start, value, len) == 0;
+}
+
+
+/**
+ * parse_key()
+ *
+ */
+static gboolean
+parse_key (MatchElement *element, const char *key_start, const char *key_end)
+{
+  gboolean res = TRUE;
+
+  if (is_key (key_start, key_end, "type"))
+    {
+      element->type = MATCH_ELEMENT_TYPE;
+    }
+  else if (is_key (key_start, key_end, "sender"))
+    {
+      element->type = MATCH_ELEMENT_SENDER;
+    }
+  else if (is_key (key_start, key_end, "interface"))
+    {
+      element->type = MATCH_ELEMENT_INTERFACE;
+    }
+  else if (is_key (key_start, key_end, "member"))
+    {
+      element->type = MATCH_ELEMENT_MEMBER;
+    }
+  else if (is_key (key_start, key_end, "path"))
+    {
+      element->type = MATCH_ELEMENT_PATH;
+    }
+  else if (is_key (key_start, key_end, "path_namespace"))
+    {
+      element->type = MATCH_ELEMENT_PATH_NAMESPACE;
+    }
+  else if (is_key (key_start, key_end, "destination"))
+    {
+      element->type = MATCH_ELEMENT_DESTINATION;
+    }
+  else if (is_key (key_start, key_end, "arg0namespace"))
+    {
+      element->type = MATCH_ELEMENT_ARG0NAMESPACE;
+    }
+  else if (is_key (key_start, key_end, "eavesdrop"))
+    {
+      element->type = MATCH_ELEMENT_EAVESDROP;
+    }
+  else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg"))
+    {
+      const char *digits = key_start + 3;
+      const char *end_digits = digits;
+
+      while (end_digits < key_end && g_ascii_isdigit (*end_digits))
+        end_digits++;
+
+      if (end_digits == key_end) /* argN */
+        {
+          element->type = MATCH_ELEMENT_ARGN;
+          element->arg = atoi (digits);
+        }
+      else if (is_key (end_digits, key_end, "path")) /* argNpath */
+        {
+          element->type = MATCH_ELEMENT_ARGNPATH;
+          element->arg = atoi (digits);
+        }
+      else
+        res = FALSE;
+    }
+  else
+    res = FALSE;
+
+  return res;
+}
+
+
+/**
+ * parse_value()
+ *
+ */
+static const char *
+parse_value (MatchElement *element, const char *s)
+{
+  char quote_char;
+  GString *value;
+
+  value = g_string_new ("");
+
+  quote_char = 0;
+
+  for (;*s; s++)
+    {
+      if (quote_char == 0)
+        {
+          switch (*s)
+            {
+            case '\'':
+              quote_char = '\'';
+              break;
+
+            case ',':
+              s++;
+              goto out;
+
+            case '\\':
+              quote_char = '\\';
+              break;
+
+            default:
+              g_string_append_c (value, *s);
+              break;
+            }
+        }
+      else if (quote_char == '\\')
+        {
+          /* \ only counts as an escape if escaping a quote mark */
+          if (*s != '\'')
+            g_string_append_c (value, '\\');
+
+          g_string_append_c (value, *s);
+          quote_char = 0;
+        }
+      else /* quote_char == ' */
+        {
+          if (*s == '\'')
+            quote_char = 0;
+          else
+            g_string_append_c (value, *s);
+        }
+    }
+
+ out:
+  if (quote_char == '\\')
+    g_string_append_c (value, '\\');
+  else if (quote_char == '\'')
+    {
+      g_string_free (value, TRUE);
+      return NULL;
+    }
+
+  element->value = g_string_free (value, FALSE);
+  return s;
+}
+
+
+/**
+ * match_new()
+ *
+ */
+static Match *
+match_new (const char *str)
+{
+  Match *match;
+  GArray *elements;
+  const char *p;
+  const char *key_start;
+  const char *key_end;
+  MatchElement element;
+  int i;
+
+  elements = g_array_new (TRUE, TRUE, sizeof (MatchElement));
+
+  p = str;
+
+  while (*p != 0)
+    {
+      memset (&element, 0, sizeof (element));
+
+      /* Skip initial whitespace */
+      while (*p && g_ascii_isspace (*p))
+        p++;
+
+      key_start = p;
+
+      /* Read non-whitespace non-equals chars */
+      while (*p && *p != '=' && !g_ascii_isspace (*p))
+        p++;
+
+      key_end = p;
+
+      /* Skip any whitespace after key */
+      while (*p && g_ascii_isspace (*p))
+        p++;
+
+      if (key_start == key_end)
+        continue; /* Allow trailing whitespace */
+      if (*p != '=')
+        goto error;
+
+      ++p;
+
+      if (!parse_key (&element, key_start, key_end))
+        goto error;
+
+      p = parse_value (&element, p);
+      if (p == NULL)
+        goto error;
+
+      g_array_append_val (elements, element);
+    }
+
+  match = g_new0 (Match, 1);
+  match->n_elements = elements->len;
+  match->elements = (MatchElement *)g_array_free (elements, FALSE);
+
+  return match;
+
+ error:
+  for (i = 0; i < elements->len; i++)
+    g_free (g_array_index (elements, MatchElement, i).value);
+  g_array_free (elements, TRUE);
+  return NULL;
+}
+
+
+/**
+ * match_free()
+ *
+ */
+static void
+match_free (Match *match)
+{
+  int i;
+  for (i = 0; i < match->n_elements; i++)
+    g_free (match->elements[i].value);
+  g_free (match->elements);
+  g_free (match);
+}
+
+
 /**
  * g_kdbus_finalize:
  *
@@ -197,9 +574,9 @@ kdbus_ready (gint         fd,
 }
 
 gboolean
-_g_kdbus_open (GKDBusWorker       *worker,
-               const gchar  *address,
-               GError      **error)
+_g_kdbus_open (GKDBusWorker  *worker,
+               const gchar   *address,
+               GError       **error)
 {
   g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
 
@@ -212,11 +589,6 @@ _g_kdbus_open (GKDBusWorker       *worker,
 
   worker->closed = FALSE;
 
-  worker->context = g_main_context_ref_thread_default ();
-  worker->source = g_unix_fd_source_new (worker->fd, G_IO_IN);
-  g_source_set_callback (worker->source, (GSourceFunc) kdbus_ready, worker, NULL);
-  g_source_attach (worker->source, worker->context);
-
   return TRUE;
 }
 
@@ -229,12 +601,13 @@ static gboolean
 g_kdbus_free_data (GKDBusWorker      *kdbus,
                    guint64      offset)
 {
-  struct kdbus_cmd_free cmd;
+  struct kdbus_cmd_free cmd = {
+    .size = sizeof(cmd),
+    .offset = offset,
+    .flags = 0
+  };
   int ret;
 
-  cmd.offset = offset;
-  cmd.flags = 0;
-
   ret = ioctl (kdbus->fd, KDBUS_CMD_FREE, &cmd);
   if (ret < 0)
       return FALSE;
@@ -313,7 +686,7 @@ GVariant *
 _g_kdbus_Hello (GKDBusWorker *worker,
                 GError    **error)
 {
-  struct kdbus_cmd_hello *hello;
+  struct kdbus_cmd_hello *cmd;
   struct kdbus_item *item;
 
   gchar *conn_name;
@@ -325,20 +698,20 @@ _g_kdbus_Hello (GKDBusWorker *worker,
   size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_hello, items)) +
          KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1);
 
-  hello = g_alloca0 (size);
-  hello->flags = worker->flags;
-  hello->attach_flags_send = worker->attach_flags_send;
-  hello->attach_flags_recv = worker->attach_flags_recv;
-  hello->size = size;
-  hello->pool_size = KDBUS_POOL_SIZE;
+  cmd = g_alloca0 (size);
+  cmd->flags = worker->flags;
+  cmd->attach_flags_send = worker->attach_flags_send;
+  cmd->attach_flags_recv = worker->attach_flags_recv;
+  cmd->size = size;
+  cmd->pool_size = KDBUS_POOL_SIZE;
 
-  item = hello->items;
+  item = cmd->items;
   item->size = G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1;
   item->type = KDBUS_ITEM_CONN_DESCRIPTION;
   memcpy (item->str, conn_name, conn_name_size+1);
   item = KDBUS_ITEM_NEXT (item);
 
-  if (ioctl(worker->fd, KDBUS_CMD_HELLO, hello))
+  if (ioctl(worker->fd, KDBUS_CMD_HELLO, cmd))
     {
       g_set_error (error, G_IO_ERROR,
                    g_io_error_from_errno (errno),
@@ -357,7 +730,7 @@ _g_kdbus_Hello (GKDBusWorker *worker,
       return NULL;
     }
 
-  if (hello->bus_flags > 0xFFFFFFFFULL)
+  if (cmd->bus_flags > 0xFFFFFFFFULL)
     {
       g_set_error_literal (error,
                            G_IO_ERROR,
@@ -366,14 +739,14 @@ _g_kdbus_Hello (GKDBusWorker *worker,
       return NULL;
     }
 
-  memcpy (worker->bus_id, hello->id128, 16);
+  memcpy (worker->bus_id, cmd->id128, 16);
 
-  worker->unique_id = hello->id;
-  asprintf(&worker->unique_name, ":1.%llu", (unsigned long long) hello->id);
+  worker->unique_id = cmd->id;
+  asprintf(&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id);
 
   /* read bloom filters parameters */
-  worker->bloom_size = (gsize) hello->bloom.size;
-  worker->bloom_n_hash = (guint) hello->bloom.n_hash;
+  //worker->bloom_size = (gsize) cmd->bloom.size;
+  //worker->bloom_n_hash = (guint) cmd->bloom.n_hash;
 
   return g_variant_new ("(s)", worker->unique_name);
 }
@@ -390,7 +763,7 @@ _g_kdbus_RequestName (GKDBusWorker        *worker,
                       GError             **error)
 {
   GVariant *result;
-  struct kdbus_cmd_name *kdbus_name;
+  struct kdbus_cmd *cmd;
   guint64 kdbus_flags;
   gssize len, size;
   gint status, ret;
@@ -418,15 +791,15 @@ _g_kdbus_RequestName (GKDBusWorker        *worker,
   g_kdbus_translate_nameowner_flags (flags, &kdbus_flags);
 
   len = strlen(name) + 1;
-  size = G_STRUCT_OFFSET (struct kdbus_cmd_name, items) + KDBUS_ITEM_SIZE(len);
-  kdbus_name = g_alloca0 (size);
-  kdbus_name->size = size;
-  kdbus_name->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
-  kdbus_name->items[0].type = KDBUS_ITEM_NAME;
-  kdbus_name->flags = kdbus_flags;
-  memcpy (kdbus_name->items[0].str, name, len);
-
-  ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, kdbus_name);
+  size = G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+  cmd->items[0].type = KDBUS_ITEM_NAME;
+  cmd->flags = kdbus_flags;
+  memcpy (cmd->items[0].str, name, len);
+
+  ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, cmd);
   if (ret < 0)
     {
       if (errno == EEXIST)
@@ -443,7 +816,7 @@ _g_kdbus_RequestName (GKDBusWorker        *worker,
         }
     }
 
-  if (kdbus_name->flags & KDBUS_NAME_IN_QUEUE)
+  if (cmd->flags & KDBUS_NAME_IN_QUEUE)
     status = G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE;
 
   result = g_variant_new ("(u)", status);
@@ -462,7 +835,7 @@ _g_kdbus_ReleaseName (GKDBusWorker     *worker,
                       GError             **error)
 {
   GVariant *result;
-  struct kdbus_cmd_name *kdbus_name;
+  struct kdbus_cmd *cmd;
   gssize len, size;
   gint status, ret;
 
@@ -487,14 +860,14 @@ _g_kdbus_ReleaseName (GKDBusWorker     *worker,
     }
 
   len = strlen(name) + 1;
-  size = G_STRUCT_OFFSET (struct kdbus_cmd_name, items) + KDBUS_ITEM_SIZE(len);
-  kdbus_name = g_alloca0 (size);
-  kdbus_name->size = size;
-  kdbus_name->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
-  kdbus_name->items[0].type = KDBUS_ITEM_NAME;
-  memcpy (kdbus_name->items[0].str, name, len);
-
-  ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, kdbus_name);
+  size = G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+  cmd->items[0].type = KDBUS_ITEM_NAME;
+  memcpy (cmd->items[0].str, name, len);
+
+  ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, cmd);
   if (ret < 0)
     {
       if (errno == ESRCH)
@@ -552,10 +925,10 @@ _g_kdbus_GetListNames (GKDBusWorker  *worker,
 {
   GVariant *result;
   GVariantBuilder *builder;
-
-  struct kdbus_cmd_name_list cmd = {};
-  struct kdbus_name_list *name_list;
-  struct kdbus_name_info *name;
+  struct kdbus_info *name_list, *name;
+  struct kdbus_cmd_list cmd = {
+    .size = sizeof(cmd)
+  };
 
   guint64 prev_id;
   gint ret;
@@ -563,11 +936,11 @@ _g_kdbus_GetListNames (GKDBusWorker  *worker,
   prev_id = 0;
 
   if (list_name_type)
-    cmd.flags = KDBUS_NAME_LIST_ACTIVATORS;                     /* ListActivatableNames */
+    cmd.flags = KDBUS_LIST_ACTIVATORS;                /* ListActivatableNames */
   else
-    cmd.flags = KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_NAMES; /* ListNames */
+    cmd.flags = KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES; /* ListNames */
 
-  ret = ioctl(worker->fd, KDBUS_CMD_NAME_LIST, &cmd);
+  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
   if (ret < 0)
     {
       g_set_error (error,
@@ -577,23 +950,23 @@ _g_kdbus_GetListNames (GKDBusWorker  *worker,
       return NULL;
     }
 
-  name_list = (struct kdbus_name_list *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
+  name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
   builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
 
-  KDBUS_ITEM_FOREACH(name, name_list, names)
+  KDBUS_FOREACH(name, name_list, cmd.list_size)
     {
       struct kdbus_item *item;
       const gchar *item_name = "";
 
-      if ((cmd.flags & KDBUS_NAME_LIST_UNIQUE) && name->owner_id != prev_id)
+      if ((cmd.flags & KDBUS_LIST_UNIQUE) && name->id != prev_id)
         {
           GString *unique_name;
 
           unique_name = g_string_new (NULL);
-          g_string_printf (unique_name, ":1.%llu", name->owner_id);
+          g_string_printf (unique_name, ":1.%llu", name->id);
           g_variant_builder_add (builder, "s", unique_name->str);
           g_string_free (unique_name,TRUE);
-          prev_id = name->owner_id;
+          prev_id = name->id;
         }
 
        KDBUS_ITEM_FOREACH(item, name, items)
@@ -658,17 +1031,19 @@ g_kdbus_NameHasOwner_internal (GKDBusWorker       *worker,
  */
 GVariant *
 _g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
-                              const gchar      *name,
-                              GError          **error)
+                              const gchar   *name,
+                              GError       **error)
 {
   GVariant *result;
   GVariantBuilder *builder;
   GString *unique_name;
   gint ret;
 
-  struct kdbus_cmd_name_list cmd = {};
-  struct kdbus_name_list *name_list;
-  struct kdbus_name_info *kname;
+  struct kdbus_info *name_list, *kname;
+  struct kdbus_cmd_list cmd = {
+    .size = sizeof(cmd),
+    .flags = KDBUS_LIST_QUEUED
+  };
 
   if (!g_dbus_is_name (name))
     {
@@ -688,8 +1063,7 @@ _g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
       return NULL;
     }
 
-  cmd.flags = KDBUS_NAME_LIST_QUEUED;
-  ret = ioctl(worker->fd, KDBUS_CMD_NAME_LIST, &cmd);
+  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
   if (ret < 0)
     {
       g_set_error (error,
@@ -699,11 +1073,11 @@ _g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
       return NULL;
     }
 
-  name_list = (struct kdbus_name_list *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
+  name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
 
   unique_name = g_string_new (NULL);
   builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
-  KDBUS_ITEM_FOREACH(kname, name_list, names)
+  KDBUS_FOREACH(kname, name_list, cmd.list_size)
     {
       struct kdbus_item *item;
       const char *item_name = "";
@@ -715,7 +1089,7 @@ _g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
       if (strcmp(item_name, name))
         continue;
 
-      g_string_printf (unique_name, ":1.%llu", kname->owner_id);
+      g_string_printf (unique_name, ":1.%llu", kname->id);
       g_variant_builder_add (builder, "s", item_name);
     }
 
@@ -782,7 +1156,7 @@ g_kdbus_GetConnInfo_internal (GKDBusWorker  *worker,
        memcpy (cmd->items[0].str, name, len);
     }
 
-  cmd->flags = _KDBUS_ATTACH_ALL;
+  cmd->attach_flags = _KDBUS_ATTACH_ALL;
   cmd->size = size;
 
   ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
@@ -905,22 +1279,260 @@ _g_kdbus_GetConnectionUnixUser (GKDBusWorker  *worker,
 
 
 /**
- * _g_kdbus_match_remove:
+ * g_kdbus_bloom_add_data:
+ * Based on bus-bloom.c from systemd
+ * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
+ */
+static void
+g_kdbus_bloom_add_data (GKDBusWorker  *worker,
+                        guint64        bloom_data [],
+                        const void    *data,
+                        gsize          n)
+{
+  guint8 hash[8];
+  guint64 bit_num;
+  guint bytes_num = 0;
+  guint cnt_1, cnt_2;
+
+  guint c = 0;
+  guint64 p = 0;
+
+  bit_num = worker->bloom_size * 8;
+
+  if (bit_num > 1)
+    bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
+
+  for (cnt_1 = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++)
+    {
+      for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++)
+        {
+          if (c <= 0)
+            {
+              _g_siphash24(hash, data, n, hash_keys[cnt_1++]);
+              c += 8;
+            }
+
+          p = (p << 8ULL) | (guint64) hash[8 - c];
+          c--;
+        }
+
+      p &= bit_num - 1;
+      bloom_data[p >> 6] |= 1ULL << (p & 63);
+    }
+}
+
+
+/**
+ * g_kdbus_bloom_add_pair:
  *
  */
 static void
-_g_kdbus_match_remove (GKDBusWorker  *worker,
-                       guint             cookie)
+g_kdbus_bloom_add_pair (GKDBusWorker  *worker,
+                        guint64        bloom_data [],
+                        const gchar   *parameter,
+                        const gchar   *value)
 {
-  struct kdbus_cmd_match cmd_match = {};
-  gint ret;
+  gchar buf[1024];
+  gsize size;
+
+  size = strlen(parameter) + strlen(value) + 1;
+  if (size > 1024)
+    return;
+
+  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
+  g_kdbus_bloom_add_data(worker, bloom_data, buf, size);
+}
+
 
-  cmd_match.size = sizeof (cmd_match);
-  cmd_match.cookie = cookie;
+/**
+ * g_kdbus_bloom_add_prefixes:
+ *
+ */
+static void
+g_kdbus_bloom_add_prefixes (GKDBusWorker  *worker,
+                            guint64        bloom_data [],
+                            const gchar   *parameter,
+                            const gchar   *value,
+                            gchar          separator)
+{
+  gchar buf[1024];
+  gsize size;
+
+  size = strlen(parameter) + strlen(value) + 1;
+  if (size > 1024)
+    return;
+
+  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
+
+  for (;;)
+    {
+      gchar *last_sep;
+      last_sep = strrchr(buf, separator);
+      if (!last_sep || last_sep == buf)
+        break;
+
+      *last_sep = 0;
+      g_kdbus_bloom_add_data(worker, bloom_data, buf, last_sep-buf);
+    }
+}
 
-  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_REMOVE, &cmd_match);
+
+/**
+ * _g_kdbus_AddMatch:
+ *
+ */
+void
+_g_kdbus_AddMatch (GKDBusWorker  *worker,
+                   const gchar   *match_rule,
+                   guint          cookie)
+{
+  Match *match;
+  MatchElement *element;
+  const gchar *sender_name;
+  gsize sender_len, size;
+  struct kdbus_cmd_match *cmd;
+  struct kdbus_item *item;
+  guint64 *bloom;
+  guint64 src_id;
+  gchar *type;
+  gint cnt, ret;
+
+  if (match_rule[0] == '-')
+    return;
+
+  match = match_new (match_rule);
+  if (!match)
+    {
+      match_free (match);
+      return;
+    }
+
+  sender_name = NULL;
+  src_id = KDBUS_MATCH_ID_ANY;
+
+  bloom = g_alloca0 (worker->bloom_size);
+  size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_match, items));
+  match = match_new (match_rule);
+
+  for (cnt = 0; cnt < match->n_elements; cnt++)
+    {
+      element = &match->elements[cnt];
+      switch (element->type)
+        {
+          case MATCH_ELEMENT_SENDER:
+            if (g_dbus_is_unique_name(element->value))
+              {
+                src_id = g_ascii_strtoull ((element->value)+3, NULL, 10);
+                size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id));
+              }
+            else if (g_dbus_is_name (element->value))
+              {
+                sender_name = element->value;
+                sender_len = strlen(element->value) + 1;
+                size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len);
+              }
+            else
+              {
+                g_critical ("Error while adding a match: %d", cookie);
+                match_free (match);
+                return;
+              }
+            break;
+
+          case MATCH_ELEMENT_TYPE:
+            g_kdbus_bloom_add_pair (worker, bloom, "message-type", element->value);
+            break;
+
+          case MATCH_ELEMENT_INTERFACE:
+            g_kdbus_bloom_add_pair (worker, bloom, "interface", element->value);
+            break;
+
+          case MATCH_ELEMENT_MEMBER:
+            g_kdbus_bloom_add_pair (worker, bloom, "member", element->value);
+            break;
+
+          case MATCH_ELEMENT_PATH:
+            g_kdbus_bloom_add_pair (worker, bloom, "path", element->value);
+            break;
+
+          case MATCH_ELEMENT_PATH_NAMESPACE:
+            if (g_strcmp0 (element->value, "/"))
+              g_kdbus_bloom_add_pair (worker, bloom, "path-slash-prefix", element->value);
+            break;
+
+          case MATCH_ELEMENT_ARGN:
+            asprintf (&type, "arg%u", element->arg);
+            g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
+            free (type);
+            break;
+
+          case MATCH_ELEMENT_ARGNPATH:
+            asprintf (&type, "arg%u-slash-prefix", element->arg);
+            g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
+            free (type);
+            break;
+
+          case MATCH_ELEMENT_ARG0NAMESPACE:
+            g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value);
+            break;
+
+          case MATCH_ELEMENT_DESTINATION:
+          case MATCH_ELEMENT_EAVESDROP:
+            break;
+        }
+    }
+
+  size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size);
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+
+  item = cmd->items;
+  item->size = G_STRUCT_OFFSET(struct kdbus_item, data64) + worker->bloom_size;
+  item->type = KDBUS_ITEM_BLOOM_MASK;
+  memcpy(item->data64, bloom, worker->bloom_size);
+  item = KDBUS_ITEM_NEXT(item);
+
+  if (src_id != KDBUS_MATCH_ID_ANY)
+    {
+      item->size = G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id);
+      item->type = KDBUS_ITEM_ID;
+      item->id = src_id;
+      item = KDBUS_ITEM_NEXT(item);
+    }
+
+  if (sender_name)
+    {
+      item->size = G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len;
+      item->type = KDBUS_ITEM_NAME;
+      memcpy (item->str, sender_name, sender_len);
+    }
+
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
   if (ret < 0)
-    g_warning ("ERROR - %d\n", (int) errno);
+    g_critical ("Error while adding a match: %d", cookie);
+
+  match_free (match);
+}
+
+
+/**
+ * _g_kdbus_RemoveMatch:
+ *
+ */
+void
+_g_kdbus_RemoveMatch (GKDBusWorker  *worker,
+                      guint          cookie)
+{
+  struct kdbus_cmd_match cmd = {
+    .size = sizeof(cmd),
+    .cookie = cookie
+  };
+  gint ret;
+
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_REMOVE, &cmd);
+  if (ret < 0)
+    g_warning ("Error while removing a match: %d\n", (int) errno);
 }
 
 
@@ -936,7 +1548,7 @@ _g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
                                        guint             cookie)
 {
   struct kdbus_item *item;
-  struct kdbus_cmd_match *cmd_match;
+  struct kdbus_cmd_match *cmd;
   gssize size, len;
   gint ret;
   guint64 old_id = 0; /* XXX why? */
@@ -947,10 +1559,10 @@ _g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
                       G_STRUCT_OFFSET (struct kdbus_item, name_change) +
                       G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
-  cmd_match = g_alloca0 (size);
-  cmd_match->size = size;
-  cmd_match->cookie = cookie;
-  item = cmd_match->items;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
 
   if (old_name[0] == 0)
     {
@@ -976,10 +1588,10 @@ _g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
         return;
     }
 
-  cmd_match = g_alloca0 (size);
-  cmd_match->size = size;
-  cmd_match->cookie = cookie;
-  item = cmd_match->items;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
 
   /* KDBUS_ITEM_NAME_CHANGE */
   item->type = KDBUS_ITEM_NAME_CHANGE;
@@ -990,7 +1602,7 @@ _g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
                G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
   item = KDBUS_ITEM_NEXT(item);
 
-  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd_match);
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
   if (ret < 0)
     g_warning ("ERROR - %d\n", (int) errno);
 }
@@ -1005,7 +1617,7 @@ _g_kdbus_subscribe_name_acquired (GKDBusWorker  *worker,
                                   const gchar      *name)
 {
   struct kdbus_item *item;
-  struct kdbus_cmd_match *cmd_match;
+  struct kdbus_cmd_match *cmd;
   gssize size, len;
   guint64 cookie;
   gint ret;
@@ -1016,10 +1628,10 @@ _g_kdbus_subscribe_name_acquired (GKDBusWorker  *worker,
                       G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
   cookie = 0xbeefbeefbeefbeef;
-  cmd_match = g_alloca0 (size);
-  cmd_match->size = size;
-  cmd_match->cookie = cookie;
-  item = cmd_match->items;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
 
   /* KDBUS_ITEM_NAME_ADD */
   item->type = KDBUS_ITEM_NAME_ADD;
@@ -1030,7 +1642,7 @@ _g_kdbus_subscribe_name_acquired (GKDBusWorker  *worker,
                G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
   item = KDBUS_ITEM_NEXT(item);
 
-  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd_match);
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
   if (ret < 0)
     g_warning ("ERROR - %d\n", (int) errno);
 
@@ -1047,7 +1659,7 @@ _g_kdbus_subscribe_name_lost (GKDBusWorker  *worker,
                               const gchar      *name)
 {
   struct kdbus_item *item;
-  struct kdbus_cmd_match *cmd_match;
+  struct kdbus_cmd_match *cmd;
   gssize size, len;
   guint64 cookie;
   gint ret;
@@ -1058,10 +1670,10 @@ _g_kdbus_subscribe_name_lost (GKDBusWorker  *worker,
                       G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
   cookie = 0xdeafdeafdeafdeaf;
-  cmd_match = g_alloca0 (size);
-  cmd_match->size = size;
-  cmd_match->cookie = cookie;
-  item = cmd_match->items;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
 
   /* KDBUS_ITEM_NAME_REMOVE */
   item->type = KDBUS_ITEM_NAME_REMOVE;
@@ -1072,7 +1684,7 @@ _g_kdbus_subscribe_name_lost (GKDBusWorker  *worker,
                G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
   item = KDBUS_ITEM_NEXT(item);
 
-  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd_match);
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
   if (ret < 0)
     g_warning ("ERROR - %d\n", (int) errno);
 
@@ -1090,7 +1702,7 @@ _g_kdbus_unsubscribe_name_acquired (GKDBusWorker  *worker)
   guint64 cookie;
 
   cookie = 0xbeefbeefbeefbeef;
-  _g_kdbus_match_remove (worker, cookie);
+  _g_kdbus_RemoveMatch (worker, cookie);
 }
 
 
@@ -1104,7 +1716,7 @@ _g_kdbus_unsubscribe_name_lost (GKDBusWorker  *worker)
   guint64 cookie;
 
   cookie = 0xdeafdeafdeafdeaf;
-  _g_kdbus_match_remove (worker, cookie);
+  _g_kdbus_RemoveMatch (worker, cookie);
 }
 
 
@@ -1131,97 +1743,6 @@ g_kdbus_append_bloom (struct kdbus_item **item,
 
 
 /**
- * g_kdbus_bloom_add_data:
- * Based on bus-bloom.c from systemd
- * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
- */
-static void
-g_kdbus_bloom_add_data (GKDBusWorker      *worker,
-                        guint64      bloom_data [],
-                        const void  *data,
-                        gsize        n)
-{
-  guint8 hash[8];
-  guint64 bit_num;
-  guint bytes_num = 0;
-  guint cnt_1, cnt_2;
-
-  guint c = 0;
-  guint64 p = 0;
-
-  bit_num = worker->bloom_size * 8;
-
-  if (bit_num > 1)
-    bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
-
-  for (cnt_1 = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++)
-    {
-      for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++)
-        {
-          if (c <= 0)
-            {
-              g_siphash24(hash, data, n, hash_keys[cnt_1++]);
-              c += 8;
-            }
-
-          p = (p << 8ULL) | (guint64) hash[8 - c];
-          c--;
-        }
-
-      p &= bit_num - 1;
-      bloom_data[p >> 6] |= 1ULL << (p & 63);
-    }
-}
-
-
-/**
- * g_kdbus_bloom_add_pair:
- *
- */
-static void
-g_kdbus_bloom_add_pair (GKDBusWorker       *worker,
-                        guint64       bloom_data [],
-                        const gchar  *parameter,
-                        const gchar  *value)
-{
-  GString *data = g_string_new (NULL);
-
-  g_string_printf (data,"%s:%s",parameter,value);
-  g_kdbus_bloom_add_data(worker, bloom_data, data->str, data->len);
-  g_string_free (data, TRUE);
-}
-
-
-/**
- * g_kdbus_bloom_add_prefixes:
- *
- */
-static void
-g_kdbus_bloom_add_prefixes (GKDBusWorker       *worker,
-                            guint64       bloom_data [],
-                            const gchar  *parameter,
-                            const gchar  *value,
-                            gchar         separator)
-{
-  GString *data = g_string_new (NULL);
-
-  g_string_printf (data,"%s:%s",parameter,value);
-
-  for (;;)
-    {
-      gchar *last_sep;
-      last_sep = strrchr(data->str, separator);
-      if (!last_sep || last_sep == data->str)
-        break;
-
-      *last_sep = 0;
-      g_kdbus_bloom_add_data(worker, bloom_data, data->str, last_sep-(data->str));
-    }
-  g_string_free (data, TRUE);
-}
-
-
-/**
  * g_kdbus_setup_bloom:
  * Based on bus-bloom.c from systemd
  * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
@@ -1232,8 +1753,6 @@ g_kdbus_setup_bloom (GKDBusWorker                     *worker,
                      struct kdbus_bloom_filter  *bloom_filter)
 {
   GVariant *body;
-  GVariantIter iter;
-  GVariant *child;
 
   const gchar *message_type;
   const gchar *interface;
@@ -1241,7 +1760,6 @@ g_kdbus_setup_bloom (GKDBusWorker                     *worker,
   const gchar *path;
 
   void *bloom_data;
-  gint cnt = 0;
 
   body = g_dbus_message_get_body (dbus_msg);
   message_type = _g_dbus_enum_to_string (G_TYPE_DBUS_MESSAGE_TYPE, g_dbus_message_get_message_type (dbus_msg));
@@ -1270,23 +1788,30 @@ g_kdbus_setup_bloom (GKDBusWorker                     *worker,
 
   if (body != NULL)
     {
-      g_variant_iter_init (&iter, body);
-      while ((child = g_variant_iter_next_value (&iter)))
+      const GVariantType *body_type;
+      const GVariantType *arg_type;
+      guint cnt;
+
+      body_type = g_variant_get_type (body);
+
+      for (arg_type = g_variant_type_first (body_type), cnt = 0;
+           arg_type;
+           arg_type = g_variant_type_next (arg_type), cnt++)
         {
+          gchar type_char = g_variant_type_peek_string (arg_type)[0];
           gchar buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
-          gchar *child_string;
+          const gchar *str;
+          GVariant *child;
           gchar *e;
 
-          /* Is it necessary? */
-          //if (g_variant_is_container (child))
-          //  iterate_container_recursive (child);
-
-          if (!(g_variant_is_of_type (child, G_VARIANT_TYPE_STRING)) &&
-              !(g_variant_is_of_type (child, G_VARIANT_TYPE_OBJECT_PATH)) &&
-              !(g_variant_is_of_type (child, G_VARIANT_TYPE_SIGNATURE)))
-            break;
+          if (type_char != 's' && type_char != 'o')
+            /* XXX: kdbus docs say "stop after first non-string" but I
+             * think they're wrong (vs. dbus-1 compat)...
+             */
+            continue;
 
-          child_string = g_variant_dup_string (child, NULL);
+          child = g_variant_get_child_value (body, cnt);
+          str = g_variant_get_string (child, NULL);
 
           e = stpcpy(buf, "arg");
           if (cnt < 10)
@@ -1297,18 +1822,21 @@ g_kdbus_setup_bloom (GKDBusWorker                     *worker,
               *(e++) = '0' + (char) (cnt % 10);
             }
 
-          *e = 0;
-          g_kdbus_bloom_add_pair(worker, bloom_data, buf, child_string);
+          /* We add this one for both strings and object paths */
+          strcpy(e, "-slash-prefix");
+          g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '/');
 
-          strcpy(e, "-dot-prefix");
-          g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, child_string, '.');
+          /* But the others are only for strings */
+          if (type_char == 's')
+            {
+              strcpy(e, "-dot-prefix");
+              g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '.');
 
-          strcpy(e, "-slash-prefix");
-          g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, child_string, '/');
+              *e = 0;
+              g_kdbus_bloom_add_pair(worker, bloom_data, buf, str);
+            }
 
-          g_free (child_string);
           g_variant_unref (child);
-          cnt++;
         }
     }
 }
@@ -1456,7 +1984,7 @@ g_kdbus_decode_dbus_msg (GKDBusWorker           *worker,
             flavour = body_size & 7;
             g_assert ((item->vec.offset & 7) == flavour);
 
-            vector.gbytes = g_bytes_new (((guchar *) worker->kdbus_buffer) + item->vec.offset - flavour, item->vec.size + flavour);
+            vector.gbytes = g_bytes_new (((guchar *) msg) + item->vec.offset - flavour, item->vec.size + flavour);
             vector.data.pointer = g_bytes_get_data (vector.gbytes, NULL);
             vector.data.pointer += flavour;
             vector.size = item->vec.size;
@@ -1570,7 +2098,11 @@ g_kdbus_decode_dbus_msg (GKDBusWorker           *worker,
 
   g_print ("Received:\n%s\n", g_dbus_message_print (message, 2));
 
-  return message;
+  (* worker->message_received_callback) (message, worker->user_data);
+
+  g_object_unref (message);
+
+  return 0;
 }
 
 
@@ -1601,6 +2133,10 @@ again:
         if (errno == EAGAIN)
           return 0;
 
+        g_warning ("in holding pattern over %d %d\n", kdbus->fd, errno);
+        while (1)
+          sleep(1);
+
         g_set_error (error, G_IO_ERROR,
                      g_io_error_from_errno (errno),
                      _("Error while receiving message: %s"),
@@ -1608,7 +2144,9 @@ again:
         return -1;
       }
 
-   msg = (struct kdbus_msg *)((guint8 *)kdbus->kdbus_buffer + recv.reply.offset);
+    g_print ("but sometimes that's okay\n");
+
+   msg = (struct kdbus_msg *)((guint8 *)kdbus->kdbus_buffer + recv.msg.offset);
 
    if (msg->payload_type == KDBUS_PAYLOAD_DBUS)
      g_kdbus_decode_dbus_msg (kdbus, msg);
@@ -1623,7 +2161,7 @@ again:
        return -1;
      }
 
-  ioctl(kdbus->fd, KDBUS_CMD_FREE, &recv.reply.offset);
+  ioctl(kdbus->fd, KDBUS_CMD_FREE, &recv.msg.offset);
 
    return 0;
 }
@@ -2076,14 +2614,6 @@ need_compact:
 GKDBusWorker *
 g_kdbus_worker_new (const gchar  *address,
                     GError      **error)
-#if 0
-                    GDBusCapabilityFlags                     capabilities,
-                    gboolean                                 initially_frozen,
-                    GDBusWorkerMessageReceivedCallback       message_received_callback,
-                    GDBusWorkerMessageAboutToBeSentCallback  message_about_to_be_sent_callback,
-                    GDBusWorkerDisconnectedCallback          disconnected_callback,
-                    gpointer                                 user_data)
-#endif
 {
   GKDBusWorker *worker;
 
@@ -2098,8 +2628,37 @@ g_kdbus_worker_new (const gchar  *address,
 }
 
 void
+g_kdbus_worker_associate (GKDBusWorker                            *worker,
+                          GDBusCapabilityFlags                     capabilities,
+                          GDBusWorkerMessageReceivedCallback       message_received_callback,
+                          GDBusWorkerMessageAboutToBeSentCallback  message_about_to_be_sent_callback,
+                          GDBusWorkerDisconnectedCallback          disconnected_callback,
+                          gpointer                                 user_data)
+{
+  worker->capabilities = capabilities;
+  worker->message_received_callback = message_received_callback;
+  worker->message_about_to_be_sent_callback = message_about_to_be_sent_callback;
+  worker->disconnected_callback = disconnected_callback;
+  worker->user_data = user_data;
+}
+
+void
 g_kdbus_worker_unfreeze (GKDBusWorker *worker)
 {
+  gchar *name;
+
+  if (worker->source != NULL)
+    return;
+
+  worker->context = g_main_context_ref_thread_default ();
+  worker->source = g_unix_fd_source_new (worker->fd, G_IO_IN);
+
+  g_source_set_callback (worker->source, (GSourceFunc) kdbus_ready, worker, NULL);
+  name = g_strdup_printf ("kdbus worker");
+  g_source_set_name (worker->source, name);
+  g_free (name);
+
+  g_source_attach (worker->source, worker->context);
 }
 
 gboolean