summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarol Lewandowski <k.lewandowsk@samsung.com>2024-02-07 16:37:21 +0100
committerKarol Lewandowski <k.lewandowsk@samsung.com>2024-02-14 11:08:57 +0100
commit06011c7637b5411ecc64219fae7733e3fca2db82 (patch)
treef7efcd1033d1ced9a3e5c5da6fdb34f09597928f
parentac1188828a7660cfb2345d3d86ef2155936058c2 (diff)
downloadglib-06011c7637b5411ecc64219fae7733e3fca2db82.tar.gz
glib-06011c7637b5411ecc64219fae7733e3fca2db82.tar.bz2
glib-06011c7637b5411ecc64219fae7733e3fca2db82.zip
tizen: Import kdbus patchesglib_2.78.4_tizen
This commit imports kdbus support patches to main tree. The kdbus is applied and built only at last stage of the build, leaving regular build without any kdbus and glib-core (gvariant) patches.
-rw-r--r--packaging/glib2.spec65
-rw-r--r--packaging/kdbus1.patch4424
-rw-r--r--packaging/kdbus2.patch8287
3 files changed, 12774 insertions, 2 deletions
diff --git a/packaging/glib2.spec b/packaging/glib2.spec
index c3b9299eb..52e577fc1 100644
--- a/packaging/glib2.spec
+++ b/packaging/glib2.spec
@@ -1,11 +1,12 @@
# Do not create provides from extension .so files because the main package
# should anchor any reverse-dependencies
-%global __provides_exclude_from ^(.*\\.extension-symbolic)$
+%global __provides_exclude_from ^(.*\\.extension-kdbus|.*\\.extension-symbolic)$
%define baseline 2.78.4
%define with_systemtap 0
%define keepstatic 1
%define build_dir _build
+%define build_dir_kdbus _build_kdbus
%define build_dir_symbolic _build_symbolic
%define relative_dbus_tests_base_dir %{_libdir}/dbus-tests
@@ -30,6 +31,8 @@ Source6: macros.glib2
Source7: gtk-doc.m4
Source8: LICENSE.MIT
Source99: baselibs.conf
+Source200: kdbus1.patch
+Source201: kdbus2.patch
Source1001: glib2.manifest
BuildRequires: automake
BuildRequires: fdupes
@@ -51,6 +54,8 @@ BuildRequires: pkgconfig(libffi)
BuildRequires: pkgconfig(libpcre2-8) >= 10.32
BuildRequires: pkgconfig(mount) >= 2.28
BuildRequires: pkgconfig(zlib)
+# Enable support for libdbuspolicy (only for kdbus transport)
+BuildRequires: pkgconfig(libdbuspolicy1)
%description
GLib is a general-purpose utility library, which provides many useful
@@ -164,9 +169,25 @@ a main loop abstraction, and so on.
GIO provides a modern, easy-to-use VFS API.
+%package -n libgio-extension-kdbus
+Summary: Extension for libgio to support KDBUS in Tizen
+Requires: libgio = %{version}-%{release}
+
+%description -n libgio-extension-kdbus
+This modifies libgio to support KDBUS in Tizen.
+
+%package -n libglib-extension-kdbus
+Summary: Extension for libgio to support KDBUS in Tizen
+Requires: libgio-extension-kdbus = %{version}-%{release}
+Conflicts: libglib-extension-symbolic
+
+%description -n libglib-extension-kdbus
+This modifies libgio to support KDBUS in Tizen.
+
%package -n libglib-extension-symbolic
Summary: Extension for libglib to allow the override of internal defined global function.
Requires: libglib = %{version}-%{release}
+Conflicts: libglib-extension-kdbus
%description -n libglib-extension-symbolic
This modifies libglib to allow the override of internal defined global function.
@@ -235,6 +256,18 @@ fi
-Db_pie=true
%meson_build
+# Configure kdbus extension build
+%{__patch} -p1 < %{SOURCE200}
+%{__patch} -p1 < %{SOURCE201}
+%define _vpath_builddir %{build_dir_kdbus}
+%meson \
+ --default-library=both \
+ -Dkdbus=true \
+ -Dlibdbuspolicy=true \
+ -Dselinux=disabled \
+ -Db_pie=true
+%meson_build
+
# compile test-runner for 'dbus-integration-test' framework
%__cc %{_builddir}/%{name}-%{version}/test-runner.c -DTESTS_DIR='"'%{relative_dbus_tests_base_dir}/test-suites/glib-tests'"' -fPIC -pie -o %{_builddir}/%{name}-%{version}/glib-tests
@@ -244,7 +277,12 @@ fi
%meson_install
for FILE in %{buildroot}%{_libdir}/libglib*; do mv "$FILE" "$FILE.extension-symbolic"; done
-# normal install
+# kdbus extension install and gather required files
+%define _vpath_builddir %{build_dir_kdbus}
+%meson_install
+for FILE in %{buildroot}%{_libdir}/libgio*[0-9a] %{buildroot}%{_libdir}/libglib*[0-9a]; do mv "$FILE" "$FILE.extension-kdbus"; done
+
+# normal install overwriting kdbus extension install
%define _vpath_builddir %{build_dir}
%meson_install
%find_lang glib20 %{?no_lang_C}
@@ -324,6 +362,7 @@ chmod 755 %{buildroot}%{_bindir}/*
%manifest %{name}.manifest
%defattr(-, root, root)
%license COPYING
+%exclude %{_libdir}/libglib*.so.*.extension-kdbus
%exclude %{_libdir}/libglib*.so.*.extension-symbolic
%{_libdir}/libglib*.so.*
@@ -349,6 +388,7 @@ chmod 755 %{buildroot}%{_bindir}/*
%manifest %{name}.manifest
%defattr(-, root, root)
%license COPYING
+%exclude %{_libdir}/libgio*.so.*.extension-kdbus
%{_libdir}/libgio*.so.*
%dir %{_libdir}/gio
%dir %{_libdir}/gio/modules
@@ -359,6 +399,26 @@ chmod 755 %{buildroot}%{_bindir}/*
%lang_package -f glib20
+%post -n libgio-extension-kdbus
+pushd %{_libdir}
+for FILE in libgio*.so.*.extension-kdbus; do mv "$FILE" "${FILE%.extension-kdbus}"; done
+popd
+
+%files -n libgio-extension-kdbus
+%manifest %{name}.manifest
+%license COPYING
+%{_libdir}/libgio*.so.*.extension-kdbus
+
+%post -n libglib-extension-kdbus
+pushd %{_libdir}
+for FILE in libglib*.so.*.extension-kdbus; do mv "$FILE" "${FILE%.extension-kdbus}"; done
+popd
+
+%files -n libglib-extension-kdbus
+%manifest %{name}.manifest
+%license COPYING
+%{_libdir}/libglib*.so.*.extension-kdbus
+
%post -n libglib-extension-symbolic
pushd %{_libdir}
for FILE in libglib*.so.*.extension-symbolic; do mv "$FILE" "${FILE%.extension-symbolic}"; done
@@ -418,6 +478,7 @@ popd
%defattr(-,root,root)
%license COPYING
%{_libdir}/lib*.a
+%{_libdir}/lib*.a.extension-kdbus
%{_libdir}/lib*.a.extension-symbolic
%files tests
diff --git a/packaging/kdbus1.patch b/packaging/kdbus1.patch
new file mode 100644
index 000000000..3a8ab17a3
--- /dev/null
+++ b/packaging/kdbus1.patch
@@ -0,0 +1,4424 @@
+From 43d5579bb0a0394f16c655616ab32adcadc38e4a Mon Sep 17 00:00:00 2001
+From: Karol Lewandowski <k.lewandowsk@samsung.com>
+Date: Tue, 6 Feb 2024 16:00:05 +0100
+Subject: [PATCH] tizen: Import glib & gio adjustments needed for kdbus
+ upstream
+
+This commit imports multiple squashed glib core code needed to support kdbus.
+Originally, the code has been authored by Ryan Lortie and Daving King and
+can be found at:
+
+ https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+
+Original commit logs can be found below (for identification purspose).
+
+.
+
+commit 52ca5a3d8fc7c6a0407f24158c7c5de57aac564b
+Author: Ryan Lortie <desrt@desrt.ca>
+Date: Fri Aug 21 16:26:03 2015 +0200
+
+ gvariant: substantial rework for kdbus purposes
+
+ This patch is 'squashed' version of Ryan Lortie's patches from
+ wip/kdbus-junk branch [1].
+
+ Main changes:
+ - add GVariantVectors utility struct,
+ - support serialising/deserialising to/from GVariantVectors,
+ - support for single precision floats,
+ - GVariantVectors serialisation tests,
+
+ [1] https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: If70bd22337b5a9cddac545dce45474eba542d0de
+
+commit 5fa9680246ae47f5a2cdb62a672fcc3ddc0a629e
+Author: Ryan Lortie <desrt@desrt.ca>
+Date: Fri Aug 21 16:15:25 2015 +0200
+
+ glib-unix: add new internal header glib-linux.h
+
+ This defines some constants and syscall wrappers for features which may
+ not yet have appeared in the libc.
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: I7e83dadf8be9121f91fa0743bb8c1c612f923e13
+
+commit 6bc456e19258c081a65bdf7d6de27a967300c00c
+Author: David King <dking@redhat.com>
+Date: Fri Aug 21 16:10:59 2015 +0200
+
+ glib-unix: add function to ensure an fd is sealed
+
+ Add a function that checks if a fd is sealed and, if it's not, seals it.
+
+ On Linux this is more or less an operation on memfd. On other systems,
+ it currently always returns FALSE.
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: I021e40249c2cdd0f1137f6f9f3813f5d4e5083c5
+
+commit e3bcd736dbf8ac67440d839f767d4720450fa686
+Author: Ryan Lortie <desrt@desrt.ca>
+Date: Fri Aug 21 16:06:15 2015 +0200
+
+ gbytes: substantial rework for kdbus purposes
+
+ This patch is 'squashed' version of Ryan Lortie's patches from
+ wip/kdbus-junk branch [1].
+
+ Main changes:
+
+ - introduce a new type of GBytes: 'inline' - this allows us to
+ make a single allocation instead of two in the g_bytes_new() case,
+
+ - new g_bytes_take_zero_copy_fd() function - function takes a memfd,
+ seals it, and creates a GBytes based on it,
+
+ - add g_bytes_get_zero_copy_fd() function - add a way to get the
+ zero-copy fd back out of a GBytes that was created from one.
+
+ [1] https://git.gnome.org/browse/glib/log/?h=wip/kdbus-junk
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+---
+ docs/reference/glib/glib-sections.txt.in | 6 +
+ docs/reference/glib/gvariant-text.xml | 7 +-
+ glib/gbytes.c | 380 +++++++++---
+ glib/gbytes.h | 12 +
+ glib/glib-linux.h | 129 ++++
+ glib/glib-private.c | 4 +
+ glib/glib-private.h | 17 +
+ glib/glib-unix.c | 90 +++
+ glib/glib-unix.h | 3 +
+ glib/gmessages.h | 84 ++-
+ glib/gtestutils.c | 19 +
+ glib/gtestutils.h | 2 +
+ glib/gvariant-core.c | 722 ++++++++++++++---------
+ glib/gvariant-core.h | 12 +-
+ glib/gvariant-parser.c | 38 +-
+ glib/gvariant-serialiser.c | 555 +++++++++++++++--
+ glib/gvariant-serialiser.h | 28 +-
+ glib/gvariant-vectors.c | 248 ++++++++
+ glib/gvariant-vectors.h | 63 ++
+ glib/gvariant.c | 233 ++++++--
+ glib/gvariant.h | 5 +
+ glib/gvarianttype.c | 13 +-
+ glib/gvarianttype.h | 9 +
+ glib/gvarianttypeinfo.c | 4 +-
+ glib/meson.build | 1 +
+ glib/tests/bytes.c | 52 +-
+ glib/tests/gvariant.c | 168 +++++-
+ 27 files changed, 2404 insertions(+), 500 deletions(-)
+ create mode 100644 glib/glib-linux.h
+ create mode 100644 glib/gvariant-vectors.c
+ create mode 100644 glib/gvariant-vectors.h
+
+diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
+index 5258544..8b5efd4 100644
+--- a/docs/reference/glib/glib-sections.txt.in
++++ b/docs/reference/glib/glib-sections.txt.in
+@@ -1129,6 +1129,8 @@ g_set_printerr_handler
+ <SUBSECTION>
+ g_return_if_fail
+ g_return_val_if_fail
++g_return_if_fail_se
++g_return_val_if_fail_se
+ g_return_if_reached
+ g_return_val_if_reached
+ g_warn_if_fail
+@@ -2304,6 +2306,7 @@ GUnixFDSourceFunc
+ g_unix_fd_add
+ g_unix_fd_add_full
+ g_unix_fd_source_new
++g_unix_fd_ensure_zero_copy_safe
+
+ <SUBSECTION>
+ g_unix_get_passwd_entry
+@@ -2784,12 +2787,14 @@ g_byte_array_free_to_bytes
+ GBytes
+ g_bytes_new
+ g_bytes_new_take
++g_bytes_new_take_zero_copy_fd
+ g_bytes_new_static
+ g_bytes_new_with_free_func
+ g_bytes_new_from_bytes
+ g_bytes_get_data
+ g_bytes_get_region
+ g_bytes_get_size
++g_bytes_get_zero_copy_fd
+ g_bytes_hash
+ g_bytes_equal
+ g_bytes_compare
+@@ -3368,6 +3373,7 @@ g_test_rand_double
+ g_test_rand_double_range
+
+ g_assert
++g_assert_se
+ g_assert_not_reached
+
+ g_assert_cmpstr
+diff --git a/docs/reference/glib/gvariant-text.xml b/docs/reference/glib/gvariant-text.xml
+index 55d476e..51e9567 100644
+--- a/docs/reference/glib/gvariant-text.xml
++++ b/docs/reference/glib/gvariant-text.xml
+@@ -224,6 +224,7 @@
+ <literal>handle</literal>,
+ <literal>int64</literal>,
+ <literal>uint64</literal>,
++ <literal>float</literal>,
+ <literal>double</literal>,
+ <literal>string</literal>,
+ <literal>objectpath</literal>,
+@@ -534,9 +535,9 @@
+ Type keywords can be seen as more verbose (and more legible) versions of a common subset of the type codes.
+ The type keywords <literal>boolean</literal>, <literal>byte</literal>, <literal>int16</literal>,
+ <literal>uint16</literal>, <literal>int32</literal>, <literal>uint32</literal>, <literal>handle</literal>,
+- <literal>int64</literal>, <literal>uint64</literal>, <literal>double</literal>, <literal>string</literal>,
+- <literal>objectpath</literal> and literal <literal>signature</literal> are each exactly equivalent to their
+- corresponding type code.
++ <literal>int64</literal>, <literal>uint64</literal>, <literal>float</literal>, <literal>double</literal>,
++ <literal>string</literal>, <literal>objectpath</literal> and literal <literal>signature</literal> are each
++ exactly equivalent to their corresponding type code.
+ </para>
+ <para>
+ Type codes are an <literal>@</literal> ("at" sign) followed by a definite GVariant type string. Some
+diff --git a/glib/gbytes.c b/glib/gbytes.c
+index 380e39f..72fa764 100644
+--- a/glib/gbytes.c
++++ b/glib/gbytes.c
+@@ -35,6 +35,14 @@
+ #include <glib/grefcount.h>
+
+ #include <string.h>
++#include <errno.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++
++#ifdef G_OS_UNIX
++#include "glib-unix.h"
++#include <sys/mman.h>
++#endif
+
+ /**
+ * GBytes:
+@@ -70,13 +78,75 @@
+ /* Keep in sync with glib/tests/bytes.c */
+ struct _GBytes
+ {
+- gconstpointer data; /* may be NULL iff (size == 0) */
+- gsize size; /* may be 0 */
++ gsize size;
+ gatomicrefcount ref_count;
+- GDestroyNotify free_func;
+- gpointer user_data;
++ gint type_or_fd;
+ };
+
++typedef struct
++{
++ GBytes bytes;
++#if GLIB_SIZEOF_SIZE_T == 4
++ guint pad;
++#endif
++
++ guchar data[1];
++} GBytesInline;
++
++/* important: the ->data field of GBytesInline should always be 'nicely
++ * aligned'.
++ */
++G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % (2 * sizeof (gpointer)) == 0);
++G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % 8 == 0);
++
++
++typedef struct
++{
++ GBytes bytes;
++
++ gpointer data;
++} GBytesData;
++
++typedef struct
++{
++ GBytesData data_bytes;
++
++ GDestroyNotify notify;
++ gpointer user_data;
++} GBytesNotify;
++
++#define G_BYTES_TYPE_INLINE (-1)
++#define G_BYTES_TYPE_STATIC (-2)
++#define G_BYTES_TYPE_FREE (-3)
++#define G_BYTES_TYPE_NOTIFY (-4)
++
++/* All bytes are either inline or subtypes of GBytesData */
++#define G_BYTES_IS_INLINE(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_INLINE)
++#define G_BYTES_IS_DATA(bytes) (!G_BYTES_IS_INLINE(bytes))
++
++/* More specific subtypes of GBytesData */
++#define G_BYTES_IS_STATIC(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_STATIC)
++#define G_BYTES_IS_FREE(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_FREE)
++#define G_BYTES_IS_NOTIFY(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_NOTIFY)
++
++/* we have a memfd if type_or_fd >= 0 */
++#define G_BYTES_IS_MEMFD(bytes) ((bytes)->type_or_fd >= 0)
++
++static gpointer
++g_bytes_allocate (guint struct_size,
++ guint type_or_fd,
++ gsize data_size)
++{
++ GBytes *bytes;
++
++ bytes = g_slice_alloc (struct_size);
++ bytes->size = data_size;
++ g_atomic_ref_count_init (&bytes->ref_count);
++ bytes->type_or_fd = type_or_fd;
++
++ return bytes;
++}
++
+ /**
+ * g_bytes_new:
+ * @data: (transfer none) (array length=size) (element-type guint8) (nullable):
+@@ -95,11 +165,75 @@ GBytes *
+ g_bytes_new (gconstpointer data,
+ gsize size)
+ {
++ GBytesInline *bytes;
++
+ g_return_val_if_fail (data != NULL || size == 0, NULL);
+
+- return g_bytes_new_take (g_memdup2 (data, size), size);
++ bytes = g_bytes_allocate (G_STRUCT_OFFSET (GBytesInline, data[size]), G_BYTES_TYPE_INLINE, size);
++ memcpy (bytes->data, data, size);
++
++ return (GBytes *) bytes;
++}
++
++/**
++ * g_bytes_new_take_zero_copy_fd:
++ * @fd: a file descriptor capable of being zero-copy-safe
++ *
++ * Creates a new #GBytes from @fd.
++ *
++ * @fd must be capable of being made zero-copy-safe. In concrete terms,
++ * this means that a call to g_unix_fd_ensure_zero_copy_safe() on @fd
++ * will succeed. This call will be made before returning.
++ *
++ * This call consumes @fd, transferring ownership to the returned
++ * #GBytes.
++ *
++ * Returns: (transfer full): a new #GBytes
++ *
++ */
++#ifdef G_OS_UNIX
++GBytes *
++g_bytes_new_take_zero_copy_fd (gint fd)
++{
++ GBytes *bytes;
++ struct stat buf;
++
++ /* We already checked this is a memfd... */
++ g_assert_se (fstat (fd, &buf) == 0);
++
++ bytes = g_bytes_new_take_zero_copy_fd_size(fd, buf.st_size);
++
++ if (buf.st_size == 0)
++ {
++ g_assert_se (close (fd) == 0);
++ }
++
++ return bytes;
+ }
+
++GBytes *
++g_bytes_new_take_zero_copy_fd_size (gint fd, gsize size)
++{
++ GBytesData *bytes;
++
++ g_return_val_if_fail_se (g_unix_fd_ensure_zero_copy_safe (fd), NULL);
++
++ /* We already checked this is a memfd... */
++ if (size == 0)
++ {
++ return g_bytes_new (NULL, 0);
++ }
++
++ bytes = g_bytes_allocate (sizeof (GBytesData), fd, size);
++ bytes->data = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
++ if (bytes->data == MAP_FAILED)
++ /* this is similar to malloc() failing, so do the same... */
++ g_error ("mmap() on memfd failed: %s\n", g_strerror (errno));
++
++ return (GBytes *) bytes;
++}
++#endif /* G_OS_UNIX */
++
+ /**
+ * g_bytes_new_take:
+ * @data: (transfer full) (array length=size) (element-type guint8) (nullable):
+@@ -125,9 +259,13 @@ GBytes *
+ g_bytes_new_take (gpointer data,
+ gsize size)
+ {
+- return g_bytes_new_with_free_func (data, size, g_free, data);
+-}
++ GBytesData *bytes;
++
++ bytes = g_bytes_allocate (sizeof (GBytesData), G_BYTES_TYPE_FREE, size);
++ bytes->data = data;
+
++ return (GBytes *) bytes;
++}
+
+ /**
+ * g_bytes_new_static: (skip)
+@@ -148,7 +286,14 @@ GBytes *
+ g_bytes_new_static (gconstpointer data,
+ gsize size)
+ {
+- return g_bytes_new_with_free_func (data, size, NULL, NULL);
++ GBytesData *bytes;
++
++ g_return_val_if_fail (data != NULL || size == 0, NULL);
++
++ bytes = g_bytes_allocate (sizeof (GBytesData), G_BYTES_TYPE_STATIC, size);
++ bytes->data = (gpointer) data;
++
++ return (GBytes *) bytes;
+ }
+
+ /**
+@@ -179,18 +324,17 @@ g_bytes_new_with_free_func (gconstpointer data,
+ GDestroyNotify free_func,
+ gpointer user_data)
+ {
+- GBytes *bytes;
++ GBytesNotify *bytes;
+
+- g_return_val_if_fail (data != NULL || size == 0, NULL);
++ if (!free_func)
++ return g_bytes_new_static (data, size);
+
+- bytes = g_slice_new (GBytes);
+- bytes->data = data;
+- bytes->size = size;
+- bytes->free_func = free_func;
++ bytes = g_bytes_allocate (sizeof (GBytesNotify), G_BYTES_TYPE_NOTIFY, size);
++ bytes->data_bytes.data = (gpointer) data;
++ bytes->notify = free_func;
+ bytes->user_data = user_data;
+- g_atomic_ref_count_init (&bytes->ref_count);
+
+- return (GBytes *)bytes;
++ return (GBytes *) bytes;
+ }
+
+ /**
+@@ -221,6 +365,7 @@ g_bytes_new_from_bytes (GBytes *bytes,
+ gsize length)
+ {
+ gchar *base;
++ gchar *data;
+
+ /* Note that length may be 0. */
+ g_return_val_if_fail (bytes != NULL, NULL);
+@@ -231,18 +376,19 @@ g_bytes_new_from_bytes (GBytes *bytes,
+ if (offset == 0 && length == bytes->size)
+ return g_bytes_ref (bytes);
+
+- base = (gchar *)bytes->data + offset;
++ base = (gchar *)g_bytes_get_data (bytes, NULL) + offset;
+
+ /* Avoid referencing intermediate GBytes. In practice, this should
+ * only loop once.
+ */
+- while (bytes->free_func == (gpointer)g_bytes_unref)
+- bytes = bytes->user_data;
++ while (((GBytesNotify*)bytes)->notify == (gpointer)g_bytes_unref)
++ bytes = ((GBytesNotify*)bytes)->user_data;
+
++ data = (gchar *)g_bytes_get_data (bytes, NULL);
+ g_return_val_if_fail (bytes != NULL, NULL);
+- g_return_val_if_fail (base >= (gchar *)bytes->data, NULL);
+- g_return_val_if_fail (base <= (gchar *)bytes->data + bytes->size, NULL);
+- g_return_val_if_fail (base + length <= (gchar *)bytes->data + bytes->size, NULL);
++ g_return_val_if_fail (base >= data, NULL);
++ g_return_val_if_fail (base <= data + bytes->size, NULL);
++ g_return_val_if_fail (base + length <= data + bytes->size, NULL);
+
+ return g_bytes_new_with_free_func (base, length,
+ (GDestroyNotify)g_bytes_unref, g_bytes_ref (bytes));
+@@ -268,12 +414,27 @@ g_bytes_new_from_bytes (GBytes *bytes,
+ */
+ gconstpointer
+ g_bytes_get_data (GBytes *bytes,
+- gsize *size)
++ gsize *size)
+ {
+ g_return_val_if_fail (bytes != NULL, NULL);
++
+ if (size)
+ *size = bytes->size;
+- return bytes->data;
++
++ if (G_BYTES_IS_DATA (bytes))
++ {
++ GBytesData *data_bytes = (GBytesData *) bytes;
++
++ return data_bytes->data;
++ }
++ else if (G_BYTES_IS_INLINE (bytes))
++ {
++ GBytesInline *inline_bytes = (GBytesInline *) bytes;
++
++ return inline_bytes->data;
++ }
++ else
++ g_assert_not_reached ();
+ }
+
+ /**
+@@ -295,6 +456,35 @@ g_bytes_get_size (GBytes *bytes)
+ return bytes->size;
+ }
+
++/**
++ * g_bytes_get_zero_copy_fd:
++ * @bytes: a #GBytes
++ *
++ * Gets the zero-copy fd from a #GBytes, if it has one.
++ *
++ * Returns -1 if @bytes was not created from a zero-copy fd.
++ *
++ * A #GBytes created with a zero-copy fd may have been internally
++ * converted into another type of #GBytes for any reason at all. This
++ * function may therefore return -1 at any time, even for a #GBytes that
++ * was created with g_bytes_new_take_zero_copy_fd().
++ *
++ * The returned file descriptor belongs to @bytes. Do not close it.
++ *
++ * Returns: a file descriptor, or -1
++ *
++ * Since: 2.44
++ */
++gint
++g_bytes_get_zero_copy_fd (GBytes *bytes)
++{
++ g_return_val_if_fail (bytes != NULL, -1);
++
++ if (G_BYTES_IS_MEMFD (bytes))
++ return bytes->type_or_fd;
++ else
++ return -1;
++}
+
+ /**
+ * g_bytes_ref:
+@@ -333,9 +523,52 @@ g_bytes_unref (GBytes *bytes)
+
+ if (g_atomic_ref_count_dec (&bytes->ref_count))
+ {
+- if (bytes->free_func != NULL)
+- bytes->free_func (bytes->user_data);
+- g_slice_free (GBytes, bytes);
++ switch (bytes->type_or_fd)
++ {
++ case G_BYTES_TYPE_STATIC:
++ /* data does not need to be freed */
++ g_slice_free (GBytesData, (GBytesData *) bytes);
++ break;
++
++ case G_BYTES_TYPE_INLINE:
++ /* data will be freed along with struct */
++ g_slice_free1 (G_STRUCT_OFFSET (GBytesInline, data[bytes->size]), bytes);
++ break;
++
++ case G_BYTES_TYPE_FREE:
++ {
++ GBytesData *data_bytes = (GBytesData *) bytes;
++
++ g_free (data_bytes->data);
++
++ g_slice_free (GBytesData, data_bytes);
++ break;
++ }
++
++ case G_BYTES_TYPE_NOTIFY:
++ {
++ GBytesNotify *notify_bytes = (GBytesNotify *) bytes;
++
++ /* We don't create GBytesNotify if callback was NULL */
++ (* notify_bytes->notify) (notify_bytes->user_data);
++
++ g_slice_free (GBytesNotify, notify_bytes);
++ break;
++ }
++
++ default:
++ {
++ GBytesData *data_bytes = (GBytesData *) bytes;
++
++ g_assert (bytes->type_or_fd >= 0);
++
++ g_assert_se (munmap (data_bytes->data, bytes->size) == 0);
++ g_assert_se (close (bytes->type_or_fd) == 0);
++
++ g_slice_free (GBytesData, data_bytes);
++ break;
++ }
++ }
+ }
+ }
+
+@@ -358,14 +591,22 @@ gboolean
+ g_bytes_equal (gconstpointer bytes1,
+ gconstpointer bytes2)
+ {
+- const GBytes *b1 = bytes1;
+- const GBytes *b2 = bytes2;
++ gconstpointer d1, d2;
++ gsize s1, s2;
+
+ g_return_val_if_fail (bytes1 != NULL, FALSE);
+ g_return_val_if_fail (bytes2 != NULL, FALSE);
+
+- return b1->size == b2->size &&
+- (b1->size == 0 || memcmp (b1->data, b2->data, b1->size) == 0);
++ d1 = g_bytes_get_data ((GBytes *) bytes1, &s1);
++ d2 = g_bytes_get_data ((GBytes *) bytes2, &s2);
++
++ if (s1 != s2)
++ return FALSE;
++
++ if (d1 == d2)
++ return TRUE;
++
++ return memcmp (d1, d2, s1) == 0;
+ }
+
+ /**
+@@ -384,14 +625,18 @@ g_bytes_equal (gconstpointer bytes1,
+ guint
+ g_bytes_hash (gconstpointer bytes)
+ {
+- const GBytes *a = bytes;
+- const signed char *p, *e;
++ const guchar *data;
++ const guchar *end;
++ gsize size;
+ guint32 h = 5381;
+
+ g_return_val_if_fail (bytes != NULL, 0);
+
+- for (p = (signed char *)a->data, e = (signed char *)a->data + a->size; p != e; p++)
+- h = (h << 5) + h + *p;
++ data = g_bytes_get_data ((GBytes *) bytes, &size);
++ end = data + size;
++
++ while (data != end)
++ h = (h << 5) + h + *(data++);
+
+ return h;
+ }
+@@ -422,43 +667,23 @@ gint
+ g_bytes_compare (gconstpointer bytes1,
+ gconstpointer bytes2)
+ {
+- const GBytes *b1 = bytes1;
+- const GBytes *b2 = bytes2;
++ gconstpointer d1, d2;
++ gsize s1, s2;
+ gint ret;
+
+ g_return_val_if_fail (bytes1 != NULL, 0);
+ g_return_val_if_fail (bytes2 != NULL, 0);
+
+- ret = memcmp (b1->data, b2->data, MIN (b1->size, b2->size));
+- if (ret == 0 && b1->size != b2->size)
+- ret = b1->size < b2->size ? -1 : 1;
+- return ret;
+-}
+-
+-static gpointer
+-try_steal_and_unref (GBytes *bytes,
+- GDestroyNotify free_func,
+- gsize *size)
+-{
+- gpointer result;
+-
+- if (bytes->free_func != free_func || bytes->data == NULL ||
+- bytes->user_data != bytes->data)
+- return NULL;
++ d1 = g_bytes_get_data ((GBytes *) bytes1, &s1);
++ d2 = g_bytes_get_data ((GBytes *) bytes2, &s2);
+
+- /* Are we the only reference? */
+- if (g_atomic_ref_count_compare (&bytes->ref_count, 1))
+- {
+- *size = bytes->size;
+- result = (gpointer)bytes->data;
+- g_slice_free (GBytes, bytes);
+- return result;
+- }
++ ret = memcmp (d1, d2, MIN (s1, s2));
++ if (ret == 0 && s1 != s2)
++ ret = s1 < s2 ? -1 : 1;
+
+- return NULL;
++ return ret;
+ }
+
+-
+ /**
+ * g_bytes_unref_to_data:
+ * @bytes: (transfer full): a #GBytes
+@@ -487,20 +712,26 @@ g_bytes_unref_to_data (GBytes *bytes,
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (size != NULL, NULL);
+
++
+ /*
+ * Optimal path: if this is was the last reference, then we can return
+ * the data from this GBytes without copying.
+ */
+-
+- result = try_steal_and_unref (bytes, g_free, size);
+- if (result == NULL)
++ if (G_BYTES_IS_FREE(bytes) && g_atomic_int_get (&bytes->ref_count) == 1)
+ {
+- /*
+- * Copy: Non g_malloc (or compatible) allocator, or static memory,
+- * so we have to copy, and then unref.
+- */
+- result = g_memdup2 (bytes->data, bytes->size);
++ GBytesData *data_bytes = (GBytesData *) bytes;
++
++ result = data_bytes->data;
+ *size = bytes->size;
++
++ g_slice_free (GBytesData, data_bytes);
++ }
++ else
++ {
++ gconstpointer data;
++
++ data = g_bytes_get_data (bytes, size);
++ result = g_memdup (data, *size);
+ g_bytes_unref (bytes);
+ }
+
+@@ -608,5 +839,8 @@ g_bytes_get_region (GBytes *bytes,
+ * 0 <= offset <= end_offset <= bytes->size
+ */
+
+- return ((guchar *) bytes->data) + offset;
+-}
+\ No newline at end of file
++ /* NB: The following line has been adapted to the kdbus rework by Mateusz
++ * and not by a competent programmer. Tread with caution!
++ */
++ return ((guchar *) g_bytes_get_data (bytes, NULL)) + offset;
++}
+diff --git a/glib/gbytes.h b/glib/gbytes.h
+index d934989..9cec5de 100644
+--- a/glib/gbytes.h
++++ b/glib/gbytes.h
+@@ -41,6 +41,15 @@ GLIB_AVAILABLE_IN_ALL
+ GBytes * g_bytes_new_take (gpointer data,
+ gsize size);
+
++#ifdef G_OS_UNIX
++GLIB_AVAILABLE_IN_2_44
++GBytes * g_bytes_new_take_zero_copy_fd (gint fd);
++
++GLIB_AVAILABLE_IN_2_44
++GBytes * g_bytes_new_take_zero_copy_fd_size (gint fd,
++ gsize size);
++#endif
++
+ GLIB_AVAILABLE_IN_ALL
+ GBytes * g_bytes_new_static (gconstpointer data,
+ gsize size);
+@@ -63,6 +72,9 @@ gconstpointer g_bytes_get_data (GBytes *bytes,
+ GLIB_AVAILABLE_IN_ALL
+ gsize g_bytes_get_size (GBytes *bytes);
+
++GLIB_AVAILABLE_IN_2_44
++gint g_bytes_get_zero_copy_fd (GBytes *bytes);
++
+ GLIB_AVAILABLE_IN_ALL
+ GBytes * g_bytes_ref (GBytes *bytes);
+
+diff --git a/glib/glib-linux.h b/glib/glib-linux.h
+new file mode 100644
+index 0000000..8f8882c
+--- /dev/null
++++ b/glib/glib-linux.h
+@@ -0,0 +1,129 @@
++/*
++ * Copyright © 2014 Canonical Limited
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the licence, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
++ *
++ * Author: Ryan Lortie <desrt@desrt.ca>
++ */
++
++#ifndef __GLIB_LINUX_H__
++#define __GLIB_LINUX_H__
++
++#include <errno.h>
++
++/* If we know that we are on Linux, add some features, even if they are
++ * not (yet) advertised in the glibc or kernel headers.
++ *
++ * This allows us to use functionality regardless of if it was available
++ * when GLib was compiled or not.
++ *
++ * We take care not to define these things on non-Linux systems where
++ * certain numeric values could mean something different.
++ *
++ * This file is populated on an as-needed basis.
++ *
++ * As things in this file filter into glibc and the distributions we can
++ * remove them from this file and add unconditional dependencies. Never
++ * add a configure.ac check in order to remove something from this file.
++ *
++ * import: include this header LAST
++ */
++
++#ifdef __linux__
++
++#define GLIB_LINUX
++
++#include <sys/syscall.h>
++
++static inline int
++glib_linux_enosys (void)
++{
++ errno = ENOSYS;
++ return -1;
++}
++
++/* futex */
++#include <linux/futex.h>
++
++static inline int
++glib_linux_futex (int *uaddr,
++ int op,
++ int val,
++ const struct timespec *timeout,
++ int *uaddr2,
++ int val3)
++{
++ return syscall (__NR_futex, uaddr, op, val, timeout, uaddr2, val3);
++}
++
++/* memfd */
++#ifndef MFD_CLOEXEC
++#define MFD_CLOEXEC 0x0001U
++#endif
++
++#ifndef MFD_ALLOW_SEALING
++#define MFD_ALLOW_SEALING 0x0002U
++#endif
++
++#ifndef __NR_memfd_create
++# if defined __x86_64__
++# define __NR_memfd_create 319
++# elif defined i386
++# define __NR_memfd_create 356
++# elif defined __arm__
++# define __NR_memfd_create 385
++# elif defined __aarch64__
++# define __NR_memfd_create 279
++# elif defined _MIPS_SIM
++# if _MIPS_SIM == _MIPS_SIM_ABI32
++# define __NR_memfd_create 4354
++# endif
++# if _MIPS_SIM == _MIPS_SIM_NABI32
++# define __NR_memfd_create 6318
++# endif
++# if _MIPS_SIM == _MIPS_SIM_ABI64
++# define __NR_memfd_create 5314
++# endif
++# endif
++#endif
++
++static inline int
++glib_linux_memfd_create (const char *name,
++ unsigned int flags)
++{
++#ifdef __NR_memfd_create
++ return syscall (__NR_memfd_create, name, flags);
++#else
++ return glib_linux_enosys ();
++#endif
++}
++
++/* Linux-specific fcntl() operations */
++#ifndef F_LINUX_SPECIFIC_BASE
++#define F_LINUX_SPECIFIC_BASE 1024
++#endif
++
++#ifndef F_ADD_SEALS
++#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
++#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
++
++#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
++#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
++#define F_SEAL_GROW 0x0004 /* prevent file from growing */
++#define F_SEAL_WRITE 0x0008 /* prevent writes */
++#endif
++
++#endif /* __linux __ */
++
++#endif /* __GLIB_LINUX_H__ */
+diff --git a/glib/glib-private.c b/glib/glib-private.c
+index 9f4887a..cceaf73 100644
+--- a/glib/glib-private.c
++++ b/glib/glib-private.c
+@@ -53,6 +53,10 @@ glib__private__ (void)
+ g_dir_open_with_errno,
+ g_dir_new_from_dirp,
+
++ g_variant_to_vectors,
++ g_variant_from_vectors,
++ g_variant_vectors_deinit,
++
+ glib_init,
+
+ #ifdef G_OS_WIN32
+diff --git a/glib/glib-private.h b/glib/glib-private.h
+index f49e38f..eb1ee50 100644
+--- a/glib/glib-private.h
++++ b/glib/glib-private.h
+@@ -22,6 +22,7 @@
+
+ #include <glib.h>
+ #include "gwakeup.h"
++#include "gvariant-vectors.h"
+ #include "gstdioprivate.h"
+
+ /* gcc defines __SANITIZE_ADDRESS__, clang sets the address_sanitizer
+@@ -120,6 +121,13 @@ g_end_ignore_leaks (void)
+ GMainContext * g_get_worker_context (void);
+ gboolean g_check_setuid (void);
+ GMainContext * g_main_context_new_with_next_id (guint next_id);
++void g_variant_to_vectors (GVariant *value,
++ GVariantVectors *vectors);
++GVariant * g_variant_from_vectors (const GVariantType *type,
++ GVariantVector *vectors,
++ gsize n_vectors,
++ gsize size,
++ gboolean trusted);
+
+ #if (defined (HAVE__SET_THREAD_LOCAL_INVALID_PARAMETER_HANDLER) || \
+ defined (HAVE__SET_INVALID_PARAMETER_HANDLER)) && \
+@@ -182,6 +190,15 @@ typedef struct {
+ guint flags);
+ GDir * (* g_dir_new_from_dirp) (gpointer dirp);
+
++ void (* g_variant_to_vectors) (GVariant *value,
++ GVariantVectors *vectors);
++ GVariant * (* g_variant_from_vectors) (const GVariantType *type,
++ GVariantVector *vectors,
++ gsize n_vectors,
++ gsize size,
++ gboolean trusted);
++ void (* g_variant_vectors_deinit) (GVariantVectors *vectors);
++
+ /* See glib-init.c */
+ void (* glib_init) (void);
+
+diff --git a/glib/glib-unix.c b/glib/glib-unix.c
+index df5726c..505923c 100644
+--- a/glib/glib-unix.c
++++ b/glib/glib-unix.c
+@@ -43,6 +43,37 @@ G_STATIC_ASSERT (G_ALIGNOF (GPid) == G_ALIGNOF (pid_t));
+ * might not be true everywhere. */
+ G_STATIC_ASSERT (O_NONBLOCK != FD_CLOEXEC);
+
++#include <fcntl.h>
++#include <sys/syscall.h>
++#include <unistd.h>
++
++#ifdef __linux__
++
++/* We want to support these features of Linux even when building GLib
++ * against older versions of system headers. This will allow us to
++ * 'automatically' start supporting a particular feature when GLib is
++ * used with a newer kernel, without recompile.
++ *
++ * This means that we're not changing functionality of GLib simply based
++ * on the set of headers we happen to compile against...
++ */
++
++#ifndef F_LINUX_SPECIFIC_BASE
++#define F_LINUX_SPECIFIC_BASE 1024
++#endif
++
++#ifndef F_ADD_SEALS
++#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
++#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
++
++#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
++#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
++#define F_SEAL_GROW 0x0004 /* prevent file from growing */
++#define F_SEAL_WRITE 0x0008 /* prevent writes */
++#endif
++
++#endif
++
+ /**
+ * SECTION:gunix
+ * @title: UNIX-specific utilities and integration
+@@ -404,6 +435,65 @@ g_unix_fd_add (gint fd,
+ return g_unix_fd_add_full (G_PRIORITY_DEFAULT, fd, condition, function, user_data, NULL);
+ }
+
++/**
++ * g_unix_fd_ensure_zero_copy_safe:
++ * @fd: a file descriptor
++ *
++ * Checks whether @fd can be use in zero-copy mode. On Linux, this checks that
++ * the descriptor is a memfd, created with memfd_create(), and tries to apply
++ * seals to it so that it can be safely consumed by another process. On other
++ * Unix systems, this function will fail.
++ *
++ * Returns: whether the fd is safe to use in zero-copy mode. Sealing of memfds
++ * is required for the @fd to be considered safe. If sealing fails, or
++ * memfds are not available, or the @fd is not a memfd, returns %FALSE
++ *
++ * Since: 2.44
++ **/
++gboolean
++g_unix_fd_ensure_zero_copy_safe (gint fd)
++{
++#ifdef F_GET_SEALS
++ gint seals;
++ const gint IMMUTABLE_SEALS = F_SEAL_WRITE |
++ F_SEAL_SHRINK |
++ F_SEAL_GROW |
++ F_SEAL_SEAL;
++
++ g_return_val_if_fail (fd >= 0, FALSE);
++
++ /* Seal the fd if possible (only on Linux 3.17+, and if the fd was created
++ * with memfd_create()). */
++ seals = fcntl (fd, F_GET_SEALS);
++ if (seals == -1)
++ {
++ g_debug ("Retrieving fd seals failed: %s", g_strerror (errno));
++ return FALSE;
++ }
++
++ /* Seal the fd, if it is not already. */
++ if ((seals & (IMMUTABLE_SEALS)) >= IMMUTABLE_SEALS)
++ {
++ g_debug ("%s", "fd already sealed");
++ }
++ else
++ {
++ gint error;
++
++ error = fcntl (fd, F_ADD_SEALS, IMMUTABLE_SEALS);
++ if (error == -1)
++ {
++ g_debug ("fd sealing failed: %s", g_strerror (errno));
++ return FALSE;
++ }
++ }
++
++ return TRUE;
++#else
++ return FALSE;
++#endif
++}
++
+ /**
+ * g_unix_get_passwd_entry:
+ * @user_name: the username to get the passwd file entry for
+diff --git a/glib/glib-unix.h b/glib/glib-unix.h
+index 7cf4f0d..205c9af 100644
+--- a/glib/glib-unix.h
++++ b/glib/glib-unix.h
+@@ -116,6 +116,9 @@ guint g_unix_fd_add (gint fd,
+ GUnixFDSourceFunc function,
+ gpointer user_data);
+
++GLIB_AVAILABLE_IN_2_44
++gboolean g_unix_fd_ensure_zero_copy_safe (gint fd);
++
+ GLIB_AVAILABLE_IN_2_64
+ struct passwd *g_unix_get_passwd_entry (const gchar *user_name,
+ GError **error);
+diff --git a/glib/gmessages.h b/glib/gmessages.h
+index eab6d06..825a587 100644
+--- a/glib/gmessages.h
++++ b/glib/gmessages.h
+@@ -606,13 +606,52 @@ GPrintFunc g_set_printerr_handler (GPrintFunc func);
+ * the result is usually that a critical message is logged and @val is
+ * returned from the current function.
+ *
+- * If `G_DISABLE_CHECKS` is defined then the check is not performed. You
+- * should therefore not depend on any side effects of @expr.
++ * If G_DISABLE_CHECKS is defined then the check is not performed. You
++ * should therefore not depend on any side effects of @expr. See
++ * g_return_if_fail_se() for a version that guarantees side effects.
+ *
+ * See g_return_if_fail() for guidance on how to debug failure of this check.
+ */
+ #define g_return_val_if_fail(expr,val) G_STMT_START{ (void)0; }G_STMT_END
+
++/**
++ * g_return_if_fail_se:
++ * @expr: the expression to check
++ *
++ * Verifies that the expression @expr, usually representing a
++ * precondition, evaluates to %TRUE.
++ *
++ * This is the same as g_return_if_fail() except that @expr is
++ * guaranteed to be evaluated, so it may contain side effects.
++ *
++ * Note: it is still undefined if this function will actually return or
++ * not, or if any side effects of @val will be evaluated. There is only
++ * a guarantee with respect to side effects of @expr.
++ *
++ * Since: 2.44
++ */
++#define g_return_if_fail_se(expr) ((void) (expr))
++
++/**
++ * g_return_val_if_fail_se:
++ * @expr: the expression to check
++ * @val: the value to return from the current function
++ * if the expression is not true
++ *
++ * Verifies that the expression @expr, usually representing a
++ * precondition, evaluates to %TRUE.
++ *
++ * This is the same as g_return_val_if_fail() except that @expr is
++ * guaranteed to be evaluated, so it may contain side effects.
++ *
++ * Note: it is still undefined if this function will actually return or
++ * not, or if any side effects of @val will be evaluated. There is only
++ * a guarantee with respect to side effects of @expr.
++ *
++ * Since: 2.44
++ */
++#define g_return_val_if_fail_se(expr,val) ((void) (expr))
++
+ /**
+ * g_return_if_reached:
+ *
+@@ -661,27 +700,26 @@ GPrintFunc g_set_printerr_handler (GPrintFunc func);
+ } \
+ } G_STMT_END
+
+-#define g_return_if_reached() \
+- G_STMT_START { \
+- g_log (G_LOG_DOMAIN, \
+- G_LOG_LEVEL_CRITICAL, \
+- "file %s: line %d (%s): should not be reached", \
+- __FILE__, \
+- __LINE__, \
+- G_STRFUNC); \
+- return; \
+- } G_STMT_END
+-
+-#define g_return_val_if_reached(val) \
+- G_STMT_START { \
+- g_log (G_LOG_DOMAIN, \
+- G_LOG_LEVEL_CRITICAL, \
+- "file %s: line %d (%s): should not be reached", \
+- __FILE__, \
+- __LINE__, \
+- G_STRFUNC); \
+- return (val); \
+- } G_STMT_END
++#define g_return_if_fail_se(expr) g_return_if_fail((expr))
++#define g_return_val_if_fail_se(expr,val) g_return_val_if_fail((expr), (val))
++
++#define g_return_if_reached() G_STMT_START{ \
++ g_log (G_LOG_DOMAIN, \
++ G_LOG_LEVEL_CRITICAL, \
++ "file %s: line %d (%s): should not be reached", \
++ __FILE__, \
++ __LINE__, \
++ G_STRFUNC); \
++ return; }G_STMT_END
++
++#define g_return_val_if_reached(val) G_STMT_START{ \
++ g_log (G_LOG_DOMAIN, \
++ G_LOG_LEVEL_CRITICAL, \
++ "file %s: line %d (%s): should not be reached", \
++ __FILE__, \
++ __LINE__, \
++ G_STRFUNC); \
++ return (val); }G_STMT_END
+
+ #endif /* !G_DISABLE_CHECKS */
+
+diff --git a/glib/gtestutils.c b/glib/gtestutils.c
+index 483275a..becb4a8 100644
+--- a/glib/gtestutils.c
++++ b/glib/gtestutils.c
+@@ -482,6 +482,25 @@
+ * in unit tests, otherwise the unit tests will be ineffective if compiled with
+ * `G_DISABLE_ASSERT`. Use g_assert_true() and related macros in unit tests
+ * instead.
++ *
++ * For a version which is guaranteed to evaluate side effects in @expr,
++ * see g_assert_se().
++ */
++
++/**
++ * g_assert_se:
++ * @expr: the expression to check
++ *
++ * Debugging macro to terminate the application if the assertion
++ * fails. If the assertion fails (i.e. the expression is not true),
++ * an error message is logged and the application is terminated.
++ *
++ * The check can be turned off in final releases of code by defining
++ * `G_DISABLE_ASSERT` when compiling the application.
++ *
++ * Unlike g_assert(), this macro is guaranteed to evaluate side effects
++ * of @expr, even if checks are disabled. It is still undefined if the
++ * program will actually be aborted or not.
+ */
+
+ /**
+diff --git a/glib/gtestutils.h b/glib/gtestutils.h
+index 30ede25..385ae42 100644
+--- a/glib/gtestutils.h
++++ b/glib/gtestutils.h
+@@ -270,6 +270,7 @@ typedef void (*GTestFixtureFunc) (gpointer fixture,
+ #endif
+
+ #define g_assert(expr) G_STMT_START { (void) 0; } G_STMT_END
++#define g_assert_se(expr) ((void) (expr))
+ #else /* !G_DISABLE_ASSERT */
+ #define g_assert_not_reached() G_STMT_START { g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, NULL); } G_STMT_END
+ #define g_assert(expr) G_STMT_START { \
+@@ -277,6 +278,7 @@ typedef void (*GTestFixtureFunc) (gpointer fixture,
+ g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ #expr); \
+ } G_STMT_END
++#define g_assert_se(expr) g_assert((expr))
+ #endif /* !G_DISABLE_ASSERT */
+
+ GLIB_AVAILABLE_IN_ALL
+diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
+index 06e857b..7bd2ff3 100644
+--- a/glib/gvariant-core.c
++++ b/glib/gvariant-core.c
+@@ -22,6 +22,7 @@
+ #include "config.h"
+
+ #include <glib/gvariant-core.h>
++#include "glib-private.h"
+
+ #include <glib/gvariant-internal.h>
+ #include <glib/gvariant-serialiser.h>
+@@ -115,21 +116,11 @@ struct _GVariant
+ * The type_info field never changes during the life of the
+ * instance, so it can be accessed without a lock.
+ *
+- * size: this is the size of the serialized form for the instance, if it
+- * is known. If the instance is in serialized form then it is, by
+- * definition, known. If the instance is in tree form then it may
+- * be unknown (in which case it is -1). It is possible for the
+- * size to be known when in tree form if, for example, the user
+- * has called g_variant_get_size() without calling
+- * g_variant_get_data(). Additionally, even when the user calls
+- * g_variant_get_data() the size of the data must first be
+- * determined so that a large enough buffer can be allocated for
+- * the data.
+- *
+- * Once the size is known, it can never become unknown again.
+- * g_variant_ensure_size() is used to ensure that the size is in
+- * the known state -- it calculates the size if needed. After
+- * that, the size field can be accessed without a lock.
++ * size: this is the size of the serialised form for the instance. It
++ * is known for serialised instances and also tree-form instances
++ * (for which it is calculated at construction time, from the
++ * known sizes of the children used). After construction, it
++ * never changes and therefore can be accessed without a lock.
+ *
+ * contents: a union containing either the information associated with
+ * holding a value in serialized form or holding a value in
+@@ -309,7 +300,32 @@ g_variant_release_children (GVariant *value)
+ g_free (value->contents.tree.children);
+ }
+
+-/* This begins the main body of the recursive serializer.
++/* < private >
++ * g_variant_lock_in_tree_form:
++ * @value: a #GVariant
++ *
++ * Locks @value if it is in tree form.
++ *
++ * Returns: %TRUE if @value is now in tree form with the lock acquired
++ */
++static gboolean
++g_variant_lock_in_tree_form (GVariant *value)
++{
++ if (g_atomic_int_get (&value->state) & STATE_SERIALISED)
++ return FALSE;
++
++ g_variant_lock (value);
++
++ if (value->state & STATE_SERIALISED)
++ {
++ g_variant_unlock (value);
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++/* This begins the main body of the recursive serialiser.
+ *
+ * There are 3 functions here that work as a team with the serializer to
+ * get things done. g_variant_store() has a trivial role, but as a
+@@ -326,31 +342,19 @@ g_variant_release_children (GVariant *value)
+ * instances are always in serialized form. For these instances,
+ * storing their serialized form merely involves a memcpy().
+ *
+- * Serialization is a two-step process. First, the size of the
+- * serialized data must be calculated so that an appropriately-sized
+- * buffer can be allocated. Second, the data is written into the
+- * buffer.
++ * Converting to serialised form:
+ *
+- * Determining the size:
+- * The process of determining the size is triggered by a call to
+- * g_variant_ensure_size() on a container. This invokes the
+- * serializer code to determine the size. The serializer is passed
+- * g_variant_fill_gvs() as a callback.
+- *
+- * g_variant_fill_gvs() is called by the serializer on each child of
+- * the container which, in turn, calls g_variant_ensure_size() on
+- * itself and fills in the result of its own size calculation.
+- *
+- * The serializer uses the size information from the children to
+- * calculate the size needed for the entire container.
++ * The first step in the process of converting a GVariant to
++ * serialised form is to allocate a buffer. The size of the buffer is
++ * always known because we computed at construction time of the
++ * GVariant.
+ *
+- * Writing the data:
+ * After the buffer has been allocated, g_variant_serialise() is
+- * called on the container. This invokes the serializer code to write
+- * the bytes to the container. The serializer is, again, passed
++ * called on the container. This invokes the serialiser code to write
++ * the bytes to the container. The serialiser is passed
+ * g_variant_fill_gvs() as a callback.
+ *
+- * This time, when g_variant_fill_gvs() is called for each child, the
++ * At the time that g_variant_fill_gvs() is called for each child, the
+ * child is given a pointer to a sub-region of the allocated buffer
+ * where it should write its data. This is done by calling
+ * g_variant_store(). In the event that the instance is in serialized
+@@ -363,34 +367,6 @@ g_variant_release_children (GVariant *value)
+ */
+ static void g_variant_fill_gvs (GVariantSerialised *, gpointer);
+
+-/* < private >
+- * g_variant_ensure_size:
+- * @value: a #GVariant
+- *
+- * Ensures that the ->size field of @value is filled in properly. This
+- * must be done as a precursor to any serialization of the value in
+- * order to know how large of a buffer is needed to store the data.
+- *
+- * The current thread must hold the lock on @value.
+- */
+-static void
+-g_variant_ensure_size (GVariant *value)
+-{
+- g_assert (value->state & STATE_LOCKED);
+-
+- if (value->size == (gsize) -1)
+- {
+- gpointer *children;
+- gsize n_children;
+-
+- children = (gpointer *) value->contents.tree.children;
+- n_children = value->contents.tree.n_children;
+- value->size = g_variant_serialiser_needed_size (value->type_info,
+- g_variant_fill_gvs,
+- children, n_children);
+- }
+-}
+-
+ /* < private >
+ * g_variant_to_serialised:
+ * @value: a #GVariant
+@@ -462,9 +438,12 @@ g_variant_serialise (GVariant *value,
+ *
+ * - reporting its type
+ *
+- * - reporting its serialized size (requires knowing the size first)
++ * - reporting its serialised size
+ *
+- * - possibly storing its serialized form into the provided buffer
++ * - possibly storing its serialised form into the provided buffer
++ *
++ * This callback is also used during g_variant_new_from_children() in
++ * order to discover the size and type of each child.
+ */
+ static void
+ g_variant_fill_gvs (GVariantSerialised *serialised,
+@@ -472,10 +451,6 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+ {
+ GVariant *value = data;
+
+- g_variant_lock (value);
+- g_variant_ensure_size (value);
+- g_variant_unlock (value);
+-
+ if (serialised->type_info == NULL)
+ serialised->type_info = value->type_info;
+ g_assert (serialised->type_info == value->type_info);
+@@ -511,26 +486,20 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
+ *
+ * Ensures that @value is in serialized form.
+ *
+- * If @value is in tree form then this function ensures that the
+- * serialized size is known and then allocates a buffer of that size and
+- * serializes the instance into the buffer. The 'children' array is
+- * then released and the instance is set to serialized form based on the
+- * contents of the buffer.
+- *
+- * The current thread must hold the lock on @value.
++ * If @value is in tree form then this function allocates a buffer of
++ * that size and serialises the instance into the buffer. The
++ * 'children' array is then released and the instance is set to
++ * serialised form based on the contents of the buffer.
+ */
+ static void
+ g_variant_ensure_serialised (GVariant *value)
+ {
+- g_assert (value->state & STATE_LOCKED);
+-
+- if (~value->state & STATE_SERIALISED)
++ if (g_variant_lock_in_tree_form (value))
+ {
+ GBytes *bytes;
+ gpointer data;
+
+ TRACE(GLIB_VARIANT_START_SERIALISE(value, value->type_info));
+- g_variant_ensure_size (value);
+ data = g_malloc (value->size);
+ g_variant_serialise (value, data);
+
+@@ -543,12 +512,69 @@ g_variant_ensure_serialised (GVariant *value)
+ value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
+ value->state |= STATE_SERIALISED;
+ TRACE(GLIB_VARIANT_END_SERIALISE(value, value->type_info));
++
++ g_variant_unlock (value);
++ }
++}
++
++/* Now we have the code to recursively serialise a GVariant into a
++ * GVariantVectors structure.
++ *
++ * We want to do this in cases where the GVariant contains large chunks
++ * of serialised data in order to avoid having to copy this data.
++ *
++ * This generally works the same as normal serialising (co-recursion
++ * with the serialiser) but instead of using a callback we just hard-code
++ * the callback with the name g_variant_callback_write_to_vectors().
++ *
++ * This is a private API that will be used by GDBus.
++ */
++gsize
++g_variant_callback_write_to_vectors (GVariantVectors *vectors,
++ gpointer data,
++ GVariantTypeInfo **type_info)
++{
++ GVariant *value = data;
++
++ if (g_variant_lock_in_tree_form (value))
++ {
++ g_variant_serialiser_write_to_vectors (vectors, value->type_info, value->size,
++ (gpointer *) value->contents.tree.children,
++ value->contents.tree.n_children);
++
++ g_variant_unlock (value);
+ }
++ else
++ g_variant_vectors_append_gbytes (vectors, value->contents.serialised.bytes,
++ value->contents.serialised.data, value->size);
++
++ if (type_info)
++ *type_info = value->type_info;
++
++ return value->size;
++}
++
++/* < private >
++ * g_variant_serialise_to_vectors:
++ * @value: a #GVariant
++ * @vectors: (out): the result
++ *
++ * Serialises @value into @vectors.
++ *
++ * The caller must free @vectors.
++ */
++void
++g_variant_to_vectors (GVariant *value,
++ GVariantVectors *vectors)
++{
++ g_variant_vectors_init (vectors);
++
++ g_variant_callback_write_to_vectors (vectors, value, NULL);
+ }
+
+ /* < private >
+ * g_variant_alloc:
+- * @type: the type of the new instance
++ * @type_info: (transfer full) the type info of the new instance
+ * @serialised: if the instance will be in serialised form
+ * @trusted: if the instance will be trusted
+ *
+@@ -559,73 +585,105 @@ g_variant_ensure_serialised (GVariant *value)
+ * Returns: a new #GVariant with a floating reference
+ */
+ static GVariant *
+-g_variant_alloc (const GVariantType *type,
+- gboolean serialised,
+- gboolean trusted)
++g_variant_alloc (GVariantTypeInfo *type_info,
++ gboolean serialised,
++ gboolean trusted)
+ {
+ GVariant *value;
+
+ value = g_slice_new (GVariant);
+- value->type_info = g_variant_type_info_get (type);
++ value->type_info = type_info;
+ value->state = (serialised ? STATE_SERIALISED : 0) |
+ (trusted ? STATE_TRUSTED : 0) |
+ STATE_FLOATING;
+- value->size = (gssize) -1;
+ g_atomic_ref_count_init (&value->ref_count);
+ value->depth = 0;
+
+ return value;
+ }
+
+-/**
+- * g_variant_new_from_bytes:
+- * @type: a #GVariantType
+- * @bytes: a #GBytes
+- * @trusted: if the contents of @bytes are trusted
++/* -- internal -- */
++
++/* < internal >
++ * g_variant_new_from_children:
++ * @type_info: (transfer full) a #GVariantTypeInfo
++ * @children: an array of #GVariant pointers. Consumed.
++ * @n_children: the length of @children
++ * @trusted: %TRUE if every child in @children in trusted
++ *
++ * Constructs a new tree-mode #GVariant instance. This is the inner
++ * interface for creation of new tree-mode values that gets called from
++ * various functions in gvariant.c.
+ *
+- * Constructs a new serialized-mode #GVariant instance. This is the
+- * inner interface for creation of new serialized values that gets
+- * called from various functions in gvariant.c.
++ * @children is consumed by this function. g_free() will be called on
++ * it some time later.
++ *
++ * Returns: a new #GVariant with a floating reference
++ */
++GVariant *
++g_variant_new_from_children (GVariantTypeInfo *type_info,
++ GVariant **children,
++ gsize n_children,
++ gboolean trusted)
++{
++ GVariant *value;
++
++ value = g_variant_alloc (type_info, FALSE, trusted);
++ value->contents.tree.children = children;
++ value->contents.tree.n_children = n_children;
++ value->size = g_variant_serialiser_needed_size (value->type_info, g_variant_fill_gvs,
++ (gpointer *) children, n_children);
++ TRACE(GLIB_VARIANT_FROM_CHILDREN(value, value->type_info, value->ref_count, value->state));
++
++ return value;
++}
++
++/* < internal >
++ * g_variant_new_serialised:
++ * @type_info: (transfer full): a #GVariantTypeInfo
++ * @bytes: (transfer full): the #GBytes holding @data
++ * @data: a pointer to the serialised data
++ * @size: the size of @data, in bytes
++ * @trusted: %TRUE if @data is trusted
++ *
++ * Constructs a new serialised #GVariant instance. This is the inner
++ * interface for creation of new serialised values that gets called from
++ * various functions in gvariant.c.
+ *
+- * A reference is taken on @bytes.
++ * @bytes is consumed by this function. g_bytes_unref() will be called
++ * on it some time later.
+ *
+ * The data in @bytes must be aligned appropriately for the @type being loaded.
+ * Otherwise this function will internally create a copy of the memory (since
+ * GLib 2.60) or (in older versions) fail and exit the process.
+ *
+- * Returns: (transfer none): a new #GVariant with a floating reference
+- *
+- * Since: 2.36
++ * Returns: a new #GVariant with a floating reference
+ */
+ GVariant *
+-g_variant_new_from_bytes (const GVariantType *type,
+- GBytes *bytes,
+- gboolean trusted)
++g_variant_new_serialised (GVariantTypeInfo *type_info,
++ GBytes *bytes,
++ gconstpointer data,
++ gsize size,
++ gboolean trusted)
+ {
+ GVariant *value;
+ guint alignment;
+- gsize size;
++ gsize fixed_size;
+ GBytes *owned_bytes = NULL;
+- GVariantSerialised serialised;
+
+- value = g_variant_alloc (type, TRUE, trusted);
++ value = g_variant_alloc (type_info, TRUE, trusted);
++ value->contents.serialised.data = data;
++ value->size = size;
+
+ g_variant_type_info_query (value->type_info,
+- &alignment, &size);
+-
++ &alignment, &fixed_size);
+ /* Ensure the alignment is correct. This is a huge performance hit if it’s
+ * not correct, but that’s better than aborting if a caller provides data
+ * with the wrong alignment (which is likely to happen very occasionally, and
+ * only cause an abort on some architectures — so is unlikely to be caught
+ * in testing). Callers can always actively ensure they use the correct
+ * alignment to avoid the performance hit. */
+- serialised.type_info = value->type_info;
+- serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
+- serialised.depth = 0;
+- serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
+- serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
+-
+- if (!g_variant_serialised_check (serialised))
++ if ((alignment & (gsize) g_bytes_get_data (bytes, NULL)) != 0)
+ {
+ #ifdef HAVE_POSIX_MEMALIGN
+ gpointer aligned_data = NULL;
+@@ -641,37 +699,43 @@ g_variant_new_from_bytes (const GVariantType *type,
+ if (aligned_size != 0)
+ memcpy (aligned_data, g_bytes_get_data (bytes, NULL), aligned_size);
+
+- bytes = owned_bytes = g_bytes_new_with_free_func (aligned_data,
+- aligned_size,
+- free, aligned_data);
++ owned_bytes = g_bytes_new_with_free_func (aligned_data,
++ aligned_size,
++ free, aligned_data);
+ aligned_data = NULL;
+ #else
+ /* NOTE: there may be platforms that lack posix_memalign() and also
+ * have malloc() that returns non-8-aligned. if so, we need to try
+ * harder here.
+ */
+- bytes = owned_bytes = g_bytes_new (g_bytes_get_data (bytes, NULL),
+- g_bytes_get_size (bytes));
++ owned_bytes = g_bytes_new (g_bytes_get_data (bytes, NULL),
++ g_bytes_get_size (bytes));
+ #endif
++ g_bytes_unref (bytes);
++ bytes = g_bytes_ref (owned_bytes);
+ }
+
+- value->contents.serialised.bytes = g_bytes_ref (bytes);
++ value->contents.serialised.bytes = bytes;
+
+- if (size && g_bytes_get_size (bytes) != size)
++ if G_UNLIKELY (fixed_size && size != fixed_size)
+ {
+ /* Creating a fixed-sized GVariant with a bytes of the wrong
+ * size.
+ *
+ * We should do the equivalent of pulling a fixed-sized child out
+- * of a brozen container (ie: data is NULL size is equal to the correct
++ * of a broken container (ie: data is NULL size is equal to the correct
+ * fixed size).
++ *
++ * This really ought not to happen if the data is trusted...
++ */
++ if (trusted)
++ g_error ("Attempting to create a trusted GVariant instance out of invalid data");
++
++ /* We hang on to the GBytes (even though we don't use it anymore)
++ * because every GVariant must have a GBytes.
+ */
+ value->contents.serialised.data = NULL;
+- value->size = size;
+- }
+- else
+- {
+- value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
++ value->size = fixed_size;
+ }
+
+ value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
+@@ -684,40 +748,6 @@ g_variant_new_from_bytes (const GVariantType *type,
+ return value;
+ }
+
+-/* -- internal -- */
+-
+-/* < internal >
+- * g_variant_new_from_children:
+- * @type: a #GVariantType
+- * @children: an array of #GVariant pointers. Consumed.
+- * @n_children: the length of @children
+- * @trusted: %TRUE if every child in @children is trusted
+- *
+- * Constructs a new tree-mode #GVariant instance. This is the inner
+- * interface for creation of new serialized values that gets called from
+- * various functions in gvariant.c.
+- *
+- * @children is consumed by this function. g_free() will be called on
+- * it some time later.
+- *
+- * Returns: a new #GVariant with a floating reference
+- */
+-GVariant *
+-g_variant_new_from_children (const GVariantType *type,
+- GVariant **children,
+- gsize n_children,
+- gboolean trusted)
+-{
+- GVariant *value;
+-
+- value = g_variant_alloc (type, FALSE, trusted);
+- value->contents.tree.children = children;
+- value->contents.tree.n_children = n_children;
+- TRACE(GLIB_VARIANT_FROM_CHILDREN(value, value->type_info, value->ref_count, value->state));
+-
+- return value;
+-}
+-
+ /* < internal >
+ * g_variant_get_type_info:
+ * @value: a #GVariant
+@@ -756,6 +786,191 @@ g_variant_is_trusted (GVariant *value)
+ return (value->state & STATE_TRUSTED) != 0;
+ }
+
++/* < internal >
++ * g_variant_get_serialised:
++ * @value: a #GVariant
++ * @bytes: (out) (transfer none): a location to store the #GBytes
++ * @size: (out): a location to store the size of the returned data
++ *
++ * Ensures that @value is in serialised form and returns information
++ * about it. This is called from various APIs in gvariant.c
++ *
++ * Returns: data, of length @size
++ */
++gconstpointer
++g_variant_get_serialised (GVariant *value,
++ GBytes **bytes,
++ gsize *size)
++{
++ g_variant_ensure_serialised (value);
++
++ if (bytes)
++ *bytes = value->contents.serialised.bytes;
++
++ *size = value->size;
++
++ return value->contents.serialised.data;
++}
++
++static GVariant *
++g_variant_vector_deserialise (GVariantTypeInfo *type_info,
++ GVariantVector *first_vector,
++ GVariantVector *last_vector,
++ gsize size,
++ gboolean trusted,
++ GArray *unpacked_children)
++{
++ g_assert (size > 0);
++
++ if (first_vector < last_vector)
++ {
++ GVariantVector *vector = first_vector;
++ const guchar *end_pointer;
++ GVariant **children;
++ guint save_point;
++ guint n_children;
++ gboolean failed;
++ guint i;
++
++ end_pointer = last_vector->data.pointer + last_vector->size;
++ save_point = unpacked_children->len;
++
++ if (!g_variant_serialiser_unpack_all (type_info, end_pointer, last_vector->size, size, unpacked_children))
++ {
++ for (i = save_point; i < unpacked_children->len; i++)
++ g_variant_type_info_unref (g_array_index (unpacked_children, GVariantUnpacked, i).type_info);
++ g_array_set_size (unpacked_children, save_point);
++
++ g_variant_type_info_unref (type_info);
++
++ return NULL;
++ }
++
++ n_children = unpacked_children->len - save_point;
++ children = g_new (GVariant *, n_children);
++ failed = FALSE;
++
++ for (i = 0; i < n_children; i++)
++ {
++ GVariantUnpacked *unpacked = &g_array_index (unpacked_children, GVariantUnpacked, save_point + i);
++ const guchar *resume_at_data;
++ gsize resume_at_size;
++ GVariantVector *fv;
++
++ /* Skip the alignment.
++ *
++ * We can destroy vectors because we won't be going back.
++ *
++ * We do a >= compare because we want to go to the next vector
++ * if it is the start of our child.
++ */
++ while (unpacked->skip >= vector->size)
++ {
++ unpacked->skip -= vector->size;
++ vector++;
++ }
++ g_assert (vector <= last_vector);
++
++ fv = vector;
++ fv->data.pointer += unpacked->skip;
++ fv->size -= unpacked->skip;
++
++ if (unpacked->size == 0)
++ {
++ children[i] = g_variant_new_serialised (unpacked->type_info, g_bytes_new (NULL, 0), NULL, 0, trusted);
++ g_variant_ref_sink (children[i]);
++ continue;
++ }
++
++ /* Now skip to the end, according to 'size'.
++ *
++ * We cannot destroy everything here because we will probably
++ * end up reusing the last one.
++ *
++ * We do a > compare because we want to stay on this vector if
++ * it is the end of our child.
++ */
++ size = unpacked->size;
++ while (unpacked->size > vector->size)
++ {
++ unpacked->size -= vector->size;
++ vector++;
++ }
++ g_assert (vector <= last_vector);
++
++ /* We have to modify the vectors for the benefit of the
++ * recursive step. We also have to remember where we left
++ * off, keeping in mind that the recursive step may itself
++ * modify the vectors.
++ */
++ resume_at_data = vector->data.pointer + unpacked->size;
++ resume_at_size = vector->size - unpacked->size;
++ vector->size = unpacked->size;
++
++ children[i] = g_variant_vector_deserialise (unpacked->type_info, fv, vector,
++ size, trusted, unpacked_children);
++
++ vector->data.pointer = resume_at_data;
++ vector->size = resume_at_size;
++
++ if (children[i])
++ g_variant_ref_sink (children[i]);
++ else
++ failed = TRUE;
++ }
++
++ /* We consumed all the type infos */
++ g_array_set_size (unpacked_children, save_point);
++
++ if G_UNLIKELY (failed)
++ {
++ for (i = 0; i < n_children; i++)
++ if (children[i])
++ g_variant_unref (children[i]);
++
++ g_variant_type_info_unref (type_info);
++ g_free (children);
++
++ return NULL;
++ }
++
++ return g_variant_new_from_children (type_info, children, n_children, trusted);
++ }
++ else
++ {
++ g_assert (first_vector == last_vector);
++ g_assert (size == first_vector->size);
++
++ return g_variant_new_serialised (type_info, g_bytes_ref (first_vector->gbytes),
++ first_vector->data.pointer, size, trusted);
++ }
++}
++
++GVariant *
++g_variant_from_vectors (const GVariantType *type,
++ GVariantVector *vectors,
++ gsize n_vectors,
++ gsize size,
++ gboolean trusted)
++{
++ GVariant *result;
++ GArray *tmp;
++
++ g_return_val_if_fail (vectors != NULL || n_vectors == 0, NULL);
++
++ if (size == 0)
++ return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_new (NULL, 0), NULL, 0, trusted);
++
++ tmp = g_array_new (FALSE, FALSE, sizeof (GVariantUnpacked));
++ result = g_variant_vector_deserialise (g_variant_type_info_get (type),
++ vectors, vectors + n_vectors - 1, size, trusted, tmp);
++ if (result)
++ g_variant_ref_sink (result);
++ g_array_free (tmp, TRUE);
++
++ return result;
++}
++
+ /* < internal >
+ * g_variant_get_depth:
+ * @value: a #GVariant
+@@ -982,10 +1197,6 @@ g_variant_is_floating (GVariant *value)
+ gsize
+ g_variant_get_size (GVariant *value)
+ {
+- g_variant_lock (value);
+- g_variant_ensure_size (value);
+- g_variant_unlock (value);
+-
+ return value->size;
+ }
+
+@@ -1026,56 +1237,11 @@ g_variant_get_size (GVariant *value)
+ gconstpointer
+ g_variant_get_data (GVariant *value)
+ {
+- g_variant_lock (value);
+ g_variant_ensure_serialised (value);
+- g_variant_unlock (value);
+
+ return value->contents.serialised.data;
+ }
+
+-/**
+- * g_variant_get_data_as_bytes:
+- * @value: a #GVariant
+- *
+- * Returns a pointer to the serialized form of a #GVariant instance.
+- * The semantics of this function are exactly the same as
+- * g_variant_get_data(), except that the returned #GBytes holds
+- * a reference to the variant data.
+- *
+- * Returns: (transfer full): A new #GBytes representing the variant data
+- *
+- * Since: 2.36
+- */
+-GBytes *
+-g_variant_get_data_as_bytes (GVariant *value)
+-{
+- const gchar *bytes_data;
+- const gchar *data;
+- gsize bytes_size;
+- gsize size;
+-
+- g_variant_lock (value);
+- g_variant_ensure_serialised (value);
+- g_variant_unlock (value);
+-
+- bytes_data = g_bytes_get_data (value->contents.serialised.bytes, &bytes_size);
+- data = value->contents.serialised.data;
+- size = value->size;
+-
+- if (data == NULL)
+- {
+- g_assert (size == 0);
+- data = bytes_data;
+- }
+-
+- if (data == bytes_data && size == bytes_size)
+- return g_bytes_ref (value->contents.serialised.bytes);
+- else
+- return g_bytes_new_from_bytes (value->contents.serialised.bytes,
+- data - bytes_data, size);
+-}
+-
+-
+ /**
+ * g_variant_n_children:
+ * @value: a container #GVariant
+@@ -1101,15 +1267,14 @@ g_variant_n_children (GVariant *value)
+ {
+ gsize n_children;
+
+- g_variant_lock (value);
+-
+- if (value->state & STATE_SERIALISED)
++ if (g_variant_lock_in_tree_form (value))
++ {
++ n_children = value->contents.tree.n_children;
++ g_variant_unlock (value);
++ }
++ else
+ n_children = g_variant_serialised_n_children (
+ g_variant_to_serialised (value));
+- else
+- n_children = value->contents.tree.n_children;
+-
+- g_variant_unlock (value);
+
+ return n_children;
+ }
+@@ -1151,75 +1316,63 @@ GVariant *
+ g_variant_get_child_value (GVariant *value,
+ gsize index_)
+ {
++ GVariant *child;
++
+ g_return_val_if_fail (value->depth < G_MAXSIZE, NULL);
+
+- if (~g_atomic_int_get (&value->state) & STATE_SERIALISED)
++ if (g_variant_lock_in_tree_form (value))
+ {
+- /* g_variant_serialised_get_child() does its own checks on index_ */
+- g_return_val_if_fail (index_ < g_variant_n_children (value), NULL);
++ /*
++ * g_variant_serialised_get_child() does its own checks on index_.
++ * Additionally, it would be preferable to use g_variant_n_children.
++ * However, it would try to lock the value again.
++ */
++ g_return_val_if_fail (index_ < value->contents.tree.n_children, NULL);
+
+- g_variant_lock (value);
++ child = g_variant_ref (value->contents.tree.children[index_]);
++ g_variant_unlock (value);
++ }
++ else
++ {
++ GVariantSerialised serialised = g_variant_to_serialised (value);
++ GVariantSerialised s_child;
+
+- if (~value->state & STATE_SERIALISED)
++ /* get the serialiser to extract the serialised data for the child
++ * from the serialised data for the container
++ */
++ s_child = g_variant_serialised_get_child (serialised, index_);
++
++ /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
++ value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
++ value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
++
++ /* Check whether this would cause nesting too deep. If so, return a fake
++ * child. The only situation we expect this to happen in is with a variant,
++ * as all other deeply-nested types have a static type, and hence should
++ * have been rejected earlier. In the case of a variant whose nesting plus
++ * the depth of its child is too great, return a unit variant () instead of
++ * the real child. */
++ if (!(value->state & STATE_TRUSTED) &&
++ g_variant_type_info_query_depth (s_child.type_info) >=
++ G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
+ {
+- GVariant *child;
+-
+- child = g_variant_ref (value->contents.tree.children[index_]);
+- g_variant_unlock (value);
+-
+- return child;
++ g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
++ g_variant_type_info_unref (s_child.type_info);
++ return g_variant_new_tuple (NULL, 0);
+ }
+
+- g_variant_unlock (value);
++ /* create a new serialised instance out of it */
++ child = g_variant_new_serialised (s_child.type_info,
++ g_bytes_ref (value->contents.serialised.bytes),
++ s_child.data, s_child.size,
++ value->state & STATE_TRUSTED);
++ child->state &= ~STATE_FLOATING;
++ child->depth = value->depth + 1;
+ }
+
+- {
+- GVariantSerialised serialised = g_variant_to_serialised (value);
+- GVariantSerialised s_child;
+- GVariant *child;
+-
+- /* get the serializer to extract the serialized data for the child
+- * from the serialized data for the container
+- */
+- s_child = g_variant_serialised_get_child (serialised, index_);
+-
+- /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
+- value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
+- value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
+-
+- /* Check whether this would cause nesting too deep. If so, return a fake
+- * child. The only situation we expect this to happen in is with a variant,
+- * as all other deeply-nested types have a static type, and hence should
+- * have been rejected earlier. In the case of a variant whose nesting plus
+- * the depth of its child is too great, return a unit variant () instead of
+- * the real child. */
+- if (!(value->state & STATE_TRUSTED) &&
+- g_variant_type_info_query_depth (s_child.type_info) >=
+- G_VARIANT_MAX_RECURSION_DEPTH - value->depth)
+- {
+- g_assert (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT));
+- g_variant_type_info_unref (s_child.type_info);
+- return g_variant_new_tuple (NULL, 0);
+- }
++ TRACE(GLIB_VARIANT_FROM_PARENT(child, child->type_info, child->ref_count, child->state, value));
+
+- /* create a new serialized instance out of it */
+- child = g_slice_new (GVariant);
+- child->type_info = s_child.type_info;
+- child->state = (value->state & STATE_TRUSTED) |
+- STATE_SERIALISED;
+- child->size = s_child.size;
+- g_atomic_ref_count_init (&child->ref_count);
+- child->depth = value->depth + 1;
+- child->contents.serialised.bytes =
+- g_bytes_ref (value->contents.serialised.bytes);
+- child->contents.serialised.data = s_child.data;
+- child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
+- child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
+-
+- TRACE(GLIB_VARIANT_FROM_PARENT(child, child->type_info, child->ref_count, child->state, value));
+-
+- return child;
+- }
++ return child;
+ }
+
+ /**
+@@ -1316,19 +1469,18 @@ void
+ g_variant_store (GVariant *value,
+ gpointer data)
+ {
+- g_variant_lock (value);
+-
+- if (value->state & STATE_SERIALISED)
++ if (g_variant_lock_in_tree_form (value))
++ {
++ g_variant_serialise (value, data);
++ g_variant_unlock (value);
++ }
++ else
+ {
+ if (value->contents.serialised.data != NULL)
+ memcpy (data, value->contents.serialised.data, value->size);
+ else
+ memset (data, 0, value->size);
+ }
+- else
+- g_variant_serialise (value, data);
+-
+- g_variant_unlock (value);
+ }
+
+ /**
+@@ -1356,9 +1508,13 @@ g_variant_store (GVariant *value,
+ gboolean
+ g_variant_is_normal_form (GVariant *value)
+ {
+- if (value->state & STATE_TRUSTED)
++ if (g_atomic_int_get (&value->state) & STATE_TRUSTED)
+ return TRUE;
+
++ /* We always take the lock here because we expect to find that the
++ * value is in normal form and in that case, we need to update the
++ * state, which requires holding the lock.
++ */
+ g_variant_lock (value);
+
+ if (value->depth >= G_VARIANT_MAX_RECURSION_DEPTH)
+diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h
+index fb5f4bf..b09ac1b 100644
+--- a/glib/gvariant-core.h
++++ b/glib/gvariant-core.h
+@@ -27,15 +27,25 @@
+
+ /* gvariant-core.c */
+
+-GVariant * g_variant_new_from_children (const GVariantType *type,
++GVariant * g_variant_new_from_children (GVariantTypeInfo *type_info,
+ GVariant **children,
+ gsize n_children,
+ gboolean trusted);
+
++GVariant * g_variant_new_serialised (GVariantTypeInfo *type_info,
++ GBytes *bytes,
++ gconstpointer data,
++ gsize size,
++ gboolean trusted);
++
+ gboolean g_variant_is_trusted (GVariant *value);
+
+ GVariantTypeInfo * g_variant_get_type_info (GVariant *value);
+
++gconstpointer g_variant_get_serialised (GVariant *value,
++ GBytes **bytes,
++ gsize *size);
++
+ gsize g_variant_get_depth (GVariant *value);
+
+ GVariant * g_variant_maybe_get_child_value (GVariant *value,
+diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
+index 7973ecf..fe7ef4e 100644
+--- a/glib/gvariant-parser.c
++++ b/glib/gvariant-parser.c
+@@ -470,7 +470,13 @@ pattern_coalesce (const gchar *left,
+ (*one)++;
+ }
+
+- else if (**one == 'N' && strchr ("ynqiuxthd", **the_other))
++ else if (**one == 'N' && strchr ("ynqiuxthfd", **the_other))
++ {
++ *out++ = *(*the_other)++;
++ (*one)++;
++ }
++
++ else if (**one == 'D' && (**the_other == 'f' || **the_other == 'd'))
+ {
+ *out++ = *(*the_other)++;
+ (*one)++;
+@@ -631,6 +637,10 @@ ast_resolve (AST *ast,
+ pattern[j++] = 'i';
+ break;
+
++ case 'D':
++ pattern[j++] = 'd';
++ break;
++
+ default:
+ pattern[j++] = pattern[i];
+ break;
+@@ -1342,7 +1352,7 @@ dictionary_get_pattern (AST *ast,
+ /* the basic types,
+ * plus undetermined number type and undetermined string type.
+ */
+- if (!strchr ("bynqiuxthdsogNS", key_char))
++ if (!strchr ("bynqiuxthfdsogNDS", key_char))
+ {
+ ast_set_error (ast, error, NULL,
+ G_VARIANT_PARSE_ERROR_BASIC_TYPE_EXPECTED,
+@@ -1909,7 +1919,7 @@ number_get_pattern (AST *ast,
+ (!g_str_has_prefix (number->token, "0x") && strchr (number->token, 'e')) ||
+ strstr (number->token, "inf") ||
+ strstr (number->token, "nan"))
+- return g_strdup ("Md");
++ return g_strdup ("MD");
+
+ return g_strdup ("MN");
+ }
+@@ -1934,17 +1944,16 @@ number_get_value (AST *ast,
+ Number *number = (Number *) ast;
+ const gchar *token;
+ gboolean negative;
+- gboolean floating;
+ guint64 abs_val;
+ gdouble dbl_val;
++ gchar typechar;
+ gchar *end;
+
++ typechar = *g_variant_type_peek_string (type);
+ token = number->token;
+
+- if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
++ if (typechar == 'f' || typechar == 'd')
+ {
+- floating = TRUE;
+-
+ errno = 0;
+ dbl_val = g_ascii_strtod (token, &end);
+ if (dbl_val != 0.0 && errno == ERANGE)
+@@ -1961,7 +1970,6 @@ number_get_value (AST *ast,
+ }
+ else
+ {
+- floating = FALSE;
+ negative = token[0] == '-';
+ if (token[0] == '-')
+ token++;
+@@ -1997,10 +2005,7 @@ number_get_value (AST *ast,
+ return NULL;
+ }
+
+- if (floating)
+- return g_variant_new_double (dbl_val);
+-
+- switch (*g_variant_type_peek_string (type))
++ switch (typechar)
+ {
+ case 'y':
+ if (negative || abs_val > G_MAXUINT8)
+@@ -2054,6 +2059,12 @@ number_get_value (AST *ast,
+ return g_variant_new_handle (negative ?
+ -((gint32) abs_val) : ((gint32) abs_val));
+
++ case 'f':
++ return g_variant_new_float (dbl_val);
++
++ case 'd':
++ return g_variant_new_double (dbl_val);
++
+ default:
+ return ast_type_error (ast, type, error);
+ }
+@@ -2350,6 +2361,9 @@ typedecl_parse (TokenStream *stream,
+ else if (token_stream_consume (stream, "uint64"))
+ type = g_variant_type_copy (G_VARIANT_TYPE_UINT64);
+
++ else if (token_stream_consume (stream, "float"))
++ type = g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
++
+ else if (token_stream_consume (stream, "double"))
+ type = g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
+
+diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
+index 4e4a73a..40a891a 100644
+--- a/glib/gvariant-serialiser.c
++++ b/glib/gvariant-serialiser.c
+@@ -278,6 +278,27 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
+ return value;
+ }
+
++static gboolean
++gvs_fixed_sized_maybe_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ if (total_size)
++ {
++ GVariantUnpacked unpacked;
++
++ unpacked.type_info = g_variant_type_info_ref (g_variant_type_info_element (type_info));
++ unpacked.skip = 0;
++ unpacked.size = total_size;
++
++ g_array_append_val (unpacked_children, unpacked);
++ }
++
++ return TRUE;
++}
++
+ static gsize
+ gvs_fixed_sized_maybe_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -311,6 +332,19 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
+ }
+ }
+
++static gsize
++gvs_fixed_sized_maybe_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ if (!n_children)
++ return 0;
++
++ return g_variant_callback_write_to_vectors (vectors, children[0], NULL);
++}
++
+ static gboolean
+ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
+ {
+@@ -376,6 +410,27 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
+ return value;
+ }
+
++static gboolean
++gvs_variable_sized_maybe_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ if (total_size)
++ {
++ GVariantUnpacked unpacked;
++
++ unpacked.type_info = g_variant_type_info_ref (g_variant_type_info_element (type_info));
++ unpacked.skip = 0;
++ unpacked.size = total_size - 1;
++
++ g_array_append_val (unpacked_children, unpacked);
++ }
++
++ return TRUE;
++}
++
+ static gsize
+ gvs_variable_sized_maybe_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -410,6 +465,20 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
+ }
+ }
+
++static void
++gvs_variable_sized_maybe_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ if (n_children)
++ {
++ g_variant_callback_write_to_vectors (vectors, children[0], NULL);
++ g_variant_vectors_append_copy (vectors, "", 1);
++ }
++}
++
+ static gboolean
+ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
+ {
+@@ -479,6 +548,39 @@ gvs_fixed_sized_array_get_child (GVariantSerialised value,
+ return child;
+ }
+
++static gboolean
++gvs_fixed_sized_array_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ GVariantTypeInfo *element;
++ gsize element_fixed_size;
++ gsize i, n;
++
++ element = g_variant_type_info_element (type_info);
++ g_variant_type_info_query (element, NULL, &element_fixed_size);
++
++ if (total_size % element_fixed_size)
++ return FALSE;
++
++ n = total_size / element_fixed_size;
++
++ for (i = 0; i < n; i++)
++ {
++ GVariantUnpacked unpacked;
++
++ unpacked.type_info = g_variant_type_info_ref (element);
++ unpacked.skip = 0;
++ unpacked.size = element_fixed_size;
++
++ g_array_append_val (unpacked_children, unpacked);
++ }
++
++ return TRUE;
++}
++
+ static gsize
+ gvs_fixed_sized_array_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -513,6 +615,19 @@ gvs_fixed_sized_array_serialise (GVariantSerialised value,
+ }
+ }
+
++static void
++gvs_fixed_sized_array_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ gsize i;
++
++ for (i = 0; i < n_children; i++)
++ g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++}
++
+ static gboolean
+ gvs_fixed_sized_array_is_normal (GVariantSerialised value)
+ {
+@@ -828,6 +943,80 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
+ return child;
+ }
+
++static gboolean
++gvs_variable_sized_array_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ GVariantTypeInfo *element;
++ guint element_alignment;
++ const guchar *offsets;
++ gsize offset_size;
++ gsize offsets_array_size;
++ gsize prev_end;
++ gsize last_end;
++ gsize i, n;
++
++ if (total_size == 0)
++ return TRUE;
++
++ element = g_variant_type_info_element (type_info);
++ g_variant_type_info_query (element, &element_alignment, NULL);
++
++ offset_size = gvs_get_offset_size (total_size);
++
++ if (offset_size > end_size)
++ return FALSE;
++
++ last_end = gvs_read_unaligned_le (end_pointer - offset_size, offset_size);
++
++ if (last_end > total_size)
++ return 0;
++
++ offsets_array_size = total_size - last_end;
++
++ if (offsets_array_size > end_size)
++ return FALSE;
++
++ offsets = end_pointer - offsets_array_size;
++
++ if (offsets_array_size % offset_size)
++ return FALSE;
++
++ n = offsets_array_size / offset_size;
++
++ if (n == 0)
++ return FALSE;
++
++ prev_end = 0;
++
++ for (i = 0; i < n; i++)
++ {
++ GVariantUnpacked unpacked;
++ gsize start;
++ gsize end;
++
++ start = prev_end + ((-prev_end) & element_alignment);
++ end = gvs_read_unaligned_le (offsets, offset_size);
++ offsets += offset_size;
++
++ if (start < prev_end || end < start || end > last_end)
++ return FALSE;
++
++ unpacked.type_info = g_variant_type_info_ref (element);
++ unpacked.skip = start - prev_end;
++ unpacked.size = end - start;
++
++ g_array_append_val (unpacked_children, unpacked);
++
++ prev_end = end;
++ }
++
++ return TRUE;
++}
++
+ static gsize
+ gvs_variable_sized_array_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -887,6 +1076,38 @@ gvs_variable_sized_array_serialise (GVariantSerialised value,
+ }
+ }
+
++static void
++gvs_variable_sized_array_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ guint offset_key;
++ guint alignment;
++ gsize offset;
++ gsize i;
++
++ if (n_children == 0)
++ return;
++
++ offset_key = g_variant_vectors_reserve_offsets (vectors, n_children, gvs_get_offset_size (size));
++ g_variant_type_info_query (type_info, &alignment, NULL);
++ offset = 0;
++
++ for (i = 0; i < n_children; i++)
++ {
++ if ((-offset) & alignment)
++ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++
++ g_variant_vectors_write_to_offsets (vectors, i, offset, offset_key);
++ }
++
++ g_variant_vectors_commit_offsets (vectors, offset_key);
++}
++
+ static gboolean
+ gvs_variable_sized_array_is_normal (GVariantSerialised value)
+ {
+@@ -1126,6 +1347,86 @@ gvs_tuple_get_child (GVariantSerialised value,
+ return child;
+ }
+
++static gboolean
++gvs_tuple_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ gsize offset_size;
++ gsize prev_end;
++ gsize i, n;
++
++ n = g_variant_type_info_n_members (type_info);
++
++ /* An empty tuple (n = 0) is always encoded as a single byte, which
++ * means that we should not be attempting to unpack it from multiple
++ * vectors.
++ */
++ if (n == 0)
++ return FALSE;
++
++ offset_size = gvs_get_offset_size (total_size);
++
++ prev_end = 0;
++
++ for (i = 0; i < n; i++)
++ {
++ const GVariantMemberInfo *member_info;
++ GVariantUnpacked unpacked;
++ gsize fixed_size;
++ guint alignment;
++ gsize start;
++ gsize end;
++
++ member_info = g_variant_type_info_member_info (type_info, i);
++ g_variant_type_info_query (member_info->type_info, &alignment, &fixed_size);
++
++ start = prev_end + ((-prev_end) & alignment);
++
++ switch (member_info->ending_type)
++ {
++ case G_VARIANT_MEMBER_ENDING_FIXED:
++ end = start + fixed_size;
++ break;
++
++ case G_VARIANT_MEMBER_ENDING_LAST:
++ end = total_size;
++ break;
++
++ case G_VARIANT_MEMBER_ENDING_OFFSET:
++ if (end_size < offset_size)
++ return FALSE;
++
++ end_pointer -= offset_size;
++ total_size -= offset_size;
++ end_size -= offset_size;
++
++ end = gvs_read_unaligned_le (end_pointer, offset_size);
++ break;
++
++ default:
++ g_assert_not_reached ();
++ }
++
++ if (start < prev_end || end < start || end > total_size)
++ return FALSE;
++
++ unpacked.type_info = g_variant_type_info_ref (member_info->type_info);
++ unpacked.skip = start - prev_end;
++ unpacked.size = end - start;
++
++ g_array_append_val (unpacked_children, unpacked);
++
++ prev_end = end;
++ }
++
++ g_assert (prev_end == total_size);
++
++ return TRUE;
++}
++
+ static gsize
+ gvs_tuple_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -1212,6 +1513,95 @@ gvs_tuple_serialise (GVariantSerialised value,
+ value.data[offset++] = '\0';
+ }
+
++
++static void
++gvs_tuple_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ const GVariantMemberInfo *member_info = NULL;
++ gsize fixed_size;
++ gsize offset;
++ gsize i;
++
++ if (n_children == 0)
++ {
++ g_variant_vectors_append_copy (vectors, "", 1);
++ return;
++ }
++
++ g_variant_type_info_query (type_info, NULL, &fixed_size);
++ offset = 0;
++
++ if (!fixed_size)
++ {
++ gsize n_offsets;
++
++ member_info = g_variant_type_info_member_info (type_info, n_children - 1);
++ n_offsets = member_info->i + 1;
++
++ if (n_offsets)
++ {
++ gsize offset_key = 0;
++
++ offset_key = g_variant_vectors_reserve_offsets (vectors, n_offsets, gvs_get_offset_size (size));
++
++ for (i = 0; i < n_children; i++)
++ {
++ guint alignment;
++
++ member_info = g_variant_type_info_member_info (type_info, i);
++ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++ if ((-offset) & alignment)
++ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++
++ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
++ g_variant_vectors_write_to_offsets (vectors, --n_offsets, offset, offset_key);
++ }
++
++ g_variant_vectors_commit_offsets (vectors, offset_key);
++ }
++ else
++ {
++ for (i = 0; i < n_children; i++)
++ {
++ guint alignment;
++
++ member_info = g_variant_type_info_member_info (type_info, i);
++ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++ if ((-offset) & alignment)
++ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++ }
++ }
++ }
++ else
++ {
++ for (i = 0; i < n_children; i++)
++ {
++ guint alignment;
++
++ member_info = g_variant_type_info_member_info (type_info, i);
++ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
++
++ if ((-offset) & alignment)
++ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
++
++ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
++ }
++
++ g_assert (fixed_size - offset < 8);
++ g_variant_vectors_append_pad (vectors, fixed_size - offset);
++ }
++}
++
+ static gboolean
+ gvs_tuple_is_normal (GVariantSerialised value)
+ {
+@@ -1358,66 +1748,100 @@ gvs_variant_n_children (GVariantSerialised value)
+ return 1;
+ }
+
+-static inline GVariantSerialised
+-gvs_variant_get_child (GVariantSerialised value,
+- gsize index_)
++static GVariantTypeInfo *
++gvs_variant_find_type (const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ gsize *child_size,
++ gsize depth)
+ {
+- GVariantSerialised child = { 0, };
++ gsize i;
+
+- /* NOTE: not O(1) and impossible for it to be... */
+- if (value.size)
+- {
+- /* find '\0' character */
+- for (child.size = value.size - 1; child.size; child.size--)
+- if (value.data[child.size] == '\0')
+- break;
++ for (i = 1; i <= end_size; i++)
++ if (end_pointer[-i] == '\0')
++ {
++ const gchar *type_string = (gchar *) end_pointer - i + 1;
++ const gchar *limit = (gchar *) end_pointer;
++ const gchar *end;
+
+- /* ensure we didn't just hit the start of the string */
+- if (value.data[child.size] == '\0')
+- {
+- const gchar *type_string = (gchar *) &value.data[child.size + 1];
+- const gchar *limit = (gchar *) &value.data[value.size];
+- const gchar *end;
++ /* We may have a type string of length 'i'. Check for validity. */
++ if (g_variant_type_string_scan (type_string, limit, &end) && end == limit)
++ {
++ const GVariantType *type = (GVariantType *) type_string;
+
+- if (g_variant_type_string_scan (type_string, limit, &end) &&
+- end == limit)
+- {
+- const GVariantType *type = (GVariantType *) type_string;
++ if (g_variant_type_is_definite (type))
++ {
++ GVariantTypeInfo *type_info;
++ gsize fixed_size;
++ gsize child_type_depth;
+
+- if (g_variant_type_is_definite (type))
+- {
+- gsize fixed_size;
+- gsize child_type_depth;
++ type_info = g_variant_type_info_get (type);
+
+- child.type_info = g_variant_type_info_get (type);
+- child.depth = value.depth + 1;
++ g_variant_type_info_query (type_info, NULL, &fixed_size);
++ child_type_depth = g_variant_type_info_query_depth (type_info);
+
+- if (child.size != 0)
+- /* only set to non-%NULL if size > 0 */
+- child.data = value.data;
++ if ((!fixed_size || fixed_size == total_size - i) &&
++ depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth)
++ {
++ *child_size = total_size - i;
+
+- g_variant_type_info_query (child.type_info,
+- NULL, &fixed_size);
+- child_type_depth = g_variant_type_info_query_depth (child.type_info);
++ return type_info;
++ }
+
+- if ((!fixed_size || fixed_size == child.size) &&
+- value.depth < G_VARIANT_MAX_RECURSION_DEPTH - child_type_depth)
+- return child;
++ g_variant_type_info_unref (type_info);
++ }
++ }
+
+- g_variant_type_info_unref (child.type_info);
+- }
+- }
+- }
++ /* No sense in trying other lengths if we already failed */
++ break;
++ }
++
++ return NULL;
++}
++
++static inline GVariantSerialised
++gvs_variant_get_child (GVariantSerialised value,
++ gsize index_)
++{
++ GVariantSerialised child = { 0, };
++
++ if ((child.type_info = gvs_variant_find_type (value.data + value.size, value.size, value.size, &child.size, value.depth)))
++ {
++ if (child.size != 0)
++ child.data = value.data;
++ }
++ else
++ {
++ child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
++ child.size = 1;
+ }
+
+- child.type_info = g_variant_type_info_get (G_VARIANT_TYPE_UNIT);
+- child.data = NULL;
+- child.size = 1;
+ child.depth = value.depth + 1;
+
+ return child;
+ }
+
++static gboolean
++gvs_variant_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ GVariantUnpacked unpacked;
++
++ if ((unpacked.type_info = gvs_variant_find_type (end_pointer, end_size, total_size, &unpacked.size, 0)))
++ {
++ unpacked.skip = 0;
++
++ g_array_append_val (unpacked_children, unpacked);
++
++ return TRUE;
++ }
++
++ return FALSE;
++}
++
+ static inline gsize
+ gvs_variant_needed_size (GVariantTypeInfo *type_info,
+ GVariantSerialisedFiller gvs_filler,
+@@ -1450,6 +1874,23 @@ gvs_variant_serialise (GVariantSerialised value,
+ memcpy (value.data + child.size + 1, type_string, strlen (type_string));
+ }
+
++static void
++gvs_variant_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ GVariantTypeInfo *child_type_info;
++ const gchar *type_string;
++
++ g_variant_callback_write_to_vectors (vectors, children[0], &child_type_info);
++ type_string = g_variant_type_info_get_type_string (child_type_info);
++
++ g_variant_vectors_append_copy (vectors, "", 1);
++ g_variant_vectors_append_copy (vectors, type_string, strlen (type_string));
++}
++
+ static inline gboolean
+ gvs_variant_is_normal (GVariantSerialised value)
+ {
+@@ -1662,7 +2103,35 @@ g_variant_serialiser_needed_size (GVariantTypeInfo *type_info,
+
+ return gvs_/**/,/**/_needed_size (type_info, gvs_filler,
+ children, n_children);
++ )
++ g_assert_not_reached ();
++}
++
++gboolean
++g_variant_serialiser_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children)
++{
++ DISPATCH_CASES (type_info,
++ return gvs_/**/,/**/_unpack_all (type_info, end_pointer, end_size, total_size, unpacked_children);
++ )
++
++ /* We are here because type_info is not a container type */
++ return FALSE;
++}
+
++void
++g_variant_serialiser_write_to_vectors (GVariantVectors *vectors,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children)
++{
++ DISPATCH_CASES (type_info,
++ gvs_/**/,/**/_write_to_vectors (vectors, type_info, size, children, n_children);
++ return;
+ )
+ g_assert_not_reached ();
+ }
+diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
+index eb74fe7..c8b59d9 100644
+--- a/glib/gvariant-serialiser.h
++++ b/glib/gvariant-serialiser.h
+@@ -24,6 +24,7 @@
+ #define __G_VARIANT_SERIALISER_H__
+
+ #include "gvarianttypeinfo.h"
++#include "gvariant-vectors.h"
+
+ typedef struct
+ {
+@@ -54,14 +55,27 @@ typedef struct
+ gsize checked_offsets_up_to;
+ } GVariantSerialised;
+
+-/* deserialization */
++typedef struct
++{
++ GVariantTypeInfo *type_info;
++ gsize skip;
++ gsize size;
++} GVariantUnpacked;
++
++/* deserialisation */
+ GLIB_AVAILABLE_IN_ALL
+ gsize g_variant_serialised_n_children (GVariantSerialised container);
+ GLIB_AVAILABLE_IN_ALL
+ GVariantSerialised g_variant_serialised_get_child (GVariantSerialised container,
+ gsize index);
+
+-/* serialization */
++gboolean g_variant_serialiser_unpack_all (GVariantTypeInfo *type_info,
++ const guchar *end_pointer,
++ gsize end_size,
++ gsize total_size,
++ GArray *unpacked_children);
++
++/* serialisation */
+ typedef void (*GVariantSerialisedFiller) (GVariantSerialised *serialised,
+ gpointer data);
+
+@@ -96,4 +110,14 @@ GLIB_AVAILABLE_IN_ALL
+ gboolean g_variant_serialiser_is_signature (gconstpointer data,
+ gsize size);
+
++
++gsize g_variant_callback_write_to_vectors (GVariantVectors *vectors,
++ gpointer data,
++ GVariantTypeInfo **type_info);
++void g_variant_serialiser_write_to_vectors (GVariantVectors *items,
++ GVariantTypeInfo *type_info,
++ gsize size,
++ const gpointer *children,
++ gsize n_children);
++
+ #endif /* __G_VARIANT_SERIALISER_H__ */
+diff --git a/glib/gvariant-vectors.c b/glib/gvariant-vectors.c
+new file mode 100644
+index 0000000..5e07786
+--- /dev/null
++++ b/glib/gvariant-vectors.c
+@@ -0,0 +1,248 @@
++#include "config.h"
++
++#include "gvariant-vectors.h"
++#include "gtestutils.h"
++
++static void
++append_zeros (GByteArray *array,
++ guint n)
++{
++ guchar zeros[8] = "";
++
++ g_byte_array_append (array, zeros, n);
++}
++
++void
++g_variant_vectors_init (GVariantVectors *vectors)
++{
++
++ /* The first 8 bytes of 'extra_bytes' is always 0. We use this for
++ * inserting padding in between two GBytes records.
++ */
++ vectors->extra_bytes = g_byte_array_new ();
++ append_zeros (vectors->extra_bytes, 8);
++
++ vectors->vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
++
++ vectors->offsets = g_byte_array_new ();
++}
++
++void
++g_variant_vectors_deinit (GVariantVectors *vectors)
++{
++ int i;
++ for (i = 0; i < vectors->vectors->len; i++)
++ {
++ GVariantVector *v = &g_array_index (vectors->vectors, GVariantVector, i);
++ g_bytes_unref (v->gbytes);
++ }
++ g_byte_array_unref (vectors->extra_bytes);
++ g_array_unref (vectors->vectors);
++ g_byte_array_unref (vectors->offsets);
++}
++
++gsize
++g_variant_vectors_append_pad (GVariantVectors *vectors,
++ gsize padding)
++{
++ /* If the last vector that we stored was 'pad' or 'copy' then we will
++ * expand it instead of adding a new one.
++ */
++ if (vectors->vectors->len)
++ {
++ GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
++
++ if (expand_vector->gbytes == NULL)
++ {
++ expand_vector->size += padding;
++
++ /* If the vector points to data, we need to add the padding to
++ * the end of that data. If it points to the zero bytes at
++ * the start then we can just grow it (but we must ensure that
++ * it doesn't get too large).
++ */
++ if (expand_vector->data.offset)
++ append_zeros (vectors->extra_bytes, padding);
++ else
++ g_assert (expand_vector->size < 8);
++
++ return padding;
++ }
++
++ /* If the last vector was a GBytes then fall through */
++ }
++
++ /* Otherwise, record a new vector pointing to the padding bytes at the
++ * start.
++ */
++ {
++ GVariantVector v;
++
++ v.gbytes = NULL;
++ v.data.offset = 0;
++ v.size = padding;
++
++ g_array_append_val (vectors->vectors, v);
++ }
++
++ return padding;
++}
++
++void
++g_variant_vectors_append_copy (GVariantVectors *vectors,
++ gconstpointer data,
++ gsize size)
++{
++ /* If the last vector that we stored was 'pad' or 'copy' then we will
++ * expand it instead of adding a new one.
++ */
++ if (vectors->vectors->len)
++ {
++ GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
++
++ if (expand_vector->gbytes == NULL)
++ {
++ /* If this was a padding vector then we must convert it to
++ * data first.
++ */
++ if (expand_vector->data.offset == 0)
++ {
++ expand_vector->data.offset = vectors->extra_bytes->len;
++ append_zeros (vectors->extra_bytes, expand_vector->size);
++ }
++
++ /* We have a vector pointing to data at the end of the
++ * extra_bytes array, so just append there and grow the
++ * vector.
++ */
++ g_byte_array_append (vectors->extra_bytes, data, size);
++ expand_vector->size += size;
++ return;
++ }
++
++ /* If the last vector was a GBytes then fall through */
++ }
++
++ /* Otherwise, copy the data and record a new vector. */
++ {
++ GVariantVector v;
++
++ v.gbytes = NULL;
++ v.data.offset = vectors->extra_bytes->len;
++ v.size = size;
++
++ g_byte_array_append (vectors->extra_bytes, data, size);
++ g_array_append_val (vectors->vectors, v);
++ }
++}
++
++void
++g_variant_vectors_append_gbytes (GVariantVectors *vectors,
++ GBytes *gbytes,
++ gconstpointer data,
++ gsize size)
++{
++ GVariantVector v;
++
++ /* Some very rough profiling has indicated that the trade-off for
++ * overhead on the atomic operations involved in the ref/unref on the
++ * GBytes is larger than the cost of the copy at ~128 bytes.
++ */
++ if (size < 128)
++ {
++ g_variant_vectors_append_copy (vectors, data, size);
++ return;
++ }
++
++ v.gbytes = g_bytes_ref (gbytes);
++ v.data.pointer = data;
++ v.size = size;
++
++ g_array_append_val (vectors->vectors, v);
++}
++
++typedef void (* WriteFunction) (gpointer base, gsize offset, gsize value);
++static void write_1 (gpointer base, gsize offset, gsize value) { ((guint8 *) base)[offset] = value; }
++static void write_2 (gpointer base, gsize offset, gsize value) { ((guint16 *) base)[offset] = GUINT16_TO_LE (value); }
++static void write_4 (gpointer base, gsize offset, gsize value) { ((guint32 *) base)[offset] = GUINT32_TO_LE (value); }
++static void write_8 (gpointer base, gsize offset, gsize value) { ((guint64 *) base)[offset] = GUINT64_TO_LE (value); }
++
++typedef struct
++{
++ gsize size;
++ WriteFunction func;
++} OffsetsHeader;
++
++gsize
++g_variant_vectors_reserve_offsets (GVariantVectors *vectors,
++ guint n_offsets,
++ guint offset_size)
++{
++ OffsetsHeader *header;
++ gsize total_size;
++ gsize add_size;
++ guint key;
++
++ total_size = n_offsets * offset_size;
++
++ /* Add room for the metadata and round up to multiple of 8 */
++ add_size = (sizeof (OffsetsHeader) + total_size + 7) & ~7ull;
++ key = vectors->offsets->len;
++ g_byte_array_set_size (vectors->offsets, key + add_size);
++ header = (OffsetsHeader *) (vectors->offsets->data + key);
++ key += sizeof (OffsetsHeader);
++ header->size = total_size;
++
++ switch (offset_size)
++ {
++ case 1:
++ header->func = write_1;
++ break;
++
++ case 2:
++ header->func = write_2;
++ break;
++
++ case 4:
++ header->func = write_4;
++ break;
++
++ case 8:
++ header->func = write_8;
++ break;
++
++ default:
++ g_assert_not_reached ();
++ }
++
++ return key;
++}
++
++void
++g_variant_vectors_write_to_offsets (GVariantVectors *vectors,
++ gsize offset,
++ gsize value,
++ gsize key)
++{
++ OffsetsHeader *header;
++ guchar *offsets;
++
++ offsets = vectors->offsets->data + key;
++ header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
++
++ header->func (offsets, offset, value);
++}
++
++void
++g_variant_vectors_commit_offsets (GVariantVectors *vectors,
++ gsize key)
++{
++ OffsetsHeader *header;
++ guchar *offsets;
++
++ offsets = vectors->offsets->data + key;
++ header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
++
++ g_variant_vectors_append_copy (vectors, offsets, header->size);
++ g_byte_array_set_size (vectors->offsets, key - sizeof (OffsetsHeader));
++}
+diff --git a/glib/gvariant-vectors.h b/glib/gvariant-vectors.h
+new file mode 100644
+index 0000000..05e2258
+--- /dev/null
++++ b/glib/gvariant-vectors.h
+@@ -0,0 +1,63 @@
++
++#ifndef __G_VARIANT_VECTORS_H__
++#define __G_VARIANT_VECTORS_H__
++
++#include <glib/garray.h>
++
++typedef struct
++{
++ GByteArray *extra_bytes;
++ GArray *vectors;
++ GByteArray *offsets;
++} GVariantVectors;
++
++
++/* If ->bytes is NULL then offset/size point inside of extra_bytes,
++ * otherwise pointer/size point to memory owned by the GBytes.
++ */
++typedef struct
++{
++ GBytes *gbytes;
++ union {
++ const guchar *pointer;
++ gsize offset;
++ } data;
++ gsize size;
++} GVariantVector;
++
++void g_variant_vectors_init (GVariantVectors *vectors);
++
++
++void g_variant_vectors_deinit (GVariantVectors *vectors);
++
++
++gsize g_variant_vectors_append_pad (GVariantVectors *vectors,
++ gsize padding);
++
++
++void g_variant_vectors_append_copy (GVariantVectors *vectors,
++ gconstpointer data,
++ gsize size);
++
++
++void g_variant_vectors_append_gbytes (GVariantVectors *vectors,
++ GBytes *gbytes,
++ gconstpointer data,
++ gsize size);
++
++
++gsize g_variant_vectors_reserve_offsets (GVariantVectors *vectors,
++ guint n_offsets,
++ guint offset_size);
++
++
++void g_variant_vectors_write_to_offsets (GVariantVectors *vectors,
++ gsize offset,
++ gsize value,
++ gsize offset_key);
++
++
++void g_variant_vectors_commit_offsets (GVariantVectors *vectors,
++ gsize offset_key);
++
++#endif /* __G_GVARIANT_VECTORS_H__ */
+diff --git a/glib/gvariant.c b/glib/gvariant.c
+index b7e4241..b1838e2 100644
+--- a/glib/gvariant.c
++++ b/glib/gvariant.c
+@@ -139,7 +139,7 @@
+ * endianness, or of the length or type of the top-level variant.
+ *
+ * The amount of memory required to store a boolean is 1 byte. 16,
+- * 32 and 64 bit integers and double precision floating point numbers
++ * 32 and 64 bit integers and floating point numbers
+ * use their "natural" size. Strings (including object path and
+ * signature strings) are stored with a nul terminator, and as such
+ * use the length of the string plus 1 byte.
+@@ -225,7 +225,7 @@
+ *
+ * This means that in total, for our "a{sv}" example, 91 bytes of
+ * type information would be allocated.
+- *
++ *
+ * The type information cache, additionally, uses a #GHashTable to
+ * store and look up the cached items and stores a pointer to this
+ * hash table in static storage. The hash table is freed when there
+@@ -320,14 +320,10 @@ g_variant_new_from_trusted (const GVariantType *type,
+ gconstpointer data,
+ gsize size)
+ {
+- GVariant *value;
+- GBytes *bytes;
+-
+- bytes = g_bytes_new (data, size);
+- value = g_variant_new_from_bytes (type, bytes, TRUE);
+- g_bytes_unref (bytes);
++ gpointer mydata = g_memdup (data, size);
+
+- return value;
++ return g_variant_new_serialised (g_variant_type_info_get (type),
++ g_bytes_new_take (mydata, size), mydata, size, TRUE);
+ }
+
+ /**
+@@ -374,7 +370,7 @@ g_variant_get_boolean (GVariant *value)
+ }
+
+ /* the constructors and accessors for byte, int{16,32,64}, handles and
+- * doubles all look pretty much exactly the same, so we reduce
++ * floats all look pretty much exactly the same, so we reduce
+ * copy/pasting here.
+ */
+ #define NUMERIC_TYPE(TYPE, type, ctype) \
+@@ -598,6 +594,31 @@ NUMERIC_TYPE (UINT64, uint64, guint64)
+ **/
+ NUMERIC_TYPE (HANDLE, handle, gint32)
+
++/**
++ * g_variant_new_float:
++ * @value: a #gfloat floating point value
++ *
++ * Creates a new float #GVariant instance.
++ *
++ * Returns: (transfer none): a floating reference to a new float #GVariant instance
++ *
++ * Since: 2.44
++ **/
++/**
++ * g_variant_get_float:
++ * @value: a float #GVariant instance
++ *
++ * Returns the single precision floating point value of @value.
++ *
++ * It is an error to call this function with a @value of any type
++ * other than %G_VARIANT_TYPE_FLOAT.
++ *
++ * Returns: a #gfloat
++ *
++ * Since: 2.44
++ **/
++NUMERIC_TYPE (FLOAT, float, gfloat)
++
+ /**
+ * g_variant_new_double:
+ * @value: a #gdouble floating point value
+@@ -648,8 +669,8 @@ GVariant *
+ g_variant_new_maybe (const GVariantType *child_type,
+ GVariant *child)
+ {
++ GVariantTypeInfo *type_info;
+ GVariantType *maybe_type;
+- GVariant *value;
+
+ g_return_val_if_fail (child_type == NULL || g_variant_type_is_definite
+ (child_type), 0);
+@@ -662,6 +683,8 @@ g_variant_new_maybe (const GVariantType *child_type,
+ child_type = g_variant_get_type (child);
+
+ maybe_type = g_variant_type_new_maybe (child_type);
++ type_info = g_variant_type_info_get (maybe_type);
++ g_variant_type_free (maybe_type);
+
+ if (child != NULL)
+ {
+@@ -672,14 +695,10 @@ g_variant_new_maybe (const GVariantType *child_type,
+ children[0] = g_variant_ref_sink (child);
+ trusted = g_variant_is_trusted (children[0]);
+
+- value = g_variant_new_from_children (maybe_type, children, 1, trusted);
++ return g_variant_new_from_children (type_info, children, 1, trusted);
+ }
+ else
+- value = g_variant_new_from_children (maybe_type, NULL, 0, TRUE);
+-
+- g_variant_type_free (maybe_type);
+-
+- return value;
++ return g_variant_new_from_children (type_info, NULL, 0, TRUE);
+ }
+
+ /**
+@@ -725,7 +744,7 @@ g_variant_new_variant (GVariant *value)
+
+ g_variant_ref_sink (value);
+
+- return g_variant_new_from_children (G_VARIANT_TYPE_VARIANT,
++ return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_VARIANT),
+ g_memdup2 (&value, sizeof value),
+ 1, g_variant_is_trusted (value));
+ }
+@@ -781,10 +800,10 @@ g_variant_new_array (const GVariantType *child_type,
+ GVariant * const *children,
+ gsize n_children)
+ {
++ GVariantTypeInfo *type_info;
+ GVariantType *array_type;
+ GVariant **my_children;
+ gboolean trusted;
+- GVariant *value;
+ gsize i;
+
+ g_return_val_if_fail (n_children > 0 || child_type != NULL, NULL);
+@@ -798,6 +817,8 @@ g_variant_new_array (const GVariantType *child_type,
+ if (child_type == NULL)
+ child_type = g_variant_get_type (children[0]);
+ array_type = g_variant_type_new_array (child_type);
++ type_info = g_variant_type_info_get (array_type);
++ g_variant_type_free (array_type);
+
+ for (i = 0; i < n_children; i++)
+ {
+@@ -813,11 +834,7 @@ g_variant_new_array (const GVariantType *child_type,
+ trusted &= g_variant_is_trusted (children[i]);
+ }
+
+- value = g_variant_new_from_children (array_type, my_children,
+- n_children, trusted);
+- g_variant_type_free (array_type);
+-
+- return value;
++ return g_variant_new_from_children (type_info, my_children, n_children, trusted);
+ }
+
+ /*< private >
+@@ -868,10 +885,10 @@ GVariant *
+ g_variant_new_tuple (GVariant * const *children,
+ gsize n_children)
+ {
++ GVariantTypeInfo *type_info;
+ GVariantType *tuple_type;
+ GVariant **my_children;
+ gboolean trusted;
+- GVariant *value;
+ gsize i;
+
+ g_return_val_if_fail (n_children == 0 || children != NULL, NULL);
+@@ -886,11 +903,10 @@ g_variant_new_tuple (GVariant * const *children,
+ }
+
+ tuple_type = g_variant_make_tuple_type (children, n_children);
+- value = g_variant_new_from_children (tuple_type, my_children,
+- n_children, trusted);
++ type_info = g_variant_type_info_get (tuple_type);
+ g_variant_type_free (tuple_type);
+
+- return value;
++ return g_variant_new_from_children (type_info, my_children, n_children, trusted);
+ }
+
+ /*< private >
+@@ -928,6 +944,7 @@ GVariant *
+ g_variant_new_dict_entry (GVariant *key,
+ GVariant *value)
+ {
++ GVariantTypeInfo *type_info;
+ GVariantType *dict_type;
+ GVariant **children;
+ gboolean trusted;
+@@ -941,10 +958,10 @@ g_variant_new_dict_entry (GVariant *key,
+ trusted = g_variant_is_trusted (key) && g_variant_is_trusted (value);
+
+ dict_type = g_variant_make_dict_entry_type (key, value);
+- value = g_variant_new_from_children (dict_type, children, 2, trusted);
++ type_info = g_variant_type_info_get (dict_type);
+ g_variant_type_free (dict_type);
+
+- return value;
++ return g_variant_new_from_children (type_info, children, 2, trusted);
+ }
+
+ /**
+@@ -1120,6 +1137,7 @@ g_variant_lookup_value (GVariant *dictionary,
+ * - %G_VARIANT_TYPE_BOOLEAN: #guchar (not #gboolean!)
+ * - %G_VARIANT_TYPE_BYTE: #guint8
+ * - %G_VARIANT_TYPE_HANDLE: #guint32
++ * - %G_VARIANT_TYPE_FLOAT: #gfloat
+ * - %G_VARIANT_TYPE_DOUBLE: #gdouble
+ *
+ * For example, if calling this function for an array of 32-bit integers,
+@@ -1577,7 +1595,7 @@ g_variant_new_strv (const gchar * const *strv,
+ for (i = 0; i < length_unsigned; i++)
+ strings[i] = g_variant_ref_sink (g_variant_new_string (strv[i]));
+
+- return g_variant_new_from_children (G_VARIANT_TYPE_STRING_ARRAY,
++ return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_STRING_ARRAY),
+ strings, length_unsigned, TRUE);
+ }
+
+@@ -1714,7 +1732,7 @@ g_variant_new_objv (const gchar * const *strv,
+ for (i = 0; i < length_unsigned; i++)
+ strings[i] = g_variant_ref_sink (g_variant_new_object_path (strv[i]));
+
+- return g_variant_new_from_children (G_VARIANT_TYPE_OBJECT_PATH_ARRAY,
++ return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_OBJECT_PATH_ARRAY),
+ strings, length_unsigned, TRUE);
+ }
+
+@@ -1955,7 +1973,7 @@ g_variant_new_bytestring_array (const gchar * const *strv,
+ for (i = 0; i < length_unsigned; i++)
+ strings[i] = g_variant_ref_sink (g_variant_new_bytestring (strv[i]));
+
+- return g_variant_new_from_children (G_VARIANT_TYPE_BYTESTRING_ARRAY,
++ return g_variant_new_from_children (g_variant_type_info_get (G_VARIANT_TYPE_BYTESTRING_ARRAY),
+ strings, length_unsigned, TRUE);
+ }
+
+@@ -2166,6 +2184,8 @@ g_variant_is_container (GVariant *value)
+ * @G_VARIANT_CLASS_UINT64: The #GVariant is an unsigned 64 bit integer.
+ * @G_VARIANT_CLASS_HANDLE: The #GVariant is a file handle index.
+ * @G_VARIANT_CLASS_DOUBLE: The #GVariant is a double precision floating
++ * @G_VARIANT_CLASS_FLOAT: The #GVariant is a single precision floating
++ * point value.
+ * point value.
+ * @G_VARIANT_CLASS_STRING: The #GVariant is a normal string.
+ * @G_VARIANT_CLASS_OBJECT_PATH: The #GVariant is a D-Bus object path
+@@ -2587,6 +2607,32 @@ g_variant_print_string (GVariant *value,
+ g_variant_get_uint64 (value));
+ break;
+
++ case G_VARIANT_CLASS_FLOAT:
++ {
++ gchar buffer[100];
++ gint i;
++
++ g_ascii_dtostr (buffer, sizeof buffer, g_variant_get_float (value));
++
++ for (i = 0; buffer[i]; i++)
++ if (buffer[i] == '.' || buffer[i] == 'e' ||
++ buffer[i] == 'n' || buffer[i] == 'N')
++ break;
++
++ /* if there is no '.' or 'e' in the float then add one */
++ if (buffer[i] == '\0')
++ {
++ buffer[i++] = '.';
++ buffer[i++] = '0';
++ buffer[i++] = '\0';
++ }
++
++ if (type_annotate)
++ g_string_append (string, "float ");
++ g_string_append (string, buffer);
++ }
++ break;
++
+ case G_VARIANT_CLASS_DOUBLE:
+ {
+ gchar buffer[100];
+@@ -2711,6 +2757,7 @@ g_variant_hash (gconstpointer value_)
+ case G_VARIANT_CLASS_INT32:
+ case G_VARIANT_CLASS_UINT32:
+ case G_VARIANT_CLASS_HANDLE:
++ case G_VARIANT_CLASS_FLOAT:
+ {
+ const guint *ptr;
+
+@@ -2833,7 +2880,7 @@ g_variant_equal (gconstpointer one,
+ * two values that have types that are not exactly equal. For example,
+ * you cannot compare a 32-bit signed integer with a 32-bit unsigned
+ * integer. Also note that this function is not particularly
+- * well-behaved when it comes to comparison of doubles; in particular,
++ * well-behaved when it comes to comparison of floats; in particular,
+ * the handling of incomparable values (ie: NaN) is undefined.
+ *
+ * If you only require an equality comparison, g_variant_equal() is more
+@@ -2904,6 +2951,14 @@ g_variant_compare (gconstpointer one,
+ return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
+ }
+
++ case G_VARIANT_CLASS_FLOAT:
++ {
++ gfloat a_val = g_variant_get_float (a);
++ gfloat b_val = g_variant_get_float (b);
++
++ return (a_val == b_val) ? 0 : (a_val > b_val) ? 1 : -1;
++ }
++
+ case G_VARIANT_CLASS_DOUBLE:
+ {
+ gdouble a_val = g_variant_get_double (a);
+@@ -3109,7 +3164,7 @@ g_variant_iter_free (GVariantIter *iter)
+ * you no longer need it.
+ *
+ * Here is an example for iterating with g_variant_iter_next_value():
+- * |[<!-- language="C" -->
++ * |[<!-- language="C" -->
+ * // recursively iterate a container
+ * void
+ * iterate_container_recursive (GVariant *container)
+@@ -3773,7 +3828,7 @@ g_variant_builder_end (GVariantBuilder *builder)
+ else
+ g_assert_not_reached ();
+
+- value = g_variant_new_from_children (my_type,
++ value = g_variant_new_from_children (g_variant_type_info_get (my_type),
+ g_renew (GVariant *,
+ GVSB(builder)->children,
+ GVSB(builder)->offset),
+@@ -4374,8 +4429,8 @@ g_variant_format_string_scan (const gchar *string,
+ switch (next_char())
+ {
+ case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+- case 'x': case 't': case 'h': case 'd': case 's': case 'o':
+- case 'g': case 'v': case '*': case '?': case 'r':
++ case 'x': case 't': case 'h': case 'f': case 'd': case 's':
++ case 'o': case 'g': case 'v': case '*': case '?': case 'r':
+ break;
+
+ case 'm':
+@@ -5052,6 +5107,7 @@ g_variant_valist_skip_leaf (const gchar **str,
+ va_arg (*app, guint64);
+ return;
+
++ case 'f':
+ case 'd':
+ va_arg (*app, gdouble);
+ return;
+@@ -5097,6 +5153,9 @@ g_variant_valist_new_leaf (const gchar **str,
+ case 'h':
+ return g_variant_new_handle (va_arg (*app, gint));
+
++ case 'f':
++ return g_variant_new_float (va_arg (*app, gdouble));
++
+ case 'd':
+ return g_variant_new_double (va_arg (*app, gdouble));
+
+@@ -5107,6 +5166,7 @@ g_variant_valist_new_leaf (const gchar **str,
+
+ /* The code below assumes this */
+ G_STATIC_ASSERT (sizeof (gboolean) == sizeof (guint32));
++G_STATIC_ASSERT (sizeof (gfloat) == sizeof (guint32));
+ G_STATIC_ASSERT (sizeof (gdouble) == sizeof (guint64));
+
+ static void
+@@ -5180,6 +5240,10 @@ g_variant_valist_get_leaf (const gchar **str,
+ *(gint32 *) ptr = g_variant_get_handle (value);
+ return;
+
++ case 'f':
++ *(gfloat *) ptr = g_variant_get_float (value);
++ return;
++
+ case 'd':
+ *(gdouble *) ptr = g_variant_get_double (value);
+ return;
+@@ -5202,6 +5266,7 @@ g_variant_valist_get_leaf (const gchar **str,
+ case 'u':
+ case 'h':
+ case 'b':
++ case 'f':
+ *(guint32 *) ptr = 0;
+ return;
+
+@@ -6011,6 +6076,9 @@ g_variant_deep_copy (GVariant *value,
+ else
+ return g_variant_new_handle (g_variant_get_handle (value));
+
++ case G_VARIANT_CLASS_FLOAT:
++ return g_variant_new_float (g_variant_get_float (value));
++
+ case G_VARIANT_CLASS_DOUBLE:
+ if (byteswap)
+ {
+@@ -6096,8 +6164,7 @@ g_variant_get_normal_form (GVariant *value)
+ * Performs a byteswapping operation on the contents of @value. The
+ * result is that all multi-byte numeric data contained in @value is
+ * byteswapped. That includes 16, 32, and 64bit signed and unsigned
+- * integers as well as file handles and double precision floating point
+- * values.
++ * integers as well as file handles and floating point values.
+ *
+ * This function is an identity mapping on any value that does not
+ * contain multi-byte numeric data. That include strings, booleans,
+@@ -6211,21 +6278,99 @@ g_variant_new_from_data (const GVariantType *type,
+ GDestroyNotify notify,
+ gpointer user_data)
+ {
+- GVariant *value;
+ GBytes *bytes;
+
+ g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
+ g_return_val_if_fail (data != NULL || size == 0, NULL);
+
++ if (size == 0)
++ {
++ if (notify)
++ {
++ (* notify) (user_data);
++ notify = NULL;
++ }
++
++ data = NULL;
++ }
++
+ if (notify)
+ bytes = g_bytes_new_with_free_func (data, size, notify, user_data);
+ else
+ bytes = g_bytes_new_static (data, size);
+
+- value = g_variant_new_from_bytes (type, bytes, trusted);
+- g_bytes_unref (bytes);
++ return g_variant_new_serialised (g_variant_type_info_get (type), bytes, data, size, trusted);
++}
+
+- return value;
++/**
++ * g_variant_new_from_bytes:
++ * @type: a #GVariantType
++ * @bytes: a #GBytes
++ * @trusted: if the contents of @bytes are trusted
++ *
++ * Constructs a new serialised-mode #GVariant instance. This is the
++ * inner interface for creation of new serialised values that gets
++ * called from various functions in gvariant.c.
++ *
++ * A reference is taken on @bytes.
++ *
++ * Returns: (transfer none): a new #GVariant with a floating reference
++ *
++ * Since: 2.36
++ */
++GVariant *
++g_variant_new_from_bytes (const GVariantType *type,
++ GBytes *bytes,
++ gboolean trusted)
++{
++ gconstpointer data;
++ gsize size;
++
++ g_return_val_if_fail (g_variant_type_is_definite (type), NULL);
++
++ data = g_bytes_get_data (bytes, &size);
++
++ return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_ref (bytes), data, size, trusted);
++}
++
++/**
++ * g_variant_get_data_as_bytes:
++ * @value: a #GVariant
++ *
++ * Returns a pointer to the serialised form of a #GVariant instance.
++ * The semantics of this function are exactly the same as
++ * g_variant_get_data(), except that the returned #GBytes holds
++ * a reference to the variant data.
++ *
++ * Returns: (transfer full): A new #GBytes representing the variant data
++ *
++ * Since: 2.36
++ */
++GBytes *
++g_variant_get_data_as_bytes (GVariant *value)
++{
++ gconstpointer data;
++ GBytes *bytes;
++ gsize size;
++ gconstpointer bytes_data;
++ gsize bytes_size;
++
++ data = g_variant_get_serialised (value, &bytes, &size);
++ bytes_data = g_bytes_get_data (bytes, &bytes_size);
++
++ /* Try to reuse the GBytes held internally by GVariant, if it exists
++ * and is covering exactly the correct range.
++ */
++ if (data == bytes_data && size == bytes_size)
++ return g_bytes_ref (bytes);
++
++ /* See g_variant_get_data() about why it can return NULL... */
++ else if (data == NULL)
++ return g_bytes_new_take (g_malloc0 (size), size);
++
++ /* Otherwise, make a new GBytes with reference to the old. */
++ else
++ return g_bytes_new_with_free_func (data, size, (GDestroyNotify) g_bytes_unref, g_bytes_ref (bytes));
+ }
+
+ /* Epilogue {{{1 */
+diff --git a/glib/gvariant.h b/glib/gvariant.h
+index bdc3795..852376a 100644
+--- a/glib/gvariant.h
++++ b/glib/gvariant.h
+@@ -46,6 +46,7 @@ typedef enum
+ G_VARIANT_CLASS_INT64 = 'x',
+ G_VARIANT_CLASS_UINT64 = 't',
+ G_VARIANT_CLASS_HANDLE = 'h',
++ G_VARIANT_CLASS_FLOAT = 'f',
+ G_VARIANT_CLASS_DOUBLE = 'd',
+ G_VARIANT_CLASS_STRING = 's',
+ G_VARIANT_CLASS_OBJECT_PATH = 'o',
+@@ -97,6 +98,8 @@ GLIB_AVAILABLE_IN_ALL
+ GVariant * g_variant_new_uint64 (guint64 value);
+ GLIB_AVAILABLE_IN_ALL
+ GVariant * g_variant_new_handle (gint32 value);
++GLIB_AVAILABLE_IN_2_44
++GVariant * g_variant_new_float (gfloat value);
+ GLIB_AVAILABLE_IN_ALL
+ GVariant * g_variant_new_double (gdouble value);
+ GLIB_AVAILABLE_IN_ALL
+@@ -150,6 +153,8 @@ GLIB_AVAILABLE_IN_ALL
+ guint64 g_variant_get_uint64 (GVariant *value);
+ GLIB_AVAILABLE_IN_ALL
+ gint32 g_variant_get_handle (GVariant *value);
++GLIB_AVAILABLE_IN_2_44
++gfloat g_variant_get_float (GVariant *value);
+ GLIB_AVAILABLE_IN_ALL
+ gdouble g_variant_get_double (GVariant *value);
+ GLIB_AVAILABLE_IN_ALL
+diff --git a/glib/gvarianttype.c b/glib/gvarianttype.c
+index 58a4a59..2959da5 100644
+--- a/glib/gvarianttype.c
++++ b/glib/gvarianttype.c
+@@ -113,7 +113,7 @@
+ * A basic type string describes a basic type (as per
+ * g_variant_type_is_basic()) and is always a single character in length.
+ * The valid basic type strings are "b", "y", "n", "q", "i", "u", "x", "t",
+- * "h", "d", "s", "o", "g" and "?".
++ * "h", "f", "d", "s", "o", "g" and "?".
+ *
+ * The above definition is recursive to arbitrary depth. "aaaaai" and
+ * "(ui(nq((y)))s)" are both valid type strings, as is
+@@ -134,6 +134,8 @@
+ * - `h`: the type string of %G_VARIANT_TYPE_HANDLE; a signed 32 bit value
+ * that, by convention, is used as an index into an array of file
+ * descriptors that are sent alongside a D-Bus message.
++ * - `f`: the type string of %G_VARIANT_TYPE_FLOAT; a single precision
++ * floating point value.
+ * - `d`: the type string of %G_VARIANT_TYPE_DOUBLE; a double precision
+ * floating point value.
+ * - `s`: the type string of %G_VARIANT_TYPE_STRING; a string.
+@@ -234,7 +236,7 @@ variant_type_string_scan_internal (const gchar *string,
+ case '{':
+ if (depth_limit == 0 ||
+ string == limit || *string == '\0' || /* { */
+- !strchr ("bynqihuxtdsog?", *string++) || /* key */
++ !strchr ("bynqihuxtfdsog?", *string++) || /* key */
+ !variant_type_string_scan_internal (string, limit, &string,
+ &child_depth, depth_limit - 1) || /* value */
+ string == limit || *string++ != '}') /* } */
+@@ -254,7 +256,7 @@ variant_type_string_scan_internal (const gchar *string,
+
+ case 'b': case 'y': case 'n': case 'q': case 'i': case 'u':
+ case 'x': case 't': case 'd': case 's': case 'o': case 'g':
+- case 'v': case 'r': case '*': case '?': case 'h':
++ case 'v': case 'r': case '*': case '?': case 'h': case 'f':
+ max_depth = MAX (max_depth, 1);
+ break;
+
+@@ -600,8 +602,8 @@ g_variant_type_is_container (const GVariantType *type)
+ *
+ * Determines if the given @type is a basic type.
+ *
+- * Basic types are booleans, bytes, integers, doubles, strings, object
+- * paths and signatures.
++ * Basic types are booleans, bytes, integers, floats, doubles, strings,
++ * object paths and signatures.
+ *
+ * Only a basic type may be used as the key of a dictionary entry.
+ *
+@@ -631,6 +633,7 @@ g_variant_type_is_basic (const GVariantType *type)
+ case 'u':
+ case 't':
+ case 'x':
++ case 'f':
+ case 'd':
+ case 's':
+ case 'o':
+diff --git a/glib/gvarianttype.h b/glib/gvarianttype.h
+index 6374957..c613c5a 100644
+--- a/glib/gvarianttype.h
++++ b/glib/gvarianttype.h
+@@ -105,6 +105,15 @@ typedef struct _GVariantType GVariantType;
+ **/
+ #define G_VARIANT_TYPE_UINT64 ((const GVariantType *) "t")
+
++/**
++ * G_VARIANT_TYPE_FLOAT:
++ *
++ * The type of a single precision IEEE754 floating point number. You
++ * can store a number as large as 3.40e38 in these (plus and minus) but
++ * there are some gaps on the way.
++ **/
++#define G_VARIANT_TYPE_FLOAT ((const GVariantType *) "f")
++
+ /**
+ * G_VARIANT_TYPE_DOUBLE:
+ *
+diff --git a/glib/gvarianttypeinfo.c b/glib/gvarianttypeinfo.c
+index d2df7fd..466055d 100644
+--- a/glib/gvarianttypeinfo.c
++++ b/glib/gvarianttypeinfo.c
+@@ -120,7 +120,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
+ /* 'c' */ { not_a_type },
+ /* 'd' */ { fixed_aligned(8) }, /* double */
+ /* 'e' */ { not_a_type },
+- /* 'f' */ { not_a_type },
++ /* 'f' */ { fixed_aligned(4) }, /* float */
+ /* 'g' */ { unaligned }, /* signature string */
+ /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */
+ /* 'i' */ { fixed_aligned(4) }, /* int32 */
+@@ -152,7 +152,7 @@ static const GVariantTypeInfo g_variant_type_info_basic_table[24] = {
+ * GVariantTypeInfo itself, we save a bunch of relocations.
+ */
+ static const char g_variant_type_info_basic_chars[24][2] = {
+- "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ",
++ "b", " ", "d", " ", "f", "g", "h", "i", " ", " ", " ", " ",
+ "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y"
+ };
+
+diff --git a/glib/meson.build b/glib/meson.build
+index c26a35e..6703deb 100644
+--- a/glib/meson.build
++++ b/glib/meson.build
+@@ -344,6 +344,7 @@ glib_sources += files(
+ 'gvariant-core.c',
+ 'gvariant-parser.c',
+ 'gvariant-serialiser.c',
++ 'gvariant-vectors.c',
+ 'gvarianttypeinfo.c',
+ 'gvarianttype.c',
+ 'gversion.c',
+diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c
+index e81a5f4..cd87734 100644
+--- a/glib/tests/bytes.c
++++ b/glib/tests/bytes.c
+@@ -16,7 +16,12 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <errno.h>
++#include <unistd.h>
+ #include "glib.h"
++#include "glib-unix.h"
++
++#include "glib-linux.h"
+
+ /* Keep in sync with glib/gbytes.c */
+ struct _GBytes
+@@ -271,7 +276,7 @@ test_to_data_transferred (void)
+ GBytes *bytes;
+
+ /* Memory transferred: one reference, and allocated with g_malloc */
+- bytes = g_bytes_new (NYAN, N_NYAN);
++ bytes = g_bytes_new_take (g_memdup (NYAN, N_NYAN), N_NYAN);
+ memory = g_bytes_get_data (bytes, NULL);
+ data = g_bytes_unref_to_data (bytes, &size);
+ g_assert_true (data == memory);
+@@ -349,7 +354,7 @@ test_to_array_transferred (void)
+ GBytes *bytes;
+
+ /* Memory transferred: one reference, and allocated with g_malloc */
+- bytes = g_bytes_new (NYAN, N_NYAN);
++ bytes = g_bytes_new_take (g_memdup (NYAN, N_NYAN), N_NYAN);
+ memory = g_bytes_get_data (bytes, NULL);
+ array = g_bytes_unref_to_array (bytes);
+ g_assert_nonnull (array);
+@@ -483,6 +488,46 @@ test_unref_null (void)
+ g_bytes_unref (NULL);
+ }
+
++#ifdef GLIB_LINUX
++static void
++test_memfd (void)
++{
++ GBytes *bytes;
++ gint fd;
++
++ fd = glib_linux_memfd_create ("", MFD_CLOEXEC);
++ if (fd == -1 && errno == EINVAL)
++ {
++ g_test_skip ("missing kernel memfd support");
++ return;
++ }
++
++ /* We should not be able to seal this one */
++ g_assert (!g_unix_fd_ensure_zero_copy_safe (fd));
++ close (fd);
++
++ /* but this one will work */
++ fd = glib_linux_memfd_create ("", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++ bytes = g_bytes_new_take_zero_copy_fd (fd);
++ g_assert_cmpint (g_bytes_get_size (bytes), ==, 0);
++ g_bytes_unref (bytes);
++
++ /* try with real data */
++ fd = glib_linux_memfd_create ("", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++ g_assert_se (write (fd, NYAN, N_NYAN) == N_NYAN);
++ bytes = g_bytes_new_take_zero_copy_fd (fd);
++ g_assert_cmpint (g_bytes_get_size (bytes), ==, N_NYAN);
++ g_assert (memcmp (g_bytes_get_data (bytes, NULL), NYAN, N_NYAN) == 0);
++ g_assert (g_bytes_get_zero_copy_fd (bytes) == fd);
++
++ /* ensure that we cannot modify the fd further */
++ g_assert_se (write (fd, NYAN, N_NYAN) == -1);
++
++ /* that's enough for now */
++ g_bytes_unref (bytes);
++}
++#endif
++
+ int
+ main (int argc, char *argv[])
+ {
+@@ -507,6 +552,9 @@ main (int argc, char *argv[])
+ g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs);
+ g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc);
+ g_test_add_func ("/bytes/null", test_null);
++#ifdef GLIB_LINUX
++ g_test_add_func ("/bytes/memfd", test_memfd);
++#endif
+ g_test_add_func ("/bytes/get-region", test_get_region);
+ g_test_add_func ("/bytes/unref-null", test_unref_null);
+
+diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
+index c8f1336..3dc781c 100644
+--- a/glib/tests/gvariant.c
++++ b/glib/tests/gvariant.c
+@@ -18,14 +18,15 @@
+ #include "config.h"
+
+ #include <glib/gvariant-internal.h>
++#include <glib/glib-private.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <glib.h>
+
+-#define BASIC "bynqiuxthdsog?"
++#define BASIC "bynqiuxthfdsog?"
+ #define N_BASIC (G_N_ELEMENTS (BASIC) - 1)
+
+-#define INVALIDS "cefjklpwz&@^$"
++#define INVALIDS "cejklpwz&@^$"
+ #define N_INVALIDS (G_N_ELEMENTS (INVALIDS) - 1)
+
+ /* see comment in gvariant-serialiser.c about this madness.
+@@ -85,6 +86,8 @@ append_type_string (GString *string,
+ return g_variant_type_copy (G_VARIANT_TYPE_UINT64);
+ case 'h':
+ return g_variant_type_copy (G_VARIANT_TYPE_HANDLE);
++ case 'f':
++ return g_variant_type_copy (G_VARIANT_TYPE_FLOAT);
+ case 'd':
+ return g_variant_type_copy (G_VARIANT_TYPE_DOUBLE);
+ case 's':
+@@ -452,6 +455,8 @@ describe_type (const GVariantType *type)
+ result = g_strdup ("t");
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
+ result = g_strdup ("h");
++ else if (g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT))
++ result = g_strdup ("f");
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_DOUBLE))
+ result = g_strdup ("d");
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
+@@ -807,6 +812,7 @@ calculate_type_info (const GVariantType *type,
+
+ else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32) ||
+ g_variant_type_equal (type, G_VARIANT_TYPE_UINT32) ||
++ g_variant_type_equal (type, G_VARIANT_TYPE_FLOAT) ||
+ g_variant_type_equal (type, G_VARIANT_TYPE_HANDLE))
+ {
+ al = fs = 4;
+@@ -1960,6 +1966,7 @@ struct _TreeInstance
+
+ union {
+ guint64 integer;
++ gfloat single;
+ gdouble floating;
+ gchar string[200];
+ } data;
+@@ -2074,6 +2081,11 @@ tree_instance_new (const GVariantType *type,
+ instance->data_size = 8;
+ break;
+
++ case 'f':
++ instance->data.single = g_test_rand_double ();
++ instance->data_size = 4;
++ break;
++
+ case 'd':
+ instance->data.floating = g_test_rand_double ();
+ instance->data_size = 8;
+@@ -2597,6 +2609,10 @@ tree_instance_get_gvariant (TreeInstance *tree)
+ result = g_variant_new_handle (tree->data.integer);
+ break;
+
++ case 'f':
++ result = g_variant_new_float (tree->data.single);
++ break;
++
+ case 'd':
+ result = g_variant_new_double (tree->data.floating);
+ break;
+@@ -2620,6 +2636,19 @@ tree_instance_get_gvariant (TreeInstance *tree)
+ return result;
+ }
+
++static GVariant *
++create_random_gvariant (guint depth)
++{
++ TreeInstance *tree;
++ GVariant *value;
++
++ tree = tree_instance_new (NULL, depth);
++ value = g_variant_take_ref (tree_instance_get_gvariant (tree));
++ tree_instance_free (tree);
++
++ return value;
++}
++
+ static gboolean
+ tree_instance_check_gvariant (TreeInstance *tree,
+ GVariant *value)
+@@ -2724,6 +2753,13 @@ tree_instance_check_gvariant (TreeInstance *tree,
+ case 'h':
+ return g_variant_get_handle (value) == (gint32) tree->data.integer;
+
++ case 'f':
++ {
++ gfloat floating = g_variant_get_float (value);
++
++ return memcmp (&floating, &tree->data.single, sizeof floating) == 0;
++ }
++
+ case 'd':
+ {
+ gdouble floating = g_variant_get_double (value);
+@@ -3897,6 +3933,7 @@ test_gv_byteswap_non_normal_non_aligned (void)
+ static void
+ test_parser (void)
+ {
++ GError *error = NULL;
+ TreeInstance *tree;
+ GVariant *parsed;
+ GVariant *value;
+@@ -3910,16 +3947,19 @@ test_parser (void)
+ pt = g_variant_print (value, TRUE);
+ p = g_variant_print (value, FALSE);
+
+- parsed = g_variant_parse (NULL, pt, NULL, NULL, NULL);
++ parsed = g_variant_parse (NULL, pt, NULL, NULL, &error);
++ g_assert_no_error (error);
+ res = g_variant_print (parsed, FALSE);
+- g_assert_cmpstr (p, ==, res);
++ if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
++ g_assert_cmpstr (p, ==, res);
+ g_variant_unref (parsed);
+ g_free (res);
+
+- parsed = g_variant_parse (g_variant_get_type (value), p,
+- NULL, NULL, NULL);
++ parsed = g_variant_parse (g_variant_get_type (value), p, NULL, NULL, &error);
++ g_assert_no_error (error);
+ res = g_variant_print (parsed, TRUE);
+- g_assert_cmpstr (pt, ==, res);
++ if (!strstr (pt, "float")) /* FIXME: need reliable round-trip for floats */
++ g_assert_cmpstr (pt, ==, res);
+ g_variant_unref (parsed);
+ g_free (res);
+
+@@ -4925,10 +4965,13 @@ test_gbytes (void)
+ {
+ GVariant *a;
+ GVariant *tuple;
++ GVariant *b;
++ GVariant *c;
+ GBytes *bytes;
+ GBytes *bytes2;
+ const guint8 values[5] = { 1, 2, 3, 4, 5 };
+ const guint8 *elts;
++ gchar *tmp;
+ gsize n_elts;
+ gsize i;
+
+@@ -4959,6 +5002,41 @@ test_gbytes (void)
+ g_bytes_unref (bytes2);
+ g_variant_unref (a);
+ g_variant_unref (tuple);
++
++ /* Feed in some non-normal data... make sure it's aligned.
++ *
++ * Here we have an array of three elements. The first and last are
++ * normal ints ('iiii') and array-of-bytes data ('ayay'). The middle
++ * element is zero-bytes wide, which will present a problem when
++ * fetching the fixed-size integer out of it.
++ * */
++ tmp = g_strdup ("iiiiayayiiiisayay\x08\x08\x10");
++ bytes = g_bytes_new_take (tmp, strlen (tmp));
++ a = g_variant_new_from_bytes (G_VARIANT_TYPE ("a(iay)"), bytes, FALSE);
++ g_bytes_unref (bytes);
++
++ /* The middle tuple is zero bytes */
++ b = g_variant_get_child_value (a, 1);
++ g_assert_cmpint (g_variant_get_size (b), ==, 0);
++
++ /* But we're going to pull a 4-byte child out of it... */
++ c = g_variant_get_child_value (b, 0);
++ g_assert_cmpint (g_variant_get_size (c), ==, 4);
++
++ /* g_variant_get_data() is allowed to fail in this case.
++ * NB: if someone finds a way to avoid this then that's fine too...
++ */
++ g_assert (g_variant_get_data (c) == NULL);
++
++ /* but since it's four bytes, it ought to have data... */
++ bytes = g_variant_get_data_as_bytes (c);
++ g_assert_cmpint (g_bytes_get_size (bytes), ==, 4);
++ g_assert (memcmp (g_bytes_get_data (bytes, NULL), "\0\0\0\0", 4) == 0);
++ g_bytes_unref (bytes);
++
++ g_variant_unref (c);
++ g_variant_unref (b);
++ g_variant_unref (a);
+ }
+
+ typedef struct {
+@@ -5002,6 +5080,81 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ G_GNUC_END_IGNORE_DEPRECATIONS
+ }
+
++static GByteArray *
++flatten_vectors (GVariantVectors *v)
++{
++ GByteArray *result;
++ guint i;
++
++ result = g_byte_array_new ();
++
++ for (i = 0; i < v->vectors->len; i++)
++ {
++ GVariantVector vec = g_array_index (v->vectors, GVariantVector, i);
++
++ if (vec.gbytes)
++ g_byte_array_append (result, vec.data.pointer, vec.size);
++ else
++ g_byte_array_append (result, v->extra_bytes->data + vec.data.offset, vec.size);
++ }
++
++ return result;
++}
++
++static void
++test_vector_serialiser (void)
++{
++ GVariantVectors vectors;
++ GByteArray *flattened;
++ GVariant *value;
++ guint i;
++
++ for (i = 0; i < 100; i++)
++ {
++ guint j;
++
++ value = create_random_gvariant (2);
++ //g_print (">>> %s\n", g_variant_print (value, TRUE));
++
++ GLIB_PRIVATE_CALL(g_variant_to_vectors) (value, &vectors);
++ for (j = 0; j < vectors.vectors->len; j++)
++ {
++ GVariantVector *v = &g_array_index (vectors.vectors, GVariantVector, j);
++
++ if (!v->gbytes)
++ {
++ v->gbytes = g_bytes_new (NULL, 0);
++ v->data.pointer = v->data.offset + vectors.extra_bytes->data;
++ }
++
++ //g_print (" V %p %p %d\n", v, v->data.pointer, (guint) v->size);
++ }
++ GLIB_PRIVATE_CALL(g_variant_from_vectors) (g_variant_get_type (value), (GVariantVector *) vectors.vectors->data, vectors.vectors->len, g_variant_get_size (value), TRUE);
++ continue;
++ flattened = flatten_vectors (&vectors);
++ g_byte_array_free (vectors.extra_bytes, TRUE);
++ g_byte_array_free (vectors.offsets, TRUE);
++ g_array_free (vectors.vectors, TRUE);
++
++#if 0
++ if (flattened->len != g_variant_get_size (value) ||
++ memcmp (flattened->data, g_variant_get_data (value), flattened->len) != 0)
++ {
++ g_file_set_contents ("flattened", flattened->data, flattened->len, NULL);
++ g_file_set_contents ("serialised", g_variant_get_data (value), g_variant_get_size (value), NULL);
++ g_print ("type is %s\n", g_variant_get_type_string (value));
++ g_assert_not_reached ();
++ }
++#endif
++
++ g_assert_cmpint (flattened->len, ==, g_variant_get_size (value));
++ g_assert (memcmp (flattened->data, g_variant_get_data (value), flattened->len) == 0);
++
++ g_byte_array_free (flattened, TRUE);
++ g_variant_unref (value);
++ }
++}
++
+ static void
+ test_stack_builder_init (void)
+ {
+@@ -5866,6 +6019,7 @@ main (int argc, char **argv)
+ g_test_add_func ("/gvariant/gbytes", test_gbytes);
+ g_test_add_func ("/gvariant/print-context", test_print_context);
+ g_test_add_func ("/gvariant/error-quark", test_error_quark);
++ g_test_add_func ("/gvariant/vector-serialiser", test_vector_serialiser);
+
+ g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init);
+ g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init);
+--
+2.25.1
+
diff --git a/packaging/kdbus2.patch b/packaging/kdbus2.patch
new file mode 100644
index 000000000..edf5f8fba
--- /dev/null
+++ b/packaging/kdbus2.patch
@@ -0,0 +1,8287 @@
+From 3800128928e1df0df28629e1d16250f342a6cfff Mon Sep 17 00:00:00 2001
+From: Karol Lewandowski <k.lewandowsk@samsung.com>
+Date: Tue, 6 Feb 2024 16:00:27 +0100
+Subject: [PATCH] tizen: Add kdbus
+
+This commit provides kdbus originally from upstream but later long
+maintained and significantly improved for Tizen project by Lukasz Skalski,
+Adrian Szyndela, Hyotaek Shim, Insun Pyo and Michal Eljasiewicz.
+
+See tizen_8.0 and older branches for complete history. The commits below
+list only the start of kdbus history (for identifiaction purposes).
+
+.
+
+commit f8c5231931f3a0d7b0508598e3f51e5cef13017f
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date: Fri Aug 21 17:42:53 2015 +0200
+
+ gdbus: integrate kdbus into GDBus core
+
+ Patch includes also changes made by Ryan Lortie <desrt@desrt.ca>
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: I6bab6fee34787816efeb42d8e3e60c4c551a8a2b
+
+commit d2e6e8fd848d104a4ae83af5eced5cf009b5f29c
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date: Fri Aug 21 17:41:27 2015 +0200
+
+ gdbus: add kdbus core files
+
+ Patch includes also changes made by Ryan Lortie <desrt@desrt.ca>
+ and Adrian Szyndela <a.szyndela@samsung.com>
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: Ie90076a28c611320cd9b5ed6f2123d0ef85c6326
+
+commit 2eae20f44d0300b3d151ae8a1f260bf122da702a
+Author: Lukasz Skalski <l.skalski@samsung.com>
+Date: Fri Aug 21 16:47:05 2015 +0200
+
+ gdbus: import kdbus interface header
+
+ kdbus.h taken from https://github.com/systemd/kdbus, commit 9c04b6b989c9.
+
+ Hosting of our own copy of the kdbus header is temporary solution.
+ In the future, we should include it from the system kernel headers.
+
+ https://bugzilla.gnome.org/show_bug.cgi?id=721861
+
+ Change-Id: I2a42c91648b10c7c14f4df9ce2755f7c60524903
+
+---
+ gio/gdbusaddress.c | 107 +-
+ gio/gdbusconnection.c | 1376 ++++++++--
+ gio/gdbusconnection.h | 42 +
+ gio/gdbusmessage.c | 7 +
+ gio/gdbusmethodinvocation.c | 38 +
+ gio/gdbusmethodinvocation.h | 5 +
+ gio/gdbusnameowning.c | 19 +-
+ gio/gdbusnamewatching.c | 24 +-
+ gio/gdbusobjectmanagerclient.c | 34 +-
+ gio/gdbusprivate.c | 202 +-
+ gio/gdbusprivate.h | 20 +-
+ gio/gdbusproxy.c | 20 +-
+ gio/gdbusutils.c | 9 +
+ gio/gioenums.h | 60 +
+ gio/gkdbus.c | 4356 ++++++++++++++++++++++++++++++++
+ gio/gkdbus.h | 203 ++
+ gio/gkdbusfakedaemon.c | 694 +++++
+ gio/gkdbusfakedaemon.h | 39 +
+ gio/gunixfdlist.h | 3 +
+ gio/meson.build | 19 +-
+ meson_options.txt | 10 +
+ 21 files changed, 6936 insertions(+), 351 deletions(-)
+ mode change 100644 => 100755 gio/gdbusconnection.c
+ mode change 100644 => 100755 gio/gdbusnamewatching.c
+ create mode 100755 gio/gkdbus.c
+ create mode 100644 gio/gkdbus.h
+ create mode 100644 gio/gkdbusfakedaemon.c
+ create mode 100644 gio/gkdbusfakedaemon.h
+
+diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
+index b73ff0d..e23dff8 100644
+--- a/gio/gdbusaddress.c
++++ b/gio/gdbusaddress.c
+@@ -42,6 +42,9 @@
+ #include "gdbusprivate.h"
+ #include "gstdio.h"
+
++#ifdef G_OS_UNIX
++#include "gkdbus.h"
++#endif
+ #ifdef HAVE_UNISTD_H
+ #include <unistd.h>
+ #endif
+@@ -550,8 +553,9 @@ out:
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
+-static GIOStream *
++static GObject *
+ g_dbus_address_try_connect_one (const gchar *address_entry,
++ gboolean kdbus_okay,
+ gchar **out_guid,
+ GCancellable *cancellable,
+ GError **error);
+@@ -561,14 +565,15 @@ g_dbus_address_try_connect_one (const gchar *address_entry,
+ * point. That way we can implement a D-Bus transport over X11 without
+ * making libgio link to libX11...
+ */
+-static GIOStream *
++static GObject *
+ g_dbus_address_connect (const gchar *address_entry,
+ const gchar *transport_name,
++ gboolean kdbus_okay,
+ GHashTable *key_value_pairs,
+ GCancellable *cancellable,
+ GError **error)
+ {
+- GIOStream *ret;
++ GObject *ret;
+ GSocketConnectable *connectable;
+ const gchar *nonce_file;
+
+@@ -576,7 +581,35 @@ g_dbus_address_connect (const gchar *address_entry,
+ ret = NULL;
+ nonce_file = NULL;
+
+- if (g_strcmp0 (transport_name, "unix") == 0)
++ if (FALSE)
++ {
++ }
++#if defined (G_OS_UNIX) && defined (KDBUS)
++ else if (kdbus_okay && g_str_equal (transport_name, "kernel"))
++ {
++ GKDBusWorker *worker;
++ const gchar *path;
++
++ path = g_hash_table_lookup (key_value_pairs, "path");
++
++ if (path == NULL)
++ {
++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
++ _("Error in address '%s' - the kernel transport requires a path"),
++ address_entry);
++ }
++ else
++ {
++ worker = _g_kdbus_worker_new (path, error);
++
++ if (worker == NULL)
++ return NULL;
++
++ return G_OBJECT (worker);
++ }
++ }
++#endif
++ else if (g_strcmp0 (transport_name, "unix") == 0)
+ {
+ const gchar *path;
+ const gchar *abstract;
+@@ -665,7 +698,7 @@ g_dbus_address_connect (const gchar *address_entry,
+ autolaunch_address = get_session_address_dbus_launch (error);
+ if (autolaunch_address != NULL)
+ {
+- ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error);
++ ret = g_dbus_address_try_connect_one (autolaunch_address, kdbus_okay, NULL, cancellable, error);
+ g_free (autolaunch_address);
+ goto out;
+ }
+@@ -707,7 +740,7 @@ g_dbus_address_connect (const gchar *address_entry,
+ if (connection == NULL)
+ goto out;
+
+- ret = G_IO_STREAM (connection);
++ ret = G_OBJECT (connection);
+
+ if (nonce_file != NULL)
+ {
+@@ -763,7 +796,7 @@ g_dbus_address_connect (const gchar *address_entry,
+ }
+ fclose (f);
+
+- if (!g_output_stream_write_all (g_io_stream_get_output_stream (ret),
++ if (!g_output_stream_write_all (g_io_stream_get_output_stream (G_IO_STREAM (connection)),
+ nonce_contents,
+ 16,
+ NULL,
+@@ -783,13 +816,14 @@ g_dbus_address_connect (const gchar *address_entry,
+ return ret;
+ }
+
+-static GIOStream *
++static GObject *
+ g_dbus_address_try_connect_one (const gchar *address_entry,
++ gboolean kdbus_okay,
+ gchar **out_guid,
+ GCancellable *cancellable,
+ GError **error)
+ {
+- GIOStream *ret;
++ GObject *ret;
+ GHashTable *key_value_pairs;
+ gchar *transport_name;
+ const gchar *guid;
+@@ -806,6 +840,7 @@ g_dbus_address_try_connect_one (const gchar *address_entry,
+
+ ret = g_dbus_address_connect (address_entry,
+ transport_name,
++ kdbus_okay,
+ key_value_pairs,
+ cancellable,
+ error);
+@@ -969,7 +1004,25 @@ g_dbus_address_get_stream_sync (const gchar *address,
+ GCancellable *cancellable,
+ GError **error)
+ {
+- GIOStream *ret;
++ GObject *result;
++
++ result = g_dbus_address_get_stream_internal (address, FALSE, out_guid, cancellable, error);
++ g_assert (result == NULL || G_IS_IO_STREAM (result));
++
++ if (result)
++ return G_IO_STREAM (result);
++
++ return NULL;
++}
++
++GObject *
++g_dbus_address_get_stream_internal (const gchar *address,
++ gboolean kdbus_okay,
++ gchar **out_guid,
++ GCancellable *cancellable,
++ GError **error)
++{
++ GObject *ret;
+ gchar **addr_array;
+ guint n;
+ GError *last_error;
+@@ -996,6 +1049,7 @@ g_dbus_address_get_stream_sync (const gchar *address,
+
+ this_error = NULL;
+ ret = g_dbus_address_try_connect_one (addr,
++ kdbus_okay,
+ out_guid,
+ cancellable,
+ &this_error);
+@@ -1071,6 +1125,30 @@ out:
+ #endif
+ }
+
++static gchar *
++get_session_address_kdbus (void)
++{
++#ifdef G_OS_UNIX
++ gchar *ret = NULL;
++ gchar *bus;
++ GStatBuf buf;
++
++ bus = g_strdup_printf ("/sys/fs/kdbus/%d-user/bus", getuid());
++
++ /* if ENOENT, EPERM, etc., quietly don't use it */
++ if (g_stat (bus, &buf) < 0)
++ goto out;
++
++ ret = g_strconcat ("kernel:path=", bus, NULL);
++
++out:
++ g_free (bus);
++ return ret;
++#else
++ return NULL;
++#endif
++}
++
+ /* ---------------------------------------------------------------------------------------------------- */
+
+ #ifdef G_OS_UNIX
+@@ -1299,6 +1377,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
+ if (G_UNLIKELY (_g_dbus_debug_address ()))
+ {
+ guint n;
++ gchar *s;
+ _g_dbus_debug_print_lock ();
+ s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type);
+ g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n",
+@@ -1342,7 +1421,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#ftn.id-1.13.6.4.3.3
+ * or, on systems where /run is the same as /var/run, runstatedir:
+ * https://gitlab.freedesktop.org/dbus/dbus/-/merge_requests/209 */
+- ret = g_strdup ("unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
++ ret = g_strdup ("kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=" GLIB_RUNSTATEDIR "/dbus/system_bus_socket");
+ }
+ break;
+
+@@ -1354,7 +1433,11 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type,
+
+ if (ret == NULL)
+ {
+- ret = get_session_address_platform_specific (&local_error);
++ ret = get_session_address_kdbus ();
++ if (ret == NULL)
++ ret = get_session_address_platform_specific (&local_error);
++ if (ret == NULL)
++ goto out;
+ }
+ break;
+
+diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
+old mode 100644
+new mode 100755
+index 1ee2ab4..960a83c
+--- a/gio/gdbusconnection.c
++++ b/gio/gdbusconnection.c
+@@ -116,6 +116,9 @@
+ #include "gmarshal-internal.h"
+
+ #ifdef G_OS_UNIX
++#ifdef KDBUS
++#include "gkdbus.h"
++#endif
+ #include "gunixconnection.h"
+ #include "gunixfdmessage.h"
+ #endif
+@@ -365,6 +368,12 @@ struct _GDBusConnection
+ */
+ GDBusWorker *worker;
+
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ GKDBusWorker *kdbus_worker;
++#endif
++#endif
++
+ /* If connected to a message bus, this contains the unique name assigned to
+ * us by the bus (e.g. ":1.42").
+ * Read-only after initable_init(), so it may be read if you either
+@@ -603,6 +612,7 @@ g_dbus_connection_dispose (GObject *object)
+
+ G_LOCK (message_bus_lock);
+ CONNECTION_LOCK (connection);
++
+ if (connection->worker != NULL)
+ {
+ _g_dbus_worker_stop (connection->worker);
+@@ -610,6 +620,17 @@ g_dbus_connection_dispose (GObject *object)
+ if (alive_connections != NULL)
+ g_warn_if_fail (g_hash_table_remove (alive_connections, connection));
+ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker != NULL)
++ {
++ _g_kdbus_worker_stop (connection->kdbus_worker);
++ connection->kdbus_worker = NULL;
++ if (alive_connections != NULL)
++ g_warn_if_fail (g_hash_table_remove (alive_connections, connection));
++ }
++#endif
++#endif
+ else
+ {
+ if (alive_connections != NULL)
+@@ -1096,6 +1117,19 @@ g_dbus_connection_init (GDBusConnection *connection)
+ connection->filters = g_ptr_array_new ();
+ }
+
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++gboolean
++_g_dbus_connection_is_kdbus (GDBusConnection *connection)
++{
++ if (connection->kdbus_worker)
++ return TRUE;
++ else
++ return FALSE;
++}
++#endif
++#endif
++
+ /**
+ * g_dbus_connection_get_stream:
+ * @connection: a #GDBusConnection
+@@ -1142,8 +1176,16 @@ g_dbus_connection_start_message_processing (GDBusConnection *connection)
+ if (!check_initialized (connection))
+ return;
+
+- g_assert (connection->worker != NULL);
+- _g_dbus_worker_unfreeze (connection->worker);
++ if (connection->worker)
++ _g_dbus_worker_unfreeze (connection->worker);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ _g_kdbus_worker_unfreeze (connection->kdbus_worker);
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+ }
+
+ /**
+@@ -1334,11 +1376,22 @@ g_dbus_connection_flush_sync (GDBusConnection *connection,
+ if (!check_unclosed (connection, 0, error))
+ goto out;
+
+- g_assert (connection->worker != NULL);
+-
+- ret = _g_dbus_worker_flush_sync (connection->worker,
+- cancellable,
+- error);
++ if (connection->worker != NULL)
++ {
++ ret = _g_dbus_worker_flush_sync (connection->worker,
++ cancellable,
++ error);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker != NULL)
++ {
++ ret = _g_kdbus_worker_flush_sync (connection->kdbus_worker);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached();
+
+ out:
+ return ret;
+@@ -1438,7 +1491,7 @@ schedule_closed_unlocked (GDBusConnection *connection,
+ * of the thread that @connection was constructed in.
+ *
+ * This is an asynchronous method. When the operation is finished,
+- * @callback will be invoked in the
++ * @callback will be invoked in the
+ * [thread-default main context][g-main-context-push-thread-default]
+ * of the thread you are calling this method from. You can
+ * then call g_dbus_connection_close_finish() to get the result of the
+@@ -1457,108 +1510,763 @@ g_dbus_connection_close (GDBusConnection *connection,
+
+ g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+
+- /* do not use g_return_val_if_fail(), we want the memory barrier */
+- if (!check_initialized (connection))
+- return;
++ /* do not use g_return_val_if_fail(), we want the memory barrier */
++ if (!check_initialized (connection))
++ return;
++
++ task = g_task_new (connection, cancellable, callback, user_data);
++ g_task_set_source_tag (task, g_dbus_connection_close);
++ if (connection->worker)
++ {
++ _g_dbus_worker_close (connection->worker, task);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ _g_kdbus_worker_close (connection->kdbus_worker, task);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached();
++ g_object_unref (task);
++}
++
++/**
++ * g_dbus_connection_close_finish:
++ * @connection: a #GDBusConnection
++ * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed
++ * to g_dbus_connection_close()
++ * @error: return location for error or %NULL
++ *
++ * Finishes an operation started with g_dbus_connection_close().
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ *
++ * Since: 2.26
++ */
++gboolean
++g_dbus_connection_close_finish (GDBusConnection *connection,
++ GAsyncResult *res,
++ GError **error)
++{
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++ g_return_val_if_fail (g_task_is_valid (res, connection), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ return g_task_propagate_boolean (G_TASK (res), error);
++}
++
++typedef struct {
++ GMainLoop *loop;
++ GAsyncResult *result;
++} SyncCloseData;
++
++/* Can be called by any thread, without the connection lock */
++static void
++sync_close_cb (GObject *source_object,
++ GAsyncResult *res,
++ gpointer user_data)
++{
++ SyncCloseData *data = user_data;
++
++ data->result = g_object_ref (res);
++ g_main_loop_quit (data->loop);
++}
++
++/**
++ * g_dbus_connection_close_sync:
++ * @connection: a #GDBusConnection
++ * @cancellable: (nullable): a #GCancellable or %NULL
++ * @error: return location for error or %NULL
++ *
++ * Synchronously closes @connection. The calling thread is blocked
++ * until this is done. See g_dbus_connection_close() for the
++ * asynchronous version of this method and more details about what it
++ * does.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ *
++ * Since: 2.26
++ */
++gboolean
++g_dbus_connection_close_sync (GDBusConnection *connection,
++ GCancellable *cancellable,
++ GError **error)
++{
++ gboolean ret;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ ret = FALSE;
++
++ if (check_unclosed (connection, 0, error))
++ {
++ GMainContext *context;
++ SyncCloseData data;
++
++ context = g_main_context_new ();
++ g_main_context_push_thread_default (context);
++ data.loop = g_main_loop_new (context, TRUE);
++ data.result = NULL;
++
++ g_dbus_connection_close (connection, cancellable, sync_close_cb, &data);
++ g_main_loop_run (data.loop);
++ ret = g_dbus_connection_close_finish (connection, data.result, error);
++
++ g_object_unref (data.result);
++ g_main_loop_unref (data.loop);
++ g_main_context_pop_thread_default (context);
++ g_main_context_unref (context);
++ }
++
++ return ret;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/**
++ * g_dbus_request_name:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name (name to request)
++ * @flags: a set of flags from the GBusNameOwnerFlags enumeration
++ * @error: return location for error or %NULL
++ *
++ * Synchronously acquires name on the bus and returns status code
++ * from the #GBusRequestNameReplyFlags enumeration.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: status code or %G_BUS_REQUEST_NAME_FLAGS_ERROR
++ * if @error is set.
++ *
++ * Since: 2.44
++ */
++GBusRequestNameReplyFlags
++g_dbus_request_name (GDBusConnection *connection,
++ const gchar *name,
++ GBusNameOwnerFlags flags,
++ GError **error)
++{
++ GVariant *result;
++ guint32 request_name_reply;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++ g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++ g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR);
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_RequestName (connection->kdbus_worker, name, flags, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++ "org.freedesktop.DBus", "RequestName",
++ g_variant_new ("(su)", name, flags), G_VARIANT_TYPE ("(u)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(u)", &request_name_reply);
++ g_variant_unref (result);
++ }
++ else
++ request_name_reply = G_BUS_REQUEST_NAME_FLAGS_ERROR;
++
++ return (GBusRequestNameReplyFlags) request_name_reply;
++}
++
++/**
++ * g_dbus_release_name:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name (name to release)
++ * @error: return location for error or %NULL
++ *
++ * Synchronously releases name on the bus and returns status code
++ * from the #GBusReleaseNameReplyFlags enumeration.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: status code or %G_BUS_RELEASE_NAME_FLAGS_ERROR
++ * if @error is set.
++ *
++ * Since: 2.44
++ */
++GBusReleaseNameReplyFlags
++g_dbus_release_name (GDBusConnection *connection,
++ const gchar *name,
++ GError **error)
++{
++ GVariant *result;
++ guint32 release_name_reply;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++ g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR);
++ g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR);
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_ReleaseName (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++ "org.freedesktop.DBus", "ReleaseName",
++ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(u)", &release_name_reply);
++ g_variant_unref (result);
++ }
++ else
++ release_name_reply = G_BUS_RELEASE_NAME_FLAGS_ERROR;
++
++ return (GBusReleaseNameReplyFlags) release_name_reply;
++}
++
++/**
++ * g_dbus_add_match:
++ * @connection: a #GDBusConnection
++ * @match_rule: match rule to add to the @connection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously adds a match rule to match messages.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE
++ * if @error is set.
++ *
++ * Since: 2.44
++ */
++gboolean
++g_dbus_add_match (GDBusConnection *connection,
++ const gchar *match_rule,
++ GError **error)
++{
++ GVariant *result;
++ gboolean ret;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ result = NULL;
++ ret = FALSE;
++
++ if (match_rule[0] == '-')
++ return ret;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_AddMatch (connection->kdbus_worker, match_rule, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++ "org.freedesktop.DBus", "AddMatch",
++ g_variant_new ("(s)", match_rule), NULL,
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ ret = TRUE;
++ g_variant_unref (result);
++ }
++
++ return ret;
++}
++
++/**
++ * g_dbus_remove_match:
++ * @connection: a #GDBusConnection
++ * @match_rule: match rule to remove from the @connection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously removes the first rule that matches.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: %TRUE if the operation succeeded, %FALSE
++ * if @error is set.
++ *
++ * Since: 2.44
++ */
++gboolean
++g_dbus_remove_match (GDBusConnection *connection,
++ const gchar *match_rule,
++ GError **error)
++{
++ GVariant *result;
++ gboolean ret;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ result = NULL;
++ ret = FALSE;
++
++ if (match_rule[0] == '-')
++ return ret;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_RemoveMatch (connection->kdbus_worker, match_rule, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus",
++ "org.freedesktop.DBus", "RemoveMatch",
++ g_variant_new ("(s)", match_rule), NULL,
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ ret = TRUE;
++ g_variant_unref (result);
++ }
++
++ return ret;
++}
++
++/**
++ * g_dbus_get_bus_id:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique ID of the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique ID of the bus or %NULL if @error is set.
++ * Free with g_free().
++ *
++ * Since: 2.44
++ */
++gchar *
++g_dbus_get_bus_id (GDBusConnection *connection,
++ GError **error)
++{
++ GVariant *result;
++ gchar *bus_id;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ result = NULL;
++ bus_id = NULL;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetBusId (connection->kdbus_worker, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "GetId",
++ NULL, G_VARIANT_TYPE ("(s)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(s)", &bus_id);
++ g_variant_unref (result);
++ }
++
++ return bus_id;
++}
++
++typedef enum
++{
++ LIST_NAMES,
++ LIST_ACTIVATABLE_NAMES,
++ LIST_QUEUED_OWNERS
++} GDBusListNameType;
++
++static gchar **
++_g_dbus_get_list_internal (GDBusConnection *connection,
++ const gchar *name,
++ GDBusListNameType list_name_type,
++ GError **error)
++{
++ gchar **strv;
++ GVariant *result;
++ GVariantIter *iter;
++ gchar *str;
++ gsize n, i;
++
++ result = NULL;
++ strv = NULL;
++
++ if (list_name_type == LIST_QUEUED_OWNERS)
++ {
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetListQueuedOwners (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "ListQueuedOwners",
++ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(as)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ }
++ else
++ {
++ gchar *method_name;
++
++ if (list_name_type == LIST_NAMES)
++ method_name = "ListNames";
++ else
++ method_name = "ListActivatableNames";
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetListNames (connection->kdbus_worker, list_name_type, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", method_name,
++ NULL, G_VARIANT_TYPE ("(as)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ }
++
++ if (result != NULL)
++ {
++ g_variant_get (result, "(as)", &iter);
++ n = g_variant_iter_n_children (iter);
++ strv = g_new (gchar *, n + 1);
++
++ i = 0;
++ while (g_variant_iter_loop (iter, "s", &str))
++ {
++ strv[i] = g_strdup (str);
++ i++;
++ }
++ strv[i] = NULL;
++
++ g_variant_iter_free (iter);
++ g_variant_unref (result);
++ }
++
++ return strv;
++}
++
++/**
++ * g_dbus_get_list_names:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns a list of all currently-owned names on the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: a list of all currently-owned names on the bus or %NULL if
++ * @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_names (GDBusConnection *connection,
++ GError **error)
++{
++ gchar **strv;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ strv = _g_dbus_get_list_internal (connection, NULL, LIST_NAMES, error);
++
++ return strv;
++}
++
++/**
++ * g_dbus_get_list_activatable_names:
++ * @connection: a #GDBusConnection
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns a list of all names that can be activated on the bus.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: a list of all names that can be activated on the bus or %NULL if
++ * @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_activatable_names (GDBusConnection *connection,
++ GError **error)
++{
++ gchar **strv;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ strv = _g_dbus_get_list_internal (connection, NULL, LIST_ACTIVATABLE_NAMES, error);
++
++ return strv;
++}
++
++/**
++ * g_dbus_get_list_queued_names:
++ * @connection: a #GDBusConnection
++ * @name: a unique or well-known bus name
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique bus names of connections currently queued
++ * for the @name.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns %NULL and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique bus names of connections currently queued for the @name
++ * or %NULL if @error is set. Free with g_strfreev().
++ *
++ * Since: 2.44
++ */
++gchar **
++g_dbus_get_list_queued_owners (GDBusConnection *connection,
++ const gchar *name,
++ GError **error)
++{
++ gchar **strv;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++ g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ strv = _g_dbus_get_list_internal (connection, name, LIST_QUEUED_OWNERS, error);
++
++ return strv;
++}
++
++/**
++ * g_dbus_get_name_owner:
++ * @connection: a #GDBusConnection
++ * @name: well-known bus name to get the owner of
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the unique connection name of the primary owner of
++ * the name given. If the requested name doesn't have an owner, an @error is
++ * returned.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns %NULL and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the unique connection name of the primary owner of the
++ * name given. If the requested name doesn't have an owner, function
++ * returns %NULL and @error is set. Free with g_free().
++ *
++ * Since: 2.44
++ */
++gchar *
++g_dbus_get_name_owner (GDBusConnection *connection,
++ const gchar *name,
++ GError **error)
++{
++ GVariant *result;
++ gchar *name_owner;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
++ g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL);
++ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
++
++ name_owner = NULL;
++ result = NULL;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetNameOwner (connection->kdbus_worker, name, error);
++#endif
++#endif
++
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "GetNameOwner",
++ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(s)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(s)", &name_owner);
++ g_variant_unref (result);
++ }
++ else
++ name_owner = NULL;
++
++ return name_owner;
++}
++
++/**
++ * g_dbus_get_connection_pid:
++ * @connection: a #GDBusConnection
++ * @name: a unique or well-known bus name of the connection to query
++ * @error: return location for error or %NULL
++ *
++ * Synchronously returns the Unix process ID of the process connected to the
++ * bus. If unable to determine it, an @error is returned.
++ *
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and @error is set.
++ *
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the Unix process ID of the process connected to the bus or -1
++ * if @error is set.
++ *
++ * Since: 2.44
++ */
++pid_t
++g_dbus_get_connection_pid (GDBusConnection *connection,
++ const gchar *name,
++ GError **error)
++{
++ GVariant *result;
++ pid_t pid;
++
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++ g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++ g_return_val_if_fail (error == NULL || *error == NULL, -1);
++
++ result = NULL;
++ pid = -1;
++
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetConnectionUnixProcessID (connection->kdbus_worker, name, error);
++#endif
++#endif
+
+- g_assert (connection->worker != NULL);
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "GetConnectionUnixProcessID",
++ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(u)", &pid);
++ g_variant_unref (result);
++ }
+
+- task = g_task_new (connection, cancellable, callback, user_data);
+- g_task_set_source_tag (task, g_dbus_connection_close);
+- _g_dbus_worker_close (connection->worker, task);
+- g_object_unref (task);
++ return pid;
+ }
+
+ /**
+- * g_dbus_connection_close_finish:
++ * g_dbus_get_connection_uid:
+ * @connection: a #GDBusConnection
+- * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed
+- * to g_dbus_connection_close()
++ * @name: a unique or well-known bus name of the connection to query
+ * @error: return location for error or %NULL
+ *
+- * Finishes an operation started with g_dbus_connection_close().
++ * Synchronously returns the Unix user ID of the process connected to the
++ * bus. If unable to determine it, an @error is returned.
+ *
+- * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ * If @name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and @error is set.
+ *
+- * Since: 2.26
++ * The calling thread is blocked until a reply is received.
++ *
++ * Returns: the Unix user ID of the process connected to the bus or -1
++ * if @error is set.
++ *
++ * Since: 2.44
+ */
+-gboolean
+-g_dbus_connection_close_finish (GDBusConnection *connection,
+- GAsyncResult *res,
+- GError **error)
++uid_t
++g_dbus_get_connection_uid (GDBusConnection *connection,
++ const gchar *name,
++ GError **error)
+ {
+- g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+- g_return_val_if_fail (g_task_is_valid (res, connection), FALSE);
+- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++ GVariant *result;
++ uid_t uid;
+
+- return g_task_propagate_boolean (G_TASK (res), error);
+-}
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++ g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+-typedef struct {
+- GMainLoop *loop;
+- GAsyncResult *result;
+-} SyncCloseData;
++ result = NULL;
++ uid = -1;
+
+-/* Can be called by any thread, without the connection lock */
+-static void
+-sync_close_cb (GObject *source_object,
+- GAsyncResult *res,
+- gpointer user_data)
+-{
+- SyncCloseData *data = user_data;
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_GetConnectionUnixUser (connection->kdbus_worker, name, error);
++#endif
++#endif
+
+- data->result = g_object_ref (res);
+- g_main_loop_quit (data->loop);
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "GetConnectionUnixUser",
++ g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(u)", &uid);
++ g_variant_unref (result);
++ }
++
++ return uid;
+ }
+
+ /**
+- * g_dbus_connection_close_sync:
++ * g_dbus_start_service_by_name:
+ * @connection: a #GDBusConnection
+- * @cancellable: (nullable): a #GCancellable or %NULL
++ * @name: name of the service to start
++ * @flags: (currently not used)
+ * @error: return location for error or %NULL
+ *
+- * Synchronously closes @connection. The calling thread is blocked
+- * until this is done. See g_dbus_connection_close() for the
+- * asynchronous version of this method and more details about what it
+- * does.
++ * Synchronously tries to launch the executable associated
++ * with a @name.
+ *
+- * Returns: %TRUE if the operation succeeded, %FALSE if @error is set
++ * The calling thread is blocked until a reply is received.
+ *
+- * Since: 2.26
++ * Returns: status code or %G_BUS_START_SERVICE_REPLY_ERROR
++ * if @error is set.
++ *
++ * Since: 2.44
+ */
+-gboolean
+-g_dbus_connection_close_sync (GDBusConnection *connection,
+- GCancellable *cancellable,
++GBusStartServiceReplyFlags
++g_dbus_start_service_by_name (GDBusConnection *connection,
++ const gchar *name,
++ guint32 flags,
+ GError **error)
+ {
+- gboolean ret;
+-
+- g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
+- g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+-
+- ret = FALSE;
++ GVariant *result;
++ guint32 ret;
+
+- if (check_unclosed (connection, 0, error))
+- {
+- GMainContext *context;
+- SyncCloseData data;
++ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1);
++ g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1);
++ g_return_val_if_fail (error == NULL || *error == NULL, -1);
+
+- context = g_main_context_new ();
+- g_main_context_push_thread_default (context);
+- data.loop = g_main_loop_new (context, TRUE);
+- data.result = NULL;
++ result = NULL;
++ ret = G_BUS_START_SERVICE_REPLY_ERROR;
+
+- g_dbus_connection_close (connection, cancellable, sync_close_cb, &data);
+- g_main_loop_run (data.loop);
+- ret = g_dbus_connection_close_finish (connection, data.result, error);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker)
++ return _g_kdbus_StartServiceByName (connection->kdbus_worker, name, flags, NULL, error);
++#endif
++#endif
+
+- g_object_unref (data.result);
+- g_main_loop_unref (data.loop);
+- g_main_context_pop_thread_default (context);
+- g_main_context_unref (context);
++ result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/",
++ "org.freedesktop.DBus", "StartServiceByName",
++ g_variant_new ("(su)", name, flags), G_VARIANT_TYPE ("(u)"),
++ G_DBUS_CALL_FLAGS_NONE, -1, NULL, error);
++ if (result != NULL)
++ {
++ g_variant_get (result, "(u)", &ret);
++ g_variant_unref (result);
+ }
+
+- return ret;
++ return (GBusStartServiceReplyFlags) ret;
+ }
+
+ /* ---------------------------------------------------------------------------------------------------- */
+@@ -1601,11 +2309,13 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
+ GDBusMessage *message,
+ GDBusSendMessageFlags flags,
+ guint32 *out_serial,
+- GError **error)
++ GError **error,
++ gint timeout_msec)
+ {
+ guchar *blob;
+ gsize blob_size;
+ guint32 serial_to_use;
++ gboolean ret;
+
+ CONNECTION_ENSURE_LOCK (connection);
+
+@@ -1614,6 +2324,9 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
+
+ /* TODO: check all necessary headers are present */
+
++ ret = FALSE;
++ blob = NULL;
++
+ if (out_serial != NULL)
+ *out_serial = 0;
+
+@@ -1625,33 +2338,41 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
+ if (!check_unclosed (connection,
+ (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0,
+ error))
+- return FALSE;
++ goto out;
+
+- blob = g_dbus_message_to_blob (message,
+- &blob_size,
+- connection->capabilities,
+- error);
+- if (blob == NULL)
+- return FALSE;
++ if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
++ g_dbus_message_set_serial (message, ++connection->last_serial);
+
+- if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)
+- serial_to_use = g_dbus_message_get_serial (message);
+- else
+- serial_to_use = ++connection->last_serial; /* TODO: handle overflow */
++ serial_to_use = g_dbus_message_get_serial (message);
+
+- switch (blob[0])
++ /*
++ * serializes message to a blob
++ */
++ if (connection->worker)
+ {
+- case 'l':
+- ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use);
+- break;
+- case 'B':
+- ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use);
+- break;
+- default:
+- g_assert_not_reached ();
+- break;
++ blob = g_dbus_message_to_blob (message,
++ &blob_size,
++ connection->capabilities,
++ error);
++ if (blob == NULL)
++ goto out;
++
++ switch (blob[0])
++ {
++ case 'l':
++ ((guint32 *) blob)[2] = GUINT32_TO_LE (serial_to_use);
++ break;
++ case 'B':
++ ((guint32 *) blob)[2] = GUINT32_TO_BE (serial_to_use);
++ break;
++ default:
++ g_assert_not_reached ();
++ break;
++ }
+ }
+
++ g_dbus_message_lock (message);
++
+ #if 0
+ g_printerr ("Writing message of %" G_GSIZE_FORMAT " bytes (serial %d) on %p:\n",
+ blob_size, serial_to_use, connection);
+@@ -1674,17 +2395,32 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
+ g_thread_self (),
+ GUINT_TO_POINTER (serial_to_use));
+
+- if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
+- g_dbus_message_set_serial (message, serial_to_use);
++ ret = TRUE;
+
+- g_dbus_message_lock (message);
++ if (connection->worker)
++ {
++ _g_dbus_worker_send_message (connection->worker,
++ message,
++ (gchar*) blob,
++ blob_size);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ ret = _g_kdbus_worker_send_message (connection->kdbus_worker, message, timeout_msec, error);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+
+- _g_dbus_worker_send_message (connection->worker,
+- message,
+- (gchar*) blob, /* transfer ownership */
+- blob_size);
++ blob = NULL; /* since _g_dbus_worker_send_message() steals the blob */
+
+- return TRUE;
++ out:
++ g_free (blob);
++
++ return ret;
+ }
+
+ /**
+@@ -1738,7 +2474,7 @@ g_dbus_connection_send_message (GDBusConnection *connection,
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+- ret = g_dbus_connection_send_message_unlocked (connection, message, flags, (guint32 *) out_serial, error);
++ ret = g_dbus_connection_send_message_unlocked (connection, message, flags, (guint32 *) out_serial, error, -1);
+ CONNECTION_UNLOCK (connection);
+ return ret;
+ }
+@@ -1921,9 +2657,6 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect
+ if (out_serial == NULL)
+ out_serial = &serial;
+
+- if (timeout_msec == -1)
+- timeout_msec = 25 * 1000;
+-
+ data = g_slice_new0 (SendMessageData);
+ task = g_task_new (connection, cancellable, callback, user_data);
+ g_task_set_source_tag (task,
+@@ -1936,7 +2669,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect
+ return;
+ }
+
+- if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error))
++ if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error, timeout_msec))
+ {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+@@ -1952,9 +2685,16 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect
+ g_object_unref);
+ }
+
+- if (timeout_msec != G_MAXINT)
++ if (timeout_msec != G_MAXINT
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ /* Userspace timeouts unnecessary on unix/kdbus - kdbus handles timeouts. */
++ && !connection->kdbus_worker
++#endif
++#endif
++ )
+ {
+- data->timeout_source = g_timeout_source_new (timeout_msec);
++ data->timeout_source = g_timeout_source_new (timeout_msec == -1 ? DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec);
+ g_source_set_static_name (data->timeout_source, "[gio] send_message_with_reply_unlocked");
+ g_task_attach_source (task, data->timeout_source,
+ (GSourceFunc) send_message_with_reply_timeout_cb);
+@@ -1996,7 +2736,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect
+ * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT.
+ *
+ * This is an asynchronous method. When the operation is finished, @callback
+- * will be invoked in the
++ * will be invoked in the
+ * [thread-default main context][g-main-context-push-thread-default]
+ * of the thread you are calling this method from. You can then call
+ * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation.
+@@ -2158,32 +2898,102 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connecti
+ g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+- data.res = NULL;
+- data.context = g_main_context_new ();
+- data.loop = g_main_loop_new (data.context, FALSE);
++ if (connection->worker)
++ {
++ data.res = NULL;
++ data.context = g_main_context_new ();
++ data.loop = g_main_loop_new (data.context, FALSE);
+
+- g_main_context_push_thread_default (data.context);
++ g_main_context_push_thread_default (data.context);
+
+- g_dbus_connection_send_message_with_reply (connection,
+- message,
+- flags,
+- timeout_msec,
+- out_serial,
+- cancellable,
+- (GAsyncReadyCallback) send_message_with_reply_sync_cb,
+- &data);
+- g_main_loop_run (data.loop);
+- reply = g_dbus_connection_send_message_with_reply_finish (connection,
+- data.res,
+- error);
++ g_dbus_connection_send_message_with_reply (connection,
++ message,
++ flags,
++ timeout_msec,
++ out_serial,
++ cancellable,
++ (GAsyncReadyCallback) send_message_with_reply_sync_cb,
++ &data);
++ g_main_loop_run (data.loop);
++ reply = g_dbus_connection_send_message_with_reply_finish (connection,
++ data.res,
++ error);
++
++ g_main_context_pop_thread_default (data.context);
++
++ g_main_context_unref (data.context);
++ g_main_loop_unref (data.loop);
++ if (data.res)
++ g_object_unref (data.res);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ /* kdbus supports blocking synchronous calls, so let's use them instead of mainloops */
++
++ volatile guint32 serial;
++ guint32 serial_to_use;
++
++ reply = NULL;
++
++ CONNECTION_LOCK (connection);
++
++ if (out_serial == NULL)
++ out_serial = &serial;
++ else
++ *out_serial = 0;
++
++ /*
++ * Check that we never actually send a message if the GCancellable
++ * is already cancelled
++ */
++ if (g_cancellable_is_cancelled (cancellable))
++ {
++ g_set_error_literal (error,
++ G_IO_ERROR,
++ G_IO_ERROR_CANCELLED,
++ _("Operation was cancelled"));
++ CONNECTION_UNLOCK (connection);
++ goto out;
++ }
++
++ if (!check_unclosed (connection,
++ (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0,
++ error))
++ {
++ CONNECTION_UNLOCK (connection);
++ goto out;
++ }
++
++ if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
++ g_dbus_message_set_serial (message, ++connection->last_serial);
++
++ serial_to_use = g_dbus_message_get_serial (message);
+
+- g_main_context_pop_thread_default (data.context);
++ g_dbus_message_lock (message);
++
++ if (out_serial != NULL)
++ *out_serial = serial_to_use;
++
++ CONNECTION_UNLOCK (connection);
+
+- g_main_context_unref (data.context);
+- g_main_loop_unref (data.loop);
+- if (data.res)
+- g_object_unref (data.res);
++ /* Reference is still kept for the connection, so there is no worry
++ * that it will be freed. Same for connection->kdbus_worker - by reference
++ * from the connection. Inside _g_kdbus_worker_send_message_sync only
++ * initialized-once, read-only fields from kdbus_worker are used, except
++ * 'matches', which now has got its own mutex.
++ */
++ _g_kdbus_worker_send_message_sync (connection->kdbus_worker, message,
++ &reply, timeout_msec, cancellable, error);
++
++ }
++#endif /* G_OS_UNIX */
++#endif /* G_OS_UNIX */
++ else
++ g_assert_not_reached ();
+
++out:
+ return reply;
+ }
+
+@@ -2252,8 +3062,7 @@ free_filter_list (FilterData **filters)
+
+ /* Called in GDBusWorker's thread - we must not block - with no lock held */
+ static void
+-on_worker_message_received (GDBusWorker *worker,
+- GDBusMessage *message,
++on_worker_message_received (GDBusMessage *message,
+ gpointer user_data)
+ {
+ GDBusConnection *connection;
+@@ -2272,7 +3081,25 @@ on_worker_message_received (GDBusWorker *worker,
+ g_object_ref (connection);
+ G_UNLOCK (message_bus_lock);
+
+- //g_debug ("in on_worker_message_received");
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (_g_dbus_connection_is_kdbus (connection))
++ {
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " <<<< RECEIVED D-Bus message\n");
++ s = g_dbus_message_print (message, 2);
++ g_print ("%s", s);
++ g_free (s);
++ _g_dbus_debug_print_unlock ();
++ }
++ }
++#endif
++#endif
+
+ g_object_ref (message);
+ g_dbus_message_lock (message);
+@@ -2350,8 +3177,7 @@ on_worker_message_received (GDBusWorker *worker,
+
+ /* Called in GDBusWorker's thread, lock is not held */
+ static GDBusMessage *
+-on_worker_message_about_to_be_sent (GDBusWorker *worker,
+- GDBusMessage *message,
++on_worker_message_about_to_be_sent (GDBusMessage *message,
+ gpointer user_data)
+ {
+ GDBusConnection *connection;
+@@ -2424,10 +3250,9 @@ cancel_method_on_close (gpointer key, gpointer value, gpointer user_data)
+
+ /* Called in GDBusWorker's thread - we must not block - without lock held */
+ static void
+-on_worker_closed (GDBusWorker *worker,
+- gboolean remote_peer_vanished,
+- GError *error,
+- gpointer user_data)
++on_worker_closed (gboolean remote_peer_vanished,
++ GError *error,
++ gpointer user_data)
+ {
+ GDBusConnection *connection;
+ gboolean alive;
+@@ -2489,6 +3314,7 @@ initable_init (GInitable *initable,
+ GError **error)
+ {
+ GDBusConnection *connection = G_DBUS_CONNECTION (initable);
++ gboolean initially_frozen;
+ gboolean ret;
+
+ /* This method needs to be idempotent to work with the singleton
+@@ -2526,6 +3352,8 @@ initable_init (GInitable *initable,
+ */
+ if (connection->address != NULL)
+ {
++ GObject *ret;
++
+ g_assert (connection->stream == NULL);
+
+ if ((connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) ||
+@@ -2539,12 +3367,22 @@ initable_init (GInitable *initable,
+ goto out;
+ }
+
+- connection->stream = g_dbus_address_get_stream_sync (connection->address,
+- NULL, /* TODO: out_guid */
+- cancellable,
+- &connection->initialization_error);
+- if (connection->stream == NULL)
++ ret = g_dbus_address_get_stream_internal (connection->address, TRUE,
++ NULL, /* TODO: out_guid */
++ cancellable, &connection->initialization_error);
++ if (ret == NULL)
+ goto out;
++
++ if (G_IS_IO_STREAM (ret))
++ connection->stream = G_IO_STREAM (ret);
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (G_IS_KDBUS_WORKER (ret))
++ connection->kdbus_worker = G_KDBUS_WORKER (ret);
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+ }
+ else if (connection->stream != NULL)
+ {
+@@ -2555,6 +3393,22 @@ initable_init (GInitable *initable,
+ g_assert_not_reached ();
+ }
+
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ /* Skip authentication process for kdbus transport */
++ if (connection->kdbus_worker)
++ {
++ if (!_g_kdbus_can_connect (connection->kdbus_worker,
++ &connection->initialization_error))
++ goto out;
++
++ /* kdbus connection always supports exchanging UNIX file descriptors with the remote peer */
++ connection->capabilities |= G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING;
++ goto authenticated;
++ }
++#endif
++#endif
++
+ /* Authenticate the connection */
+ if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER)
+ {
+@@ -2595,6 +3449,8 @@ initable_init (GInitable *initable,
+ connection->authentication_observer = NULL;
+ }
+
++authenticated:
++
+ //g_output_stream_flush (G_SOCKET_CONNECTION (connection->stream)
+
+ //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING);
+@@ -2615,13 +3471,34 @@ initable_init (GInitable *initable,
+ g_hash_table_add (alive_connections, connection);
+ G_UNLOCK (message_bus_lock);
+
+- connection->worker = _g_dbus_worker_new (connection->stream,
+- connection->capabilities,
+- ((connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0),
+- on_worker_message_received,
+- on_worker_message_about_to_be_sent,
+- on_worker_closed,
+- connection);
++ initially_frozen = (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0;
++
++ if (0)
++ {
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ _g_kdbus_worker_associate (connection->kdbus_worker,
++ connection->capabilities,
++ on_worker_message_received,
++ on_worker_message_about_to_be_sent,
++ on_worker_closed,
++ connection);
++ }
++#endif
++#endif
++ else
++ {
++ connection->worker = _g_dbus_worker_new (connection->stream,
++ connection->capabilities,
++ initially_frozen,
++ on_worker_message_received,
++ on_worker_message_about_to_be_sent,
++ on_worker_closed,
++ connection);
++ }
+
+ /* if a bus connection, call org.freedesktop.DBus.Hello - this is how we're getting a name */
+ if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
+@@ -2638,25 +3515,50 @@ initable_init (GInitable *initable,
+ goto out;
+ }
+
+- hello_result = g_dbus_connection_call_sync (connection,
+- "org.freedesktop.DBus", /* name */
+- "/org/freedesktop/DBus", /* path */
+- "org.freedesktop.DBus", /* interface */
+- "Hello",
+- NULL, /* parameters */
+- G_VARIANT_TYPE ("(s)"),
+- CALL_FLAGS_INITIALIZING,
+- -1,
+- NULL, /* TODO: cancellable */
+- &connection->initialization_error);
+- if (hello_result == NULL)
+- goto out;
++ if (connection->worker)
++ {
++ hello_result = g_dbus_connection_call_sync (connection,
++ "org.freedesktop.DBus", /* name */
++ "/org/freedesktop/DBus", /* path */
++ "org.freedesktop.DBus", /* interface */
++ "Hello",
++ NULL, /* parameters */
++ G_VARIANT_TYPE ("(s)"),
++ CALL_FLAGS_INITIALIZING,
++ -1,
++ NULL, /* TODO: cancellable */
++ &connection->initialization_error);
++ if (hello_result == NULL)
++ goto out;
++
++ g_variant_get (hello_result, "(s)", &connection->bus_unique_name);
++ g_variant_unref (hello_result);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ const gchar *unique_name;
++
++ unique_name = _g_kdbus_Hello (connection->kdbus_worker, &connection->initialization_error);
++ if (unique_name == NULL)
++ goto out;
+
+- g_variant_get (hello_result, "(s)", &connection->bus_unique_name);
+- g_variant_unref (hello_result);
+- //g_debug ("unique name is '%s'", connection->bus_unique_name);
++ connection->bus_unique_name = g_strdup (unique_name);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+ }
+
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ if (connection->kdbus_worker && !initially_frozen)
++ _g_kdbus_worker_unfreeze (connection->kdbus_worker);
++#endif
++#endif
++
+ ret = TRUE;
+ out:
+ if (!ret)
+@@ -3376,7 +4278,8 @@ add_match_rule (GDBusConnection *connection,
+ message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ NULL,
+- &error))
++ &error,
++ -1))
+ {
+ g_critical ("Error while sending AddMatch() message: %s", error->message);
+ g_error_free (error);
+@@ -3408,7 +4311,8 @@ remove_match_rule (GDBusConnection *connection,
+ message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ NULL,
+- &error))
++ &error,
++ -1))
+ {
+ /* If we could get G_IO_ERROR_CLOSED here, it wouldn't be reasonable to
+ * critical; but we're holding the lock, and our caller checked whether
+@@ -3455,7 +4359,7 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
+ * subscription is removed or %NULL
+ *
+ * Subscribes to signals on @connection and invokes @callback whenever
+- * the signal is received. Note that @callback will be invoked in the
++ * the signal is received. Note that @callback will be invoked in the
+ * [thread-default main context][g-main-context-push-thread-default]
+ * of the thread you are calling this method from.
+ *
+@@ -3603,8 +4507,60 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
+ */
+ if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
+ {
+- if (!is_signal_data_for_name_lost_or_acquired (signal_data))
+- add_match_rule (connection, signal_data->rule);
++ if (connection->worker)
++ {
++ if (!is_signal_data_for_name_lost_or_acquired (signal_data))
++ add_match_rule (connection, signal_data->rule);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ gboolean special_rule = FALSE;
++ /* rule for special message */
++ if (!signal_data->sender_unique_name[0] || g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0)
++ {
++ if (signal_data->sender_unique_name[0]) /* So, this is org.freedesktop.DBus */
++ special_rule = TRUE;
++ if (!signal_data->object_path || g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0)
++ {
++ if (!signal_data->interface_name || g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0)
++ {
++ /* By https://dbus.freedesktop.org/doc/dbus-specification.html, org.freedesktop.DBus
++ * has three signal types: NameAcquired, NameLost, NameOwnerChanged (all covered below).
++ *
++ * 1. if sender is NULL and other parameters are matched with special rule, add BOTH special and standard,
++ * - (NULL, NULL or DBUS, NULL or DBUS, NULL or Special Name)
++ *
++ * if other parameters are NOT matched with special rule, add standard rule
++ * - (NULL, X, X, "NotSpecial")
++ *
++ * 2. if sender is 'org.freedesktop.DBus' and other parameters are matched with special rule, add special rule
++ * - (org.freedesktop.DBus, NULL or DBUS, NULL or DBUS, NULL or Special Name)
++ *
++ * if other parameters are NOT matched with special rule, then ignore
++ * - (org.freedesktop.DBus, X, X, "NotSpecial")
++ *
++ * for every other cases, add standard rule,
++ */
++
++ if (g_strcmp0 (signal_data->member, "NameAcquired") == 0)
++ _g_kdbus_subscribe_name_acquired (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++ else if (g_strcmp0 (signal_data->member, "NameLost") == 0)
++ _g_kdbus_subscribe_name_lost (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++ else if (!signal_data->member || g_strcmp0 (signal_data->member, "NameOwnerChanged") == 0)
++ _g_kdbus_subscribe_name_owner_changed (connection->kdbus_worker, signal_data->rule, arg0, NULL);
++ }
++ }
++ }
++ /* standard rule */
++ if (!special_rule)
++ _g_kdbus_AddMatch (connection->kdbus_worker, signal_data->rule, NULL);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+ }
+
+ signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array,
+@@ -3683,7 +4639,6 @@ unsubscribe_id_internal (GDBusConnection *connection,
+
+ /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */
+ if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) &&
+- !is_signal_data_for_name_lost_or_acquired (signal_data) &&
+ !g_dbus_connection_is_closed (connection) &&
+ !connection->finalizing)
+ {
+@@ -3693,7 +4648,21 @@ unsubscribe_id_internal (GDBusConnection *connection,
+ * so on_worker_closed() can't happen between the check we just
+ * did, and releasing the lock later.
+ */
+- remove_match_rule (connection, signal_data->rule);
++ if (connection->worker)
++ {
++ if (!is_signal_data_for_name_lost_or_acquired (signal_data))
++ remove_match_rule (connection, signal_data->rule);
++ }
++#ifdef G_OS_UNIX
++#ifdef KDBUS
++ else if (connection->kdbus_worker)
++ {
++ _g_kdbus_RemoveMatch (connection->kdbus_worker, signal_data->rule, NULL);
++ }
++#endif
++#endif
++ else
++ g_assert_not_reached ();
+ }
+
+ signal_data_free (signal_data);
+@@ -3777,7 +4746,7 @@ emit_signal_instance_in_idle_cb (gpointer data)
+ }
+ else
+ {
+- g_variant_ref_sink (parameters);
++ g_variant_ref (parameters);
+ }
+
+ #if 0
+@@ -4421,7 +5390,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ _("No such property “%s”"),
+ property_name);
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -4433,7 +5402,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ _("Property “%s” is not readable"),
+ property_name);
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -4444,7 +5413,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ _("Property “%s” is not writable"),
+ property_name);
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -4465,7 +5434,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect
+ _("Error setting property “%s”: Expected type “%s” but got “%s”"),
+ property_name, property_info->signature,
+ g_variant_get_type_string (value));
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_variant_unref (value);
+ g_object_unref (reply);
+ handled = TRUE;
+@@ -4569,7 +5538,7 @@ handle_getset_property (GDBusConnection *connection,
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ _("No such interface “%s”"),
+ interface_name);
+- g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -4784,7 +5753,7 @@ handle_get_all_properties (GDBusConnection *connection,
+ "org.freedesktop.DBus.Error.InvalidArgs",
+ _("No such interface “%s”"),
+ interface_name);
+- g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -4968,7 +5937,7 @@ handle_introspect (GDBusConnection *connection,
+
+ reply = g_dbus_message_new_method_reply (message);
+ g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ g_string_free (s, TRUE);
+
+@@ -5101,7 +6070,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection,
+ "org.freedesktop.DBus.Error.UnknownMethod",
+ _("No such method “%s”"),
+ g_dbus_message_get_member (message));
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ handled = TRUE;
+ goto out;
+@@ -5133,7 +6102,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection,
+ _("Type of message, “%s”, does not match expected type “%s”"),
+ g_variant_get_type_string (parameters),
+ type_string);
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_variant_type_free (in_type);
+ g_variant_unref (parameters);
+ g_object_unref (reply);
+@@ -5250,7 +6219,7 @@ obj_message_func (GDBusConnection *connection,
+ * D-Bus interface that is described in @interface_info.
+ *
+ * Calls to functions in @vtable (and @user_data_free_func) will happen
+- * in the
++ * in the
+ * [thread-default main context][g-main-context-push-thread-default]
+ * of the thread you are calling this method from.
+ *
+@@ -5818,6 +6787,16 @@ decode_method_reply (GDBusMessage *reply,
+
+ case G_DBUS_MESSAGE_TYPE_ERROR:
+ g_dbus_message_to_gerror (reply, error);
++ if (error == NULL || *error == NULL)
++ break;
++ if ((*error)->code == G_DBUS_ERROR_NO_REPLY)
++ {
++ g_clear_error(error);
++ g_set_error (error,
++ G_IO_ERROR,
++ G_IO_ERROR_TIMED_OUT,
++ _("Timeout was reached"));
++ }
+ break;
+
+ default:
+@@ -7076,7 +8055,7 @@ handle_generic_ping_unlocked (GDBusConnection *connection,
+ {
+ GDBusMessage *reply;
+ reply = g_dbus_message_new_method_reply (message);
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ }
+
+@@ -7109,7 +8088,7 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection,
+ reply = g_dbus_message_new_method_reply (message);
+ g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id));
+ }
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ }
+
+@@ -7136,7 +8115,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection,
+
+ reply = g_dbus_message_new_method_reply (message);
+ g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+ g_string_free (s, TRUE);
+ }
+@@ -7288,7 +8267,7 @@ distribute_method_call (GDBusConnection *connection,
+ path);
+ }
+
+- g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
++ g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1);
+ g_object_unref (reply);
+
+ out:
+@@ -7602,3 +8581,10 @@ g_bus_get_finish (GAsyncResult *res,
+ }
+
+ /* ---------------------------------------------------------------------------------------------------- */
++#ifdef _TIZEN_DBUS_TOUCH
++static void __attribute__((constructor)) glib_type_dbus_connection_gc( void )
++{
++ GObject* temp = g_type_class_ref(G_TYPE_DBUS_CONNECTION);
++ g_type_class_unref(temp);
++}
++#endif
+diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h
+index feda7c0..b4452b3 100644
+--- a/gio/gdbusconnection.h
++++ b/gio/gdbusconnection.h
+@@ -32,6 +32,8 @@
+
+ G_BEGIN_DECLS
+
++#define DBUS_DEFAULT_TIMEOUT_MSEC 25000U
++
+ #define G_TYPE_DBUS_CONNECTION (g_dbus_connection_get_type ())
+ #define G_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_CONNECTION, GDBusConnection))
+ #define G_IS_DBUS_CONNECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_CONNECTION))
+@@ -94,6 +96,46 @@ GDBusConnection *g_dbus_connection_new_for_address_sync (const gchar
+
+ /* ---------------------------------------------------------------------------------------------------- */
+
++gboolean _g_dbus_connection_is_kdbus (GDBusConnection *connection) TIZEN_PUBLIC_DEPRECATED_API;
++
++GBusRequestNameReplyFlags g_dbus_request_name (GDBusConnection *connection,
++ const gchar *name,
++ GBusNameOwnerFlags flags,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++GBusReleaseNameReplyFlags g_dbus_release_name (GDBusConnection *connection,
++ const gchar *name,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gboolean g_dbus_add_match (GDBusConnection *connection,
++ const gchar *match_rule,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gboolean g_dbus_remove_match (GDBusConnection *connection,
++ const gchar *match_rule,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar *g_dbus_get_bus_id (GDBusConnection *connection,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar **g_dbus_get_list_names (GDBusConnection *connection,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar **g_dbus_get_list_activatable_names (GDBusConnection *connection,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar **g_dbus_get_list_queued_owners (GDBusConnection *connection,
++ const gchar *name,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++gchar *g_dbus_get_name_owner (GDBusConnection *connection,
++ const gchar *name,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++pid_t g_dbus_get_connection_pid (GDBusConnection *connection,
++ const gchar *name,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++uid_t g_dbus_get_connection_uid (GDBusConnection *connection,
++ const gchar *name,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++GBusStartServiceReplyFlags g_dbus_start_service_by_name (GDBusConnection *connection,
++ const gchar *name,
++ guint32 flags,
++ GError **error) TIZEN_PUBLIC_DEPRECATED_API;
++
++/* ---------------------------------------------------------------------------------------------------- */
++
+ GIO_AVAILABLE_IN_ALL
+ void g_dbus_connection_start_message_processing (GDBusConnection *connection) TIZEN_PUBLIC_DEPRECATED_API;
+ GIO_AVAILABLE_IN_ALL
+diff --git a/gio/gdbusmessage.c b/gio/gdbusmessage.c
+index adddb31..3894d19 100644
+--- a/gio/gdbusmessage.c
++++ b/gio/gdbusmessage.c
+@@ -3868,3 +3868,10 @@ g_dbus_message_copy (GDBusMessage *message,
+ #endif
+ return ret;
+ }
++
++void
++g_dbus_message_init_header_iter (GDBusMessage *message,
++ GHashTableIter *iter)
++{
++ g_hash_table_iter_init (iter, message->headers);
++}
+diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c
+index e5a9166..cd210c2 100644
+--- a/gio/gdbusmethodinvocation.c
++++ b/gio/gdbusmethodinvocation.c
+@@ -310,6 +310,44 @@ g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation)
+ return invocation->parameters;
+ }
+
++/**
++ * g_dbus_method_invocation_peek_unix_fd:
++ * @invocation: A #GDBusMethodInvocation.
++ * @index_: the index
++ *
++ * Gets the fd associated with @index in the method invocation.
++ *
++ * If there is no file descriptor at the given index, -1 is returned.
++ *
++ * The returned file descriptor is owned by the message and must not be
++ * closed by the caller. Use dup() if you want your own copy.
++ *
++ * Returns: the file descriptor, or -1
++ */
++#ifdef G_OS_UNIX
++gint
++g_dbus_method_invocation_peek_unix_fd (GDBusMethodInvocation *invocation,
++ guint index_)
++{
++ GUnixFDList *fd_list;
++
++ fd_list = g_dbus_message_get_unix_fd_list (invocation->message);
++
++ if (fd_list)
++ {
++ const gint *fds;
++ gint n_fds;
++
++ fds = g_unix_fd_list_peek_fds (fd_list, &n_fds);
++
++ if (index_ < (guint) n_fds)
++ return fds[index_];
++ }
++
++ return -1;
++}
++#endif
++
+ /**
+ * g_dbus_method_invocation_get_user_data: (skip)
+ * @invocation: A #GDBusMethodInvocation.
+diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h
+index eecef03..bbfa190 100644
+--- a/gio/gdbusmethodinvocation.h
++++ b/gio/gdbusmethodinvocation.h
+@@ -91,6 +91,11 @@ GIO_AVAILABLE_IN_ALL
+ GDBusMessage *g_dbus_method_invocation_get_message (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
+ GIO_AVAILABLE_IN_ALL
+ GVariant *g_dbus_method_invocation_get_parameters (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
++#ifdef G_OS_UNIX
++GIO_AVAILABLE_IN_2_44
++gint g_dbus_method_invocation_peek_unix_fd (GDBusMethodInvocation *invocation,
++ guint index_) TIZEN_PUBLIC_DEPRECATED_API;
++#endif
+ GIO_AVAILABLE_IN_ALL
+ gpointer g_dbus_method_invocation_get_user_data (GDBusMethodInvocation *invocation) TIZEN_PUBLIC_DEPRECATED_API;
+
+diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c
+index 289ea03..5090d26 100644
+--- a/gio/gdbusnameowning.c
++++ b/gio/gdbusnameowning.c
+@@ -926,6 +926,19 @@ g_bus_unown_name (guint owner_id)
+ /* do callback without holding lock */
+ if (client != NULL)
+ {
++ /* Kdbus sends NameLost after replying to ReleaseName,
++ * contrary to dbus-daemon. Let's disable subscriptions now,
++ * as we're no longer interested in them, anyway. It prevents
++ * a race condition with receiving NameLost after
++ * an unowned name gets owned again.
++ */
++ if (client->name_lost_subscription_id > 0)
++ g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
++ if (client->name_acquired_subscription_id > 0)
++ g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
++ client->name_acquired_subscription_id = 0;
++ client->name_lost_subscription_id = 0;
++
+ /* Release the name if needed */
+ if (client->needs_release &&
+ client->connection != NULL &&
+@@ -975,13 +988,7 @@ g_bus_unown_name (guint owner_id)
+
+ if (client->disconnected_signal_handler_id > 0)
+ g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
+- if (client->name_acquired_subscription_id > 0)
+- g_dbus_connection_signal_unsubscribe (client->connection, client->name_acquired_subscription_id);
+- if (client->name_lost_subscription_id > 0)
+- g_dbus_connection_signal_unsubscribe (client->connection, client->name_lost_subscription_id);
+ client->disconnected_signal_handler_id = 0;
+- client->name_acquired_subscription_id = 0;
+- client->name_lost_subscription_id = 0;
+ if (client->connection != NULL)
+ {
+ g_object_unref (client->connection);
+diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c
+old mode 100644
+new mode 100755
+index c834fe1..fc230a8
+--- a/gio/gdbusnamewatching.c
++++ b/gio/gdbusnamewatching.c
+@@ -353,9 +353,6 @@ on_name_owner_changed (GDBusConnection *connection,
+ if (client == NULL)
+ return;
+
+- if (!client->initialized)
+- goto out;
+-
+ if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 ||
+ g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 ||
+ g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0)
+@@ -371,7 +368,7 @@ on_name_owner_changed (GDBusConnection *connection,
+ if (g_strcmp0 (name, client->name) != 0)
+ goto out;
+
+- if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL)
++ if (old_owner != NULL && strlen (old_owner) > 0)
+ {
+ g_free (client->name_owner);
+ client->name_owner = NULL;
+@@ -380,12 +377,18 @@ on_name_owner_changed (GDBusConnection *connection,
+
+ if (new_owner != NULL && strlen (new_owner) > 0)
+ {
+- g_warn_if_fail (client->name_owner == NULL);
+ g_free (client->name_owner);
+ client->name_owner = g_strdup (new_owner);
+ call_appeared_handler (client);
+ }
+
++ /* initialized set to TRUE means that signal was delivered and processed.
++ * Now, if we receive a reply to GetNameOwner call, we may just ignore it as it carries the same
++ * information as the current signal, or if something changed in the meantime we will
++ * get next signal very soon.
++ */
++ client->initialized = TRUE;
++
+ out:
+ client_unref (client);
+ }
+@@ -407,6 +410,13 @@ get_name_owner_cb (GObject *source_object,
+ result = g_dbus_connection_call_finish (client->connection,
+ res,
+ NULL);
++ /* In case we already received NameOwnerChanged signal, we don't need to
++ * process GetNameOwner answer, because all the information we needed was already
++ * delivered with the signal and processed by the signal handler.
++ */
++ if (client->initialized)
++ goto out;
++
+ if (result != NULL)
+ {
+ g_variant_get (result, "(&s)", &name_owner);
+@@ -423,10 +433,10 @@ get_name_owner_cb (GObject *source_object,
+ call_vanished_handler (client);
+ }
+
+- client->initialized = TRUE;
+-
++ out:
+ if (result != NULL)
+ g_variant_unref (result);
++
+ client_unref (client);
+ }
+
+diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c
+index b6b3b21..8997e8c 100644
+--- a/gio/gdbusobjectmanagerclient.c
++++ b/gio/gdbusobjectmanagerclient.c
+@@ -1133,7 +1133,6 @@ subscribe_signals (GDBusObjectManagerClient *manager,
+ const gchar *name_owner)
+ {
+ GError *error = NULL;
+- GVariant *ret;
+
+ g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
+ g_return_if_fail (manager->priv->signal_subscription_id == 0);
+@@ -1159,22 +1158,7 @@ subscribe_signals (GDBusObjectManagerClient *manager,
+
+ /* The bus daemon may not implement path_namespace so gracefully
+ * handle this by using a fallback triggered if @error is set. */
+- ret = g_dbus_connection_call_sync (manager->priv->connection,
+- "org.freedesktop.DBus",
+- "/org/freedesktop/DBus",
+- "org.freedesktop.DBus",
+- "AddMatch",
+- g_variant_new ("(s)",
+- manager->priv->match_rule),
+- NULL, /* reply_type */
+- G_DBUS_CALL_FLAGS_NONE,
+- -1, /* default timeout */
+- NULL, /* TODO: Cancellable */
+- &error);
+-
+- /* yay, bus daemon supports path_namespace */
+- if (ret != NULL)
+- g_variant_unref (ret);
++ g_dbus_add_match (manager->priv->connection, manager->priv->match_rule, &error);
+ }
+
+ if (error == NULL)
+@@ -1244,19 +1228,9 @@ maybe_unsubscribe_signals (GDBusObjectManagerClient *manager)
+ /* Since the AddMatch call succeeded this is guaranteed to not
+ * fail - therefore, don't bother checking the return value
+ */
+- g_dbus_connection_call (manager->priv->connection,
+- "org.freedesktop.DBus",
+- "/org/freedesktop/DBus",
+- "org.freedesktop.DBus",
+- "RemoveMatch",
+- g_variant_new ("(s)",
+- manager->priv->match_rule),
+- NULL, /* reply_type */
+- G_DBUS_CALL_FLAGS_NONE,
+- -1, /* default timeout */
+- NULL, /* GCancellable */
+- NULL, /* GAsyncReadyCallback */
+- NULL); /* user data */
++ g_dbus_remove_match (manager->priv->connection,
++ manager->priv->match_rule,
++ NULL);
+ g_free (manager->priv->match_rule);
+ manager->priv->match_rule = NULL;
+ }
+diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c
+index 2c9238c..4132e82 100644
+--- a/gio/gdbusprivate.c
++++ b/gio/gdbusprivate.c
+@@ -63,6 +63,10 @@
+
+ #include "glibintl.h"
+
++#define READ_BUFFER_MIN_READ_SIZE_FOR_HEADER 16
++/* TODO: 4096 is randomly chosen; might want a better chosen default minimum */
++#define READ_BUFFER_MIN_BUFFER_SIZE 4096
++
+ static gboolean _g_dbus_worker_do_initial_read (gpointer data);
+ static void schedule_pending_close (GDBusWorker *worker);
+
+@@ -481,7 +485,7 @@ _g_dbus_worker_emit_disconnected (GDBusWorker *worker,
+ GError *error)
+ {
+ if (!g_atomic_int_get (&worker->stopped))
+- worker->disconnected_callback (worker, remote_peer_vanished, error, worker->user_data);
++ worker->disconnected_callback (remote_peer_vanished, error, worker->user_data);
+ }
+
+ static void
+@@ -489,7 +493,7 @@ _g_dbus_worker_emit_message_received (GDBusWorker *worker,
+ GDBusMessage *message)
+ {
+ if (!g_atomic_int_get (&worker->stopped))
+- worker->message_received_callback (worker, message, worker->user_data);
++ worker->message_received_callback (message, worker->user_data);
+ }
+
+ static GDBusMessage *
+@@ -498,7 +502,7 @@ _g_dbus_worker_emit_message_about_to_be_sent (GDBusWorker *worker,
+ {
+ GDBusMessage *ret;
+ if (!g_atomic_int_get (&worker->stopped))
+- ret = worker->message_about_to_be_sent_callback (worker, g_steal_pointer (&message), worker->user_data);
++ ret = worker->message_about_to_be_sent_callback (g_steal_pointer (&message), worker->user_data);
+ else
+ ret = g_steal_pointer (&message);
+ return ret;
+@@ -567,6 +571,36 @@ _g_dbus_worker_unfreeze (GDBusWorker *worker)
+
+ static void _g_dbus_worker_do_read_unlocked (GDBusWorker *worker);
+
++static gssize _g_dbus_worker_buffer_get_next_full_message_length (GDBusWorker *worker)
++{
++ GError *error;
++
++ if (worker->read_buffer == NULL)
++ return 0;
++
++ if (worker->read_buffer_cur_size >= READ_BUFFER_MIN_READ_SIZE_FOR_HEADER)
++ {
++ gssize message_len;
++ /* OK, got the header - determine how many more bytes are needed */
++ error = NULL;
++ message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer,
++ READ_BUFFER_MIN_READ_SIZE_FOR_HEADER,
++ &error);
++ if (message_len == -1)
++ {
++ g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message);
++ _g_dbus_worker_emit_disconnected (worker, FALSE, error);
++ g_error_free (error);
++ return -1;
++ }
++ if (message_len <= worker->read_buffer_cur_size)
++ return message_len;
++
++ worker->read_buffer_bytes_wanted = message_len;
++ }
++ return 0;
++}
++
+ /* called in private thread shared by all GDBusConnection instances (without read-lock held) */
+ static void
+ _g_dbus_worker_do_read_cb (GInputStream *input_stream,
+@@ -576,6 +610,7 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream,
+ GDBusWorker *worker = user_data;
+ GError *error;
+ gssize bytes_read;
++ gssize message_len;
+
+ g_mutex_lock (&worker->read_lock);
+
+@@ -722,97 +757,106 @@ _g_dbus_worker_do_read_cb (GInputStream *input_stream,
+ read_message_print_transport_debug (bytes_read, worker);
+
+ worker->read_buffer_cur_size += bytes_read;
+- if (worker->read_buffer_bytes_wanted == worker->read_buffer_cur_size)
++
++ while ((message_len = _g_dbus_worker_buffer_get_next_full_message_length (worker) ) > 0)
+ {
+ /* OK, got what we asked for! */
+- if (worker->read_buffer_bytes_wanted == 16)
+- {
+- gssize message_len;
+- /* OK, got the header - determine how many more bytes are needed */
+- error = NULL;
+- message_len = g_dbus_message_bytes_needed ((guchar *) worker->read_buffer,
+- 16,
+- &error);
+- if (message_len == -1)
+- {
+- g_warning ("_g_dbus_worker_do_read_cb: error determining bytes needed: %s", error->message);
+- _g_dbus_worker_emit_disconnected (worker, FALSE, error);
+- g_error_free (error);
+- goto out;
+- }
++ GDBusMessage *message;
++ error = NULL;
+
+- worker->read_buffer_bytes_wanted = message_len;
+- _g_dbus_worker_do_read_unlocked (worker);
++ /* TODO: use connection->priv->auth to decode the message */
++
++ message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
++ message_len,
++ worker->capabilities,
++ &error);
++ if (message == NULL)
++ {
++ gchar *s;
++ s = _g_dbus_hexdump (worker->read_buffer, message_len, 2);
++ g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
++ "The error is: %s\n"
++ "The payload is as follows:\n"
++ "%s",
++ message_len,
++ error->message,
++ s);
++ g_free (s);
++ _g_dbus_worker_emit_disconnected (worker, FALSE, error);
++ g_error_free (error);
++ goto out;
+ }
+- else
++
++#ifdef G_OS_UNIX
++ if (worker->read_fd_list != NULL)
+ {
+- GDBusMessage *message;
+- error = NULL;
++ guint fds_needed;
++ GUnixFDList *fd_list_for_message;
+
+- /* TODO: use connection->priv->auth to decode the message */
++ fds_needed = g_dbus_message_get_num_unix_fds (message);
+
+- message = g_dbus_message_new_from_blob ((guchar *) worker->read_buffer,
+- worker->read_buffer_cur_size,
+- worker->capabilities,
+- &error);
+- if (message == NULL)
++ if ((guint)g_unix_fd_list_get_length (worker->read_fd_list) > fds_needed)
+ {
+- gchar *s;
+- s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+- g_warning ("Error decoding D-Bus message of %" G_GSIZE_FORMAT " bytes\n"
+- "The error is: %s\n"
+- "The payload is as follows:\n"
+- "%s",
+- worker->read_buffer_cur_size,
+- error->message,
+- s);
+- g_free (s);
+- _g_dbus_worker_emit_disconnected (worker, FALSE, error);
+- g_error_free (error);
+- goto out;
+- }
++ gint num_fds;
++ GUnixFDList *old_worker_read_fd_list;
++ gint *fds;
+
+-#ifdef G_OS_UNIX
+- if (worker->read_fd_list != NULL)
++ old_worker_read_fd_list = worker->read_fd_list;
++ fds = g_unix_fd_list_steal_fds (old_worker_read_fd_list, &num_fds);
++ fd_list_for_message = g_unix_fd_list_new_from_array (fds, fds_needed);
++ worker->read_fd_list = g_unix_fd_list_new_from_array (fds + fds_needed, num_fds - fds_needed);
++ g_object_unref (old_worker_read_fd_list);
++ g_free (fds);
++ }
++ else
+ {
+- g_dbus_message_set_unix_fd_list (message, worker->read_fd_list);
+- g_object_unref (worker->read_fd_list);
++ fd_list_for_message = worker->read_fd_list;
+ worker->read_fd_list = NULL;
+ }
++
++ if (fds_needed > 0)
++ g_dbus_message_set_unix_fd_list (message, fd_list_for_message);
++ g_object_unref (fd_list_for_message);
++ }
+ #endif
+
+- if (G_UNLIKELY (_g_dbus_debug_message ()))
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
++ message_len);
++ s = g_dbus_message_print (message, 2);
++ g_print ("%s", s);
++ g_free (s);
++ if (G_UNLIKELY (_g_dbus_debug_payload ()))
+ {
+- gchar *s;
+- _g_dbus_debug_print_lock ();
+- g_print ("========================================================================\n"
+- "GDBus-debug:Message:\n"
+- " <<<< RECEIVED D-Bus message (%" G_GSIZE_FORMAT " bytes)\n",
+- worker->read_buffer_cur_size);
+- s = g_dbus_message_print (message, 2);
+- g_print ("%s", s);
++ s = _g_dbus_hexdump (worker->read_buffer, message_len, 2);
++ g_print ("%s\n", s);
+ g_free (s);
+- if (G_UNLIKELY (_g_dbus_debug_payload ()))
+- {
+- s = _g_dbus_hexdump (worker->read_buffer, worker->read_buffer_cur_size, 2);
+- g_print ("%s\n", s);
+- g_free (s);
+- }
+- _g_dbus_debug_print_unlock ();
+ }
++ _g_dbus_debug_print_unlock ();
++ }
+
+- /* yay, got a message, go deliver it */
+- _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message));
++ /* yay, got a message, go deliver it */
++ _g_dbus_worker_queue_or_deliver_received_message (worker, g_steal_pointer (&message));
+
+- /* start reading another message! */
+- worker->read_buffer_bytes_wanted = 0;
+- worker->read_buffer_cur_size = 0;
+- _g_dbus_worker_do_read_unlocked (worker);
++ /* set up reading another message */
++ if (worker->read_buffer_cur_size > message_len)
++ {
++ memmove (worker->read_buffer,
++ worker->read_buffer + message_len,
++ worker->read_buffer_cur_size - message_len);
+ }
++ worker->read_buffer_cur_size -= message_len;
++ worker->read_buffer_bytes_wanted = 0;
+ }
+- else
++
++ if (message_len == 0)
+ {
+- /* didn't get all the bytes we requested - so repeat the request... */
++ /* start reading another message or repeat the request to get all the bytes */
+ _g_dbus_worker_do_read_unlocked (worker);
+ }
+
+@@ -837,15 +881,21 @@ _g_dbus_worker_do_read_unlocked (GDBusWorker *worker)
+ /* if bytes_wanted is zero, it means start reading a message */
+ if (worker->read_buffer_bytes_wanted == 0)
+ {
+- worker->read_buffer_cur_size = 0;
+- worker->read_buffer_bytes_wanted = 16;
++ if (worker->socket != NULL)
++ {
++ worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_BUFFER_SIZE;
++ }
++ else
++ {
++ worker->read_buffer_cur_size = 0;
++ worker->read_buffer_bytes_wanted = READ_BUFFER_MIN_READ_SIZE_FOR_HEADER;
++ }
+ }
+
+ /* ensure we have a (big enough) buffer */
+ if (worker->read_buffer == NULL || worker->read_buffer_bytes_wanted > worker->read_buffer_allocated_size)
+ {
+- /* TODO: 4096 is randomly chosen; might want a better chosen default minimum */
+- worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, 4096);
++ worker->read_buffer_allocated_size = MAX (worker->read_buffer_bytes_wanted, READ_BUFFER_MIN_BUFFER_SIZE);
+ worker->read_buffer = g_realloc (worker->read_buffer, worker->read_buffer_allocated_size);
+ }
+
+diff --git a/gio/gdbusprivate.h b/gio/gdbusprivate.h
+index e7a5bfa..898b11d 100644
+--- a/gio/gdbusprivate.h
++++ b/gio/gdbusprivate.h
+@@ -31,16 +31,13 @@ G_BEGIN_DECLS
+
+ typedef struct GDBusWorker GDBusWorker;
+
+-typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusWorker *worker,
+- GDBusMessage *message,
++typedef void (*GDBusWorkerMessageReceivedCallback) (GDBusMessage *message,
+ gpointer user_data);
+
+-typedef GDBusMessage *(*GDBusWorkerMessageAboutToBeSentCallback) (GDBusWorker *worker,
+- GDBusMessage *message,
++typedef GDBusMessage *(*GDBusWorkerMessageAboutToBeSentCallback) (GDBusMessage *message,
+ gpointer user_data);
+
+-typedef void (*GDBusWorkerDisconnectedCallback) (GDBusWorker *worker,
+- gboolean remote_peer_vanished,
++typedef void (*GDBusWorkerDisconnectedCallback) (gboolean remote_peer_vanished,
+ GError *error,
+ gpointer user_data);
+
+@@ -158,6 +155,17 @@ gchar *_g_dbus_hexencode (const gchar *str,
+ GDBusConnection *_g_bus_get_singleton_if_exists (GBusType bus_type);
+ void _g_bus_forget_singleton (GBusType bus_type);
+
++void g_dbus_message_init_header_iter (GDBusMessage *message,
++ GHashTableIter *iter);
++
++GObject *
++g_dbus_address_get_stream_internal (const gchar *address,
++ gboolean kdbus_okay,
++ gchar **out_uuid,
++ GCancellable *cancellable,
++ GError **error);
++
++
+ G_END_DECLS
+
+ #endif /* __G_DBUS_PRIVATE_H__ */
+diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
+index afc6fe9..3166dd6 100644
+--- a/gio/gdbusproxy.c
++++ b/gio/gdbusproxy.c
+@@ -2598,26 +2598,10 @@ out:
+ static const gchar *
+ get_destination_for_call (GDBusProxy *proxy)
+ {
+- const gchar *ret;
+-
+- ret = NULL;
+-
+- /* If proxy->priv->name is a unique name, then proxy->priv->name_owner
+- * is never NULL and always the same as proxy->priv->name. We use this
+- * knowledge to avoid checking if proxy->priv->name is a unique or
+- * well-known name.
+- */
+- ret = proxy->priv->name_owner;
+- if (ret != NULL)
+- goto out;
+-
+ if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
+- goto out;
+-
+- ret = proxy->priv->name;
++ return proxy->priv->name_owner;
+
+- out:
+- return ret;
++ return proxy->priv->name;
+ }
+
+ /* ---------------------------------------------------------------------------------------------------- */
+diff --git a/gio/gdbusutils.c b/gio/gdbusutils.c
+index 4b4b7e1..7fb3305 100644
+--- a/gio/gdbusutils.c
++++ b/gio/gdbusutils.c
+@@ -453,6 +453,11 @@ g_dbus_gvariant_to_gvalue (GVariant *value,
+ g_value_set_uint64 (out_gvalue, g_variant_get_uint64 (value));
+ break;
+
++ case G_VARIANT_CLASS_FLOAT:
++ g_value_init (out_gvalue, G_TYPE_FLOAT);
++ g_value_set_float (out_gvalue, g_variant_get_float (value));
++ break;
++
+ case G_VARIANT_CLASS_DOUBLE:
+ g_value_init (out_gvalue, G_TYPE_DOUBLE);
+ g_value_set_double (out_gvalue, g_variant_get_double (value));
+@@ -627,6 +632,10 @@ g_dbus_gvalue_to_gvariant (const GValue *gvalue,
+ ret = g_variant_ref_sink (g_variant_new_uint64 (g_value_get_uint64 (gvalue)));
+ break;
+
++ case G_VARIANT_CLASS_FLOAT:
++ ret = g_variant_ref_sink (g_variant_new_float (g_value_get_float (gvalue)));
++ break;
++
+ case G_VARIANT_CLASS_DOUBLE:
+ ret = g_variant_ref_sink (g_variant_new_double (g_value_get_double (gvalue)));
+ break;
+diff --git a/gio/gioenums.h b/gio/gioenums.h
+index c820cd3..c8b9eb1 100644
+--- a/gio/gioenums.h
++++ b/gio/gioenums.h
+@@ -999,6 +999,66 @@ typedef enum
+ /* When adding new flags, their numeric values must currently match those
+ * used in the D-Bus Specification. */
+
++/**
++ * GBusRequestNameReplyFlags:
++ * @G_BUS_REQUEST_NAME_FLAGS_ERROR: Error flag.
++ * @G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER: Caller is now the primary owner of the name, replacing
++ * any previous owner.
++ * @G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE: The name already had an owner, the application will be
++ * placed in a queue.
++ * @G_BUS_REQUEST_NAME_FLAGS_EXISTS: The name already has an owner.
++ * @G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER: The application trying to request ownership of a name
++ * is already the owner of it.
++ *
++ * Flags used in g_dbus_request_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++ G_BUS_REQUEST_NAME_FLAGS_ERROR = 0,
++ G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER = 1,
++ G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE = 2,
++ G_BUS_REQUEST_NAME_FLAGS_EXISTS = 3,
++ G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER = 4
++} GBusRequestNameReplyFlags;
++
++/**
++ * GBusReleaseNameReplyFlags:
++ * @G_BUS_RELEASE_NAME_FLAGS_ERROR: Error flag.
++ * @G_BUS_RELEASE_NAME_FLAGS_RELEASED: The caller has released his claim on the given name.
++ * @G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT: The given name does not exist on this bus.
++ * @G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER: The caller not waiting in the queue to own this name.
++ *
++ * Flags used in g_dbus_release_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++ G_BUS_RELEASE_NAME_FLAGS_ERROR = 0,
++ G_BUS_RELEASE_NAME_FLAGS_RELEASED = 1,
++ G_BUS_RELEASE_NAME_FLAGS_NON_EXISTENT = 2,
++ G_BUS_RELEASE_NAME_FLAGS_NOT_OWNER = 3
++} GBusReleaseNameReplyFlags;
++
++/**
++ * GBusStartServiceReplyFlags:
++ * @G_BUS_START_SERVICE_REPLY_ERROR: Error flag.
++ * @G_BUS_START_SERVICE_REPLY_SUCCESS: The service was successfully started.
++ * @G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING: A connection already owns the given name.
++ *
++ * Flags used in g_dbus_start_service_by_name().
++ *
++ * Since: 2.44
++ */
++typedef enum
++{
++ G_BUS_START_SERVICE_REPLY_ERROR = 0,
++ G_BUS_START_SERVICE_REPLY_SUCCESS = 1,
++ G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING = 2
++} GBusStartServiceReplyFlags;
++
+ /**
+ * GBusNameWatcherFlags:
+ * @G_BUS_NAME_WATCHER_FLAGS_NONE: No flags set.
+diff --git a/gio/gkdbus.c b/gio/gkdbus.c
+new file mode 100755
+index 0000000..aeb4408
+--- /dev/null
++++ b/gio/gkdbus.c
+@@ -0,0 +1,4356 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski <l.skalski@samsung.com>
++ * Author: Adrian Szyndela <adrian.s@samsung.com>
++ * Author: Michal Eljasiewicz <m.eljasiewic@samsung.com>
++ */
++
++#include "config.h"
++#include "gdbusconnection.h" /* DBUS_DEFAULT_TIMEOUT_MSEC */
++#include "gkdbus.h"
++#include "glib-unix.h"
++#include "glib-linux.h"
++#include "glibintl.h"
++#include "gvariant-serialiser.h"
++#include <linux/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
++
++#ifdef HAVE_SYS_UIO_H
++#include <sys/uio.h>
++#endif
++
++#ifdef LIBDBUSPOLICY
++#include <dbuspolicy/libdbuspolicy1.h>
++#endif
++
++#define DBUS_DAEMON_EMULATION
++#ifdef DBUS_DAEMON_EMULATION
++#include "gkdbusfakedaemon.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"
++
++#define KDBUS_MSG_MAX_SIZE 8192
++#define KDBUS_INFINITE_TIMEOUT_NS 0x3fffffffffffffffLLU
++
++#define RECEIVE_POOL_SIZE_DEFAULT_SIZE (16 * 1024LU * 1024LU)
++#define RECEIVE_POOL_SIZE_ENV_VAR_NAME "KDBUS_MEMORY_POOL_SIZE"
++#define RECEIVE_POOL_SIZE_MAX_MBYTES 64
++#define RECEIVE_POOL_SIZE_MIN_KBYTES 16
++
++#define KDBUS_MEMFD_THRESHOLD (512 * 1024LU)
++#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE 0x00200000 /* maximum size of message header and items */
++
++#define KDBUS_ALIGN8(l) (((l) + 7) & ~7)
++#define KDBUS_ALIGN8_PTR(p) ((void*) (uintptr_t)(p))
++
++#define KDBUS_ITEM_HEADER_SIZE G_STRUCT_OFFSET(struct kdbus_item, data)
++#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE)
++
++#define KDBUS_ITEM_NEXT(item) \
++ (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))
++
++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)")
++
++/*
++ * Macros for SipHash algorithm
++ */
++
++#define ROTL(x,b) (guint64)( ((x) << (b)) | ( (x) >> (64 - (b))) )
++
++#define U32TO8_LE(p, v) \
++ (p)[0] = (guint8)((v) ); (p)[1] = (guint8)((v) >> 8); \
++ (p)[2] = (guint8)((v) >> 16); (p)[3] = (guint8)((v) >> 24);
++
++#define U64TO8_LE(p, v) \
++ U32TO8_LE((p), (guint32)((v) )); \
++ U32TO8_LE((p) + 4, (guint32)((v) >> 32));
++
++#define U8TO64_LE(p) \
++ (((guint64)((p)[0]) ) | \
++ ((guint64)((p)[1]) << 8) | \
++ ((guint64)((p)[2]) << 16) | \
++ ((guint64)((p)[3]) << 24) | \
++ ((guint64)((p)[4]) << 32) | \
++ ((guint64)((p)[5]) << 40) | \
++ ((guint64)((p)[6]) << 48) | \
++ ((guint64)((p)[7]) << 56))
++
++#define SIPROUND \
++ do { \
++ v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \
++ v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \
++ v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \
++ v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \
++ } while(0)
++
++typedef GObjectClass GKDBusWorkerClass;
++
++struct _GKDBusWorker
++{
++ GObject parent_instance;
++
++ const gchar *address;
++ gint fd;
++
++ GMainContext *context;
++ GMainLoop *loop;
++ GThread *thread;
++ GSource *source;
++
++ gchar *kdbus_buffer;
++ guint64 receive_pool_size;
++ gchar *unique_name;
++ guint64 unique_id;
++
++ 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];
++
++ GMutex matches_mutex;
++ GList *matches;
++
++#ifdef LIBDBUSPOLICY
++ void *dbuspolicy;
++#endif
++
++ GDBusCapabilityFlags capabilities;
++ GDBusWorkerMessageReceivedCallback message_received_callback;
++ GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback;
++ GDBusWorkerDisconnectedCallback disconnected_callback;
++ gpointer user_data;
++};
++
++static gboolean _g_kdbus_send (GKDBusWorker *worker,
++ GDBusMessage *message,
++ GDBusMessage **reply,
++ gint timeout_msec,
++ GCancellable *cancellable,
++ GError **error);
++
++static void _g_kdbus_receive (GKDBusWorker *worker,
++ GError **error);
++
++G_DEFINE_TYPE (GKDBusWorker, g_kdbus_worker, G_TYPE_OBJECT)
++
++/* Hash keys for bloom filters*/
++const guint8 hash_keys[8][16] =
++{
++ {0xb9,0x66,0x0b,0xf0,0x46,0x70,0x47,0xc1,0x88,0x75,0xc4,0x9c,0x54,0xb9,0xbd,0x15},
++ {0xaa,0xa1,0x54,0xa2,0xe0,0x71,0x4b,0x39,0xbf,0xe1,0xdd,0x2e,0x9f,0xc5,0x4a,0x3b},
++ {0x63,0xfd,0xae,0xbe,0xcd,0x82,0x48,0x12,0xa1,0x6e,0x41,0x26,0xcb,0xfa,0xa0,0xc8},
++ {0x23,0xbe,0x45,0x29,0x32,0xd2,0x46,0x2d,0x82,0x03,0x52,0x28,0xfe,0x37,0x17,0xf5},
++ {0x56,0x3b,0xbf,0xee,0x5a,0x4f,0x43,0x39,0xaf,0xaa,0x94,0x08,0xdf,0xf0,0xfc,0x10},
++ {0x31,0x80,0xc8,0x73,0xc7,0xea,0x46,0xd3,0xaa,0x25,0x75,0x0f,0x9e,0x4c,0x09,0x29},
++ {0x7d,0xf7,0x18,0x4b,0x7b,0xa4,0x44,0xd5,0x85,0x3c,0x06,0xe0,0x65,0x53,0x96,0x6d},
++ {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,
++};
++
++static guint64 _global_match_cookie = 1;
++
++/* MatchElement struct */
++typedef struct {
++ guint16 type;
++ guint16 arg;
++ char *value;
++} MatchElement;
++
++/* Match struct */
++typedef struct {
++ int n_elements;
++ MatchElement *elements;
++ guint64 cookie;
++} Match;
++
++
++/*
++ * SipHash algorithm
++ */
++static void
++_g_siphash24 (guint8 out[8],
++ const void *_in,
++ gsize inlen,
++ const guint8 k[16])
++{
++ /* "somepseudorandomlygeneratedbytes" */
++ guint64 v0 = 0x736f6d6570736575ULL;
++ guint64 v1 = 0x646f72616e646f6dULL;
++ guint64 v2 = 0x6c7967656e657261ULL;
++ guint64 v3 = 0x7465646279746573ULL;
++ guint64 b;
++ guint64 k0 = U8TO64_LE (k);
++ guint64 k1 = U8TO64_LE (k + 8);
++ guint64 m;
++ const guint8 *in = _in;
++ const guint8 *end = in + inlen - (inlen % sizeof(guint64));
++ const int left = inlen & 7;
++ b = ((guint64) inlen) << 56;
++ v3 ^= k1;
++ v2 ^= k0;
++ v1 ^= k1;
++ v0 ^= k0;
++
++ for (; in != end; in += 8)
++ {
++ m = U8TO64_LE (in);
++ v3 ^= m;
++ SIPROUND;
++ SIPROUND;
++ v0 ^= m;
++ }
++
++ switch (left)
++ {
++ case 7: b |= ((guint64) in[6]) << 48;
++ case 6: b |= ((guint64) in[5]) << 40;
++ case 5: b |= ((guint64) in[4]) << 32;
++ case 4: b |= ((guint64) in[3]) << 24;
++ case 3: b |= ((guint64) in[2]) << 16;
++ case 2: b |= ((guint64) in[1]) << 8;
++ case 1: b |= ((guint64) in[0]); break;
++ case 0: break;
++ }
++
++ v3 ^= b;
++ SIPROUND;
++ SIPROUND;
++ v0 ^= b;
++
++ v2 ^= 0xff;
++ SIPROUND;
++ SIPROUND;
++ SIPROUND;
++ SIPROUND;
++ b = v0 ^ v1 ^ v2 ^ v3;
++ U64TO8_LE (out, b);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static gboolean
++is_key (const char *key_start, const char *key_end, char *value)
++{
++ gsize len = strlen (value);
++
++ if (len != key_end - key_start)
++ return FALSE;
++
++ return strncmp (key_start, value, len) == 0;
++}
++
++static gboolean
++parse_key (MatchElement *element, const char *key_start, const char *key_end)
++{
++ gboolean res = TRUE;
++
++ if (is_key (key_start, key_end, "type"))
++ {
++ element->type = MATCH_ELEMENT_TYPE;
++ }
++ else if (is_key (key_start, key_end, "sender"))
++ {
++ element->type = MATCH_ELEMENT_SENDER;
++ }
++ else if (is_key (key_start, key_end, "interface"))
++ {
++ element->type = MATCH_ELEMENT_INTERFACE;
++ }
++ else if (is_key (key_start, key_end, "member"))
++ {
++ element->type = MATCH_ELEMENT_MEMBER;
++ }
++ else if (is_key (key_start, key_end, "path"))
++ {
++ element->type = MATCH_ELEMENT_PATH;
++ }
++ else if (is_key (key_start, key_end, "path_namespace"))
++ {
++ element->type = MATCH_ELEMENT_PATH_NAMESPACE;
++ }
++ else if (is_key (key_start, key_end, "destination"))
++ {
++ element->type = MATCH_ELEMENT_DESTINATION;
++ }
++ else if (is_key (key_start, key_end, "arg0namespace"))
++ {
++ element->type = MATCH_ELEMENT_ARG0NAMESPACE;
++ }
++ else if (is_key (key_start, key_end, "eavesdrop"))
++ {
++ element->type = MATCH_ELEMENT_EAVESDROP;
++ }
++ else if (key_end - key_start > 3 && is_key (key_start, key_start + 3, "arg"))
++ {
++ const char *digits = key_start + 3;
++ const char *end_digits = digits;
++
++ while (end_digits < key_end && g_ascii_isdigit (*end_digits))
++ end_digits++;
++
++ if (end_digits == key_end) /* argN */
++ {
++ element->type = MATCH_ELEMENT_ARGN;
++ element->arg = atoi (digits);
++ }
++ else if (is_key (end_digits, key_end, "path")) /* argNpath */
++ {
++ element->type = MATCH_ELEMENT_ARGNPATH;
++ element->arg = atoi (digits);
++ }
++ else
++ res = FALSE;
++ }
++ else
++ res = FALSE;
++
++ return res;
++}
++
++static const char *
++parse_value (MatchElement *element, const char *s)
++{
++ char quote_char;
++ GString *value;
++
++ value = g_string_new ("");
++
++ quote_char = 0;
++
++ for (;*s; s++)
++ {
++ if (quote_char == 0)
++ {
++ switch (*s)
++ {
++ case '\'':
++ quote_char = '\'';
++ break;
++
++ case ',':
++ s++;
++ goto out;
++
++ case '\\':
++ quote_char = '\\';
++ break;
++
++ default:
++ g_string_append_c (value, *s);
++ break;
++ }
++ }
++ else if (quote_char == '\\')
++ {
++ /* \ only counts as an escape if escaping a quote mark */
++ if (*s != '\'')
++ g_string_append_c (value, '\\');
++
++ g_string_append_c (value, *s);
++ quote_char = 0;
++ }
++ else /* quote_char == ' */
++ {
++ if (*s == '\'')
++ quote_char = 0;
++ else
++ g_string_append_c (value, *s);
++ }
++ }
++
++ out:
++ if (quote_char == '\\')
++ g_string_append_c (value, '\\');
++ else if (quote_char == '\'')
++ {
++ g_string_free (value, TRUE);
++ return NULL;
++ }
++
++ element->value = g_string_free (value, FALSE);
++ return s;
++}
++
++static Match *
++match_new (const char *str)
++{
++ Match *match;
++ GArray *elements;
++ const char *p;
++ const char *key_start;
++ const char *key_end;
++ MatchElement element;
++ int i;
++
++ elements = g_array_new (TRUE, TRUE, sizeof (MatchElement));
++
++ p = str;
++
++ while (*p != 0)
++ {
++ memset (&element, 0, sizeof (element));
++
++ /* Skip initial whitespace */
++ while (*p && g_ascii_isspace (*p))
++ p++;
++
++ key_start = p;
++
++ /* Read non-whitespace non-equals chars */
++ while (*p && *p != '=' && !g_ascii_isspace (*p))
++ p++;
++
++ key_end = p;
++
++ /* Skip any whitespace after key */
++ while (*p && g_ascii_isspace (*p))
++ p++;
++
++ if (key_start == key_end)
++ continue; /* Allow trailing whitespace */
++ if (*p != '=')
++ goto error;
++
++ ++p;
++
++ if (!parse_key (&element, key_start, key_end))
++ goto error;
++
++ p = parse_value (&element, p);
++ if (p == NULL)
++ goto error;
++
++ g_array_append_val (elements, element);
++ }
++
++ match = g_new0 (Match, 1);
++ match->n_elements = elements->len;
++ match->elements = (MatchElement *)g_array_free (elements, FALSE);
++ match->cookie = _global_match_cookie++;
++
++ 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;
++}
++
++static gboolean
++match_equal (Match *a, Match *b)
++{
++ int i;
++
++ if (a->n_elements != b->n_elements)
++ return FALSE;
++
++ for (i = 0; i < a->n_elements; i++)
++ {
++ if (a->elements[i].type != b->elements[i].type ||
++ a->elements[i].arg != b->elements[i].arg ||
++ strcmp (a->elements[i].value, b->elements[i].value) != 0)
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++static void
++match_free (Match *match)
++{
++ int i;
++
++ for (i = 0; i < match->n_elements; i++)
++ g_free (match->elements[i].value);
++
++ g_free (match->elements);
++ g_free (match);
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/*
++ * _g_kdbus_open
++ */
++gboolean
++_g_kdbus_open (GKDBusWorker *worker,
++ const gchar *address,
++ GError **error)
++{
++ g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
++
++ worker->fd = g_open(address, O_RDWR|O_NOCTTY|O_CLOEXEC, 0);
++ if (worker->fd<0)
++ {
++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot open kdbus endpoint"));
++ return FALSE;
++ }
++
++ worker->address = g_strdup(address);
++ worker->closed = FALSE;
++ return TRUE;
++}
++
++static gboolean
++_g_kdbus_quit_loop (gpointer loop)
++{
++ g_main_loop_quit ((GMainLoop*)loop);
++ return FALSE;
++}
++
++gboolean
++_g_kdbus_can_connect (GKDBusWorker *worker,
++ GError **error)
++{
++#ifdef LIBDBUSPOLICY
++ worker->dbuspolicy = dbuspolicy1_init_shared (worker->address, worker->fd);
++ if (worker->dbuspolicy == NULL)
++ {
++ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot load dbus policy for kdbus transport or access to bus denied by security policy"));
++ return FALSE;
++ }
++#endif
++ return TRUE;
++}
++
++/*
++ * _g_kdbus_close
++ */
++gboolean
++_g_kdbus_close (GKDBusWorker *worker)
++{
++ g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++ if (worker->closed)
++ return TRUE;
++
++ g_main_context_invoke (worker->context, _g_kdbus_quit_loop, worker->loop);
++
++ g_main_context_unref (worker->context);
++ worker->context = NULL;
++
++ g_main_loop_unref (worker->loop);
++
++ g_thread_unref(worker->thread);
++
++ worker->thread = NULL;
++
++ g_free ((char*)worker->address);
++
++ close (worker->fd);
++ worker->fd = -1;
++
++ worker->closed = TRUE;
++ return TRUE;
++}
++
++/*
++ * _g_kdbus_is_closed
++ */
++gboolean
++_g_kdbus_is_closed (GKDBusWorker *worker)
++{
++ g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++ return worker->closed;
++}
++
++static void
++g_kdbus_free_data (GKDBusWorker *worker,
++ guint64 offset)
++{
++ struct kdbus_cmd_free cmd = {
++ .size = sizeof(cmd),
++ .offset = offset,
++ .flags = 0
++ };
++
++ if (ioctl (worker->fd, KDBUS_CMD_FREE, &cmd) < 0)
++ {
++ g_error ("kdbus: invalid KDBUS_CMD_FREE ioctl : %" G_GUINT64_FORMAT " offset\n", offset);
++ }
++}
++
++static void
++g_kdbus_close_msg (GKDBusWorker *worker,
++ struct kdbus_msg *msg)
++{
++ guint64 offset;
++
++ offset = (guint8 *)msg - (guint8 *)worker->kdbus_buffer;
++ g_kdbus_free_data (worker, offset);
++}
++
++
++/*
++ * g_kdbus_translate_nameowner_flags
++ */
++static void
++g_kdbus_translate_nameowner_flags (GBusNameOwnerFlags flags,
++ guint64 *kdbus_flags)
++{
++ guint64 new_flags;
++
++ new_flags = 0;
++
++ 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;
++
++ if (!(flags & G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE))
++ new_flags |= KDBUS_NAME_QUEUE;
++
++ *kdbus_flags = new_flags;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++/* < internal >
++ *
++ * _g_kdbus_Hello:
++ *
++ * Gets the unique name.
++ *
++ * Returns: the unique name or NULL. Do not free this string,
++ * it is owned by GKDBusWorker.
++ */
++const gchar *
++_g_kdbus_Hello (GKDBusWorker *worker,
++ GError **error)
++{
++ struct kdbus_cmd_hello *cmd;
++ struct kdbus_bloom_parameter *bloom;
++ struct kdbus_item *item, *items;
++
++ gchar *conn_name;
++ size_t size, conn_name_size;
++
++ const gchar *env_pool;
++ guint64 receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
++
++ conn_name = "gdbus-kdbus";
++ conn_name_size = strlen (conn_name);
++
++ 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;
++
++ env_pool = getenv (RECEIVE_POOL_SIZE_ENV_VAR_NAME);
++ if(env_pool)
++ {
++ guint64 size;
++ guint32 multiply = 1;
++ gint64 page_size;
++
++ page_size = sysconf(_SC_PAGESIZE);
++ if(page_size == -1)
++ {
++ size = 0;
++ goto finish;
++ }
++
++ errno = 0;
++ size = strtoul(env_pool, (char**)&env_pool, 10);
++ if(errno == EINVAL || size == 0)
++ {
++ size = 0;
++ goto finish;
++ }
++
++ if(*env_pool == 'k')
++ {
++ multiply = 1024;
++ env_pool++;
++ }
++ else if (*env_pool == 'M')
++ {
++ multiply = 1024 * 1024;
++ env_pool++;
++ }
++
++ if(*env_pool != '\0')
++ {
++ size = 0;
++ goto finish;
++ }
++
++ receive_pool_size = size * multiply;
++
++ if((receive_pool_size > RECEIVE_POOL_SIZE_MAX_MBYTES * 1024 * 1024) ||
++ (receive_pool_size < RECEIVE_POOL_SIZE_MIN_KBYTES * 1024) ||
++ ((receive_pool_size & (page_size - 1)) != 0))
++ size = 0;
++
++ finish:
++ if(size == 0)
++ {
++ g_warning ("%s value is invalid, default value %luB will be used.", RECEIVE_POOL_SIZE_ENV_VAR_NAME,
++ RECEIVE_POOL_SIZE_DEFAULT_SIZE);
++ g_warning ("Correct value must be between %ukB and %uMB and must be aligned to page size: %" G_GINT64_FORMAT "B.",
++ RECEIVE_POOL_SIZE_MIN_KBYTES, RECEIVE_POOL_SIZE_MAX_MBYTES, page_size);
++
++ receive_pool_size = RECEIVE_POOL_SIZE_DEFAULT_SIZE;
++ }
++ }
++
++ g_debug ("[KDBUS] receive pool size set to %" G_GUINT64_FORMAT "\n", receive_pool_size);
++ worker->receive_pool_size = receive_pool_size;
++ cmd->pool_size = worker->receive_pool_size;
++
++ item = cmd->items;
++ item->size = (guint64) 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 (worker->unique_id != -1)
++ {
++ g_set_error (error, G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Already handled an Hello message");
++ return NULL;
++ }
++
++ 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;
++ }
++
++ worker->kdbus_buffer = mmap(NULL, worker->receive_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 (cmd->bus_flags > 0xFFFFFFFFULL)
++ {
++ g_set_error_literal (error,
++ G_IO_ERROR,
++ G_IO_ERROR_FAILED,
++ _("Incompatible HELLO flags"));
++ return NULL;
++ }
++
++ memcpy (worker->bus_id, cmd->id128, 16);
++
++ worker->unique_id = cmd->id;
++ if (asprintf (&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id) < 0)
++ {
++ g_set_error (error,
++ G_IO_ERROR,
++ G_IO_ERROR_UNKNOWN,
++ "asprintf error: %s",
++ g_strerror(errno));
++ return NULL;
++ }
++
++ /* read bloom filters parameters */
++ bloom = NULL;
++ items = (void*)(worker->kdbus_buffer + cmd->offset);
++ KDBUS_FOREACH(item, items, cmd->items_size)
++ {
++ switch (item->type)
++ {
++ case KDBUS_ITEM_BLOOM_PARAMETER:
++ bloom = &item->bloom_parameter;
++ break;
++ }
++ }
++
++ if (bloom != NULL)
++ {
++ worker->bloom_size = (gsize) bloom->size;
++ worker->bloom_n_hash = (guint) bloom->n_hash;
++ }
++ else
++ {
++ g_set_error_literal (error,
++ G_IO_ERROR,
++ G_IO_ERROR_FAILED,
++ _("Can't read bloom filter parameters"));
++ return NULL;
++ }
++
++ g_kdbus_free_data (worker, cmd->offset);
++
++#ifdef LIBDBUSPOLICY
++ dbuspolicy1_init_set_pool (worker->dbuspolicy, worker->kdbus_buffer);
++#endif
++
++ return worker->unique_name;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_RequestName:
++ *
++ * Synchronously acquires name on the bus and returns status code
++ * from the GBusRequestNameReplyFlags enumeration.
++ *
++ * Returns: status code or G_BUS_REQUEST_NAME_FLAGS_ERROR
++ * if error is set.
++ */
++GBusRequestNameReplyFlags
++_g_kdbus_RequestName (GKDBusWorker *worker,
++ const gchar *name,
++ GBusNameOwnerFlags flags,
++ GError **error)
++{
++ GBusRequestNameReplyFlags status;
++ struct kdbus_cmd *cmd;
++ guint64 kdbus_flags;
++ gsize len, size;
++ gint ret;
++
++ status = G_BUS_REQUEST_NAME_FLAGS_PRIMARY_OWNER;
++
++ 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 G_BUS_REQUEST_NAME_FLAGS_ERROR;
++ }
++
++ if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_INVALID_ARGS,
++ "Cannot acquire a service named '%s', because that is reserved", name);
++ return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++ }
++
++ if (*name == ':')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_INVALID_ARGS,
++ "Cannot acquire a service starting with ':' such as \"%s\"", name);
++ return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++ }
++
++#ifdef LIBDBUSPOLICY
++ if (worker->dbuspolicy != NULL)
++ {
++ if (dbuspolicy1_can_own (worker->dbuspolicy, name) != 1)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_ACCESS_DENIED,
++ "Connection \"%s\" is not allowed to own the "
++ "service \"%s\" due to security policies", worker->unique_name, name);
++ return G_BUS_REQUEST_NAME_FLAGS_ERROR;
++ }
++ }
++#endif
++
++ g_kdbus_translate_nameowner_flags (flags, &kdbus_flags);
++
++ len = strlen(name) + 1;
++ size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++ cmd->items[0].type = KDBUS_ITEM_NAME;
++ cmd->flags = kdbus_flags;
++ memcpy (cmd->items[0].str, name, len);
++
++ ret = ioctl(worker->fd, KDBUS_CMD_NAME_ACQUIRE, cmd);
++ if (ret < 0)
++ {
++ if (errno == EEXIST)
++ 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 G_BUS_REQUEST_NAME_FLAGS_ERROR;
++ }
++ }
++ else if ((cmd->return_flags & KDBUS_NAME_PRIMARY)
++ && !(cmd->return_flags & KDBUS_NAME_ACQUIRED))
++ status = G_BUS_REQUEST_NAME_FLAGS_ALREADY_OWNER;
++
++ if (cmd->return_flags & KDBUS_NAME_IN_QUEUE)
++ status = G_BUS_REQUEST_NAME_FLAGS_IN_QUEUE;
++
++ return status;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_ReleaseName:
++ *
++ * Synchronously releases name on the bus and returns status code
++ * from the GBusReleaseNameReplyFlags enumeration.
++ *
++ * Returns: status code or G_BUS_RELEASE_NAME_FLAGS_ERROR
++ * if error is set.
++ */
++GBusReleaseNameReplyFlags
++_g_kdbus_ReleaseName (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GBusReleaseNameReplyFlags status;
++ struct kdbus_cmd *cmd;
++ gsize len, size;
++ gint ret;
++
++ status = G_BUS_RELEASE_NAME_FLAGS_RELEASED;
++
++ 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 G_BUS_RELEASE_NAME_FLAGS_ERROR;
++ }
++
++ if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_INVALID_ARGS,
++ "Cannot release a service named '%s', because that is owned by the bus", name);
++ return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++ }
++
++ if (*name == ':')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_INVALID_ARGS,
++ "Cannot release a service starting with ':' such as \"%s\"", name);
++ return G_BUS_RELEASE_NAME_FLAGS_ERROR;
++ }
++
++ len = strlen(name) + 1;
++ size = (gulong) G_STRUCT_OFFSET (struct kdbus_cmd, items) + KDBUS_ITEM_SIZE(len);
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
++ cmd->items[0].type = KDBUS_ITEM_NAME;
++ memcpy (cmd->items[0].str, name, len);
++
++ ret = ioctl(worker->fd, KDBUS_CMD_NAME_RELEASE, cmd);
++ if (ret < 0)
++ {
++ if (errno == ESRCH)
++ 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 G_BUS_RELEASE_NAME_FLAGS_ERROR;
++ }
++ }
++
++ return status;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetBusId:
++ *
++ * Synchronously returns the unique ID of the bus.
++ *
++ * Returns: the unique ID of the bus or NULL if error is set.
++ * Free with g_free().
++ */
++gchar *
++_g_kdbus_GetBusId (GKDBusWorker *worker,
++ GError **error)
++{
++ GString *result;
++ guint cnt;
++
++ result = g_string_new (NULL);
++
++ for (cnt=0; cnt<16; cnt++)
++ g_string_append_printf (result, "%02x", worker->bus_id[cnt]);
++
++ return g_string_free (result, FALSE);
++}
++
++
++static void
++expand_strv (gchar ***strv_ptr,
++ gchar *value)
++{
++ gchar **strv;
++ guint strv_len;
++
++ if (!value)
++ return;
++
++ strv = *strv_ptr;
++ strv_len = g_strv_length (strv);
++
++ strv = g_renew (gchar *, strv, strv_len + 2);
++ strv[strv_len] = value;
++ strv[strv_len + 1] = NULL;
++
++ *strv_ptr = strv;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetListNames:
++ *
++ * Synchronously returns a list of:
++ * - all currently-owned names on the bus (activatable = FALSE),
++ * - all names that can be activated on the bus (activatable = TRUE),
++ *
++ * or NULL if error is set. Free with g_strfreev().
++ */
++gchar **
++_g_kdbus_GetListNames (GKDBusWorker *worker,
++ gboolean activatable,
++ GError **error)
++{
++ struct kdbus_info *name_list, *name;
++ struct kdbus_cmd_list cmd = {
++ .size = sizeof(cmd)
++ };
++
++ gchar **listnames;
++ guint64 prev_id;
++ gint ret;
++
++ prev_id = 0;
++
++ if (activatable)
++ cmd.flags = KDBUS_LIST_ACTIVATORS; /* ListActivatableNames */
++ else
++ cmd.flags = KDBUS_LIST_UNIQUE | KDBUS_LIST_NAMES; /* ListNames */
++
++ ret = ioctl(worker->fd, KDBUS_CMD_LIST, &cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ _("Error listing names"));
++ return NULL;
++ }
++
++ listnames = g_new0 (gchar *, 1);
++ name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
++
++ KDBUS_FOREACH (name, name_list, cmd.list_size)
++ {
++ struct kdbus_item *item;
++
++ if ((cmd.flags & KDBUS_LIST_UNIQUE) && name->id != prev_id)
++ {
++ gchar *unique_name;
++
++ if (asprintf (&unique_name, ":1.%llu", name->id) < 0)
++ {
++ g_set_error (error,
++ G_IO_ERROR,
++ G_IO_ERROR_UNKNOWN,
++ "asprintf error: %s",
++ g_strerror(errno));
++ goto error;
++ }
++
++ expand_strv (&listnames, unique_name);
++ prev_id = name->id;
++ }
++
++ KDBUS_ITEM_FOREACH (item, name, items)
++ {
++ if (item->type == KDBUS_ITEM_OWNED_NAME)
++ {
++ if (g_dbus_is_name (item->name.name))
++ expand_strv (&listnames, g_strdup (item->name.name));
++ }
++ }
++ }
++
++ /* org.freedesktop.DBus.ListNames */
++ if (!activatable)
++ expand_strv (&listnames, g_strdup ("org.freedesktop.DBus"));
++
++ g_kdbus_free_data (worker, cmd.offset);
++
++ return listnames;
++
++error:
++ g_strfreev(listnames);
++ return NULL;
++}
++
++
++static gboolean
++g_kdbus_NameHasOwner_internal (GKDBusWorker *worker,
++ const gchar *name)
++{
++ struct kdbus_cmd_info *cmd;
++ struct kdbus_info *conn_info;
++ gsize size, len;
++ gint ret;
++
++ if (g_dbus_is_unique_name(name))
++ {
++ size = (gulong) 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 = (gulong) 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;
++
++ ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
++ if (ret < 0)
++ return FALSE;
++
++ conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset);
++
++ if (conn_info->flags & KDBUS_HELLO_ACTIVATOR)
++ ret = -1;
++
++ g_kdbus_free_data (worker, cmd->offset);
++
++ if (ret < 0)
++ return FALSE;
++ else
++ return TRUE;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetListQueuedOwners:
++ *
++ * Synchronously returns the unique bus names of connections currently
++ * queued for the name.
++ *
++ * Returns: the unique bus names of connections currently queued for the
++ * 'name' or NULL if error is set. Free with g_strfreev().
++ */
++gchar **
++_g_kdbus_GetListQueuedOwners (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ struct kdbus_info *name_list, *kname;
++ struct kdbus_cmd_list cmd = {
++ .size = sizeof(cmd),
++ .flags = KDBUS_LIST_QUEUED
++ };
++
++ gchar **queued_owners;
++ gint ret;
++
++ 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_NameHasOwner_internal (worker, name))
++ {
++ 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(worker->fd, KDBUS_CMD_LIST, &cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ _("Error listing names"));
++ return NULL;
++ }
++
++ queued_owners = g_new0 (gchar *, 1);
++ name_list = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd.offset);
++
++ KDBUS_FOREACH(kname, name_list, cmd.list_size)
++ {
++ struct kdbus_item *item;
++
++ KDBUS_ITEM_FOREACH(item, kname, items)
++ {
++ if (item->type == KDBUS_ITEM_OWNED_NAME)
++ {
++ gchar *unique_name;
++
++ if (strcmp(item->name.name, name))
++ continue;
++
++ if (asprintf (&unique_name, ":1.%llu", kname->id) != -1)
++ expand_strv (&queued_owners, unique_name);
++ }
++ }
++ }
++
++ g_kdbus_free_data (worker, cmd.offset);
++
++ return queued_owners;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_NameHasOwner:
++ *
++ * Checks if the specified name exists (currently has an owner).
++ *
++ * Returns: TRUE if the name exists and FALSE when name doesn't exists
++ * or error is set.
++ */
++gboolean
++_g_kdbus_NameHasOwner (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ 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 FALSE;
++ }
++
++ if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++ return TRUE;
++
++ if (!g_kdbus_NameHasOwner_internal (worker, name))
++ return FALSE; /* Don't make g_set_error, otherwise NameHasOwner will fail. */
++ else
++ return TRUE;
++}
++
++
++GDBusCredentials *
++_g_kdbus_GetConnInfo (GKDBusWorker *worker,
++ const gchar *name,
++ guint flags,
++ GError **error)
++{
++ GDBusCredentials *creds;
++ struct kdbus_cmd_info *cmd;
++ struct kdbus_info *conn_info;
++ struct kdbus_item *item;
++ gsize size, len;
++ gint ret;
++
++ creds = g_new0 (GDBusCredentials, 1);
++
++ 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);
++ goto error;
++ }
++
++ if (!g_kdbus_NameHasOwner_internal (worker, name))
++ {
++ 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);
++ goto error;
++ }
++
++ if (g_dbus_is_unique_name(name))
++ {
++ size = (gulong) 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 = (gulong) 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->attach_flags = _KDBUS_ATTACH_ALL;
++ cmd->size = size;
++
++ ret = ioctl(worker->fd, KDBUS_CMD_CONN_INFO, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ _("Could not get connection info"));
++ goto error;
++ }
++
++ conn_info = (struct kdbus_info *) ((guint8 *) worker->kdbus_buffer + cmd->offset);
++
++ if (flags & G_DBUS_CREDS_UNIQUE_NAME)
++ {
++ if (asprintf (&creds->unique_name, ":1.%llu", (unsigned long long) conn_info->id) < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "asprintf error: %s",
++ g_strerror(errno));
++ goto error;
++ }
++ }
++
++ KDBUS_ITEM_FOREACH(item, conn_info, items)
++ {
++ switch (item->type)
++ {
++ case KDBUS_ITEM_PIDS:
++ if (flags & G_DBUS_CREDS_PID)
++ creds->pid = item->pids.pid;
++ break;
++
++ case KDBUS_ITEM_CREDS:
++ if (flags & G_DBUS_CREDS_UID)
++ creds->uid = item->creds.uid;
++ break;
++
++ case KDBUS_ITEM_SECLABEL:
++ if (flags & G_DBUS_CREDS_SEC_LABEL)
++ creds->sec_label = g_strdup (item->str);
++ 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_AUDIT:
++ case KDBUS_ITEM_CONN_DESCRIPTION:
++ case KDBUS_ITEM_AUXGROUPS:
++ case KDBUS_ITEM_OWNED_NAME:
++ break;
++ }
++ }
++
++ g_kdbus_free_data (worker, cmd->offset);
++ return creds;
++
++error:
++ g_free (creds->unique_name);
++ g_free (creds->sec_label);
++ g_free (creds);
++ return NULL;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetNameOwner:
++ *
++ * Synchronously returns the unique connection name of the primary owner of
++ * the name given. If the requested name doesn't have an owner, an error is
++ * returned.
++ *
++ * Returns: the unique connection name of the primary owner of the
++ * name given. If the requested name doesn't have an owner, function
++ * returns NULL and error is set. Free with g_free().
++ */
++gchar *
++_g_kdbus_GetNameOwner (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GDBusCredentials *creds;
++ gchar *unique_name;
++ guint flags;
++
++ creds = NULL;
++ unique_name = NULL;
++
++ if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++ return g_strdup (name);
++
++ flags = G_DBUS_CREDS_UNIQUE_NAME;
++ creds = _g_kdbus_GetConnInfo (worker,
++ name,
++ flags,
++ error);
++ if (creds != NULL)
++ {
++ unique_name = creds->unique_name;
++ g_free (creds);
++ }
++
++ return unique_name;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionUnixProcessID:
++ *
++ * Synchronously returns the Unix process ID of the process connected to the
++ * bus. If unable to determine it, an error is returned.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: the Unix process ID of the process connected to the bus or -1
++ * if error is set.
++ */
++pid_t
++_g_kdbus_GetConnectionUnixProcessID (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GDBusCredentials *creds;
++ guint flags;
++ pid_t pid;
++
++ creds = NULL;
++ pid = -1;
++
++ flags = G_DBUS_CREDS_PID;
++ creds = _g_kdbus_GetConnInfo (worker,
++ name,
++ flags,
++ error);
++ if (creds != NULL)
++ {
++ pid = creds->pid;
++ g_free (creds);
++ }
++
++ return pid;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionUnixUser:
++ *
++ * Synchronously returns the Unix user ID of the process connected to the
++ * bus. If unable to determine it, an error is returned.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: the Unix user ID of the process connected to the bus or -1
++ * if error is set.
++ */
++uid_t
++_g_kdbus_GetConnectionUnixUser (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GDBusCredentials *creds;
++ guint flags;
++ uid_t uid;
++
++ creds = NULL;
++ uid = -1;
++
++ flags = G_DBUS_CREDS_UID;
++ creds = _g_kdbus_GetConnInfo (worker,
++ name,
++ flags,
++ error);
++ if (creds != NULL)
++ {
++ uid = creds->uid;
++ g_free (creds);
++ }
++
++ return uid;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_GetConnectionSecurityLabel:
++ *
++ * Synchronously returns security label of the process connected to the bus.
++ *
++ * If name contains a value not compatible with the D-Bus syntax and naming
++ * conventions for bus names, the operation returns -1 and error is set.
++ *
++ * Returns: security label of the process connected to the bus or -1
++ */
++gchar *
++_g_kdbus_GetConnectionSecurityLabel (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GDBusCredentials *creds;
++ gchar *sec_label;
++ guint flags;
++
++ creds = NULL;
++ sec_label = NULL;
++
++ flags = G_DBUS_CREDS_SEC_LABEL;
++ creds = _g_kdbus_GetConnInfo (worker,
++ name,
++ flags,
++ error);
++ if (creds != NULL)
++ {
++ sec_label = creds->sec_label;
++ g_free (creds);
++ }
++
++ return sec_label;
++}
++
++typedef struct
++{
++ GKDBusWorker *worker;
++ GDBusMessage *message;
++} SyntheticReplyData;
++
++static gboolean
++deliver_synthetic_reply (gpointer user_data)
++{
++ SyntheticReplyData *data;
++ GKDBusWorker *worker;
++ GDBusMessage *message;
++
++ data = user_data;
++ worker = data->worker;
++ message = data->message;
++
++ (* worker->message_received_callback) (message, worker->user_data);
++
++ g_object_unref (message);
++ g_free (data);
++
++ return FALSE;
++}
++
++static void
++send_synthetic_message (GKDBusWorker *worker,
++ GDBusMessage *message)
++{
++ SyntheticReplyData *reply_data;
++ reply_data = g_new0 (SyntheticReplyData, 1);
++ reply_data->worker = worker;
++ reply_data->message = message;
++ g_main_context_invoke (worker->context, deliver_synthetic_reply, reply_data);
++}
++
++static GBusStartServiceReplyFlags
++_g_kdbus_send_Ping (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error)
++{
++ GDBusMessage *ping_message;
++ GDBusMessage *reply = NULL;
++ gboolean ret;
++
++ ping_message = g_dbus_message_new_method_call (name, "/", "org.freedesktop.DBus.Peer", "Ping");
++ g_dbus_message_set_serial (ping_message, -1);
++
++ ret = _g_kdbus_send (worker, ping_message, &reply, 25000, NULL, NULL);
++ g_object_unref (ping_message);
++ if (reply)
++ g_object_unref (reply);
++
++ if (!ret)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_SERVICE_UNKNOWN,
++ "The name %s was not provided by any .service files", name);
++ return G_BUS_START_SERVICE_REPLY_ERROR;
++ }
++ return G_BUS_START_SERVICE_REPLY_SUCCESS;
++}
++
++static void
++_g_kdbus_send_Ping_thread (GTask *task,
++ gpointer source_object,
++ gpointer task_data,
++ GCancellable *cancellable)
++{
++ GKDBusWorker *worker = source_object;
++ GDBusMessage *message = task_data;
++ const gchar *name = NULL;
++ GError *error = NULL;
++
++ GBusStartServiceReplyFlags status;
++
++ g_variant_get (g_dbus_message_get_body (message), "(su)", &name, NULL);
++
++ status = _g_kdbus_send_Ping (worker, name, &error);
++ g_free ((gchar*) name);
++
++ if (status == G_BUS_START_SERVICE_REPLY_SUCCESS)
++ g_task_return_int (task, G_BUS_START_SERVICE_REPLY_SUCCESS);
++ else
++ g_task_return_error (task, error);
++
++}
++
++static void
++_g_kdbus_send_Ping_finish (GKDBusWorker *worker,
++ GAsyncResult *result,
++ gpointer user_data)
++{
++ GDBusMessage *message = user_data;
++ GDBusMessage *synthetic_reply = NULL;
++ GError *error = NULL;
++ gssize status = g_task_propagate_int (G_TASK (result), &error);
++ if (status != -1)
++ {
++ synthetic_reply = g_dbus_message_new_method_reply (message);
++ g_dbus_message_set_body (synthetic_reply, g_variant_new ("(u)", status));
++ }
++ else
++ {
++ gchar *dbus_error_name = g_dbus_error_encode_gerror (error);
++ synthetic_reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", error->message);
++ g_free (dbus_error_name);
++ g_error_free (error);
++ }
++ g_object_unref (message);
++ g_dbus_message_set_serial (synthetic_reply, -1);
++ send_synthetic_message (worker, synthetic_reply);
++}
++
++/* < internal >
++ *
++ * _g_kdbus_StartServiceByName:
++ *
++ * Synchronously tries to launch the executable associated
++ * with a name.
++ *
++ * Returns: status code or G_BUS_START_SERVICE_REPLY_ERROR
++ if error is set.
++ */
++GBusStartServiceReplyFlags
++_g_kdbus_StartServiceByName (GKDBusWorker *worker,
++ const gchar *name,
++ guint32 flags,
++ GDBusMessage *message,
++ GError **error)
++{
++ 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 G_BUS_START_SERVICE_REPLY_ERROR;
++ }
++
++ if (g_strcmp0 (name, "org.freedesktop.DBus") == 0)
++ return G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING;
++
++ if (!g_kdbus_NameHasOwner_internal (worker, name))
++ {
++ if (!message)
++ {
++ return _g_kdbus_send_Ping(worker, name, error);
++ }
++ else
++ {
++ GTask *task;
++ task = g_task_new (worker,
++ NULL,
++ (GAsyncReadyCallback) _g_kdbus_send_Ping_finish,
++ g_object_ref(message));
++ g_task_set_task_data (task, message, NULL);
++ g_task_run_in_thread (task, _g_kdbus_send_Ping_thread);
++ g_object_unref (task);
++ return G_BUS_START_SERVICE_REPLY_SUCCESS;
++ }
++ }
++ else
++ {
++ return G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING;
++ }
++}
++
++
++/*
++ * g_kdbus_bloom_add_data:
++ * Based on bus-bloom.c from systemd
++ * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
++ */
++static void
++g_kdbus_bloom_add_data (GKDBusWorker *worker,
++ guint64 bloom_data[],
++ const void *data,
++ gsize n)
++{
++ guint8 hash[8];
++ guint64 bit_num;
++ guint bytes_num = 0;
++ guint cnt_1, cnt_2;
++ guint hash_index = 0;
++
++ guint c = 0;
++
++ bit_num = (guint64)worker->bloom_size * 8;
++
++ if (bit_num > 1)
++ bytes_num = ((__builtin_clzll(bit_num) ^ 63U) + 7) / 8;
++
++ for (cnt_1 = 0, hash_index = 0; cnt_1 < (worker->bloom_n_hash); cnt_1++)
++ {
++ guint64 p = 0;
++ for (cnt_2 = 0; cnt_2 < bytes_num; cnt_2++)
++ {
++ if (c <= 0)
++ {
++ _g_siphash24(hash, data, n, hash_keys[hash_index++]);
++ c += 8;
++ }
++
++ p = (p << 8ULL) | (guint64) hash[8 - c];
++ c--;
++ }
++
++ p &= bit_num - 1;
++ bloom_data[p >> 6] |= 1ULL << (p & 63);
++ }
++}
++
++static void
++g_kdbus_bloom_add_pair (GKDBusWorker *worker,
++ guint64 bloom_data[],
++ const gchar *parameter,
++ const gchar *value)
++{
++ gchar buf[1024];
++ gsize size;
++
++ size = strlen(parameter) + strlen(value) + 1;
++ if (size >= 1024)
++ return;
++
++ strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
++ g_kdbus_bloom_add_data(worker, bloom_data, buf, size);
++}
++
++static void
++g_kdbus_bloom_add_prefixes (GKDBusWorker *worker,
++ guint64 bloom_data[],
++ const gchar *parameter,
++ const gchar *value,
++ gchar separator)
++{
++ gchar buf[1024];
++ gsize size;
++
++ size = strlen(parameter) + strlen(value) + 1;
++ if (size >= 1024)
++ return;
++
++ strcpy(stpcpy(stpcpy(buf, parameter), ":"), value);
++
++ for (;;)
++ {
++ gchar *last_sep;
++ last_sep = strrchr(buf, separator);
++ if (!last_sep || last_sep == buf)
++ break;
++
++ *last_sep = 0;
++ g_kdbus_bloom_add_data(worker, bloom_data, buf, last_sep-buf);
++ }
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_AddMatch:
++ *
++ * Synchronously adds a match rule to match messages.
++ *
++ * Returns: TRUE if the operation succeeded, FALSE
++ * if error is set.
++ *
++ */
++gboolean
++_g_kdbus_AddMatch (GKDBusWorker *worker,
++ const gchar *match_rule,
++ GError **error)
++{
++ Match *match;
++ MatchElement *element;
++ const gchar *sender_name;
++ gsize sender_len, size;
++ struct kdbus_cmd_match *cmd;
++ struct kdbus_item *item;
++ guint64 *bloom;
++ guint64 src_id;
++ gchar *type;
++ gint cnt, ret;
++
++ if (match_rule[0] == '-')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ match = match_new (match_rule);
++ if (!match)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ 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));
++
++ 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_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ match_free (match);
++ return FALSE;
++ }
++ break;
++
++ case MATCH_ELEMENT_TYPE:
++ g_kdbus_bloom_add_pair (worker, bloom, "message-type", element->value);
++ break;
++
++ case MATCH_ELEMENT_INTERFACE:
++ g_kdbus_bloom_add_pair (worker, bloom, "interface", element->value);
++ break;
++
++ case MATCH_ELEMENT_MEMBER:
++ g_kdbus_bloom_add_pair (worker, bloom, "member", element->value);
++ break;
++
++ case MATCH_ELEMENT_PATH:
++ g_kdbus_bloom_add_pair (worker, bloom, "path", element->value);
++ break;
++
++ case MATCH_ELEMENT_PATH_NAMESPACE:
++ if (g_strcmp0 (element->value, "/"))
++ g_kdbus_bloom_add_pair (worker, bloom, "path-slash-prefix", element->value);
++ break;
++
++ case MATCH_ELEMENT_ARGN:
++ if (asprintf (&type, "arg%u", element->arg) < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "asprintf failed: %s", g_strerror(errno));
++ match_free (match);
++ return FALSE;
++ }
++ else
++ {
++ g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
++ free (type);
++ }
++ break;
++
++ case MATCH_ELEMENT_ARGNPATH:
++ if (asprintf (&type, "arg%u-slash-prefix", element->arg) < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "asprintf failed: %s", g_strerror(errno));
++ match_free (match);
++ return FALSE;
++ }
++ else
++ {
++ g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
++ free (type);
++ }
++ break;
++
++ case MATCH_ELEMENT_ARG0NAMESPACE:
++ g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value);
++ break;
++
++ case MATCH_ELEMENT_DESTINATION:
++ case MATCH_ELEMENT_EAVESDROP:
++ break;
++ }
++ }
++
++ size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size);
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = match->cookie;
++
++ item = cmd->items;
++ item->size = (guint64) G_STRUCT_OFFSET(struct kdbus_item, data64) + worker->bloom_size;
++ item->type = KDBUS_ITEM_BLOOM_MASK;
++ memcpy(item->data64, bloom, worker->bloom_size);
++ item = KDBUS_ITEM_NEXT(item);
++
++ if (src_id != KDBUS_MATCH_ID_ANY)
++ {
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id);
++ item->type = KDBUS_ITEM_ID;
++ item->id = src_id;
++ item = KDBUS_ITEM_NEXT(item);
++ }
++
++ if (sender_name)
++ {
++ item->size = (guint64) 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_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ g_mutex_lock (&worker->matches_mutex);
++ worker->matches = g_list_prepend (worker->matches, match);
++ g_mutex_unlock (&worker->matches_mutex);
++ return TRUE;
++}
++
++
++/* < internal >
++ *
++ * _g_kdbus_RemoveMatch:
++ *
++ * Synchronously removes the first rule that matches.
++ *
++ * Returns: TRUE if the operation succeeded, FALSE
++ * if error is set.
++ */
++gboolean
++_g_kdbus_RemoveMatch (GKDBusWorker *worker,
++ const gchar *match_rule,
++ GError **error)
++{
++ Match *match, *other_match;
++ GList *matches;
++ guint64 cookie;
++
++ if (match_rule[0] == '-')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ match = match_new (match_rule);
++ if (!match)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ g_mutex_lock (&worker->matches_mutex);
++ for (matches = worker->matches; matches != NULL; matches = matches->next)
++ {
++ other_match = matches->data;
++ if (match_equal (match, other_match))
++ {
++ cookie = other_match->cookie;
++ match_free (other_match);
++ worker->matches = g_list_delete_link (worker->matches, matches);
++ break;
++ }
++ }
++ g_mutex_unlock (&worker->matches_mutex);
++
++ if (matches == NULL)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
++ "The given match rule wasn't found and can't be removed");
++ match_free (match);
++ return FALSE;
++ }
++ else
++ {
++ struct kdbus_cmd_match cmd = {
++ .size = sizeof(cmd),
++ .cookie = cookie
++ };
++ gint ret;
++
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_REMOVE, &cmd);
++ if (ret < 0)
++ {
++ if (errno == EBADSLT)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
++ "A match entry with the given cookie could not be found");
++ }
++ else
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++ "Error while removing a match");
++ }
++ match_free (match);
++ return FALSE;
++ }
++ }
++
++ match_free (match);
++ return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_owner_changed_internal
++ */
++static gboolean
++_g_kdbus_subscribe_name_owner_changed_internal (GKDBusWorker *worker,
++ const gchar *name,
++ const gchar *old_name,
++ const gchar *new_name,
++ guint64 cookie,
++ GError **error)
++{
++ struct kdbus_item *item;
++ struct kdbus_cmd_match *cmd;
++ gsize size, len;
++ gint ret;
++ guint64 old_id = 0;
++ guint64 new_id = KDBUS_MATCH_ID_ANY;
++
++ if (name)
++ len = strlen(name) + 1;
++ else
++ len = 0;
++
++ 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);
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = cookie;
++ item = cmd->items;
++
++ if (old_name == NULL)
++ {
++ old_id = KDBUS_MATCH_ID_ANY;
++ }
++ else
++ {
++ if (g_dbus_is_unique_name(old_name))
++ old_id = strtoull (old_name + 3, NULL, 10);
++ else
++ return TRUE;
++ }
++
++ if (new_name == NULL)
++ {
++ new_id = KDBUS_MATCH_ID_ANY;
++ }
++ else
++ {
++ if (g_dbus_is_unique_name(new_name))
++ new_id = strtoull (new_name + 3, NULL, 10);
++ else
++ return TRUE;
++ }
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = cookie;
++ item = cmd->items;
++
++ item->type = KDBUS_ITEM_NAME_CHANGE;
++ item->name_change.old_id.id = old_id;
++ item->name_change.new_id.id = new_id;
++
++ if (name)
++ memcpy(item->name_change.name, name, len);
++
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++ (guint64) 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_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ return FALSE;
++ }
++
++ return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_acquired
++ */
++gboolean
++_g_kdbus_subscribe_name_acquired (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error)
++{
++ Match *match;
++ struct kdbus_item *item;
++ struct kdbus_cmd_match *cmd;
++ gsize size, len;
++ gint ret;
++
++ if (match_rule[0] == '-')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ if (name != NULL)
++ {
++ if (!g_dbus_is_name (name))
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++ }
++
++ match = match_new (match_rule);
++ if (!match)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ if (name)
++ len = strlen(name) + 1;
++ else
++ len = 0;
++
++ 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);
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = match->cookie;
++ item = cmd->items;
++
++ 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;
++
++ if (name)
++ memcpy(item->name_change.name, name, len);
++
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++ (guint64) 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_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ if (!_g_kdbus_subscribe_name_owner_changed_internal (worker, name, NULL, worker->unique_name, match->cookie, error))
++ {
++ match_free (match);
++ return FALSE;
++ }
++
++ g_mutex_lock (&worker->matches_mutex);
++ worker->matches = g_list_prepend (worker->matches, match);
++ g_mutex_unlock (&worker->matches_mutex);
++ return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_lost
++ */
++gboolean
++_g_kdbus_subscribe_name_lost (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error)
++{
++ Match *match;
++ struct kdbus_item *item;
++ struct kdbus_cmd_match *cmd;
++ gsize size, len;
++ gint ret;
++
++ if (match_rule[0] == '-')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ if (name != NULL)
++ {
++ if (!g_dbus_is_name (name))
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++ }
++
++ match = match_new (match_rule);
++ if (!match)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ if (name)
++ len = strlen(name) + 1;
++ else
++ len = 0;
++
++ 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);
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = match->cookie;
++ item = cmd->items;
++
++ 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;
++
++ if (name)
++ memcpy(item->name_change.name, name, len);
++
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++ (guint64) 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_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ if (!_g_kdbus_subscribe_name_owner_changed_internal (worker, name, worker->unique_name, NULL, match->cookie, error))
++ {
++ match_free (match);
++ return FALSE;
++ }
++
++ g_mutex_lock (&worker->matches_mutex);
++ worker->matches = g_list_prepend (worker->matches, match);
++ g_mutex_unlock (&worker->matches_mutex);
++ return TRUE;
++}
++
++
++/*
++ * _g_kdbus_subscribe_name_owner_changed
++ */
++gboolean
++_g_kdbus_subscribe_name_owner_changed (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error)
++{
++ Match *match;
++ struct kdbus_item *item;
++ struct kdbus_cmd_match *cmd;
++ gsize size, len;
++ gint ret;
++
++ if (match_rule[0] == '-')
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ if (name != NULL)
++ {
++ if (!g_dbus_is_name (name))
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_INVALID_ARGS,
++ "Invalid dbus name: %s", name);
++ return FALSE;
++ }
++ }
++
++ match = match_new (match_rule);
++ if (!match)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_MATCH_RULE_INVALID,
++ "Invalid rule: %s", match_rule);
++ return FALSE;
++ }
++
++ /* 'name' argument is missing or is a unique name */
++ if (name == NULL || g_dbus_is_unique_name (name))
++ {
++ size = KDBUS_ALIGN8(G_STRUCT_OFFSET (struct kdbus_cmd_match, items) +
++ G_STRUCT_OFFSET (struct kdbus_item, id_change) +
++ sizeof (struct kdbus_notify_id_change));
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = match->cookie;
++ item = cmd->items;
++
++ if (name)
++ item->id_change.id = strtoull (name + 3, NULL, 10);
++ else
++ item->id_change.id = KDBUS_MATCH_ID_ANY;
++
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, id_change) +
++ sizeof (struct kdbus_notify_id_change);
++
++ item->type = KDBUS_ITEM_ID_ADD;
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ item->type = KDBUS_ITEM_ID_REMOVE;
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++ }
++
++ /* 'name' argument is missing or is a well-known name */
++ if (name == NULL || !g_dbus_is_unique_name (name))
++ {
++ if (name)
++ len = strlen(name) + 1;
++ else
++ len = 0;
++
++ 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);
++
++ cmd = g_alloca0 (size);
++ cmd->size = size;
++ cmd->cookie = match->cookie;
++ item = cmd->items;
++
++ item->name_change.old_id.id = KDBUS_MATCH_ID_ANY;
++ item->name_change.new_id.id = KDBUS_MATCH_ID_ANY;
++ item->size = (guint64) G_STRUCT_OFFSET (struct kdbus_item, name_change) +
++ (guint64) G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
++
++ if (name)
++ memcpy(item->name_change.name, name, len);
++
++ item->type = KDBUS_ITEM_NAME_ADD;
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ item->type = KDBUS_ITEM_NAME_REMOVE;
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++
++ item->type = KDBUS_ITEM_NAME_CHANGE;
++ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
++ if (ret < 0)
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ "Error while adding a match");
++ match_free (match);
++ return FALSE;
++ }
++ }
++
++ g_mutex_lock (&worker->matches_mutex);
++ worker->matches = g_list_prepend (worker->matches, match);
++ g_mutex_unlock (&worker->matches_mutex);
++ return TRUE;
++}
++
++
++/*
++ * g_kdbus_setup_bloom:
++ * Based on bus-bloom.c from systemd
++ * http://cgit.freedesktop.org/systemd/systemd/tree/src/libsystemd/sd-bus/bus-bloom.c
++ */
++static void
++g_kdbus_setup_bloom (GKDBusWorker *worker,
++ GDBusMessage *dbus_msg,
++ struct kdbus_bloom_filter *bloom_filter)
++{
++ GVariant *body;
++ gchar *message_type;
++ const gchar *interface;
++ const gchar *member;
++ const gchar *path;
++ void *bloom_data;
++
++ 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));
++ interface = g_dbus_message_get_interface (dbus_msg);
++ member = g_dbus_message_get_member (dbus_msg);
++ path = g_dbus_message_get_path (dbus_msg);
++
++ bloom_data = bloom_filter->data;
++ memset (bloom_data, 0, worker->bloom_size);
++ bloom_filter->generation = 0;
++
++ g_kdbus_bloom_add_pair(worker, bloom_data, "message-type", message_type);
++
++ if (interface)
++ g_kdbus_bloom_add_pair(worker, bloom_data, "interface", interface);
++
++ if (member)
++ g_kdbus_bloom_add_pair(worker, bloom_data, "member", member);
++
++ if (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)
++ {
++ 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")];
++ const gchar *str;
++ GVariant *child;
++ gchar *e;
++
++ 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 = g_variant_get_child_value (body, cnt);
++ str = g_variant_get_string (child, NULL);
++
++ e = stpcpy(buf, "arg");
++ if (cnt < 10)
++ *(e++) = '0' + (char) cnt;
++ else
++ {
++ *(e++) = '0' + (char) (cnt / 10);
++ *(e++) = '0' + (char) (cnt % 10);
++ }
++
++ /* We add this one for both strings and object paths */
++ strcpy(e, "-slash-prefix");
++ 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, '.');
++
++ *e = 0;
++ g_kdbus_bloom_add_pair(worker, bloom_data, buf, str);
++ }
++
++ g_variant_unref (child);
++ }
++ }
++ g_free (message_type);
++}
++
++
++/*
++ * g_kdbus_translate_id_change
++ */
++static void
++g_kdbus_translate_id_change (GKDBusWorker *worker,
++ struct kdbus_item *item)
++{
++ GDBusMessage *signal_message;
++ gchar *name;
++
++ signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++ "org.freedesktop.DBus",
++ "NameOwnerChanged");
++
++ name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->id_change.id);
++
++ g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++ g_dbus_message_set_body (signal_message,
++ g_variant_new ("(sss)",
++ name,
++ item->type == KDBUS_ITEM_ID_ADD ? "" : name,
++ item->type == KDBUS_ITEM_ID_ADD ? name : ""));
++
++ (* worker->message_received_callback) (signal_message, worker->user_data);
++
++ g_free (name);
++ g_object_unref (signal_message);
++}
++
++
++/*
++ * g_kdbus_translate_name_change
++ */
++static void
++g_kdbus_translate_name_change (GKDBusWorker *worker,
++ struct kdbus_item *item)
++{
++ GDBusMessage *signal_message;
++
++ signal_message = NULL;
++
++ /* NameAcquired */
++ if ((item->type == KDBUS_ITEM_NAME_ADD) ||
++ (item->type == KDBUS_ITEM_NAME_CHANGE && ((guint64)item->name_change.new_id.id == worker->unique_id)))
++ {
++ signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++ "org.freedesktop.DBus",
++ "NameAcquired");
++
++ g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++ g_dbus_message_set_body (signal_message,
++ g_variant_new ("(s)", item->name_change.name));
++
++ (* worker->message_received_callback) (signal_message, worker->user_data);
++ g_object_unref (signal_message);
++ }
++
++ /* NameLost */
++ if ((item->type == KDBUS_ITEM_NAME_REMOVE) ||
++ (item->type == KDBUS_ITEM_NAME_CHANGE && ((guint64)item->name_change.old_id.id == worker->unique_id)))
++ {
++ signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++ "org.freedesktop.DBus",
++ "NameLost");
++
++ g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++ g_dbus_message_set_body (signal_message,
++ g_variant_new ("(s)", item->name_change.name));
++
++ (* worker->message_received_callback) (signal_message, worker->user_data);
++ g_object_unref (signal_message);
++ }
++
++ /* NameOwnerChanged */
++ if (1)
++ {
++ gchar *old_name;
++ gchar *new_name;
++
++ old_name = NULL;
++ new_name = NULL;
++
++ /* old_name */
++ if (!(item->type == KDBUS_ITEM_NAME_ADD || (item->name_change.old_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))))
++ old_name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->name_change.old_id.id);
++
++ /* new_name */
++ if (!(item->type == KDBUS_ITEM_NAME_REMOVE || (item->name_change.new_id.flags & (KDBUS_NAME_IN_QUEUE|KDBUS_NAME_ACTIVATOR))))
++ new_name = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) item->name_change.new_id.id);
++ else
++ if (old_name == NULL)
++ return;
++
++ /* send signal */
++ signal_message = g_dbus_message_new_signal ("/org/freedesktop/DBus",
++ "org.freedesktop.DBus",
++ "NameOwnerChanged");
++ g_dbus_message_set_sender (signal_message, "org.freedesktop.DBus");
++ g_dbus_message_set_body (signal_message,
++ g_variant_new ("(sss)",
++ item->name_change.name,
++ old_name ? old_name : "",
++ new_name ? new_name : ""));
++
++ (* worker->message_received_callback) (signal_message, worker->user_data);
++
++ g_free (old_name);
++ g_free (new_name);
++ g_object_unref (signal_message);
++ }
++}
++
++
++/*
++ * g_kdbus_translate_kernel_reply
++ */
++static void
++g_kdbus_translate_kernel_reply (GKDBusWorker *worker,
++ struct kdbus_msg *msg,
++ struct kdbus_item *item)
++{
++ GDBusMessage *message;
++
++ message = g_dbus_message_new ();
++
++ g_dbus_message_set_message_type (message, G_DBUS_MESSAGE_TYPE_ERROR);
++ g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED);
++ g_dbus_message_set_reply_serial (message, (guint32) msg->cookie_reply);
++
++ g_dbus_message_set_sender (message, "org.freedesktop.DBus");
++ g_dbus_message_set_destination (message, worker->unique_name);
++
++ g_dbus_message_set_error_name (message, "org.freedesktop.DBus.Error.NoReply");
++
++ if (item->type == KDBUS_ITEM_REPLY_TIMEOUT)
++ g_dbus_message_set_body (message, g_variant_new ("(s)", "Method call timed out"));
++ else
++ g_dbus_message_set_body (message, g_variant_new ("(s)", "Method call peer died"));
++
++ (* worker->message_received_callback) (message, worker->user_data);
++
++ g_object_unref (message);
++}
++
++
++/*
++ * g_kdbus_decode_kernel_msg
++ */
++static void
++g_kdbus_decode_kernel_msg (GKDBusWorker *worker,
++ struct kdbus_msg *msg)
++{
++ struct kdbus_item *item = NULL;
++
++ KDBUS_ITEM_FOREACH(item, msg, items)
++ {
++ switch (item->type)
++ {
++ case KDBUS_ITEM_ID_ADD:
++ case KDBUS_ITEM_ID_REMOVE:
++ g_kdbus_translate_id_change (worker, item);
++ break;
++
++ case KDBUS_ITEM_NAME_ADD:
++ case KDBUS_ITEM_NAME_REMOVE:
++ case KDBUS_ITEM_NAME_CHANGE:
++ g_kdbus_translate_name_change (worker, item);
++ break;
++
++ case KDBUS_ITEM_REPLY_TIMEOUT:
++ case KDBUS_ITEM_REPLY_DEAD:
++ g_kdbus_translate_kernel_reply (worker, msg, item);
++ break;
++
++ case KDBUS_ITEM_TIMESTAMP:
++ break;
++
++ default:
++ g_warning ("kdbus: unknown field in kernel message - %lld", item->type);
++ }
++ }
++}
++
++#define DEFINE_READ_FUNCTION(name, type) \
++ static inline type \
++ read_ ## name (GVariantSerialised data) \
++ { \
++ if (data.size < sizeof(type)) \
++ return 0; \
++ return *(type *)data.data; \
++ }
++
++DEFINE_READ_FUNCTION (byte, guchar)
++DEFINE_READ_FUNCTION (uint64, guint64)
++DEFINE_READ_FUNCTION (uint32, guint32)
++
++static const gchar *
++check_str (GVariantSerialised data)
++{
++ const gchar *str = (gchar *)data.data;
++ if (str[data.size] == '\0')
++ return str;
++ return "";
++}
++
++/*
++ * g_kdbus_decode_dbus_msg
++ */
++static GKDBusMessage *
++g_kdbus_decode_dbus_msg (GKDBusWorker *worker,
++ struct kdbus_msg *msg)
++{
++ GKDBusMessage *kmsg;
++ struct kdbus_item *item;
++ gssize data_size = 0;
++ GArray *body_vectors;
++ gsize body_size;
++ GVariant *body;
++ gchar *sender;
++ guint i;
++ GVariant *parts[2];
++ GString *owned_name;
++ guint8 type = 0, flags = 0;
++ guint64 serial = 0;
++ GVariantSerialised gvs = { 0, };
++
++ kmsg = g_new0 (GKDBusMessage, 1);
++ kmsg->message = g_dbus_message_new();
++ kmsg->sender_euid = (uid_t) -1;
++ kmsg->sender_egid = (gid_t) -1;
++ kmsg->sender_seclabel = NULL;
++ kmsg->sender_names = NULL;
++
++ body_vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
++
++ item = msg->items;
++ body_size = 0;
++ owned_name = NULL;
++
++ KDBUS_ITEM_FOREACH(item, msg, items)
++ {
++ if (item->size < KDBUS_ITEM_HEADER_SIZE)
++ g_error("kdbus: %llu bytes - invalid data record", item->size);
++
++ data_size = item->size - KDBUS_ITEM_HEADER_SIZE;
++
++ switch (item->type)
++ {
++ case KDBUS_ITEM_DST_NAME:
++ /* 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;
++
++ case KDBUS_ITEM_PAYLOAD_OFF:
++ {
++ 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;
++
++ case KDBUS_ITEM_PAYLOAD_MEMFD:
++ {
++ GVariantVector vector;
++ const guchar *data;
++ gsize size;
++
++ vector.gbytes = g_bytes_new_take_zero_copy_fd_size (item->memfd.fd, item->memfd.size);
++ data = g_bytes_get_data (vector.gbytes, &size);
++
++ g_assert (item->memfd.size == 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;
++
++ case KDBUS_ITEM_FDS:
++ {
++ GUnixFDList *fd_list;
++
++ fd_list = g_unix_fd_list_new_from_array (item->fds, data_size / sizeof (int));
++ g_dbus_message_set_unix_fd_list (kmsg->message, fd_list);
++ g_object_unref (fd_list);
++ }
++ break;
++
++ /* [libdbuspolicy] read euid and egid values */
++ case KDBUS_ITEM_CREDS:
++
++ if ((uid_t) item->creds.euid != (uid_t) -1)
++ kmsg->sender_euid = (uid_t) item->creds.euid;
++
++ if ((gid_t) item->creds.egid != (gid_t) -1)
++ kmsg->sender_egid = (gid_t) item->creds.egid;
++
++ break;
++
++ /* [libdbuspolicy] read security label value */
++ case KDBUS_ITEM_SECLABEL:
++
++ /*if (item->str != NULL)*/
++ kmsg->sender_seclabel = g_strdup (item->str);
++
++ break;
++
++ /* [libdbuspolicy] read all owned well-known names */
++ case KDBUS_ITEM_OWNED_NAME:
++
++ if (g_dbus_is_name (item->name.name))
++ {
++ if (owned_name == NULL)
++ owned_name = g_string_new (item->name.name);
++ else
++ g_string_append_printf (owned_name, " %s", item->name.name);
++ }
++
++ break;
++
++ case KDBUS_ITEM_TIMESTAMP:
++ case KDBUS_ITEM_PIDS:
++ 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_AUDIT:
++ case KDBUS_ITEM_CAPS:
++ case KDBUS_ITEM_CONN_DESCRIPTION:
++ case KDBUS_ITEM_AUXGROUPS:
++ case KDBUS_ITEM_NAME:
++ case KDBUS_ITEM_DST_ID:
++ case KDBUS_ITEM_BLOOM_FILTER:
++ break;
++
++ default:
++ g_warning ("kdbus: unknown filed - %lld", item->type);
++ break;
++ }
++ }
++
++ 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);
++
++ parts[0] = g_variant_get_child_value (body, 0);
++ gvs.type_info = g_variant_type_info_get (G_VARIANT_TYPE ("(yyyyuta{tv})"));
++ gvs.data = (guchar *)g_variant_get_data (parts[0]);
++ gvs.size = g_variant_get_size (parts[0]);
++ gvs.depth = 0;
++ gvs.ordered_offsets_up_to = 0;
++ gvs.checked_offsets_up_to = 0;
++
++ type = read_byte (g_variant_serialised_get_child(gvs, 1));
++ flags = read_byte (g_variant_serialised_get_child(gvs, 2));
++ serial = read_uint64 (g_variant_serialised_get_child(gvs, 5));
++
++ {
++ guint8 i = 0;
++ gsize n_children;
++ GVariantSerialised gvsc;
++
++ gvsc = g_variant_serialised_get_child(gvs, 6);
++ n_children = g_variant_serialised_n_children(gvsc);
++
++ for (i = 0; i < n_children; i++) {
++ GVariantSerialised item;
++ GVariantSerialised variant;
++ guint64 key;
++ GVariantSerialised variant_value;
++
++ item = g_variant_serialised_get_child (gvsc, i);
++ key = read_uint64 (g_variant_serialised_get_child (item, 0));
++ variant = g_variant_serialised_get_child (item, 1);
++ variant_value = g_variant_serialised_get_child (variant, 0); /* strip variant */
++
++ switch (key) {
++ case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
++ {
++ guint64 val = read_uint64 (variant_value);
++ g_dbus_message_set_reply_serial (kmsg->message, (guint32)val);
++ }
++ break;
++ case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
++ g_dbus_message_set_header (kmsg->message, key, g_variant_new_uint32 (read_uint32 (variant_value)));
++ break;
++ case G_DBUS_MESSAGE_HEADER_FIELD_PATH:
++ g_dbus_message_set_header (kmsg->message, key, g_variant_new_object_path (check_str (variant_value)));
++ break;
++ case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
++ g_dbus_message_set_header (kmsg->message, key, g_variant_new_signature (check_str (variant_value)));
++ break;
++ default:
++ g_dbus_message_set_header (kmsg->message, key, g_variant_new_string (check_str (variant_value)));
++ break;
++ }
++ }
++ }
++
++ parts[1] = g_variant_get_child_value (body, 1);
++ g_variant_unref (body);
++
++ g_variant_unref (parts[0]);
++
++ for (i = 0; i < body_vectors->len; i++)
++ g_bytes_unref (g_array_index (body_vectors, GVariantVector, i).gbytes);
++
++ g_array_free (body_vectors, TRUE);
++
++ g_dbus_message_set_flags (kmsg->message, flags);
++ g_dbus_message_set_serial (kmsg->message, serial);
++ g_dbus_message_set_message_type (kmsg->message, type);
++
++ body = g_variant_get_variant (parts[1]);
++ if (!g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ g_dbus_message_set_body (kmsg->message, body);
++ else
++ g_dbus_message_set_body (kmsg->message, NULL);
++
++ g_variant_unref (body);
++ g_variant_unref (parts[1]);
++
++ /* set 'sender' field */
++ sender = g_strdup_printf (":1.%"G_GUINT64_FORMAT, (guint64) msg->src_id);
++ g_dbus_message_set_sender (kmsg->message, sender);
++ g_free (sender);
++
++ /* owned name */
++ if (owned_name != NULL)
++ kmsg->sender_names = g_string_free (owned_name, FALSE);
++
++ return kmsg;
++}
++
++
++/*
++ * _g_kdbus_receive
++ */
++static void
++_g_kdbus_receive (GKDBusWorker *worker,
++ GError **error)
++{
++ struct kdbus_cmd_recv recv;
++ struct kdbus_msg *msg;
++ gboolean can_receive;
++ gint ret = 0;
++
++ can_receive = TRUE;
++ memset (&recv, 0, sizeof recv);
++ recv.size = sizeof (recv);
++
++again:
++ ret = ioctl (worker->fd, KDBUS_CMD_RECV, &recv);
++ if (ret < 0)
++ ret = errno;
++
++ if (recv.return_flags & KDBUS_RECV_RETURN_DROPPED_MSGS)
++ g_warning ("kdbus: %lld dropped broadcast messages", recv.dropped_msgs);
++
++ if (ret != 0)
++ {
++ if (ret == EINTR)
++ goto again;
++
++ if (ret == EAGAIN)
++ return;
++
++ g_set_error (error, G_IO_ERROR,
++ g_io_error_from_errno (ret),
++ _("Error while receiving message: %s"),
++ g_strerror (ret));
++ return;
++ }
++
++ msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + recv.msg.offset);
++
++ if (msg->payload_type == KDBUS_PAYLOAD_DBUS)
++ {
++ GKDBusMessage *kmsg;
++
++ kmsg = g_kdbus_decode_dbus_msg (worker, msg);
++
++#ifdef LIBDBUSPOLICY
++ if (worker->dbuspolicy != NULL)
++ {
++ if (g_dbus_message_get_message_type (kmsg->message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL ||
++ g_dbus_message_get_message_type (kmsg->message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
++ {
++ if ((kmsg->sender_euid != (uid_t) -1) && (kmsg->sender_egid != (gid_t) -1) &&
++ (kmsg->sender_seclabel != NULL))
++ {
++ gint check;
++ const gchar *destination = g_dbus_message_get_destination (kmsg->message);
++ if (!destination)
++ destination = worker->unique_name;
++
++ check = dbuspolicy1_check_in (worker->dbuspolicy,
++ destination,
++ kmsg->sender_names,
++ kmsg->sender_seclabel,
++ kmsg->sender_euid,
++ kmsg->sender_egid,
++ g_dbus_message_get_path (kmsg->message),
++ g_dbus_message_get_interface (kmsg->message),
++ g_dbus_message_get_member (kmsg->message),
++ g_dbus_message_get_message_type (kmsg->message),
++ NULL, 0, 0);
++ if (check != 1)
++ {
++ can_receive = FALSE;
++ }
++ }
++ else
++ {
++ can_receive = FALSE;
++ }
++ }
++ }
++#endif
++
++ if (can_receive)
++ (* worker->message_received_callback) (kmsg->message, worker->user_data);
++
++ if (kmsg->sender_seclabel != NULL)
++ g_free (kmsg->sender_seclabel);
++
++ if (kmsg->sender_names != NULL)
++ g_free (kmsg->sender_names);
++
++ g_object_unref (kmsg->message);
++ g_free (kmsg);
++ }
++ else if (msg->payload_type == KDBUS_PAYLOAD_KERNEL)
++ g_kdbus_decode_kernel_msg (worker, msg);
++ else
++ {
++ g_set_error (error,
++ G_DBUS_ERROR,
++ G_DBUS_ERROR_FAILED,
++ _("Received unknown payload type"));
++ }
++
++ g_kdbus_close_msg (worker, msg);
++ return;
++}
++
++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);
++
++ 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)
++{
++ gboolean ok = TRUE;
++ do {
++ struct kdbus_vec vec = {
++ .size = size <= KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE ? size : KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE,
++ .address = (gsize) data
++ };
++ ok = g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_VEC, &vec, sizeof vec);
++ data = (char*)data + vec.size;
++ size -= vec.size;
++ } while (size > 0 && ok);
++ return ok;
++}
++
++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);
++}
++
++static struct kdbus_bloom_filter *
++g_kdbus_msg_append_bloom (struct kdbus_msg *msg,
++ gsize size)
++{
++ struct kdbus_item *bloom_item;
++ gsize bloom_item_size;
++
++ bloom_item_size = (gulong) G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
++ (gulong) G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
++ size;
++
++ msg->size += (-msg->size) & 7;
++ bloom_item = (struct kdbus_item *) ((guchar *) msg + msg->size);
++
++ bloom_item->size = bloom_item_size;
++ bloom_item->type = KDBUS_ITEM_BLOOM_FILTER;
++
++ msg->size += bloom_item->size;
++ return &bloom_item->bloom_filter;
++}
++
++/*
++ * Returns header size.
++ */
++static gsize
++prepare_body_vectors (GKDBusWorker *worker,
++ GDBusMessage *message,
++ GVariantVectors *body_vectors,
++ gboolean *lg_h_field_exist)
++{
++ struct dbus_fixed_header fh;
++ GHashTableIter header_iter;
++ GVariantBuilder builder;
++ gpointer key, value;
++ GVariant *parts[3];
++ GVariant *body;
++ gsize header_size;
++
++ *lg_h_field_exist = FALSE;
++
++ 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_assert_nonnull (parts[0]);
++
++ 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, worker->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_REPLY_SERIAL:
++ g_variant_builder_add (&builder, "{tv}", key_int, g_variant_new_uint64 (g_dbus_message_get_reply_serial(message)));
++ continue;
++
++
++ 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_DESTINATION:
++ case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
++ 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.
++ *
++ * Anyway, kdbus does not check it, and libraries handle it...
++ */
++
++ if (g_variant_get_size (value) >= 128)
++ *lg_h_field_exist = TRUE;
++
++ 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:
++ continue;
++
++ default:
++ g_assert_not_reached ();
++ }
++ }
++ parts[1] = g_variant_builder_end (&builder);
++ g_assert_nonnull (parts[1]);
++
++ body = g_dbus_message_get_body (message);
++ if (!body)
++ body = g_variant_new ("()");
++ parts[2] = g_variant_new_variant (body);
++ g_assert_nonnull (parts[2]);
++
++ header_size = g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]);
++
++ body = g_variant_ref_sink (g_variant_new_tuple (parts, G_N_ELEMENTS (parts)));
++ GLIB_PRIVATE_CALL(g_variant_to_vectors) (body, body_vectors);
++
++ /* 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; this
++ * condition must be assured later, during composing kdbus vectors.
++ */
++ g_assert_cmpint (body_vectors->vectors->len, >, 0);
++
++ g_variant_unref (body);
++
++ return header_size;
++}
++
++static void
++make_single_header_vector (GVariantVectors *body_vectors,
++ gsize header_size,
++ gboolean lg_h_field_exist)
++{
++ guint i = 0;
++ gsize added_vectors_size = 0;
++ GVariantVector *first_vector;
++
++ /* Merge all header vectors into single vector */
++ first_vector = &g_array_index (body_vectors->vectors, GVariantVector, 0);
++ if (first_vector->size < header_size)
++ {
++ /* There are multiple header vectors, we have to join them together into a single one */
++ GByteArray *header = g_byte_array_sized_new (header_size);
++ gsize dummy_size;
++
++ g_assert_nonnull (header);
++ g_assert_true (lg_h_field_exist);
++
++ while (added_vectors_size < header_size && i < body_vectors->vectors->len)
++ {
++ GVariantVector *vector = &g_array_index (body_vectors->vectors, GVariantVector, i);
++
++ if (vector->gbytes)
++ {
++ g_byte_array_append (header, vector->data.pointer, vector->size);
++ g_bytes_unref (vector->gbytes);
++ }
++ else
++ {
++ g_byte_array_append (header, body_vectors->extra_bytes->data + vector->data.offset, vector->size);
++ }
++
++ added_vectors_size += vector->size;
++ i++;
++ }
++
++ /* Sanity check if the first vector contains at least the complete header */
++ g_assert_cmpint (added_vectors_size, >=, header_size);
++
++ first_vector->gbytes = g_byte_array_free_to_bytes (header);
++ first_vector->data.pointer = g_bytes_get_data (first_vector->gbytes, &dummy_size);
++ first_vector->size = added_vectors_size;
++
++ g_array_remove_range (body_vectors->vectors, 1, i-1);
++ }
++ /* else: If there is only a single header vector, then just go */
++}
++
++static gboolean
++add_message_destination_item (struct kdbus_msg *msg,
++ GDBusMessage *message,
++ GError **error)
++{
++ const gchar *dst_name;
++
++ dst_name = g_dbus_message_get_destination (message);
++ if (dst_name != NULL)
++ {
++ 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);
++ }
++ else
++ {
++ g_kdbus_msg_append_item (msg, KDBUS_ITEM_DST_NAME, dst_name, strlen (dst_name) + 1);
++ msg->dst_id = KDBUS_DST_ID_NAME;
++ }
++ }
++ else
++ msg->dst_id = KDBUS_DST_ID_BROADCAST;
++
++ return TRUE;
++}
++
++static void
++add_file_descriptors_item (struct kdbus_msg *msg,
++ GDBusMessage *message)
++{
++ GUnixFDList *fd_list;
++
++ fd_list = g_dbus_message_get_unix_fd_list (message);
++ if (fd_list != NULL)
++ {
++ const gint *fds;
++ gint n_fds;
++
++ 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);
++ }
++}
++
++static gboolean
++add_body_vectors (struct kdbus_msg *msg,
++ GVariantVectors *body_vectors,
++ gint *memfd_fd)
++{
++ guint i = 0;
++
++ /* Add all the vectors to the kdbus message */
++ for (; i < body_vectors->vectors->len; i++)
++ {
++ GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
++
++ if (vector.gbytes)
++ {
++ const guchar *bytes_data;
++ gboolean use_memfd;
++ gsize bytes_size;
++
++ use_memfd = FALSE;
++ bytes_data = g_bytes_get_data (vector.gbytes, &bytes_size);
++
++ /* check whether we can and should use memfd */
++ if ((msg->dst_id != KDBUS_DST_ID_BROADCAST) && (bytes_size > KDBUS_MEMFD_THRESHOLD))
++ use_memfd = TRUE;
++
++ if (use_memfd)
++ {
++ const guchar *bytes_data_wr;
++ gint64 wr;
++
++ /* create memfd object */
++ *memfd_fd = glib_linux_memfd_create ("glib-kdbus-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING);
++ if (*memfd_fd == -1)
++ {
++ g_warning ("kdbus: missing kernel memfd support");
++ use_memfd = FALSE; /* send as PAYLOAD_VEC item */
++ }
++ else
++ {
++ /* write data to memfd */
++ bytes_data_wr = bytes_data;
++ while (bytes_size)
++ {
++ wr = write (*memfd_fd, bytes_data_wr, bytes_size);
++ if (wr < 0)
++ g_warning ("kdbus: writing to memfd failed: (%d) %m", errno);
++
++ bytes_size -= wr;
++ bytes_data_wr += wr;
++ }
++
++ /* seal memfd */
++ if (!g_unix_fd_ensure_zero_copy_safe (*memfd_fd))
++ {
++ g_warning ("kdbus: memfd sealing failed");
++ use_memfd = FALSE; /* send as PAYLOAD_VEC item */
++ }
++ else
++ {
++ /* attach memfd item */
++ if (!g_kdbus_msg_append_payload_memfd (msg, *memfd_fd, vector.data.pointer - bytes_data, vector.size))
++ return FALSE;
++ }
++ } /* *memfd_fd == -1 */
++ } /* use_memfd */
++
++ if (!use_memfd)
++ if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
++ return FALSE;
++ }
++ else
++ if (!g_kdbus_msg_append_payload_vec (msg, body_vectors->extra_bytes->data + vector.data.offset, vector.size))
++ return FALSE;
++ }
++ return TRUE;
++}
++
++static gboolean
++add_bloom_item (struct kdbus_msg *msg,
++ GDBusMessage *message,
++ GKDBusWorker *worker)
++{
++ struct kdbus_bloom_filter *bloom_filter;
++
++ if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
++ {
++ msg->flags |= KDBUS_MSG_SIGNAL;
++ bloom_filter = g_kdbus_msg_append_bloom (msg, worker->bloom_size);
++ if (bloom_filter == NULL)
++ return FALSE;
++ g_kdbus_setup_bloom (worker, message, bloom_filter);
++ }
++ return TRUE;
++}
++
++static gsize
++compute_msg_size (GDBusMessage *message,
++ GKDBusWorker *worker,
++ GVariantVectors *body_vectors)
++{
++ gsize items_size = 0;
++ GUnixFDList *fd_list;
++ const gchar *dst_name;
++ guint i;
++ GDBusMessageType msg_type = g_dbus_message_get_message_type (message);
++
++ /* payload - check which vectors will become memfds */
++ for (i = 0; i < body_vectors->vectors->len; i++)
++ {
++ GVariantVector vector = g_array_index (body_vectors->vectors, GVariantVector, i);
++ gsize payload_size;
++ gsize kdbus_num_vectors;
++
++ if (vector.gbytes)
++ {
++ payload_size = g_bytes_get_size (vector.gbytes);
++ if (msg_type != G_DBUS_MESSAGE_TYPE_SIGNAL && payload_size > KDBUS_MEMFD_THRESHOLD)
++ {
++ /* We can do this because we know that struct kdbus_memfd is larger or equal than
++ struct kdbus_vec. struct kdbus_vec is fallback option if memfd does not work */
++ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_memfd));
++ continue;
++ }
++ }
++ else
++ {
++ payload_size = vector.size;
++ }
++
++ kdbus_num_vectors = (payload_size + KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE - 1) / KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE;
++ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_vec)) * kdbus_num_vectors;
++ }
++
++ /* file descriptors */
++ fd_list = g_dbus_message_get_unix_fd_list (message);
++ if (fd_list != NULL)
++ items_size += KDBUS_ITEM_SIZE (g_unix_fd_list_get_length (fd_list) * sizeof(gint));
++
++ /* destination */
++ dst_name = g_dbus_message_get_destination (message);
++ if (dst_name != NULL)
++ items_size += KDBUS_ITEM_SIZE (strlen (dst_name) + 1);
++
++ /* bloom filters */
++ if (msg_type == G_DBUS_MESSAGE_TYPE_SIGNAL)
++ items_size += KDBUS_ITEM_SIZE (sizeof (struct kdbus_bloom_filter) + worker->bloom_size);
++
++ return sizeof (struct kdbus_msg) + items_size;
++}
++
++#ifdef LIBDBUSPOLICY
++static GDBusMessage*
++create_access_error_reply (GDBusMessage *message,
++ const GQuark domain,
++ const gint code,
++ const gchar *format,
++ ...)
++{
++ gchar *error_text;
++ GError *access_error;
++ gchar *dbus_error_name;
++ GDBusMessage *error_reply;
++ va_list args;
++
++ va_start (args, format);
++ error_text = g_strdup_vprintf (format, args);
++ va_end (args);
++
++ access_error = NULL;
++ g_set_error (&access_error, domain, code, "%s", error_text);
++ dbus_error_name = g_dbus_error_encode_gerror (access_error);
++ g_error_free (access_error);
++
++ error_reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", error_text);
++ g_free (dbus_error_name);
++ g_free (error_text);
++ return error_reply;
++}
++#endif
++
++#ifdef LIBDBUSPOLICY
++static GDBusMessage*
++check_result_to_error_reply (GDBusMessage *message,
++ const gint check_result)
++{
++ switch (check_result)
++ {
++ case DBUSPOLICY_RESULT_DENY:
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++ "Cannot send message - message rejected due to XML security policies");
++
++ case DBUSPOLICY_RESULT_DEST_NOT_AVAILABLE:
++ if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++ "Name \"%s\" does not exist", g_dbus_message_get_destination (message));
++ else
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++ "Cannot send message - destination '%s' not known", g_dbus_message_get_destination (message));
++
++ case DBUSPOLICY_RESULT_KDBUS_ERROR:
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++ "Cannot send message - message rejected due to internal libdbuspolicy error (kdbus)");
++
++ case DBUSPOLICY_RESULT_CYNARA_ERROR:
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++ "Cannot send message - message rejected due to internal libdbuspolicy error (Cynara)");
++
++ default:
++ return create_access_error_reply (message, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
++ "Cannot send message - unknown libdbuspolicy error");
++ }
++}
++#endif
++
++/*
++ * _g_kdbus_send
++ */
++static gboolean
++_g_kdbus_send (GKDBusWorker *worker,
++ GDBusMessage *message,
++ GDBusMessage **out_reply,
++ gint timeout_msec,
++ GCancellable *cancellable,
++ GError **error)
++{
++ struct kdbus_msg *msg;
++ GVariantVectors body_vectors;
++ struct kdbus_cmd_send *send;
++ gsize send_size;
++ gboolean result = FALSE;
++ gboolean lg_h_field_exist = FALSE;
++
++ gint memfd_fd;
++ gint cancel_fd;
++
++ gsize header_size;
++ gsize msg_size;
++
++ g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
++
++ send = NULL;
++ send_size = sizeof(*send);
++
++ /* Prepare message body - it is needed for kdbus msg size computation */
++ header_size = prepare_body_vectors (worker, message, &body_vectors, &lg_h_field_exist);
++ make_single_header_vector (&body_vectors, header_size, lg_h_field_exist);
++
++ /* We precompute needed size for the message to allocate exact space instead
++ of some arbitrary amount */
++ msg_size = compute_msg_size (message, worker, &body_vectors);
++ if (msg_size <= KDBUS_MSG_MAX_SIZE)
++ {
++ msg = g_alloca (msg_size);
++ memset (msg, 0, msg_size);
++ }
++ else
++ {
++ msg = g_malloc0 (msg_size);
++ }
++ g_assert_nonnull (msg);
++
++ memfd_fd = -1;
++ cancel_fd = -1;
++
++ /* fill in as we go... */
++ msg->size = sizeof (struct kdbus_msg);
++ msg->payload_type = KDBUS_PAYLOAD_DBUS;
++ msg->cookie = g_dbus_message_get_serial(message);
++
++ /* Message destination */
++ if (!add_message_destination_item (msg, message, error))
++ goto out;
++
++ /* File descriptors */
++ add_file_descriptors_item (msg, message);
++
++ if (!add_body_vectors (msg, &body_vectors, &memfd_fd))
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++ "message serialisation error: body vectors");
++ g_warning ("kdbus: message serialisation error: body vectors");
++ goto out;
++ }
++
++ /*
++ * set message flags
++ */
++ 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 = 1U | ( /* ensure nonzero */
++ 1000U * g_get_monotonic_time() + (
++ timeout_msec == -1 ? DBUS_DEFAULT_TIMEOUT_MSEC * 1000000LLU :
++ timeout_msec == G_MAXINT ? KDBUS_INFINITE_TIMEOUT_NS :
++ (guint64)timeout_msec * 1000000U
++ )
++ );
++ else
++ msg->cookie_reply = g_dbus_message_get_reply_serial(message);
++
++ /*
++ * append bloom filter item for broadcast signals
++ */
++ if (!add_bloom_item (msg, message, worker))
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
++ "message serialisation error: bloom filters");
++ g_warning ("kdbus: message serialisation error: bloom filters");
++ goto out;
++ }
++
++ if (out_reply != NULL && cancellable)
++ {
++ cancel_fd = g_cancellable_get_fd (cancellable);
++ if (cancel_fd != -1)
++ send_size += KDBUS_ITEM_SIZE (sizeof(cancel_fd));
++ }
++
++ g_assert_cmpuint (msg->size, <=, msg_size);
++
++ send = g_alloca0 (send_size);
++ send->size = send_size;
++ send->msg_address = (gsize) msg;
++
++ if (out_reply != NULL)
++ {
++ /* synchronous call */
++ send->flags = KDBUS_SEND_SYNC_REPLY;
++
++ if (cancel_fd != -1)
++ {
++ struct kdbus_item *item;
++
++ item = send->items;
++ item->type = KDBUS_ITEM_CANCEL_FD;
++ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(cancel_fd);
++ item->fds[0] = cancel_fd;
++ }
++ }
++ else
++ {
++ /* asynchronous call */
++ send->flags = 0;
++ }
++
++ /*
++ * show debug
++ */
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " >>>> SENT D-Bus/kdbus message\n");
++ s = g_dbus_message_print (message, 2);
++ g_print ("%s", s);
++ g_free (s);
++ _g_dbus_debug_print_unlock ();
++ }
++
++ /*
++ * check policy
++ */
++#ifdef LIBDBUSPOLICY
++ if (worker->dbuspolicy != NULL)
++ {
++ if (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_METHOD_CALL ||
++ (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL &&
++ g_dbus_message_get_destination (message) != NULL))
++ {
++ gint check;
++
++ check = dbuspolicy1_check_out (worker->dbuspolicy,
++ g_dbus_message_get_destination (message),
++ worker->unique_name,
++ g_dbus_message_get_path (message),
++ g_dbus_message_get_interface (message),
++ g_dbus_message_get_member (message),
++ g_dbus_message_get_message_type (message),
++ NULL, 0, 0);
++ if (check != DBUSPOLICY_RESULT_ALLOW)
++ {
++ GDBusMessage *access_error_message;
++ access_error_message = check_result_to_error_reply (message, check);
++ result = TRUE;
++ if (out_reply != NULL)
++ {
++ *out_reply = access_error_message;
++ }
++ else
++ {
++ send_synthetic_message (worker, access_error_message);
++ }
++
++ goto out;
++ }
++ }
++ }
++#endif
++
++ /*
++ * send message
++ */
++ if (ioctl(worker->fd, KDBUS_CMD_SEND, send))
++ {
++ int ret = errno;
++ gchar *info;
++ if (asprintf (&info, "sender=%s destination=%s path=%s interface=%s member=%s type=%d",
++ g_dbus_message_get_sender (message),
++ g_dbus_message_get_destination (message),
++ g_dbus_message_get_path (message),
++ g_dbus_message_get_interface (message),
++ g_dbus_message_get_member (message),
++ g_dbus_message_get_message_type (message)) < 0)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", g_strerror(errno));
++ /* If asprintf fails, we lose the ioctl errno,
++ * but without info, we can't give a useful error message.
++ * In practice though, asprintf failure is exceedingly rare.
++ */
++ }
++ else
++ {
++ errno = ret;
++ if (errno == ENXIO || errno == ESRCH)
++ {
++ if (g_dbus_message_get_flags (message) & G_DBUS_MESSAGE_FLAGS_NO_AUTO_START)
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER,
++ "Name \"%s\" does not exist, %s", g_dbus_message_get_destination (message), info);
++ else
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++ "Destination '%s' not known, %s", g_dbus_message_get_destination (message), info);
++ }
++ else if (errno == EADDRNOTAVAIL)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
++ "No support for activation for name: %s, %s", g_dbus_message_get_destination (message), info);
++ }
++ else if (errno == EXFULL)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++ "The memory pool of the receiver is full, %s", info);
++ }
++ else if (errno == ENOBUFS)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++ "Too many pending messages on the receiver side, %s", info);
++ }
++ else if (errno == EMSGSIZE)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++ "The size of the message is excessive, %s", info);
++ }
++ else if (errno == EMLINK)
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_LIMITS_EXCEEDED,
++ "The maximum number of pending replies per connection has been reached, %s", info);
++ }
++ else if (errno == ECANCELED)
++ {
++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
++ "Operation was cancelled, %s", info);
++ }
++ else if (errno == ETIMEDOUT)
++ {
++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
++ "Timeout was reached, %s", info);
++ }
++ else if (errno == EPERM)
++ {
++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
++ "Permission denied, %s", info);
++ }
++ else
++ {
++ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s, %s", g_strerror(errno), info);
++ g_warning ("kdbus: %s, %s", g_strerror(errno), info);
++ }
++ free(info);
++ }
++ }
++ else
++ {
++ result = TRUE;
++ if (out_reply != NULL)
++ {
++ GKDBusMessage *kmsg;
++ struct kdbus_msg *kdbus_msg;
++
++ kdbus_msg = (struct kdbus_msg *)((guint8 *)worker->kdbus_buffer + send->reply.offset);
++
++ kmsg = g_kdbus_decode_dbus_msg (worker, kdbus_msg);
++ g_kdbus_close_msg (worker, kdbus_msg);
++
++ *out_reply = kmsg->message;
++
++ if (kmsg->sender_seclabel != NULL)
++ g_free (kmsg->sender_seclabel);
++
++ if (kmsg->sender_names != NULL)
++ g_free (kmsg->sender_names);
++
++ g_free (kmsg);
++
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " <<<< RECEIVED D-Bus message\n");
++ s = g_dbus_message_print (*out_reply, 2);
++ g_print ("%s", s);
++ g_free (s);
++ _g_dbus_debug_print_unlock ();
++ }
++ }
++ }
++
++out:
++ if (msg_size > KDBUS_MSG_MAX_SIZE)
++ g_free (msg);
++
++ if (cancel_fd != -1)
++ g_cancellable_release_fd (cancellable);
++
++ if (memfd_fd != -1)
++ close (memfd_fd);
++
++ GLIB_PRIVATE_CALL(g_variant_vectors_deinit) (&body_vectors);
++
++ return result;
++}
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++static void
++g_kdbus_worker_finalize (GObject *object)
++{
++ GKDBusWorker *worker;
++ GList *match;
++
++ worker = G_KDBUS_WORKER (object);
++
++ if (worker->kdbus_buffer != NULL)
++ {
++ munmap (worker->kdbus_buffer, worker->receive_pool_size);
++ worker->kdbus_buffer = NULL;
++ worker->receive_pool_size = 0;
++ }
++
++ if (worker->unique_name != NULL)
++ g_free (worker->unique_name);
++ worker->unique_name = NULL;
++
++ for (match = worker->matches; match != NULL; match = match->next)
++ match_free (match->data);
++ g_list_free (worker->matches);
++ g_mutex_clear (&worker->matches_mutex);
++
++#ifdef LIBDBUSPOLICY
++ if (worker->dbuspolicy != NULL)
++ dbuspolicy1_free (worker->dbuspolicy);
++#endif
++
++ if (worker->fd != -1 && !worker->closed)
++ _g_kdbus_close (worker);
++
++ 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;
++}
++
++static void
++g_kdbus_worker_init (GKDBusWorker *worker)
++{
++ worker->fd = -1;
++
++ worker->context = NULL;
++ worker->loop = NULL;
++ worker->thread = NULL;
++ worker->source = 0;
++
++ worker->kdbus_buffer = NULL;
++ worker->receive_pool_size = 0;
++ worker->unique_name = NULL;
++ worker->unique_id = -1;
++
++ worker->flags = KDBUS_HELLO_ACCEPT_FD;
++ worker->attach_flags_send = _KDBUS_ATTACH_ALL;
++ /* Attach items we really use only. Each collected item brings unneccessary overhead.
++ *
++ * The flags are used by libdbuspolicy library which uses these to apply dbus policy
++ * to each received message.
++ */
++ worker->attach_flags_recv = KDBUS_ATTACH_PIDS | KDBUS_ATTACH_CREDS | KDBUS_ATTACH_SECLABEL;
++
++ worker->bloom_size = 0;
++ worker->bloom_n_hash = 0;
++ worker->matches = NULL;
++ g_mutex_init (&worker->matches_mutex);
++
++#ifdef LIBDBUSPOLICY
++ worker->dbuspolicy = NULL;
++#endif
++}
++
++static gpointer
++_g_kdbus_worker_thread (gpointer _data)
++{
++ GMainLoop *loop = (GMainLoop *) _data;
++
++ g_main_loop_run (loop);
++
++ g_main_loop_unref (loop);
++
++ return NULL;
++}
++
++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;
++ }
++
++ worker->context = g_main_context_new ();
++ worker->loop = g_main_loop_new (worker->context, FALSE);
++ worker->thread = g_thread_new ("gkdbus", _g_kdbus_worker_thread, g_main_loop_ref(worker->loop));
++
++ 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;
++}
++
++static gboolean
++g_kdbus_ready (gint fd,
++ GIOCondition condition,
++ gpointer user_data)
++{
++ GKDBusWorker *worker;
++ GError *error;
++
++ worker = user_data;
++ error = NULL;
++
++ _g_kdbus_receive (worker, &error);
++ g_assert_no_error (error);
++
++ return G_SOURCE_CONTINUE;
++}
++
++void
++_g_kdbus_worker_unfreeze (GKDBusWorker *worker)
++{
++ gchar *name;
++
++ if (worker->source != NULL)
++ return;
++
++ worker->source = g_unix_fd_source_new (worker->fd, G_IO_IN);
++
++ g_source_set_callback (worker->source, (GSourceFunc) g_kdbus_ready,
++ g_object_ref (worker), g_object_unref);
++ name = g_strdup_printf ("kdbus worker");
++ g_source_set_name (worker->source, name);
++ g_free (name);
++
++ g_source_attach (worker->source, worker->context);
++}
++
++gboolean
++_g_kdbus_worker_send_message (GKDBusWorker *worker,
++ GDBusMessage *message,
++ gint timeout_msec,
++ GError **error)
++{
++#ifdef DBUS_DAEMON_EMULATION
++ if (_is_message_to_dbus_daemon (message))
++ {
++ GDBusMessage *reply = NULL;
++ reply = _dbus_daemon_synthetic_reply (worker, message, FALSE);
++
++ if (reply)
++ {
++ SyntheticReplyData *data;
++
++ data = g_new0 (SyntheticReplyData, 1);
++
++ data->worker = worker;
++ data->message = reply;
++
++ g_main_context_invoke (worker->context, deliver_synthetic_reply, data);
++ }
++
++ return TRUE;
++ }
++#endif /* DBUS_DAEMON_EMULATION */
++
++ return _g_kdbus_send (worker, message, NULL, timeout_msec, NULL, error);
++}
++
++gboolean
++_g_kdbus_worker_send_message_sync (GKDBusWorker *worker,
++ GDBusMessage *message,
++ GDBusMessage **out_reply,
++ gint timeout_msec,
++ GCancellable *cancellable,
++ GError **error)
++{
++#ifdef DBUS_DAEMON_EMULATION
++ if (_is_message_to_dbus_daemon (message))
++ {
++ *out_reply = _dbus_daemon_synthetic_reply (worker, message, TRUE);
++ return TRUE;
++ }
++#endif /* DBUS_DAEMON_EMULATION */
++
++ return _g_kdbus_send (worker, message, out_reply, timeout_msec, cancellable, error);
++}
++
++gboolean
++_g_kdbus_worker_flush_sync (GKDBusWorker *worker)
++{
++ return TRUE;
++}
++
++void
++_g_kdbus_worker_stop (GKDBusWorker *worker)
++{
++ g_source_destroy (worker->source);
++ g_source_unref (worker->source);
++ worker->source = 0;
++
++ g_object_unref (worker);
++}
++
++void
++_g_kdbus_worker_close (GKDBusWorker *worker,
++ GTask *task)
++{
++ worker->disconnected_callback (FALSE, NULL, worker->user_data);
++ g_task_return_boolean (task, TRUE);
++}
+diff --git a/gio/gkdbus.h b/gio/gkdbus.h
+new file mode 100644
+index 0000000..acbc0d5
+--- /dev/null
++++ b/gio/gkdbus.h
+@@ -0,0 +1,203 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski <l.skalski@samsung.com>
++ * Author: Michal Eljasiewicz <m.eljasiewic@samsung.com>
++ */
++
++#ifndef __G_KDBUS_H__
++#define __G_KDBUS_H__
++
++#if !defined (GIO_COMPILATION)
++#error "gkdbus.h is a private header file."
++#endif
++
++#include <gio/giotypes.h>
++#include "gdbusprivate.h"
++
++#define G_TYPE_KDBUS_WORKER (g_kdbus_worker_get_type ())
++#define G_KDBUS_WORKER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
++ G_TYPE_KDBUS_WORKER, GKDBusWorker))
++#define G_KDBUS_WORKER_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
++ G_TYPE_KDBUS_WORKER, GKDBusWorkerClass))
++#define G_IS_KDBUS_WORKER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \
++ G_TYPE_KDBUS_WORKER))
++#define G_IS_KDBUS_WORKER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \
++ G_TYPE_KDBUS_WORKER))
++#define G_KDBUS_WORKER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
++ G_TYPE_KDBUS_WORKER, GKDBusWorkerClass))
++typedef enum {
++ G_DBUS_CREDS_NONE = 0,
++ G_DBUS_CREDS_PID = (1<<0),
++ G_DBUS_CREDS_UID = (1<<1),
++ G_DBUS_CREDS_UNIQUE_NAME = (1<<2),
++ G_DBUS_CREDS_SEC_LABEL = (1<<3)
++} GDBusCredentialsFlags;
++
++typedef struct
++{
++ guint pid;
++ guint uid;
++ gchar *unique_name;
++ gchar *sec_label;
++} GDBusCredentials;
++
++typedef struct
++{
++ GDBusMessage *message;
++ uid_t sender_euid;
++ gid_t sender_egid;
++ gchar *sender_seclabel;
++ gchar *sender_names;
++} GKDBusMessage;
++
++typedef struct _GKDBusWorker GKDBusWorker;
++
++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);
++
++GType g_kdbus_worker_get_type (void);
++
++GKDBusWorker * _g_kdbus_worker_new (const gchar *address,
++ GError **error);
++
++void _g_kdbus_worker_unfreeze (GKDBusWorker *worker);
++
++gboolean _g_kdbus_worker_send_message (GKDBusWorker *worker,
++ GDBusMessage *message,
++ gint timeout_msec,
++ GError **error);
++
++gboolean _g_kdbus_worker_send_message_sync (GKDBusWorker *worker,
++ GDBusMessage *message,
++ GDBusMessage **out_reply,
++ gint timeout_msec,
++ GCancellable *cancellable,
++ GError **error);
++
++void _g_kdbus_worker_stop (GKDBusWorker *worker);
++
++gboolean _g_kdbus_worker_flush_sync (GKDBusWorker *worker);
++
++void _g_kdbus_worker_close (GKDBusWorker *worker,
++ GTask *task);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++gboolean _g_kdbus_open (GKDBusWorker *worker,
++ const gchar *address,
++ GError **error);
++
++gboolean _g_kdbus_can_connect (GKDBusWorker *worker,
++ GError **error);
++
++gboolean _g_kdbus_close (GKDBusWorker *worker);
++
++gboolean _g_kdbus_is_closed (GKDBusWorker *worker);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++const gchar * _g_kdbus_Hello (GKDBusWorker *worker,
++ GError **error);
++
++gchar * _g_kdbus_GetBusId (GKDBusWorker *worker,
++ GError **error);
++
++GBusRequestNameReplyFlags _g_kdbus_RequestName (GKDBusWorker *worker,
++ const gchar *name,
++ GBusNameOwnerFlags flags,
++ GError **error);
++
++GBusReleaseNameReplyFlags _g_kdbus_ReleaseName (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++gchar ** _g_kdbus_GetListNames (GKDBusWorker *worker,
++ gboolean activatable,
++ GError **error);
++
++gchar ** _g_kdbus_GetListQueuedOwners (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++gboolean _g_kdbus_NameHasOwner (GKDBusWorker *connection,
++ const gchar *name,
++ GError **error);
++
++gchar * _g_kdbus_GetNameOwner (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++pid_t _g_kdbus_GetConnectionUnixProcessID (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++uid_t _g_kdbus_GetConnectionUnixUser (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++gchar * _g_kdbus_GetConnectionSecurityLabel (GKDBusWorker *worker,
++ const gchar *name,
++ GError **error);
++
++GBusStartServiceReplyFlags _g_kdbus_StartServiceByName (GKDBusWorker *worker,
++ const gchar *name,
++ guint32 flags,
++ GDBusMessage *message,
++ GError **error);
++
++gboolean _g_kdbus_AddMatch (GKDBusWorker *worker,
++ const gchar *match_rule,
++ GError **error);
++
++gboolean _g_kdbus_RemoveMatch (GKDBusWorker *worker,
++ const gchar *match_rule,
++ GError **error);
++
++GDBusCredentials * _g_kdbus_GetConnInfo (GKDBusWorker *worker,
++ const gchar *name,
++ guint flags,
++ GError **error);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++gboolean _g_kdbus_subscribe_name_acquired (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error);
++
++gboolean _g_kdbus_subscribe_name_lost (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error);
++
++gboolean _g_kdbus_subscribe_name_owner_changed (GKDBusWorker *worker,
++ const gchar *match_rule,
++ const gchar *name,
++ GError **error);
++
++/* ---------------------------------------------------------------------------------------------------- */
++
++G_END_DECLS
++
++#endif /* __G_KDBUS_H__ */
+diff --git a/gio/gkdbusfakedaemon.c b/gio/gkdbusfakedaemon.c
+new file mode 100644
+index 0000000..04272f3
+--- /dev/null
++++ b/gio/gkdbusfakedaemon.c
+@@ -0,0 +1,694 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski <l.skalski@samsung.com>
++ */
++
++#include "config.h"
++#include "gkdbus.h"
++#include "gkdbusfakedaemon.h"
++
++#include <gio/gio.h>
++#include <string.h>
++
++static gchar *introspect =
++ "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
++ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
++ "<node>\n"
++ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
++ " <method name=\"Introspect\">\n"
++ " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
++ " </method>\n"
++ " </interface>\n"
++ " <interface name=\"org.freedesktop.DBus\">\n"
++ " <method name=\"AddMatch\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " </method>\n"
++ " <method name=\"RemoveMatch\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " </method>\n"
++ " <method name=\"GetConnectionCredentials\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"a{sv}\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"GetConnectionSELinuxSecurityContext\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"ay\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"GetConnectionUnixProcessID\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"GetConnectionUnixUser\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"GetId\">\n"
++ " <arg type=\"s\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"GetNameOwner\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"s\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"Hello\">\n"
++ " <arg type=\"s\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"ListActivatableNames\">\n"
++ " <arg type=\"as\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"ListNames\">\n"
++ " <arg type=\"as\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"ListQueuedOwners\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"as\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"NameHasOwner\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"b\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"ReleaseName\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"ReloadConfig\">\n"
++ " </method>\n"
++ " <method name=\"RequestName\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"StartServiceByName\">\n"
++ " <arg type=\"s\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"in\"/>\n"
++ " <arg type=\"u\" direction=\"out\"/>\n"
++ " </method>\n"
++ " <method name=\"UpdateActivationEnvironment\">\n"
++ " <arg type=\"a{ss}\" direction=\"in\"/>\n"
++ " </method>\n"
++ " <signal name=\"NameAcquired\">\n"
++ " <arg type=\"s\"/>\n"
++ " </signal>\n"
++ " <signal name=\"NameLost\">\n"
++ " <arg type=\"s\"/>\n"
++ " </signal>\n"
++ " <signal name=\"NameOwnerChanged\">\n"
++ " <arg type=\"s\"/>\n"
++ " <arg type=\"s\"/>\n"
++ " <arg type=\"s\"/>\n"
++ " </signal>\n"
++ " </interface>\n"
++ "</node>\n";
++
++static gboolean
++_mac_smack_use (void)
++{
++ static int cached_use = -1;
++
++ if (cached_use < 0)
++ cached_use = access("/sys/fs/smackfs/", F_OK) >= 0;
++
++ return cached_use;
++}
++
++/**
++ * _is_message_to_dbus_daemon()
++ */
++gboolean
++_is_message_to_dbus_daemon (GDBusMessage *message)
++{
++ return g_strcmp0 (g_dbus_message_get_destination (message), "org.freedesktop.DBus") == 0 &&
++ (g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus") == 0 ||
++ g_strcmp0 (g_dbus_message_get_interface (message), "org.freedesktop.DBus.Introspectable") == 0) &&
++ (g_strcmp0 (g_dbus_message_get_path (message), "/org/freedesktop/DBus") == 0 ||
++ g_strcmp0 (g_dbus_message_get_path (message), "/") == 0);
++}
++
++
++/**
++ * _dbus_daemon_synthetic_reply()
++ */
++GDBusMessage *
++_dbus_daemon_synthetic_reply (GKDBusWorker *worker,
++ GDBusMessage *message,
++ gboolean sync)
++{
++ GDBusMessage *reply;
++ GVariant *reply_body;
++ GVariant *body;
++ GError *local_error;
++ const gchar *member;
++
++ reply = NULL;
++ reply_body = NULL;
++ local_error = NULL;
++
++ member = g_dbus_message_get_member (message);
++ body = g_dbus_message_get_body (message);
++
++ /* show debug */
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " >>>> SENT D-Bus/kdbus message\n");
++ s = g_dbus_message_print (message, 2);
++ g_print ("%s", s);
++ g_free (s);
++ _g_dbus_debug_print_unlock ();
++ }
++
++ /*
++ * Introspect
++ */
++ if (!g_strcmp0 (member, "Introspect"))
++ {
++ reply_body = g_variant_new ("(s)", introspect);
++ }
++
++ /*
++ * AddMatch
++ */
++ else if (!g_strcmp0 (member, "AddMatch"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *rule;
++
++ g_variant_get (body, "(&s)", &rule);
++
++ _g_kdbus_AddMatch (worker, rule, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("()", NULL);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'AddMatch' has wrong args (expected s)");
++ }
++
++ /*
++ * RemoveMatch
++ */
++ else if (!g_strcmp0 (member, "RemoveMatch"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *rule;
++
++ g_variant_get (body, "(&s)", &rule);
++
++ _g_kdbus_RemoveMatch (worker, rule, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("()", NULL);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'RemoveMatch' has wrong args (expected s)");
++ }
++
++ /*
++ * GetConnectionCredentials
++ */
++ else if (!g_strcmp0 (member, "GetConnectionCredentials"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ GDBusCredentials *creds;
++ gchar *name;
++ guint flags;
++
++ creds = NULL;
++ flags = G_DBUS_CREDS_PID | G_DBUS_CREDS_UID | G_DBUS_CREDS_SEC_LABEL;
++
++ g_variant_get (body, "(&s)", &name);
++
++ creds = _g_kdbus_GetConnInfo (worker,
++ name,
++ flags,
++ &local_error);
++ if (local_error == NULL)
++ {
++ GVariantBuilder builder;
++
++ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{sv})"));
++ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
++
++ g_variant_builder_add (&builder, "{sv}", "UnixUserID", g_variant_new_uint32 (creds->uid));
++ g_variant_builder_add (&builder, "{sv}", "ProcessID", g_variant_new_uint32 (creds->pid));
++
++ if (creds->sec_label != NULL)
++ {
++ GVariantBuilder *label_builder;
++ gint counter;
++ gint label_size;
++
++ label_size = strlen (creds->sec_label);
++ label_builder = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
++ for (counter = 0 ; counter < label_size + 1; counter++) /* label_size + 1 : include the \0 in the payload, for zero-copy reading. From dbus/bus/driver.c */
++ g_variant_builder_add (label_builder, "y", creds->sec_label[counter]);
++
++ g_variant_builder_add (&builder, "{sv}", "LinuxSecurityLabel", g_variant_new ("ay", label_builder));
++
++ g_variant_builder_unref (label_builder);
++ g_free (creds->sec_label);
++ }
++
++ g_variant_builder_close (&builder);
++ reply_body = g_variant_builder_end (&builder);
++ g_free (creds);
++ }
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetConnectionCredentials' has wrong args (expected s)");
++ }
++
++ /*
++ * GetConnectionSELinuxSecurityContext
++ */
++ else if (!g_strcmp0 (member, "GetConnectionSELinuxSecurityContext"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *name;
++ gchar *label;
++
++ g_variant_get (body, "(&s)", &name);
++
++ label = _g_kdbus_GetConnectionSecurityLabel (worker, name, &local_error);
++ if (label == NULL && local_error == NULL)
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Operation not supported");
++ else if (local_error == NULL)
++ {
++ /* 'label' (KDBUS_ITEM_SECLABEL item) contains valid LSM security label... */
++ if (_mac_smack_use())
++ {
++ /* but if we are using SMACK - to keep compatibility with legacy dbus1 - return error */
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN,
++ "Could not determine security context for '%s'", name);
++ }
++ else
++ {
++ /* if it is not SMACK - let's assume that it's SELinux label */
++ GVariantBuilder builder;
++ gint counter;
++
++ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ay)"));
++ g_variant_builder_open (&builder, G_VARIANT_TYPE ("ay"));
++
++ for (counter = 0 ; counter < strlen (label) ; counter++)
++ g_variant_builder_add (&builder, "y", label[counter]);
++
++ g_variant_builder_close (&builder);
++ reply_body = g_variant_builder_end (&builder);
++ }
++ g_free (label);
++ }
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetConnectionSELinuxSecurityContext' has wrong args (expected s)");
++ }
++
++ /*
++ * GetConnectionUnixProcessID
++ */
++ else if (!g_strcmp0 (member, "GetConnectionUnixProcessID"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *name;
++ pid_t pid;
++
++ g_variant_get (body, "(&s)", &name);
++ pid = _g_kdbus_GetConnectionUnixProcessID (worker, name, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(u)", pid);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetConnectionUnixProcessID' has wrong args (expected s)");
++ }
++
++ /*
++ * GetConnectionUnixUser
++ */
++ else if (!g_strcmp0 (member, "GetConnectionUnixUser"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *name;
++ uid_t uid;
++
++ g_variant_get (body, "(&s)", &name);
++ uid = _g_kdbus_GetConnectionUnixUser (worker, name, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(u)", uid);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetConnectionUnixUser' has wrong args (expected s)");
++ }
++
++ /*
++ * GetId
++ */
++ else if (!g_strcmp0 (member, "GetId"))
++ {
++ if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ {
++ gchar *bus_id;
++
++ bus_id = _g_kdbus_GetBusId (worker, NULL);
++ reply_body = g_variant_new ("(s)", bus_id);
++
++ g_free (bus_id);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetId' has wrong args");
++ }
++
++ /*
++ * GetNameOwner
++ */
++ else if (!g_strcmp0 (member, "GetNameOwner"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar *unique_name;
++ gchar *name;
++
++ g_variant_get (body, "(&s)", &name);
++
++ unique_name = _g_kdbus_GetNameOwner (worker, name, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(s)", unique_name);
++ g_free (unique_name);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'GetNameOwner' has wrong args (expected s)");
++ }
++
++ /*
++ * Hello
++ */
++ else if (!g_strcmp0 (member, "Hello"))
++ {
++ if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ {
++ const gchar *unique_name;
++
++ unique_name = _g_kdbus_Hello (worker, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(s)", unique_name);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'Hello' has wrong args");
++ }
++
++ /*
++ * ListActivatableNames
++ */
++ else if (!g_strcmp0 (member, "ListActivatableNames"))
++ {
++ if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ {
++ gchar **strv;
++ gint cnt;
++
++ cnt = 0;
++
++ strv = _g_kdbus_GetListNames (worker, TRUE, &local_error);
++ if (local_error == NULL)
++ {
++ GVariantBuilder *builder;
++
++ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++ while (strv[cnt])
++ g_variant_builder_add (builder, "s", strv[cnt++]);
++
++ reply_body = g_variant_new ("(as)", builder);
++
++ g_variant_builder_unref (builder);
++ }
++ g_strfreev (strv);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'ListActivatableNames' has wrong args");
++ }
++
++ /*
++ * ListNames
++ */
++ else if (!g_strcmp0 (member, "ListNames"))
++ {
++ if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ {
++ gchar **strv;
++ gint cnt;
++
++ cnt = 0;
++
++ strv = _g_kdbus_GetListNames (worker, FALSE, &local_error);
++ if (local_error == NULL)
++ {
++ GVariantBuilder *builder;
++
++ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++ while (strv[cnt])
++ g_variant_builder_add (builder, "s", strv[cnt++]);
++
++ reply_body = g_variant_new ("(as)", builder);
++
++ g_variant_builder_unref (builder);
++ }
++ g_strfreev (strv);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'ListNames' has wrong args");
++ }
++
++ /*
++ * ListQueuedOwners
++ */
++ else if (!g_strcmp0 (member, "ListQueuedOwners"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gchar **strv;
++ gchar *name;
++ gint cnt;
++
++ cnt = 0;
++
++ g_variant_get (body, "(&s)", &name);
++ strv = _g_kdbus_GetListQueuedOwners (worker, name, &local_error);
++ if (local_error == NULL)
++ {
++ GVariantBuilder *builder;
++
++ builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
++
++ while (strv[cnt])
++ g_variant_builder_add (builder, "s", strv[cnt++]);
++
++ reply_body = g_variant_new ("(as)", builder);
++
++ g_variant_builder_unref (builder);
++ }
++ g_strfreev (strv);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'ListQueuedOwners' has wrong args (expected s)");
++ }
++
++ /*
++ * NameHasOwner
++ */
++ else if (!g_strcmp0 (member, "NameHasOwner"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ gboolean ret;
++ gchar *name;
++
++ g_variant_get (body, "(&s)", &name);
++
++ ret = _g_kdbus_NameHasOwner (worker, name, &local_error);
++ if (local_error == NULL)
++ {
++ if (ret)
++ reply_body = g_variant_new ("(b)", TRUE);
++ else
++ reply_body = g_variant_new ("(b)", FALSE);
++ }
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'NameHasOwner' has wrong args (expected s)");
++ }
++
++ /*
++ * ReleaseName
++ */
++ else if (!g_strcmp0 (member, "ReleaseName"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(s)")))
++ {
++ GBusReleaseNameReplyFlags status;
++ gchar *name;
++
++ g_variant_get (body, "(&s)", &name);
++
++ status = _g_kdbus_ReleaseName (worker, name, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(u)", status);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'ReleaseName' has wrong args (expected s)");
++ }
++
++ /*
++ * ReloadConfig
++ */
++ else if (!g_strcmp0 (member, "ReloadConfig"))
++ {
++ if ((body == NULL) || g_variant_is_of_type (body, G_VARIANT_TYPE ("()")))
++ reply_body = g_variant_new ("()", NULL);
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'ReloadConfig' has wrong args");
++ }
++
++ /*
++ * RequestName
++ */
++ else if (!g_strcmp0 (member, "RequestName"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(su)")))
++ {
++ GBusRequestNameReplyFlags status;
++ guint32 flags;
++ gchar *name;
++
++ g_variant_get (body, "(&su)", &name, &flags);
++
++ status = _g_kdbus_RequestName (worker, name, flags, &local_error);
++ if (local_error == NULL)
++ reply_body = g_variant_new ("(u)", status);
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'RequestName' has wrong args (expected su)");
++ }
++
++ /*
++ * StartServiceByName
++ */
++ else if (!g_strcmp0 (member, "StartServiceByName"))
++ {
++ if (body != NULL && g_variant_is_of_type (body, G_VARIANT_TYPE ("(su)")))
++ {
++ GBusStartServiceReplyFlags status;
++ gchar *name;
++ guint32 flags;
++
++ g_variant_get (body, "(&su)", &name, &flags);
++
++ if (sync)
++ status = _g_kdbus_StartServiceByName (worker, name, flags, NULL, &local_error);
++ else
++ status = _g_kdbus_StartServiceByName (worker, name, flags, message, &local_error);
++
++ if (local_error == NULL)
++ {
++ if (status == G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING || sync)
++ reply_body = g_variant_new ("(u)", status);
++ else
++ return NULL;
++ }
++ }
++ else
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
++ "Call to 'StartServiceByName' has wrong args (expected su)");
++ }
++
++ /*
++ * UpdateActivationEnvironment
++ */
++ else if (!g_strcmp0 (member, "UpdateActivationEnvironment"))
++ {
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED,
++ "'%s' method not supported", member);
++ }
++
++ /*
++ * Method not supported
++ */
++ else
++ {
++ g_set_error (&local_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD,
++ "org.freedesktop.DBus does not understand message %s", member);
++ }
++
++ if (reply_body == NULL && local_error)
++ {
++ gchar *dbus_error_name;
++
++ dbus_error_name = g_dbus_error_encode_gerror (local_error);
++ reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", local_error->message);
++ g_free (dbus_error_name);
++ }
++ else
++ {
++ reply = g_dbus_message_new_method_reply (message);
++ g_dbus_message_set_body (reply, reply_body);
++ }
++
++ g_dbus_message_set_serial (reply, -1);
++
++ if (local_error)
++ g_error_free (local_error);
++
++ if (G_UNLIKELY (_g_dbus_debug_message ()))
++ {
++ gchar *s;
++ _g_dbus_debug_print_lock ();
++ g_print ("========================================================================\n"
++ "GDBus-debug:Message:\n"
++ " <<<< RECEIVED synthetic D-Bus message\n");
++ s = g_dbus_message_print (reply, 2);
++ g_print ("%s", s);
++ g_free (s);
++ _g_dbus_debug_print_unlock ();
++ }
++
++ return reply;
++}
+diff --git a/gio/gkdbusfakedaemon.h b/gio/gkdbusfakedaemon.h
+new file mode 100644
+index 0000000..969b7b5
+--- /dev/null
++++ b/gio/gkdbusfakedaemon.h
+@@ -0,0 +1,39 @@
++/* GIO - GLib Input, Output and Streaming Library
++ *
++ * Copyright (C) 2015 Samsung Electronics
++ *
++ * This library is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2 of the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General
++ * Public License along with this library; if not, write to the
++ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
++ * Boston, MA 02111-1307, USA.
++ *
++ * Author: Lukasz Skalski <l.skalski@samsung.com>
++ */
++
++#ifndef __G_KDBUSFAKEDAEMON_H__
++#define __G_KDBUSFAKEDAEMON_H__
++
++#if !defined (GIO_COMPILATION)
++#error "gkdbusfakedaemon.h is a private header file."
++#endif
++
++#include <gio/giotypes.h>
++
++gboolean _is_message_to_dbus_daemon (GDBusMessage *message);
++
++GDBusMessage * _dbus_daemon_synthetic_reply (GKDBusWorker *worker,
++ GDBusMessage *message,
++ gboolean sync);
++G_END_DECLS
++
++#endif /* __G_KDBUSFAKEDAEMON_H__ */
+diff --git a/gio/gunixfdlist.h b/gio/gunixfdlist.h
+index df5587e..d5a9e8e 100644
+--- a/gio/gunixfdlist.h
++++ b/gio/gunixfdlist.h
+@@ -71,6 +71,9 @@ GIO_AVAILABLE_IN_ALL
+ GUnixFDList * g_unix_fd_list_new_from_array (const gint *fds,
+ gint n_fds);
+
++GIO_AVAILABLE_IN_2_44
++void g_unix_fd_list_lock (GUnixFDList *list);
++
+ GIO_AVAILABLE_IN_ALL
+ gint g_unix_fd_list_append (GUnixFDList *list,
+ gint fd,
+diff --git a/gio/meson.build b/gio/meson.build
+index a4e9b30..f230c05 100644
+--- a/gio/meson.build
++++ b/gio/meson.build
+@@ -231,6 +231,13 @@ gdbus_sources = files(
+ 'gtestdbus.c',
+ )
+
++if get_option('kdbus')
++gdbus_sources += files(
++ 'gkdbus.c',
++ 'gkdbusfakedaemon.c',
++)
++endif
++
+ # Generate gdbus-codegen
+ subdir('gdbus-2.0/codegen')
+
+@@ -837,6 +844,16 @@ else
+ gio_dtrace_hdr = []
+ endif
+
++if get_option('kdbus')
++ gio_c_args += '-DKDBUS'
++endif
++
++libdbuspolicy_dep = dependency('', required : false)
++if get_option('libdbuspolicy')
++ libdbuspolicy_dep = dependency('libdbuspolicy1', version : '>=1', required : true)
++ gio_c_args += '-DLIBDBUSPOLICY'
++endif
++
+ libgio = library('gio-2.0',
+ gioenumtypes_h, gioenumtypes_c, gnetworking_h, gio_sources,
+ gio_dtrace_hdr, gio_dtrace_obj,
+@@ -849,7 +866,7 @@ libgio = library('gio-2.0',
+ link_with: internal_deps,
+ dependencies : [libz_dep, libdl_dep, libmount_dep, libglib_dep,
+ libgobject_dep, libgmodule_dep, selinux_dep, xattr_dep,
+- platform_deps, network_libs, libsysprof_capture_dep,
++ platform_deps, network_libs, libdbuspolicy_dep, libsysprof_capture_dep,
+ gioenumtypes_dep, gvdb_dep],
+ c_args : [gio_c_args, gio_c_args_internal],
+ objc_args : [gio_c_args, gio_c_args_internal],
+diff --git a/meson_options.txt b/meson_options.txt
+index 517d575..7c95d03 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -93,6 +93,16 @@ option('nls',
+ yield: true,
+ description : 'Enable native language support (translations)')
+
++option('kdbus',
++ type : 'boolean',
++ value : false,
++ description : 'enable kdbus transport')
++
++option('libdbuspolicy',
++ type : 'boolean',
++ value : false,
++ description : 'enable libdbuspolicy for kdbus transport')
++
+ option('oss_fuzz',
+ type : 'feature',
+ value : 'disabled',
+--
+2.25.1
+