[kdbus] Make SipHash function as a private utility function to kdbus
[platform/upstream/glib.git] / gio / gkdbus.c
index 480854c..10becde 100644 (file)
  */
 
 #include "config.h"
-
 #include "gkdbus.h"
 #include "glib-unix.h"
+#include "glibintl.h"
+#include "kdbus.h"
 
+#include <gio/gio.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/mman.h>
+#include <sys/ioctl.h>
 #include <stdio.h>
+#include <stdint.h>
 
 #ifdef HAVE_SYS_FILIO_H
 # include <sys/filio.h>
 #endif
 
 #include <glib/gstdio.h>
+#include <glib/glib-private.h>
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
 
 #include "glibintl.h"
 #include "gunixfdmessage.h"
-#include "gdbusprivate.h"
-#include "kdbus.h"
-
-
-/**
- * SECTION:gkdbus
- * @short_description: Low-level kdbus object
- * @include: gio/gio.h
- *
- * A #GKdbus is a lowlevel adapter for kdbus IPC solution. It is meant
- * to replace DBUS  as fundamental IPC solution for  Linux, however it
- * is  still experimental  work in  progress.  You  may  find detailed
- * description in kdbus.txt at https://github.com/gregkh/kdbus
- *
- */
-
 
-/* Size of memory registered with kdbus for receiving messages */
-#define KDBUS_POOL_SIZE (16 * 1024 * 1024)
-
-#define ALIGN8(l) (((l) + 7) & ~7)
-#define ALIGN8_PTR(p) ((void*) ALIGN8((gulong) 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) ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
+#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
 
 #define KDBUS_ITEM_NEXT(item) \
-        (typeof(item))(((guint8 *)item) + ALIGN8((item)->size))
-
-#define KDBUS_ITEM_FOREACH(item, head, first)                           \
-        for (item = (head)->first;                                      \
-             ((guint8 *)(item) < (guint8 *)(head) + (head)->size) &&    \
-             ((guint8 *)(item) >= (guint8 *)(head));                    \
+        (typeof(item))(((guint8 *)item) + KDBUS_ALIGN8((item)->size))
+#define KDBUS_ITEM_FOREACH(item, head, first)                    \
+        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))
 
-/**
- * use systemd-bus-drvierd (from systemd) which implements the all
- * org.freedesktop.DBus methods on kdbus
- */
-
-#define SYSTEMD_BUS_DRIVERD
-
-
-/* GBusNameOwnerReturnFlags */
-typedef enum
-{
-  G_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1, /* Caller is now the primary owner of the name, replacing any previous owner */
-  G_BUS_REQUEST_NAME_REPLY_IN_QUEUE = 2,      /* The name already had an owner, the application will be placed in a queue */
-  G_BUS_REQUEST_NAME_REPLY_EXISTS = 3,        /* The name already has an owner */
-  G_BUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4  /* The application trying to request ownership of a name is already the owner of it */
-} GBusNameOwnerReturnFlags;
-
-
-/* GBusReleaseNameReturnFlags */
-typedef enum
-{
-  G_BUS_RELEASE_NAME_REPLY_RELEASED = 1,     /* The caller has released his claim on the given name */
-  G_BUS_RELEASE_NAME_REPLY_NON_EXISTENT = 2, /* The given name does not exist on this bus*/
-  G_BUS_RELEASE_NAME_REPLY_NOT_OWNER = 3     /* The caller not waiting in the queue to own this name*/
-} GBusReleaseNameReturnFlags;
+struct dbus_fixed_header {
+  guint8  endian;
+  guint8  type;
+  guint8  flags;
+  guint8  version;
+  guint32 reserved;
+  guint64 serial;
+};
 
+#define DBUS_FIXED_HEADER_TYPE     ((const GVariantType *) "(yyyyut)")
+#define DBUS_EXTENDED_HEADER_TYPE  ((const GVariantType *) "a{tv}")
+#define DBUS_MESSAGE_TYPE          ((const GVariantType *) "((yyyyut)a{tv}v)")
 
-/* GBusStartServiceReturnFlags */
-typedef enum
-{
-  G_BUS_START_REPLY_SUCCESS = 1,             /* The service was successfully started */
-  G_BUS_START_REPLY_ALREADY_RUNNING = 2,     /* A connection already owns the given name */
-} GBusStartServiceReturnFlags;
+/*
+ * 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)
 
-/* GBusCredentialsFlags */
 typedef enum
 {
   G_BUS_CREDS_PID              = 1,
@@ -126,61 +127,48 @@ typedef enum
   G_BUS_CREDS_SELINUX_CONTEXT  = 4
 } GBusCredentialsFlags;
 
+typedef GObjectClass GKDBusWorkerClass;
 
-static void     g_kdbus_initable_iface_init (GInitableIface  *iface);
-static gboolean g_kdbus_initable_init       (GInitable       *initable,
-                                             GCancellable    *cancellable,
-                                             GError         **error);
+struct _GKDBusWorker
+{
+  GObject            parent_instance;
 
-#define g_kdbus_get_type _g_kdbus_get_type
-G_DEFINE_TYPE_WITH_CODE (GKdbus, g_kdbus, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
-                                                g_kdbus_initable_iface_init));
+  gint               fd;
 
+  GMainContext      *context;
+  GSource           *source;
 
-struct _GKdbusPrivate
-{
-  gint               fd;
-  gchar             *path;
   gchar             *kdbus_buffer;
-  GSList            *kdbus_msg_items;
+
+  gchar             *unique_name;
   guint64            unique_id;
-  guint64            hello_flags;
-  guint64            attach_flags;
+
+  guint64            flags;
+  guint64            attach_flags_send;
+  guint64            attach_flags_recv;
+
+  gsize              bloom_size;
+  guint              bloom_n_hash;
+
   guint              closed : 1;
   guint              inited : 1;
   guint              timeout;
   guint              timed_out : 1;
-  guchar             bus_id[16];
-  struct kdbus_msg  *kmsg;
-  GString           *msg_sender;
-  GString           *msg_destination;
 
-  gsize              bloom_size;
-  guint              bloom_n_hash;
-
-  gint              *fds;
-  gint               num_fds;
+  guchar             bus_id[16];
 
-  gint               memfd;
+  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,
+                                GCancellable  *cancellable,
+                                GError       **error);
 
-typedef struct {
-  GSource        source;
-  GPollFD        pollfd;
-  GKdbus        *kdbus;
-  GIOCondition   condition;
-  GCancellable  *cancellable;
-  GPollFD        cancel_pollfd;
-  gint64         timeout_time;
-} GKdbusSource;
-
-
-typedef gboolean (*GKdbusSourceFunc) (GKdbus *kdbus,
-                                      GIOCondition condition,
-                                      gpointer user_data);
-
+G_DEFINE_TYPE (GKDBusWorker, g_kdbus_worker, G_TYPE_OBJECT)
 
 /* Hash keys for bloom filters*/
 const guint8 hash_keys[8][16] =
@@ -195,1762 +183,1562 @@ 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,
+};
 
-/**
- * _g_kdbus_get_last_msg_sender
- *
- */
-gchar *
-_g_kdbus_get_last_msg_sender (GKdbus  *kdbus)
-{
-  return kdbus->priv->msg_sender->str;
-}
-
+/* MatchElement struct */
+typedef struct {
+  guint16 type;
+  guint16 arg;
+  char *value;
+} MatchElement;
 
-/**
- * _g_kdbus_get_last_msg_destination
- *
- */
-gchar *
-_g_kdbus_get_last_msg_destination (GKdbus  *kdbus)
-{
-  return kdbus->priv->msg_destination->str;
-}
+/* Match struct */
+typedef struct {
+  int n_elements;
+  MatchElement *elements;
+} Match;
 
 
 /**
- * _g_kdbus_get_last_msg_items:
+ * SipHash algorithm
  *
  */
-GSList *
-_g_kdbus_get_last_msg_items (GKdbus  *kdbus)
-{
-  return kdbus->priv->kdbus_msg_items;
-}
+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;
+    }
 
-/**
- * g_kdbus_add_msg_part:
- *
- */
-static void
-g_kdbus_add_msg_part (GKdbus  *kdbus,
-                      gchar   *data,
-                      gsize    size)
-{
-  msg_part* part = g_new (msg_part, 1);
-  part->data = data;
-  part->size = size;
-  kdbus->priv->kdbus_msg_items = g_slist_append(kdbus->priv->kdbus_msg_items, part);
+  v3 ^= b;
+  SIPROUND;
+  SIPROUND;
+  v0 ^= b;
+
+  v2 ^= 0xff;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  SIPROUND;
+  b = v0 ^ v1 ^ v2  ^ v3;
+  U64TO8_LE (out, b);
 }
 
 
 /**
- * _g_kdbus_hexdump_all_items:
+ * is_key()
  *
  */
-gchar *
-_g_kdbus_hexdump_all_items (GSList  *kdbus_msg_items)
+static gboolean
+is_key (const char *key_start, const char *key_end, char *value)
 {
+  gsize len = strlen (value);
 
-  GString *ret;
-  gint item = 1;
-  ret = g_string_new (NULL);
-
-  while (kdbus_msg_items != NULL)
-    {
-      g_string_append_printf (ret, "\n  Item %d\n", item);
-      g_string_append (ret, _g_dbus_hexdump (((msg_part*)kdbus_msg_items->data)->data, ((msg_part*)kdbus_msg_items->data)->size, 2));
-
-      kdbus_msg_items = g_slist_next(kdbus_msg_items);
-      item++;
-    }
+  if (len != key_end - key_start)
+    return FALSE;
 
-  return g_string_free (ret, FALSE);
+  return strncmp (key_start, value, len) == 0;
 }
 
 
 /**
- * g_kdbus_finalize:
+ * parse_key()
  *
  */
-static void
-g_kdbus_finalize (GObject  *object)
+static gboolean
+parse_key (MatchElement *element, const char *key_start, const char *key_end)
 {
-  GKdbus *kdbus = G_KDBUS (object);
-
-  if (kdbus->priv->kdbus_buffer != NULL)
-    munmap (kdbus->priv->kdbus_buffer, KDBUS_POOL_SIZE);
+  gboolean res = TRUE;
 
-  kdbus->priv->kdbus_buffer = NULL;
+  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;
 
-  if (kdbus->priv->fd != -1 && !kdbus->priv->closed)
-    _g_kdbus_close (kdbus, NULL);
+      while (end_digits < key_end && g_ascii_isdigit (*end_digits))
+        end_digits++;
 
-  g_string_free (kdbus->priv->msg_sender, TRUE);
-  g_string_free (kdbus->priv->msg_destination, TRUE);
+      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;
 
-  if (G_OBJECT_CLASS (g_kdbus_parent_class)->finalize)
-    (*G_OBJECT_CLASS (g_kdbus_parent_class)->finalize) (object);
+  return res;
 }
 
 
 /**
- * g_kdbus_class_init:
+ * parse_value()
  *
  */
-static void
-g_kdbus_class_init (GKdbusClass  *klass)
+static const char *
+parse_value (MatchElement *element, const char *s)
 {
-  GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);
-
-  g_type_class_add_private (klass, sizeof (GKdbusPrivate));
-  gobject_class->finalize = g_kdbus_finalize;
-}
+  char quote_char;
+  GString *value;
 
+  value = g_string_new ("");
 
-/**
- * g_kdbus_initable_iface_init:
- *
- */
-static void
-g_kdbus_initable_iface_init (GInitableIface  *iface)
-{
-  iface->init = g_kdbus_initable_init;
-}
+  quote_char = 0;
 
+  for (;*s; s++)
+    {
+      if (quote_char == 0)
+        {
+          switch (*s)
+            {
+            case '\'':
+              quote_char = '\'';
+              break;
 
-/**
- * g_kdbus_init:
- *
- */
-static void
-g_kdbus_init (GKdbus  *kdbus)
-{
-  kdbus->priv = G_TYPE_INSTANCE_GET_PRIVATE (kdbus, G_TYPE_KDBUS, GKdbusPrivate);
+            case ',':
+              s++;
+              goto out;
 
-  kdbus->priv->fd = -1;
-  kdbus->priv->unique_id = -1;
-  kdbus->priv->memfd = -1;
+            case '\\':
+              quote_char = '\\';
+              break;
 
-  kdbus->priv->path = NULL;
-  kdbus->priv->kdbus_buffer = NULL;
-  kdbus->priv->kdbus_msg_items = NULL;
+            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, '\\');
 
-  kdbus->priv->msg_sender = g_string_new (NULL);
-  kdbus->priv->msg_destination = g_string_new (NULL);
+          g_string_append_c (value, *s);
+          quote_char = 0;
+        }
+      else /* quote_char == ' */
+        {
+          if (*s == '\'')
+            quote_char = 0;
+          else
+            g_string_append_c (value, *s);
+        }
+    }
 
-  kdbus->priv->fds = NULL;
-  kdbus->priv->num_fds = 0;
+ out:
+  if (quote_char == '\\')
+    g_string_append_c (value, '\\');
+  else if (quote_char == '\'')
+    {
+      g_string_free (value, TRUE);
+      return NULL;
+    }
 
-  kdbus->priv->hello_flags = KDBUS_HELLO_ACCEPT_FD;
-  kdbus->priv->attach_flags = KDBUS_ATTACH_NAMES;
+  element->value = g_string_free (value, FALSE);
+  return s;
 }
 
 
 /**
- * g_kdbus_initable_init:
+ * match_new()
  *
  */
-static gboolean
-g_kdbus_initable_init (GInitable     *initable,
-                       GCancellable  *cancellable,
-                       GError       **error)
+static Match *
+match_new (const char *str)
 {
-  GKdbus *kdbus;
+  Match *match;
+  GArray *elements;
+  const char *p;
+  const char *key_start;
+  const char *key_end;
+  MatchElement element;
+  int i;
 
-  g_return_val_if_fail (G_IS_KDBUS (initable), FALSE);
+  elements = g_array_new (TRUE, TRUE, sizeof (MatchElement));
 
-  kdbus = G_KDBUS (initable);
+  p = str;
 
-  if (cancellable != NULL)
+  while (*p != 0)
     {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                           _("Cancellable initialization not supported"));
-      return FALSE;
-    }
+      memset (&element, 0, sizeof (element));
 
-  kdbus->priv->inited = TRUE;
+      /* Skip initial whitespace */
+      while (*p && g_ascii_isspace (*p))
+        p++;
 
-  return TRUE;
-}
+      key_start = p;
 
+      /* Read non-whitespace non-equals chars */
+      while (*p && *p != '=' && !g_ascii_isspace (*p))
+        p++;
 
-/**
- * kdbus_source_prepare:
- *
- */
-static gboolean
-kdbus_source_prepare (GSource  *source,
-                      gint     *timeout)
-{
-  GKdbusSource *kdbus_source = (GKdbusSource *)source;
+      key_end = p;
 
-  if (g_cancellable_is_cancelled (kdbus_source->cancellable))
-    return TRUE;
+      /* Skip any whitespace after key */
+      while (*p && g_ascii_isspace (*p))
+        p++;
 
-  if (kdbus_source->timeout_time)
-    {
-      gint64 now;
+      if (key_start == key_end)
+        continue; /* Allow trailing whitespace */
+      if (*p != '=')
+        goto error;
 
-      now = g_source_get_time (source);
+      ++p;
 
-      *timeout = (kdbus_source->timeout_time - now + 999) / 1000;
-      if (*timeout < 0)
-        {
-          kdbus_source->kdbus->priv->timed_out = TRUE;
-          *timeout = 0;
-          return TRUE;
-        }
+      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);
     }
-  else
-    *timeout = -1;
 
-  if ((kdbus_source->condition & kdbus_source->pollfd.revents) != 0)
-    return TRUE;
+  match = g_new0 (Match, 1);
+  match->n_elements = elements->len;
+  match->elements = (MatchElement *)g_array_free (elements, FALSE);
 
-  return 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;
 }
 
 
 /**
- * kdbus_source_check:
+ * match_free()
  *
  */
-static gboolean
-kdbus_source_check (GSource  *source)
+static void
+match_free (Match *match)
 {
-  gint timeout;
-
-  return kdbus_source_prepare (source, &timeout);
+  int i;
+  for (i = 0; i < match->n_elements; i++)
+    g_free (match->elements[i].value);
+  g_free (match->elements);
+  g_free (match);
 }
 
 
 /**
- * kdbus_source_dispatch
+ * g_kdbus_finalize:
  *
  */
-static gboolean
-kdbus_source_dispatch  (GSource      *source,
-                        GSourceFunc   callback,
-                        gpointer      user_data)
+static void
+g_kdbus_worker_finalize (GObject *object)
 {
-  GKdbusSourceFunc func = (GKdbusSourceFunc)callback;
-  GKdbusSource *kdbus_source = (GKdbusSource *)source;
-  GKdbus *kdbus = kdbus_source->kdbus;
-  gboolean ret;
-
-  if (kdbus_source->kdbus->priv->timed_out)
-    kdbus_source->pollfd.revents |= kdbus_source->condition & (G_IO_IN | G_IO_OUT);
+  GKDBusWorker *kdbus = G_KDBUS_WORKER (object);
 
-  ret = (*func) (kdbus,
-                 kdbus_source->pollfd.revents & kdbus_source->condition,
-                 user_data);
+  if (kdbus->kdbus_buffer != NULL)
+    munmap (kdbus->kdbus_buffer, KDBUS_POOL_SIZE);
 
-  if (kdbus->priv->timeout)
-    kdbus_source->timeout_time = g_get_monotonic_time ()
-                               + kdbus->priv->timeout * 1000000;
+  kdbus->kdbus_buffer = NULL;
 
-  else
-    kdbus_source->timeout_time = 0;
+  if (kdbus->fd != -1 && !kdbus->closed)
+    _g_kdbus_close (kdbus);
 
-  return ret;
+  G_OBJECT_CLASS (g_kdbus_worker_parent_class)->finalize (object);
 }
 
+static void
+g_kdbus_worker_class_init (GKDBusWorkerClass *class)
+{
+  class->finalize = g_kdbus_worker_finalize;
+}
 
-/**
- * kdbus_source_finalize
- *
- */
 static void
-kdbus_source_finalize (GSource  *source)
+g_kdbus_worker_init (GKDBusWorker *kdbus)
 {
-  GKdbusSource *kdbus_source = (GKdbusSource *)source;
-  GKdbus *kdbus;
+  kdbus->fd = -1;
 
-  kdbus = kdbus_source->kdbus;
+  kdbus->unique_id = -1;
+  kdbus->unique_name = NULL;
 
-  g_object_unref (kdbus);
+  kdbus->kdbus_buffer = NULL;
 
-  if (kdbus_source->cancellable)
-    {
-      g_cancellable_release_fd (kdbus_source->cancellable);
-      g_object_unref (kdbus_source->cancellable);
-    }
+  kdbus->flags = 0; /* KDBUS_HELLO_ACCEPT_FD */
+  kdbus->attach_flags_send = _KDBUS_ATTACH_ALL;
+  kdbus->attach_flags_recv = _KDBUS_ATTACH_ALL;
 }
 
-
-/**
- * kdbus_source_closure_callback:
- *
- */
 static gboolean
-kdbus_source_closure_callback (GKdbus        *kdbus,
-                               GIOCondition   condition,
-                               gpointer       data)
+kdbus_ready (gint         fd,
+             GIOCondition condition,
+             gpointer     user_data)
 {
-  GClosure *closure = data;
-  GValue params[2] = { G_VALUE_INIT, G_VALUE_INIT };
-  GValue result_value = G_VALUE_INIT;
-  gboolean result;
-
-  g_value_init (&result_value, G_TYPE_BOOLEAN);
-
-  g_value_init (&params[0], G_TYPE_KDBUS);
-  g_value_set_object (&params[0], kdbus);
-  g_value_init (&params[1], G_TYPE_IO_CONDITION);
-  g_value_set_flags (&params[1], condition);
-
-  g_closure_invoke (closure, &result_value, 2, params, NULL);
+  GKDBusWorker *kdbus = user_data;
+  GError *error = NULL;
 
-  result = g_value_get_boolean (&result_value);
-  g_value_unset (&result_value);
-  g_value_unset (&params[0]);
-  g_value_unset (&params[1]);
+  _g_kdbus_receive (kdbus, NULL, &error);
+  g_assert_no_error (error);
 
-  return result;
+  return G_SOURCE_CONTINUE;
 }
 
-
-static GSourceFuncs kdbus_source_funcs =
-{
-  kdbus_source_prepare,
-  kdbus_source_check,
-  kdbus_source_dispatch,
-  kdbus_source_finalize,
-  (GSourceFunc)kdbus_source_closure_callback,
-};
-
-
-/**
- * kdbus_source_new:
- *
- */
-static GSource *
-kdbus_source_new (GKdbus        *kdbus,
-                  GIOCondition   condition,
-                  GCancellable  *cancellable)
+gboolean
+_g_kdbus_open (GKDBusWorker  *worker,
+               const gchar   *address,
+               GError       **error)
 {
-  GSource *source;
-  GKdbusSource *kdbus_source;
-
-  source = g_source_new (&kdbus_source_funcs, sizeof (GKdbusSource));
-  g_source_set_name (source, "GKdbus");
-  kdbus_source = (GKdbusSource *)source;
-
-  kdbus_source->kdbus = g_object_ref (kdbus);
-  kdbus_source->condition = condition;
+  g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
 
-  if (g_cancellable_make_pollfd (cancellable,
-                                 &kdbus_source->cancel_pollfd))
+  worker->fd = open(address, O_RDWR|O_NOCTTY|O_CLOEXEC);
+  if (worker->fd<0)
     {
-      kdbus_source->cancellable = g_object_ref (cancellable);
-      g_source_add_poll (source, &kdbus_source->cancel_pollfd);
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't open kdbus endpoint"));
+      return FALSE;
     }
 
-  kdbus_source->pollfd.fd = kdbus->priv->fd;
-  kdbus_source->pollfd.events = condition;
-  kdbus_source->pollfd.revents = 0;
-  g_source_add_poll (source, &kdbus_source->pollfd);
-
-  if (kdbus->priv->timeout)
-    kdbus_source->timeout_time = g_get_monotonic_time ()
-                               + kdbus->priv->timeout * 1000000;
-  else
-    kdbus_source->timeout_time = 0;
+  worker->closed = FALSE;
 
-  return source;
+  return TRUE;
 }
 
 
 /**
- * _g_kdbus_create_source:
+ * g_kdbus_free_data:
  *
  */
-GSource *
-_g_kdbus_create_source (GKdbus        *kdbus,
-                        GIOCondition   condition,
-                        GCancellable  *cancellable)
+static gboolean
+g_kdbus_free_data (GKDBusWorker      *kdbus,
+                   guint64      offset)
 {
-  g_return_val_if_fail (G_IS_KDBUS (kdbus) && (cancellable == NULL || G_IS_CANCELLABLE (cancellable)), NULL);
+  struct kdbus_cmd_free cmd = {
+    .size = sizeof(cmd),
+    .offset = offset,
+    .flags = 0
+  };
+  int ret;
+
+  ret = ioctl (kdbus->fd, KDBUS_CMD_FREE, &cmd);
+  if (ret < 0)
+      return FALSE;
 
-  return kdbus_source_new (kdbus, condition, cancellable);
+  return TRUE;
 }
 
 
 /**
- * _g_kdbus_open:
- * @kdbus: a #GKdbus.
- * @address: path to kdbus bus file.
- * @error: #GError for error reporting, or %NULL to ignore.
+ * g_kdbus_translate_nameowner_flags:
  *
- * Opens file descriptor to kdbus bus control.
- * It is located in /dev/kdbus/uid-name/bus.
- *
- * Returns: TRUE on success.
  */
-gboolean
-_g_kdbus_open (GKdbus       *kdbus,
-               const gchar  *address,
-               GError      **error)
+static void
+g_kdbus_translate_nameowner_flags (GBusNameOwnerFlags   flags,
+                                   guint64             *kdbus_flags)
 {
-  g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE);
+  guint64 new_flags;
 
-  kdbus->priv->fd = open(address, O_RDWR|O_NOCTTY|O_CLOEXEC);
+  new_flags = 0;
 
-  if (kdbus->priv->fd<0)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't open kdbus endpoint"));
-      return FALSE;
-    }
+  if (flags & G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT)
+    new_flags |= KDBUS_NAME_ALLOW_REPLACEMENT;
+
+  if (flags & G_BUS_NAME_OWNER_FLAGS_REPLACE)
+    new_flags |= KDBUS_NAME_REPLACE_EXISTING;
 
-  kdbus->priv->closed = FALSE;
+  if (!(flags & G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE))
+    new_flags |= KDBUS_NAME_QUEUE;
 
-  return TRUE;
+  *kdbus_flags = new_flags;
 }
 
 
 /**
  * _g_kdbus_close:
- * @kdbus: a #GKdbus.
- * @error: #GError for error reporting, or %NULL to ignore.
- *
- * Closes file descriptor to kdbus bus.
- * Disconnect a connection. If the connection's message list is empty,
- * the calls succeeds, closes file descriptor to kdbus bus. Otherwise
- * FALSE is returned without any further side-effects.
- *
- * Returns: TRUE on success.
  *
  */
-gboolean
-_g_kdbus_close (GKdbus  *kdbus,
-                GError **error)
+void
+_g_kdbus_close (GKDBusWorker *kdbus)
 {
-  gint res;
+  g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), FALSE);
 
-  if (kdbus->priv->closed)
-    return TRUE; /* Multiple close not an error */
+  if (kdbus->closed)
+    return;
 
-  g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE);
+  g_source_destroy (kdbus->source);
+  kdbus->source = 0;
 
-  if (ioctl(kdbus->priv->fd, KDBUS_CMD_BYEBYE) < 0)
-    return FALSE;
-
-  while (1)
-    {
-      res = close (kdbus->priv->fd);
-
-      if (res == -1)
-        {
-          if (errno == EINTR)
-            continue;
-
-          g_set_error (error, G_IO_ERROR,
-                       g_io_error_from_errno (errno),
-                       _("Error closing kdbus fd: %s"),
-                       g_strerror (errno));
-          return FALSE;
-        }
-      break;
-    }
+  g_main_context_unref (kdbus->context);
+  kdbus->context = NULL;
 
-  kdbus->priv->closed = TRUE;
-  kdbus->priv->fd = -1;
+  close (kdbus->fd);
+  kdbus->fd = -1;
 
-  return TRUE;
+  kdbus->closed = TRUE;
 }
 
-
 /**
  * _g_kdbus_is_closed:
- * @kdbus: a #GKdbus.
- *
- * checks whether a kdbus is closed.
  *
  */
 gboolean
-_g_kdbus_is_closed (GKdbus  *kdbus)
+_g_kdbus_is_closed (GKDBusWorker  *kdbus)
 {
-  g_return_val_if_fail (G_IS_KDBUS (kdbus), FALSE);
+  g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), FALSE);
 
-  return kdbus->priv->closed;
+  return kdbus->closed;
 }
 
 
 /**
- * g_kdbus_generate_local_reply:
+ * _g_kdbus_Hello:
  *
  */
-static GDBusMessage *
-g_kdbus_generate_local_reply (GDBusMessage       *message,
-                              GDBusMessageType    message_type,
-                              GDBusMessageFlags   message_flags,
-                              guint32             message_reply_serial,
-                              GVariant           *message_body,
-                              const gchar        *error_name)
+GVariant *
+_g_kdbus_Hello (GKDBusWorker *worker,
+                GError    **error)
 {
-  GDBusMessage *reply;
+  struct kdbus_cmd_hello *cmd;
+  struct kdbus_item *item;
+
+  gchar *conn_name;
+  size_t size, conn_name_size;
 
-  reply = g_dbus_message_new ();
+  conn_name = "gdbus-kdbus";
+  conn_name_size = strlen (conn_name);
 
-  g_dbus_message_set_sender (reply, "org.freedesktop.DBus");
-  g_dbus_message_set_message_type (reply, message_type);
-  g_dbus_message_set_flags (reply, message_flags);
-  g_dbus_message_set_reply_serial (reply, message_reply_serial);
+  size = KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_cmd_hello, items)) +
+         KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, str) + conn_name_size + 1);
+
+  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;
 
-  g_dbus_message_set_body (reply, message_body);
+  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 (message != NULL)
-    g_dbus_message_set_destination (reply, g_dbus_message_get_sender (message));
+  if (ioctl(worker->fd, KDBUS_CMD_HELLO, cmd))
+    {
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_errno (errno),
+                   _("Failed to send HELLO: %s"),
+                   g_strerror (errno));
+      return NULL;
+    }
 
-  if (message_type == G_DBUS_MESSAGE_TYPE_ERROR)
-    g_dbus_message_set_error_name (reply, error_name);
+  worker->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, worker->fd, 0);
+  if (worker->kdbus_buffer == MAP_FAILED)
+    {
+      g_set_error (error, G_IO_ERROR,
+                   g_io_error_from_errno (errno),
+                   _("mmap error: %s"),
+                   g_strerror (errno));
+      return NULL;
+    }
 
-  if (G_UNLIKELY (_g_dbus_debug_message ()))
+  if (cmd->bus_flags > 0xFFFFFFFFULL)
     {
-      gchar *s;
-      _g_dbus_debug_print_lock ();
-      g_print ("========================================================================\n"
-               "GDBus-debug:Message:\n"
-               "  <<<< RECEIVED LOCAL D-Bus message (N/A bytes)\n");
-
-      s = g_dbus_message_print (reply, 2);
-      g_print ("%s", s);
-      g_free (s);
-      _g_dbus_debug_print_unlock ();
+      g_set_error_literal (error,
+                           G_IO_ERROR,
+                           G_IO_ERROR_FAILED,
+                           _("Incompatible HELLO flags"));
+      return NULL;
     }
 
-  return reply;
+  memcpy (worker->bus_id, cmd->id128, 16);
+
+  worker->unique_id = cmd->id;
+  asprintf(&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id);
+
+  /* read bloom filters parameters */
+  //worker->bloom_size = (gsize) cmd->bloom.size;
+  //worker->bloom_n_hash = (guint) cmd->bloom.n_hash;
+
+  return g_variant_new ("(s)", worker->unique_name);
 }
 
 
 /**
- * g_kdbus_generate_local_error:
+ * _g_kdbus_RequestName:
  *
  */
-static void
-g_kdbus_generate_local_error (GDBusWorker   *worker,
-                              GDBusMessage  *dbus_msg,
-                              GVariant      *message_body,
-                              gint           error_code)
-{
-  GDBusMessage *reply;
-  GError *error = NULL;
-  gchar *dbus_error_name;
-
-  error = g_error_new_literal (G_DBUS_ERROR, error_code, "");
-  dbus_error_name = g_dbus_error_encode_gerror (error);
-
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_ERROR,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        message_body,
-                                        dbus_error_name);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-}
-
+GVariant *
+_g_kdbus_RequestName (GKDBusWorker        *worker,
+                      const gchar         *name,
+                      GBusNameOwnerFlags   flags,
+                      GError             **error)
+{
+  GVariant *result;
+  struct kdbus_cmd *cmd;
+  guint64 kdbus_flags;
+  gssize len, size;
+  gint status, ret;
 
-/**
- * g_kdbus_check_signature:
- * Returns: TRUE on success.
- */
-static gboolean
-g_kdbus_check_signature (GDBusWorker         *worker,
-                         GDBusMessage        *dbus_msg,
-                         const gchar         *method_name,
-                         GVariant            *body,
-                         const GVariantType  *type)
-{
+  status = G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER;
 
-  if (!g_variant_is_of_type (body, type))
+  if (!g_dbus_is_name (name))
     {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Call to %s has wrong args (expected %s)", method_name, g_variant_type_peek_string (type));
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_INVALID_ARGS);
-      g_string_free (error_name,TRUE);
-      return FALSE;
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Given bus name \"%s\" is not valid", name);
+      return NULL;
     }
-  else
-    return TRUE;
-}
-
 
-/**
- * g_kdbus_check_name:
- * Returns: TRUE on success.
- */
-static gboolean
-g_kdbus_check_name (GDBusWorker   *worker,
-                    GDBusMessage  *dbus_msg,
-                    const gchar   *name)
-{
-  if (!g_dbus_is_name (name))
+  if (*name == ':')
     {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Name \"%s\" is not valid", name);
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_INVALID_ARGS);
-      g_string_free (error_name,TRUE);
-      return FALSE;
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Cannot acquire a service starting with ':' such as \"%s\"", name);
+      return NULL;
     }
-  else
-    return TRUE;
-}
 
+  g_kdbus_translate_nameowner_flags (flags, &kdbus_flags);
 
-/**
- * g_kdbus_translate_request_name_flags:
- *
- */
-static void
-g_kdbus_translate_request_name_flags (GBusNameOwnerFlags   flags,
-                                      guint64             *kdbus_flags)
-{
-  guint64 new_flags = 0;
+  len = strlen(name) + 1;
+  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);
 
-  if (flags & G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT)
-    new_flags |= KDBUS_NAME_ALLOW_REPLACEMENT;
+  ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, cmd);
+  if (ret < 0)
+    {
+      if (errno == EEXIST)
+        status = G_BUS_REQUEST_NAME_FLAGS_EXISTS;
+      else if (errno == EALREADY)
+        status = G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER;
+      else
+        {
+          g_set_error (error, G_IO_ERROR,
+                       g_io_error_from_errno (errno),
+                       _("Error while acquiring name: %s"),
+                       g_strerror (errno));
+          return NULL;
+        }
+    }
 
-  if (flags & G_BUS_NAME_OWNER_FLAGS_REPLACE)
-    new_flags |= KDBUS_NAME_REPLACE_EXISTING;
+  if (cmd->flags & KDBUS_NAME_IN_QUEUE)
+    status = G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE;
 
-  *kdbus_flags = new_flags;
+  result = g_variant_new ("(u)", status);
+
+  return result;
 }
 
 
 /**
- * g_kdbus_NameHasOwner:
- * Returns: TRUE on success.
+ * _g_kdbus_ReleaseName:
+ *
  */
-static gboolean
-g_kdbus_NameHasOwner (GKdbus *kdbus, const gchar *name)
+GVariant *
+_g_kdbus_ReleaseName (GKDBusWorker     *worker,
+                      const gchar         *name,
+                      GError             **error)
 {
-  struct kdbus_cmd_conn_info *cmd;
-  gssize size;
-  gint ret;
+  GVariant *result;
+  struct kdbus_cmd *cmd;
+  gssize len, size;
+  gint status, ret;
 
-  if (g_dbus_is_unique_name(name))
+  status = G_BUS_RELEASE_NAME_FLAGS_RELEASED;
+
+  if (!g_dbus_is_name (name))
     {
-       size = G_STRUCT_OFFSET (struct kdbus_cmd_conn_info, name);
-       cmd = g_alloca0 (size);
-       cmd->id = g_ascii_strtoull (name+3,NULL,10);
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Given bus name \"%s\" is not valid", name);
+      return NULL;
     }
-  else
+
+  if (*name == ':')
     {
-       size = G_STRUCT_OFFSET (struct kdbus_cmd_conn_info, name) + strlen(name) + 1;
-       cmd = g_alloca0 (size);
-       strcpy(cmd->name, name);
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Cannot release a service starting with ':' such as \"%s\"", name);
+      return NULL;
     }
 
-  cmd->flags = KDBUS_ATTACH_NAMES;
+  len = strlen(name) + 1;
+  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(kdbus->priv->fd, KDBUS_CMD_CONN_INFO, cmd);
+  ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, cmd);
+  if (ret < 0)
+    {
+      if (errno == ESRCH)
+        status = G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT;
+      else if (errno == EADDRINUSE)
+        status = G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER;
+      else
+        {
+          g_set_error (error, G_IO_ERROR,
+                       g_io_error_from_errno (errno),
+                       _("Error while releasing name: %s"),
+                       g_strerror (errno));
+          return NULL;
+        }
+    }
 
-  if (ret<0)
-    return FALSE;
-  else
-    return TRUE;
+  result = g_variant_new ("(u)", status);
+
+  return result;
 }
 
 
 /**
- * g_kdbus_take_fd:
+ * _g_kdbus_GetBusId:
  *
  */
-static void
-g_kdbus_take_fd (GKdbus  *kdbus)
+GVariant *
+_g_kdbus_GetBusId (GKDBusWorker  *worker,
+                   GError          **error)
 {
-  struct kdbus_cmd_hello *hello;
-  struct kdbus_item *item;
-  gchar *conn_name;
-  size_t size, conn_name_size;
-
-  conn_name = "gdbus-kdbus";
-  conn_name_size = strlen (conn_name);
-
-  size = ALIGN8(G_STRUCT_OFFSET(struct kdbus_cmd_hello, items)) +
-         ALIGN8(G_STRUCT_OFFSET(struct kdbus_item, str) + conn_name_size + 1);
+  GVariant *result;
+  GString  *result_str;
+  guint     cnt;
 
-  hello = g_alloca0(size);
-  hello->conn_flags = kdbus->priv->hello_flags;
-  hello->attach_flags =  kdbus->priv->attach_flags;
-  hello->size = size;
-  hello->pool_size = KDBUS_POOL_SIZE;
+  result_str = g_string_new (NULL);
 
-  /* connection's human-readable name (only for debugging purposes)*/
-  item = hello->items;
-  item->size = G_STRUCT_OFFSET(struct kdbus_item, str) + conn_name_size + 1;
-  item->type = KDBUS_ITEM_CONN_NAME;
-  memcpy(item->str, conn_name, conn_name_size+1);
-  item = KDBUS_ITEM_NEXT(item);
-
-  if (ioctl(kdbus->priv->fd, KDBUS_CMD_HELLO, hello))
-    g_error("[KDBUS] fd=%d failed to send hello: %m, %d", kdbus->priv->fd,errno);
-
-  kdbus->priv->kdbus_buffer = mmap(NULL, KDBUS_POOL_SIZE, PROT_READ, MAP_SHARED, kdbus->priv->fd, 0);
-
-  if (kdbus->priv->kdbus_buffer == MAP_FAILED)
-    g_error("[KDBUS] error when mmap: %m, %d", errno);
-
-  if (hello->bus_flags > 0xFFFFFFFFULL || hello->conn_flags > 0xFFFFFFFFULL)
-    g_error("[KDBUS] incompatible flags");
-
-  /* read bloom filters parameters */
-  kdbus->priv->bloom_size = (gsize) hello->bloom.size;
-  kdbus->priv->bloom_n_hash = (guint) hello->bloom.n_hash;
+  for (cnt=0; cnt<16; cnt++)
+    g_string_append_printf (result_str, "%02x", worker->bus_id[cnt]);
 
-  /* validate bloom filters parameters here? */
+  result = g_variant_new ("(s)", result_str->str);
+  g_string_free (result_str, TRUE);
 
-  kdbus->priv->unique_id = hello->id;
-  memcpy (kdbus->priv->bus_id, hello->id128, 16);
+  return result;
 }
 
 
 /**
- * g_kdbus_Hello_reply:
- * Returns: TRUE on success.
+ * _g_kdbus_GetListNames:
+ *
  */
-static gboolean
-g_kdbus_Hello_reply (GDBusWorker   *worker,
-                     GKdbus        *kdbus,
-                     GDBusMessage  *dbus_msg)
+GVariant *
+_g_kdbus_GetListNames (GKDBusWorker  *worker,
+                       guint             list_name_type,
+                       GError          **error)
 {
-  GString *unique_name;
-  GDBusMessage *reply;
-
-  unique_name = g_string_new(NULL);
-  g_string_printf (unique_name,":1.%" G_GUINT64_FORMAT, kdbus->priv->unique_id);
-
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(s)",unique_name->str),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-
-  g_string_free (unique_name,TRUE);
-  return TRUE;
-}
-
+  GVariant *result;
+  GVariantBuilder *builder;
+  struct kdbus_info *name_list, *name;
+  struct kdbus_cmd_list cmd = {
+    .size = sizeof(cmd)
+  };
 
-/**
- * g_kdbus_RequestName_handler:
- * Returns: TRUE on success.
- */
-static gboolean
-g_kdbus_RequestName_handler (GDBusWorker   *worker,
-                             GKdbus        *kdbus,
-                             GDBusMessage  *dbus_msg)
-{
-  GDBusMessage *reply;
-  GBusNameOwnerFlags flags;
-  struct kdbus_cmd_name *kdbus_name;
-  const gchar *name;
-  guint64 kdbus_flags;
-  guint64 size;
+  guint64 prev_id;
   gint ret;
-  gint status = G_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
-
-  /* read and validate message */
-  GVariant *body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "RequestName", body, G_VARIANT_TYPE("(su)")))
-    return TRUE;
-
-  g_variant_get (body, "(&su)", &name, &flags);
 
-  if (!g_kdbus_check_name (worker, dbus_msg, name))
-    return TRUE;
+  prev_id = 0;
 
-  if (*name == ':')
-    {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Cannot acquire a service starting with ':' such as \"%s\"", name);
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_INVALID_ARGS);
-      g_string_free (error_name,TRUE);
-      return TRUE;
-    }
-
-  g_kdbus_translate_request_name_flags (flags, &kdbus_flags);
-
-  /* calculate size */
-  size = sizeof(*kdbus_name) + strlen(name) + 1;
-  kdbus_name = g_alloca(size);
-
-  /* set message header */
-  memset(kdbus_name, 0, size);
-  strcpy(kdbus_name->name, name);
-  kdbus_name->size = size;
-  kdbus_name->flags = kdbus_flags;
+  if (list_name_type)
+    cmd.flags = KDBUS_LIST_ACTIVATORS;                /* ListActivatableNames */
+  else
+    cmd.flags = KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES; /* ListNames */
 
-  /* send message */
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_ACQUIRE, kdbus_name);
+  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
   if (ret < 0)
     {
-      if (errno == EEXIST)
-        status = G_BUS_REQUEST_NAME_REPLY_EXISTS;
-      else if (errno == EALREADY)
-        status = G_BUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
-      else
-        return FALSE;
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Error listing names"));
+      return NULL;
     }
 
-  if (kdbus_name->flags & KDBUS_NAME_IN_QUEUE)
-    status = G_BUS_REQUEST_NAME_REPLY_IN_QUEUE;
-
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(u)",status),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-
-  return TRUE;
-}
-
-
-/**
- * g_kdbus_ReleaseName_handler:
- * Returns: TRUE on success.
- */
-static gboolean
-g_kdbus_ReleaseName_handler (GDBusWorker   *worker,
-                             GKdbus        *kdbus,
-                             GDBusMessage  *dbus_msg)
-{
-  GDBusMessage *reply;
-  struct kdbus_cmd_name *kdbus_name;
-  const gchar *name;
-  guint64 size;
-  gint ret;
-  gint status = G_BUS_RELEASE_NAME_REPLY_RELEASED;
-
-  /* read and validate message */
-  GVariant *body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "ReleaseName", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &name);
-
-  if (!g_kdbus_check_name (worker, dbus_msg, name))
-    return TRUE;
+  name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
+  builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
 
-  if (*name == ':')
+  KDBUS_FOREACH(name, name_list, cmd.list_size)
     {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Cannot release a service starting with ':' such as \"%s\"", name);
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_INVALID_ARGS);
-      g_string_free (error_name,TRUE);
-      return TRUE;
-    }
+      struct kdbus_item *item;
+      const gchar *item_name = "";
 
-  /* calculate size */
-  size = sizeof(*kdbus_name) + strlen(name) + 1;
-  kdbus_name = g_alloca(size);
+      if ((cmd.flags & KDBUS_LIST_UNIQUE) && name->id != prev_id)
+        {
+          GString *unique_name;
 
-  /* set message header */
-  memset(kdbus_name, 0, size);
-  strcpy(kdbus_name->name, name);
-  kdbus_name->size = size;
+          unique_name = g_string_new (NULL);
+          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->id;
+        }
 
-  /* send message */
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_RELEASE, kdbus_name);
-  if (ret < 0)
-    {
-      if (errno == ESRCH)
-        status = G_BUS_RELEASE_NAME_REPLY_NON_EXISTENT;
-      else if (errno == EADDRINUSE)
-        status = G_BUS_RELEASE_NAME_REPLY_NOT_OWNER;
-      else
-        return FALSE;
+       KDBUS_ITEM_FOREACH(item, name, items)
+         if (item->type == KDBUS_ITEM_OWNED_NAME)
+           item_name = item->name.name;
+
+        if (g_dbus_is_name (item_name))
+          g_variant_builder_add (builder, "s", item_name);
     }
 
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(u)",status),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
+  result = g_variant_new ("(as)", builder);
+  g_variant_builder_unref (builder);
 
-  return TRUE;
+  g_kdbus_free_data (worker, cmd.offset);
+  return result;
 }
 
 
 /**
- * g_kdbus_ListNames_handler:
- * Returns: TRUE on success.
+ * _g_kdbus_NameHasOwner_internal:
+ *
  */
 static gboolean
-g_kdbus_ListNames_handler (GDBusWorker   *worker,
-                           GKdbus        *kdbus,
-                           GDBusMessage  *dbus_msg,
-                           guint64        flags)
+g_kdbus_NameHasOwner_internal (GKDBusWorker       *worker,
+                               const gchar  *name,
+                               GError      **error)
 {
-  GDBusMessage *reply;
-  GVariantBuilder *builder;
-  struct kdbus_cmd_name_list cmd = {};
-  struct kdbus_name_list *name_list;
-  struct kdbus_cmd_name *name;
-  guint64 prev_id = 0;
+  struct kdbus_cmd_info *cmd;
+  gssize size, len;
   gint ret;
 
-  cmd.flags = flags;
-
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_LIST, &cmd);
-  if (ret < 0)
-    return FALSE;
-
-  /* get name list */
-  name_list = (struct kdbus_name_list *) ((guint8 *) kdbus->priv->kdbus_buffer + cmd.offset);
-
-  builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
-  KDBUS_ITEM_FOREACH(name, name_list, names)
+  if (g_dbus_is_unique_name(name))
     {
-      if ((flags & KDBUS_NAME_LIST_UNIQUE) && name->owner_id != prev_id)
-        {
-          GString *unique_name = g_string_new (NULL);
-
-          g_string_printf (unique_name, ":1.%llu", name->owner_id);
-
-          g_variant_builder_add (builder, "s", unique_name->str);
-          g_string_free (unique_name,TRUE);
-          prev_id = name->owner_id;
-        }
-
-        if (g_dbus_is_name (name->name))
-          g_variant_builder_add (builder, "s", name->name);
+       size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
+       cmd = g_alloca0 (size);
+       cmd->id = g_ascii_strtoull (name+3, NULL, 10);
     }
+  else
+    {
+       len = strlen(name) + 1;
+       size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
+       cmd = g_alloca0 (size);
+       cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+       cmd->items[0].type = KDBUS_ITEM_NAME;
+       memcpy (cmd->items[0].str, name, len);
+    }
+  cmd->size = size;
 
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(as)", builder),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
+  ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
+  g_kdbus_free_data (worker, cmd->offset);
 
-  g_variant_builder_unref (builder);
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_FREE, &cmd.offset);
   if (ret < 0)
     return FALSE;
-
-  return TRUE;
+  else
+    return TRUE;
 }
 
 
 /**
- * g_kdbus_ListQueuedOwners_handler:
- * Returns: TRUE on success.
+ * _g_kdbus_GetListQueuedOwners:
+ *
  */
-static gboolean
-g_kdbus_ListQueuedOwners_handler (GDBusWorker   *worker,
-                                  GKdbus        *kdbus,
-                                  GDBusMessage  *dbus_msg)
+GVariant *
+_g_kdbus_GetListQueuedOwners (GKDBusWorker  *worker,
+                              const gchar   *name,
+                              GError       **error)
 {
-  GDBusMessage *reply;
-  GString *unique_name;
+  GVariant *result;
   GVariantBuilder *builder;
-  struct kdbus_cmd_name_list cmd = {};
-  struct kdbus_name_list *name_list;
-  struct kdbus_cmd_name *name;
-  const gchar *service;
+  GString *unique_name;
   gint ret;
 
-  /* read and validate message */
-  GVariant *body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "ListQueuedOwners", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &service);
+  struct kdbus_info *name_list, *kname;
+  struct kdbus_cmd_list cmd = {
+    .size = sizeof(cmd),
+    .flags = KDBUS_LIST_QUEUED
+  };
 
-  if (!g_kdbus_check_name (worker, dbus_msg, service))
-    return TRUE;
-
-  if (!g_kdbus_NameHasOwner (kdbus, service))
+  if (!g_dbus_is_name (name))
     {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Could not get owners of name \'%s\': no such name", service);
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_NAME_HAS_NO_OWNER);
-      g_string_free (error_name,TRUE);
-      return TRUE;
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Given bus name \"%s\" is not valid", name);
+      return NULL;
     }
 
-  /* get queued name list */
-  cmd.flags = KDBUS_NAME_LIST_QUEUED;
+  if (!g_kdbus_NameHasOwner_internal (worker, name, error))
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_NAME_HAS_NO_OWNER,
+                   "Could not get owner of name '%s': no such name", name);
+      return NULL;
+    }
 
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_NAME_LIST, &cmd);
+  ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
   if (ret < 0)
-    return FALSE;
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Error listing names"));
+      return NULL;
+    }
 
-  name_list = (struct kdbus_name_list *) ((guint8 *) kdbus->priv->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(name, name_list, names)
+  KDBUS_FOREACH(kname, name_list, cmd.list_size)
     {
-      if (name->size <= sizeof(*name))
-        continue;
+      struct kdbus_item *item;
+      const char *item_name = "";
 
-      if (strcmp(name->name, service))
-        continue;
+      KDBUS_ITEM_FOREACH(item, kname, items)
+        if (item->type == KDBUS_ITEM_NAME)
+          item_name = item->str;
 
-      g_string_printf (unique_name, ":1.%llu", name->owner_id);
-      g_variant_builder_add (builder, "s", unique_name);
+      if (strcmp(item_name, name))
+        continue;
 
+      g_string_printf (unique_name, ":1.%llu", kname->id);
+      g_variant_builder_add (builder, "s", item_name);
     }
 
-  /* generate reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(as)", builder),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-
+  result = g_variant_new ("(as)", builder);
   g_variant_builder_unref (builder);
   g_string_free (unique_name,TRUE);
 
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_FREE, &cmd.offset);
-  if (ret < 0)
-    return FALSE;
-
-  return TRUE;
+  g_kdbus_free_data (worker, cmd.offset);
+  return result;
 }
 
 
 /**
- * g_kdbus_GetOwner_handler:
- * Returns: TRUE on success.
+ * g_kdbus_GetConnInfo_internal:
+ *
  */
-static gboolean
-g_kdbus_GetOwner_handler (GDBusWorker   *worker,
-                          GKdbus        *kdbus,
-                          GDBusMessage  *dbus_msg,
-                          guint64        flag)
+static GVariant *
+g_kdbus_GetConnInfo_internal (GKDBusWorker  *worker,
+                              const gchar      *name,
+                              guint64           flag,
+                              GError          **error)
 {
-  GVariant *result = NULL;
-  GDBusMessage *reply;
-  struct kdbus_cmd_conn_info *cmd;
-  struct kdbus_conn_info *conn_info;
+  GVariant *result;
+
+  struct kdbus_cmd_info *cmd;
+  struct kdbus_info *conn_info;
   struct kdbus_item *item;
-  const gchar *name;
-  gssize size;
+  gssize size, len;
   gint ret;
 
-  /* read and validate message */
-  GVariant *body = g_dbus_message_get_body (dbus_msg);
+  result = NULL;
 
-  if (!g_kdbus_check_signature (worker, dbus_msg, "GetOwner", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &name);
+  if (!g_dbus_is_name (name))
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_INVALID_ARGS,
+                   "Given bus name \"%s\" is not valid", name);
+      return NULL;
+    }
 
-  if (!g_kdbus_check_name (worker, dbus_msg, name))
-    return TRUE;
+  if (!g_kdbus_NameHasOwner_internal (worker, name, error))
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_NAME_HAS_NO_OWNER,
+                   "Could not get owner of name '%s': no such name", name);
+      return NULL;
+    }
 
-  /* setup kmsg for ioctl */
   if (g_dbus_is_unique_name(name))
     {
-       size = G_STRUCT_OFFSET (struct kdbus_cmd_conn_info, name);
+       size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
        cmd = g_alloca0 (size);
-       cmd->id = g_ascii_strtoull (name+3,NULL,10);
+       cmd->id = g_ascii_strtoull (name+3, NULL, 10);
     }
   else
     {
-       size = G_STRUCT_OFFSET (struct kdbus_cmd_conn_info, name) + strlen(name) + 1;
+       len = strlen(name) + 1;
+       size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
        cmd = g_alloca0 (size);
-       strcpy(cmd->name, name);
+       cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+       cmd->items[0].type = KDBUS_ITEM_NAME;
+       memcpy (cmd->items[0].str, name, len);
     }
 
-  cmd->flags = KDBUS_ATTACH_NAMES;
+  cmd->attach_flags = _KDBUS_ATTACH_ALL;
   cmd->size = size;
 
-  /* get info about connection */
-  ret = ioctl(kdbus->priv->fd, KDBUS_CMD_CONN_INFO, cmd);
+  ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
   if (ret < 0)
     {
-      GString *error_name = g_string_new (NULL);
-      g_string_printf (error_name, "Could not get owners of name \'%s\': no such name", name);
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_NAME_HAS_NO_OWNER);
-      g_string_free (error_name, TRUE);
-      return TRUE;
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Could not get connection info"));
+      return NULL;
     }
 
-  conn_info = (struct kdbus_conn_info *) ((guint8 *) kdbus->priv->kdbus_buffer + cmd->offset);
+  conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset);
 
+  /*
   if (conn_info->flags & KDBUS_HELLO_ACTIVATOR)
-    return FALSE;
+    {}
+  */
 
   if (flag == G_BUS_CREDS_UNIQUE_NAME)
     {
-       GString *unique_name = g_string_new (NULL);
+       GString *unique_name;
 
+       unique_name = g_string_new (NULL);
        g_string_printf (unique_name, ":1.%llu", (unsigned long long) conn_info->id);
-
-       result = g_variant_new ("(s)", unique_name);
+       result = g_variant_new ("(s)", unique_name->str);
        g_string_free (unique_name,TRUE);
-       goto send_reply;
+       goto exit;
     }
 
-  /* read creds info */
   KDBUS_ITEM_FOREACH(item, conn_info, items)
    {
-
       switch (item->type)
         {
-
-          case KDBUS_ITEM_CREDS:
+          case KDBUS_ITEM_PIDS:
 
             if (flag == G_BUS_CREDS_PID)
               {
-                guint pid = item->creds.pid;
+                guint pid = item->pids.pid;
                 result = g_variant_new ("(u)", pid);
-                goto send_reply;
+                goto exit;
               }
 
+          case KDBUS_ITEM_CREDS:
+
             if (flag == G_BUS_CREDS_UID)
               {
                 guint uid = item->creds.uid;
                 result = g_variant_new ("(u)", uid);
-                goto send_reply;
+                goto exit;
               }
 
           case KDBUS_ITEM_SECLABEL:
-            if (flag == G_BUS_CREDS_SELINUX_CONTEXT)
-              {
-                gint counter;
-                gchar *label;
-                GVariantBuilder *builder = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
-
-                label = g_strdup (item->str);
-                if (!label)
-                  goto exit;
-
-                for (counter = 0 ; counter < strlen (label) ; counter++)
-                  {
-                    g_variant_builder_add (builder, "y", label);
-                    label++;
-                  }
-
-                result = g_variant_new ("(ay)", builder);
-                g_variant_builder_unref (builder);
-                g_free (label);
-                goto send_reply;
-
-              }
-            break;
-
           case KDBUS_ITEM_PID_COMM:
           case KDBUS_ITEM_TID_COMM:
           case KDBUS_ITEM_EXE:
           case KDBUS_ITEM_CMDLINE:
           case KDBUS_ITEM_CGROUP:
           case KDBUS_ITEM_CAPS:
-          case KDBUS_ITEM_NAME:
           case KDBUS_ITEM_AUDIT:
+          case KDBUS_ITEM_CONN_DESCRIPTION:
+          case KDBUS_ITEM_AUXGROUPS:
+          case KDBUS_ITEM_OWNED_NAME:
             break;
-
         }
    }
 
-send_reply:
-  if (result == NULL)
-    goto exit;
-
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        result,
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-
 exit:
-  ioctl(kdbus->priv->fd, KDBUS_CMD_FREE, &cmd->offset);
-  return TRUE;
-
+  g_kdbus_free_data (worker, cmd->offset);
+  return result;
 }
 
 
 /**
- * g_kdbus_NameHasOwner_handler:
- * Returns: TRUE on success.
+ * _g_kdbus_GetNameOwner:
+ *
  */
-static gboolean
-g_kdbus_NameHasOwner_handler (GDBusWorker   *worker,
-                              GKdbus        *kdbus,
-                              GDBusMessage  *dbus_msg)
-{
-  GDBusMessage *reply;
-  GVariant *result = NULL;
-  const gchar *name;
-
-  /* read and validate message */
-  GVariant *body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "NameHasOwner", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &name);
+GVariant *
+_g_kdbus_GetNameOwner (GKDBusWorker  *worker,
+                       const gchar      *name,
+                       GError          **error)
+{
+  return g_kdbus_GetConnInfo_internal (worker,
+                                       name,
+                                       G_BUS_CREDS_UNIQUE_NAME,
+                                       error);
+}
 
-  if (!g_kdbus_check_name (worker, dbus_msg, name))
-    return TRUE;
 
-  /* check whether name has owner */
-  if (!g_kdbus_NameHasOwner (kdbus, name))
-    result = g_variant_new ("(b)", FALSE);
-  else
-    result = g_variant_new ("(b)", TRUE);
-
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        result,
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-  return TRUE;
+/**
+ * _g_kdbus_GetConnectionUnixProcessID:
+ *
+ */
+GVariant *
+_g_kdbus_GetConnectionUnixProcessID (GKDBusWorker  *worker,
+                                     const gchar      *name,
+                                     GError          **error)
+{
+  return g_kdbus_GetConnInfo_internal (worker,
+                                       name,
+                                       G_BUS_CREDS_PID,
+                                       error);
 }
 
 
 /**
- * g_kdbus_GetId_handler:
- * Returns: TRUE on success.
+ * _g_kdbus_GetConnectionUnixUser:
+ *
  */
-static gboolean
-g_kdbus_GetId_handler (GDBusWorker   *worker,
-                       GKdbus        *kdbus,
-                       GDBusMessage  *dbus_msg)
-{
-  GDBusMessage *reply;
-  GString *result = g_string_new (NULL);
-  gint i;
-
-  for (i=0; i<16; i++)
-    g_string_append_printf (result, "%02x", kdbus->priv->bus_id[i]);
-
-  /* generate local reply */
-  reply = g_kdbus_generate_local_reply (dbus_msg,
-                                        G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        g_dbus_message_get_serial (dbus_msg),
-                                        g_variant_new ("(s)", result->str),
-                                        NULL);
-  _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-
-  g_string_free (result,TRUE);
-  return TRUE;
+GVariant *
+_g_kdbus_GetConnectionUnixUser (GKDBusWorker  *worker,
+                                const gchar      *name,
+                                GError          **error)
+{
+  return g_kdbus_GetConnInfo_internal (worker,
+                                       name,
+                                       G_BUS_CREDS_UID,
+                                       error);
 }
 
 
 /**
- * g_kdbus_StartServiceByName_handler:
- * Returns: TRUE on success.
- *
+ * 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 gboolean
-g_kdbus_StartServiceByName_handler (GDBusWorker   *worker,
-                                    GKdbus        *kdbus,
-                                    GDBusMessage  *dbus_msg)
+static void
+g_kdbus_bloom_add_data (GKDBusWorker  *worker,
+                        guint64        bloom_data [],
+                        const void    *data,
+                        gsize          n)
 {
-  GDBusMessage *reply;
-  GVariant *body;
-  const gchar *name;
-  guint64 flags;
-
-  body = g_dbus_message_get_body (dbus_msg);
+  guint8 hash[8];
+  guint64 bit_num;
+  guint bytes_num = 0;
+  guint cnt_1, cnt_2;
 
-  if (!g_kdbus_check_signature (worker, dbus_msg, "StartServiceByName", body, G_VARIANT_TYPE("(su)")))
-    return TRUE;
+  guint c = 0;
+  guint64 p = 0;
 
-  g_variant_get (body, "(&su)", &name, &flags);
+  bit_num = worker->bloom_size * 8;
 
-  if (!g_kdbus_check_name (worker, dbus_msg, name))
-    return TRUE;
+  if (bit_num > 1)
+    bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
 
-  if (g_kdbus_NameHasOwner (kdbus, name))
+  for (cnt_1 = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++)
     {
-      reply = g_kdbus_generate_local_reply (dbus_msg,
-                                            G_DBUS_MESSAGE_TYPE_METHOD_RETURN,
-                                            G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                            g_dbus_message_get_serial (dbus_msg),
-                                            g_variant_new ("(u)", G_BUS_START_REPLY_ALREADY_RUNNING),
-                                            NULL);
-      _g_dbus_worker_queue_or_deliver_received_message (worker, reply);
-      return TRUE;
-    }
+      for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++)
+        {
+          if (c <= 0)
+            {
+              _g_siphash24(hash, data, n, hash_keys[cnt_1++]);
+              c += 8;
+            }
 
-  /* TODO */
-  g_error ("[KDBUS] StartServiceByName method is not implemented yet");
+          p = (p << 8ULL) | (guint64) hash[8 - c];
+          c--;
+        }
 
-  return TRUE;
+      p &= bit_num - 1;
+      bloom_data[p >> 6] |= 1ULL << (p & 63);
+    }
 }
 
 
 /**
- * g_kdbus_AddMatch_handler:
- * Returns: TRUE on success.
+ * g_kdbus_bloom_add_pair:
  *
  */
-static gboolean
-g_kdbus_AddMatch_handler (GDBusWorker   *worker,
-                          GKdbus        *kdbus,
-                          GDBusMessage  *dbus_msg)
+static void
+g_kdbus_bloom_add_pair (GKDBusWorker  *worker,
+                        guint64        bloom_data [],
+                        const gchar   *parameter,
+                        const gchar   *value)
 {
-  GVariant *body;
-  const gchar *rule;
+  gchar buf[1024];
+  gsize size;
 
-  body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "AddMatch", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &rule);
+  size = strlen(parameter) + strlen(value) + 1;
+  if (size > 1024)
+    return;
 
-  /* TODO */
-  g_error ("[KDBUS] AddMatch method is not implemented yet");
-
-  return TRUE;
+  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
+  g_kdbus_bloom_add_data(worker, bloom_data, buf, size);
 }
 
 
 /**
- * g_kdbus_RemoveMatch_handler:
- * Returns: TRUE on success.
+ * g_kdbus_bloom_add_prefixes:
  *
  */
-static gboolean
-g_kdbus_RemoveMatch_handler (GDBusWorker   *worker,
-                             GKdbus        *kdbus,
-                             GDBusMessage  *dbus_msg)
+static void
+g_kdbus_bloom_add_prefixes (GKDBusWorker  *worker,
+                            guint64        bloom_data [],
+                            const gchar   *parameter,
+                            const gchar   *value,
+                            gchar          separator)
 {
-  GVariant *body;
-  const gchar *rule;
-
-  body = g_dbus_message_get_body (dbus_msg);
-
-  if (!g_kdbus_check_signature (worker, dbus_msg, "RemoveMatch", body, G_VARIANT_TYPE("(s)")))
-    return TRUE;
-
-  g_variant_get (body, "(&s)", &rule);
+  gchar buf[1024];
+  gsize size;
 
-  /* TODO */
-  g_error ("[KDBUS] RemoveMatch method is not implemented yet");
+  size = strlen(parameter) + strlen(value) + 1;
+  if (size > 1024)
+    return;
 
-  return TRUE;
-}
+  strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
 
+  for (;;)
+    {
+      gchar *last_sep;
+      last_sep = strrchr(buf, separator);
+      if (!last_sep || last_sep == buf)
+        break;
 
-/**
- * g_kdbus_UnsupportedMethod_handler:
- * Returns: TRUE on success.
- */
-static gboolean
-g_kdbus_UnsupportedMethod_handler (GDBusWorker   *worker,
-                                   GKdbus        *kdbus,
-                                   GDBusMessage  *dbus_msg,
-                                   const gchar   *method_name)
-{
-  GString *error_name = g_string_new (NULL);
-  g_string_printf (error_name, "Method \"%s\" is not supported", method_name);
-  g_kdbus_generate_local_error (worker,
-                                dbus_msg,
-                                g_variant_new ("(s)",error_name->str),
-                                G_DBUS_ERROR_UNKNOWN_METHOD);
-  g_string_free (error_name,TRUE);
-  return TRUE;
+      *last_sep = 0;
+      g_kdbus_bloom_add_data(worker, bloom_data, buf, last_sep-buf);
+    }
 }
 
 
 /**
- * g_kdbus_bus_driver:
+ * _g_kdbus_AddMatch:
  *
  */
-static gboolean
-g_kdbus_bus_driver (GDBusWorker   *worker,
-                    GKdbus        *kdbus,
-                    GDBusMessage  *dbus_msg)
-{
-  gboolean ret = FALSE;
-
-  /* Hello */
-  if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "Hello") == 0)
-    {
-      g_kdbus_take_fd (kdbus);
-      ret = g_kdbus_Hello_reply (worker, kdbus, dbus_msg);
-    }
+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;
 
-  /* RequestName and ReleaseName */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "RequestName") == 0)
-    ret = g_kdbus_RequestName_handler (worker, kdbus, dbus_msg);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "ReleaseName") == 0)
-    ret = g_kdbus_ReleaseName_handler (worker, kdbus, dbus_msg);
-
-  /* All List* Methods */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "ListNames") == 0)
-    ret = g_kdbus_ListNames_handler (worker, kdbus, dbus_msg, KDBUS_NAME_LIST_UNIQUE | KDBUS_NAME_LIST_NAMES);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "ListActivatableNames") == 0)
-    ret = g_kdbus_ListNames_handler (worker, kdbus, dbus_msg, KDBUS_NAME_LIST_ACTIVATORS);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "ListQueuedOwners") == 0)
-    ret = g_kdbus_ListQueuedOwners_handler (worker, kdbus, dbus_msg);
-
-  /* All Get* Methods */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "GetNameOwner") == 0)
-    ret = g_kdbus_GetOwner_handler (worker, kdbus, dbus_msg, G_BUS_CREDS_UNIQUE_NAME);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "GetConnectionUnixProcessID") == 0)
-    ret = g_kdbus_GetOwner_handler (worker, kdbus, dbus_msg, G_BUS_CREDS_PID);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "GetConnectionUnixUser") == 0)
-    ret = g_kdbus_GetOwner_handler (worker, kdbus, dbus_msg, G_BUS_CREDS_UID);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "GetConnectionSELinuxSecurityContext") == 0)
-    ret = g_kdbus_GetOwner_handler (worker, kdbus, dbus_msg, G_BUS_CREDS_SELINUX_CONTEXT);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "GetId") == 0)
-    ret = g_kdbus_GetId_handler (worker, kdbus, dbus_msg);
-
-  /* NameHasOwner nad StartServiceByName methods */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "NameHasOwner") == 0)
-    ret = g_kdbus_NameHasOwner_handler (worker, kdbus, dbus_msg);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "StartServiceByName") == 0)
-    ret = g_kdbus_StartServiceByName_handler (worker, kdbus, dbus_msg);
-
-  /* AddMatch and RemoveMatch */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "AddMatch") == 0)
-    ret = g_kdbus_AddMatch_handler (worker, kdbus, dbus_msg);
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "RemoveMatch") == 0)
-    ret = g_kdbus_RemoveMatch_handler (worker, kdbus, dbus_msg);
-
-  /* Unsupported Methods */
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "ReloadConfig") == 0)
-    ret = g_kdbus_UnsupportedMethod_handler (worker, kdbus, dbus_msg, "ReloadConfig");
-  else if (g_strcmp0(g_dbus_message_get_member(dbus_msg), "UpdateActivationEnvironment") == 0)
-    ret = g_kdbus_UnsupportedMethod_handler (worker, kdbus, dbus_msg, "UpdateActivationEnvironment");
+  if (match_rule[0] == '-')
+    return;
 
-  else
+  match = match_new (match_rule);
+  if (!match)
     {
-      GString *error_name;
-
-      error_name = g_string_new (NULL);
-      g_string_printf (error_name, "org.freedesktop.DBus does not understand message %s", g_dbus_message_get_member(dbus_msg));
-
-      g_kdbus_generate_local_error (worker,
-                                    dbus_msg,
-                                    g_variant_new ("(s)",error_name->str),
-                                    G_DBUS_ERROR_UNKNOWN_METHOD);
-      g_string_free (error_name,TRUE);
+      match_free (match);
+      return;
     }
 
-  return ret;
-}
+  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);
 
-/**
- * g_kdbus_alloc_memfd:
- *
- */
-static gboolean
-g_kdbus_alloc_memfd (GKdbus  *kdbus)
-{
-  struct kdbus_cmd_memfd_make *memfd;
-  struct kdbus_item *item;
-  gssize size;
-  gchar *name = "gdbus-memfd";
+  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;
 
-  size = ALIGN8(G_STRUCT_OFFSET(struct kdbus_cmd_memfd_make, items)) +
-         ALIGN8(G_STRUCT_OFFSET(struct kdbus_item, str)) +
-         strlen(name) + 1;
+          case MATCH_ELEMENT_TYPE:
+            g_kdbus_bloom_add_pair (worker, bloom, "message-type", element->value);
+            break;
 
-  memfd = g_alloca0 (size);
-  memfd->size = size;
+          case MATCH_ELEMENT_INTERFACE:
+            g_kdbus_bloom_add_pair (worker, bloom, "interface", element->value);
+            break;
 
-  item = memfd->items;
-  item->size = ALIGN8(offsetof(struct kdbus_item, str)) + strlen(name) + 1;
-  item->type = KDBUS_ITEM_MEMFD_NAME;
-  memcpy(item->str, name, strlen(name) + 1);
+          case MATCH_ELEMENT_MEMBER:
+            g_kdbus_bloom_add_pair (worker, bloom, "member", element->value);
+            break;
 
-  if (ioctl(kdbus->priv->fd, KDBUS_CMD_MEMFD_NEW, memfd) < 0)
-    return FALSE;
+          case MATCH_ELEMENT_PATH:
+            g_kdbus_bloom_add_pair (worker, bloom, "path", element->value);
+            break;
 
-  kdbus->priv->memfd = memfd->fd;
+          case MATCH_ELEMENT_PATH_NAMESPACE:
+            if (g_strcmp0 (element->value, "/"))
+              g_kdbus_bloom_add_pair (worker, bloom, "path-slash-prefix", element->value);
+            break;
 
-  return TRUE;
-}
+          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;
 
-/**
- * _g_kdbus_release_msg:
- * Release memory occupied by kdbus_msg.
- * Use after DBUS message is extracted.
- */
-void
-_g_kdbus_release_kmsg (GKdbus  *kdbus)
-{
-  struct kdbus_item *item = NULL;
-  GSList *iterator = NULL;
-  guint64 offset;
+          case MATCH_ELEMENT_ARG0NAMESPACE:
+            g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value);
+            break;
 
-  offset = (guint8 *)kdbus->priv->kmsg - (guint8 *)kdbus->priv->kdbus_buffer;
-  ioctl(kdbus->priv->fd, KDBUS_CMD_FREE, &offset);
+          case MATCH_ELEMENT_DESTINATION:
+          case MATCH_ELEMENT_EAVESDROP:
+            break;
+        }
+    }
 
-  for (iterator = kdbus->priv->kdbus_msg_items; iterator; iterator = iterator->next)
-    g_free ((msg_part*)iterator->data);
+  size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size);
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
 
-  g_slist_free (kdbus->priv->kdbus_msg_items);
-  kdbus->priv->kdbus_msg_items = NULL;
+  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);
 
-  KDBUS_ITEM_FOREACH (item, kdbus->priv->kmsg, items)
+  if (src_id != KDBUS_MATCH_ID_ANY)
     {
-      if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD)
-        close(item->memfd.fd);
-      else if (item->type == KDBUS_ITEM_FDS)
-        {
-          gint i;
-          gint num_fds = (item->size - G_STRUCT_OFFSET(struct kdbus_item, fds)) / sizeof(int);
+      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);
+    }
 
-          for (i = 0; i < num_fds; i++)
-            close(item->fds[i]);
-        }
+  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_critical ("Error while adding a match: %d", cookie);
 
-/**
- * g_kdbus_append_payload_vec:
- *
- */
-static void
-g_kdbus_append_payload_vec (struct kdbus_item **item,
-                            const void         *data_ptr,
-                            gssize              size)
-{
-  *item = ALIGN8_PTR(*item);
-  (*item)->size = G_STRUCT_OFFSET (struct kdbus_item, vec) + sizeof(struct kdbus_vec);
-  (*item)->type = KDBUS_ITEM_PAYLOAD_VEC;
-  (*item)->vec.address = (guint64)((guintptr)data_ptr);
-  (*item)->vec.size = size;
-  *item = KDBUS_ITEM_NEXT(*item);
+  match_free (match);
 }
 
 
 /**
- * g_kdbus_append_payload_memfd:
+ * _g_kdbus_RemoveMatch:
  *
  */
-static void
-g_kdbus_append_payload_memfd (struct kdbus_item **item,
-                              int                 fd,
-                              gssize              size)
+void
+_g_kdbus_RemoveMatch (GKDBusWorker  *worker,
+                      guint          cookie)
 {
-  *item = ALIGN8_PTR(*item);
-  (*item)->size = G_STRUCT_OFFSET (struct kdbus_item, memfd) + sizeof(struct kdbus_memfd);
-  (*item)->type = KDBUS_ITEM_PAYLOAD_MEMFD;
-  (*item)->memfd.fd = fd;
-  (*item)->memfd.size = size;
-  *item = KDBUS_ITEM_NEXT(*item);
+  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);
 }
 
 
 /**
- * g_kdbus_append_payload_destiantion:
+ * _g_kdbus_subscribe_name_acquired:
  *
  */
 static void
-g_kdbus_append_destination (struct kdbus_item **item,
-                            const gchar        *destination,
-                            gsize               size)
+_g_kdbus_subscribe_name_owner_changed (GKDBusWorker  *worker,
+                                       const gchar      *name,
+                                       const gchar      *old_name,
+                                       const gchar      *new_name,
+                                       guint             cookie)
 {
-  *item = ALIGN8_PTR(*item);
-  (*item)->size = G_STRUCT_OFFSET (struct kdbus_item, str) + size + 1;
-  (*item)->type = KDBUS_ITEM_DST_NAME;
-  memcpy ((*item)->str, destination, size+1);
-  *item = KDBUS_ITEM_NEXT(*item);
-}
+  struct kdbus_item *item;
+  struct kdbus_cmd_match *cmd;
+  gssize size, len;
+  gint ret;
+  guint64 old_id = 0; /* XXX why? */
+  guint64 new_id = KDBUS_MATCH_ID_ANY;
 
+  len = strlen(name) + 1;
+  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
+                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
-/**
- * g_kdbus_append_payload_bloom:
- *
- */
-static struct kdbus_bloom_filter *
-g_kdbus_append_bloom (struct kdbus_item **item,
-                      gsize               size)
-{
-  struct kdbus_item *bloom_item;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
 
-  bloom_item = ALIGN8_PTR(*item);
-  bloom_item->size = G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
-                     G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
-                     size;
+  if (old_name[0] == 0)
+    {
+      old_id = KDBUS_MATCH_ID_ANY;
+    }
+  else
+    {
+      if (g_dbus_is_unique_name(old_name))
+        old_id = old_id+3;
+      else
+        return;
+    }
 
-  bloom_item->type = KDBUS_ITEM_BLOOM_FILTER;
+  if (new_name[0] == 0)
+    {
+      new_id = KDBUS_MATCH_ID_ANY;
+    }
+  else
+    {
+      if (g_dbus_is_unique_name(new_name))
+        new_id = new_id+3;
+      else
+        return;
+    }
 
-  *item = KDBUS_ITEM_NEXT(bloom_item);
-  return &bloom_item->bloom_filter;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
+
+  /* KDBUS_ITEM_NAME_CHANGE */
+  item->type = KDBUS_ITEM_NAME_CHANGE;
+  item->name_change.old_id.id = old_id;
+  item->name_change.new_id.id = new_id;
+  memcpy(item->name_change.name, name, len);
+  item->size = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+               G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
+  item = KDBUS_ITEM_NEXT(item);
+
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+  if (ret < 0)
+    g_warning ("ERROR - %d\n", (int) errno);
 }
 
 
 /**
- * g_kdbus_append_fds:
+ * _g_kdbus_subscribe_name_acquired:
  *
  */
-static void
-g_kdbus_append_fds (struct kdbus_item **item,
-                    GUnixFDList        *fd_list)
+void
+_g_kdbus_subscribe_name_acquired (GKDBusWorker  *worker,
+                                  const gchar      *name)
 {
-  *item = ALIGN8_PTR(*item);
-  (*item)->size = G_STRUCT_OFFSET (struct kdbus_item, fds) + sizeof(int) * g_unix_fd_list_get_length(fd_list);
-  (*item)->type = KDBUS_ITEM_FDS;
-  memcpy ((*item)->fds, g_unix_fd_list_peek_fds(fd_list, NULL), sizeof(int) * g_unix_fd_list_get_length(fd_list));
+  struct kdbus_item *item;
+  struct kdbus_cmd_match *cmd;
+  gssize size, len;
+  guint64 cookie;
+  gint ret;
+
+  len = strlen(name) + 1;
+  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
+                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
-  *item = KDBUS_ITEM_NEXT(*item);
+  cookie = 0xbeefbeefbeefbeef;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
+
+  /* KDBUS_ITEM_NAME_ADD */
+  item->type = KDBUS_ITEM_NAME_ADD;
+  item->name_change.old_id.id = KDBUS_MATCH_ID_ANY;
+  item->name_change.new_id.id = worker->unique_id;
+  memcpy(item->name_change.name, name, len);
+  item->size = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+               G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
+  item = KDBUS_ITEM_NEXT(item);
+
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+  if (ret < 0)
+    g_warning ("ERROR - %d\n", (int) errno);
+
+  _g_kdbus_subscribe_name_owner_changed (worker, name, "", worker->unique_name, cookie);
 }
 
 
 /**
- * _g_kdbus_attach_fds_to_msg:
+ * _g_kdbus_subscribe_name_lost:
  *
  */
 void
-_g_kdbus_attach_fds_to_msg (GKdbus       *kdbus,
-                            GUnixFDList **fd_list)
+_g_kdbus_subscribe_name_lost (GKDBusWorker  *worker,
+                              const gchar      *name)
 {
-  if ((kdbus->priv->fds != NULL) && (kdbus->priv->num_fds > 0))
-    {
-      gint n;
+  struct kdbus_item *item;
+  struct kdbus_cmd_match *cmd;
+  gssize size, len;
+  guint64 cookie;
+  gint ret;
 
-      if (*fd_list == NULL)
-        *fd_list = g_unix_fd_list_new();
+  len = strlen(name) + 1;
+  size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
+                      G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+                      G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len);
 
-      for (n = 0; n < kdbus->priv->num_fds; n++)
-        {
-          g_unix_fd_list_append (*fd_list, kdbus->priv->fds[n], NULL);
-          (void) g_close (kdbus->priv->fds[n], NULL);
-        }
+  cookie = 0xdeafdeafdeafdeaf;
+  cmd = g_alloca0 (size);
+  cmd->size = size;
+  cmd->cookie = cookie;
+  item = cmd->items;
+
+  /* KDBUS_ITEM_NAME_REMOVE */
+  item->type = KDBUS_ITEM_NAME_REMOVE;
+  item->name_change.old_id.id = worker->unique_id;
+  item->name_change.new_id.id = KDBUS_MATCH_ID_ANY;
+  memcpy(item->name_change.name, name, len);
+  item->size = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+               G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
+  item = KDBUS_ITEM_NEXT(item);
 
-      g_free (kdbus->priv->fds);
-      kdbus->priv->fds = NULL;
-      kdbus->priv->num_fds = 0;
-    }
+  ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+  if (ret < 0)
+    g_warning ("ERROR - %d\n", (int) errno);
+
+  _g_kdbus_subscribe_name_owner_changed (worker, name, worker->unique_name, "", cookie);
 }
 
 
 /**
- * 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
+ * _g_kdbus_unsubscribe_name_acquired:
+ *
  */
-static void
-g_kdbus_bloom_add_data (GKdbus      *kdbus,
-                        guint64      bloom_data [],
-                        const void  *data,
-                        gsize        n)
+void
+_g_kdbus_unsubscribe_name_acquired (GKDBusWorker  *worker)
 {
-  guint8 hash[8];
-  guint64 bit_num;
-  guint bytes_num = 0;
-  guint cnt_1, cnt_2;
-
-  guint c = 0;
-  guint64 p = 0;
-
-  bit_num = kdbus->priv->bloom_size * 8;
-
-  if (bit_num > 1)
-    bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
-
-  for (cnt_1 = 0; cnt_1 < (kdbus->priv->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--;
-        }
+  guint64 cookie;
 
-      p &= bit_num - 1;
-      bloom_data[p >> 6] |= 1ULL << (p & 63);
-    }
+  cookie = 0xbeefbeefbeefbeef;
+  _g_kdbus_RemoveMatch (worker, cookie);
 }
 
 
 /**
- * g_kdbus_bloom_add_pair:
+ * _g_kdbus_unsubscribe_name_lost:
  *
  */
-static void
-g_kdbus_bloom_add_pair (GKdbus       *kdbus,
-                        guint64       bloom_data [],
-                        const gchar  *parameter,
-                        const gchar  *value)
+void
+_g_kdbus_unsubscribe_name_lost (GKDBusWorker  *worker)
 {
-  GString *data = g_string_new (NULL);
+  guint64 cookie;
 
-  g_string_printf (data,"%s:%s",parameter,value);
-  g_kdbus_bloom_add_data(kdbus, bloom_data, data->str, data->len);
-  g_string_free (data, TRUE);
+  cookie = 0xdeafdeafdeafdeaf;
+  _g_kdbus_RemoveMatch (worker, cookie);
 }
 
 
 /**
- * g_kdbus_bloom_add_prefixes:
+ * g_kdbus_append_payload_bloom:
  *
  */
-static void
-g_kdbus_bloom_add_prefixes (GKdbus       *kdbus,
-                            guint64       bloom_data [],
-                            const gchar  *parameter,
-                            const gchar  *value,
-                            gchar         separator)
+static struct kdbus_bloom_filter *
+g_kdbus_append_bloom (struct kdbus_item **item,
+                      gsize               size)
 {
-  GString *data = g_string_new (NULL);
+  struct kdbus_item *bloom_item;
 
-  g_string_printf (data,"%s:%s",parameter,value);
+  bloom_item = KDBUS_ALIGN8_PTR(*item);
+  bloom_item->size = G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
+                     G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
+                     size;
 
-  for (;;)
-    {
-      gchar *last_sep;
-      last_sep = strrchr(data->str, separator);
-      if (!last_sep || last_sep == data->str)
-        break;
+  bloom_item->type = KDBUS_ITEM_BLOOM_FILTER;
 
-      *last_sep = 0;
-      g_kdbus_bloom_add_data(kdbus, bloom_data, data->str, last_sep-(data->str));
-    }
-  g_string_free (data, TRUE);
+  *item = KDBUS_ITEM_NEXT(bloom_item);
+  return &bloom_item->bloom_filter;
 }
 
 
@@ -1960,13 +1748,11 @@ g_kdbus_bloom_add_prefixes (GKdbus       *kdbus,
  * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
  */
 static void
-g_kdbus_setup_bloom (GKdbus                     *kdbus,
+g_kdbus_setup_bloom (GKDBusWorker                     *worker,
                      GDBusMessage               *dbus_msg,
                      struct kdbus_bloom_filter  *bloom_filter)
 {
   GVariant *body;
-  GVariantIter iter;
-  GVariant *child;
 
   const gchar *message_type;
   const gchar *interface;
@@ -1974,7 +1760,6 @@ g_kdbus_setup_bloom (GKdbus                     *kdbus,
   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));
@@ -1983,43 +1768,50 @@ g_kdbus_setup_bloom (GKdbus                     *kdbus,
   path = g_dbus_message_get_path (dbus_msg);
 
   bloom_data = bloom_filter->data;
-  memset (bloom_data, 0, kdbus->priv->bloom_size);
+  memset (bloom_data, 0, worker->bloom_size);
   bloom_filter->generation = 0;
 
-  g_kdbus_bloom_add_pair(kdbus, bloom_data, "message-type", message_type);
+  g_kdbus_bloom_add_pair(worker, bloom_data, "message-type", message_type);
 
   if (interface)
-    g_kdbus_bloom_add_pair(kdbus, bloom_data, "interface", interface);
+    g_kdbus_bloom_add_pair(worker, bloom_data, "interface", interface);
 
   if (member)
-    g_kdbus_bloom_add_pair(kdbus, bloom_data, "member", member);
+    g_kdbus_bloom_add_pair(worker, bloom_data, "member", member);
 
   if (path)
     {
-      g_kdbus_bloom_add_pair(kdbus, bloom_data, "path", path);
-      g_kdbus_bloom_add_pair(kdbus, bloom_data, "path-slash-prefix", path);
-      g_kdbus_bloom_add_prefixes(kdbus, bloom_data, "path-slash-prefix", path, '/');
+      g_kdbus_bloom_add_pair(worker, bloom_data, "path", path);
+      g_kdbus_bloom_add_pair(worker, bloom_data, "path-slash-prefix", path);
+      g_kdbus_bloom_add_prefixes(worker, bloom_data, "path-slash-prefix", path, '/');
     }
 
   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)
@@ -2030,174 +1822,79 @@ g_kdbus_setup_bloom (GKdbus                     *kdbus,
               *(e++) = '0' + (char) (cnt % 10);
             }
 
-          *e = 0;
-          g_kdbus_bloom_add_pair(kdbus, bloom_data, buf, child_string);
-
-          strcpy(e, "-dot-prefix");
-          g_kdbus_bloom_add_prefixes(kdbus, bloom_data, buf, child_string, '.');
-
+          /* We add this one for both strings and object paths */
           strcpy(e, "-slash-prefix");
-          g_kdbus_bloom_add_prefixes(kdbus, bloom_data, buf, child_string, '/');
-
-          g_free (child_string);
-          g_variant_unref (child);
-          cnt++;
-        }
-    }
-}
+          g_kdbus_bloom_add_prefixes(worker, bloom_data, buf, str, '/');
 
+          /* 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, '.');
 
-/**
- * g_kdbus_NameOwnerChanged_generate:
- * TODO: Not tesed yet
- */
-static gssize
-g_kdbus_NameOwnerChanged_generate (GKdbus             *kdbus,
-                                   struct kdbus_item  *item)
-{
-  GVariant *result = NULL;
-  GDBusMessage *reply;
-  GError *error;
-  guchar *blob;
-  gssize reply_size = 0;
-
-  gchar *owner;
-  gchar *old_owner;
-  gchar *new_owner;
-
-  /* ID change */
-  if (item->type == KDBUS_ITEM_ID_ADD || item->type == KDBUS_ITEM_ID_REMOVE)
-    {
-      owner = "";
+              *e = 0;
+              g_kdbus_bloom_add_pair(worker, bloom_data, buf, str);
+            }
 
-      if (item->type == KDBUS_ITEM_ID_ADD)
-        {
-          old_owner = NULL;
-          new_owner = owner;
-        }
-      else
-        {
-          old_owner = owner;
-          new_owner = NULL;
+          g_variant_unref (child);
         }
     }
-
-  /* name change */
-  if (item->type == KDBUS_ITEM_NAME_ADD ||
-      item->type == KDBUS_ITEM_NAME_REMOVE ||
-      item->type == KDBUS_ITEM_NAME_CHANGE )
-    {
-     g_error ("[KDBUS] 'NameChange' is not implemented yet");
-    }
-
-  result = g_variant_new ("(sss)", owner, old_owner, new_owner);
-  reply = g_kdbus_generate_local_reply (NULL,
-                                        G_DBUS_MESSAGE_TYPE_SIGNAL,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        -1,
-                                        result,
-                                        NULL);
-
-  //_g_dbus_message_set_protocol_ver (reply,2);
-  blob =  g_dbus_message_to_blob (reply, (gsize*) &reply_size, 0, &error);
-  if (blob == NULL)
-
-    g_error ("[KDBUS] NameOwnerChanged: %s\n",error->message);
-
-  ((guint32 *) blob)[2] = GUINT32_TO_LE (-1);
-  g_kdbus_add_msg_part (kdbus, (gchar*)blob, reply_size);
-
-  return reply_size;
-
 }
 
 
-/**
- * g_kdbus_KernelMethodError_generate:
- *
+/*
+ * TODO: g_kdbus_NameOwnerChanged_generate, g_kdbus_KernelMethodError_generate
  */
-static gssize
-g_kdbus_KernelMethodError_generate (GKdbus             *kdbus,
-                                    struct kdbus_item  *item)
-{
-  GVariant *error_name;
-  GDBusMessage *reply;
-  GError *error;
-  guchar *blob;
-  gssize reply_size = 0;
-
-  if (item->type == KDBUS_ITEM_REPLY_TIMEOUT)
-    error_name = g_variant_new ("(s)", "Method call timed out");
-  else
-    error_name = g_variant_new ("(s)", "Method call peer died");
-
-  error = NULL;
-  reply = g_kdbus_generate_local_reply (NULL,
-                                        G_DBUS_MESSAGE_TYPE_ERROR,
-                                        G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED,
-                                        -1,
-                                        error_name,
-                                        "org.freedesktop.DBus.Error.NoReply");
-
-  //_g_dbus_message_set_protocol_ver (reply,2);
-  blob =  g_dbus_message_to_blob (reply, (gsize*) &reply_size, 0, &error);
-
-  if (blob == NULL)
-    g_error ("[KDBUS] KernelMethodError: %s\n",error->message);
-
-  ((guint32 *) blob)[2] = GUINT32_TO_LE (-1);
-  g_kdbus_add_msg_part (kdbus, (gchar*)blob, reply_size);
-
-  return reply_size;
-}
-
 
 /**
  * g_kdbus_decode_kernel_msg:
  *
  */
-static gssize
-g_kdbus_decode_kernel_msg (GKdbus  *kdbus)
+static void
+g_kdbus_decode_kernel_msg (GKDBusWorker           *worker,
+                           struct kdbus_msg *msg)
 {
   struct kdbus_item *item = NULL;
-  gssize size = 0;
 
-  KDBUS_ITEM_FOREACH(item, kdbus->priv->kmsg, items)
+  KDBUS_ITEM_FOREACH(item, msg, items)
     {
-      switch (item->type)
+     switch (item->type)
         {
           case KDBUS_ITEM_ID_ADD:
           case KDBUS_ITEM_ID_REMOVE:
           case KDBUS_ITEM_NAME_ADD:
           case KDBUS_ITEM_NAME_REMOVE:
           case KDBUS_ITEM_NAME_CHANGE:
-            size = g_kdbus_NameOwnerChanged_generate (kdbus, item);
+            //size = g_kdbus_NameOwnerChanged_generate (worker, item);
+            g_error ("'NameOwnerChanged'");
             break;
 
           case KDBUS_ITEM_REPLY_TIMEOUT:
           case KDBUS_ITEM_REPLY_DEAD:
-            size = g_kdbus_KernelMethodError_generate (kdbus, item);
+            //size = g_kdbus_KernelMethodError_generate (worker, item);
+            g_error ("'KernelMethodError'");
             break;
 
           default:
-            g_error ("[KDBUS] KERNEL: Unknown filed - %lld", item->type);
-        }
+            g_warning ("Unknown field in kernel message - %lld", item->type);
+       }
     }
 
+#if 0
   /* Override information from the user header with data from the kernel */
-  g_string_printf (kdbus->priv->msg_sender, "org.freedesktop.DBus");
+  g_string_printf (worker->msg_sender, "org.freedesktop.DBus");
 
   /* for destination */
-  if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_BROADCAST)
+  if (worker->kmsg->dst_id == KDBUS_DST_ID_BROADCAST)
     /* for broadcast messages we don't have to set destination */
     ;
-  else if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_NAME)
-    g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->unique_id);
+  else if (worker->kmsg->dst_id == KDBUS_DST_ID_NAME)
+    g_string_printf (worker->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) worker->unique_id);
   else
-    g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->kmsg->dst_id);
-
+   g_string_printf (worker->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) worker->kmsg->dst_id);
 
   return size;
+#endif
 }
 
 
@@ -2205,16 +1902,38 @@ g_kdbus_decode_kernel_msg (GKdbus  *kdbus)
  * g_kdbus_decode_dbus_msg:
  *
  */
-static gssize
-g_kdbus_decode_dbus_msg (GKdbus  *kdbus)
+static GDBusMessage *
+g_kdbus_decode_dbus_msg (GKDBusWorker           *worker,
+                         struct kdbus_msg *msg)
 {
+  GDBusMessage *message;
   struct kdbus_item *item;
-  gchar *msg_ptr;
-  gssize ret_size = 0;
   gssize data_size = 0;
-  const gchar *destination = NULL;
+  GArray *body_vectors;
+  gsize body_size;
+  GVariant *body;
+  gchar *tmp;
+  guint i;
+  GVariant *parts[2];
+  GVariantIter *fields_iter;
+  guint8 endianness, type, flags, version;
+  guint64 key;
+  GVariant *value;
+  guint64 serial;
+
+
+  message = g_dbus_message_new ();
+
+  body_vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
+
+  tmp = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) msg->src_id);
+  g_dbus_message_set_sender (message, tmp);
+  g_free (tmp);
+
+  item = msg->items;
+  body_size = 0;
 
-  KDBUS_ITEM_FOREACH(item, kdbus->priv->kmsg, items)
+  KDBUS_ITEM_FOREACH(item, msg, items)
     {
       if (item->size <= KDBUS_ITEM_HEADER_SIZE)
         g_error("[KDBUS] %llu bytes - invalid data record\n", item->size);
@@ -2223,51 +1942,110 @@ g_kdbus_decode_dbus_msg (GKdbus  *kdbus)
 
       switch (item->type)
         {
-
          /* KDBUS_ITEM_DST_NAME */
          case KDBUS_ITEM_DST_NAME:
-           destination = item->str;
+           /* Classic D-Bus doesn't make this known to the receiver, so
+            * we don't do it here either (for compatibility with the
+            * fallback case).
+            */
            break;
 
         /* KDBUS_ITEM_PALOAD_OFF */
         case KDBUS_ITEM_PAYLOAD_OFF:
-
-          msg_ptr = (gchar*) kdbus->priv->kmsg + item->vec.offset;
-          g_kdbus_add_msg_part (kdbus, msg_ptr, item->vec.size);
-          ret_size += item->vec.size;
-
+          {
+            GVariantVector vector;
+            gsize flavour;
+
+            /* We want to make sure the bytes are aligned the same as
+             * they would be if they appeared in a contiguously
+             * allocated chunk of aligned memory.
+             *
+             * We decide what the alignment 'should' be by consulting
+             * body_size, which has been tracking the total size of the
+             * message up to this point.
+             *
+             * We then play around with the pointer by removing as many
+             * bytes as required to get it to the proper alignment (and
+             * copy extra bytes accordingly).  This means that we will
+             * grab some extra data in the 'bytes', but it won't be
+             * shared with GVariant (which means there is no chance of
+             * it being accidentally retransmitted).
+             *
+             * The kernel does the same thing, so make sure we get the
+             * expected result.  Because of the kernel doing the same,
+             * the result is that we will always be rounding-down to a
+             * multiple of 8 for the pointer, which means that the
+             * pointer will always be valid, assuming the original
+             * address was.
+             *
+             * We could fix this with a new GBytes constructor that took
+             * 'flavour' as a parameter, but it's not worth it...
+             */
+            flavour = body_size & 7;
+            g_assert ((item->vec.offset & 7) == 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;
+
+            g_array_append_val (body_vectors, vector);
+            body_size += vector.size;
+          }
           break;
 
         /* KDBUS_ITEM_PAYLOAD_MEMFD */
         case KDBUS_ITEM_PAYLOAD_MEMFD:
+          {
+            GVariantVector vector;
+            const guchar *data;
+            gsize size;
 
-          msg_ptr = mmap(NULL, item->memfd.size, PROT_READ, MAP_SHARED, item->memfd.fd, 0);
+            vector.gbytes = g_bytes_new_take_zero_copy_fd (item->memfd.fd);
+            data = g_bytes_get_data (vector.gbytes, &size);
 
-          if (msg_ptr == MAP_FAILED)
-            {
-              g_print ("mmap() fd=%i failed:%m", item->memfd.fd);
-              break;
-            }
+            g_assert (item->memfd.start + item->memfd.size <= size);
 
-          g_kdbus_add_msg_part (kdbus, msg_ptr, item->memfd.size);
-          ret_size += item->memfd.size;
+            vector.data.pointer = data + item->memfd.start;
+            vector.size = item->memfd.size;
 
+            g_array_append_val (body_vectors, vector);
+            body_size += vector.size;
+          }
           break;
 
         /* KDBUS_ITEM_FDS */
         case KDBUS_ITEM_FDS:
+          {
+            GUnixFDList *fd_list;
 
-          kdbus->priv->num_fds = data_size / sizeof(int);
-          kdbus->priv->fds = g_malloc0 (sizeof(int) * kdbus->priv->num_fds);
-          memcpy(kdbus->priv->fds, item->fds, sizeof(int) * kdbus->priv->num_fds);
-
+            fd_list = g_unix_fd_list_new_from_array (item->fds, data_size / sizeof (int));
+            g_dbus_message_set_unix_fd_list (message, fd_list);
+            g_object_unref (fd_list);
+          }
           break;
 
         /* All of the following items, like CMDLINE,
            CGROUP, etc. need some GDBUS API extensions and
            should be implemented in the future */
-        case KDBUS_ITEM_CREDS:
         case KDBUS_ITEM_TIMESTAMP:
+          {
+            g_print ("time: seq %llu mon %llu real %llu\n",
+                     item->timestamp.seqnum, item->timestamp.monotonic_ns, item->timestamp.realtime_ns);
+            //g_dbus_message_set_timestamp (message, item->timestamp.monotonic_ns / 1000);
+            //g_dbus_message_set_serial (message, item->timestamp.seqnum);
+            break;
+          }
+
+        case KDBUS_ITEM_CREDS:
+          {
+            g_print ("creds: u%u eu %u su%u fsu%u g%u eg%u sg%u fsg%u\n",
+                     item->creds.uid, item->creds.euid, item->creds.suid, item->creds.fsuid,
+                     item->creds.gid, item->creds.egid, item->creds.sgid, item->creds.fsgid);
+            break;
+          }
+
+        case KDBUS_ITEM_PIDS:
         case KDBUS_ITEM_PID_COMM:
         case KDBUS_ITEM_TID_COMM:
         case KDBUS_ITEM_EXE:
@@ -2276,8 +2054,11 @@ g_kdbus_decode_dbus_msg (GKdbus  *kdbus)
         case KDBUS_ITEM_AUDIT:
         case KDBUS_ITEM_CAPS:
         case KDBUS_ITEM_SECLABEL:
-        case KDBUS_ITEM_CONN_NAME:
+        case KDBUS_ITEM_CONN_DESCRIPTION:
+        case KDBUS_ITEM_AUXGROUPS:
+        case KDBUS_ITEM_OWNED_NAME:
         case KDBUS_ITEM_NAME:
+          g_print ("unhandled %04x\n", (int) item->type);
           break;
 
         default:
@@ -2286,24 +2067,42 @@ g_kdbus_decode_dbus_msg (GKdbus  *kdbus)
         }
     }
 
-  /* Override information from the user header with data from the kernel */
+  body = GLIB_PRIVATE_CALL(g_variant_from_vectors) (G_VARIANT_TYPE ("((yyyyuta{tv})v)"),
+                                                    (GVariantVector *) body_vectors->data,
+                                                    body_vectors->len, body_size, FALSE);
+  g_assert (body);
 
-  if (kdbus->priv->kmsg->src_id == KDBUS_SRC_ID_KERNEL)
-    g_string_printf (kdbus->priv->msg_sender, "org.freedesktop.DBus");
-  else
-    g_string_printf (kdbus->priv->msg_sender, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->kmsg->src_id);
+  for (i = 0; i < body_vectors->len; i++)
+    g_bytes_unref (g_array_index (body_vectors, GVariantVector, i).gbytes);
 
-  if (destination)
-    g_string_printf (kdbus->priv->msg_destination, "%s", destination);
-  else if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_BROADCAST)
-    /* for broadcast messages we don't have to set destination */
-    ;
-  else if (kdbus->priv->kmsg->dst_id == KDBUS_DST_ID_NAME)
-    g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->unique_id);
-  else
-    g_string_printf (kdbus->priv->msg_destination, ":1.%" G_GUINT64_FORMAT, (guint64) kdbus->priv->kmsg->dst_id);
+  g_array_free (body_vectors, TRUE);
+
+  parts[0] = g_variant_get_child_value (body, 0);
+  parts[1] = g_variant_get_child_value (body, 1);
+  g_variant_unref (body);
+
+  body = g_variant_get_variant (parts[1]);
+  g_variant_unref (parts[1]);
+  g_dbus_message_set_body (message, body);
+  g_variant_unref (body);
+
+  g_variant_get (parts[0], "(yyyyuta{tv})", &endianness, &type, &flags, &version, NULL, &serial, &fields_iter);
+  g_variant_unref (parts[0]);
+
+  while (g_variant_iter_loop (fields_iter, "{tv}", &key, &value))
+    g_dbus_message_set_header (message, key, value);
+
+  g_dbus_message_set_flags (message, flags);
+  g_dbus_message_set_serial (message, serial);
+  g_dbus_message_set_message_type (message, type);
+
+  g_print ("Received:\n%s\n", g_dbus_message_print (message, 2));
 
-  return ret_size;
+  (* worker->message_received_callback) (message, worker->user_data);
+
+  g_object_unref (message);
+
+  return 0;
 }
 
 
@@ -2311,229 +2110,444 @@ g_kdbus_decode_dbus_msg (GKdbus  *kdbus)
  * _g_kdbus_receive:
  *
  */
-gssize
-_g_kdbus_receive (GKdbus        *kdbus,
+static gssize
+_g_kdbus_receive (GKDBusWorker        *kdbus,
                   GCancellable  *cancellable,
                   GError       **error)
 {
-  struct kdbus_cmd_recv recv = {};
-  gssize size = 0;
+  struct kdbus_cmd_recv recv;
+  struct kdbus_msg *msg;
+
+  memset (&recv, 0, sizeof recv);
+  recv.size = sizeof (recv);
 
   if (g_cancellable_set_error_if_cancelled (cancellable, error))
     return -1;
 
-  again:
-    if (ioctl(kdbus->priv->fd, KDBUS_CMD_MSG_RECV, &recv) < 0)
+again:
+    if (ioctl(kdbus->fd, KDBUS_CMD_RECV, &recv) < 0)
       {
-        if (errno == EINTR || errno == EAGAIN)
+        if (errno == EINTR)
           goto again;
 
-        g_set_error (error, G_IO_ERROR, g_io_error_from_errno(errno),_("Error receiving message - KDBUS_CMD_MSG_RECV error"));
+        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"),
+                     g_strerror (errno));
         return -1;
       }
 
-    kdbus->priv->kmsg = (struct kdbus_msg *)((guint8 *)kdbus->priv->kdbus_buffer + recv.offset);
+    g_print ("but sometimes that's okay\n");
 
-    if (kdbus->priv->kmsg->payload_type == KDBUS_PAYLOAD_DBUS)
-      size = g_kdbus_decode_dbus_msg (kdbus);
-    else if (kdbus->priv->kmsg->payload_type == KDBUS_PAYLOAD_KERNEL)
-      size = g_kdbus_decode_kernel_msg (kdbus);
-    else
-      g_error ("[KDBUS] Unknown payload type: %llu", kdbus->priv->kmsg->payload_type);
+   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);
+   else if (msg->payload_type == KDBUS_PAYLOAD_KERNEL)
+     g_kdbus_decode_kernel_msg (kdbus, msg);
+   else
+     {
+       g_set_error (error,
+                    G_DBUS_ERROR,
+                    G_DBUS_ERROR_FAILED,
+                    _("Received unknown payload type"));
+       return -1;
+     }
+
+  ioctl(kdbus->fd, KDBUS_CMD_FREE, &recv.msg.offset);
+
+   return 0;
+}
+
+static gboolean
+g_kdbus_msg_append_item (struct kdbus_msg *msg,
+                         gsize             type,
+                         gconstpointer     data,
+                         gsize             size)
+{
+  struct kdbus_item *item;
+  gsize item_size;
+
+  item_size = size + G_STRUCT_OFFSET(struct kdbus_item, data);
+
+  if (msg->size + item_size > KDBUS_MSG_MAX_SIZE)
+    return FALSE;
+
+  /* align */
+  msg->size += (-msg->size) & 7;
+  item = (struct kdbus_item *) ((guchar *) msg + msg->size);
+  item->type = type;
+  item->size = item_size;
+  memcpy (item->data, data, size);
+
+  msg->size += item_size;
+
+  return TRUE;
+}
+
+static gboolean
+g_kdbus_msg_append_payload_vec (struct kdbus_msg *msg,
+                                gconstpointer     data,
+                                gsize             size)
+{
+  struct kdbus_vec vec = {
+    .size = size,
+    .address = (gsize) data
+  };
 
-    return size;
+  return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_VEC, &vec, sizeof vec);
 }
 
+static gboolean
+g_kdbus_msg_append_payload_memfd (struct kdbus_msg *msg,
+                                  gint              fd,
+                                  gsize             offset,
+                                  gsize             size)
+{
+  struct kdbus_memfd mfd = {
+   .start = offset,
+   .size = size,
+   .fd = fd,
+  };
+
+  return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_MEMFD, &mfd, sizeof mfd);
+}
+
+#if 0
+#include "dbusheader.h"
+
+void dump_header (gconstpointer data, gsize size) ;
+void
+dump_header (gconstpointer data,
+             gsize size)
+{
+  GDBusMessageHeaderFieldsIterator iter;
+  GDBusMessageHeader header;
+
+  header = g_dbus_message_header_new (data, size);
+  g_print ("header e/%c t/%u f/x%x v/%u s/%"G_GUINT64_FORMAT"\n",
+           g_dbus_message_header_get_endian (header),
+           g_dbus_message_header_get_type (header),
+           g_dbus_message_header_get_flags (header),
+           g_dbus_message_header_get_version (header),
+           g_dbus_message_header_get_serial (header));
+
+  iter = g_dbus_message_header_iterate_fields (header);
+
+  while (g_dbus_message_header_fields_iterator_next (&iter))
+    {
+      const gchar *string;
+      guint64 reply_to;
+      guint64 key;
+
+      key = g_dbus_message_header_fields_iterator_get_key (iter);
+
+      switch (key)
+        {
+          case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
+            if (g_dbus_message_header_fields_iterator_get_value_as_object_path (iter, &string))
+              g_print ("  path: %s\n", string);
+            else
+              g_print ("  path: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  interface: %s\n", string);
+            else
+              g_print ("  interface: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  member: %s\n", string);
+            else
+              g_print ("  member: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  error: %s\n", string);
+            else
+              g_print ("  error: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  serial: %s\n", string);
+            else
+              g_print ("  serial: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  destination: %s\n", string);
+            else
+              g_print ("  destination: <<invalid string>>\n");
+            break;
+
+          case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
+            if (g_dbus_message_header_fields_iterator_get_value_as_string (iter, &string))
+              g_print ("  sender: %s\n", string);
+            else
+              g_print ("  sender: <<invalid string>>\n");
+            break;
+
+          default:
+            g_print ("unknown field code %"G_GUINT64_FORMAT"\n", key);
+            g_assert_not_reached ();
+        }
+    }
+
+  g_print ("\n");
+
+}
+#endif
 
 /**
  * _g_kdbus_send:
  * Returns: size of data sent or -1 when error
  */
-gsize
-_g_kdbus_send (GDBusWorker   *worker,
-               GKdbus        *kdbus,
-               GDBusMessage  *dbus_msg,
-               gchar         *blob,
-               gsize          blob_size,
-               GUnixFDList   *fd_list,
-               GCancellable  *cancellable,
+static gboolean
+_g_kdbus_send (GKDBusWorker        *kdbus,
+               GDBusMessage  *message,
                GError       **error)
 {
-  struct kdbus_msg* kmsg;
-  struct kdbus_item *item;
-  guint64 kmsg_size = 0;
-  const gchar *name;
-  gboolean use_memfd = FALSE;
-  guint64 dst_id = KDBUS_DST_ID_BROADCAST;
+  struct kdbus_msg *msg = alloca (KDBUS_MSG_MAX_SIZE);
+  GVariantVectors body_vectors;
+  struct kdbus_cmd_send send;
 
-  g_return_val_if_fail (G_IS_KDBUS (kdbus), -1);
+  g_return_val_if_fail (G_IS_KDBUS_WORKER (kdbus), -1);
 
-  if (g_cancellable_set_error_if_cancelled (cancellable, error))
-    return -1;
+  /* fill in as we go... */
+  memset (msg, 0, sizeof (struct kdbus_msg));
+  msg->size = sizeof (struct kdbus_msg);
+  msg->payload_type = KDBUS_PAYLOAD_DBUS;
+  msg->src_id = kdbus->unique_id;
+  msg->cookie = g_dbus_message_get_serial(message);
 
+  /* Message destination */
+  {
+    const gchar *dst_name;
 
-  /*
-   * If systemd-bus-driverd from systemd isn't available
-   * try to process the bus driver messages locally
-   */
-#ifndef SYSTEMD_BUS_DRIVERD
-  if (g_strcmp0(g_dbus_message_get_destination(dbus_msg), "org.freedesktop.DBus") == 0)
-    {
-      if (g_kdbus_bus_driver (worker, kdbus, dbus_msg))
-        return blob_size;
-      else
-        return -1;
-    }
-#else
-    if ((g_strcmp0(g_dbus_message_get_destination(dbus_msg), "org.freedesktop.DBus") == 0) &&
-        (g_strcmp0(g_dbus_message_get_member(dbus_msg), "Hello") == 0))
+    dst_name = g_dbus_message_get_destination (message);
+
+    if (dst_name != NULL)
       {
-        g_kdbus_take_fd (kdbus);
+        if (g_dbus_is_unique_name (dst_name))
+          {
+            if (dst_name[1] != '1' || dst_name[2] != '.')
+              {
+                g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
+                             "Invalid unique D-Bus name '%s'", dst_name);
+                return FALSE;
+              }
+
+            /* We already know that it passes the checks for unique
+             * names, so no need to perform error checking on strtoull.
+             */
+            msg->dst_id = strtoull (dst_name + 3, NULL, 10);
+            dst_name = NULL;
+          }
+        else
+          {
+            g_kdbus_msg_append_item (msg, KDBUS_ITEM_DST_NAME, dst_name, strlen (dst_name) + 1);
+            msg->dst_id = KDBUS_DST_ID_NAME;
+          }
       }
-#endif
+    else
+      msg->dst_id = KDBUS_DST_ID_BROADCAST;
+  }
 
+  /* File descriptors */
+  {
+    GUnixFDList *fd_list;
 
-  /*
-   * check destination
-   */
-  if ((name = g_dbus_message_get_destination(dbus_msg)))
-    {
-      dst_id = KDBUS_DST_ID_NAME;
-      if ((name[0] == ':') && (name[1] == '1') && (name[2] == '.'))
-        {
-          dst_id = strtoull(&name[3], NULL, 10);
-          name=NULL;
-        }
-    }
+    fd_list = g_dbus_message_get_unix_fd_list (message);
 
+    if (fd_list != NULL)
+      {
+        const gint *fds;
+        gint n_fds;
 
-  /*
-   * check whether we should use memfd transport (for messages > 512K)
-   */
-  if (name && (blob_size > 524288))
-    use_memfd = TRUE;
+        fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
 
+        if (n_fds)
+          g_kdbus_msg_append_item (msg, KDBUS_ITEM_FDS, fds, sizeof (gint) * n_fds);
+      }
+  }
+
+  /* Message body */
+  {
+    struct dbus_fixed_header fh;
+    GHashTableIter header_iter;
+    GVariantBuilder builder;
+    gpointer key, value;
+    GVariant *parts[3];
+    GVariant *body;
+
+    fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B';
+    fh.type = g_dbus_message_get_message_type (message);
+    fh.flags = g_dbus_message_get_flags (message);
+    fh.version = 2;
+    fh.reserved = 0;
+    fh.serial = g_dbus_message_get_serial (message);
+    parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL);
+
+    g_dbus_message_init_header_iter (message, &header_iter);
+    g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE);
+
+    /* We set the sender field to the correct value for ourselves */
+    g_variant_builder_add (&builder, "{tv}",
+                           (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
+                           g_variant_new_printf (":1.%"G_GUINT64_FORMAT, kdbus->unique_id));
+
+    while (g_hash_table_iter_next (&header_iter, &key, &value))
+      {
+        guint64 key_int = (gsize) key;
+
+        switch (key_int)
+          {
+            /* These are the normal header fields that get passed
+             * straight through.
+             */
+            case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
+            case G_DBUS_MESSAGE_HEADER_FIELD_INTERFACE:
+            case G_DBUS_MESSAGE_HEADER_FIELD_MEMBER:
+            case G_DBUS_MESSAGE_HEADER_FIELD_ERROR_NAME:
+            case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
+            case G_DBUS_MESSAGE_HEADER_FIELD_DESTINATION:
+              g_variant_builder_add (&builder, "{tv}", key_int, value);
+              /* This is a little bit gross.
+               *
+               * We must send the header part of the message in a single
+               * vector as per kdbus rules, but the GVariant serialiser
+               * code will split any item >= 128 bytes into its own
+               * vector to save the copy.
+               *
+               * No header field should be that big anyway... right?
+               */
+              g_assert_cmpint (g_variant_get_size (value), <, 128);
+              continue;
+
+            /* We send this one unconditionally, but set it ourselves */
+            case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
+              continue;
+
+            /* We don't send these at all in GVariant format */
+            case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
+            case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
+              continue;
+
+            default:
+              g_assert_not_reached ();
+          }
+      }
+    parts[1] = g_variant_builder_end (&builder);
 
-  /*
-   * check and set message size
-   */
-  kmsg_size = sizeof(struct kdbus_msg);
-  if (use_memfd)
-    {
-      kmsg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));   /* header */
-      kmsg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); /* body */
-    }
-  else
-    kmsg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec));   /* header + body */
+    body = g_dbus_message_get_body (message);
+    if (!body)
+      body = g_variant_new ("()");
+    parts[2] = g_variant_new_variant (body);
 
-  if (fd_list != NULL && g_unix_fd_list_get_length (fd_list) > 0)
-    kmsg_size += ALIGN8(G_STRUCT_OFFSET(struct kdbus_item, fds) + sizeof(int) * g_unix_fd_list_get_length(fd_list));
+    body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts)));
+    GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, &body_vectors);
 
-  if (name)
-    kmsg_size += KDBUS_ITEM_SIZE(strlen(name) + 1);
-  else if (dst_id == KDBUS_DST_ID_BROADCAST)
-    kmsg_size += ALIGN8(G_STRUCT_OFFSET(struct kdbus_item, bloom_filter) +
-                        G_STRUCT_OFFSET(struct kdbus_bloom_filter, data) +
-                        kdbus->priv->bloom_size);
+    /* Sanity check to make sure the header is really contiguous:
+     *
+     *  - we must have at least one vector in the output
+     *  - the first vector must completely contain at least the header
+     */
+    g_assert_cmpint (body_vectors.vectors->len, >, 0);
+    g_assert_cmpint (g_array_index (body_vectors.vectors, GVariantVector, 0).size, >=,
+                     g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]));
 
-  kmsg = malloc(kmsg_size);
-  if (!kmsg)
-    g_error ("[KDBUS] kmsg malloc error");
+    g_variant_unref (body);
+  }
 
+  {
+    guint i;
 
-  /*
-   * set message header
-   */
-  memset(kmsg, 0, kmsg_size);
-  kmsg->size = kmsg_size;
-  kmsg->payload_type = KDBUS_PAYLOAD_DBUS;
-  kmsg->dst_id = name ? 0 : dst_id;
-  kmsg->src_id = kdbus->priv->unique_id;
-  kmsg->cookie = g_dbus_message_get_serial(dbus_msg);
-  kmsg->priority = 0;
+    for (i = 0; i < body_vectors.vectors->len; i++)
+      {
+        GVariantVector vector = g_array_index (body_vectors.vectors, GVariantVector, i);
 
+        if (vector.gbytes)
+          {
+            gint fd;
 
-  /*
-   * set message flags
-   */
-  kmsg->flags = ((g_dbus_message_get_flags (dbus_msg) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
-                ((g_dbus_message_get_flags (dbus_msg) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
+            fd = g_bytes_get_zero_copy_fd (vector.gbytes);
 
-  if ((kmsg->flags) & KDBUS_MSG_FLAGS_EXPECT_REPLY)
-    kmsg->timeout_ns = 2000000000;
-  else
-    kmsg->cookie_reply = g_dbus_message_get_reply_serial(dbus_msg);
+            if (fd >= 0)
+              {
+                const guchar *bytes_data;
+                gsize bytes_size;
+
+                bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size);
 
+                if (bytes_data <= vector.data.pointer && vector.data.pointer + vector.size <= bytes_data + bytes_size)
+                  {
+                    if (!g_kdbus_msg_append_payload_memfd (msg, fd, vector.data.pointer - bytes_data, vector.size))
+                      goto need_compact;
+                  }
+                else
+                  {
+                    if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
+                      goto need_compact;
+                  }
+              }
+            else
+              {
+                if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
+                  goto need_compact;
+              }
+          }
+        else
+          if (!g_kdbus_msg_append_payload_vec (msg, body_vectors.extra_bytes->data + vector.data.offset, vector.size))
+            goto need_compact;
+      }
+  }
 
   /*
-   * append payload
+   * set message flags
    */
-  item = kmsg->items;
-  if (use_memfd)
-    {
-      gint32 body_size;
-
-      if (!g_kdbus_alloc_memfd (kdbus))
-        g_error ("Can't alloc memfd");
-
-      /* split blob to header and body */
-      memcpy (&body_size, blob+4, 4);
-      body_size = GINT32_FROM_LE (body_size);
-
-      /*
-       * write blob and seal
-       * We should build up whole messsage directly in memfd object without
-       * making copy but memfd will be completly reworked soon [1],
-       * so we're still waiting for this:
-       *
-       * [1] https://code.google.com/p/d-bus/source/browse/TODO
-       */
-
-      if (write(kdbus->priv->memfd, blob + (blob_size-body_size), body_size) <= 0)
-        g_error ("Can't write data to memfd object");
-
-      if (ioctl(kdbus->priv->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) < 0)
-        g_error ("Can't seal memfd object");
-
-      /* message header in its entirety must be contained in a single PAYLOAD_VEC item */
-      g_kdbus_append_payload_vec (&item, blob, blob_size - body_size);
-      /* send body as as PAYLOAD_MEMFD item */
-      g_kdbus_append_payload_memfd (&item, kdbus->priv->memfd, body_size);
-    }
+  msg->flags = ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_EXPECT_REPLY) |
+                ((g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START) ? KDBUS_MSG_NO_AUTO_START : 0);
+
+  if ((msg->flags) & KDBUS_MSG_EXPECT_REPLY)
+    msg->timeout_ns = 1000LU * g_get_monotonic_time() + KDBUS_TIMEOUT_NS;
   else
-    {
-      /* if we don't use memfd, send whole message as a PAYLOAD_VEC item */
-      g_kdbus_append_payload_vec (&item, blob, blob_size);
-    }
+    msg->cookie_reply = g_dbus_message_get_reply_serial(message);
 
 
   /*
-   * append destination or bloom filters
-   */
-  if (name)
-    g_kdbus_append_destination (&item, name, strlen(name));
-  else if (dst_id == KDBUS_DST_ID_BROADCAST)
+  if (dst_id == KDBUS_DST_ID_BROADCAST)
     {
       struct kdbus_bloom_filter *bloom_filter;
 
-      bloom_filter = g_kdbus_append_bloom (&item, kdbus->priv->bloom_size);
-      g_kdbus_setup_bloom (kdbus, dbus_msg, bloom_filter);
+      bloom_filter = g_kdbus_append_bloom (&item, kdbus->bloom_size);
+      g_kdbus_setup_bloom (kdbus, message, bloom_filter);
     }
+    */
 
-
-  /*
-   * append fds if any
-   */
-  if (fd_list != NULL && g_unix_fd_list_get_length (fd_list) > 0)
-    g_kdbus_append_fds (&item, fd_list);
-
+  send.size = sizeof (send);
+  send.flags = 0;
+  send.msg_address = (gsize) msg;
 
   /*
    * send message
    */
-again:
-  if (ioctl(kdbus->priv->fd, KDBUS_CMD_MSG_SEND, kmsg))
+//again:
+  if (ioctl(kdbus->fd, KDBUS_CMD_SEND, &send))
     {
+/*
       GString *error_name;
       error_name = g_string_new (NULL);
 
@@ -2578,13 +2592,96 @@ again:
 
       g_print ("[KDBUS] ioctl error sending kdbus message:%d (%m)\n",errno);
       g_set_error (error, G_IO_ERROR, g_io_error_from_errno(errno), _("Error sending message - KDBUS_CMD_MSG_SEND error"));
-      return -1;
+*/
+      perror("ioctl send");
+      g_error ("IOCTL SEND: %d\n",errno);
+      return FALSE;
+    }
+
+  return TRUE;
+
+need_compact:
+  /* We end up here if:
+   *  - too many kdbus_items
+   *  - too large kdbus_msg size
+   *  - too much vector data
+   *
+   * We need to compact the message before sending it.
+   */
+  g_assert_not_reached ();
+}
+
+GKDBusWorker *
+g_kdbus_worker_new (const gchar  *address,
+                    GError      **error)
+{
+  GKDBusWorker *worker;
+
+  worker = g_object_new (G_TYPE_KDBUS_WORKER, NULL);
+  if (!_g_kdbus_open (worker, address, error))
+    {
+      g_object_unref (worker);
+      return NULL;
     }
 
-  if (kdbus->priv->memfd >= 0)
-    close(kdbus->priv->memfd);
+  return worker;
+}
+
+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);
 
-  free(kmsg);
+  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);
 
-  return blob_size;
+  g_source_attach (worker->source, worker->context);
+}
+
+gboolean
+g_kdbus_worker_send_message (GKDBusWorker  *worker,
+                             GDBusMessage  *message,
+                             GError       **error)
+{
+  return _g_kdbus_send (worker, message, error);
+}
+
+void
+g_kdbus_worker_stop (GKDBusWorker *worker)
+{
+}
+
+void
+g_kdbus_worker_flush_sync (GKDBusWorker *worker)
+{
+}
+
+void
+g_kdbus_worker_close (GKDBusWorker       *worker,
+                      GCancellable       *cancellable,
+                      GSimpleAsyncResult *result)
+{
 }