+ Overview of changes in GLib 2.52.2
+ ==================================
+
+ * Bug fixes:
+ 734946 Implement GContentType on OSX
+ 761102 Increase performance for main loop
+ 780300 gio/gosxappinfo.c uses deprecated LSFindApplicationForInfo
+ 780309 gio/tests/appinfo build fails: gdesktopappinfo.c skipped on OS X
+ 781298 gfileutils.c:330:3: error: ISO C90 forbids mixed declarations and code
+
+ * Translation updates:
+ Indonesian
+
+
+ Overview of changes in GLib 2.52.1
+ ==================================
+
+ * Bug fixes:
+ 674885 type initialisation deadlock in GObject
+ 698064 Add g_ptr_array_contains()
+ 725894 build: Include gettext libraries for static compilation on Mac OS X
+ 734946 Implement GContentType on OSX
+ 755046 gfileutils: Add precondition checks to g_file_test()
+ 775879 g_log_default_handler should not check G_MESSAGES_DEBUG
+ 777961 Documentation for g_app_info_equals() could be clearer
+ 778049 race in gsource detected by TSan
+ 778207 gio-querymodules: fix memory leak
+ 778287 G_MODULE_EXPORT and -fvisibility=hidden
+ 779409 Fix false positive g_warning() in remove_filter()
+ 780066 g_base64_encode_close() in glib/gbase64.c produces invalid base64 encoding
+ 780095 g_utf8_get_char_validated() stopping at nul byte even for length specified buffers
+ 780306 Unused function in gunicollate.c for CARBON
+ 780310 g_tls_database_verify_chain doesn't set the GError for failures other than cancellation
+ 780384 gio/tests/contenttype fails on OS X: "public.directory" != "public.folder"
+ 780441 Make the portal implementation of g_app_info_launch() synchronous
+ 780471 appinfo: Only use portal as fallback
+ 780924 Memory leak in gdbusmethodinvocation.c
+
+
+ * Translation updates:
+ Friulian
+ Hebrew
+ Indonesian
+ Polish
+ Russian
+
+
+ Overview of changes in GLib 2.52.0
+ ==================================
+
+ * Bug fixes:
+ 779799 gdatetime test fails with tzdata 2017a
+ 780032 Add missing attributes to two functions
+ 780144 gio/fam: Remove leftover debug print
+
+ * Translation updates:
+ French
+ Friulian
+ Latvian
+
+
+ Overview of changes in GLib 2.51.5
+ ==================================
+
+ * OS X implementations of GContentType and GAppInfo
+ have been added
+
+ * Bugs fixed:
+ 673047 gunicollate is broken on OS X (patch included!)
+ 734946 Implement GContentType on OSX
+ 747146 Implement GNotification on OSX
+ 769983 glib-mkenums generates non-reproducible Makefile snippets
+ 777203 gnulib license information is not correct in glib2.0
+ 778515 Crash in the gio kqueue backend
+ 779456 Make g_utf8_make_valid optionally take a length
+
+ * Translation updates:
+ Danish
+ Friulian
+ German
+ Hungarian
+ Korean
+ Lithuanian
+
+
+ Overview of changes in GLib 2.51.4
+ ==================================
+
+ * Memory leak fixes
+ * Fix the released tarball
+
+
+ Overview of changes in GLib 2.51.3
+ ==================================
+
+ * Bugs fixed:
+ 771997 gchecksum: Add SHA-384 support
+ 778422 gsubprocesslauncher: Clarify the behavior of set_environ()
+ 778581 gdbus-codegen: Fix -Wconversion warning
+ 778801 gdbus-codegen: Add --outdir flag
+ 778991 Plug a mem leak in gdbusauth
+ 779183 g_io_extension_point_get_extensions should check for NULL pointer
+
+ * Translation updates:
+ Basque
+ Chinese (Taiwan)
+ Danish
+ Indonesian
+ Italian
+ Serbian
+
+
+ Overview of changes in GLib 2.51.2
+ ==================================
+
+ * Minimal support for UUIDs has been added
+
+ * A new file attribute, G_FILE_ATTRIBUTE_RECENT_MODIFIED has been added
+ to improve sorting of recent files
+
+ * Bugs fixed:
+ 639078 UUID support feature request
+ 777135 gkeyfile: Be more specific about error codes in documentation
+ 777307 race condition between gdbus signal callback and g_bus_unwatch_name...
+ 777481 goutputstream: docs: fix typos
+ 777493 g_mkdtemp() not introspectable
+ 777507 Recent view sorting incorrectly
+ 777592 Add minor examples to GDBus and GVariant documentation
+ 778002 race in gdbusprivate.c detected by the ThreadSanitizer
+ 778096 race in gdbusconnection reported by TSan
+
+ * Translation updates:
+ Norwegian bokmål
+ Polish
+ Simplified Chinese
+ Slovak
+ Spanish
+ Swedish
+
+
+ Overview of changes in GLib 2.51.1
+ ==================================
+
+ * glib-compile-resources grew a --generate-phony-targets flag
+
+ * GLib now installs a valgrind suppressions file for GLib and GIO
+
+ * Bugs fixed:
+ 666114 should have infrastructure to run its tests under valgrind
+ 729730 GDBusMessage: Fix segfault if DEBUG_SERIALIZER is enabled
+ 730932 statically assert that reasonable assumptions about enums are true
+ 735731 gobject: Document behaviour of GType checking macros on NULL
+ 736810 gdbus: Fix leak in g_dbus_message_print()
+ 762283 GSocket – Fix race conditions on Win32 if multiple threads are waiting on cond...
+ 767609 Test suite problems
+ 767952 g_dbus_method_invocation_return_*, g_dbus_method_invocation_take_error: They d...
+ 769672 Assert threads for testcase 642026 are sucessfully created
+ 769745 gtask: Add guards for public functions
+ 770175 Add command line argument to mkenums and genmarshal to write output to a file
+ 770646 glib: Namespace global tapset variables by soname
+ 772160 Add g_unix_mount_for() support
+ 772989 Totem allows invalid urls that might cause segfault that's irrecoverable
+ 773823 gio: Bump copy buffer size to 256k by default
+ 774086 fix g_main_context_check declaration
+ 774368 Dependency file output of resource scanner breaks Ninja
+ 774421 Two minor patches
+ 774520 GSocket allocates and processes control messages even if not requested
+ 775309 Crash in gdbusauth
+ 775468 Improve log write supports color method on windows
+ 775510 testing with -fsanitize=undefined reports various undefined behaviour
+ 775517 Password input is echoed in the terminal
+ 775621 gmessages: Fix compilation on Android
+ 775765 FDO notification withdrawal backend sends wrong ID to the server
+ 775913 subprocesslauncher: potential infinite loop in verify_disposition()
+ 776198 Stray semicolon after g_variant_print() function in gvariant.c
+ 776586 License headers cleanup
+ 777077 Use of memory after it is freed
+
+ * Translation updates:
+ Brazilian Portuguese
+ Czech
+ Galician
+ German
+ Hebrew
+ Kazakh
+ Lithuanian
+ Spanish
+ Swedish
+
+
+ Overview of changes in GLib 2.51.0
+ ==================================
+
+ * glib-genmarshal and glib-mkenums have gained --output options
+ for better build system integration
+
+ * New API: g_utf8_make_valid
+
+ * Bugs fixed:
+ 591603 Make _g_utf8_make_valid public
+ 610969 Nice to have g_utf8_make_valid as public
+ 767882 Bit shift overflow (-Wshift-overflow) warning in gparam.h
+ 769135 External control for g_test_add/g_test_run
+ 769630 gfile: G_FILE_MONITOR_WATCH_MOVES was actually introduced in 2.46
+ 772160 Add g_unix_mount_for() support
+ 772221 Take advantage of Unicode
+ 773303 GApplication leaks option_strings
+
+ * Translation updates:
+ French
+ Galician
+ German
+ Hungarian
+ Lithuanian
+ Norwegian bokmål
+ Occitan
+ Polish
+ Slovak
+ Turkish
+
+
+ Overview of changes in GLib 2.50.1
+ ==================================
+
+ * Update Unicode support to Unicode 9.0.0
+
+ * Bugs fixed:
+ 662946 gunixmounts monitoring doesn't work correctly with libmount
+ 771591 Update to Unicode 9.0.0
+ 772054 glib/gspawn-win32-helper.c: unexpected behavior re CommandLineToArgvW()
+ 772255 gresolver: Mark GResolver as an abstract class
+ 772269 Add --version options to glib-compile-resources and glib-compile-schemas
+ 772297 completion: Complete gsettings describe
+ 772511 g_log_default_handler crashes windows apps with "Unspecified fatal err...
+
+ * Translation updates:
+ Brazilian Portuguese
+ Catalan
+ Croatian
+ Czech
+ Danish
+ Hungarian
+ Italian
+ Latvian
+ Polish
+ Swedish
+
+
+ Overview of changes in GLib 2.50.0
+ ==================================
+
+ * Bugs fixed:
+ 771438 Turn on libmount by default on linux
+ Fix the annotation for g_log_variant
+
+ * Translation updates:
+ British English
+ French
+
+
+ Overview of changes in GLib 2.49.7
+ ==================================
+
+ * Add g_log_variant, binding-friendly api for structured logging
+
+ Bugs fixed:
+ 646926 arg_data invalid after g_option_context_parse() fails
+
+ * Translation updates:
+ Danish
+ Finnish
+ Galician
+ German
+ Hebrew
+ Kazakh
+ Korean
+ Latvian
+ Lithuanian
+ Polish
+ Portuguese
+ Serbian
+ Slovak
+ Spanish
+ Swedish
+ Thai
+
+
+ Overview of changes in GLib 2.49.6
+ ==================================
+
+ * The gsettings commandline tool now has a describe command
+
+ Bugs fixed:
+ 745754 Add gcc-style dependency output to glib-compile-resources
+ 769076 Fix warning: attempt to override closure->va_marshal with new marshal
+ 770372 gdbus-codegen: Strip @since parameters before comparison
+
+ Translation updates:
+ Brazilian Portuguese
+ Czech
+ German
+ Hungarian
+ Polish
+ Portuguese
+ Spanish
+
+
+ Overview of changes in GLib 2.49.5
+ ==================================
+
+ * Structured logging:
+ - drop libsystemd dependency
+ - document that g_test_expect_message does not work with structured logs
+
+ * Use libmount for unix mount support
+
+ * Add an async variant of g_app_info_launch_default_for_uri
+
+ Bugs fixed:
+ 522053 GUnixMountMonitor needs to use /proc/self/mountinfo on recent Linux
+ 682794 Add usage guidance to logging documentation
+ 744456 Structured logging API
+ 766370 Add a macro for initializing g_auto(GVariantBuilder)
+ 767240 Regex failures with pcre 8.38
+ 768198 Can't build glib with systemtap enabled
+ 768453 Gdbus test: compilation fails due to -Werror=format-y2k errors
+ 768752 Add async variant of g_app_info_launch_default_for_uri
+ 769027 Docs misleadingly imply G_CHECKSUM_SHA512 is available since 2.16
+ 769029 gmessage: compiler complains about -Wformat-nonliteral
+ 769042 'O_CLOEXEC' undeclared (first use in this function)
+ 769087 gmessages: support NULL log domain
+ 769089 Fix gsettings uint64 testcase
+ 769104 Build failure when using _GLIB_CHECKED_ADD_U32 with the Intel compiler
+ 769139 g_log_writer_journald uses non-standard 'htole64' function
+ 769238 memory increases every time I umount and mount my secondary hard disk.
+ 769245 is_valid_heap_iter define misses NULL pointer check
+ 769507 gmessages: Don’t require is_journald() call before writer_journald()
+ 769785 gmessages: Expand documentation further for structured logging
+ 769995 gdbus-codegen: Allow '@since: UNRELEASED' in documentation comments
+
+ Translation updates:
+ Catalan
+ Hebrew
+ Lithuanian
+ Slovak
+ Spanish
+
+
+ Overview of changes in GLib 2.49.4
+ ==================================
+
+ * Change the just-introduced structured logging API. The arguments
+ of g_log_structured() had to be reordered to enable an implementation
+ within the limits of what the standards guarantee about var args.
+
+ Bugs fixed:
+ 744456 Structured logging API
+ 768936 gio doc build fails because of missing gio.xml in the tarballs
+ 768963 improper va_list use in g_log_structured()
+ 768968 gio/tests/socket-listener hangs since e4ee307
+
+ Translation updates:
+ Spanish
+
+
+ Overview of changes in GLib 2.49.3
+ ==================================
+
+ * GLib has a structured logging API, g_log_structured, with support
+ for writing to the systemd journal. It also supports colored output
+ in terminals
+
+ * Some new GBytes API has been added:
+ - g_key_file_load_from_bytes
+ - g_compute_hmac_for_bytes
+
+ * Stack-allocated GVariantBuilder and GVariantDict objects can now be
+ initialized with G_VARIANT_BUILDER_INIT and G_VARIANT_DICT_INIT
+
+ * gio:
+ - Add a way to register handlers for custom uri schemes
+ - Add a G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE attribute to
+ have these heuristics in a single place
+ - Include a gio tool that makes the functionality of the
+ various gvfs commandline tools available in a single place
+ - Add portal support to g_app_info_launch_default_for_uri
+ - Add portal support to GNetworkMonitor
+ - Add portal support to GProxyResolver
+ - Add portal support to g_application_send_notification
+
+ Bugs fixed:
+ 547200 g_utf8_find_next_char() issues
+ 662802 systemtap multiarch issue
+ 723506 fork/exec from non-main thread when autolaunching could be avoided...
+ 725902 build: simplify dtrace configuration
+ 728207 gsocketservice: Documentation does not mention that is already act...
+ 729914 instead of DEBUG_CODE and IF_DEBUG, provide a common macro to supp...
+ 744456 Structured logging API
+ 744678 Unable to delete relocatable schemas
+ 746685 clarify that g_variant_get_data() can be used instead of g_variant...
+ 747134 glib-compile-resources --generate should detect common C++ file ex...
+ 750257 GSettings changed signal should clearly state the order required
+ 753231 Memory is potentially used after free
+ 754012 missing filename in "Error loading css: Failed to import: Error op...
+ 760115 gtestutils: add missing dash in seed argument's --help documentation
+ 760423 gio-querymodules prints error messages as question marks on some l...
+ 761102 Increase performance for main loop
+ 765338 GLib.compute_hmac_for_data throws every time
+ 766370 Add a macro for initializing g_auto(GVariantBuilder)
+ 766899 Superflous HTML/XML comments in GDBusProxyTypeFunc documentation s...
+ 766933 GSocketAddress leaks in gnetworkmonitornetlink.c:read_netlink_mess...
+ 767765 Add names and tags to various GSources and GTasks constructed in GLib
+ 767880 gkeyfile: add g_key_file_load_from_bytes() API
+ 767887 vfs: add g_vfs_register_uri_scheme()
+ 767949 [patch] Typos in glib docs
+ 768029 infinite loop in parse_name_internal()
+ 768119 Fix fallout from get_supported_schemes() changes
+ 768357 Build the gio tool on Windows/MSVC
+ 768498 portal support for glib
+ 768504 keyfile: g_key_file_get_double behavior doesn't follow documentation
+ 768549 Test failure: test_ip_sync_dgram
+ 768551 Test failure: test_socket_address_to_string
+ 768560 gio/tests/gsettings: fix GSettings reference leaks in some tests
+ 768780 O_PATH is a non-standard flag which may be unavailable on non-Lin...
+ 768806 gdbus tool must swallow -- argument
+
+ Translation updates:
+ Chinese (Taiwan)
+ French
+ Hebrew
+ Indonesian
+ Lithuanian
+ Portuguese
+ Spanish
+
+
+ Overview of changes in GLib 2.49.2
+ ==================================
+
+ * GMainContext and GTask have gained more systemtap probes
+
+ Bugs fixed:
+ 673101 resource compiler dependency generation not working for gen...
+ 700756 GFile.new_for_path arguments misses (type filename) annotation
+ 730187 glocalfileoutputstream: Fix an FD leak in an error path
+ 755439 Memory leak in gdbusproxy.c
+ 759813 Add more SystemTap/DTrace probes for main context and GTask
+ 761810 gio: Support using GDBusObjectManagerServer at path ‘/’
+ 767172 docs: Move GIO_USE_VFS to "okay for production" section
+ 767218 Remove a UTF-8 ellipsis from gsignal.h
+ 767245 Add filename type annotations
+ 767824 Some UTC timezones incorrectly recognized on Windows 7
+
+ Translation updates:
+ Occitan
+
+
+ Overview of changes in GLib 2.49.1
+ ==================================
+
+ * GDesktopAppInfo now allows bus activation with dashes. This is
+ not technically allowed per the Desktop Entry specification, but
+ it happens in the wild. Rather than forcing people to go through
+ another traumatic desktop file rename, accept it and translate - to _.
+
+ * The support for giving names to threads has been improved. Thread names
+ are now supported on Solaris as well, and the Linux support no longer
+ uses prctl() but the pthread api.
+
+ * GIO resources can now be overridden at runtime, using the G_RESOURCE_OVERLAYS
+ environment variable.
+
+ * gdbus-codegen can now generate autocleanup definitions for the types
+ it generates. Use the --c-generate-autocleanup option to control this
+
+ Bugs fixed:
+ 665446 Use g_abort() instead of abort()
+ 731988 glocalfile: Avoid a potential NULL pointer dereference
+ 742898 g_value_type_transformable() description differs from the code
+ 747107 GVariant varargs documentation: g_variant_get() example
+ 747478 g_system_thread_set_name() is not implemented for gthread-win32
+ 748474 g_get_language_names() is not thread-safe
+ 748530 gthread: W32 implementation of g_get_num_processors() has lame fallback
+ 748806 GVariant: Better introduction to the concepts and its uses
+ 749583 GSequence performance improvements
+ 749606 tests: always remove app.desktop
+ 755898 [PATCH] settings: add get/set uint64
+ 758174 Fix documentation typos
+ 758738 Usage of GType properties causes crashes due to gulong/gpointer mismatch
+ 760186 namespace clash with gdb pretty-printing code
+ 762994 Race condition in GIO/AppFileChooser crashes Firefox/Gtk3
+ 763379 codegen: Add support for g_autoptr to gdbus-codegen–generated objects
+ 763821 build: Also dist Systemtap files always for gobject/
+ 764092 gstrfuncs: Document the behaviour of g_strjoinv()
+ 764163 g_task_had_error doesn't remember the error after g_task_propagate_*
+ 764415 Very High CPU usage in g_poll() Windows implementation
+ 764574 build: Fix all statfs() tests failing
+ 764575 tests: Fix compilation errors due to Y2K format problems
+ 764685 GApplication documentation about handling command-line options is confusing
+ 764754 '-' in application id: unbreak bus activation and notifications
+ 765173 documentation of g_main_context_push_thread_default() regarding GIO...
+ 765668 GResources: add support for resource overlays
+ 765710 gdbus-tool: only print note about expected argument types if that...
+ 765712 tests: Fix compilation
+ 765861 task: avoid context lock when setting source name
+ 765900 Add g_drive_is_removable() support
+ 765924 Improve external drives detection
+ 765959 socket: set fd field to -1 after closing socket
+ 765990 Visual Studio: Define inline only when necessary
+ 765991 Compilation of gresource.c is broken due to S_ISDIR
+ 766092 Incorrect locale handling in g_date_time_format_locale()
+ 766211 Fix the upper bound in g_unichar_iswide_bsearch
+ 766407 Some build-related defects in glib testsuite
+ 766570 build: Fix a misnamed variable in glib-tap.mk
+
+ Translation updates:
+ Basque
+ Catalan
+ Chinese
+ Occitan
+ Portuguese
+ Turkish
+ Vietnamese
+
+
+ Overview of changes in GLib 2.48.0
+ ==================================
+
+ * a minor build fix in the name of determinism
+
+ * a few coverity fixes
+
+ Bugs fixed:
+ 763617 giotypefuncs.c: Sort _get_type functions in the 'C' locale
+
+ Translations updated:
+ Danish
+ Italian
+
+ Overview of changes in GLib 2.47.92
+ ===================================
+
+ * gdbus-codegen now supports g_autoptr()
+
+ * g_get_user_runtime_dir() now reliably returns an existing directory
+
+ * g_array_remove_range() can now remove 0 items from the end of an array
+
+ * Many fixes for Windows
+ * build fixes
+ * file monitoring
+ * gsettings backend
+ * streams
+ * random numbers
+ * wide character support
+
+ * documentation improvements
+
+ * other small bugfixes
+
+ Bugs fixed:
+ 724847 Segmentation fault on "gsettings list-recursively"
+ 743933 gapplication: add --app-id command line option
+ 756706 [PATCH] gio/gtestdbus.c: don't use non-standard %m printf modifier
+ 757506 gsettings: schema_list should use the passed schema's source
+ 760694 W32: Apps linked with -mwindows make cursor busy sometimes
+ 762202 g_win32_error_message improvements
+ 762637 build: Unconditionally dist tapset files
+ 762748 Undefined behavior
+ 762937 Mention that g_clear_error can be used with an "empty" GError
+ 763339 array: Support clearing an empty array with g_array_remove_range()
+ 763344 g_get_user_runtime_dir(): ensure directory exists
+ 763379 codegen: Add support for g_autoptr to gdbus-codegen–generated objects
+
+ Translations updated:
+ Brazilian Portuguese
+ Czech
+ Finnish
+ French
+ Galician s
+ German
+ Greek
+ Hebrew
+ Hungarian
+ Italian
+ Kazakh
+ Korean
+ Latvian
+ Lithuanian
+ Occitan
+ Polish
+ Russian
+ Serbian
+ Slovak
+ Slovenian
+ Spanish
+ Swedish
+
+ Overview of changes in GLib 2.47.6
+ ==================================
+
+ * Windows usupport:
+ - Fixes and improvements to the GSettings registry backend
+ - Handle readability and writability of registry keys
+ - Use Unicode registry APIs
+
+ * Bugs fixed:
+ 760852 744772 761126 747927 761337 744570 761504 761550 761843
+ 744570 GString is missing (transfer none) annotations on many of its methods
+ 744772 systemtap and gdb scripts install in wrong place
+ 747927 Documentation: various small improvements
+ 760852 gdbusobjectmanagerserver: Clarify recommended ObjectManager paths
+ 761126 winiconv: update to upstream version
+ 761337 Fix some annotations
+ 761504 W32 registry GSettings backend does not use Unicode
+ 761550 Cannot build with default flags under Fedora rawhide (-Werror=format-...
+ 761843 gmacros.h is testing attributes with __has_feature (when compiling wi...
+
+ * Translation updates:
+ Brazilian Portuguese
+ Bulagarian
+ Chinese (Taiwan)
+ Hungarian
+ Polish
+ Slovak
+ Slovenian
+ Spanish
+ Swedish
+
+
+ Overview of changes in GLib 2.47.5
+ ==================================
+
+ * the system copy of PCRE is now used by default to implement GRegex.
+ Configure with --with-pcre=internal if a system PCRE version
+ is unavailable or undesired.
+
+ * interfaces for DTLS support have been added. A new version of
+ glib-networking will also be required.
+
+ * GDBusMethodInvocation now drops replies if the sender set the
+ NO_REPLY_EXPECTED flag
+
+ * several GApplication fixes, including fixes for commandline arguments
+ in interpreted languages on Windows
+
+ Bugs fixed:
+ 624186 Deprecate glib-gettext macros
+ 734095 gtk-demo.py of PyGObject fails to run on Windows (and likely other binding scripts using g_application_run())
+ 735754 Implement close on TLS GOutputStream
+ 748064 gnulib vfprintf returns desired (not actual) number of bytes, ignores errors
+ 752240 Add DTLS support to GIO
+ 755421 GDBus ignores NO_REPLY_EXPECTED flag in messages, leading to warnings on system bus
+ 756875 Include ntdef.h for NTSTATUS
+ 759554 g_application_run() calls g_main_context_default() repeatidly
+ 760199 gsettings: Install gettext ITS rules
+ 760215 G_LIKELY/_UNLIKELY macros need more parentheses
+ 760683 regex test: Check the expected PCRE exceptions at runtime
+
+ Translations updated:
+ Brazilian Portuguese
+ Czech
+ German
+ Lithuanian
+ Swedish
+
+ Overview of changes in GLib 2.47.4
+ ==================================
+
+ * The GApplication documentation has been improved in several areas.
+
+ * Bugs fixed:
+ 749092 gdb pretty-printers fail on Python 3 with a TypeError...
+ 757374 macros: clean up "inline" mess
+ 758641 Memory leak in g_dbus_proxy_new_for_bus_sync()
+ 759134 Add missing checks for gnulib vasnprintf()
+ 759408 Do not use uninitialized var
+ 756475 Stop supporting non-POSIX getpwuid_r, getgrgid_r
+ 757372 GApplication: destroy the impl on shutdown
+ 728099 macros: add G_GNUC_CHECK_VERSION() for compiler checks
+ 757299 glib-compile-resources: do not leak c_name
+ 758553 Fix gettext use
+ 758823 file monitors: reorder some code to avoid segfault
+ 756214 gsettings: Don't translate ""
+ 710243 Add GParamSpec object ref management annotations
+ 735696 xdgmime: Finer handling for cases where mmap() is not available
+ 752983 gapplication: Acquire the main context before running
+
+ * Translation updates:
+ Swedish
+
+
+ Overview of changes in GLib 2.47.3
+ ==================================
+
+ The inline cleanup in the last release accidentally removed three
+ symbols from libglib-2.0.so. It is unlikely that this will have caused
+ any problems because these symbols were only backup symbols for
+ definitions exported as inlines in the header files, but ABI is ABI.
+
+ This release corrects only this problem.
+
+ Overview of changes in GLib 2.47.2
+ ==================================
+
+ * We have formalised the assumption that all compilers that are
+ interested in support 'static inline' and simplified the macros around
+ this considerably. Please watch for and report unintentional fallout.
+
+ * New API: hardware-assisted helpers for overflow-checked integer math.
+
+ * other fixes
+
+ Bugs fixed:
+ 696324 gtester-report doesn't work with Python 3.x
+ 719966 glib: Add missing (nullable) and (optional) annotations
+ 752837 gobject and glib-compile-resources rely on .CRT$XCU section, no longer works with Win 10 UCRT (VS 2015)
+ 755364 make gtkdoc-check happy again
+ 756134 Segmentation fault on calling g_simple_action_group_add_action with bad action constructor call
+ 756179 gwin32.c: Replace VerifyVersionInfoW() with RtlGetVersion() due to API deprecation
+ 756988 GSequence should document each function's complexity
+ 757294 Move G_POLLFD_FORMAT to glibconfig.h
+ 757374 macros: clean up "inline" mess
+ 757451 doc: fix g_task_attach_source() example
+ 757628 gio tests fail to build when cross compiling 2.46.1
+ 757693 Invalid free in g_local_file_trash()
+ 757742 Fix up annotations in ghash.c
+ 758181 GTask: fix wrong example code
+
+ Translations updated:
+ Greek
+ Hebrew
+ Hungarian
+ Norwegian bokmål
+ Portuguese
+ Scottish Gaelic
+ Simplified Chinese
+ Spanish
+
+ Overview of changes in GLib 2.47.1
+ ==================================
+
+ * The Unicode support has been updated to version 8.0 of the Unicode standard
+
+ * GDesktopAppInfo no longer sets the DISPLAY environment variable when
+ launching apps. This is now done in the GAppLaunchContext implementations
+ when appropriate
+
+ * Bug fixes:
+ 664740 Key-value file parser, space after integer
+ 687223 cleverer GThreadPool management
+ 692085 stderr and stdout are not always file descriptors 1 and 2
+ 697907 Add interface for socket-like things (GSocket, DTLS, etc)
+ 735754 Implement close on TLS GOutputStream
+ 737116 Add functions to print GSocketConnectables and addresses as strings
+ 743011 Minor additions to GError documentation
+ 749161 undefined reference to `__imp__stat32i64'
+ 749314 Cannot restore a just-trashed file
+ 751924 Add recvmmsg()-like API on GSocket
+ 752240 Add DTLS support to GIO
+ 752837 gobject and glib-compile-resources rely on .CRT$XCU section, no longer...
+ 753310 Remove `#pragma GCC system_header` from gmessages.h
+ 753935 Update example namespace and class names in GObject tutorial
+ 754855 Object instantiation documentation refers to example that no longer ex...
+ 754983 Wayland: g_desktop_app_info_launch_uris_with_spawn() forces DISPLAY va...
+ 754994 g_date_time_get_second () sometimes returns an off-by-one result
+ 755083 Clarify in G_ADD_PRIVATE that it is safe to call _get_instance_private...
+ 755351 Example still contains g_autoptr(gchar)
+ 755355 Move GStrv to glib.h so it can be used with g_auto()
+ 755374 g_variant_get_child(): flatten-first logic on '&'
+ 755496 glib 2.46 fails GStreamer test suite
+ 755609 glib 2.46.0 breaks Sun Java JVM 1.8.0.60
+ 755766 gvalue: The g_auto cleanup function assert if value is G_VALUE_INIT
+ 755795 2.46 considers empty files as octet-stream rather than text (leads to...
+ 755961 Fix up annotations in gbytes.c
+ 756053 MSVC doesn't understand the symbol 'msghdr'
+ 756054 MSVC linker error due to 'g_socket_send_message_with_timeout()'
+ 756077 testutils: remove internal ABI comment
+ 756099 g_main_context_query(): Annotate @n_fds as (in) parameter
+ 756139 musl: ctors called in the wrong order
+ 756179 gwin32.c: Replace VerifyVersionInfoW() with RtlGetVersion() due to AP...
+ 756251 The documentation of G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is confusing
+ 756255 GOutputStream swallowing errors in splice with G_OUTPUT_STREAM_SPLICE...
+ 756316 GSequence should provide fast api to check if empty
+ 756382 snprintf used on Windows with VS2015 doesn't support %n
+ 756477 gio/gthreadedresolver.c has outdated copy of bionic headers (for android)
+ 756550 gtypes.h: Make G_MININTn literals negative
+ 756875 Include ntdef.h for NTSTATUS
+ 756952 giomodule: return a copy of module name
+
+ * Translation updates:
+ Basque
+ Czech
+ Serbian
+ Serbian Latin
+ Vietnamese
+
+
+ Overview of changes in GLib 2.46.0
+ ==================================
+
+ * Disable runtime-deprecation warnings
+
+ * Fix marshalling of flags on bigendian 64bit architectures
+
+ * Translation updates
+ Brazilian Portuguese
+ Danish
+ German
+ Latvian
+ Russian
+ Turkish
+
+
+ Overview of changes in GLib 2.45.8
+ ==================================
+
+ * utf8 validation and utf8-to-ucs4 conversion are faster
+
+ * Small speedups to property change notification
+
+ * Various other small optimizations for GQuark, GData
+
+ * Bugs fixed:
+ 696426 GParamSpecTypeInfo do not need to be static
+ 735429 Cleanup MSVC Project Files Generation
+ 738504 Optimize UTF-8 decoding by unrolling branches and expressions
+ 742903 Add missing (transfer) annotation to GString
+ 748633 g_set_object order of operations
+ 754431 Fix build of glib/gstrfuncs.c on Windows
+ 754560 gioerror: Add more mappings for WinSock error codes
+ 754582 Glib cannot compile
+ 754601 Make g_strerror work with non-glibc POSIX systems
+ 754636 tests/unicode-encoding test fails for glib 2.45.7 on x86-64
+ 754788 more g_strerror stuff
+ 754831 autocleanups: Add GString type
+ 754924 Improve test coverage of g_utf8_validate() by added known-...
+ 754986 Avoid unnecessary signal emission during draw
+
+ * Translation updates:
+ Italian
+ Kazakh
+ Korean
+ Lithuanian
+ Slovenian
+ Swedish
+
+
+ Overview of changes in GLib 2.45.7
+ ==================================
+
+ * Add G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE for use by non-POSIX-like
+ backends (e.g. cloud storage).
+
+ * GFileMonitor: Make the inotify backend work with atomic renames again
+
+ * GSettings: change notification is again working unconditionally
+
+ * GListStore has a sort function now
+
+ * Test infrastructure:
+ - Tests are now required to have unique names
+ - TAP support has been improved
+ - A macro for asserting that two memory regions have identical content
+ has been added
+
+ * Bugs fixed:
+ 708525 A "g_file_query_info" on the file path "/sys/kernel/debug/hid"...
+ 742849 inotify: send paired events to both sides
+ 744060 Update GObject tutorial documentation to use G_DECLARE_FINAL_T...
+ 747364 Fix GError leak in g_file_query_writable_namespaces()
+ 749492 Support file creation time on FreeBSD and NetBSD
+ 752769 (g_socket_receive_message | g_socket_send_message) performance
+ 753745 glib-genmarshal still needed for cross-compilation
+ 754152 Add g_list_store_sort
+ 754211 Memory leak in g_file_enumerator_iterate ()
+ 754264 GLib 2.44 certificate chain construction fails if the PEM incl...
+ 754283 gtestutils: add g_assert_cmpmem()
+ 754284 gtestutils: print the TAP test plan first, not last
+ 754286 misc gtestutils fixes
+ 754307 size of array '_GStaticAssertCompileTimeAssertion_3387' is neg...
+
+
+ * Translation updates:
+ Chinese (Taiwan)
+ French
+ Galician
+ Greek
+ Hebrew
+ Hungarian
+ Indonesian
+ Polish
+ Portuguese
+
+
+ Overview of changes in GLib 2.45.6
+ ==================================
+
+ * Fix a test failure and a build failure
+
+ Overview of changes in GLib 2.45.5
+ ==================================
+
+ * GNetworkMonitor now provides information about metered networks
+
+ * g_mem_set_vtable has been deprecated; it has not been working for
+ quite a while. The recommendation is to use valgrind, or replace
+ malloc itself.
+
+ * Bugs fixed:
+ 656325 Make GDBusInterfaceVTable binding friendly
+ 741779 Documentation tweaks addressing real-world API misuses
+ 741822 Fails to build with VS 2015
+ 742386 gdbusconnection: Don't g_printerr() when exiting
+ 743018 gobject: Add more cross-links between documentation pages
+ 750282 Add g_network_monitor_get_network_metered() to get if the connection...
+ 751358 GFileMonitor doesn't react to "mv some-file watched-file"
+ 751592 Stop using GMemVtable
+ 751598 Stop 'handle-local-options' propagation when callback reports an err...
+ 751610 g_str_hash produces collisions with strings of length 2
+ 751751 Wrong docs of g_async_queue_remove
+ 752210 gdbus command crashes with SIGSEGV
+ 752656 gdbusconnection: Fix signal subscription documentation
+ 752767 Fix typo in g_hash_table_replace() documentation
+ 753278 gdbus: Don't use g_assert_no_error() GDBusObjectManagerServer
+ 753285 g_menu_item_set_icon fails if called with NULL icon
+
+ * Translation updates:
+ Catalan
+ Czech
+ French
+ Indonesian
+ Lithuanian
+ Norwegian bokmål
+ Slovak
+ Spanish
+ Thai
+ Turkish
+
+
+ Overview of changes in GLib 2.45.4
+ ==================================
+
+ * Bugs fixed:
+ 727829 win32: glibconfig.h.win32 updates
+ 741901 Clang cannot know that g_error don't return
+ 746339 GSocket kills process when fd is not a socket
+ 747676 gio/tests/socket fails: test_fd_roundtrip
+ 748610 Some tests fail with non-English locales
+ 749911 g_inet_address_to_string broken on XP/2003
+ 749912 g_inet_address_new_from_string broken on XP/2003
+ 750625 Should dismiss Software Updates Available notification after...
+ 750807 G_BREAKPOINT doesn't work as intended on Darwin
+ 751160 gtask does unnecessary work
+ 751672 -Wduplicate-decl-specifier in glib/tests/keyfile.c
+ 751731 GFile/DirectoryMonitor emit move events with other_file=NULL
+ 751737 gio/tests/appmonitor test fails in 2.45.3
+ 751798 Wrong enum type used in some test-cases
+ 752089 make gsocketservice::active a property
+ 752293 small cleanup: use list_free_full
+
+ * Translation updates:
+ Greek
+ Hebrew
+ Portuguese
+
+
+ Overview of changes in GLib 2.45.3
+ ==================================
+
+ * Improve performance of g_signal_handler_disconnect for signals
+ with many handlers
+
+ * GDBus has gained a new call flag to allow interactive authorization
+
+ * GSettings:
+ - New API: g_settings_schema_list_keys
+ - Deprecated: g_settings_list_keys
+
+ * OS X:
+ - Implement GNotification
+ - Bump the OS X requirement to 10.9
+
+ * Windows:
+ - Add registry reading API
+ - Reimplement GAppInfo using registry information
+
+ * Bugs fixed:
+ 666831 Support URI opening on W32
+ 728489 property action with inverted boolean state
+ 730168 Incorrect annotation on g_action_group_get_action_state_type return...
+ 733325 Several regex tests fail with pcre3 8.35
+ 734888 GLib has no helper functions to work with W32 Registry
+ 737009 signal handler lookup doesn't scale
+ 738185 Misleading language about "file name encoding" in the docs on g_env...
+ 738504 Optimize UTF-8 decoding by unrolling branches and expressions
+ 739122 glib not handling -1 return ("no limit") from sysconf (_SC_THREAD_S...
+ 739424 gnome-shell crashes when files are added, deleted, or modified in $...
+ 739616 DBus; Add new call flag to allow interactive authorization
+ 740308 Add g_settings_schema_list_keys() method
+ 740516 RFE: please provide an introspectible version of g_log_set_handler
+ 741788 Document GSettings build system integration
+ 745013 GBinding not thread safe
+ 747146 Implement GNotification on OSX
+ 747941 try XDG_RUNTIME_DIR/bus before falling back to X11 autolaunch (dbus...
+ 748727 Filechooser dialog shows no icons for directories on W32
+ 749693 GActionGroupExporter: flush queue on requests
+ 750203 GNetworkMonitorNetlink hangs in user namespace
+ 750322 gapplication: Make sure --help output is translated
+ 750344 GTlsInteractionClass is missing from doc
+ 750369 Various GBinding cleanups
+ 750386 Race condition in g_io_condition_get_type
+ 750399 Typo "equilalent" in glib documentation's glib-Error-Reporting.html...
+ 750573 GTlsDatabaseClass is not documented
+ 750918 genmarshal: silence register storage class warnings
+ 751122 gsocket: avoid unnecessary g_socket_cond_wait() in _send_messages()
+ 479730 The "g_key_file_set_comment" interface prepends '#' character to...
+
+ * Translation updates:
+ Hungarian
+ Spanish
+
+
+ Overview of changes in GLib 2.45.2
+ ==================================
+
+ * Improve error reporting in glib-compile-schemas.
+
+ * Add introspection annotations to GListStore.
+
+ * Bugs fixed:
+ 696749 win32 : failed to compile because of careless mistake in the code
+ 723394 const parameter to GtkPopover gtk_popover_set_pointing_to
+ 724113 gdbus-connection-loss test can fail on slow machines
+ 725981 tap-driver.sh: internal error getting exit status
+ 733325 Several regex tests fail with pcre3 8.35
+ 744895 Unknown or unsupported transport 'this-should-not-be-used-and-will...
+ 747882 gtype: Bump allowed number of children
+ 748534 gtest: if a subprocess assertion fails, print its stdout and stderr
+ 748612 de_DE locale used in option-context test is not supported by FreeBSD
+ 748614 Double unref in g_socket_listener_add_inet_port
+ 748834 glocalfilemonitor: Emit notification on rate limit change
+ 749079 gdbus-peer test: TCP tests can fail with ECONNRESET due to a race...
+ 749080 gdatetime test: fails if close to rollover between seconds
+ 749180 gnetworkaddress: add return type annotation to parse methods
+ 749352 g_binding_unbind() fails when source is also the target
+ 749353 GBinding does not connect to the detailed notify signal
+
+ * Translation updates:
+ Catalan
+ French
+ Slovak
+ Thai
+
+
+ Overview of changes in GLib 2.45.1
+ ==================================
+
+ * The GSettings schema compiler, glib-compile-schemas has been changed
+ to reject schema xml that has duplicate <summary> or <description>
+ elements. Such elements typically occur when translations are merged
+ into the schema, with xml:lang attributes. This is not the correct
+ way to translate schemas. Instead keep the translations in the .mo
+ file and set the gettext-domain attribute on the <schemalist> element.
+
+ * The file monitoring infrastructure has been rewritten, and all backends
+ have seen major improvements.
+
+ The inotify backend is reporting events with less delay (no event will
+ be delayed more than 10ms) and wakeups due to file monitoring have been
+ significantly reduced. A CHANGES_DONE event will also be sent when new
+ files appear.
+
+ The poll implementation is now using the thread default main context.
+
+ The fam implmentation is now running in the worker thread.
+
+ The fen implementation has been removed, since it was unmaintained.
+
+ * The GSettings schema compiler, glib-compile-schemas, is more strict
+ about rejecting schemas with xml:lang style merged translations.
+ Schema translations should be done by specifying the gettext domain
+ in the xml, and keeping the translations in gettext. To avoid breaking
+ already-installed schemas, this change is only taking effect when
+ you use the --strict option.
+
+ * The hardcoded 10-thread limit of GTask's thread pool has been removed,
+ since it was prone to causing deadlocks. The thread pool is now allowed
+ to grow dynamically and will shrink back over time.
+
+ * GSimpleAsyncResult has been deprecated in favor of GTask.
+
+ * The algorithm used by GAppInfo to find default handlers for mime types
+ has been tweaked to prefer apps that handle the specific subtype over
+ default handlers for a generic supertype.
+
+ * Bug fixes:
+ 627285 inotify file monitor hardwired delay
+ 631597 Segmentation fault in append_escaped_text
+ 661767 merge/improve various bits of run-in-thread functionality
+ 687223 cleverer GThreadPool management
+ 711547 win32: silence some build warnings
+ 719966 glib: Add missing (nullable) and (optional) annotations
+ 726447 Possibly an error in text string
+ 728663 W32: wrong stat struct is used when built with MinGW-w64
+ 728669 W32: GLocalFile can't measure size of files larger than 2^32...
+ 730188 gsocket: Document FD ownership with g_socket_new_from_fd()
+ 733325 Several regex tests fail with pcre3 8.35
+ 738207 Add a way to set SO_SENDBUF and SO_RECVBUF on listener (and...
+ 739850 GClosure: add valgrind hints
+ 741791 gmain: Save errno when handling unix signals
+ 744282 gvfs-open for application/x-virt-viewer changed behaviour bet...
+ 745255 Add support for copying sessions between GTlsClientConnections
+ 745745 gdbus: fix out-of-bound array access
+ 745821 Don't use __alloc_size__ attribute with clang
+ 746749 GLib-GIO:ERROR:inotify-kernel.c:327:ik_source_dispatch: ass...
+ 746753 Glib-compile-resources --generate-header not using ".h" as ...
+ 747209 glib-compile-schemas ought to reject repeated <summary> and...
+ 747349 Conversion of gdbus to use GTask causes deadlocks
+ 747363 gatomic: Add missing new line in API doc comment
+ 747472 Don't ignore already-installed schemas with multiple <summa...
+ 747541 gdbus segfaults with invalid --dest
+ 747772 Having hardcoded utf8 strings in the source code does not p...
+ 748019 gsocketconnection: Fix copy-pasto in documentation
+ 748177 not all test schemas are distributed, "make distcheck" fails
+
+ * Translation updates:
+ Basque
+ Czech
+ Danish
+ Finnish
+ German
+ Hebrew
+ Icelandic
+ Norwegian bokmål
+ Russian
+ Turkish
+
+Overview of changes in GLib 2.44.1
+==================================
+
+* Don't redefine typedefs to avoid build problems on OpenBSD
+
+* Improve the default application algorithm
+
+* Bump the number of children a GType can have
+
+* Various testsuite improvements
+
+* Translation updates:
+ Czech
+ Icelandic
+ Russian
+
+
+Overview of changes in GLib 2.44.0
+===================================
+
+With the exception of translation and documentation, there have been no
+changes since the prerelease.
+
+Bugs fixed:
+ 730188 gsocket: Document FD ownership with g_socket_new_from_fd()
+
+Translations updated:
+ Basque language
+ Brazilian Portuguese
+ Chinese (Taiwan)
+ Danish
+ Galician s
+ Hebrew
+ Indonesian
+ Norwegian bokmål
+ Turkish
+
+
Overview of changes in GLib 2.43.92
===================================
--- /dev/null
- GCancellable *cancellable,
- GSimpleAsyncResult *result)
+/* 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 "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_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;
+
+ 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;
+ }
+
+#ifdef LIBDBUSPOLICY
+ worker->dbuspolicy = dbuspolicy1_init (address);
+ if (worker->dbuspolicy == NULL)
+ {
+ close (worker->fd);
+ worker->fd = -1;
+
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Cannot load dbus policy for kdbus transport"));
+ return FALSE;
+ }
+#endif
+
+ worker->closed = FALSE;
+ return TRUE;
+}
+
+static gboolean
+_g_kdbus_quit_loop (gpointer loop)
+{
+ g_main_loop_quit ((GMainLoop*)loop);
+ return FALSE;
+}
+
+/*
+ * _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;
+
+ 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 = 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;
+ asprintf (&worker->unique_name, ":1.%llu", (unsigned long long) cmd->id);
+
+ /* 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);
+
+ 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;
+ gssize 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 = 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;
+ gssize 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 = 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;
+
+ asprintf (&unique_name, ":1.%llu", name->id);
+ 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;
+}
+
+
+static gboolean
+g_kdbus_NameHasOwner_internal (GKDBusWorker *worker,
+ const gchar *name)
+{
+ struct kdbus_cmd_info *cmd;
+ struct kdbus_info *conn_info;
+ gssize size, len;
+ gint ret;
+
+ if (g_dbus_is_unique_name(name))
+ {
+ size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
+ cmd = g_alloca0 (size);
+ cmd->id = g_ascii_strtoull (name+3, NULL, 10);
+ }
+ else
+ {
+ len = strlen(name) + 1;
+ size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
+ cmd = g_alloca0 (size);
+ cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+ cmd->items[0].type = KDBUS_ITEM_NAME;
+ memcpy (cmd->items[0].str, name, len);
+ }
+ cmd->size = size;
+
+ 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;
+ gssize 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 = G_STRUCT_OFFSET (struct kdbus_cmd_info, items);
+ cmd = g_alloca0 (size);
+ cmd->id = g_ascii_strtoull (name+3, NULL, 10);
+ }
+ else
+ {
+ len = strlen(name) + 1;
+ size = G_STRUCT_OFFSET (struct kdbus_cmd_info, items) + KDBUS_ITEM_SIZE(len);
+ cmd = g_alloca0 (size);
+ cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + len;
+ cmd->items[0].type = KDBUS_ITEM_NAME;
+ memcpy (cmd->items[0].str, name, len);
+ }
+
+ cmd->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)
+ {
+ asprintf (&creds->unique_name, ":1.%llu", (unsigned long long) conn_info->id);
+ //TODO: Error handling
+ }
+
+ 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;
+}
+
+
+/* < 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,
+ GError **error)
+{
+ GBusStartServiceReplyFlags status;
+
+ 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))
+ {
+ GDBusMessage *message;
+ GDBusMessage *reply;
+ gint ret;
+
+ reply = NULL;
+
+ message = g_dbus_message_new_method_call (name, "/", "org.freedesktop.DBus.Peer", "Ping");
+ g_dbus_message_set_serial (message, -1);
+
+ ret = _g_kdbus_send (worker, message, &reply, 25000, NULL, NULL);
+ 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;
+ }
+ g_object_unref (reply);
+ status = G_BUS_START_SERVICE_REPLY_SUCCESS;
+ }
+ else
+ status = G_BUS_START_SERVICE_REPLY_ALREADY_RUNNING;
+
+ return status;
+}
+
+
+/*
+ * 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;
+ guint64 p = 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; cnt_1 < (worker->bloom_n_hash); cnt_1++)
+ {
+ for (cnt_2 = 0, hash_index = 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:
+ asprintf (&type, "arg%u", element->arg);
+ g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
+ free (type);
+ break;
+
+ case MATCH_ELEMENT_ARGNPATH:
+ asprintf (&type, "arg%u-slash-prefix", element->arg);
+ g_kdbus_bloom_add_pair (worker, bloom, type, element->value);
+ free (type);
+ break;
+
+ case MATCH_ELEMENT_ARG0NAMESPACE:
+ g_kdbus_bloom_add_pair (worker, bloom, "arg0-dot-prefix", element->value);
+ break;
+
+ case MATCH_ELEMENT_DESTINATION:
+ case MATCH_ELEMENT_EAVESDROP:
+ break;
+ }
+ }
+
+ size += KDBUS_ALIGN8 (G_STRUCT_OFFSET (struct kdbus_item, data64) + worker->bloom_size);
+ cmd = g_alloca0 (size);
+ cmd->size = size;
+ cmd->cookie = match->cookie;
+
+ item = cmd->items;
+ item->size = G_STRUCT_OFFSET(struct kdbus_item, data64) + worker->bloom_size;
+ item->type = KDBUS_ITEM_BLOOM_MASK;
+ memcpy(item->data64, bloom, worker->bloom_size);
+ item = KDBUS_ITEM_NEXT(item);
+
+ if (src_id != KDBUS_MATCH_ID_ANY)
+ {
+ item->size = G_STRUCT_OFFSET (struct kdbus_item, id) + sizeof(src_id);
+ item->type = KDBUS_ITEM_ID;
+ item->id = src_id;
+ item = KDBUS_ITEM_NEXT(item);
+ }
+
+ if (sender_name)
+ {
+ item->size = G_STRUCT_OFFSET (struct kdbus_item, str) + sender_len;
+ item->type = KDBUS_ITEM_NAME;
+ memcpy (item->str, sender_name, sender_len);
+ }
+
+ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (ret < 0)
+ {
+ g_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;
+ gssize 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 = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+ G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
+ item = KDBUS_ITEM_NEXT(item);
+
+ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (ret < 0)
+ {
+ g_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;
+ gssize 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;
+ }
+
+ 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 = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+ G_STRUCT_OFFSET (struct kdbus_notify_name_change, name) + len;
+ item = KDBUS_ITEM_NEXT(item);
+
+ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (ret < 0)
+ {
+ g_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;
+ gssize 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;
+ }
+
+ 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 = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+ G_STRUCT_OFFSET(struct kdbus_notify_name_change, name) + len;
+ item = KDBUS_ITEM_NEXT(item);
+
+ ret = ioctl(worker->fd, KDBUS_CMD_MATCH_ADD, cmd);
+ if (ret < 0)
+ {
+ g_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;
+ gssize 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;
+ }
+
+ 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 != 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;
+ }
+ }
+
+ /* '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 = 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 = G_STRUCT_OFFSET (struct kdbus_item, name_change) +
+ 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);
+ }
+ }
+}
+
+
+/*
+ * 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, *value;
+ gchar *sender;
+ guint i;
+ GVariant *parts[2];
+ GVariantIter *fields_iter;
+ GString *owned_name;
+ guint8 endianness, type, flags, version;
+ guint64 key, serial;
+
+ 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);
+
+ 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);
+
+ parts[0] = g_variant_get_child_value (body, 0);
+ parts[1] = g_variant_get_child_value (body, 1);
+ g_variant_unref (body);
+
+ g_variant_get (parts[0], "(yyyyuta{tv})", &endianness, &type, &flags, &version, NULL, &serial, &fields_iter);
+ g_variant_unref (parts[0]);
+
+ while (g_variant_iter_loop (fields_iter, "{tv}", &key, &value))
+ {
+ switch (key)
+ {
+ case G_DBUS_MESSAGE_HEADER_FIELD_REPLY_SERIAL:
+ g_dbus_message_set_reply_serial (kmsg->message, (guint32) g_variant_get_uint64 (value));
+ continue;
+
+ default:
+ g_dbus_message_set_header (kmsg->message, key, value);
+ continue;
+ }
+ }
+
+ g_variant_iter_free (fields_iter);
+
+ 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)
+ {
+ if ((kmsg->sender_euid != (uid_t) -1) && (kmsg->sender_egid != (gid_t) -1) &&
+ (kmsg->sender_seclabel != NULL))
+ {
+ gint check;
+
+ check = dbuspolicy1_check_in (worker->dbuspolicy,
+ g_dbus_message_get_destination (kmsg->message),
+ 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);
+
+ if (msg->size + item_size > KDBUS_MSG_MAX_SIZE)
+ return FALSE;
+
+ msg->size += (-msg->size) & 7;
+ item = (struct kdbus_item *) ((guchar *) msg + msg->size);
+ item->type = type;
+ item->size = item_size;
+ memcpy (item->data, data, size);
+
+ msg->size += item_size;
+
+ return TRUE;
+}
+
+static gboolean
+g_kdbus_msg_append_payload_vec (struct kdbus_msg *msg,
+ gconstpointer data,
+ gsize size)
+{
+ struct kdbus_vec vec = {
+ .size = size,
+ .address = (gsize) data
+ };
+
+ return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_VEC, &vec, sizeof vec);
+}
+
+static gboolean
+g_kdbus_msg_append_payload_memfd (struct kdbus_msg *msg,
+ gint fd,
+ gsize offset,
+ gsize size)
+{
+ struct kdbus_memfd mfd = {
+ .start = offset,
+ .size = size,
+ .fd = fd,
+ };
+
+ return g_kdbus_msg_append_item (msg, KDBUS_ITEM_PAYLOAD_MEMFD, &mfd, sizeof mfd);
+}
+
+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 = G_STRUCT_OFFSET (struct kdbus_item, bloom_filter) +
+ G_STRUCT_OFFSET (struct kdbus_bloom_filter, data) +
+ size;
+ if (msg->size + bloom_item_size > KDBUS_MSG_MAX_SIZE)
+ return NULL;
+
+ 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;
+}
+
+
+/*
+ * _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;
+ const gchar *dst_name;
+ gboolean result;
+
+ gint memfd_fd;
+ gint cancel_fd;
+
+ g_return_val_if_fail (G_IS_KDBUS_WORKER (worker), FALSE);
+
+ send = NULL;
+ send_size = sizeof(*send);
+
+ msg = alloca (KDBUS_MSG_MAX_SIZE);
+ result = TRUE;
+
+ memfd_fd = -1;
+ cancel_fd = -1;
+
+ /* fill in as we go... */
+ memset (msg, 0, sizeof (struct kdbus_msg));
+ msg->size = sizeof (struct kdbus_msg);
+ msg->payload_type = KDBUS_PAYLOAD_DBUS;
+ msg->cookie = g_dbus_message_get_serial(message);
+
+ /* Message destination */
+ 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;
+
+ /* File descriptors */
+ {
+ 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);
+ }
+ }
+
+ /* Message body */
+ {
+ struct dbus_fixed_header fh;
+ GHashTableIter header_iter;
+ GVariantBuilder builder;
+ gpointer key, value;
+ GVariant *parts[3];
+ GVariant *body;
+
+ fh.endian = (G_BYTE_ORDER == G_LITTLE_ENDIAN) ? 'l': 'B';
+ fh.type = g_dbus_message_get_message_type (message);
+ fh.flags = g_dbus_message_get_flags (message);
+ fh.version = 2;
+ fh.reserved = 0;
+ fh.serial = g_dbus_message_get_serial (message);
+ parts[0] = g_variant_new_from_data (DBUS_FIXED_HEADER_TYPE, &fh, sizeof fh, TRUE, NULL, NULL);
+
+ g_dbus_message_init_header_iter (message, &header_iter);
+ g_variant_builder_init (&builder, DBUS_EXTENDED_HEADER_TYPE);
+
+ /* We set the sender field to the correct value for ourselves */
+ g_variant_builder_add (&builder, "{tv}",
+ (guint64) G_DBUS_MESSAGE_HEADER_FIELD_SENDER,
+ g_variant_new_printf (":1.%"G_GUINT64_FORMAT, 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:
+ g_variant_builder_add (&builder, "{tv}", key_int, value);
+ /* This is a little bit gross.
+ *
+ * We must send the header part of the message in a single
+ * vector as per kdbus rules, but the GVariant serialiser
+ * code will split any item >= 128 bytes into its own
+ * vector to save the copy.
+ *
+ * No header field should be that big anyway... right?
+ */
+ g_assert_cmpint (g_variant_get_size (value), <, 128);
+ continue;
+
+ /* We send this one unconditionally, but set it ourselves */
+ case G_DBUS_MESSAGE_HEADER_FIELD_SENDER:
+ continue;
+
+ /* We don't send these at all in GVariant format */
+ case G_DBUS_MESSAGE_HEADER_FIELD_SIGNATURE:
+ case G_DBUS_MESSAGE_HEADER_FIELD_NUM_UNIX_FDS:
+ continue;
+
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ parts[1] = g_variant_builder_end (&builder);
+ if (parts[1] == NULL)
+ g_warning ("failed to make builder");
+
+ body = g_dbus_message_get_body (message);
+ if (!body)
+ body = g_variant_new ("()");
+ parts[2] = g_variant_new_variant (body);
+
+ 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
+ */
+ g_assert_cmpint (body_vectors.vectors->len, >, 0);
+ g_assert_cmpint (g_array_index (body_vectors.vectors, GVariantVector, 0).size, >=,
+ g_variant_get_size (parts[0]) + g_variant_get_size (parts[1]));
+
+ g_variant_unref (body);
+ }
+
+ {
+ guint i;
+
+ for (i = 0; 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))
+ goto need_compact;
+ }
+ } /* memfd_fd == -1 */
+ } /* use_memfd */
+
+ if (!use_memfd)
+ if (!g_kdbus_msg_append_payload_vec (msg, vector.data.pointer, vector.size))
+ goto need_compact;
+ }
+ else
+ if (!g_kdbus_msg_append_payload_vec (msg, body_vectors.extra_bytes->data + vector.data.offset, vector.size))
+ goto need_compact;
+ }
+ }
+
+ /*
+ * 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 (g_dbus_message_get_message_type (message) == G_DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ struct kdbus_bloom_filter *bloom_filter;
+
+ msg->flags |= KDBUS_MSG_SIGNAL;
+ bloom_filter = g_kdbus_msg_append_bloom (msg, worker->bloom_size);
+ if (bloom_filter == NULL)
+ goto need_compact;
+ g_kdbus_setup_bloom (worker, message, bloom_filter);
+ }
+
+ if (out_reply != NULL && cancellable)
+ {
+ cancel_fd = g_cancellable_get_fd (cancellable);
+ if (cancel_fd != -1)
+ send_size += KDBUS_ITEM_SIZE (sizeof(cancel_fd));
+ }
+
+ 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)
+ {
+ gint check;
+
+ check = dbuspolicy1_check_out (worker->dbuspolicy,
+ g_dbus_message_get_destination (message),
+ g_dbus_message_get_sender (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),
+ NULL, 0, 0);
+ if (check != DBUSPOLICY_RESULT_ALLOW)
+ {
+ switch (check)
+ {
+ case DBUSPOLICY_RESULT_DENY:
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+ "Cannot send message - message rejected due to XML security policies");
+ break;
+
+ case DBUSPOLICY_RESULT_DEST_NOT_AVAILABLE:
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
+ "Cannot send message - destination not known");
+ break;
+
+ case DBUSPOLICY_RESULT_KDBUS_ERROR:
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+ "Cannot send message - message rejected due to internal libdbuspolicy error (kdbus)");
+ break;
+
+ case DBUSPOLICY_RESULT_CYNARA_ERROR:
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+ "Cannot send message - message rejected due to internal libdbuspolicy error (Cynara)");
+ break;
+
+ default:
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED,
+ "Cannot send message - unknown libdbuspolicy error");
+ break;
+ }
+
+ result = FALSE;
+ goto out;
+ }
+ }
+ }
+#endif
+
+ /*
+ * send message
+ */
+ if (ioctl(worker->fd, KDBUS_CMD_SEND, send))
+ {
+ int ret = errno;
+ gchar *info;
+ 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));
+
+ errno = ret;
+ if (errno == ENXIO || errno == ESRCH)
+ {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN,
+ "Destination '%s' not known, %s", dst_name, 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", dst_name, 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 == 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);
+ }
+ result = FALSE;
+ free(info);
+ }
+ else 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 (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;
+
+need_compact:
+ /* We end up here if:
+ * - too many kdbus_items
+ * - too large kdbus_msg size
+ * - too much vector data
+ */
+ g_warning ("kdbus: message serialisation error");
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "message serialisation error");
+
+ if (memfd_fd != -1)
+ close (memfd_fd);
+
+ GLIB_PRIVATE_CALL(g_variant_vectors_deinit) (&body_vectors);
+ return FALSE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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;
+ worker->attach_flags_recv = _KDBUS_ATTACH_ALL;
+
+ 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;
+}
+
+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;
+}
+
+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))
+ {
+ SyntheticReplyData *data;
+
+ data = g_new0 (SyntheticReplyData, 1);
+
+ data->worker = worker;
+ data->message = _dbus_daemon_synthetic_reply (worker, message);
+
+ 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);
+ 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,
- g_simple_async_result_complete_in_idle (result);
++ GTask *task)
+{
+ worker->disconnected_callback (FALSE, NULL, worker->user_data);
++ g_task_return_boolean (task, TRUE);
+}
--- /dev/null
- reply = g_dbus_message_new_method_error (message, dbus_error_name, local_error->message);
+/* 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)
+{
+ 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);
+
+ status = _g_kdbus_StartServiceByName (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 '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;
+}