From: Adrian Szyndela Date: Thu, 25 May 2017 09:50:11 +0000 (+0200) Subject: Merge tag 'upstream/2.52.2' into tizen X-Git-Tag: accepted/tizen/unified/20170612.073709~7 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=11ee389c920bef9a70424aca70def23826064d24;p=platform%2Fupstream%2Fglib.git Merge tag 'upstream/2.52.2' into tizen upstream/2.52.2 Change-Id: I60b56ce4eaa45877acc7e53ac4551d0a31234f7e --- 11ee389c920bef9a70424aca70def23826064d24 diff --cc NEWS index 1a649cd,e0a5835..1364860 --- a/NEWS +++ b/NEWS @@@ -1,40 -1,1142 +1,1179 @@@ + 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 or + 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 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 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 + #include + #include #include #endif @@@ -664,10 -646,10 +672,10 @@@ g_dbus_address_connect (const gchar * else if (g_strcmp0 (address_entry, "autolaunch:") == 0) { gchar *autolaunch_address; - autolaunch_address = get_session_address_platform_specific (error); + autolaunch_address = get_session_address_dbus_launch (error); if (autolaunch_address != NULL) { - ret = g_dbus_address_try_connect_one (autolaunch_address, NULL, cancellable, error); + ret = g_dbus_address_try_connect_one (autolaunch_address, kdbus_okay, NULL, cancellable, error); g_free (autolaunch_address); goto out; } @@@ -1529,11 -1579,11 +1626,12 @@@ g_dbus_address_get_for_bus_sync (GBusTy if (G_UNLIKELY (_g_dbus_debug_address ())) { guint n; + gchar *s; _g_dbus_debug_print_lock (); s = _g_dbus_enum_to_string (G_TYPE_BUS_TYPE, bus_type); - g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n", s); - g_free(s); + g_print ("GDBus-debug:Address: In g_dbus_address_get_for_bus_sync() for bus type '%s'\n", + s); + g_free (s); for (n = 0; n < 3; n++) { const gchar *k; diff --cc gio/gdbusconnection.c index 2193ea6,7c88a4e..a9d75d4 mode 100755,100644..100755 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@@ -119,10 -119,9 +119,10 @@@ #include "gasyncinitable.h" #include "giostream.h" #include "gasyncresult.h" - #include "gsimpleasyncresult.h" + #include "gtask.h" #ifdef G_OS_UNIX +#include "gkdbus.h" #include "gunixconnection.h" #include "gunixfdmessage.h" #endif @@@ -1513,26 -1444,12 +1485,21 @@@ g_dbus_connection_close (GDBusConnectio if (!check_initialized (connection)) return; - simple = g_simple_async_result_new (G_OBJECT (connection), - callback, - user_data, - g_dbus_connection_close); - g_simple_async_result_set_check_cancellable (simple, cancellable); - g_assert (connection->worker != NULL); -- + task = g_task_new (connection, cancellable, callback, user_data); + g_task_set_source_tag (task, g_dbus_connection_close); - _g_dbus_worker_close (connection->worker, task); + if (connection->worker) + { - _g_dbus_worker_close (connection->worker, cancellable, simple); ++ _g_dbus_worker_close (connection->worker, task); + } +#ifdef G_OS_UNIX + else if (connection->kdbus_worker) + { - _g_kdbus_worker_close (connection->kdbus_worker, cancellable, simple); ++ _g_kdbus_worker_close (connection->kdbus_worker, task); + } +#endif + else + g_assert_not_reached(); - - g_object_unref (simple); + g_object_unref (task); } /** @@@ -2661,64 -1901,42 +2590,44 @@@ g_dbus_connection_send_message_with_rep if (out_serial == NULL) out_serial = &serial; - simple = g_simple_async_result_new (G_OBJECT (connection), - callback, - user_data, - g_dbus_connection_send_message_with_reply); - g_simple_async_result_set_check_cancellable (simple, cancellable); - if (timeout_msec == -1) - timeout_msec = 25 * 1000; - + data = g_slice_new0 (SendMessageData); + task = g_task_new (connection, cancellable, callback, user_data); + g_task_set_source_tag (task, + g_dbus_connection_send_message_with_reply_unlocked); + g_task_set_task_data (task, data, (GDestroyNotify) send_message_data_free); - if (g_cancellable_is_cancelled (cancellable)) + if (g_task_return_error_if_cancelled (task)) { - g_simple_async_result_set_error (simple, - G_IO_ERROR, - G_IO_ERROR_CANCELLED, - _("Operation was cancelled")); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - goto out; + g_object_unref (task); + return; } - error = NULL; - if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error)) + if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error, timeout_msec)) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - goto out; + g_task_return_error (task, error); + g_object_unref (task); + return; } - - data = g_new0 (SendMessageData, 1); - data->ref_count = 1; - data->connection = g_object_ref (connection); - data->simple = simple; data->serial = *out_serial; - data->main_context = g_main_context_ref_thread_default (); if (cancellable != NULL) { - data->cancellable = g_object_ref (cancellable); data->cancellable_handler_id = g_cancellable_connect (cancellable, G_CALLBACK (send_message_with_reply_cancelled_cb), - send_message_data_ref (data), - (GDestroyNotify) send_message_data_unref); + g_object_ref (task), + g_object_unref); } - if (timeout_msec != G_MAXINT) + if (timeout_msec != G_MAXINT +#ifdef G_OS_UNIX + /* Userspace timeouts unnecessary on unix/kdbus - kdbus handles timeouts. */ + && !connection->kdbus_worker +#endif + ) { - data->timeout_source = g_timeout_source_new (timeout_msec); + data->timeout_source = g_timeout_source_new (timeout_msec == -1 ? DBUS_DEFAULT_TIMEOUT_MSEC : timeout_msec); - g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); - g_source_set_callback (data->timeout_source, - send_message_with_reply_timeout_cb, - send_message_data_ref (data), - (GDestroyNotify) send_message_data_unref); - g_source_attach (data->timeout_source, data->main_context); + g_task_attach_source (task, data->timeout_source, + (GSourceFunc) send_message_with_reply_timeout_cb); g_source_unref (data->timeout_source); } @@@ -2931,100 -2134,32 +2825,100 @@@ g_dbus_connection_send_message_with_rep g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - reply = NULL; - - data.res = NULL; - data.context = g_main_context_new (); - data.loop = g_main_loop_new (data.context, FALSE); + if (connection->worker) + { - data = g_new0 (SendMessageSyncData, 1); - data->context = g_main_context_new (); - data->loop = g_main_loop_new (data->context, FALSE); ++ data.res = NULL; ++ data.context = g_main_context_new (); ++ data.loop = g_main_loop_new (data.context, FALSE); - g_main_context_push_thread_default (data->context); - g_main_context_push_thread_default (data.context); ++ g_main_context_push_thread_default (data.context); - g_dbus_connection_send_message_with_reply (connection, - message, - flags, - timeout_msec, - out_serial, - cancellable, - (GAsyncReadyCallback) send_message_with_reply_sync_cb, - &data); - g_main_loop_run (data.loop); - reply = g_dbus_connection_send_message_with_reply_finish (connection, - data.res, - error); + g_dbus_connection_send_message_with_reply (connection, + message, + flags, + timeout_msec, + out_serial, + cancellable, + (GAsyncReadyCallback) send_message_with_reply_sync_cb, - data); - g_main_loop_run (data->loop); ++ &data); ++ g_main_loop_run (data.loop); + reply = g_dbus_connection_send_message_with_reply_finish (connection, - data->res, ++ data.res, + error); + - g_main_context_pop_thread_default (data->context); ++ g_main_context_pop_thread_default (data.context); + - g_main_context_unref (data->context); - g_main_loop_unref (data->loop); - g_object_unref (data->res); - g_free (data); ++ g_main_context_unref (data.context); ++ g_main_loop_unref (data.loop); ++ if (data.res) ++ g_object_unref (data.res); + } +#ifdef G_OS_UNIX + else if (connection->kdbus_worker) + { + /* kdbus supports blocking synchronous calls, so let's use them instead of mainloops */ + + volatile guint32 serial; + guint32 serial_to_use; - g_main_context_pop_thread_default (data.context); ++ reply = NULL; ++ + CONNECTION_LOCK (connection); + + if (out_serial == NULL) + out_serial = &serial; + else + *out_serial = 0; - g_main_context_unref (data.context); - g_main_loop_unref (data.loop); - if (data.res) - g_object_unref (data.res); + /* + * Check that we never actually send a message if the GCancellable + * is already cancelled + */ + if (g_cancellable_is_cancelled (cancellable)) + { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CANCELLED, + _("Operation was cancelled")); + CONNECTION_UNLOCK (connection); + goto out; + } + if (!check_unclosed (connection, + (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0, + error)) + { + CONNECTION_UNLOCK (connection); + goto out; + } + + if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)) + g_dbus_message_set_serial (message, ++connection->last_serial); + + serial_to_use = g_dbus_message_get_serial (message); + + g_dbus_message_lock (message); + + if (out_serial != NULL) + *out_serial = serial_to_use; + + CONNECTION_UNLOCK (connection); + + /* Reference is still kept for the connection, so there is no worry + * that it will be freed. Same for connection->kdbus_worker - by reference + * from the connection. Inside _g_kdbus_worker_send_message_sync only + * initialized-once, read-only fields from kdbus_worker are used, except + * 'matches', which now has got its own mutex. + */ + _g_kdbus_worker_send_message_sync (connection->kdbus_worker, message, + &reply, timeout_msec, cancellable, error); + + } +#endif /* G_OS_UNIX */ + else + g_assert_not_reached (); + +out: return reply; } @@@ -3042,11 -2172,52 +2931,51 @@@ typedef struc GDBusMessageFilterFunction filter_function; gpointer user_data; GDestroyNotify user_data_free_func; + GMainContext *context; } FilterData; + /* requires CONNECTION_LOCK */ + static FilterData ** + copy_filter_list (GPtrArray *filters) + { + FilterData **copy; + guint n; + + copy = g_new (FilterData *, filters->len + 1); + for (n = 0; n < filters->len; n++) + { + copy[n] = filters->pdata[n]; + copy[n]->ref_count++; + } + copy[n] = NULL; + + return copy; + } + + /* requires CONNECTION_LOCK */ + static void + free_filter_list (FilterData **filters) + { + guint n; + + for (n = 0; filters[n]; n++) + { + filters[n]->ref_count--; + if (filters[n]->ref_count == 0) + { + call_destroy_notify (filters[n]->context, + filters[n]->user_data_free_func, + filters[n]->user_data); + g_main_context_unref (filters[n]->context); + g_free (filters[n]); + } + } + g_free (filters); + } + /* Called in GDBusWorker's thread - we must not block - with no lock held */ static void -on_worker_message_received (GDBusWorker *worker, - GDBusMessage *message, +on_worker_message_received (GDBusMessage *message, gpointer user_data) { GDBusConnection *connection; @@@ -4383,10 -3480,8 +4347,10 @@@ g_dbus_connection_signal_subscribe_with subscriber.callback = callback; subscriber.user_data = user_data; subscriber.user_data_free_func = user_data_free_func; - subscriber.id = _global_subscriber_id++; /* TODO: overflow etc. */ + subscriber.id = g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */ subscriber.context = g_main_context_ref_thread_default (); + subscriber.user_data_ref_func = user_data_ref_func; + subscriber.user_data_unref_func = user_data_unref_func; /* see if we've already have this rule */ signal_data = g_hash_table_lookup (connection->map_rule_to_signal_data, rule); @@@ -5596,9 -4618,9 +5560,9 @@@ handle_get_all_properties (GDBusConnect GDBusMessage *reply; reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("No such interface"), + _("No such interface '%s'"), interface_name); - g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL, -1); g_object_unref (reply); handled = TRUE; goto out; diff --cc gio/gdbusnamewatching.c index d287c1a,14603c5..6ac4aa8 mode 100755,100644..100755 --- a/gio/gdbusnamewatching.c +++ b/gio/gdbusnamewatching.c diff --cc gio/gkdbus.c index 03d1f54,0000000..4549dfe mode 100755,000000..100755 --- a/gio/gkdbus.c +++ b/gio/gkdbus.c @@@ -1,3871 -1,0 +1,3870 @@@ +/* 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 + * Author: Adrian Szyndela + * Author: Michal Eljasiewicz + */ + +#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 +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_FILIO_H +#include +#endif + +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef LIBDBUSPOLICY +#include +#endif + +#define DBUS_DAEMON_EMULATION +#ifdef DBUS_DAEMON_EMULATION +#include "gkdbusfakedaemon.h" +#endif + +#include +#include +#include +#include + +#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, - GCancellable *cancellable, - GSimpleAsyncResult *result) ++ GTask *task) +{ + worker->disconnected_callback (FALSE, NULL, worker->user_data); - g_simple_async_result_complete_in_idle (result); ++ g_task_return_boolean (task, TRUE); +} diff --cc gio/gkdbus.h index 8a4a831,0000000..34e4410 mode 100644,000000..100644 --- a/gio/gkdbus.h +++ b/gio/gkdbus.h @@@ -1,200 -1,0 +1,199 @@@ +/* 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 + * Author: Michal Eljasiewicz + */ + +#ifndef __G_KDBUS_H__ +#define __G_KDBUS_H__ + +#if !defined (GIO_COMPILATION) +#error "gkdbus.h is a private header file." +#endif + +#include +#include "gdbusprivate.h" + +#define G_TYPE_KDBUS_WORKER (g_kdbus_worker_get_type ()) +#define G_KDBUS_WORKER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_KDBUS_WORKER, GKDBusWorker)) +#define G_KDBUS_WORKER_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \ + G_TYPE_KDBUS_WORKER, GKDBusWorkerClass)) +#define G_IS_KDBUS_WORKER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_KDBUS_WORKER)) +#define G_IS_KDBUS_WORKER_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), \ + G_TYPE_KDBUS_WORKER)) +#define G_KDBUS_WORKER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \ + G_TYPE_KDBUS_WORKER, GKDBusWorkerClass)) +typedef enum { + G_DBUS_CREDS_NONE = 0, + G_DBUS_CREDS_PID = (1<<0), + G_DBUS_CREDS_UID = (1<<1), + G_DBUS_CREDS_UNIQUE_NAME = (1<<2), + G_DBUS_CREDS_SEC_LABEL = (1<<3) +} GDBusCredentialsFlags; + +typedef struct +{ + guint pid; + guint uid; + gchar *unique_name; + gchar *sec_label; +} GDBusCredentials; + +typedef struct +{ + GDBusMessage *message; + uid_t sender_euid; + gid_t sender_egid; + gchar *sender_seclabel; + gchar *sender_names; +} GKDBusMessage; + +typedef struct _GKDBusWorker GKDBusWorker; + +void _g_kdbus_worker_associate (GKDBusWorker *worker, + GDBusCapabilityFlags capabilities, + GDBusWorkerMessageReceivedCallback message_received_callback, + GDBusWorkerMessageAboutToBeSentCallback message_about_to_be_sent_callback, + GDBusWorkerDisconnectedCallback disconnected_callback, + gpointer user_data); + +GType g_kdbus_worker_get_type (void); + +GKDBusWorker * _g_kdbus_worker_new (const gchar *address, + GError **error); + +void _g_kdbus_worker_unfreeze (GKDBusWorker *worker); + +gboolean _g_kdbus_worker_send_message (GKDBusWorker *worker, + GDBusMessage *message, + gint timeout_msec, + GError **error); + +gboolean _g_kdbus_worker_send_message_sync (GKDBusWorker *worker, + GDBusMessage *message, + GDBusMessage **out_reply, + gint timeout_msec, + GCancellable *cancellable, + GError **error); + +void _g_kdbus_worker_stop (GKDBusWorker *worker); + +gboolean _g_kdbus_worker_flush_sync (GKDBusWorker *worker); + +void _g_kdbus_worker_close (GKDBusWorker *worker, - GCancellable *cancellable, - GSimpleAsyncResult *result); ++ GTask *task); + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean _g_kdbus_open (GKDBusWorker *worker, + const gchar *address, + GError **error); + +gboolean _g_kdbus_close (GKDBusWorker *worker); + +gboolean _g_kdbus_is_closed (GKDBusWorker *worker); + +/* ---------------------------------------------------------------------------------------------------- */ + +const gchar * _g_kdbus_Hello (GKDBusWorker *worker, + GError **error); + +gchar * _g_kdbus_GetBusId (GKDBusWorker *worker, + GError **error); + +GBusRequestNameReplyFlags _g_kdbus_RequestName (GKDBusWorker *worker, + const gchar *name, + GBusNameOwnerFlags flags, + GError **error); + +GBusReleaseNameReplyFlags _g_kdbus_ReleaseName (GKDBusWorker *worker, + const gchar *name, + GError **error); + +gchar ** _g_kdbus_GetListNames (GKDBusWorker *worker, + gboolean activatable, + GError **error); + +gchar ** _g_kdbus_GetListQueuedOwners (GKDBusWorker *worker, + const gchar *name, + GError **error); + +gboolean _g_kdbus_NameHasOwner (GKDBusWorker *connection, + const gchar *name, + GError **error); + +gchar * _g_kdbus_GetNameOwner (GKDBusWorker *worker, + const gchar *name, + GError **error); + +pid_t _g_kdbus_GetConnectionUnixProcessID (GKDBusWorker *worker, + const gchar *name, + GError **error); + +uid_t _g_kdbus_GetConnectionUnixUser (GKDBusWorker *worker, + const gchar *name, + GError **error); + +gchar * _g_kdbus_GetConnectionSecurityLabel (GKDBusWorker *worker, + const gchar *name, + GError **error); + +GBusStartServiceReplyFlags _g_kdbus_StartServiceByName (GKDBusWorker *worker, + const gchar *name, + guint32 flags, + GError **error); + +gboolean _g_kdbus_AddMatch (GKDBusWorker *worker, + const gchar *match_rule, + GError **error); + +gboolean _g_kdbus_RemoveMatch (GKDBusWorker *worker, + const gchar *match_rule, + GError **error); + +GDBusCredentials * _g_kdbus_GetConnInfo (GKDBusWorker *worker, + const gchar *name, + guint flags, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + +gboolean _g_kdbus_subscribe_name_acquired (GKDBusWorker *worker, + const gchar *match_rule, + const gchar *name, + GError **error); + +gboolean _g_kdbus_subscribe_name_lost (GKDBusWorker *worker, + const gchar *match_rule, + const gchar *name, + GError **error); + +gboolean _g_kdbus_subscribe_name_owner_changed (GKDBusWorker *worker, + const gchar *match_rule, + const gchar *name, + GError **error); + +/* ---------------------------------------------------------------------------------------------------- */ + +G_END_DECLS + +#endif /* __G_KDBUS_H__ */ diff --cc gio/gkdbusfakedaemon.c index a6d8135,0000000..c13bf49 mode 100644,000000..100644 --- a/gio/gkdbusfakedaemon.c +++ b/gio/gkdbusfakedaemon.c @@@ -1,684 -1,0 +1,684 @@@ +/* 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 + */ + +#include "config.h" +#include "gkdbus.h" +#include "gkdbusfakedaemon.h" + +#include +#include + +static gchar *introspect = + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\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, local_error->message); ++ reply = g_dbus_message_new_method_error (message, dbus_error_name, "%s", local_error->message); + g_free (dbus_error_name); + } + else + { + reply = g_dbus_message_new_method_reply (message); + g_dbus_message_set_body (reply, reply_body); + } + + g_dbus_message_set_serial (reply, -1); + + if (local_error) + g_error_free (local_error); + + if (G_UNLIKELY (_g_dbus_debug_message ())) + { + gchar *s; + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Message:\n" + " <<<< RECEIVED synthetic D-Bus message\n"); + s = g_dbus_message_print (reply, 2); + g_print ("%s", s); + g_free (s); + _g_dbus_debug_print_unlock (); + } + + return reply; +} diff --cc glib/gbytes.c index e8de538,b9ca4eb..11f5159 --- a/glib/gbytes.c +++ b/glib/gbytes.c @@@ -74,78 -66,16 +74,78 @@@ struct _GBytes { - gconstpointer data; /* may be NULL iff (size == 0) */ - gsize size; /* may be 0 */ - gint ref_count; - GDestroyNotify free_func; - gpointer user_data; + gsize size; + gint ref_count; + gint type_or_fd; }; +typedef struct +{ + GBytes bytes; +#if GLIB_SIZEOF_SIZE_T == 4 + guint pad; +#endif + + guchar data[1]; +} GBytesInline; + +/* important: the ->data field of GBytesInline should always be 'nicely + * aligned'. + */ +G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % (2 * sizeof (gpointer)) == 0); +G_STATIC_ASSERT (G_STRUCT_OFFSET (GBytesInline, data) % 8 == 0); + + +typedef struct +{ + GBytes bytes; + + gpointer data; +} GBytesData; + +typedef struct +{ + GBytesData data_bytes; + + GDestroyNotify notify; + gpointer user_data; +} GBytesNotify; + +#define G_BYTES_TYPE_INLINE (-1) +#define G_BYTES_TYPE_STATIC (-2) +#define G_BYTES_TYPE_FREE (-3) +#define G_BYTES_TYPE_NOTIFY (-4) + +/* All bytes are either inline or subtypes of GBytesData */ +#define G_BYTES_IS_INLINE(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_INLINE) +#define G_BYTES_IS_DATA(bytes) (!G_BYTES_IS_INLINE(bytes)) + +/* More specific subtypes of GBytesData */ +#define G_BYTES_IS_STATIC(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_STATIC) +#define G_BYTES_IS_FREE(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_FREE) +#define G_BYTES_IS_NOTIFY(bytes) ((bytes)->type_or_fd == G_BYTES_TYPE_NOTIFY) + +/* we have a memfd if type_or_fd >= 0 */ +#define G_BYTES_IS_MEMFD(bytes) ((bytes)->type_or_fd >= 0) + +static gpointer +g_bytes_allocate (guint struct_size, + guint type_or_fd, + gsize data_size) +{ + GBytes *bytes; + + bytes = g_slice_alloc (struct_size); + bytes->size = data_size; + bytes->ref_count = 1; + bytes->type_or_fd = type_or_fd; + + return bytes; +} + /** * g_bytes_new: - * @data: (transfer none) (array length=size) (element-type guint8) (allow-none): + * @data: (transfer none) (array length=size) (element-type guint8) (nullable): * the data to be used for the bytes * @size: the size of @data * @@@ -172,67 -97,8 +172,67 @@@ g_bytes_new (gconstpointer data } /** + * g_bytes_new_take_zero_copy_fd: + * @fd: a file descriptor capable of being zero-copy-safe + * + * Creates a new #GBytes from @fd. + * + * @fd must be capable of being made zero-copy-safe. In concrete terms, + * this means that a call to g_unix_fd_ensure_zero_copy_safe() on @fd + * will succeed. This call will be made before returning. + * + * This call consumes @fd, transferring ownership to the returned + * #GBytes. + * + * Returns: (transfer full): a new #GBytes + * + */ +#ifdef G_OS_UNIX +GBytes * +g_bytes_new_take_zero_copy_fd (gint fd) +{ + GBytesData *bytes; + struct stat buf; + + /* We already checked this is a memfd... */ + g_assert_se (fstat (fd, &buf) == 0); + + bytes = g_bytes_new_take_zero_copy_fd_size(fd, buf.st_size); + + if (buf.st_size == 0) + { + g_assert_se (close (fd) == 0); + } + + return (GBytes *) bytes; +} + +GBytes * +g_bytes_new_take_zero_copy_fd_size (gint fd, gsize size) +{ + GBytesData *bytes; + + g_return_val_if_fail_se (g_unix_fd_ensure_zero_copy_safe (fd), NULL); + + /* We already checked this is a memfd... */ + if (size == 0) + { + return g_bytes_new (NULL, 0); + } + + bytes = g_bytes_allocate (sizeof (GBytesData), fd, size); + bytes->data = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (bytes->data == MAP_FAILED) + /* this is similar to malloc() failing, so do the same... */ + g_error ("mmap() on memfd failed: %s\n", g_strerror (errno)); + + return (GBytes *) bytes; +} +#endif /* G_OS_UNIX */ + +/** * g_bytes_new_take: - * @data: (transfer full) (array length=size) (element-type guint8) (allow-none): + * @data: (transfer full) (array length=size) (element-type guint8) (nullable): the data to be used for the bytes * @size: the size of @data * diff --cc glib/glib-private.c index 7658ec6,3dbf744..ba1b0ac --- a/glib/glib-private.c +++ b/glib/glib-private.c @@@ -46,9 -47,7 +47,11 @@@ glib__private__ (void g_dir_open_with_errno, g_dir_new_from_dirp, - glib_init, + g_variant_to_vectors, + g_variant_from_vectors, - g_variant_vectors_deinit ++ g_variant_vectors_deinit, ++ ++ glib_init }; return &table; diff --cc glib/glib-private.h index 1b8daf6,198e07f..0f6a472 --- a/glib/glib-private.h +++ b/glib/glib-private.h @@@ -69,15 -61,9 +69,18 @@@ typedef struct guint flags); GDir * (* g_dir_new_from_dirp) (gpointer dirp); + void (* g_variant_to_vectors) (GVariant *value, + GVariantVectors *vectors); + GVariant * (* g_variant_from_vectors) (const GVariantType *type, + GVariantVector *vectors, + gsize n_vectors, + gsize size, + gboolean trusted); + void (* g_variant_vectors_deinit) (GVariantVectors *vectors); + + /* See glib-init.c */ + void (* glib_init) (void); + /* Add other private functions here, initialize them in glib-private.c */ } GLibPrivateVTable; diff --cc glib/gtestutils.c index 9a996dd,e06256d..080528b --- a/glib/gtestutils.c +++ b/glib/gtestutils.c @@@ -380,26 -380,8 +380,27 @@@ * an error message is logged and the application is terminated. * * The macro can be turned off in final releases of code by defining - * `G_DISABLE_ASSERT` when compiling the application. + * `G_DISABLE_ASSERT` when compiling the application, so code must + * not depend on any side effects from @expr. + * + * For a version which is guaranteed to evaluate side effects in @expr, + * see g_assert_se(). + */ + +/** + * g_assert_se: + * @expr: the expression to check + * + * Debugging macro to terminate the application if the assertion + * fails. If the assertion fails (i.e. the expression is not true), + * an error message is logged and the application is terminated. + * + * The check can be turned off in final releases of code by defining + * `G_DISABLE_ASSERT` when compiling the application. + * + * Unlike g_assert(), this macro is guaranteed to evaluate side effects + * of @expr, even if checks are disabled. It is still undefined if the + * program will actually be aborted or not. */ /** diff --cc glib/tests/gvariant.c index 3d38b4f,dc738e0..cdbd916 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@@ -4612,81 -4533,72 +4611,147 @@@ G_GNUC_BEGIN_IGNORE_DEPRECATION G_GNUC_END_IGNORE_DEPRECATIONS } +static GByteArray * +flatten_vectors (GVariantVectors *v) +{ + GByteArray *result; + guint i; + + result = g_byte_array_new (); + + for (i = 0; i < v->vectors->len; i++) + { + GVariantVector vec = g_array_index (v->vectors, GVariantVector, i); + + if (vec.gbytes) + g_byte_array_append (result, vec.data.pointer, vec.size); + else + g_byte_array_append (result, v->extra_bytes->data + vec.data.offset, vec.size); + } + + return result; +} + +static void +test_vector_serialiser (void) +{ + GVariantVectors vectors; + GByteArray *flattened; + GVariant *value; + guint i; + + for (i = 0; i < 100; i++) + { + guint j; + + value = create_random_gvariant (2); + //g_print (">>> %s\n", g_variant_print (value, TRUE)); + + GLIB_PRIVATE_CALL(g_variant_to_vectors) (value, &vectors); + for (j = 0; j < vectors.vectors->len; j++) + { + GVariantVector *v = &g_array_index (vectors.vectors, GVariantVector, j); + + if (!v->gbytes) + { + v->gbytes = g_bytes_new (NULL, 0); + v->data.pointer = v->data.offset + vectors.extra_bytes->data; + } + + //g_print (" V %p %p %d\n", v, v->data.pointer, (guint) v->size); + } + GLIB_PRIVATE_CALL(g_variant_from_vectors) (g_variant_get_type (value), (GVariantVector *) vectors.vectors->data, vectors.vectors->len, g_variant_get_size (value), TRUE); + continue; + flattened = flatten_vectors (&vectors); + g_byte_array_free (vectors.extra_bytes, TRUE); + g_byte_array_free (vectors.offsets, TRUE); + g_array_free (vectors.vectors, TRUE); + +#if 0 + if (flattened->len != g_variant_get_size (value) || + memcmp (flattened->data, g_variant_get_data (value), flattened->len) != 0) + { + g_file_set_contents ("flattened", flattened->data, flattened->len, NULL); + g_file_set_contents ("serialised", g_variant_get_data (value), g_variant_get_size (value), NULL); + g_print ("type is %s\n", g_variant_get_type_string (value)); + g_assert_not_reached (); + } +#endif + + g_assert_cmpint (flattened->len, ==, g_variant_get_size (value)); + g_assert (memcmp (flattened->data, g_variant_get_data (value), flattened->len) == 0); + + g_byte_array_free (flattened, TRUE); + g_variant_unref (value); + } +} + + static void + test_stack_builder_init (void) + { + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_BYTESTRING); + GVariant *variant; + + g_variant_builder_add_value (&builder, g_variant_new_byte ('g')); + g_variant_builder_add_value (&builder, g_variant_new_byte ('l')); + g_variant_builder_add_value (&builder, g_variant_new_byte ('i')); + g_variant_builder_add_value (&builder, g_variant_new_byte ('b')); + g_variant_builder_add_value (&builder, g_variant_new_byte ('\0')); + + variant = g_variant_ref_sink (g_variant_builder_end (&builder)); + g_assert_nonnull (variant); + g_assert (g_variant_type_equal (g_variant_get_type (variant), + G_VARIANT_TYPE_BYTESTRING)); + g_assert_cmpuint (g_variant_n_children (variant), ==, 5); + g_assert_cmpstr (g_variant_get_bytestring (variant), ==, "glib"); + g_variant_unref (variant); + } + + static GVariant * + get_asv (void) + { + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); + + g_variant_builder_add (&builder, "{s@v}", "foo", g_variant_new_variant (g_variant_new_string ("FOO"))); + g_variant_builder_add (&builder, "{s@v}", "bar", g_variant_new_variant (g_variant_new_string ("BAR"))); + + return g_variant_ref_sink (g_variant_builder_end (&builder)); + } + + static void + test_stack_dict_init (void) + { + GVariant *asv = get_asv (); + GVariantDict dict = G_VARIANT_DICT_INIT (asv); + GVariant *variant; + GVariantIter iter; + gchar *key; + GVariant *value; + + g_variant_dict_insert_value (&dict, "baz", g_variant_new_string ("BAZ")); + g_variant_dict_insert_value (&dict, "quux", g_variant_new_string ("QUUX")); + + variant = g_variant_ref_sink (g_variant_dict_end (&dict)); + g_assert_nonnull (variant); + g_assert (g_variant_type_equal (g_variant_get_type (variant), + G_VARIANT_TYPE_VARDICT)); + g_assert_cmpuint (g_variant_n_children (variant), ==, 4); + + g_variant_iter_init (&iter, variant); + while (g_variant_iter_next (&iter, "{sv}", &key, &value)) + { + gchar *strup = g_ascii_strup (key, -1); + + g_assert_cmpstr (strup, ==, g_variant_get_string (value, NULL)); + g_free (key); + g_free (strup); + g_variant_unref (value); + } + + g_variant_unref (asv); + g_variant_unref (variant); + } + int main (int argc, char **argv) { @@@ -4745,7 -4657,8 +4810,9 @@@ g_test_add_func ("/gvariant/gbytes", test_gbytes); g_test_add_func ("/gvariant/print-context", test_print_context); g_test_add_func ("/gvariant/error-quark", test_error_quark); + g_test_add_func ("/gvariant/vector-serialiser", test_vector_serialiser); + g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init); + g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init); return g_test_run (); }