summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
+