diff options
-rw-r--r-- | packaging/glib2.spec | 65 | ||||
-rw-r--r-- | packaging/kdbus1.patch | 4424 | ||||
-rw-r--r-- | packaging/kdbus2.patch | 8287 |
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 + |