diff options
author | Simon McVittie <smcv@debian.org> | 2014-11-06 21:06:37 +0000 |
---|---|---|
committer | Simon McVittie <smcv@debian.org> | 2014-11-06 21:06:37 +0000 |
commit | 3d6d7094d5f391a287db6eb9556def65755a3463 (patch) | |
tree | d1e6f0c8f950472b0dc73fcf1cd497e8d33c8967 | |
parent | 0146d178c9dfab0c6176571a377d2db50d6f9621 (diff) | |
parent | 19ab6f3e7a06a8d6e6390fbe979858371e269bb9 (diff) | |
download | dbus-3d6d7094d5f391a287db6eb9556def65755a3463.tar.gz |
Imported Upstream version 1.9.2upstream/1.9.2
57 files changed, 2283 insertions, 1127 deletions
diff --git a/Makefile.in b/Makefile.in index 8cb80061..c38b798f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -107,6 +107,7 @@ DIST_COMMON = $(srcdir)/tools/lcov.am INSTALL NEWS README AUTHORS \ $(srcdir)/dbus-1.pc.in $(srcdir)/dbus-1-uninstalled.pc.in \ $(top_srcdir)/test/data/valid-config-files/debug-allow-all.conf.in \ $(top_srcdir)/test/data/valid-config-files/debug-allow-all-sha1.conf.in \ + $(top_srcdir)/test/data/valid-config-files/finite-timeout.conf.in \ $(top_srcdir)/test/data/valid-config-files/incoming-limit.conf.in \ $(top_srcdir)/test/data/valid-config-files-system/debug-allow-all-pass.conf.in \ $(top_srcdir)/test/data/valid-config-files-system/debug-allow-all-fail.conf.in \ @@ -123,8 +124,8 @@ DIST_COMMON = $(srcdir)/tools/lcov.am INSTALL NEWS README AUTHORS \ $(top_srcdir)/test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \ $(top_srcdir)/test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service.in \ $(top_srcdir)/test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \ - COPYING compile config.guess config.sub install-sh missing \ - ltmain.sh + COPYING compile config.guess config.sub depcomp install-sh \ + missing ltmain.sh subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ @@ -143,6 +144,7 @@ CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = Doxyfile dbus-1.pc dbus-1-uninstalled.pc \ test/data/valid-config-files/debug-allow-all.conf \ test/data/valid-config-files/debug-allow-all-sha1.conf \ + test/data/valid-config-files/finite-timeout.conf \ test/data/valid-config-files/incoming-limit.conf \ test/data/valid-config-files-system/debug-allow-all-pass.conf \ test/data/valid-config-files-system/debug-allow-all-fail.conf \ @@ -456,6 +458,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -583,6 +586,8 @@ test/data/valid-config-files/debug-allow-all.conf: $(top_builddir)/config.status cd $(top_builddir) && $(SHELL) ./config.status $@ test/data/valid-config-files/debug-allow-all-sha1.conf: $(top_builddir)/config.status $(top_srcdir)/test/data/valid-config-files/debug-allow-all-sha1.conf.in cd $(top_builddir) && $(SHELL) ./config.status $@ +test/data/valid-config-files/finite-timeout.conf: $(top_builddir)/config.status $(top_srcdir)/test/data/valid-config-files/finite-timeout.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $@ test/data/valid-config-files/incoming-limit.conf: $(top_builddir)/config.status $(top_srcdir)/test/data/valid-config-files/incoming-limit.conf.in cd $(top_builddir) && $(SHELL) ./config.status $@ test/data/valid-config-files-system/debug-allow-all-pass.conf: $(top_builddir)/config.status $(top_srcdir)/test/data/valid-config-files-system/debug-allow-all-pass.conf.in @@ -1,3 +1,77 @@ +D-Bus 1.9.2 (2014-11-10) +== + +The “structurally unsound flapjack” release. + +Security fixes: + +• Increase dbus-daemon's RLIMIT_NOFILE rlimit to 65536 + so that CVE-2014-3636 part A cannot exhaust the system bus' + file descriptors, completing the incomplete fix in 1.8.8. + (CVE-2014-7824, fd.o #85105; Simon McVittie, Alban Crequy) + +Enhancements: + +• D-Bus Specification version 0.25 + · new value 'const' for EmitsChangedSignal annotation + (fd.o #72958, Lennart Poettering) + · new ALLOW_INTERACTIVE_AUTHORIZATION flag, for PolicyKit and similar + (fd.o #83449; Lennart Poettering, Simon McVittie) + · annotate table of types with reserved/basic/container, and for + basic types, fixed/string-like + · clarify arbitrary limits by quoting them in mebibytes + +• New API: add accessors for the ALLOW_INTERACTIVE_AUTHORIZATION flag + (fd.o #83449, Simon McVittie) + +• Add dbus-test-tool, a D-Bus swiss army knife with multiple subcommands, + useful for debugging and performance testing: + · dbus-test-tool spam: send repeated messages + · dbus-test-tool echo: send an empty reply for all method calls + · dbus-test-tool black-hole: do not reply to method calls + (fd.o #34140; Alban Crequy, Simon McVittie, Will Thompson) + +• Add support for process ID in credentials-passing on NetBSD + (fd.o #69702, Patrick Welche) + +• Add an example script to find potentially undesired match rules + (fd.o #84598, Alban Crequy) + +• Document the central assumption that makes our use of credentials-passing + secure (fd.o #83499, Simon McVittie) + +• Replace the dbus-glib section of the tutorial with a GDBus recommendation, + and add some links to GDBus and QtDBus documentation (fd.o #25140, + Simon McVittie) + +Fixes: + +• Use a less confusing NoReply message when disconnected with a reply pending + (fd.o #76112, Simon McVittie) + +• Make the .pc file relocatable by letting pkg-config do all variable + expansion itself (fd.o #75858, Руслан Ижбулатов) + +• Fix a build failure on platforms with kqueue, which regressed in 1.9.0 + (fd.o #85563, Patrick Welche) + +• Consistently save errno after socket calls (fd.o #83625, Simon McVittie) + +• In dbus-spawn, when the grandchild process exits due to a failed exec(), + do not lose the exec() errno (fd.o #24821, Simon McVittie) + +• Do not fail the tests if a parent process has leaked non-close-on-exec + file descriptors to us (fd.o #73689, fd.o #83899; Simon McVittie) + +• Do not fail the tests on Unix platforms with incomplete + credentials-passing support, but do fail if we can't pass credentials + on a platform where it is known to work: Linux, FreeBSD, OpenBSD, NetBSD + (fd.o #69702, Simon McVittie) + +• Detect accept4, dirfd, inotify_init1, pipe2, and Unix fd passing + when building with cmake, and expand test coverage there + (fd.o #73689; Ralf Habacker, Simon McVittie) + D-Bus 1.9.0 (2014-10-01) == @@ -103,10 +103,9 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], -[dnl Rely on autoconf to set up CDPATH properly. -AC_PREREQ([2.50])dnl -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- diff --git a/bus/Makefile.in b/bus/Makefile.in index 0f409cd4..897b22c8 100644 --- a/bus/Makefile.in +++ b/bus/Makefile.in @@ -492,6 +492,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ diff --git a/bus/activation.c b/bus/activation.c index bdf9cf3d..ffedf4c5 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -1688,6 +1688,31 @@ out: return retval; } +static void +child_setup (void *user_data) +{ +#ifdef DBUS_UNIX + BusActivation *activation = user_data; + DBusRLimit *initial_fd_limit; + DBusError error; + + dbus_error_init (&error); + initial_fd_limit = bus_context_get_initial_fd_limit (activation->context); + + if (initial_fd_limit != NULL && + !_dbus_rlimit_restore_fd_limit (initial_fd_limit, &error)) + { + /* unfortunately we don't actually know the service name here */ + bus_context_log (activation->context, + DBUS_SYSTEM_LOG_INFO, + "Failed to reset fd limit before activating " + "service: %s: %s", + error.name, error.message); + } +#endif +} + + dbus_bool_t bus_activation_activate_service (BusActivation *activation, DBusConnection *connection, @@ -2121,7 +2146,8 @@ bus_activation_activate_service (BusActivation *activation, service_name, argv, envp, - NULL, activation, + child_setup, + activation, &tmp_error)) { _dbus_verbose ("Failed to spawn child\n"); @@ -64,6 +64,7 @@ struct BusContext BusPolicy *policy; BusMatchmaker *matchmaker; BusLimits limits; + DBusRLimit *initial_fd_limit; unsigned int fork : 1; unsigned int syslog : 1; unsigned int keep_umask : 1; @@ -659,19 +660,38 @@ oom: static void raise_file_descriptor_limit (BusContext *context) { +#ifdef DBUS_UNIX + DBusError error = DBUS_ERROR_INIT; - /* I just picked this out of thin air; we need some extra - * descriptors for things like any internal pipes we create, - * inotify, connections to SELinux, etc. - */ - unsigned int arbitrary_extra_fds = 32; - unsigned int limit; + /* we only do this once */ + if (context->initial_fd_limit != NULL) + return; - limit = context->limits.max_completed_connections + - context->limits.max_incomplete_connections - + arbitrary_extra_fds; + context->initial_fd_limit = _dbus_rlimit_save_fd_limit (&error); + + if (context->initial_fd_limit == NULL) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "%s: %s", error.name, error.message); + dbus_error_free (&error); + return; + } - _dbus_request_file_descriptor_limit (limit); + /* We used to compute a suitable rlimit based on the configured number + * of connections, but that breaks down as soon as we allow fd-passing, + * because each connection is allowed to pass 64 fds to us, and if + * they all did, we'd hit kernel limits. We now hard-code 64k as a + * good limit, like systemd does: that's enough to avoid DoS from + * anything short of multiple uids conspiring against us. + */ + if (!_dbus_rlimit_raise_fd_limit_if_privileged (65536, &error)) + { + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "%s: %s", error.name, error.message); + dbus_error_free (&error); + return; + } +#endif } static dbus_bool_t @@ -1130,6 +1150,10 @@ bus_context_unref (BusContext *context) dbus_free (context->pidfile); } + + if (context->initial_fd_limit) + _dbus_rlimit_free (context->initial_fd_limit); + dbus_free (context); dbus_server_free_data_slot (&server_data_slot); @@ -1294,6 +1318,12 @@ bus_context_get_reply_timeout (BusContext *context) return context->limits.reply_timeout; } +DBusRLimit * +bus_context_get_initial_fd_limit (BusContext *context) +{ + return context->initial_fd_limit; +} + void bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, ...) _DBUS_GNUC_PRINTF (3, 4); @@ -116,6 +116,7 @@ int bus_context_get_max_services_per_connection (BusContext int bus_context_get_max_match_rules_per_connection (BusContext *context); int bus_context_get_max_replies_per_connection (BusContext *context); int bus_context_get_reply_timeout (BusContext *context); +DBusRLimit * bus_context_get_initial_fd_limit (BusContext *context); void bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, diff --git a/bus/connection.c b/bus/connection.c index 519122c5..0df8a3a8 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -1619,7 +1619,12 @@ bus_pending_reply_send_no_reply (BusConnections *connections, DBUS_ERROR_NO_REPLY)) goto out; - errmsg = "Message did not receive a reply (timeout by message bus)"; + /* If you change these messages, adjust test/dbus-daemon.c to match */ + if (pending->will_send_reply == NULL) + errmsg = "Message recipient disconnected from message bus without replying"; + else + errmsg = "Message did not receive a reply (timeout by message bus)"; + dbus_message_iter_init_append (message, &iter); if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &errmsg)) goto out; diff --git a/bus/dir-watch-kqueue.c b/bus/dir-watch-kqueue.c index c05a5997..c1e83245 100644 --- a/bus/dir-watch-kqueue.c +++ b/bus/dir-watch-kqueue.c @@ -38,6 +38,7 @@ #include <dbus/dbus-internals.h> #include <dbus/dbus-list.h> +#include <dbus/dbus-sysdeps-unix.h> #include "dir-watch.h" #define MAX_DIRS_TO_WATCH 128 diff --git a/bus/dispatch.c b/bus/dispatch.c index fa2cc968..8f322f8c 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -1312,6 +1312,11 @@ check_get_connection_unix_process_id (BusContext *context, #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ defined(__linux__) || \ defined(__OpenBSD__) + /* In principle NetBSD should also be in that list, but + * its implementation of PID-passing doesn't work + * over a socketpair() as used in the debug-pipe transport. + * We test this functionality in a more realistic situation + * in test/dbus-daemon.c. */ warn_unexpected (connection, message, "not this error"); goto out; diff --git a/bus/test-main.c b/bus/test-main.c index 01d22870..788574fe 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -31,6 +31,10 @@ #include <dbus/dbus-message-internal.h> #include "selinux.h" +#ifdef DBUS_UNIX +# include <dbus/dbus-sysdeps-unix.h> +#endif + #ifdef DBUS_ENABLE_EMBEDDED_TESTS static void die (const char *failure) @@ -109,6 +113,11 @@ main (int argc, char **argv) _dbus_string_init_const (&test_data_dir, dir); +#ifdef DBUS_UNIX + /* close any inherited fds so dbus-spawn's check for close-on-exec works */ + _dbus_close_all (); +#endif + if (!_dbus_threads_init_debug ()) die ("initializing debug threads"); diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 628a681a..32250f59 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -496,6 +496,9 @@ add_subdirectory( dbus ) add_subdirectory( bus ) if (DBUS_BUILD_TESTS) add_subdirectory( test ) + add_custom_target(check + COMMAND ctest -R ^test-.* + ) endif (DBUS_BUILD_TESTS) add_subdirectory( tools ) add_subdirectory( doc ) diff --git a/cmake/ConfigureChecks.cmake b/cmake/ConfigureChecks.cmake index d290b0f2..6e8ebaee 100644 --- a/cmake/ConfigureChecks.cmake +++ b/cmake/ConfigureChecks.cmake @@ -36,6 +36,12 @@ check_symbol_exists(setlocale "locale.h" HAVE_SETLOCALE) # check_symbol_exists(localeconv "locale.h" HAVE_LOCALECONV) # dbus-sysdeps.c check_symbol_exists(strtoll "stdlib.h" HAVE_STRTOLL) # dbus-send.c check_symbol_exists(strtoull "stdlib.h" HAVE_STRTOULL) # dbus-send.c +set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) +check_symbol_exists(pipe2 "fcntl.h;unistd.h" HAVE_PIPE2) +check_symbol_exists(accept4 "sys/socket.h" HAVE_ACCEPT4) +check_symbol_exists(dirfd "dirent.h" HAVE_DIRFD) +check_symbol_exists(inotify_init1 "sys/inotify.h" HAVE_INOTIFY_INIT1) +check_symbol_exists(SCM_RIGHTS "sys/types.h;sys/socket.h;sys/un.h" HAVE_UNIX_FD_PASSING) check_struct_member(cmsgcred cmcred_pid "sys/types.h sys/socket.h" HAVE_CMSGCRED) # dbus-sysdeps.c diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index eaec1e98..8789cf91 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -190,6 +190,14 @@ /* Define to 1 if you have strtoull */ #cmakedefine HAVE_STRTOULL 1 +/* Define to 1 if you have pip2 */ +#cmakedefine HAVE_PIPE2 + +#cmakedefine HAVE_ACCEPT4 1 +#cmakedefine HAVE_DIRFD 1 +#cmakedefine HAVE_INOTIFY_INIT1 1 +#cmakedefine HAVE_UNIX_FD_PASSING 1 + // structs /* Define to 1 if you have struct cmsgred */ #cmakedefine HAVE_CMSGCRED 1 diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index 13f639b1..5e81e7f9 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -158,7 +158,10 @@ ENDFOREACH(FILE_TYPE) MESSAGE(STATUS "Copying generated bus config files to test directory") set (OUTDIR ${CMAKE_BINARY_DIR}/test/data/valid-config-files) -FILE(GLOB FILES "${CMAKE_SOURCE_DIR}/../bus/*.conf.in" ) +SET(FILES + "${CMAKE_SOURCE_DIR}/../bus/session.conf.in" + "${CMAKE_SOURCE_DIR}/../bus/system.conf.in" +) FILE(MAKE_DIRECTORY ${OUTDIR}) FOREACH(FILE ${FILES}) GET_FILENAME_COMPONENT(FILENAME ${FILE} NAME) @@ -169,7 +172,3 @@ FOREACH(FILE ${FILES}) MESSAGE("FROM: ${FILE}\nTO: ${TARGET}\n") ENDIF (CONFIG_VERBOSE) ENDFOREACH(FILE) - -add_custom_target(check - COMMAND ctest -R ^test-.* -) diff --git a/config.h.in b/config.h.in index e7ff3e4c..7dca911b 100644 --- a/config.h.in +++ b/config.h.in @@ -347,6 +347,9 @@ /* Supports sending UNIX file descriptors */ #undef HAVE_UNIX_FD_PASSING +/* Have unpcbid structure */ +#undef HAVE_UNPCBID + /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for dbus 1.9.0. +# Generated by GNU Autoconf 2.69 for dbus 1.9.2. # # Report bugs to <https://bugs.freedesktop.org/enter_bug.cgi?product=dbus>. # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='dbus' PACKAGE_TARNAME='dbus' -PACKAGE_VERSION='1.9.0' -PACKAGE_STRING='dbus 1.9.0' +PACKAGE_VERSION='1.9.2' +PACKAGE_STRING='dbus 1.9.2' PACKAGE_BUGREPORT='https://bugs.freedesktop.org/enter_bug.cgi?product=dbus' PACKAGE_URL='' @@ -648,6 +648,7 @@ DBUS_TEST_EXEC DBUS_TEST_DATA DBUS_LIBEXECDIR DBUS_BINDIR +dbus_daemondir DBUS_DAEMONDIR DBUS_DATADIR DBUS_PREFIX @@ -1513,7 +1514,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures dbus 1.9.0 to adapt to many kinds of systems. +\`configure' configures dbus 1.9.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1587,7 +1588,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of dbus 1.9.0:";; + short | recursive ) echo "Configuration of dbus 1.9.2:";; esac cat <<\_ACEOF @@ -1784,7 +1785,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -dbus configure 1.9.0 +dbus configure 1.9.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2499,11 +2500,68 @@ $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +$as_echo_n "checking for $2.$3... " >&6; } +if eval \${$4+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main () +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$4=yes" +else + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$4 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by dbus $as_me 1.9.0, which was +It was created by dbus $as_me 1.9.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3132,8 +3190,8 @@ test "$program_suffix" != NONE && ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` -# expand $ac_aux_dir to an absolute path -am_aux_dir=`cd $ac_aux_dir && pwd` +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in @@ -3446,7 +3504,7 @@ fi # Define the identity of the package. PACKAGE='dbus' - VERSION='1.9.0' + VERSION='1.9.2' cat >>confdefs.h <<_ACEOF @@ -3742,7 +3800,7 @@ _ACEOF # ## increment if the interface has additions, changes, removals. -LT_CURRENT=12 +LT_CURRENT=13 ## increment any time the source changes; set to ## 0 if you increment CURRENT @@ -3751,7 +3809,7 @@ LT_REVISION=0 ## increment if any interfaces have been added; set to 0 ## if any interfaces have been changed or removed. removal has ## precedence over adding, so set to 0 if both happened. -LT_AGE=9 +LT_AGE=10 @@ -3759,8 +3817,8 @@ LT_AGE=9 DBUS_MAJOR_VERSION=1 DBUS_MINOR_VERSION=9 -DBUS_MICRO_VERSION=0 -DBUS_VERSION=1.9.0 +DBUS_MICRO_VERSION=2 +DBUS_VERSION=1.9.2 @@ -19353,6 +19411,15 @@ $as_echo "#define HAVE_CMSGCRED 1" >>confdefs.h fi +ac_fn_c_check_member "$LINENO" "struct unpcbid" "unp_pid" "ac_cv_member_struct_unpcbid_unp_pid" "#include <sys/un.h> +" +if test "x$ac_cv_member_struct_unpcbid_unp_pid" = xyes; then : + +$as_echo "#define HAVE_UNPCBID 1" >>confdefs.h + +fi + + for ac_func in getpeerucred getpeereid do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` @@ -22616,11 +22683,14 @@ _ACEOF #### Directory to install dbus-daemon if test -z "$with_dbus_daemondir" ; then DBUS_DAEMONDIR=$EXPANDED_BINDIR + dbus_daemondir='$bindir' else DBUS_DAEMONDIR=$with_dbus_daemondir + dbus_daemondir=$with_dbus_daemondir fi + cat >>confdefs.h <<_ACEOF #define DBUS_DAEMONDIR "$DBUS_DAEMONDIR" _ACEOF @@ -22838,7 +22908,7 @@ $as_echo "#define DBUS_ENABLE_STATS 1" >>confdefs.h fi -ac_config_files="$ac_config_files Doxyfile dbus/versioninfo.rc dbus/dbus-arch-deps.h bus/system.conf bus/session.conf bus/example-system-enable-stats.conf bus/example-session-disable-stats.conf bus/messagebus bus/messagebus-config bus/org.freedesktop.dbus-session.plist bus/rc.messagebus bus/dbus.service bus/dbus.socket Makefile dbus/Makefile bus/Makefile tools/Makefile test/Makefile test/name-test/Makefile doc/Makefile doc/dbus-cleanup-sockets.1.xml doc/dbus-daemon.1.xml doc/dbus-launch.1.xml doc/dbus-monitor.1.xml doc/dbus-run-session.1.xml doc/dbus-send.1.xml doc/dbus-uuidgen.1.xml dbus-1.pc dbus-1-uninstalled.pc test/data/valid-config-files/debug-allow-all.conf test/data/valid-config-files/debug-allow-all-sha1.conf test/data/valid-config-files/incoming-limit.conf test/data/valid-config-files-system/debug-allow-all-pass.conf test/data/valid-config-files-system/debug-allow-all-fail.conf test/data/valid-service-files/org.freedesktop.DBus.TestSuite.PrivServer.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteEchoService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteForkingEchoService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteSegfaultService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteEchoService.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteSegfaultService.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service" +ac_config_files="$ac_config_files Doxyfile dbus/versioninfo.rc dbus/dbus-arch-deps.h bus/system.conf bus/session.conf bus/example-system-enable-stats.conf bus/example-session-disable-stats.conf bus/messagebus bus/messagebus-config bus/org.freedesktop.dbus-session.plist bus/rc.messagebus bus/dbus.service bus/dbus.socket Makefile dbus/Makefile bus/Makefile tools/Makefile test/Makefile test/name-test/Makefile doc/Makefile doc/dbus-cleanup-sockets.1.xml doc/dbus-daemon.1.xml doc/dbus-launch.1.xml doc/dbus-monitor.1.xml doc/dbus-run-session.1.xml doc/dbus-send.1.xml doc/dbus-uuidgen.1.xml dbus-1.pc dbus-1-uninstalled.pc test/data/valid-config-files/debug-allow-all.conf test/data/valid-config-files/debug-allow-all-sha1.conf test/data/valid-config-files/finite-timeout.conf test/data/valid-config-files/incoming-limit.conf test/data/valid-config-files-system/debug-allow-all-pass.conf test/data/valid-config-files-system/debug-allow-all-fail.conf test/data/valid-service-files/org.freedesktop.DBus.TestSuite.PrivServer.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteEchoService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteForkingEchoService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteSegfaultService.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteEchoService.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteSegfaultService.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -23475,7 +23545,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by dbus $as_me 1.9.0, which was +This file was extended by dbus $as_me 1.9.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23541,7 +23611,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -dbus config.status 1.9.0 +dbus config.status 1.9.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -24165,6 +24235,7 @@ do "dbus-1-uninstalled.pc") CONFIG_FILES="$CONFIG_FILES dbus-1-uninstalled.pc" ;; "test/data/valid-config-files/debug-allow-all.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files/debug-allow-all.conf" ;; "test/data/valid-config-files/debug-allow-all-sha1.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files/debug-allow-all-sha1.conf" ;; + "test/data/valid-config-files/finite-timeout.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files/finite-timeout.conf" ;; "test/data/valid-config-files/incoming-limit.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files/incoming-limit.conf" ;; "test/data/valid-config-files-system/debug-allow-all-pass.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files-system/debug-allow-all-pass.conf" ;; "test/data/valid-config-files-system/debug-allow-all-fail.conf") CONFIG_FILES="$CONFIG_FILES test/data/valid-config-files-system/debug-allow-all-fail.conf" ;; diff --git a/configure.ac b/configure.ac index 2f62269e..d903be12 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.63]) m4_define([dbus_major_version], [1]) m4_define([dbus_minor_version], [9]) -m4_define([dbus_micro_version], [0]) +m4_define([dbus_micro_version], [2]) m4_define([dbus_version], [dbus_major_version.dbus_minor_version.dbus_micro_version]) AC_INIT([dbus],[dbus_version],[https://bugs.freedesktop.org/enter_bug.cgi?product=dbus],[dbus]) @@ -33,7 +33,7 @@ AC_DEFINE_UNQUOTED(DBUS_DAEMON_NAME,"dbus-daemon",[Name of executable]) # ## increment if the interface has additions, changes, removals. -LT_CURRENT=12 +LT_CURRENT=13 ## increment any time the source changes; set to ## 0 if you increment CURRENT @@ -42,7 +42,7 @@ LT_REVISION=0 ## increment if any interfaces have been added; set to 0 ## if any interfaces have been changed or removed. removal has ## precedence over adding, so set to 0 if both happened. -LT_AGE=9 +LT_AGE=10 AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) @@ -818,6 +818,11 @@ if test x$dbus_have_struct_cmsgcred = xyes; then AC_DEFINE(HAVE_CMSGCRED,1,[Have cmsgcred structure]) fi +AC_CHECK_MEMBER([struct unpcbid.unp_pid], + [AC_DEFINE([HAVE_UNPCBID], 1, [Have unpcbid structure])], + [], + [[#include <sys/un.h>]]) + AC_CHECK_FUNCS(getpeerucred getpeereid) AC_CHECK_FUNCS(pipe2 accept4) @@ -1583,10 +1588,13 @@ AC_DEFINE_UNQUOTED(DBUS_DATADIR,"$DBUS_DATADIR", [Directory for installing DBUS #### Directory to install dbus-daemon if test -z "$with_dbus_daemondir" ; then DBUS_DAEMONDIR=$EXPANDED_BINDIR + dbus_daemondir='$bindir' else DBUS_DAEMONDIR=$with_dbus_daemondir + dbus_daemondir=$with_dbus_daemondir fi AC_SUBST(DBUS_DAEMONDIR) +AC_SUBST(dbus_daemondir) AC_DEFINE_UNQUOTED(DBUS_DAEMONDIR,"$DBUS_DAEMONDIR", [Directory for installing the DBUS daemon]) #### Directory to install the other binaries @@ -1776,6 +1784,7 @@ dbus-1.pc dbus-1-uninstalled.pc test/data/valid-config-files/debug-allow-all.conf test/data/valid-config-files/debug-allow-all-sha1.conf +test/data/valid-config-files/finite-timeout.conf test/data/valid-config-files/incoming-limit.conf test/data/valid-config-files-system/debug-allow-all-pass.conf test/data/valid-config-files-system/debug-allow-all-fail.conf diff --git a/dbus-1-uninstalled.pc.in b/dbus-1-uninstalled.pc.in index 038c83e2..b66ad2f6 100644 --- a/dbus-1-uninstalled.pc.in +++ b/dbus-1-uninstalled.pc.in @@ -2,12 +2,15 @@ abs_top_builddir=@abs_top_builddir@ abs_top_srcdir=@abs_top_srcdir@ prefix= exec_prefix= +bindir=@bindir@ system_bus_default_address=@DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@ -sysconfdir=@EXPANDED_SYSCONFDIR@ -session_bus_services_dir=@EXPANDED_DATADIR@/dbus-1/services -system_bus_services_dir=@EXPANDED_DATADIR@/dbus-1/system-services -interfaces_dir=@EXPANDED_DATADIR@/dbus-1/interfaces -daemondir=@DBUS_DAEMONDIR@ +datarootdir=@datarootdir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ +session_bus_services_dir=${datadir}/dbus-1/services +system_bus_services_dir=${datadir}/dbus-1/system-services +interfaces_dir=${datadir}/dbus-1/interfaces +daemondir=@dbus_daemondir@ Name: dbus Description: Free desktop message bus (uninstalled copy) diff --git a/dbus-1.pc.in b/dbus-1.pc.in index 25f8bcee..f93d1563 100644 --- a/dbus-1.pc.in +++ b/dbus-1.pc.in @@ -1,13 +1,16 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ +bindir=@bindir@ libdir=@libdir@ includedir=@includedir@ system_bus_default_address=@DBUS_SYSTEM_BUS_DEFAULT_ADDRESS@ -sysconfdir=@EXPANDED_SYSCONFDIR@ -session_bus_services_dir=@EXPANDED_DATADIR@/dbus-1/services -system_bus_services_dir=@EXPANDED_DATADIR@/dbus-1/system-services -interfaces_dir=@EXPANDED_DATADIR@/dbus-1/interfaces -daemondir=@DBUS_DAEMONDIR@ +datarootdir=@datarootdir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ +session_bus_services_dir=${datadir}/dbus-1/services +system_bus_services_dir=${datadir}/dbus-1/system-services +interfaces_dir=${datadir}/dbus-1/interfaces +daemondir=@dbus_daemondir@ Name: dbus Description: Free desktop message bus diff --git a/dbus/Makefile.in b/dbus/Makefile.in index 3205e165..226c2e23 100644 --- a/dbus/Makefile.in +++ b/dbus/Makefile.in @@ -626,6 +626,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index 6a07665f..64ad2b06 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -37,7 +37,7 @@ * @brief DBusAuth object * * DBusAuth manages the authentication negotiation when a connection - * is first established, and also manage any encryption used over a + * is first established, and also manages any encryption used over a * connection. * * @todo some SASL profiles require sending the empty string as a diff --git a/dbus/dbus-memory.c b/dbus/dbus-memory.c index 22e80727..67d5cb05 100644 --- a/dbus/dbus-memory.c +++ b/dbus/dbus-memory.c @@ -862,6 +862,13 @@ _dbus_register_shutdown_func_unlocked (DBusShutdownFunction func, * You MUST free all memory and release all reference counts * returned to you by libdbus prior to calling dbus_shutdown(). * + * If a shared connection is open, calling dbus_shutdown() will + * drain its queue of messages and disconnect it. In particular, + * this will result in processing of the special Disconnected + * signal, which may result in a call to _exit(), unless you + * have used dbus_connection_set_exit_on_disconnect() to disable + * that behaviour. + * * You can't continue to use any D-Bus objects, such as connections, * that were allocated prior to dbus_shutdown(). You can, however, * start over; call dbus_threads_init() again, create new connections, diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 974e8fa6..01c2367c 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -4876,6 +4876,54 @@ dbus_message_demarshal_bytes_needed(const char *buf, } } +/** + * Sets a flag indicating that the caller of the method is prepared + * to wait for interactive authorization to take place (for instance + * via Polkit) before the actual method is processed. + * + * The flag is #FALSE by default; that is, by default the other end is + * expected to make any authorization decisions non-interactively + * and promptly. It may use the error + * #DBUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED to signal that + * authorization failed, but could have succeeded if this flag had + * been used. + * + * For messages whose type is not #DBUS_MESSAGE_TYPE_METHOD_CALL, + * this flag is meaningless and should not be set. + * + * On the protocol level this toggles + * #DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION. + * + * @param message the message + * @param allow #TRUE if interactive authorization is acceptable + */ +void +dbus_message_set_allow_interactive_authorization (DBusMessage *message, + dbus_bool_t allow) +{ + _dbus_return_if_fail (message != NULL); + _dbus_return_if_fail (!message->locked); + + _dbus_header_toggle_flag (&message->header, + DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION, + allow); +} + +/** + * Returns whether the flag controlled by + * dbus_message_set_allow_interactive_authorization() has been set. + * + * @param message the message + */ +dbus_bool_t +dbus_message_get_allow_interactive_authorization (DBusMessage *message) +{ + _dbus_return_val_if_fail (message != NULL, FALSE); + + return _dbus_header_get_flag (&message->header, + DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION); +} + /** @} */ /* tests in dbus-message-util.c */ diff --git a/dbus/dbus-message.h b/dbus/dbus-message.h index 4fd44dab..baa7d7b5 100644 --- a/dbus/dbus-message.h +++ b/dbus/dbus-message.h @@ -302,6 +302,14 @@ DBUS_EXPORT int dbus_message_demarshal_bytes_needed (const char *str, int len); +DBUS_EXPORT +void dbus_message_set_allow_interactive_authorization (DBusMessage *message, + dbus_bool_t allow); + +DBUS_EXPORT +dbus_bool_t dbus_message_get_allow_interactive_authorization ( + DBusMessage *message); + /** @} */ DBUS_END_DECLS diff --git a/dbus/dbus-nonce.c b/dbus/dbus-nonce.c index 37f30f00..44c46b2f 100644 --- a/dbus/dbus-nonce.c +++ b/dbus/dbus-nonce.c @@ -53,10 +53,14 @@ do_check_nonce (int fd, const DBusString *nonce, DBusError *error) while (nleft) { + int saved_errno; + n = _dbus_read_socket (fd, &p, nleft); - if (n == -1 && _dbus_get_is_errno_eintr()) + saved_errno = _dbus_save_socket_errno (); + + if (n == -1 && _dbus_get_is_errno_eintr (saved_errno)) ; - else if (n == -1 && _dbus_get_is_errno_eagain_or_ewouldblock()) + else if (n == -1 && _dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) _dbus_sleep_milliseconds (100); else if (n==-1) { diff --git a/dbus/dbus-protocol.h b/dbus/dbus-protocol.h index 60605ab2..933c3658 100644 --- a/dbus/dbus-protocol.h +++ b/dbus/dbus-protocol.h @@ -254,6 +254,11 @@ extern "C" { * message. */ #define DBUS_HEADER_FLAG_NO_AUTO_START 0x2 +/** + * If set on a method call, this flag means that the caller is prepared to + * wait for interactive authorization. + */ +#define DBUS_HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION 0x4 /* Header fields */ @@ -446,6 +451,10 @@ extern "C" { /** The message meta data does not match the payload. e.g. expected number of file descriptors were not sent over the socket this message was received on. */ #define DBUS_ERROR_INCONSISTENT_MESSAGE "org.freedesktop.DBus.Error.InconsistentMessage" +/** The message is not allowed without performing interactive authorization, + * but could have succeeded if an interactive authorization step was + * allowed. */ +#define DBUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired" /* XML introspection format */ diff --git a/dbus/dbus-server-socket.c b/dbus/dbus-server-socket.c index 060a919e..70367c79 100644 --- a/dbus/dbus-server-socket.c +++ b/dbus/dbus-server-socket.c @@ -184,6 +184,7 @@ socket_handle_watch (DBusWatch *watch, { int client_fd; int listen_fd; + int saved_errno; listen_fd = dbus_watch_get_socket (watch); @@ -192,15 +193,17 @@ socket_handle_watch (DBusWatch *watch, else client_fd = _dbus_accept (listen_fd); + saved_errno = _dbus_save_socket_errno (); + if (client_fd < 0) { /* EINTR handled for us */ - if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) _dbus_verbose ("No client available to accept after all\n"); else _dbus_verbose ("Failed to accept a client connection: %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); SERVER_UNLOCK (server); } diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index b95cad6e..959c1844 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -454,9 +454,25 @@ read_data (DBusBabysitter *sitter, { if (what == CHILD_EXITED) { + /* Do not reset sitter->errnum to 0 here. We get here if + * the babysitter reports that the grandchild process has + * exited, and there are two ways that can happen: + * + * 1. grandchild successfully exec()s the desired process, + * but then the desired process exits or is terminated + * by a signal. The babysitter observes this and reports + * CHILD_EXITED. + * + * 2. grandchild fails to exec() the desired process, + * attempts to report the exec() failure (which + * we will receive as CHILD_EXEC_FAILED), and then + * exits itself (which will prompt the babysitter to + * send CHILD_EXITED). We want the CHILD_EXEC_FAILED + * to take precedence (and have its errno logged), + * which _dbus_babysitter_set_child_exit_error() does. + */ sitter->have_child_status = TRUE; sitter->status = arg; - sitter->errnum = 0; _dbus_verbose ("recorded child status exited = %d signaled = %d exitstatus = %d termsig = %d\n", WIFEXITED (sitter->status), WIFSIGNALED (sitter->status), WEXITSTATUS (sitter->status), WTERMSIG (sitter->status)); @@ -1017,7 +1033,7 @@ do_exec (int child_err_report_fd, retval = fcntl (i, F_GETFD); if (retval != -1 && !(retval & FD_CLOEXEC)) - _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i); + _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i); } #endif diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index 544429a7..f4a42d89 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1663,6 +1663,25 @@ write_credentials_byte (int server_fd, * object, then adds pid/uid if available, so any previous credentials * stored in the object are lost. * + * DBusServer makes the security assumption that the credentials + * returned by this method are the credentials that were active + * at the time the socket was opened. Do not add APIs to this + * method that would break that assumption. + * + * In particular, it is incorrect to use any API of the form + * "get the process ID at the other end of the connection, then + * determine its uid, gid, or other credentials from the pid" + * (e.g. looking in /proc on Linux). If we did that, we would + * be vulnerable to several attacks. A malicious process could + * queue up the rest of the authentication handshake and a malicious + * message that it should not be allowed to send, then race with + * the DBusServer to exec() a more privileged (e.g. setuid) binary that + * would have been allowed to send that message; or it could exit, + * and arrange for enough setuid processes to be started that its + * pid would be recycled for one of those processes with high + * probability; or it could fd-pass the connection to a more + * privileged process. + * * Return value indicates whether a byte was read, not whether * we got valid credentials. On some systems, such as Linux, * reading/writing the byte isn't actually required, but we do it @@ -1778,16 +1797,41 @@ _dbus_read_credentials_socket (int client_fd, #endif int cr_len = sizeof (cr); - if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 && - cr_len == sizeof (cr)) + if (getsockopt (client_fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) != 0) + { + _dbus_verbose ("Failed to getsockopt(SO_PEERCRED): %s\n", + _dbus_strerror (errno)); + } + else if (cr_len != sizeof (cr)) { - pid_read = cr.pid; - uid_read = cr.uid; + _dbus_verbose ("Failed to getsockopt(SO_PEERCRED), returned %d bytes, expected %d\n", + cr_len, (int) sizeof (cr)); } else { - _dbus_verbose ("Failed to getsockopt() credentials, returned len %d/%d: %s\n", - cr_len, (int) sizeof (cr), _dbus_strerror (errno)); + pid_read = cr.pid; + uid_read = cr.uid; + } +#elif defined(HAVE_UNPCBID) && defined(LOCAL_PEEREID) + /* Another variant of the above - used on NetBSD + */ + struct unpcbid cr; + socklen_t cr_len = sizeof (cr); + + if (getsockopt (client_fd, 0, LOCAL_PEEREID, &cr, &cr_len) != 0) + { + _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID): %s\n", + _dbus_strerror (errno)); + } + else if (cr_len != sizeof (cr)) + { + _dbus_verbose ("Failed to getsockopt(LOCAL_PEEREID), returned %d bytes, expected %d\n", + cr_len, (int) sizeof (cr)); + } + else + { + pid_read = cr.unp_pid; + uid_read = cr.unp_euid; } #elif defined(HAVE_CMSGCRED) /* We only check for HAVE_CMSGCRED, but we're really assuming that the @@ -3937,12 +3981,12 @@ _dbus_daemon_unpublish_session_bus_address (void) * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently * for Winsock so is abstracted) * - * @returns #TRUE if errno == EAGAIN or errno == EWOULDBLOCK + * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK */ dbus_bool_t -_dbus_get_is_errno_eagain_or_ewouldblock (void) +_dbus_get_is_errno_eagain_or_ewouldblock (int e) { - return errno == EAGAIN || errno == EWOULDBLOCK; + return e == EAGAIN || e == EWOULDBLOCK; } /** @@ -4199,4 +4243,16 @@ _dbus_append_address_from_socket (int fd, return FALSE; } +int +_dbus_save_socket_errno (void) +{ + return errno; +} + +void +_dbus_restore_socket_errno (int saved_errno) +{ + errno = saved_errno; +} + /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c index d104e41c..15465d32 100644 --- a/dbus/dbus-sysdeps-util-unix.c +++ b/dbus/dbus-sysdeps-util-unix.c @@ -380,53 +380,140 @@ _dbus_change_to_daemon_user (const char *user, } #endif /* !HAVE_LIBAUDIT */ +#ifdef HAVE_SETRLIMIT -/** - * Attempt to ensure that the current process can open - * at least @p limit file descriptors. - * - * If @p limit is lower than the current, it will not be - * lowered. No error is returned if the request can - * not be satisfied. - * - * @param limit number of file descriptors +/* We assume that if we have setrlimit, we also have getrlimit and + * struct rlimit. */ -void -_dbus_request_file_descriptor_limit (unsigned int limit) + +struct DBusRLimit { + struct rlimit lim; +}; + +DBusRLimit * +_dbus_rlimit_save_fd_limit (DBusError *error) +{ + DBusRLimit *self; + + self = dbus_new0 (DBusRLimit, 1); + + if (self == NULL) + { + _DBUS_SET_OOM (error); + return NULL; + } + + if (getrlimit (RLIMIT_NOFILE, &self->lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get fd limit: %s", _dbus_strerror (errno)); + dbus_free (self); + return NULL; + } + + return self; +} + +dbus_bool_t +_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired, + DBusError *error) { -#ifdef HAVE_SETRLIMIT struct rlimit lim; - struct rlimit target_lim; /* No point to doing this practically speaking * if we're not uid 0. We expect the system * bus to use this before we change UID, and - * the session bus takes the Linux default - * of 1024 for both cur and max. + * the session bus takes the Linux default, + * currently 1024 for cur and 4096 for max. */ if (getuid () != 0) - return; + { + /* not an error, we're probably the session bus */ + return TRUE; + } if (getrlimit (RLIMIT_NOFILE, &lim) < 0) - return; + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get fd limit: %s", _dbus_strerror (errno)); + return FALSE; + } - if (lim.rlim_cur >= limit) - return; + if (lim.rlim_cur == RLIM_INFINITY || lim.rlim_cur >= desired) + { + /* not an error, everything is fine */ + return TRUE; + } /* Ignore "maximum limit", assume we have the "superuser" * privileges. On Linux this is CAP_SYS_RESOURCE. */ - target_lim.rlim_cur = target_lim.rlim_max = limit; - /* Also ignore errors; if we fail, we will at least work - * up to whatever limit we had, which seems better than - * just outright aborting. - * - * However, in the future we should probably log this so OS builders - * have a chance to notice any misconfiguration like dbus-daemon - * being started without CAP_SYS_RESOURCE. - */ - setrlimit (RLIMIT_NOFILE, &target_lim); + lim.rlim_cur = lim.rlim_max = desired; + + if (setrlimit (RLIMIT_NOFILE, &lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set fd limit to %u: %s", + desired, _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +dbus_bool_t +_dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error) +{ + if (setrlimit (RLIMIT_NOFILE, &saved->lim) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to restore old fd limit: %s", + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + +#else /* !HAVE_SETRLIMIT */ + +static void +fd_limit_not_supported (DBusError *error) +{ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "cannot change fd limit on this platform"); +} + +DBusRLimit * +_dbus_rlimit_save_fd_limit (DBusError *error) +{ + fd_limit_not_supported (error); + return NULL; +} + +dbus_bool_t +_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired, + DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} + +dbus_bool_t +_dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} + #endif + +void +_dbus_rlimit_free (DBusRLimit *lim) +{ + dbus_free (lim); } void diff --git a/dbus/dbus-sysdeps-util-win.c b/dbus/dbus-sysdeps-util-win.c index dd53417f..2125f608 100644 --- a/dbus/dbus-sysdeps-util-win.c +++ b/dbus/dbus-sysdeps-util-win.c @@ -258,9 +258,42 @@ _dbus_change_to_daemon_user (const char *user, return TRUE; } +static void +fd_limit_not_supported (DBusError *error) +{ + dbus_set_error (error, DBUS_ERROR_NOT_SUPPORTED, + "cannot change fd limit on this platform"); +} + +DBusRLimit * +_dbus_rlimit_save_fd_limit (DBusError *error) +{ + fd_limit_not_supported (error); + return NULL; +} + +dbus_bool_t +_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired, + DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} + +dbus_bool_t +_dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error) +{ + fd_limit_not_supported (error); + return FALSE; +} + void -_dbus_request_file_descriptor_limit (unsigned int limit) +_dbus_rlimit_free (DBusRLimit *lim) { + /* _dbus_rlimit_save_fd_limit() cannot return non-NULL on Windows + * so there cannot be anything to free */ + _dbus_assert (lim == NULL); } void diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 341db8ab..2c18f40e 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -3253,12 +3253,12 @@ _dbus_flush_caches (void) * See if errno is EAGAIN or EWOULDBLOCK (this has to be done differently * for Winsock so is abstracted) * - * @returns #TRUE if errno == EAGAIN or errno == EWOULDBLOCK + * @returns #TRUE if e == EAGAIN or e == EWOULDBLOCK */ dbus_bool_t -_dbus_get_is_errno_eagain_or_ewouldblock (void) +_dbus_get_is_errno_eagain_or_ewouldblock (int e) { - return errno == WSAEWOULDBLOCK; + return e == WSAEWOULDBLOCK; } /** @@ -3725,6 +3725,18 @@ _dbus_check_setuid (void) return FALSE; } +int +_dbus_save_socket_errno (void) +{ + return errno; +} + +void +_dbus_restore_socket_errno (int saved_errno) +{ + _dbus_win_set_errno (saved_errno); +} + /** @} end of sysdeps-win */ /* tests in dbus-sysdeps-util.c */ diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index f4ba0fac..99792100 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -722,33 +722,23 @@ _dbus_set_errno_to_zero (void) } /** - * See if errno is set - * @returns #TRUE if errno is not 0 - */ -dbus_bool_t -_dbus_get_is_errno_nonzero (void) -{ - return errno != 0; -} - -/** * See if errno is ENOMEM - * @returns #TRUE if errno == ENOMEM + * @returns #TRUE if e == ENOMEM */ dbus_bool_t -_dbus_get_is_errno_enomem (void) +_dbus_get_is_errno_enomem (int e) { - return errno == ENOMEM; + return e == ENOMEM; } /** * See if errno is EINTR - * @returns #TRUE if errno == EINTR + * @returns #TRUE if e == EINTR */ dbus_bool_t -_dbus_get_is_errno_eintr (void) +_dbus_get_is_errno_eintr (int e) { - return errno == EINTR; + return e == EINTR; } /** @@ -756,9 +746,9 @@ _dbus_get_is_errno_eintr (void) * @returns #TRUE if errno == EPIPE */ dbus_bool_t -_dbus_get_is_errno_epipe (void) +_dbus_get_is_errno_epipe (int e) { - return errno == EPIPE; + return e == EPIPE; } /** @@ -766,10 +756,10 @@ _dbus_get_is_errno_epipe (void) * @returns #TRUE if errno == ETOOMANYREFS */ dbus_bool_t -_dbus_get_is_errno_etoomanyrefs (void) +_dbus_get_is_errno_etoomanyrefs (int e) { #ifdef ETOOMANYREFS - return errno == ETOOMANYREFS; + return e == ETOOMANYREFS; #else return FALSE; #endif diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 955ba9aa..09b98e11 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -376,13 +376,14 @@ dbus_bool_t _dbus_generate_random_ascii (DBusString *str, const char* _dbus_error_from_errno (int error_number); const char* _dbus_error_from_system_errno (void); +int _dbus_save_socket_errno (void); +void _dbus_restore_socket_errno (int saved_errno); void _dbus_set_errno_to_zero (void); -dbus_bool_t _dbus_get_is_errno_nonzero (void); -dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock (void); -dbus_bool_t _dbus_get_is_errno_enomem (void); -dbus_bool_t _dbus_get_is_errno_eintr (void); -dbus_bool_t _dbus_get_is_errno_epipe (void); -dbus_bool_t _dbus_get_is_errno_etoomanyrefs (void); +dbus_bool_t _dbus_get_is_errno_eagain_or_ewouldblock (int e); +dbus_bool_t _dbus_get_is_errno_enomem (int e); +dbus_bool_t _dbus_get_is_errno_eintr (int e); +dbus_bool_t _dbus_get_is_errno_epipe (int e); +dbus_bool_t _dbus_get_is_errno_etoomanyrefs (int e); const char* _dbus_strerror_from_errno (void); void _dbus_disable_sigpipe (void); @@ -544,8 +545,6 @@ dbus_bool_t _dbus_change_to_daemon_user (const char *user, void _dbus_flush_caches (void); -void _dbus_request_file_descriptor_limit (unsigned int limit); - /* * replaces the term DBUS_PREFIX in configure_time_path by the * current dbus installation directory. On unix this function is a noop @@ -564,6 +563,15 @@ _dbus_replace_install_prefix (const char *configure_time_path); */ #define DBUS_DEFAULT_MESSAGE_UNIX_FDS 16 +typedef struct DBusRLimit DBusRLimit; + +DBusRLimit *_dbus_rlimit_save_fd_limit (DBusError *error); +dbus_bool_t _dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired, + DBusError *error); +dbus_bool_t _dbus_rlimit_restore_fd_limit (DBusRLimit *saved, + DBusError *error); +void _dbus_rlimit_free (DBusRLimit *lim); + /** @} */ DBUS_END_DECLS diff --git a/dbus/dbus-test-main.c b/dbus/dbus-test-main.c index f6ef7821..9a80f853 100644 --- a/dbus/dbus-test-main.c +++ b/dbus/dbus-test-main.c @@ -31,6 +31,10 @@ #include <locale.h> #endif +#ifdef DBUS_UNIX +# include <dbus/dbus-sysdeps-unix.h> +#endif + int main (int argc, char **argv) @@ -38,6 +42,11 @@ main (int argc, const char *test_data_dir; const char *specific_test; +#ifdef DBUS_UNIX + /* close any inherited fds so dbus-spawn's check for close-on-exec works */ + _dbus_close_all (); +#endif + #if HAVE_SETLOCALE setlocale(LC_ALL, ""); #endif diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c index 199d3b54..c1e47018 100644 --- a/dbus/dbus-transport-socket.c +++ b/dbus/dbus-transport-socket.c @@ -247,13 +247,15 @@ read_data_into_auth (DBusTransport *transport, DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusString *buffer; int bytes_read; - + int saved_errno; + *oom = FALSE; _dbus_auth_get_buffer (transport->auth, &buffer); bytes_read = _dbus_read_socket (socket_transport->fd, buffer, socket_transport->max_bytes_read_per_iteration); + saved_errno = _dbus_save_socket_errno (); _dbus_auth_return_buffer (transport->auth, buffer); @@ -267,16 +269,16 @@ read_data_into_auth (DBusTransport *transport, { /* EINTR already handled for us */ - if (_dbus_get_is_errno_enomem ()) + if (_dbus_get_is_errno_enomem (saved_errno)) { *oom = TRUE; } - else if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) ; /* do nothing, just return FALSE below */ else { _dbus_verbose ("Error reading from remote app: %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); do_io_error (transport); } @@ -299,6 +301,7 @@ write_data_from_auth (DBusTransport *transport) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; int bytes_written; + int saved_errno; const DBusString *buffer; if (!_dbus_auth_get_bytes_to_send (transport->auth, @@ -308,6 +311,7 @@ write_data_from_auth (DBusTransport *transport) bytes_written = _dbus_write_socket (socket_transport->fd, buffer, 0, _dbus_string_get_length (buffer)); + saved_errno = _dbus_save_socket_errno (); if (bytes_written > 0) { @@ -318,12 +322,12 @@ write_data_from_auth (DBusTransport *transport) { /* EINTR already handled for us */ - if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) ; else { _dbus_verbose ("Error writing to remote app: %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); do_io_error (transport); } } @@ -527,6 +531,7 @@ do_writing (DBusTransport *transport) const DBusString *body; int header_len, body_len; int total_bytes_to_write; + int saved_errno; if (total > socket_transport->max_bytes_written_per_iteration) { @@ -584,6 +589,7 @@ do_writing (DBusTransport *transport) &socket_transport->encoded_outgoing, socket_transport->message_bytes_written, total_bytes_to_write - socket_transport->message_bytes_written); + saved_errno = _dbus_save_socket_errno (); } else { @@ -612,6 +618,7 @@ do_writing (DBusTransport *transport) 0, body_len, unix_fds, n); + saved_errno = _dbus_save_socket_errno (); if (bytes_written > 0 && n > 0) _dbus_verbose("Wrote %i unix fds\n", n); @@ -638,6 +645,8 @@ do_writing (DBusTransport *transport) body_len - (socket_transport->message_bytes_written - header_len)); } + + saved_errno = _dbus_save_socket_errno (); } } @@ -651,7 +660,7 @@ do_writing (DBusTransport *transport) * http://lists.freedesktop.org/archives/dbus/2008-March/009526.html */ - if (_dbus_get_is_errno_eagain_or_ewouldblock () || _dbus_get_is_errno_epipe ()) + if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) || _dbus_get_is_errno_epipe (saved_errno)) goto out; /* Since Linux commit 25888e (from 2.6.37-rc4, Nov 2010), sendmsg() @@ -663,7 +672,7 @@ do_writing (DBusTransport *transport) * https://bugs.freedesktop.org/show_bug.cgi?id=80163 */ - else if (_dbus_get_is_errno_etoomanyrefs ()) + else if (_dbus_get_is_errno_etoomanyrefs (saved_errno)) { /* We only send fds in the first byte of the message. * ETOOMANYREFS cannot happen after. @@ -686,7 +695,7 @@ do_writing (DBusTransport *transport) else { _dbus_verbose ("Error writing to remote app: %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); do_io_error (transport); goto out; } @@ -730,6 +739,7 @@ do_reading (DBusTransport *transport) int bytes_read; int total; dbus_bool_t oom; + int saved_errno; _dbus_verbose ("fd = %d\n",socket_transport->fd); @@ -774,6 +784,8 @@ do_reading (DBusTransport *transport) &socket_transport->encoded_incoming, socket_transport->max_bytes_read_per_iteration); + saved_errno = _dbus_save_socket_errno (); + _dbus_assert (_dbus_string_get_length (&socket_transport->encoded_incoming) == bytes_read); @@ -823,6 +835,7 @@ do_reading (DBusTransport *transport) buffer, socket_transport->max_bytes_read_per_iteration, fds, &n_fds); + saved_errno = _dbus_save_socket_errno (); if (bytes_read >= 0 && n_fds > 0) _dbus_verbose("Read %i unix fds\n", n_fds); @@ -834,28 +847,29 @@ do_reading (DBusTransport *transport) { bytes_read = _dbus_read_socket (socket_transport->fd, buffer, socket_transport->max_bytes_read_per_iteration); + saved_errno = _dbus_save_socket_errno (); } _dbus_message_loader_return_buffer (transport->loader, buffer); } - + if (bytes_read < 0) { /* EINTR already handled for us */ - if (_dbus_get_is_errno_enomem ()) + if (_dbus_get_is_errno_enomem (saved_errno)) { _dbus_verbose ("Out of memory in read()/do_reading()\n"); oom = TRUE; goto out; } - else if (_dbus_get_is_errno_eagain_or_ewouldblock ()) + else if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno)) goto out; else { _dbus_verbose ("Error reading from remote app: %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); do_io_error (transport); goto out; } @@ -1129,6 +1143,8 @@ socket_do_iteration (DBusTransport *transport, if (poll_fd.events) { + int saved_errno; + if (flags & DBUS_ITERATION_BLOCK) poll_timeout = timeout_milliseconds; else @@ -1147,8 +1163,9 @@ socket_do_iteration (DBusTransport *transport, again: poll_res = _dbus_poll (&poll_fd, 1, poll_timeout); + saved_errno = _dbus_save_socket_errno (); - if (poll_res < 0 && _dbus_get_is_errno_eintr ()) + if (poll_res < 0 && _dbus_get_is_errno_eintr (saved_errno)) goto again; if (flags & DBUS_ITERATION_BLOCK) @@ -1191,7 +1208,7 @@ socket_do_iteration (DBusTransport *transport, else { _dbus_verbose ("Error from _dbus_poll(): %s\n", - _dbus_strerror_from_errno ()); + _dbus_strerror (saved_errno)); } } diff --git a/doc/Makefile.in b/doc/Makefile.in index a48c7e5a..959666de 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -326,6 +326,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ diff --git a/doc/dbus-daemon.1.xml.in b/doc/dbus-daemon.1.xml.in index cd7942c3..78f0fd08 100644 --- a/doc/dbus-daemon.1.xml.in +++ b/doc/dbus-daemon.1.xml.in @@ -396,12 +396,14 @@ DBUS_SESSION_BUS_ADDRESS is set.</para> used in a listenable address to configure the interface on which the server will listen: either the hostname is the IP address of one of the local machine's interfaces (most commonly 127.0.0.1), -or a DNS name that resolves to one of those IP addresses, or '*' -to listen on all interfaces simultaneously. If not specified, +a DNS name that resolves to one of those IP addresses, '0.0.0.0' +to listen on all IPv4 interfaces simultaneously, or '::' +to listen on all IPv4 and IPv6 interfaces simultaneously (if supported +by the OS). If not specified, the default is the same value as "host".</para> -<para>Example: <listen>tcp:host=localhost,bind=*,port=0</listen></para> +<para>Example: <listen>tcp:host=localhost,bind=0.0.0.0,port=0</listen></para> <itemizedlist remap='TP'> diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index eea5eada..6316bd83 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -6,8 +6,8 @@ <article id="index"> <articleinfo> <title>D-Bus Specification</title> - <releaseinfo>Version 0.24</releaseinfo> - <date>2014-10-01</date> + <releaseinfo>Version 0.25</releaseinfo> + <date>2014-11-10</date> <authorgroup> <author> <firstname>Havoc</firstname> @@ -71,6 +71,14 @@ </authorgroup> <revhistory> <revision> + <revnumber>0.25</revnumber> + <date>2014-11-10</date> + <authorinitials>smcv, lennart</authorinitials> + <revremark> + ALLOW_INTERACTIVE_AUTHORIZATION flag, EmitsChangedSignal=const + </revremark> + </revision> + <revision> <revnumber>0.24</revnumber> <date>2014-10-01</date> <authorinitials>SMcV</authorinitials> @@ -764,6 +772,7 @@ <tgroup cols="3"> <thead> <row> + <entry>Category</entry> <entry>Conventional Name</entry> <entry>Code</entry> <entry>Description</entry> @@ -771,62 +780,77 @@ </thead> <tbody> <row> + <entry>reserved</entry> <entry><literal>INVALID</literal></entry> <entry>0 (ASCII NUL)</entry> <entry>Not a valid type code, used to terminate signatures</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>BYTE</literal></entry> <entry>121 (ASCII 'y')</entry> <entry>8-bit unsigned integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>BOOLEAN</literal></entry> <entry>98 (ASCII 'b')</entry> <entry>Boolean value, 0 is <literal>FALSE</literal> and 1 is <literal>TRUE</literal>. Everything else is invalid.</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>INT16</literal></entry> <entry>110 (ASCII 'n')</entry> <entry>16-bit signed integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>UINT16</literal></entry> <entry>113 (ASCII 'q')</entry> <entry>16-bit unsigned integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>INT32</literal></entry> <entry>105 (ASCII 'i')</entry> <entry>32-bit signed integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>UINT32</literal></entry> <entry>117 (ASCII 'u')</entry> <entry>32-bit unsigned integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>INT64</literal></entry> <entry>120 (ASCII 'x')</entry> <entry>64-bit signed integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>UINT64</literal></entry> <entry>116 (ASCII 't')</entry> <entry>64-bit unsigned integer</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>DOUBLE</literal></entry> <entry>100 (ASCII 'd')</entry> <entry>IEEE 754 double</entry> </row><row> + <entry>string-like, basic</entry> <entry><literal>STRING</literal></entry> <entry>115 (ASCII 's')</entry> <entry>UTF-8 string (<emphasis>must</emphasis> be valid UTF-8). Must be nul terminated and contain no other nul bytes.</entry> </row><row> + <entry>string-like, basic</entry> <entry><literal>OBJECT_PATH</literal></entry> <entry>111 (ASCII 'o')</entry> <entry>Name of an object instance</entry> </row><row> + <entry>string-like, basic</entry> <entry><literal>SIGNATURE</literal></entry> <entry>103 (ASCII 'g')</entry> <entry>A type signature</entry> </row><row> + <entry>container</entry> <entry><literal>ARRAY</literal></entry> <entry>97 (ASCII 'a')</entry> <entry>Array</entry> </row><row> + <entry>container</entry> <entry><literal>STRUCT</literal></entry> <entry>114 (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')')</entry> <entry>Struct; type code 114 'r' is reserved for use in @@ -834,10 +858,12 @@ concept of a struct, and must not appear in signatures used on D-Bus.</entry> </row><row> + <entry>container</entry> <entry><literal>VARIANT</literal></entry> <entry>118 (ASCII 'v') </entry> <entry>Variant type (the type of the value is part of the value itself)</entry> </row><row> + <entry>container</entry> <entry><literal>DICT_ENTRY</literal></entry> <entry>101 (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}') </entry> <entry>Entry in a dict or map (array of key-value pairs). @@ -846,11 +872,13 @@ dict or dict-entry, and must not appear in signatures used on D-Bus.</entry> </row><row> + <entry>fixed, basic</entry> <entry><literal>UNIX_FD</literal></entry> <entry>104 (ASCII 'h')</entry> <entry>Unix file descriptor</entry> </row> <row> + <entry>reserved</entry> <entry>(reserved)</entry> <entry>109 (ASCII 'm')</entry> <entry>Reserved for <ulink @@ -860,6 +888,7 @@ specified here</entry> </row> <row> + <entry>reserved</entry> <entry>(reserved)</entry> <entry>42 (ASCII '*')</entry> <entry>Reserved for use in bindings/implementations to @@ -867,6 +896,7 @@ and must not appear in signatures used on D-Bus.</entry> </row> <row> + <entry>reserved</entry> <entry>(reserved)</entry> <entry>63 (ASCII '?')</entry> <entry>Reserved for use in bindings/implementations to @@ -874,6 +904,7 @@ not appear in signatures used on D-Bus.</entry> </row> <row> + <entry>reserved</entry> <entry>(reserved)</entry> <entry>64 (ASCII '@'), 38 (ASCII '&'), 94 (ASCII '^')</entry> @@ -995,8 +1026,8 @@ <para> Arrays have a maximum length defined to be 2 to the 26th power or - 67108864. Implementations must not send or accept arrays exceeding this - length. + 67108864 (64 MiB). Implementations must not send or accept arrays + exceeding this length. </para> <para> @@ -1201,8 +1232,8 @@ <para> The maximum length of a message, including header, header alignment padding, - and body is 2 to the 27th power or 134217728. Implementations must not - send or accept messages exceeding this size. + and body is 2 to the 27th power or 134217728 (128 MiB). + Implementations must not send or accept messages exceeding this size. </para> <para> @@ -1368,6 +1399,66 @@ for the destination name in response to this message. </entry> </row> + <row> + <entry><literal>ALLOW_INTERACTIVE_AUTHORIZATION</literal></entry> + <entry>0x4</entry> + <entry> + <para> + This flag may be set on a method call message to + inform the receiving side that the caller is prepared + to wait for interactive authorization, which might + take a considerable time to complete. For instance, + if this flag is set, it would be appropriate to + query the user for passwords or confirmation via + Polkit or a similar framework. + </para> + <para> + This flag is only useful when + unprivileged code calls a more privileged method call, + and an authorization framework is deployed that allows + possibly interactive authorization. If no such framework + is deployed it has no effect. This flag should not + be set by default by client implementations. If it is + set, the caller should also set a suitably long timeout + on the method call to make sure the user interaction + may complete. This flag is only valid for method call + messages, and shall be ignored otherwise. + </para> + <para> + Interaction that takes place as a part of the + effect of the method being called is outside the scope + of this flag, even if it could also be characterized + as authentication or authorization. For instance, in + a method call that directs a network management service + to attempt to connect to a virtual private network, + this flag should control how the network management + service makes the decision "is this user allowed to + change system network configuration?", but it should + not affect how or whether the network management + service interacts with the user to obtain the credentials + that are required for access to the VPN. + </para> + <para> + If a this flag is not set on a method call, and a + service determines that the requested operation is + not allowed without interactive authorization, but + could be allowed after successful interactive + authorization, it may return the + <literal>org.freedesktop.DBus.Error.InteractiveAuthorizationRequired</literal> + error. + </para> + <para> + The absence of this flag does not guarantee that + interactive authorization will not be applied, since + existing services that pre-date this flag might + already use interactive authorization. However, + existing D-Bus APIs that will use interactive + authorization should document that the call may take + longer than usual, and new D-Bus APIs should avoid + interactive authorization in the absence of this flag. + </para> + </entry> + </row> </tbody> </tgroup> </informaltable> @@ -3919,7 +4010,7 @@ </row> <row> <entry>org.freedesktop.DBus.Property.EmitsChangedSignal</entry> - <entry>true,invalidates,false</entry> + <entry>true,invalidates,const,false</entry> <entry> <para> If set to <literal>false</literal>, the @@ -3929,6 +4020,12 @@ guaranteed to be emitted if the property changes. </para> <para> + If set to <literal>const</literal> the property never + changes value during the lifetime of the object it + belongs to, and hence the signal is never emitted for + it. + </para> + <para> If set to <literal>invalidates</literal> the signal is emitted but the value is not included in the signal. @@ -3944,6 +4041,17 @@ defaults to the value specified in the enclosing interface element. </para> + <para> + This annotation is intended to be used by code + generators to implement client-side caching of + property values. For all properties for which the + annotation is set to <literal>const</literal>, + <literal>invalidates</literal> or + <literal>true</literal> the client may + unconditionally cache the values as the properties + don't change or notifications are generated for them + if they do. + </para> </entry> </row> </tbody> diff --git a/doc/dbus-tutorial.xml b/doc/dbus-tutorial.xml index 3d14e5f7..c4d9504e 100644 --- a/doc/dbus-tutorial.xml +++ b/doc/dbus-tutorial.xml @@ -704,939 +704,19 @@ </sect1> <sect1 id="glib-client"> - <title>GLib API: Using Remote Objects</title> - - <para> - The GLib binding is defined in the header file - <literal><dbus/dbus-glib.h></literal>. - </para> - - <sect2 id="glib-typemappings"> - <title>D-Bus - GLib type mappings</title> - <para> - The heart of the GLib bindings for D-Bus is the mapping it - provides between D-Bus "type signatures" and GLib types - (<literal>GType</literal>). The D-Bus type system is composed of - a number of "basic" types, along with several "container" types. - </para> - <sect3 id="glib-basic-typemappings"> - <title>Basic type mappings</title> - <para> - Below is a list of the basic types, along with their associated - mapping to a <literal>GType</literal>. - <informaltable> - <tgroup cols="4"> - <thead> - <row> - <entry>D-Bus basic type</entry> - <entry>GType</entry> - <entry>Free function</entry> - <entry>Notes</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>BYTE</literal></entry> - <entry><literal>G_TYPE_UCHAR</literal></entry> - <entry></entry> - <entry></entry> - </row><row> - <entry><literal>BOOLEAN</literal></entry> - <entry><literal>G_TYPE_BOOLEAN</literal></entry> - <entry></entry> - <entry></entry> - </row><row> - <entry><literal>INT16</literal></entry> - <entry><literal>G_TYPE_INT</literal></entry> - <entry></entry> - <entry>Will be changed to a <literal>G_TYPE_INT16</literal> once GLib has it</entry> - </row><row> - <entry><literal>UINT16</literal></entry> - <entry><literal>G_TYPE_UINT</literal></entry> - <entry></entry> - <entry>Will be changed to a <literal>G_TYPE_UINT16</literal> once GLib has it</entry> - </row><row> - <entry><literal>INT32</literal></entry> - <entry><literal>G_TYPE_INT</literal></entry> - <entry></entry> - <entry>Will be changed to a <literal>G_TYPE_INT32</literal> once GLib has it</entry> - </row><row> - <entry><literal>UINT32</literal></entry> - <entry><literal>G_TYPE_UINT</literal></entry> - <entry></entry> - <entry>Will be changed to a <literal>G_TYPE_UINT32</literal> once GLib has it</entry> - </row><row> - <entry><literal>INT64</literal></entry> - <entry><literal>G_TYPE_GINT64</literal></entry> - <entry></entry> - <entry></entry> - </row><row> - <entry><literal>UINT64</literal></entry> - <entry><literal>G_TYPE_GUINT64</literal></entry> - <entry></entry> - <entry></entry> - </row><row> - <entry><literal>DOUBLE</literal></entry> - <entry><literal>G_TYPE_DOUBLE</literal></entry> - <entry></entry> - <entry></entry> - </row><row> - <entry><literal>STRING</literal></entry> - <entry><literal>G_TYPE_STRING</literal></entry> - <entry><literal>g_free</literal></entry> - <entry></entry> - </row><row> - <entry><literal>OBJECT_PATH</literal></entry> - <entry><literal>DBUS_TYPE_G_PROXY</literal></entry> - <entry><literal>g_object_unref</literal></entry> - <entry>The returned proxy does not have an interface set; use <literal>dbus_g_proxy_set_interface</literal> to invoke methods</entry> - </row> - </tbody> - </tgroup> - </informaltable> - As you can see, the basic mapping is fairly straightforward. - </para> - </sect3> - <sect3 id="glib-container-typemappings"> - <title>Container type mappings</title> - <para> - The D-Bus type system also has a number of "container" - types, such as <literal>DBUS_TYPE_ARRAY</literal> and - <literal>DBUS_TYPE_STRUCT</literal>. The D-Bus type system - is fully recursive, so one can for example have an array of - array of strings (i.e. type signature - <literal>aas</literal>). - </para> - <para> - However, not all of these types are in common use; for - example, at the time of this writing the author knows of no - one using <literal>DBUS_TYPE_STRUCT</literal>, or a - <literal>DBUS_TYPE_ARRAY</literal> containing any non-basic - type. The approach the GLib bindings take is pragmatic; try - to map the most common types in the most obvious way, and - let using less common and more complex types be less - "natural". - </para> - <para> - First, D-Bus type signatures which have an "obvious" - corresponding built-in GLib type are mapped using that type: - <informaltable> - <tgroup cols="6"> - <thead> - <row> - <entry>D-Bus type signature</entry> - <entry>Description</entry> - <entry>GType</entry> - <entry>C typedef</entry> - <entry>Free function</entry> - <entry>Notes</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>as</literal></entry> - <entry>Array of strings</entry> - <entry><literal>G_TYPE_STRV</literal></entry> - <entry><literal>char **</literal></entry> - <entry><literal>g_strfreev</literal></entry> - <entry></entry> - </row><row> - <entry><literal>v</literal></entry> - <entry>Generic value container</entry> - <entry><literal>G_TYPE_VALUE</literal></entry> - <entry><literal>GValue *</literal></entry> - <entry><literal>g_value_unset</literal></entry> - <entry>The calling conventions for values expect that method callers have allocated return values; see below.</entry> - </row> - </tbody> - </tgroup> - </informaltable> - </para> - <para> - The next most common recursive type signatures are arrays of - basic values. The most obvious mapping for arrays of basic - types is a <literal>GArray</literal>. Now, GLib does not - provide a builtin <literal>GType</literal> for - <literal>GArray</literal>. However, we actually need more than - that - we need a "parameterized" type which includes the - contained type. Why we need this we will see below. - </para> - <para> - The approach taken is to create these types in the D-Bus GLib - bindings; however, there is nothing D-Bus specific about them. - In the future, we hope to include such "fundamental" types in GLib - itself. - <informaltable> - <tgroup cols="6"> - <thead> - <row> - <entry>D-Bus type signature</entry> - <entry>Description</entry> - <entry>GType</entry> - <entry>C typedef</entry> - <entry>Free function</entry> - <entry>Notes</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>ay</literal></entry> - <entry>Array of bytes</entry> - <entry><literal>DBUS_TYPE_G_UCHAR_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>au</literal></entry> - <entry>Array of uint</entry> - <entry><literal>DBUS_TYPE_G_UINT_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>ai</literal></entry> - <entry>Array of int</entry> - <entry><literal>DBUS_TYPE_G_INT_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>ax</literal></entry> - <entry>Array of int64</entry> - <entry><literal>DBUS_TYPE_G_INT64_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>at</literal></entry> - <entry>Array of uint64</entry> - <entry><literal>DBUS_TYPE_G_UINT64_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>ad</literal></entry> - <entry>Array of double</entry> - <entry><literal>DBUS_TYPE_G_DOUBLE_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - <row> - <entry><literal>ab</literal></entry> - <entry>Array of boolean</entry> - <entry><literal>DBUS_TYPE_G_BOOLEAN_ARRAY</literal></entry> - <entry><literal>GArray *</literal></entry> - <entry>g_array_free</entry> - <entry></entry> - </row> - </tbody> - </tgroup> - </informaltable> - </para> - <para> - D-Bus also includes a special type DBUS_TYPE_DICT_ENTRY which - is only valid in arrays. It's intended to be mapped to a "dictionary" - type by bindings. The obvious GLib mapping here is GHashTable. Again, - however, there is no builtin <literal>GType</literal> for a GHashTable. - Moreover, just like for arrays, we need a parameterized type so that - the bindings can communiate which types are contained in the hash table. - </para> - <para> - At present, only strings are supported. Work is in progress to - include more types. - <informaltable> - <tgroup cols="6"> - <thead> - <row> - <entry>D-Bus type signature</entry> - <entry>Description</entry> - <entry>GType</entry> - <entry>C typedef</entry> - <entry>Free function</entry> - <entry>Notes</entry> - </row> - </thead> - <tbody> - <row> - <entry><literal>a{ss}</literal></entry> - <entry>Dictionary mapping strings to strings</entry> - <entry><literal>DBUS_TYPE_G_STRING_STRING_HASHTABLE</literal></entry> - <entry><literal>GHashTable *</literal></entry> - <entry>g_hash_table_destroy</entry> - <entry></entry> - </row> - </tbody> - </tgroup> - </informaltable> - </para> - </sect3> - <sect3 id="glib-generic-typemappings"> - <title>Arbitrarily recursive type mappings</title> - <para> - Finally, it is possible users will want to write or invoke D-Bus - methods which have arbitrarily complex type signatures not - directly supported by these bindings. For this case, we have a - <literal>DBusGValue</literal> which acts as a kind of special - variant value which may be iterated over manually. The - <literal>GType</literal> associated is - <literal>DBUS_TYPE_G_VALUE</literal>. - </para> - <para> - TODO insert usage of <literal>DBUS_TYPE_G_VALUE</literal> here. - </para> - </sect3> - </sect2> - <sect2 id="sample-program-1"> - <title>A sample program</title> - <para>Here is a D-Bus program using the GLib bindings. -<programlisting> -int -main (int argc, char **argv) -{ - DBusGConnection *connection; - GError *error; - DBusGProxy *proxy; - char **name_list; - char **name_list_ptr; - - g_type_init (); - - error = NULL; - connection = dbus_g_bus_get (DBUS_BUS_SESSION, - &error); - if (connection == NULL) - { - g_printerr ("Failed to open connection to bus: %s\n", - error->message); - g_error_free (error); - exit (1); - } - - /* Create a proxy object for the "bus driver" (name "org.freedesktop.DBus") */ - - proxy = dbus_g_proxy_new_for_name (connection, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - - /* Call ListNames method, wait for reply */ - error = NULL; - if (!dbus_g_proxy_call (proxy, "ListNames", &error, G_TYPE_INVALID, - G_TYPE_STRV, &name_list, G_TYPE_INVALID)) - { - /* Just do demonstrate remote exceptions versus regular GError */ - if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) - g_printerr ("Caught remote method exception %s: %s", - dbus_g_error_get_name (error), - error->message); - else - g_printerr ("Error: %s\n", error->message); - g_error_free (error); - exit (1); - } - - /* Print the results */ - - g_print ("Names on the message bus:\n"); - - for (name_list_ptr = name_list; *name_list_ptr; name_list_ptr++) - { - g_print (" %s\n", *name_list_ptr); - } - g_strfreev (name_list); - - g_object_unref (proxy); - - return 0; -} -</programlisting> - </para> - </sect2> - <sect2 id="glib-program-setup"> - <title>Program initalization</title> - <para> - A connection to the bus is acquired using - <literal>dbus_g_bus_get</literal>. Next, a proxy - is created for the object "/org/freedesktop/DBus" with - interface <literal>org.freedesktop.DBus</literal> - on the service <literal>org.freedesktop.DBus</literal>. - This is a proxy for the message bus itself. - </para> - </sect2> - <sect2 id="glib-method-invocation"> - <title>Understanding method invocation</title> - <para> - You have a number of choices for method invocation. First, as - used above, <literal>dbus_g_proxy_call</literal> sends a - method call to the remote object, and blocks until a reply is - recieved. The outgoing arguments are specified in the varargs - array, terminated with <literal>G_TYPE_INVALID</literal>. - Next, pointers to return values are specified, followed again - by <literal>G_TYPE_INVALID</literal>. - </para> - <para> - To invoke a method asynchronously, use - <literal>dbus_g_proxy_begin_call</literal>. This returns a - <literal>DBusGPendingCall</literal> object; you may then set a - notification function using - <literal>dbus_g_pending_call_set_notify</literal>. - </para> - </sect2> - <sect2 id="glib-signal-connection"> - <title>Connecting to object signals</title> - <para> - You may connect to signals using - <literal>dbus_g_proxy_add_signal</literal> and - <literal>dbus_g_proxy_connect_signal</literal>. You must - invoke <literal>dbus_g_proxy_add_signal</literal> to specify - the signature of your signal handlers; you may then invoke - <literal>dbus_g_proxy_connect_signal</literal> multiple times. - </para> - <para> - Note that it will often be the case that there is no builtin - marshaller for the type signature of a remote signal. In that - case, you must generate a marshaller yourself by using - <application>glib-genmarshal</application>, and then register - it using <literal>dbus_g_object_register_marshaller</literal>. - </para> - </sect2> - <sect2 id="glib-error-handling"> - <title>Error handling and remote exceptions</title> - <para> - All of the GLib binding methods such as - <literal>dbus_g_proxy_end_call</literal> return a - <literal>GError</literal>. This <literal>GError</literal> can - represent two different things: - <itemizedlist> - <listitem> - <para> - An internal D-Bus error, such as an out-of-memory - condition, an I/O error, or a network timeout. Errors - generated by the D-Bus library itself have the domain - <literal>DBUS_GERROR</literal>, and a corresponding code - such as <literal>DBUS_GERROR_NO_MEMORY</literal>. It will - not be typical for applications to handle these errors - specifically. - </para> - </listitem> - <listitem> - <para> - A remote D-Bus exception, thrown by the peer, bus, or - service. D-Bus remote exceptions have both a textual - "name" and a "message". The GLib bindings store this - information in the <literal>GError</literal>, but some - special rules apply. - </para> - <para> - The set error will have the domain - <literal>DBUS_GERROR</literal> as above, and will also - have the code - <literal>DBUS_GERROR_REMOTE_EXCEPTION</literal>. In order - to access the remote exception name, you must use a - special accessor, such as - <literal>dbus_g_error_has_name</literal> or - <literal>dbus_g_error_get_name</literal>. The remote - exception detailed message is accessible via the regular - GError <literal>message</literal> member. - </para> - </listitem> - </itemizedlist> - </para> - </sect2> - <sect2 id="glib-more-examples"> - <title>More examples of method invocation</title> - <sect3 id="glib-sending-stuff"> - <title>Sending an integer and string, receiving an array of bytes</title> - <para> -<programlisting> - GArray *arr; - - error = NULL; - if (!dbus_g_proxy_call (proxy, "Foobar", &error, - G_TYPE_INT, 42, G_TYPE_STRING, "hello", - G_TYPE_INVALID, - DBUS_TYPE_G_UCHAR_ARRAY, &arr, G_TYPE_INVALID)) - { - /* Handle error */ - } - g_assert (arr != NULL); - printf ("got back %u values", arr->len); -</programlisting> - </para> - </sect3> - <sect3 id="glib-sending-hash"> - <title>Sending a GHashTable</title> - <para> -<programlisting> - GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal); - guint32 ret; - - g_hash_table_insert (hash, "foo", "bar"); - g_hash_table_insert (hash, "baz", "whee"); - - error = NULL; - if (!dbus_g_proxy_call (proxy, "HashSize", &error, - DBUS_TYPE_G_STRING_STRING_HASH, hash, G_TYPE_INVALID, - G_TYPE_UINT, &ret, G_TYPE_INVALID)) - { - /* Handle error */ - } - g_assert (ret == 2); - g_hash_table_destroy (hash); -</programlisting> - </para> - </sect3> - <sect3 id="glib-receiving-bool-int"> - <title>Receiving a boolean and a string</title> - <para> -<programlisting> - gboolean boolret; - char *strret; - - error = NULL; - if (!dbus_g_proxy_call (proxy, "GetStuff", &error, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &boolret, - G_TYPE_STRING, &strret, - G_TYPE_INVALID)) - { - /* Handle error */ - } - printf ("%s %s", boolret ? "TRUE" : "FALSE", strret); - g_free (strret); -</programlisting> - </para> - </sect3> - <sect3 id="glib-sending-str-arrays"> - <title>Sending two arrays of strings</title> - <para> -<programlisting> - /* NULL terminate */ - char *strs_static[] = {"foo", "bar", "baz", NULL}; - /* Take pointer to array; cannot pass array directly */ - char **strs_static_p = strs_static; - char **strs_dynamic; - - strs_dynamic = g_new (char *, 4); - strs_dynamic[0] = g_strdup ("hello"); - strs_dynamic[1] = g_strdup ("world"); - strs_dynamic[2] = g_strdup ("!"); - /* NULL terminate */ - strs_dynamic[3] = NULL; - - error = NULL; - if (!dbus_g_proxy_call (proxy, "TwoStrArrays", &error, - G_TYPE_STRV, strs_static_p, - G_TYPE_STRV, strs_dynamic, - G_TYPE_INVALID, - G_TYPE_INVALID)) - { - /* Handle error */ - } - g_strfreev (strs_dynamic); -</programlisting> - </para> - </sect3> - <sect3 id="glib-getting-str-array"> - <title>Sending a boolean, receiving an array of strings</title> - <para> -<programlisting> - char **strs; - char **strs_p; - gboolean blah; - - error = NULL; - blah = TRUE; - if (!dbus_g_proxy_call (proxy, "GetStrs", &error, - G_TYPE_BOOLEAN, blah, - G_TYPE_INVALID, - G_TYPE_STRV, &strs, - G_TYPE_INVALID)) - { - /* Handle error */ - } - for (strs_p = strs; *strs_p; strs_p++) - printf ("got string: \"%s\"", *strs_p); - g_strfreev (strs); -</programlisting> - </para> - </sect3> - <sect3 id="glib-sending-variant"> - <title>Sending a variant</title> - <para> -<programlisting> - GValue val = {0, }; - - g_value_init (&val, G_TYPE_STRING); - g_value_set_string (&val, "hello world"); - - error = NULL; - if (!dbus_g_proxy_call (proxy, "SendVariant", &error, - G_TYPE_VALUE, &val, G_TYPE_INVALID, - G_TYPE_INVALID)) - { - /* Handle error */ - } - g_assert (ret == 2); - g_value_unset (&val); -</programlisting> - </para> - </sect3> - <sect3 id="glib-receiving-variant"> - <title>Receiving a variant</title> - <para> -<programlisting> - GValue val = {0, }; - - error = NULL; - if (!dbus_g_proxy_call (proxy, "GetVariant", &error, G_TYPE_INVALID, - G_TYPE_VALUE, &val, G_TYPE_INVALID)) - { - /* Handle error */ - } - if (G_VALUE_TYPE (&val) == G_TYPE_STRING) - printf ("%s\n", g_value_get_string (&val)); - else if (G_VALUE_TYPE (&val) == G_TYPE_INT) - printf ("%d\n", g_value_get_int (&val)); - else - ... - g_value_unset (&val); -</programlisting> - </para> - </sect3> - </sect2> - - <sect2 id="glib-generated-bindings"> - <title>Generated Bindings</title> - <para> - By using the Introspection XML files, convenient client-side bindings - can be automatically created to ease the use of a remote DBus object. - </para> - <para> - Here is a sample XML file which describes an object that exposes - one method, named <literal>ManyArgs</literal>. - <programlisting> -<?xml version="1.0" encoding="UTF-8" ?> -<node name="/com/example/MyObject"> - <interface name="com.example.MyObject"> - <method name="ManyArgs"> - <arg type="u" name="x" direction="in" /> - <arg type="s" name="str" direction="in" /> - <arg type="d" name="trouble" direction="in" /> - <arg type="d" name="d_ret" direction="out" /> - <arg type="s" name="str_ret" direction="out" /> - </method> - </interface> -</node> -</programlisting> - </para> - <para> - Run <literal>dbus-binding-tool --mode=glib-client - <replaceable>FILENAME</replaceable> > - <replaceable>HEADER_NAME</replaceable></literal> to generate the header - file. For example: <command>dbus-binding-tool --mode=glib-client - my-object.xml > my-object-bindings.h</command>. This will generate - inline functions with the following prototypes: - <programlisting> -/* This is a blocking call */ -gboolean -com_example_MyObject_many_args (DBusGProxy *proxy, const guint IN_x, - const char * IN_str, const gdouble IN_trouble, - gdouble* OUT_d_ret, char ** OUT_str_ret, - GError **error); - -/* This is a non-blocking call */ -DBusGProxyCall* -com_example_MyObject_many_args_async (DBusGProxy *proxy, const guint IN_x, - const char * IN_str, const gdouble IN_trouble, - com_example_MyObject_many_args_reply callback, - gpointer userdata); - -/* This is the typedef for the non-blocking callback */ -typedef void -(*com_example_MyObject_many_args_reply) -(DBusGProxy *proxy, gdouble OUT_d_ret, char * OUT_str_ret, - GError *error, gpointer userdata); -</programlisting> - The first argument in all functions is a <literal>DBusGProxy - *</literal>, which you should create with the usual - <literal>dbus_g_proxy_new_*</literal> functions. Following that are the - "in" arguments, and then either the "out" arguments and a - <literal>GError *</literal> for the synchronous (blocking) function, or - callback and user data arguments for the asynchronous (non-blocking) - function. The callback in the asynchronous function passes the - <literal>DBusGProxy *</literal>, the returned "out" arguments, an - <literal>GError *</literal> which is set if there was an error otherwise - <literal>NULL</literal>, and the user data. - </para> - <para> - As with the server-side bindings support (see <xref - linkend="glib-server"/>), the exact behaviour of the client-side - bindings can be manipulated using "annotations". Currently the only - annotation used by the client bindings is - <literal>org.freedesktop.DBus.GLib.NoReply</literal>, which sets the - flag indicating that the client isn't expecting a reply to the method - call, so a reply shouldn't be sent. This is often used to speed up - rapid method calls where there are no "out" arguments, and not knowing - if the method succeeded is an acceptable compromise to half the traffic - on the bus. - </para> - </sect2> - </sect1> - - <sect1 id="glib-server"> - <title>GLib API: Implementing Objects</title> + <title>GLib APIs</title> <para> - At the moment, to expose a GObject via D-Bus, you must - write XML by hand which describes the methods exported - by the object. In the future, this manual step will - be obviated by the upcoming GLib introspection support. + The recommended GLib API for D-Bus is GDBus, which has been + distributed with GLib since version 2.26. It is not documented here. + See <ulink url="https://developer.gnome.org/gio/stable/gdbus-convenience.html">the + GLib documentation</ulink> for details of how to use GDBus. </para> - <para> - Here is a sample XML file which describes an object that exposes - one method, named <literal>ManyArgs</literal>. -<programlisting> -<?xml version="1.0" encoding="UTF-8" ?> - -<node name="/com/example/MyObject"> - <interface name="com.example.MyObject"> - <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object"/> - <method name="ManyArgs"> - <!-- This is optional, and in this case is redunundant --> - <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_many_args"/> - <arg type="u" name="x" direction="in" /> - <arg type="s" name="str" direction="in" /> - <arg type="d" name="trouble" direction="in" /> - <arg type="d" name="d_ret" direction="out" /> - <arg type="s" name="str_ret" direction="out" /> - </method> - </interface> -</node> -</programlisting> - </para> <para> - This XML is in the same format as the D-Bus introspection XML - format. Except we must include an "annotation" which give the C - symbols corresponding to the object implementation prefix - (<literal>my_object</literal>). In addition, if particular - methods symbol names deviate from C convention - (i.e. <literal>ManyArgs</literal> -> - <literal>many_args</literal>), you may specify an annotation - giving the C symbol. + An older API, dbus-glib, also exists. It is deprecated and should + not be used in new code. Whenever possible, porting existing code + from dbus-glib to GDBus is also recommended. </para> - <para> - Once you have written this XML, run <literal>dbus-binding-tool --mode=glib-server <replaceable>FILENAME</replaceable> > <replaceable>HEADER_NAME</replaceable>.</literal> to - generate a header file. For example: <command>dbus-binding-tool --mode=glib-server my-object.xml > my-object-glue.h</command>. - </para> - <para> - Next, include the generated header in your program, and invoke - <literal>dbus_g_object_class_install_info</literal> in the class - initializer, passing the object class and "object info" included in the - header. For example: - <programlisting> - dbus_g_object_type_install_info (COM_FOO_TYPE_MY_OBJECT, &com_foo_my_object_info); - </programlisting> - This should be done exactly once per object class. - </para> - <para> - To actually implement the method, just define a C function named e.g. - <literal>my_object_many_args</literal> in the same file as the info - header is included. At the moment, it is required that this function - conform to the following rules: - <itemizedlist> - <listitem> - <para> - The function must return a value of type <literal>gboolean</literal>; - <literal>TRUE</literal> on success, and <literal>FALSE</literal> - otherwise. - </para> - </listitem> - <listitem> - <para> - The first parameter is a pointer to an instance of the object. - </para> - </listitem> - <listitem> - <para> - Following the object instance pointer are the method - input values. - </para> - </listitem> - <listitem> - <para> - Following the input values are pointers to return values. - </para> - </listitem> - <listitem> - <para> - The final parameter must be a <literal>GError **</literal>. - If the function returns <literal>FALSE</literal> for an - error, the error parameter must be initalized with - <literal>g_set_error</literal>. - </para> - </listitem> - </itemizedlist> - </para> - <para> - Finally, you can export an object using <literal>dbus_g_connection_register_g_object</literal>. For example: - <programlisting> - dbus_g_connection_register_g_object (connection, - "/com/foo/MyObject", - obj); - </programlisting> - </para> - - <sect2 id="glib-annotations"> - <title>Server-side Annotations</title> - <para> - There are several annotations that are used when generating the - server-side bindings. The most common annotation is - <literal>org.freedesktop.DBus.GLib.CSymbol</literal> but there are other - annotations which are often useful. - <variablelist> - <varlistentry> - <term><literal>org.freedesktop.DBus.GLib.CSymbol</literal></term> - <listitem> - <para> - This annotation is used to specify the C symbol names for - the various types (interface, method, etc), if it differs from the - name DBus generates. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term><literal>org.freedesktop.DBus.GLib.Async</literal></term> - <listitem> - <para> - This annotation marks the method implementation as an - asynchronous function, which doesn't return a response straight - away but will send the response at some later point to complete - the call. This is used to implement non-blocking services where - method calls can take time. - </para> - <para> - When a method is asynchronous, the function prototype is - different. It is required that the function conform to the - following rules: - <itemizedlist> - <listitem> - <para> - The function must return a value of type <literal>gboolean</literal>; - <literal>TRUE</literal> on success, and <literal>FALSE</literal> - otherwise. TODO: the return value is currently ignored. - </para> - </listitem> - <listitem> - <para> - The first parameter is a pointer to an instance of the object. - </para> - </listitem> - <listitem> - <para> - Following the object instance pointer are the method - input values. - </para> - </listitem> - <listitem> - <para> - The final parameter must be a - <literal>DBusGMethodInvocation *</literal>. This is used - when sending the response message back to the client, by - calling <literal>dbus_g_method_return</literal> or - <literal>dbus_g_method_return_error</literal>. - </para> - </listitem> - </itemizedlist> - </para> - </listitem> - </varlistentry> - <varlistentry> - <term><literal>org.freedesktop.DBus.GLib.Const</literal></term> - <listitem> - <para>This attribute can only be applied to "out" - <literal><arg></literal> nodes, and specifies that the - parameter isn't being copied when returned. For example, this - turns a 's' argument from a <literal>char **</literal> to a - <literal>const char **</literal>, and results in the argument not - being freed by DBus after the message is sent. - </para> - </listitem> - </varlistentry> - <varlistentry> - <term><literal>org.freedesktop.DBus.GLib.ReturnVal</literal></term> - <listitem> - <para> - This attribute can only be applied to "out" - <literal><arg></literal> nodes, and alters the expected - function signature. It currently can be set to two values: - <literal>""</literal> or <literal>"error"</literal>. The - argument marked with this attribute is not returned via a - pointer argument, but by the function's return value. If the - attribute's value is the empty string, the <literal>GError - *</literal> argument is also omitted so there is no standard way - to return an error value. This is very useful for interfacing - with existing code, as it is possible to match existing APIs. - If the attribute's value is <literal>"error"</literal>, then the - final argument is a <literal>GError *</literal> as usual. - </para> - <para> - Some examples to demonstrate the usage. This introspection XML: - <programlisting> -<method name="Increment"> - <arg type="u" name="x" /> - <arg type="u" direction="out" /> -</method> - </programlisting> - Expects the following function declaration: - <programlisting> -gboolean -my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error); - </programlisting> - </para> - <para> - This introspection XML: - <programlisting> -<method name="IncrementRetval"> - <arg type="u" name="x" /> - <arg type="u" direction="out" > - <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value=""/> - </arg> -</method> - </programlisting> - Expects the following function declaration: - <programlisting> -gint32 -my_object_increment_retval (MyObject *obj, gint32 x) - </programlisting> - </para> - <para> - This introspection XML: - <programlisting> -<method name="IncrementRetvalError"> - <arg type="u" name="x" /> - <arg type="u" direction="out" > - <annotation name="org.freedesktop.DBus.GLib.ReturnVal" value="error"/> - </arg> -</method> - </programlisting> - Expects the following function declaration: - <programlisting> -gint32 -my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error) - </programlisting> - </para> - </listitem> - </varlistentry> - </variablelist> - </para> - </sect2> </sect1> <sect1 id="python-client"> @@ -1650,18 +730,12 @@ my_object_increment_retval_error (MyObject *obj, gint32 x, GError **error) </sect1> <sect1 id="qt-client"> - <title>Qt API: Using Remote Objects</title> - <para> - - The Qt bindings are not yet documented. - - </para> - </sect1> - - <sect1 id="qt-server"> - <title>Qt API: Implementing Objects</title> + <title>Qt API</title> <para> - The Qt bindings are not yet documented. + The Qt binding for libdbus, QtDBus, has been distributed with Qt + since version 4.2. It is not documented here. See + <ulink url="http://qt-project.org/doc/qt-5/qtdbus-index.html">the Qt + documentation</ulink> for details of how to use QtDBus. </para> </sect1> </article> @@ -70,7 +70,7 @@ # compiler: $LTCC # compiler flags: $LTCFLAGS # linker: $LD (gnu? $with_gnu_ld) -# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.10 +# $progname: (GNU libtool) 2.4.2 Debian-2.4.2-1.11 # automake: $automake_version # autoconf: $autoconf_version # @@ -80,7 +80,7 @@ PROGRAM=libtool PACKAGE=libtool -VERSION="2.4.2 Debian-2.4.2-1.10" +VERSION="2.4.2 Debian-2.4.2-1.11" TIMESTAMP="" package_revision=1.3337 diff --git a/test/Makefile.am b/test/Makefile.am index 1ceb5b68..173df74b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -254,6 +254,7 @@ in_data = \ data/valid-config-files-system/debug-allow-all-pass.conf.in \ data/valid-config-files/debug-allow-all-sha1.conf.in \ data/valid-config-files/debug-allow-all.conf.in \ + data/valid-config-files/finite-timeout.conf.in \ data/valid-config-files/incoming-limit.conf.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \ diff --git a/test/Makefile.in b/test/Makefile.in index 46b8c095..4a5a343c 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -746,6 +746,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -947,6 +948,7 @@ in_data = \ data/valid-config-files-system/debug-allow-all-pass.conf.in \ data/valid-config-files/debug-allow-all-sha1.conf.in \ data/valid-config-files/debug-allow-all.conf.in \ + data/valid-config-files/finite-timeout.conf.in \ data/valid-config-files/incoming-limit.conf.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \ data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \ diff --git a/test/data/valid-config-files/finite-timeout.conf.in b/test/data/valid-config-files/finite-timeout.conf.in new file mode 100644 index 00000000..7d26d715 --- /dev/null +++ b/test/data/valid-config-files/finite-timeout.conf.in @@ -0,0 +1,19 @@ +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + <!-- Our well-known bus type, don't change this --> + <type>session</type> + <listen>@TEST_LISTEN@</listen> + + <policy context="default"> + <!-- Allow everything to be sent --> + <allow send_destination="*" eavesdrop="true"/> + <!-- Allow everything to be received --> + <allow eavesdrop="true"/> + <!-- Allow anyone to own anything --> + <allow own="*"/> + </policy> + + <!-- Forcibly time out method calls after 100ms --> + <limit name="reply_timeout">100</limit> +</busconfig> diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index 4b3b61e5..bb1d1374 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -43,6 +43,31 @@ #include "test-utils.h" +/* Platforms where we know that credentials-passing passes both the + * uid and the pid. Please keep these in alphabetical order. + * + * These platforms should #error in _dbus_read_credentials_socket() + * if we didn't detect their flavour of credentials-passing, since that + * would be a regression. + */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \ + defined(__linux__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +# define UNIX_USER_SHOULD_WORK +# define PID_SHOULD_WORK +#endif + +/* Platforms where we know that credentials-passing passes the + * uid, but not necessarily the pid. Again, alphabetical order please. + * + * These platforms should also #error in _dbus_read_credentials_socket() + * if we didn't detect their flavour of credentials-passing. + */ +#if 0 /* defined(__your_platform_here__) */ +# define UNIX_USER_SHOULD_WORK +#endif + typedef struct { gboolean skip; @@ -57,6 +82,7 @@ typedef struct { DBusConnection *right_conn; gboolean right_conn_echo; + gboolean wait_forever_called; } Fixture; #define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__) @@ -157,11 +183,19 @@ echo_filter (DBusConnection *connection, DBusMessage *message, void *user_data) { + Fixture *f = user_data; DBusMessage *reply; if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + /* WaitForever() never replies, emulating a service that has got stuck */ + if (dbus_message_is_method_call (message, "com.example", "WaitForever")) + { + f->wait_forever_called = TRUE; + return DBUS_HANDLER_RESULT_HANDLED; + } + reply = dbus_message_new_method_return (message); if (reply == NULL) @@ -257,7 +291,7 @@ setup (Fixture *f, static void add_echo_filter (Fixture *f) { - if (!dbus_connection_add_filter (f->right_conn, echo_filter, NULL, NULL)) + if (!dbus_connection_add_filter (f->right_conn, echo_filter, f, NULL)) g_error ("OOM"); f->right_conn_echo = TRUE; @@ -343,6 +377,80 @@ pending_call_store_reply (DBusPendingCall *pc, } static void +test_no_reply (Fixture *f, + gconstpointer context) +{ + const Config *config = context; + DBusMessage *m; + DBusPendingCall *pc; + DBusMessage *reply = NULL; + enum { TIMEOUT, DISCONNECT } mode; + gboolean ok; + + if (f->skip) + return; + + g_test_bug ("76112"); + + if (config != NULL && config->config_file != NULL) + mode = TIMEOUT; + else + mode = DISCONNECT; + + m = dbus_message_new_method_call ( + dbus_bus_get_unique_name (f->right_conn), "/", + "com.example", "WaitForever"); + + add_echo_filter (f); + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, + DBUS_TIMEOUT_INFINITE) || + pc == NULL) + g_error ("OOM"); + + if (dbus_pending_call_get_completed (pc)) + pending_call_store_reply (pc, &reply); + else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply, &reply, + NULL)) + g_error ("OOM"); + + dbus_pending_call_unref (pc); + dbus_message_unref (m); + + if (mode == DISCONNECT) + { + while (!f->wait_forever_called) + test_main_context_iterate (f->ctx, TRUE); + + dbus_connection_remove_filter (f->right_conn, echo_filter, f); + dbus_connection_close (f->right_conn); + dbus_connection_unref (f->right_conn); + f->right_conn = NULL; + } + + while (reply == NULL) + test_main_context_iterate (f->ctx, TRUE); + + /* using inefficient string comparison for better assertion message */ + g_assert_cmpstr ( + dbus_message_type_to_string (dbus_message_get_type (reply)), ==, + dbus_message_type_to_string (DBUS_MESSAGE_TYPE_ERROR)); + ok = dbus_set_error_from_message (&f->e, reply); + g_assert (ok); + g_assert_cmpstr (f->e.name, ==, DBUS_ERROR_NO_REPLY); + + if (mode == DISCONNECT) + g_assert_cmpstr (f->e.message, ==, + "Message recipient disconnected from message bus without replying"); + else + g_assert_cmpstr (f->e.message, ==, + "Message did not receive a reply (timeout by message bus)"); +} + +static void test_creds (Fixture *f, gconstpointer context) { @@ -445,8 +553,11 @@ test_creds (Fixture *f, dbus_message_iter_next (&arr_iter); } -#ifdef G_OS_UNIX +#ifdef UNIX_USER_SHOULD_WORK g_assert (seen & SEEN_UNIX_USER); +#endif + +#ifdef PID_SHOULD_WORK g_assert (seen & SEEN_PID); #endif @@ -458,6 +569,72 @@ test_creds (Fixture *f, } static void +test_processid (Fixture *f, + gconstpointer context) +{ + const char *unique = dbus_bus_get_unique_name (f->left_conn); + DBusMessage *m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionUnixProcessID"); + DBusPendingCall *pc; + DBusError error = DBUS_ERROR_INIT; + guint32 pid; + + if (m == NULL) + g_error ("OOM"); + + if (!dbus_message_append_args (m, + DBUS_TYPE_STRING, &unique, + DBUS_TYPE_INVALID)) + g_error ("OOM"); + + if (!dbus_connection_send_with_reply (f->left_conn, m, &pc, + DBUS_TIMEOUT_USE_DEFAULT) || + pc == NULL) + g_error ("OOM"); + + dbus_message_unref (m); + m = NULL; + + if (dbus_pending_call_get_completed (pc)) + pending_call_store_reply (pc, &m); + else if (!dbus_pending_call_set_notify (pc, pending_call_store_reply, + &m, NULL)) + g_error ("OOM"); + + while (m == NULL) + test_main_context_iterate (f->ctx, TRUE); + + if (dbus_message_get_args (m, &error, + DBUS_TYPE_UINT32, &pid, + DBUS_TYPE_INVALID)) + { + g_assert_cmpstr (dbus_message_get_signature (m), ==, "u"); + assert_no_error (&error); + + g_message ("GetConnectionUnixProcessID returned %u", pid); + +#ifdef G_OS_UNIX + g_assert_cmpuint (pid, ==, getpid ()); +#elif defined(G_OS_WIN32) + g_assert_cmpuint (pid, ==, GetCurrentProcessId ()); +#else + g_assert_not_reached (); +#endif + } + else + { + g_assert_cmpstr (error.name, ==, DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN); + +#ifdef PID_SHOULD_WORK + g_error ("Expected pid to be passed, but got %s: %s", + error.name, error.message); +#endif + + dbus_error_free (&error); + } +} + +static void teardown (Fixture *f, gconstpointer context G_GNUC_UNUSED) { @@ -475,7 +652,7 @@ teardown (Fixture *f, { if (f->right_conn_echo) { - dbus_connection_remove_filter (f->right_conn, echo_filter, NULL); + dbus_connection_remove_filter (f->right_conn, echo_filter, f); f->right_conn_echo = FALSE; } @@ -503,6 +680,10 @@ static Config limited_config = { "34393", 10000, "valid-config-files/incoming-limit.conf" }; +static Config finite_timeout_config = { + NULL, 1, "valid-config-files/finite-timeout.conf" +}; + int main (int argc, char **argv) @@ -513,7 +694,12 @@ main (int argc, g_test_add ("/echo/session", Fixture, NULL, setup, test_echo, teardown); g_test_add ("/echo/limited", Fixture, &limited_config, setup, test_echo, teardown); + g_test_add ("/no-reply/disconnect", Fixture, NULL, + setup, test_no_reply, teardown); + g_test_add ("/no-reply/timeout", Fixture, &finite_timeout_config, + setup, test_no_reply, teardown); g_test_add ("/creds", Fixture, NULL, setup, test_creds, teardown); + g_test_add ("/processid", Fixture, NULL, setup, test_processid, teardown); return g_test_run (); } diff --git a/test/name-test/Makefile.in b/test/name-test/Makefile.in index 466bd54d..392cf8ba 100644 --- a/test/name-test/Makefile.in +++ b/test/name-test/Makefile.in @@ -574,6 +574,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ diff --git a/test/name-test/test-threads-init.c b/test/name-test/test-threads-init.c index 580ffe14..a517e2a2 100644 --- a/test/name-test/test-threads-init.c +++ b/test/name-test/test-threads-init.c @@ -149,16 +149,6 @@ main (int argc, char *argv[]) &dispatch_cond1, &io_path_cond1); - /* Since 1.7 it is no longer the case that mutex1 != mutex2, because - * initializing global locks automatically initializes locks - * in general. However, it is true that the mutex is not the dummy - * implementation, which is what we really wanted to check here. */ - _dbus_assert (mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (dispatch_mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (dispatch_cond1 != (DBusCondVar *) 0xABCDEF2); - _dbus_assert (io_path_mutex1 != (DBusMutex *) 0xABCDEF); - _dbus_assert (io_path_cond1 != (DBusCondVar *) 0xABCDEF2); - _run_iteration (conn); _dbus_connection_test_get_locks (conn, &mutex2, &dispatch_mutex2, diff --git a/tools/GetAllMatchRules.py b/tools/GetAllMatchRules.py new file mode 100755 index 00000000..6a7e4cd9 --- /dev/null +++ b/tools/GetAllMatchRules.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python + +import sys +import argparse +import dbus +import time + +def get_cmdline(pid): + cmdline = '' + if pid > 0: + try: + procpath = '/proc/' + str(pid) + '/cmdline' + with open(procpath, 'r') as f: + cmdline = " ".join(f.readline().split('\0')) + except: + pass + return cmdline + +# Parsing parameters + +parser = argparse.ArgumentParser(description='Testing D-Bus match rules') +parser.add_argument('--session', help='session bus', action="store_true") +parser.add_argument('--system', help='system bus', action="store_true") +parser.add_argument('--all', help='print all match rules', action="store_true") +args = parser.parse_args() + +if args.system and args.session: + parser.print_help() + sys.exit(1) + +# Fetch data from the bus driver + +if args.system: + bus = dbus.SystemBus() +else: + bus = dbus.SessionBus() + +remote_object = bus.get_object("org.freedesktop.DBus", + "/org/freedesktop/DBus") +bus_iface = dbus.Interface(remote_object, "org.freedesktop.DBus") +stats_iface = dbus.Interface(remote_object, "org.freedesktop.DBus.Debug.Stats") + +try: + match_rules = stats_iface.GetAllMatchRules() +except: + print("GetConnectionMatchRules failed: did you enable the Stats interface?") + sys.exit(1) + +names = bus_iface.ListNames() +unique_names = [ a for a in names if a.startswith(":") ] +pids = dict((name, bus_iface.GetConnectionUnixProcessID(name)) for name in unique_names) +cmds = dict((name, get_cmdline(pids[name])) for name in unique_names) +well_known_names = [ a for a in names if a not in unique_names ] +owners = dict((wkn, bus_iface.GetNameOwner(wkn)) for wkn in well_known_names) + +rules = dict((k_rules, + dict({ + 'wkn': [k for k, v in owners.items() if v == k_rules], + 'pid': pids[k_rules], + 'cmd': cmds[k_rules] or "", + 'rules': v_rules, + 'warnings': dict({ + 'not_signal': [a for a in v_rules if "type='signal'" not in a], + 'no_sender': [a for a in v_rules if "sender=" not in a], + 'local': [a for a in v_rules if "org.freedesktop.DBus.Local" in a], + 'NameOwnerChanged_arg0': [a for a in v_rules if "member='NameOwnerChanged'" in a and "arg0" not in a] + }) + }) + ) for k_rules, v_rules in match_rules.items()) + +warnings = dict({ + 'not_signal': 'Match rule without selecting signals', + 'no_sender': 'Match rule without a sender criteria', + 'local': 'Match rule on the org.freedesktop.DBus.Local interface', + 'NameOwnerChanged_arg0': 'Match rule on NameOwnerChanged without a arg0* criteria' + }) + +# Print the match rules + +# print all match rules without analysing them +if args.all: + for name in rules: + print("Connection %s with pid %d '%s' (%s): %d match rules, %d warnings" + % (name, rules[name]['pid'], rules[name]['cmd'], + ' '.join(rules[name]['wkn']), len(rules[name]['rules']), + len(sum(rules[name]['warnings'].values(), [])))) + for rule in rules[name]['rules']: + print("\t%s" % (rule)) + print("") + sys.exit(0) + +# analyse match rules and print only the suspicious ones +for conn,data in rules.items(): + warnings_count = len(sum(data['warnings'].values(), [])) + if warnings_count == 0: + continue + + print("Connection %s with pid %d '%s' (%s): %d match rules, %d warnings" + % (conn, data['pid'], data['cmd'], ' '.join(data['wkn']), + len(data['rules']), warnings_count)) + + for warn_code,rule_list in [(warn_code,rule_list) \ + for warn_code, rule_list \ + in data['warnings'].items() \ + if len(rule_list) > 0]: + print(" - %s:" % (warnings[warn_code])) + for rule in rule_list: + print(" - %s" % (rule)) + + print("") + +sys.exit(0) diff --git a/tools/Makefile.am b/tools/Makefile.am index 73d95fcf..05d1dcb5 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -15,6 +15,7 @@ bin_PROGRAMS = \ dbus-launch \ dbus-monitor \ dbus-send \ + dbus-test-tool \ $(NULL) if DBUS_UNIX @@ -76,6 +77,21 @@ dbus_launch_LDADD = \ $(DBUS_X_LIBS) \ $(NULL) +examplesdir = ${docdir}/examples +dist_examples_SCRIPTS = \ + GetAllMatchRules.py \ + $(NULL) + +dbus_test_tool_SOURCES = \ + dbus-echo.c \ + dbus-spam.c \ + tool-common.c \ + tool-common.h \ + test-tool.c \ + test-tool.h \ + $(NULL) +dbus_test_tool_LDADD = $(top_builddir)/dbus/libdbus-1.la + EXTRA_DIST = run-with-tmp-session-bus.sh strtoll.c strtoull.c CLEANFILES = \ run-with-tmp-session-bus.conf diff --git a/tools/Makefile.in b/tools/Makefile.in index dd562f10..9105bd4d 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -14,6 +14,7 @@ @SET_MAKE@ + VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ @@ -79,7 +80,7 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = dbus-launch$(EXEEXT) dbus-monitor$(EXEEXT) \ - dbus-send$(EXEEXT) $(am__EXEEXT_1) + dbus-send$(EXEEXT) dbus-test-tool$(EXEEXT) $(am__EXEEXT_1) @DBUS_UNIX_TRUE@am__append_1 = \ @DBUS_UNIX_TRUE@ dbus-cleanup-sockets \ @DBUS_UNIX_TRUE@ dbus-run-session \ @@ -88,7 +89,7 @@ bin_PROGRAMS = dbus-launch$(EXEEXT) dbus-monitor$(EXEEXT) \ subdir = tools DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ - $(top_srcdir)/depcomp + $(dist_examples_SCRIPTS) $(top_srcdir)/depcomp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ @@ -106,7 +107,7 @@ CONFIG_CLEAN_VPATH_FILES = @DBUS_UNIX_TRUE@am__EXEEXT_1 = dbus-cleanup-sockets$(EXEEXT) \ @DBUS_UNIX_TRUE@ dbus-run-session$(EXEEXT) \ @DBUS_UNIX_TRUE@ dbus-uuidgen$(EXEEXT) -am__installdirs = "$(DESTDIR)$(bindir)" +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(examplesdir)" PROGRAMS = $(bin_PROGRAMS) am_dbus_cleanup_sockets_OBJECTS = dbus-cleanup-sockets.$(OBJEXT) dbus_cleanup_sockets_OBJECTS = $(am_dbus_cleanup_sockets_OBJECTS) @@ -138,9 +139,41 @@ am_dbus_send_OBJECTS = dbus-print-message.$(OBJEXT) \ dbus-send.$(OBJEXT) dbus_send_OBJECTS = $(am_dbus_send_OBJECTS) dbus_send_DEPENDENCIES = $(top_builddir)/dbus/libdbus-1.la +am_dbus_test_tool_OBJECTS = dbus-echo.$(OBJEXT) dbus-spam.$(OBJEXT) \ + tool-common.$(OBJEXT) test-tool.$(OBJEXT) +dbus_test_tool_OBJECTS = $(am_dbus_test_tool_OBJECTS) +dbus_test_tool_DEPENDENCIES = $(top_builddir)/dbus/libdbus-1.la am_dbus_uuidgen_OBJECTS = dbus-uuidgen.$(OBJEXT) dbus_uuidgen_OBJECTS = $(am_dbus_uuidgen_OBJECTS) dbus_uuidgen_DEPENDENCIES = $(top_builddir)/dbus/libdbus-1.la +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +SCRIPTS = $(dist_examples_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -177,11 +210,12 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(dbus_cleanup_sockets_SOURCES) $(dbus_launch_SOURCES) \ $(dbus_monitor_SOURCES) $(dbus_run_session_SOURCES) \ - $(dbus_send_SOURCES) $(dbus_uuidgen_SOURCES) + $(dbus_send_SOURCES) $(dbus_test_tool_SOURCES) \ + $(dbus_uuidgen_SOURCES) DIST_SOURCES = $(dbus_cleanup_sockets_SOURCES) \ $(am__dbus_launch_SOURCES_DIST) $(dbus_monitor_SOURCES) \ $(am__dbus_run_session_SOURCES_DIST) $(dbus_send_SOURCES) \ - $(dbus_uuidgen_SOURCES) + $(dbus_test_tool_SOURCES) $(dbus_uuidgen_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -375,6 +409,7 @@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ +dbus_daemondir = @dbus_daemondir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -472,6 +507,21 @@ dbus_launch_LDADD = \ $(DBUS_X_LIBS) \ $(NULL) +examplesdir = ${docdir}/examples +dist_examples_SCRIPTS = \ + GetAllMatchRules.py \ + $(NULL) + +dbus_test_tool_SOURCES = \ + dbus-echo.c \ + dbus-spam.c \ + tool-common.c \ + tool-common.h \ + test-tool.c \ + test-tool.h \ + $(NULL) + +dbus_test_tool_LDADD = $(top_builddir)/dbus/libdbus-1.la EXTRA_DIST = run-with-tmp-session-bus.sh strtoll.c strtoull.c CLEANFILES = \ run-with-tmp-session-bus.conf @@ -580,9 +630,48 @@ dbus-send$(EXEEXT): $(dbus_send_OBJECTS) $(dbus_send_DEPENDENCIES) $(EXTRA_dbus_ @rm -f dbus-send$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dbus_send_OBJECTS) $(dbus_send_LDADD) $(LIBS) +dbus-test-tool$(EXEEXT): $(dbus_test_tool_OBJECTS) $(dbus_test_tool_DEPENDENCIES) $(EXTRA_dbus_test_tool_DEPENDENCIES) + @rm -f dbus-test-tool$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(dbus_test_tool_OBJECTS) $(dbus_test_tool_LDADD) $(LIBS) + dbus-uuidgen$(EXEEXT): $(dbus_uuidgen_OBJECTS) $(dbus_uuidgen_DEPENDENCIES) $(EXTRA_dbus_uuidgen_DEPENDENCIES) @rm -f dbus-uuidgen$(EXEEXT) $(AM_V_CCLD)$(LINK) $(dbus_uuidgen_OBJECTS) $(dbus_uuidgen_LDADD) $(LIBS) +install-dist_examplesSCRIPTS: $(dist_examples_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(dist_examples_SCRIPTS)'; test -n "$(examplesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(examplesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(examplesdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(examplesdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(examplesdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-dist_examplesSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(dist_examples_SCRIPTS)'; test -n "$(examplesdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(examplesdir)'; $(am__uninstall_files_from_dir) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -591,6 +680,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-cleanup-sockets.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-echo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-launch-win.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-launch-x11.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-launch.Po@am__quote@ @@ -598,7 +688,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-print-message.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-run-session.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-send.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-spam.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbus-uuidgen.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-tool.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tool-common.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @@ -711,9 +804,9 @@ distdir: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(PROGRAMS) +all-am: Makefile $(PROGRAMS) $(SCRIPTS) installdirs: - for dir in "$(DESTDIR)$(bindir)"; do \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(examplesdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -769,7 +862,7 @@ info: info-am info-am: -install-data-am: install-data-local +install-data-am: install-data-local install-dist_examplesSCRIPTS install-dvi: install-dvi-am @@ -815,7 +908,7 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS +uninstall-am: uninstall-binPROGRAMS uninstall-dist_examplesSCRIPTS .MAKE: install-am install-strip @@ -824,15 +917,16 @@ uninstall-am: uninstall-binPROGRAMS ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-data-local install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installcheck-local \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ - uninstall-am uninstall-binPROGRAMS + install-data install-data-am install-data-local \ + install-dist_examplesSCRIPTS install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installcheck-local installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-dist_examplesSCRIPTS # create the /var/lib/dbus directory for dbus-uuidgen diff --git a/tools/dbus-echo.c b/tools/dbus-echo.c new file mode 100644 index 00000000..72973cdb --- /dev/null +++ b/tools/dbus-echo.c @@ -0,0 +1,243 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-echo.c - a plain libdbus echo server + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <dbus/dbus.h> + +#include "test-tool.h" +#include "tool-common.h" + +static int sleep_ms = -1; +static dbus_bool_t noreply = FALSE; +static dbus_bool_t noread = FALSE; + +static void +usage_echo (int exit_with) +{ + fprintf (stderr, + "Usage: dbus-test-tool echo [OPTIONS]\n" + "\n" + "Respond to all method calls with an empty reply.\n" + "\n" + "Options:\n" + "\n" + " --name=NAME claim this well-known name first\n" + "\n" + " --sleep=N sleep N milliseconds before sending each reply\n" + "\n" + " --session use the session bus (default)\n" + " --system use the system bus\n" + ); + exit (exit_with); +} + +static void +usage_black_hole (int exit_with) +{ + fprintf (stderr, + "Usage: dbus-test-tool black-hole [OPTIONS]\n" + "\n" + "Receive method calls but do not reply.\n" + "\n" + "Options:\n" + "\n" + " --name=NAME claim this well-known name first\n" + "\n" + " --no-read don't read anything on the D-Bus socket\n" + "\n" + " --session use the session bus (default)\n" + " --system use the system bus\n" + ); + exit (exit_with); +} + +static DBusHandlerResult +filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DBusMessage *reply; + + if (dbus_message_get_type (message) != DBUS_MESSAGE_TYPE_METHOD_CALL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (sleep_ms > 0) + { + tool_millisleep (sleep_ms); + } + + if (!noreply) + { + reply = dbus_message_new_method_return (message); + + if (reply == NULL) + tool_oom ("allocating reply"); + + if (!dbus_connection_send (connection, reply, NULL)) + tool_oom ("sending reply"); + + dbus_message_unref (reply); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusConnection * +init_connection (DBusBusType type, const char *name) +{ + DBusConnection *connection; + DBusError error = DBUS_ERROR_INIT; + + connection = dbus_bus_get (type, &error); + + if (connection == NULL) + { + fprintf (stderr, "Failed to connect to bus: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + exit (1); + } + + if (name != NULL) + { + if (dbus_bus_request_name (connection, name, DBUS_NAME_FLAG_DO_NOT_QUEUE, + NULL) != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + fprintf (stderr, "failed to take bus name %s\n", name); + exit (1); + } + } + else + { + printf ("%s\n", dbus_bus_get_unique_name (connection)); + } + + if (!dbus_connection_add_filter (connection, filter, NULL, NULL)) + tool_oom ("adding message filter"); + + return connection; +} + +int +dbus_test_tool_echo (int argc, char **argv) +{ + DBusConnection *connection; + DBusBusType type = DBUS_BUS_SESSION; + int i; + const char *name = NULL; + + /* argv[1] is the tool name, so start from 2 */ + + for (i = 2; i < argc; i++) + { + const char *arg = argv[i]; + + if (strcmp (arg, "--system") == 0) + { + type = DBUS_BUS_SYSTEM; + } + else if (strcmp (arg, "--session") == 0) + { + type = DBUS_BUS_SESSION; + } + else if (strstr (arg, "--name=") == arg) + { + name = arg + strlen ("--name="); + } + else if (strstr (arg, "--sleep-ms=") == arg) + { + sleep_ms = atoi (arg + strlen ("--sleep-ms=")); + } + else + { + usage_echo (2); + } + } + + connection = init_connection (type, name); + + while (dbus_connection_read_write_dispatch (connection, -1)) + {} + + dbus_connection_unref (connection); + return 0; +} + +int +dbus_test_tool_black_hole (int argc, char **argv) +{ + DBusConnection *connection; + DBusBusType type = DBUS_BUS_SESSION; + int i; + const char *name = NULL; + + /* argv[1] is the tool name, so start from 2 */ + + for (i = 2; i < argc; i++) + { + const char *arg = argv[i]; + + if (strcmp (arg, "--system") == 0) + { + type = DBUS_BUS_SYSTEM; + } + else if (strcmp (arg, "--session") == 0) + { + type = DBUS_BUS_SESSION; + } + else if (strstr (arg, "--name=") == arg) + { + name = arg + strlen ("--name="); + } + else if (strcmp (arg, "--no-read") == 0) + { + noread = TRUE; + } + else + { + usage_black_hole (2); + } + } + + connection = init_connection (type, name); + + if (noread) + { + while (1) + sleep (3600); + } + + noreply = TRUE; + + while (dbus_connection_read_write_dispatch (connection, -1)) + {} + + dbus_connection_unref (connection); + return 0; +} diff --git a/tools/dbus-spam.c b/tools/dbus-spam.c new file mode 100644 index 00000000..ce918754 --- /dev/null +++ b/tools/dbus-spam.c @@ -0,0 +1,521 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-spam.c - a plain libdbus message-sender, loosely based on dbus-send + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <dbus/dbus.h> + +#include "test-tool.h" +#include "tool-common.h" + +static dbus_bool_t ignore_errors = FALSE; + +static void +usage (int ecode) +{ + fprintf (stderr, + "Usage: dbus-test-tool spam [OPTIONS]\n" + "\n" + "Repeatedly call com.example.Spam() on the given D-Bus service.\n" + "\n" + "Options:\n" + "\n" + " --session use the session bus (default)\n" + " --system use the system bus\n" + "\n" + " --ignore-errors ignore errors\n" + " --dest=NAME call methods on NAME (default " DBUS_SERVICE_DBUS ")\n" + "\n" + " --count=N send N messages (default 1)\n" + " --queue=N queue up N messages at a time (default 1)\n" + " --flood send all messages immediately\n" + " --no-reply set the NO_REPLY flag (implies --flood)\n" + " --messages-per-conn=N after sending messages-per-conn, wait\n" + " for the pending replies if any, then reconnect\n" + " (default: don't reconnect)\n" + "\n" + " --string send payload as a string (default)\n" + " --bytes send payload as a byte-array\n" + " --empty send an empty payload\n" + "\n" + " --payload=S use S as payload (default \"hello, world!\")\n" + " --stdin read payload from stdin, until EOF\n" + " --message-stdin read a complete D-Bus message from stdin\n" + " --random-size read whitespace-separated ASCII decimal\n" + " payload sizes from stdin and pick one randomly\n" + " for each message\n" + "\n" + " --seed=SEED seed for srand (default is time())\n" + "\n" + ); + exit (ecode); +} + +static void +pc_notify (DBusPendingCall *pc, + void *data) +{ + DBusMessage *message; + int *received_p = data; + DBusError error; + + dbus_error_init (&error); + + message = dbus_pending_call_steal_reply (pc); + + if (!ignore_errors && dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR) + { + dbus_set_error_from_message (&error, message); + fprintf (stderr, "Failed to receive reply #%d: %s: %s\n", *received_p, + error.name, error.message); + } + else + { + VERBOSE (stderr, "received message #%d\n", *received_p); + } + + (*received_p)++; +} + +static void +consume_stdin (char **payload_p, + size_t *len_p) +{ + const size_t BLOCK_SIZE = 4096; + size_t len = BLOCK_SIZE; + size_t pos = 0; + size_t n; + char *buf; + + buf = dbus_malloc (len); + + if (buf == NULL) + tool_oom ("reading payload from stdin"); + + while (1) + { + if (len - pos < BLOCK_SIZE) + { + char *tmp = dbus_realloc (buf, len + BLOCK_SIZE); + + if (tmp == NULL) + tool_oom ("reading payload from stdin"); + + buf = tmp; + len += BLOCK_SIZE; + } + + n = fread (buf + pos, 1, len - pos, stdin); + + if (n <= 0) + { + /* EOF or error - treat as EOF */ + break; + } + + pos += n; + } + + *len_p = pos; + *payload_p = buf; +} + +int +dbus_test_tool_spam (int argc, char **argv) +{ + DBusConnection *connection = NULL; + DBusError error = DBUS_ERROR_INIT; + DBusBusType type = DBUS_BUS_SESSION; + const char *destination = DBUS_SERVICE_DBUS; + int i; + int count = 1; + int sent = 0; + int sent_in_this_conn = 0; + int received = 0; + int received_before_this_conn = 0; + int queue_len = 1; + const char *payload = NULL; + char *payload_buf = NULL; + size_t payload_len; + int payload_type = DBUS_TYPE_STRING; + DBusMessage *template = NULL; + dbus_bool_t flood = FALSE; + dbus_bool_t no_reply = FALSE; + unsigned int messages_per_conn = 0; + unsigned int seed = time (NULL); + int n_random_sizes = 0; + unsigned int *random_sizes = NULL; + + /* argv[1] is the tool name, so start from 2 */ + + for (i = 2; i < argc; i++) + { + const char *arg = argv[i]; + + if (strcmp (arg, "--system") == 0) + { + type = DBUS_BUS_SYSTEM; + } + else if (strcmp (arg, "--session") == 0) + { + type = DBUS_BUS_SESSION; + } + else if (strstr (arg, "--count=") == arg) + { + count = atoi (arg + strlen ("--count=")); + + if (count < 1) + usage (2); + } + else if (strcmp (arg, "--ignore-errors") == 0) + { + ignore_errors = TRUE; + } + else if (strstr (arg, "--dest=") == arg) + { + destination = arg + strlen ("--dest="); + } + else if (strstr (arg, "--payload=") == arg) + { + payload = arg + strlen ("--payload="); + } + else if (strcmp (arg, "--stdin") == 0) + { + consume_stdin (&payload_buf, &payload_len); + payload = payload_buf; + } + else if (strcmp (arg, "--message-stdin") == 0) + { + consume_stdin (&payload_buf, &payload_len); + payload = payload_buf; + template = dbus_message_demarshal (payload, payload_len, &error); + + if (template == NULL) + { + fprintf (stderr, "Unable to demarshal template message: %s: %s", + error.name, error.message); + exit (1); + } + + if (dbus_message_get_type (template) != DBUS_MESSAGE_TYPE_METHOD_CALL) + { + fprintf (stderr, "Template message must be a method call\n"); + exit (1); + } + } + else if (strcmp (arg, "--random-size") == 0) + { + unsigned int len, max = 0; + int j, consumed = 0; + const char *p; + + consume_stdin (&payload_buf, &payload_len); + + for (p = payload_buf; p < payload_buf + payload_len; p += consumed) + { + /* the space character matches any (or no) whitespace */ + if (sscanf (p, " %u %n", &len, &consumed) == 0) + break; + + n_random_sizes++; + } + + random_sizes = dbus_new0 (int, n_random_sizes); + + if (random_sizes == NULL) + tool_oom ("allocating array of message lengths"); + + for (p = payload_buf, j = 0; + p < payload_buf + payload_len && j < n_random_sizes; + p += consumed, j++) + { + sscanf (p, " %u %n", &len, &consumed); + random_sizes[j] = len; + + if (len > max) + max = len; + } + + dbus_free (payload_buf); + payload_len = max + 1; + payload_buf = dbus_new (char, payload_len); + payload = payload_buf; + + if (payload_buf == NULL) + tool_oom ("allocating maximum-sized payload"); + + memset (payload_buf, 'X', payload_len); + payload_buf[payload_len - 1] = '\0'; + } + else if (strcmp (arg, "--empty") == 0) + { + payload_type = DBUS_TYPE_INVALID; + } + else if (strcmp (arg, "--string") == 0) + { + payload_type = DBUS_TYPE_STRING; + } + else if (strcmp (arg, "--bytes") == 0) + { + payload_type = DBUS_TYPE_ARRAY; + } + else if (strcmp (arg, "--flood") == 0) + { + if (queue_len > 1) + usage (2); + + if (messages_per_conn > 0) + usage (2); + + flood = TRUE; + queue_len = -1; + } + else if (strcmp (arg, "--no-reply") == 0) + { + if (queue_len > 1) + usage (2); + + queue_len = -1; + no_reply = TRUE; + } + else if (strstr (arg, "--queue=") == arg) + { + if (flood || no_reply) + usage (2); + + queue_len = atoi (arg + strlen ("--queue=")); + + if (queue_len < 1) + usage (2); + } + else if (strstr (arg, "--seed=") == arg) + { + seed = strtoul (arg + strlen ("--seed="), NULL, 10); + } + else if (strstr (arg, "--messages-per-conn=") == arg) + { + messages_per_conn = atoi (arg + strlen ("--messages-per-conn=")); + + if (messages_per_conn > 0 && flood) + usage (2); + } + else + { + usage (2); + } + } + + srand (seed); + + if (payload == NULL) + { + payload = "hello, world!"; + payload_len = strlen (payload); + } + + VERBOSE (stderr, "Will send up to %d messages, with up to %d queued, max %d per connection\n", + count, queue_len, messages_per_conn); + + while (no_reply ? sent < count : received < count) + { + /* Connect? + * - In the first iteration + * or + * - When messages_per_conn messages have been sent and no replies are being waited for + */ + if (connection == NULL || + (messages_per_conn > 0 && sent_in_this_conn == messages_per_conn && + (no_reply || received - received_before_this_conn == messages_per_conn))) + { + if (connection != NULL) + { + dbus_connection_flush (connection); + dbus_connection_close (connection); + dbus_connection_unref (connection); + } + + VERBOSE (stderr, "New connection.\n"); + connection = dbus_bus_get_private (type, &error); + + if (connection == NULL) + { + fprintf (stderr, "Failed to connect to bus: %s: %s\n", + error.name, error.message); + dbus_error_free (&error); + return 1; + } + + sent_in_this_conn = 0; + received_before_this_conn = received; + } + + /* Send another message? Only if we don't exceed the 3 limits: + * - total amount of messages + * - messages sent on this connection + * - queue + */ + while (sent < count && + (messages_per_conn == 0 || sent_in_this_conn < messages_per_conn) && + (queue_len == -1 || sent_in_this_conn < queue_len + received - received_before_this_conn)) + { + DBusMessage *message; + + if (template != NULL) + { + message = dbus_message_copy (template); + + if (message == NULL) + tool_oom ("copying message"); + + dbus_message_set_no_reply (message, no_reply); + } + else + { + dbus_bool_t mem; + unsigned int len = 0; + + message = dbus_message_new_method_call (destination, + "/", + "com.example", + "Spam"); + + if (message == NULL) + tool_oom ("allocating message"); + + dbus_message_set_no_reply (message, no_reply); + + switch (payload_type) + { + case DBUS_TYPE_STRING: + if (random_sizes != NULL) + { + /* this isn't fair, strictly speaking - the first few + * are a bit more likely to be chosen, unless + * RAND_MAX is divisible by n_random_sizes - but it's + * good enough for traffic-generation */ + len = random_sizes[rand () % n_random_sizes]; + payload_buf[len] = '\0'; + } + + mem = dbus_message_append_args (message, + DBUS_TYPE_STRING, &payload, + DBUS_TYPE_INVALID); + + if (random_sizes != NULL) + { + /* undo the truncation above */ + payload_buf[len] = 'X'; + } + + break; + + case DBUS_TYPE_ARRAY: + len = payload_len; + + /* as above, not strictly fair, but close enough */ + if (random_sizes != NULL) + len = random_sizes[rand () % n_random_sizes]; + + mem = dbus_message_append_args (message, + DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE, + &payload, + (dbus_uint32_t) len, + DBUS_TYPE_INVALID); + break; + + default: + mem = TRUE; + } + + if (!mem) + tool_oom ("building message"); + } + + if (no_reply) + { + if (!dbus_connection_send (connection, message, NULL)) + tool_oom ("sending message"); + + VERBOSE (stderr, "sent message #%d\n", sent); + sent++; + sent_in_this_conn++; + } + else + { + DBusPendingCall *pc; + + if (!dbus_connection_send_with_reply (connection, + message, + &pc, + DBUS_TIMEOUT_INFINITE)) + tool_oom ("sending message"); + + VERBOSE (stderr, "sent message #%d\n", sent); + sent++; + sent_in_this_conn++; + + if (pc == NULL) + tool_oom ("sending message"); + + if (dbus_pending_call_get_completed (pc)) + pc_notify (pc, &received); + else if (!dbus_pending_call_set_notify (pc, pc_notify, &received, + NULL)) + tool_oom ("setting pending call notifier"); + + dbus_pending_call_unref (pc); + } + + dbus_message_unref (message); + } + + if (!dbus_connection_read_write_dispatch (connection, -1)) + { + fprintf (stderr, "Disconnected from bus\n"); + exit (1); + } + } + + if (connection != NULL) + { + dbus_connection_flush (connection); + dbus_connection_close (connection); + dbus_connection_unref (connection); + } + + VERBOSE (stderr, "Done\n"); + + dbus_free (payload_buf); + + if (template != NULL) + dbus_message_unref (template); + + dbus_shutdown (); + return 0; +} diff --git a/tools/test-tool.c b/tools/test-tool.c new file mode 100644 index 00000000..149c10ab --- /dev/null +++ b/tools/test-tool.c @@ -0,0 +1,88 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-test-tool - D-Bus swiss army knife + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> +#include "test-tool.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <dbus/dbus.h> + +static struct { + const char *name; + int (*callback) (int, char **); +} subcommands[] = { + { "black-hole", dbus_test_tool_black_hole }, + { "echo", dbus_test_tool_echo }, + { "spam", dbus_test_tool_spam }, + { NULL, NULL } +}; + +static void usage (int exit_with) _DBUS_GNUC_NORETURN; + +static void +usage (int exit_with) +{ + int i; + + fprintf (stderr, + "Usage: dbus-test-tool SUBCOMMAND [OPTIONS]\n" + "\n" + "Known SUBCOMMANDs are:\n" + "\n" + ); + + for (i = 0; subcommands[i].name != NULL; i++) + { + fprintf (stderr, "- %s\n", subcommands[i].name); + } + + fprintf (stderr, + "\n" + "For more information: dbus-test-tool SUBCOMMAND --help\n" + ); + + exit (exit_with); +} + +int +main (int argc, char **argv) +{ + int i; + + if (argc < 2) + { + usage (2); + } + + for (i = 0; subcommands[i].name != NULL; i++) + { + if (!strcmp (argv[1], subcommands[i].name)) + return subcommands[i].callback (argc, argv); + } + + usage (2); + return 2; +} diff --git a/tools/test-tool.h b/tools/test-tool.h new file mode 100644 index 00000000..8143cd50 --- /dev/null +++ b/tools/test-tool.h @@ -0,0 +1,31 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* dbus-test-tool - D-Bus swiss army knife + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DBUS_TEST_TOOL_H +#define DBUS_TEST_TOOL_H + +int dbus_test_tool_black_hole (int argc, char **argv); +int dbus_test_tool_echo (int argc, char **argv); +int dbus_test_tool_spam (int argc, char **argv); + +#endif diff --git a/tools/tool-common.c b/tools/tool-common.c new file mode 100644 index 00000000..b6af629f --- /dev/null +++ b/tools/tool-common.c @@ -0,0 +1,60 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* tool-common - common functionality for dbus-test-tool modules + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> +#include "tool-common.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef DBUS_WIN +#include <windows.h> +#endif + +/* a hack to avoid having to depend on the static -util version of libdbus; + * it's useful for ancillary programs to be able to use the shared library */ +void +tool_millisleep (int ms) +{ +#ifdef DBUS_WIN + Sleep (ms); +#else + fd_set nothing; + struct timeval tv; + + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + + FD_ZERO (¬hing); + select (1, ¬hing, ¬hing, ¬hing, &tv); +#endif +} + +void +tool_oom (const char *doing) +{ + fprintf (stderr, "OOM while %s\n", doing); + exit (1); +} diff --git a/tools/tool-common.h b/tools/tool-common.h new file mode 100644 index 00000000..f31076fe --- /dev/null +++ b/tools/tool-common.h @@ -0,0 +1,38 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ +/* tool-common - common functionality for dbus-test-tool modules + * + * Copyright © 2003 Philip Blundell <philb@gnu.org> + * Copyright © 2011 Nokia Corporation + * Copyright © 2014 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef DBUS_TOOL_COMMON_H +#define DBUS_TOOL_COMMON_H + +#include <dbus/dbus.h> + +#if 0 +#define VERBOSE fprintf +#else +#define VERBOSE(...) do {} while (0) +#endif + +void tool_millisleep (int ms); +void tool_oom (const char *doing); + +#endif |