summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/.gitignore3
-rw-r--r--test/Makefile.am316
-rw-r--r--test/corrupt.c13
-rw-r--r--test/data/systemd-activation/com.example.SystemdActivatable1.service4
-rw-r--r--test/data/systemd-activation/com.example.SystemdActivatable2.service4
-rw-r--r--test/data/systemd-activation/com.example.SystemdActivatable3.service4
-rw-r--r--test/data/systemd-activation/org.freedesktop.systemd1.service3
-rw-r--r--test/data/valid-config-files/.gitignore2
-rw-r--r--test/data/valid-config-files/finite-timeout.conf.in19
-rw-r--r--test/data/valid-config-files/forbidding.conf.in18
-rw-r--r--test/data/valid-config-files/listen-unix-runtime.conf11
-rw-r--r--test/data/valid-config-files/multi-user.conf.in15
-rw-r--r--test/data/valid-config-files/systemd-activation.conf.in11
-rw-r--r--test/dbus-daemon-eavesdrop.c172
-rw-r--r--test/dbus-daemon.c486
-rw-r--r--test/fdpass.c868
-rwxr-xr-xtest/glib-tap-test.sh13
-rw-r--r--test/internals/printf.c17
-rw-r--r--test/internals/refs.c11
-rw-r--r--test/internals/syslog.c9
-rw-r--r--test/loopback.c116
-rw-r--r--test/manual-authz.c2
-rw-r--r--test/manual-dir-iter.c92
-rw-r--r--test/manual-paths.c73
-rw-r--r--test/marshal.c4
-rw-r--r--test/monitor.c1523
-rw-r--r--test/name-test/Makefile.am39
-rwxr-xr-xtest/name-test/run-test-systemserver.sh89
-rwxr-xr-xtest/name-test/run-test.sh93
-rw-r--r--test/name-test/test-activation-forking.py6
-rw-r--r--test/name-test/test-threads-init.c10
-rwxr-xr-xtest/name-test/test-wait-for-echo.py6
-rw-r--r--test/relay.c11
-rw-r--r--test/sd-activation.c349
-rw-r--r--test/shell-test.c111
-rw-r--r--test/syntax.c4
-rw-r--r--test/tap-test.sh.in32
-rw-r--r--test/test-service.c8
-rw-r--r--test/test-utils-glib.c452
-rw-r--r--test/test-utils-glib.h94
-rw-r--r--test/test-utils.c55
-rw-r--r--test/test-utils.h17
-rw-r--r--test/uid-permissions.c199
43 files changed, 4736 insertions, 648 deletions
diff --git a/test/.gitignore b/test/.gitignore
index 78dfbd30..4eaae4a4 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -29,8 +29,11 @@ test-relay
test-dbus-daemon
test-marshal
manual-authz
+manual-dir-iter
+manual-tcp
test-dbus-daemon-eavesdrop
test-printf
test-refs
test-syntax
test-syslog
+internals/.dirstamp
diff --git a/test/Makefile.am b/test/Makefile.am
index e0ed3c87..9540a2c1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -4,59 +4,48 @@
SUBDIRS= . name-test
DIST_SUBDIRS=name-test
-# CPPFLAGS for binaries that are normally dynamic
+CLEANFILES =
+EXTRA_DIST =
+
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(DBUS_STATIC_BUILD_CPPFLAGS) \
+ -DDBUS_COMPILATION \
$(GLIB_CFLAGS) \
- $(DBUS_GLIB_CFLAGS) \
$(NULL)
# improve backtraces from test stuff
AM_LDFLAGS = @R_DYNAMIC_LDFLAG@
-# CPPFLAGS for binaries that are always static
-static_cppflags = \
- $(AM_CPPFLAGS) \
- -DDBUS_STATIC_BUILD \
- -DDBUS_COMPILATION \
- -DDBUS_TEST_USE_INTERNAL \
- $(NULL)
-
-noinst_LTLIBRARIES = libdbus-testutils-internal.la
+noinst_LTLIBRARIES = libdbus-testutils.la
-# You can link either libdbus-testutils, dbus-glib and libdbus-1,
-# or libdbus-testutils-internal and libdbus-internal - never both in the
-# same binary.
-if DBUS_WITH_DBUS_GLIB
-noinst_LTLIBRARIES += libdbus-testutils.la
libdbus_testutils_la_SOURCES = \
test-utils.c \
test-utils.h \
$(NULL)
-libdbus_testutils_la_LIBADD = \
- $(top_builddir)/dbus/libdbus-1.la \
- $(GLIB_LIBS) \
- $(DBUS_GLIB_LIBS) \
+
+if DBUS_WITH_GLIB
+libdbus_testutils_la_SOURCES += \
+ test-utils-glib.c \
+ test-utils-glib.h \
$(NULL)
-testutils_shared_if_possible_cppflags = $(AM_CPPFLAGS)
-testutils_shared_if_possible_libs = libdbus-testutils.la
-else
-testutils_shared_if_possible_cppflags = $(static_cppflags)
-testutils_shared_if_possible_libs = libdbus-testutils-internal.la
endif
-libdbus_testutils_internal_la_CPPFLAGS = \
- $(static_cppflags) \
- $(NULL)
-libdbus_testutils_internal_la_SOURCES = \
- test-utils.c \
- test-utils.h \
- $(NULL)
-libdbus_testutils_internal_la_LIBADD = \
+libdbus_testutils_la_LIBADD = \
+ $(top_builddir)/dbus/libdbus-1.la \
$(top_builddir)/dbus/libdbus-internal.la \
$(NULL)
+TEST_EXTENSIONS = .sh
+
+LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
+LOG_COMPILER = $(srcdir)/glib-tap-test.sh
+SH_LOG_DRIVER = $(LOG_DRIVER)
+SH_LOG_COMPILER = $(SHELL)
+EXTRA_DIST += glib-tap-test.sh
+
+TESTS =
+
if DBUS_ENABLE_EMBEDDED_TESTS
## break-loader removed for now
## these binaries are used in tests but are not themselves tests
@@ -72,154 +61,202 @@ TEST_BINARIES = \
## These are conceptually part of directories that come earlier in SUBDIRS
## order, but we don't want to run them til we arrive in this directory,
-## since they depend on stuff from this directory
-TESTS = \
- ../bus/test-bus$(EXEEXT) \
- ../bus/test-bus-system$(EXEEXT) \
- ../dbus/test-dbus$(EXEEXT) \
- $(NULL)
+## since they depend on stuff from this directory. We wrap them in a
+## simple shell script to get TAP output.
+
+wrap_bus_tests = test-bus.sh
+wrap_dbus_tests = test-dbus.sh
if DBUS_UNIX
-TESTS += ../bus/test-bus-launch-helper$(EXEEXT)
+wrap_bus_tests += test-bus-launch-helper.sh
+wrap_bus_tests += test-bus-system.sh
endif
+TESTS += $(wrap_bus_tests) $(wrap_dbus_tests)
+CLEANFILES += $(wrap_bus_tests) $(wrap_dbus_tests)
+EXTRA_DIST += tap-test.sh.in
+
+$(wrap_bus_tests): test-bus%.sh: ../bus/test-bus%$(EXEEXT) tap-test.sh.in Makefile
+ sed -e 's![@]RUN[@]!$<!' \
+ < $(srcdir)/tap-test.sh.in > $@
+
+$(wrap_dbus_tests): test-dbus%.sh: ../dbus/test-dbus%$(EXEEXT) tap-test.sh.in Makefile
+ sed -e 's![@]RUN[@]!$<!' \
+ < $(srcdir)/tap-test.sh.in > $@
+
else !DBUS_ENABLE_EMBEDDED_TESTS
TEST_BINARIES=
-TESTS=
endif !DBUS_ENABLE_EMBEDDED_TESTS
noinst_PROGRAMS= $(TEST_BINARIES)
-test_service_CPPFLAGS = $(static_cppflags)
-test_service_LDADD = libdbus-testutils-internal.la
-test_names_CPPFLAGS = $(static_cppflags)
-test_names_LDADD = libdbus-testutils-internal.la
-## break_loader_CPPFLAGS = $(static_cppflags)
+test_service_LDADD = libdbus-testutils.la
+test_names_LDADD = libdbus-testutils.la
## break_loader_LDADD = $(top_builddir)/dbus/libdbus-internal.la
-test_shell_service_CPPFLAGS = $(static_cppflags)
-test_shell_service_LDADD = libdbus-testutils-internal.la
+test_shell_service_LDADD = libdbus-testutils.la
test_shell_SOURCES = shell-test.c
-test_shell_CPPFLAGS = $(static_cppflags)
-test_shell_LDADD = libdbus-testutils-internal.la
+test_shell_LDADD = libdbus-testutils.la
test_spawn_SOURCES = spawn-test.c
-test_spawn_CPPFLAGS = $(static_cppflags)
test_spawn_LDADD = $(top_builddir)/dbus/libdbus-internal.la
test_printf_SOURCES = internals/printf.c
-test_printf_CPPFLAGS = $(static_cppflags)
test_printf_LDADD = $(top_builddir)/dbus/libdbus-internal.la
test_refs_SOURCES = internals/refs.c
-test_refs_CPPFLAGS = $(static_cppflags)
-test_refs_LDADD = libdbus-testutils-internal.la $(GLIB_LIBS)
+test_refs_LDADD = libdbus-testutils.la $(GLIB_LIBS)
test_syslog_SOURCES = internals/syslog.c
-test_syslog_CPPFLAGS = $(static_cppflags)
-test_syslog_LDADD = libdbus-testutils-internal.la $(GLIB_LIBS)
+test_syslog_LDADD = libdbus-testutils.la $(GLIB_LIBS)
+
+manual_dir_iter_SOURCES = manual-dir-iter.c
+manual_dir_iter_LDADD = $(top_builddir)/dbus/libdbus-internal.la
+
+manual_paths_SOURCES = manual-paths.c
+manual_paths_LDADD = $(top_builddir)/dbus/libdbus-internal.la
manual_tcp_SOURCES = manual-tcp.c
-manual_tcp_CPPFLAGS = $(static_cppflags)
manual_tcp_LDADD = $(top_builddir)/dbus/libdbus-internal.la
-EXTRA_DIST = dbus-test-runner
+EXTRA_DIST += dbus-test-runner
-testexecdir = $(libdir)/dbus-1.0/test
+testexecdir = $(libexecdir)/installed-tests/dbus
+testmetadir = $(datadir)/installed-tests/dbus
testexec_PROGRAMS =
+testmeta_DATA =
installable_tests = \
test-shell \
test-printf \
$(NULL)
installable_manual_tests = \
- manual-tcp \
- $(NULL)
+ manual-dir-iter \
+ manual-tcp \
+ $(NULL)
+
+if DBUS_WIN
+installable_manual_tests += manual-paths
+endif
if DBUS_WITH_GLIB
installable_tests += \
test-corrupt \
test-dbus-daemon \
test-dbus-daemon-eavesdrop \
+ test-fdpass \
+ test-monitor \
test-loopback \
test-marshal \
test-refs \
test-relay \
+ test-sd-activation \
test-syntax \
test-syslog \
+ test-uid-permissions \
$(NULL)
installable_manual_tests += \
manual-authz \
$(NULL)
endif DBUS_WITH_GLIB
+installable_test_meta = $(installable_tests:=.test)
+installable_test_meta_with_config = $(installable_tests:=_with_config.test)
+
installcheck_tests =
installcheck_environment = \
- XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR \
- DBUS_TEST_DAEMON=$(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon$(EXEEXT) \
- DBUS_TEST_HOMEDIR=@abs_top_builddir@/dbus \
- DBUS_TEST_SYSCONFDIR=$(DESTDIR)$(sysconfdir)
-
-TESTS_ENVIRONMENT = \
- XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR \
- DBUS_FATAL_WARNINGS=1 \
- DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT) \
- DBUS_TEST_DATA=@abs_top_builddir@/test/data \
- DBUS_TEST_HOMEDIR=@abs_top_builddir@/dbus \
+ export XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR; \
+ export DBUS_TEST_DAEMON=$(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon$(EXEEXT); \
+ export DBUS_TEST_HOMEDIR=@abs_top_builddir@/dbus; \
+ export DBUS_TEST_DATADIR=$(DESTDIR)$(datadir); \
+ ${NULL}
+
+AM_TESTS_ENVIRONMENT = \
+ export XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR; \
+ export DBUS_FATAL_WARNINGS=1; \
+ export DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT); \
+ export DBUS_TEST_DATA=@abs_top_builddir@/test/data; \
+ export DBUS_TEST_HOMEDIR=@abs_top_builddir@/dbus; \
$(NULL)
manual_authz_SOURCES = manual-authz.c
-manual_authz_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
manual_authz_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_corrupt_SOURCES = corrupt.c
-test_corrupt_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_corrupt_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_loopback_SOURCES = loopback.c
-test_loopback_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_loopback_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_relay_SOURCES = relay.c
-test_relay_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_relay_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_dbus_daemon_SOURCES = dbus-daemon.c
-test_dbus_daemon_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_dbus_daemon_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_dbus_daemon_eavesdrop_SOURCES = dbus-daemon-eavesdrop.c
-test_dbus_daemon_eavesdrop_CPPFLAGS = $(testutils_shared_if_possible_cppflags)
test_dbus_daemon_eavesdrop_LDADD = \
- $(testutils_shared_if_possible_libs) \
+ libdbus-testutils.la \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+test_sd_activation_SOURCES = \
+ sd-activation.c \
+ $(NULL)
+test_sd_activation_LDADD = \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_marshal_SOURCES = marshal.c
test_marshal_LDADD = \
- $(top_builddir)/dbus/libdbus-1.la \
+ libdbus-testutils.la \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+test_monitor_SOURCES = \
+ monitor.c \
+ $(NULL)
+test_monitor_LDADD = \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
test_syntax_SOURCES = syntax.c
test_syntax_LDADD = \
- $(top_builddir)/dbus/libdbus-1.la \
+ libdbus-testutils.la \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+test_uid_permissions_SOURCES = \
+ uid-permissions.c \
+ $(NULL)
+test_uid_permissions_LDADD = \
+ libdbus-testutils.la \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+test_fdpass_SOURCES = \
+ fdpass.c \
+ $(NULL)
+test_fdpass_LDADD = \
+ libdbus-testutils.la \
$(GLIB_LIBS) \
$(NULL)
@@ -229,6 +266,9 @@ installcheck_tests += $(installable_tests)
if DBUS_ENABLE_INSTALLED_TESTS
testexec_PROGRAMS += $(installable_tests) $(installable_manual_tests)
+
+ testmeta_DATA += $(installable_test_meta)
+ testmeta_DATA += $(installable_test_meta_with_config)
else !DBUS_ENABLE_INSTALLED_TESTS
noinst_PROGRAMS += $(installable_tests) $(installable_manual_tests)
endif !DBUS_ENABLE_INSTALLED_TESTS
@@ -240,13 +280,13 @@ endif DBUS_ENABLE_MODULAR_TESTS
# do a portable equivalent of setting LD_LIBRARY_PATH.
installcheck-local:
$(MAKE) check-TESTS TESTS='$$(installcheck_tests)' \
- TESTS_ENVIRONMENT='$$(installcheck_environment)'
+ AM_TESTS_ENVIRONMENT='$$(installcheck_environment)'
if DBUS_ENABLE_INSTALLED_TESTS
- test -n "$(DESTDIR)" || \
+ test -n "$(DESTDIR)" || { \
$(installcheck_environment) \
$(srcdir)/dbus-test-runner \
$(testexecdir) \
- $(testexec_PROGRAMS)
+ $(testexec_PROGRAMS) }
endif DBUS_ENABLE_INSTALLED_TESTS
in_data = \
@@ -254,7 +294,11 @@ 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/forbidding.conf.in \
data/valid-config-files/incoming-limit.conf.in \
+ data/valid-config-files/multi-user.conf.in \
+ data/valid-config-files/systemd-activation.conf.in \
data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service.in \
data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service.in \
data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service.in \
@@ -323,9 +367,14 @@ static_data = \
data/sha-1/bit-messages.sha1 \
data/sha-1/byte-hashes.sha1 \
data/sha-1/byte-messages.sha1 \
+ data/systemd-activation/com.example.SystemdActivatable1.service \
+ data/systemd-activation/com.example.SystemdActivatable2.service \
+ data/systemd-activation/com.example.SystemdActivatable3.service \
+ data/systemd-activation/org.freedesktop.systemd1.service \
data/valid-config-files/basic.conf \
data/valid-config-files/basic.d/basic.conf \
data/valid-config-files/entities.conf \
+ data/valid-config-files/listen-unix-runtime.conf \
data/valid-config-files/many-rules.conf \
data/valid-config-files/system.d/test.conf \
data/valid-messages/array-of-array-of-uint32.message \
@@ -349,25 +398,82 @@ EXTRA_DIST += $(static_data)
## copy tests to builddir so that generated tests and static tests
## are all in one place.
-all-local:
+all-local: copy-config-local uninstalled-config-local
+ @:
+
+copy-config-local:
$(AM_V_at)$(MKDIR_P) data/valid-config-files/session.d
- $(AM_V_at)set -e && \
+ $(AM_V_GEN)set -e; \
if test $(srcdir) = . || test $(srcdir) -ef .; then \
echo '-- No need to copy test data as srcdir = builddir'; \
else \
for F in $(static_data); do \
- $(MKDIR_P) $${F%/*}; \
- rm -f $$F; \
- cp $(srcdir)/$$F $$F; \
+ $(MKDIR_P) "$${F%/*}"; \
+ rm -f "$$F"; \
+ cp $(srcdir)/"$$F" "$$F"; \
done; \
fi
+uninstalled-config-local:
+ $(AM_V_GEN)set -e; \
+ for F in $(in_data); do \
+ $(MKDIR_P) "$${F%/*}"; \
+ sed \
+ -e 's,[@]DBUS_TEST_DATA[@],@abs_builddir@/data,' \
+ -e 's,[@]DBUS_TEST_EXEC[@],@abs_builddir@,' \
+ -e 's,[@]EXEEXT[@],$(EXEEXT),' \
+ -e 's,[@]TEST_LAUNCH_HELPER_BINARY[@],@abs_top_builddir@/bus/dbus-daemon-launch-helper-test$(EXEEXT),' \
+ -e 's,[@]TEST_LISTEN[@],$(TEST_LISTEN),' \
+ < $(srcdir)/"$$F" > "$${F%.in}"; \
+ done
+
+installable-config-local:
+if DBUS_ENABLE_INSTALLED_TESTS
+ $(AM_V_GEN)set -e; \
+ for F in $(in_data); do \
+ $(MKDIR_P) "installable/$${F%/*}"; \
+ sed \
+ -e 's,[@]DBUS_TEST_DATA[@],$(testexecdir)/data,' \
+ -e 's,[@]DBUS_TEST_EXEC[@],$(testexecdir),' \
+ -e 's,[@]EXEEXT[@],$(EXEEXT),' \
+ -e 's,[@]TEST_LAUNCH_HELPER_BINARY[@],/bin/false,' \
+ -e 's,[@]TEST_LISTEN[@],$(TEST_LISTEN),' \
+ < $(srcdir)/"$$F" > "installable/$${F%.in}"; \
+ done
+else
+ @:
+endif
+
+
+install-data-local: install-config-local
+ @:
+
+install-config-local: installable-config-local
+if DBUS_ENABLE_INSTALLED_TESTS
+ $(AM_V_GEN)set -e; \
+ for F in $(static_data); do \
+ install -d "$(DESTDIR)$(testexecdir)/$${F%/*}"; \
+ install -m644 "$(srcdir)/$$F" "$(DESTDIR)$(testexecdir)/$$F"; \
+ done; \
+ for F in $(in_data); do \
+ install -d "$(DESTDIR)$(testexecdir)/$${F%/*}"; \
+ install -m644 "installable/$${F%.in}" "$(DESTDIR)$(testexecdir)/$${F%.in}"; \
+ done
+ ln -nfs $(datadir)/dbus-1/session.conf $(DESTDIR)$(testexecdir)/data/valid-config-files/session.conf
+ ln -nfs $(datadir)/dbus-1/system.conf $(DESTDIR)$(testexecdir)/data/valid-config-files/system.conf
+else
+ @:
+endif
+
## this doesn't clean most copied test data files when srcdir=builddir
clean-local:
$(AM_V_at)if test $(srcdir) = . || test $(srcdir) -ef .; then \
echo '-- No need to clean test data as srcdir = builddir'; \
else \
rm -f $(static_data); \
+ for F in $(in_data); do \
+ rm -f "$${F%.in}"; \
+ done; \
fi
imported_data = \
@@ -376,8 +482,28 @@ imported_data = \
$(NULL)
noinst_DATA = $(imported_data)
-CLEANFILES = $(noinst_DATA) XDG_RUNTIME_DIR
+CLEANFILES += \
+ $(noinst_DATA) \
+ XDG_RUNTIME_DIR \
+ installable \
+ $(NULL)
$(imported_data): data/valid-config-files/%.conf: $(top_builddir)/bus/%.conf
$(AM_V_at)$(MKDIR_P) data/valid-config-files
$(AM_V_GEN)cp $< $@
+
+$(installable_test_meta): %.test: %$(EXEEXT) Makefile
+ $(AM_V_GEN) ( \
+ echo '[Test]'; \
+ echo 'Type=session'; \
+ echo 'Output=TAP'; \
+ echo 'Exec=env $(testexecdir)/$* --tap'; \
+ ) > $@.tmp && mv $@.tmp $@
+
+$(installable_test_meta_with_config): %_with_config.test: %$(EXEEXT) Makefile
+ $(AM_V_GEN) ( \
+ echo '[Test]'; \
+ echo 'Type=session'; \
+ echo 'Output=TAP'; \
+ echo 'Exec=env DBUS_TEST_DATA=$(testexecdir)/data $(testexecdir)/$* --tap'; \
+ ) > $@.tmp && mv $@.tmp $@
diff --git a/test/corrupt.c b/test/corrupt.c
index 1a7d4460..d106fcc5 100644
--- a/test/corrupt.c
+++ b/test/corrupt.c
@@ -31,7 +31,7 @@
#include <dbus/dbus.h>
-#include "test-utils.h"
+#include "test-utils-glib.h"
typedef struct {
DBusError e;
@@ -110,7 +110,7 @@ test_connect (Fixture *f,
while (f->server_conn == NULL)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -139,7 +139,7 @@ test_message (Fixture *f,
while (g_queue_is_empty (&f->client_messages))
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -231,7 +231,7 @@ test_corrupt (Fixture *f,
* rubbish, so it should disconnect */
while (g_queue_is_empty (&f->client_messages))
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -320,7 +320,7 @@ test_byte_order (Fixture *f,
* message, so it should disconnect */
while (g_queue_is_empty (&f->client_messages))
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -379,8 +379,7 @@ int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_type_init ();
+ test_init (&argc, &argv);
g_test_add ("/corrupt/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_corrupt, teardown);
diff --git a/test/data/systemd-activation/com.example.SystemdActivatable1.service b/test/data/systemd-activation/com.example.SystemdActivatable1.service
new file mode 100644
index 00000000..f15f0386
--- /dev/null
+++ b/test/data/systemd-activation/com.example.SystemdActivatable1.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.example.SystemdActivatable1
+Exec=/bin/false 1
+SystemdService=dbus-com.example.SystemdActivatable1.service
diff --git a/test/data/systemd-activation/com.example.SystemdActivatable2.service b/test/data/systemd-activation/com.example.SystemdActivatable2.service
new file mode 100644
index 00000000..dcedd734
--- /dev/null
+++ b/test/data/systemd-activation/com.example.SystemdActivatable2.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.example.SystemdActivatable2
+Exec=/bin/false 2
+SystemdService=dbus-com.example.SystemdActivatable2.service
diff --git a/test/data/systemd-activation/com.example.SystemdActivatable3.service b/test/data/systemd-activation/com.example.SystemdActivatable3.service
new file mode 100644
index 00000000..f6f0559c
--- /dev/null
+++ b/test/data/systemd-activation/com.example.SystemdActivatable3.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.example.SystemdActivatable3
+Exec=/bin/false 3
+SystemdService=dbus-com.example.SystemdActivatable3.service
diff --git a/test/data/systemd-activation/org.freedesktop.systemd1.service b/test/data/systemd-activation/org.freedesktop.systemd1.service
new file mode 100644
index 00000000..aea93113
--- /dev/null
+++ b/test/data/systemd-activation/org.freedesktop.systemd1.service
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.systemd1
+Exec=/bin/false
diff --git a/test/data/valid-config-files/.gitignore b/test/data/valid-config-files/.gitignore
index a38e9d15..2a09552e 100644
--- a/test/data/valid-config-files/.gitignore
+++ b/test/data/valid-config-files/.gitignore
@@ -1,5 +1,7 @@
debug-allow-all.conf
debug-allow-all-sha1.conf
+incoming-limit.conf
session.conf
system.conf
run-with-tmp-session-bus.conf
+finite-timeout.conf
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/data/valid-config-files/forbidding.conf.in b/test/data/valid-config-files/forbidding.conf.in
new file mode 100644
index 00000000..6a674f88
--- /dev/null
+++ b/test/data/valid-config-files/forbidding.conf.in
@@ -0,0 +1,18 @@
+<!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 -->
+ <allow send_destination="*"/>
+ <allow receive_sender="*"/>
+ <allow own="*"/>
+
+ <!-- Exception: some messages are forbidden -->
+ <deny send_interface="com.example.CannotSend"/>
+ <deny receive_interface="com.example.CannotReceive"/>
+ </policy>
+</busconfig>
diff --git a/test/data/valid-config-files/listen-unix-runtime.conf b/test/data/valid-config-files/listen-unix-runtime.conf
new file mode 100644
index 00000000..169de2cb
--- /dev/null
+++ b/test/data/valid-config-files/listen-unix-runtime.conf
@@ -0,0 +1,11 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <type>session</type>
+ <listen>unix:runtime=yes</listen>
+ <policy context="default">
+ <allow send_destination="*" eavesdrop="true"/>
+ <allow eavesdrop="true"/>
+ <allow own="*"/>
+ </policy>
+</busconfig>
diff --git a/test/data/valid-config-files/multi-user.conf.in b/test/data/valid-config-files/multi-user.conf.in
new file mode 100644
index 00000000..37a7da67
--- /dev/null
+++ b/test/data/valid-config-files/multi-user.conf.in
@@ -0,0 +1,15 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <listen>@TEST_LISTEN@</listen>
+
+ <policy context="default">
+ <allow send_interface="*"/>
+ <allow receive_interface="*"/>
+ <allow own="*"/>
+ <allow user="*"/>
+ </policy>
+
+ <!-- avoid allowing service activation since we are allowing everyone in -->
+ <servicehelper>/bin/false</servicehelper>
+</busconfig>
diff --git a/test/data/valid-config-files/systemd-activation.conf.in b/test/data/valid-config-files/systemd-activation.conf.in
new file mode 100644
index 00000000..bcd6416c
--- /dev/null
+++ b/test/data/valid-config-files/systemd-activation.conf.in
@@ -0,0 +1,11 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <listen>@TEST_LISTEN@</listen>
+ <servicedir>@DBUS_TEST_DATA@/systemd-activation</servicedir>
+ <policy context="default">
+ <allow send_destination="*"/>
+ <allow receive_sender="*"/>
+ <allow own="*"/>
+ </policy>
+</busconfig>
diff --git a/test/dbus-daemon-eavesdrop.c b/test/dbus-daemon-eavesdrop.c
index a78d8888..41985787 100644
--- a/test/dbus-daemon-eavesdrop.c
+++ b/test/dbus-daemon-eavesdrop.c
@@ -27,21 +27,9 @@
#include <config.h>
-#include <glib.h>
-
-#include <dbus/dbus.h>
-
#include <string.h>
-#ifdef DBUS_WIN
-# include <io.h>
-# include <windows.h>
-#else
-# include <signal.h>
-# include <unistd.h>
-#endif
-
-#include "test-utils.h"
+#include "test-utils-glib.h"
#define SENDER_NAME "test.eavesdrop.sender"
#define SENDER_PATH "/test/eavesdrop/sender"
@@ -91,99 +79,6 @@ typedef struct {
dbus_bool_t politelistener_got_stopper;
} Fixture;
-#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
-static void
-_assert_no_error (const DBusError *e,
- const char *file,
- int line)
-{
- if (G_UNLIKELY (dbus_error_is_set (e)))
- g_error ("%s:%d: expected success but got error: %s: %s",
- file, line, e->name, e->message);
-}
-
-static gchar *
-spawn_dbus_daemon (gchar *binary,
- gchar *configuration,
- GPid *daemon_pid)
-{
- GError *error = NULL;
- GString *address;
- gint address_fd;
- gchar *argv[] = {
- binary,
- configuration,
- "--nofork",
- "--print-address=1", /* stdout */
- NULL
- };
-
- g_spawn_async_with_pipes (NULL, /* working directory */
- argv,
- NULL, /* envp */
- G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
- NULL, /* child_setup */
- NULL, /* user data */
- daemon_pid,
- NULL, /* child's stdin = /dev/null */
- &address_fd,
- NULL, /* child's stderr = our stderr */
- &error);
- g_assert_no_error (error);
-
- address = g_string_new (NULL);
-
- /* polling until the dbus-daemon writes out its address is a bit stupid,
- * but at least it's simple, unlike dbus-launch... in principle we could
- * use select() here, but life's too short */
- while (1)
- {
- gssize bytes;
- gchar buf[4096];
- gchar *newline;
-
- bytes = read (address_fd, buf, sizeof (buf));
-
- if (bytes > 0)
- g_string_append_len (address, buf, bytes);
-
- newline = strchr (address->str, '\n');
-
- if (newline != NULL)
- {
- if ((newline > address->str) && ('\r' == newline[-1]))
- newline -= 1;
- g_string_truncate (address, newline - address->str);
- break;
- }
-
- g_usleep (G_USEC_PER_SEC / 10);
- }
-
- return g_string_free (address, FALSE);
-}
-
-static DBusConnection *
-connect_to_bus (Fixture *f,
- const gchar *address)
-{
- DBusConnection *conn;
- DBusError error = DBUS_ERROR_INIT;
- dbus_bool_t ok;
-
- conn = dbus_connection_open_private (address, &error);
- assert_no_error (&error);
- g_assert (conn != NULL);
-
- ok = dbus_bus_register (conn, &error);
- assert_no_error (&error);
- g_assert (ok);
- g_assert (dbus_bus_get_unique_name (conn) != NULL);
-
- test_connection_setup (f->ctx, conn);
- return conn;
-}
-
/* send a unicast signal to <self> to ensure that no other connection
* listening is the actual recipient for the signal */
static DBusHandlerResult
@@ -338,9 +233,9 @@ add_receiver_filter (Fixture *f)
DBusError e = DBUS_ERROR_INIT;
dbus_bus_add_match (f->receiver, RECEIVER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
dbus_bus_add_match (f->receiver, STOPPER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
if (!dbus_connection_add_filter (f->receiver,
signal_filter, f, NULL))
@@ -353,9 +248,9 @@ add_eavesdropper_filter (Fixture *f)
DBusError e = DBUS_ERROR_INIT;
dbus_bus_add_match (f->eavesdropper, EAVESDROPPER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
dbus_bus_add_match (f->eavesdropper, STOPPER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
if (!dbus_connection_add_filter (f->eavesdropper,
signal_filter, f, NULL))
@@ -368,9 +263,9 @@ add_politelistener_filter (Fixture *f)
DBusError e = DBUS_ERROR_INIT;
dbus_bus_add_match (f->politelistener, POLITELISTENER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
dbus_bus_add_match (f->politelistener, STOPPER_RULE, &e);
- assert_no_error (&e);
+ test_assert_no_error (&e);
if (!dbus_connection_add_filter (f->politelistener,
signal_filter, f, NULL))
@@ -381,8 +276,6 @@ static void
setup (Fixture *f,
gconstpointer context G_GNUC_UNUSED)
{
- gchar *dbus_daemon;
- gchar *config;
gchar *address;
f->ctx = test_main_context_get ();
@@ -390,45 +283,14 @@ setup (Fixture *f,
f->ge = NULL;
dbus_error_init (&f->e);
- dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
-
- if (dbus_daemon == NULL)
- dbus_daemon = g_strdup ("dbus-daemon");
-
- if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
- {
- config = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
- g_getenv ("DBUS_TEST_SYSCONFDIR"));
- }
- else if (g_getenv ("DBUS_TEST_DATA") != NULL)
- {
- config = g_strdup_printf (
- "--config-file=%s/valid-config-files/session.conf",
- g_getenv ("DBUS_TEST_DATA"));
- }
- else
- {
- config = g_strdup ("--session");
- }
-
- if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
- {
- address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
- }
- else
- {
- address = spawn_dbus_daemon (dbus_daemon, config, &f->daemon_pid);
- }
-
- g_free (dbus_daemon);
- g_free (config);
+ address = test_get_dbus_daemon (NULL, TEST_USER_ME, &f->daemon_pid);
- f->sender = connect_to_bus (f, address);
+ f->sender = test_connect_to_bus (f->ctx, address);
dbus_bus_request_name (f->sender, SENDER_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE,
&(f->e));
- f->receiver = connect_to_bus (f, address);
- f->eavesdropper = connect_to_bus (f, address);
- f->politelistener = connect_to_bus (f, address);
+ f->receiver = test_connect_to_bus (f->ctx, address);
+ f->eavesdropper = test_connect_to_bus (f->ctx, address);
+ f->politelistener = test_connect_to_bus (f->ctx, address);
add_receiver_filter (f);
add_politelistener_filter (f);
add_eavesdropper_filter (f);
@@ -541,12 +403,7 @@ teardown (Fixture *f,
f->eavesdropper = NULL;
}
-#ifdef DBUS_WIN
- TerminateProcess (f->daemon_pid, 1);
-#else
- kill (f->daemon_pid, SIGTERM);
-#endif
-
+ test_kill_pid (f->daemon_pid);
g_spawn_close_pid (f->daemon_pid);
test_main_context_unref (f->ctx);
@@ -556,8 +413,7 @@ int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
g_test_add ("/eavedrop/match_keyword/broadcast", Fixture, NULL,
setup, test_eavesdrop_broadcast, teardown);
diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c
index dc0f1317..72bcd08c 100644
--- a/test/dbus-daemon.c
+++ b/test/dbus-daemon.c
@@ -2,6 +2,7 @@
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2015 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
@@ -26,22 +27,47 @@
#include <config.h>
-#include <glib.h>
+#include <errno.h>
+#include <string.h>
#include <dbus/dbus.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "test-utils-glib.h"
+
#include <string.h>
-#ifdef DBUS_WIN
-# include <io.h>
-# include <windows.h>
-#else
-# include <signal.h>
+#ifdef DBUS_UNIX
# include <unistd.h>
# include <sys/types.h>
#endif
-#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,111 +83,30 @@ typedef struct {
DBusConnection *right_conn;
gboolean right_conn_echo;
-} Fixture;
-
-#define assert_no_error(e) _assert_no_error (e, __FILE__, __LINE__)
-static void
-_assert_no_error (const DBusError *e,
- const char *file,
- int line)
-{
- if (G_UNLIKELY (dbus_error_is_set (e)))
- g_error ("%s:%d: expected success but got error: %s: %s",
- file, line, e->name, e->message);
-}
-
-static gchar *
-spawn_dbus_daemon (gchar *binary,
- gchar *configuration,
- GPid *daemon_pid)
-{
- GError *error = NULL;
- GString *address;
- gint address_fd;
- gchar *argv[] = {
- binary,
- configuration,
- "--nofork",
- "--print-address=1", /* stdout */
- NULL
- };
-
- g_spawn_async_with_pipes (NULL, /* working directory */
- argv,
- NULL, /* envp */
- G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
- NULL, /* child_setup */
- NULL, /* user data */
- daemon_pid,
- NULL, /* child's stdin = /dev/null */
- &address_fd,
- NULL, /* child's stderr = our stderr */
- &error);
- g_assert_no_error (error);
-
- address = g_string_new (NULL);
-
- /* polling until the dbus-daemon writes out its address is a bit stupid,
- * but at least it's simple, unlike dbus-launch... in principle we could
- * use select() here, but life's too short */
- while (1)
- {
- gssize bytes;
- gchar buf[4096];
- gchar *newline;
-
- bytes = read (address_fd, buf, sizeof (buf));
+ gboolean wait_forever_called;
- if (bytes > 0)
- g_string_append_len (address, buf, bytes);
-
- newline = strchr (address->str, '\n');
-
- if (newline != NULL)
- {
- if ((newline > address->str) && ('\r' == newline[-1]))
- newline -= 1;
- g_string_truncate (address, newline - address->str);
- break;
- }
-
- g_usleep (G_USEC_PER_SEC / 10);
- }
-
- return g_string_free (address, FALSE);
-}
-
-static DBusConnection *
-connect_to_bus (Fixture *f,
- const gchar *address)
-{
- DBusConnection *conn;
- DBusError error = DBUS_ERROR_INIT;
- dbus_bool_t ok;
-
- conn = dbus_connection_open_private (address, &error);
- assert_no_error (&error);
- g_assert (conn != NULL);
-
- ok = dbus_bus_register (conn, &error);
- assert_no_error (&error);
- g_assert (ok);
- g_assert (dbus_bus_get_unique_name (conn) != NULL);
-
- test_connection_setup (f->ctx, conn);
- return conn;
-}
+ gchar *tmp_runtime_dir;
+ gchar *saved_runtime_dir;
+} Fixture;
static DBusHandlerResult
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)
@@ -179,6 +124,7 @@ typedef struct {
const char *bug_ref;
guint min_messages;
const char *config_file;
+ enum { SPECIFY_ADDRESS = 0, RELY_ON_DEFAULT } connect_mode;
} Config;
static void
@@ -186,78 +132,57 @@ setup (Fixture *f,
gconstpointer context)
{
const Config *config = context;
- gchar *dbus_daemon;
- gchar *arg;
gchar *address;
f->ctx = test_main_context_get ();
f->ge = NULL;
dbus_error_init (&f->e);
- if (config != NULL && config->config_file != NULL)
+ if (config != NULL && config->connect_mode == RELY_ON_DEFAULT)
{
- if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
- {
- g_message ("SKIP: cannot use DBUS_TEST_DAEMON_ADDRESS for "
- "unusally-configured dbus-daemon");
- f->skip = TRUE;
- return;
- }
-
- if (g_getenv ("DBUS_TEST_DATA") == NULL)
- {
- g_message ("SKIP: set DBUS_TEST_DATA to a directory containing %s",
- config->config_file);
- f->skip = TRUE;
- return;
- }
+ /* this is chosen to be something needing escaping */
+ f->tmp_runtime_dir = g_dir_make_tmp ("dbus=daemon=test.XXXXXX", &f->ge);
+ g_assert_no_error (f->ge);
- arg = g_strdup_printf (
- "--config-file=%s/%s",
- g_getenv ("DBUS_TEST_DATA"), config->config_file);
- }
- else if (g_getenv ("DBUS_TEST_SYSCONFDIR") != NULL)
- {
- arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
- g_getenv ("DBUS_TEST_SYSCONFDIR"));
+ /* we're relying on being single-threaded for this to be safe */
+ f->saved_runtime_dir = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
+ g_setenv ("XDG_RUNTIME_DIR", f->tmp_runtime_dir, TRUE);
}
- else if (g_getenv ("DBUS_TEST_DATA") != NULL)
- {
- arg = g_strdup_printf (
- "--config-file=%s/valid-config-files/session.conf",
- g_getenv ("DBUS_TEST_DATA"));
- }
- else
+
+ address = test_get_dbus_daemon (config ? config->config_file : NULL,
+ TEST_USER_ME,
+ &f->daemon_pid);
+
+ if (address == NULL)
{
- arg = g_strdup ("--session");
+ f->skip = TRUE;
+ return;
}
- dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
+ f->left_conn = test_connect_to_bus (f->ctx, address);
- if (dbus_daemon == NULL)
- dbus_daemon = g_strdup ("dbus-daemon");
-
- if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
+ if (config != NULL && config->connect_mode == RELY_ON_DEFAULT)
{
- address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
+ /* use the default bus for the echo service ("right"), to check that
+ * it ends up on the same bus as the client ("left") */
+ f->right_conn = dbus_bus_get_private (DBUS_BUS_SESSION, &f->e);
+ test_assert_no_error (&f->e);
+
+ if (!test_connection_setup (f->ctx, f->right_conn))
+ g_error ("OOM");
}
else
{
- address = spawn_dbus_daemon (dbus_daemon, arg, &f->daemon_pid);
+ f->right_conn = test_connect_to_bus (f->ctx, address);
}
- g_free (dbus_daemon);
- g_free (arg);
-
- f->left_conn = connect_to_bus (f, address);
- f->right_conn = connect_to_bus (f, address);
g_free (address);
}
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;
@@ -333,13 +258,77 @@ test_echo (Fixture *f,
}
static void
-pending_call_store_reply (DBusPendingCall *pc,
- void *data)
+test_no_reply (Fixture *f,
+ gconstpointer context)
{
- DBusMessage **message_p = data;
+ 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))
+ test_pending_call_store_reply (pc, &reply);
+ else if (!dbus_pending_call_set_notify (pc, test_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);
- *message_p = dbus_pending_call_steal_reply (pc);
- g_assert (*message_p != NULL);
+ /* 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
@@ -357,7 +346,8 @@ test_creds (Fixture *f,
enum {
SEEN_UNIX_USER = 1,
SEEN_PID = 2,
- SEEN_WINDOWS_SID = 4
+ SEEN_WINDOWS_SID = 4,
+ SEEN_LINUX_SECURITY_LABEL = 8
} seen = 0;
if (m == NULL)
@@ -377,8 +367,8 @@ test_creds (Fixture *f,
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,
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
&m, NULL))
g_error ("OOM");
@@ -416,13 +406,34 @@ test_creds (Fixture *f,
g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
DBUS_TYPE_UINT32);
dbus_message_iter_get_basic (&var_iter, &u32);
- g_message ("%s of this process is %u", name, u32);
+ g_test_message ("%s of this process is %u", name, u32);
g_assert_cmpuint (u32, ==, geteuid ());
seen |= SEEN_UNIX_USER;
#else
g_assert_not_reached ();
#endif
}
+ else if (g_strcmp0 (name, "WindowsSID") == 0)
+ {
+#ifdef G_OS_WIN32
+ gchar *sid;
+ char *self_sid;
+
+ g_assert (!(seen & SEEN_WINDOWS_SID));
+ g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
+ DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic (&var_iter, &sid);
+ g_test_message ("%s of this process is %s", name, sid);
+ if (_dbus_getsid (&self_sid, 0))
+ {
+ g_assert_cmpstr (self_sid, ==, sid);
+ LocalFree(self_sid);
+ }
+ seen |= SEEN_WINDOWS_SID;
+#else
+ g_assert_not_reached ();
+#endif
+ }
else if (g_strcmp0 (name, "ProcessID") == 0)
{
guint32 u32;
@@ -431,7 +442,7 @@ test_creds (Fixture *f,
g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
DBUS_TYPE_UINT32);
dbus_message_iter_get_basic (&var_iter, &u32);
- g_message ("%s of this process is %u", name, u32);
+ g_test_message ("%s of this process is %u", name, u32);
#ifdef G_OS_UNIX
g_assert_cmpuint (u32, ==, getpid ());
#elif defined(G_OS_WIN32)
@@ -441,23 +452,111 @@ test_creds (Fixture *f,
#endif
seen |= SEEN_PID;
}
+ else if (g_strcmp0 (name, "LinuxSecurityLabel") == 0)
+ {
+#ifdef __linux__
+ gchar *label;
+ int len;
+ DBusMessageIter ay_iter;
+
+ g_assert (!(seen & SEEN_LINUX_SECURITY_LABEL));
+ g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==,
+ DBUS_TYPE_ARRAY);
+ dbus_message_iter_recurse (&var_iter, &ay_iter);
+ g_assert_cmpuint (dbus_message_iter_get_arg_type (&ay_iter), ==,
+ DBUS_TYPE_BYTE);
+ dbus_message_iter_get_fixed_array (&ay_iter, &label, &len);
+ g_test_message ("%s of this process is %s", name, label);
+ g_assert_cmpuint (strlen (label) + 1, ==, len);
+ seen |= SEEN_LINUX_SECURITY_LABEL;
+#else
+ g_assert_not_reached ();
+#endif
+ }
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
#ifdef G_OS_WIN32
- /* FIXME: when implemented:
g_assert (seen & SEEN_WINDOWS_SID);
- */
#endif
}
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))
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_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");
+ test_assert_no_error (&error);
+
+ g_test_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
test_canonical_path_uae (Fixture *f,
gconstpointer context)
{
@@ -487,8 +586,8 @@ test_canonical_path_uae (Fixture *f,
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,
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
&m, NULL))
g_error ("OOM");
@@ -525,8 +624,8 @@ test_canonical_path_uae (Fixture *f,
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,
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
&m, NULL))
g_error ("OOM");
@@ -560,7 +659,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;
}
@@ -571,36 +670,75 @@ teardown (Fixture *f,
if (f->daemon_pid != 0)
{
-#ifdef DBUS_WIN
- TerminateProcess (f->daemon_pid, 1);
-#else
- kill (f->daemon_pid, SIGTERM);
-#endif
-
+ test_kill_pid (f->daemon_pid);
g_spawn_close_pid (f->daemon_pid);
f->daemon_pid = 0;
}
+ if (f->tmp_runtime_dir != NULL)
+ {
+ gchar *path;
+
+ /* the socket may exist */
+ path = g_strdup_printf ("%s/bus", f->tmp_runtime_dir);
+ g_assert (g_remove (path) == 0 || errno == ENOENT);
+ g_free (path);
+ /* there shouldn't be anything else in there */
+ g_assert_cmpint (g_rmdir (f->tmp_runtime_dir), ==, 0);
+
+ /* we're relying on being single-threaded for this to be safe */
+ if (f->saved_runtime_dir != NULL)
+ g_setenv ("XDG_RUNTIME_DIR", f->saved_runtime_dir, TRUE);
+ else
+ g_unsetenv ("XDG_RUNTIME_DIR");
+ g_free (f->saved_runtime_dir);
+ g_free (f->tmp_runtime_dir);
+ }
+
test_main_context_unref (f->ctx);
}
static Config limited_config = {
- "34393", 10000, "valid-config-files/incoming-limit.conf"
+ "34393", 10000, "valid-config-files/incoming-limit.conf",
+ SPECIFY_ADDRESS
+};
+
+static Config finite_timeout_config = {
+ NULL, 1, "valid-config-files/finite-timeout.conf",
+ SPECIFY_ADDRESS
};
+#ifdef DBUS_UNIX
+static Config listen_unix_runtime_config = {
+ "61303", 1, "valid-config-files/listen-unix-runtime.conf",
+ RELY_ON_DEFAULT
+};
+#endif
+
int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
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);
g_test_add ("/canonical-path/uae", Fixture, NULL,
setup, test_canonical_path_uae, teardown);
+#ifdef DBUS_UNIX
+ /* We can't test this in loopback.c with the rest of unix:runtime=yes,
+ * because dbus_bus_get[_private] is the only way to use the default,
+ * and that blocks on a round-trip to the dbus-daemon */
+ g_test_add ("/unix-runtime-is-default", Fixture, &listen_unix_runtime_config,
+ setup, test_echo, teardown);
+#endif
return g_test_run ();
}
diff --git a/test/fdpass.c b/test/fdpass.c
new file mode 100644
index 00000000..a74ce814
--- /dev/null
+++ b/test/fdpass.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright © 2010-2012 Nokia Corporation
+ * Copyright © 2014 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-sysdeps.h>
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef G_OS_UNIX
+# include <dbus/dbus-sysdeps-unix.h>
+
+# include <errno.h>
+# include <fcntl.h>
+# ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+# endif
+# include <sys/stat.h>
+# include <sys/time.h>
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+#include "test-utils-glib.h"
+
+/* Arbitrary; included here to avoid relying on the default */
+#define MAX_MESSAGE_UNIX_FDS 20
+/* This test won't work on Linux unless this is true. */
+_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS <= 253);
+
+/* Arbitrary; included here to avoid relying on the default. */
+#define MAX_INCOMING_UNIX_FDS (MAX_MESSAGE_UNIX_FDS * 4)
+
+/* Arbitrary, except that MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES should be
+ * less than the process's file descriptor limit. */
+#define SOME_MESSAGES 20
+/* To cover some situations on Linux we want this to be true. */
+_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES > 256);
+
+/* Linux won't allow more than 253 fds per sendmsg(). */
+#define TOO_MANY_FDS 255
+_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS < TOO_MANY_FDS);
+
+/* As in test/relay.c, this is a miniature dbus-daemon: we relay messages
+ * from the client on the left to the client on the right.
+ *
+ * left socket left dispatch right socket right
+ * client ===========> server --------------> server ===========> client
+ * conn conn conn conn
+ */
+
+typedef struct {
+ TestMainContext *ctx;
+ DBusError e;
+
+ DBusServer *server;
+
+ DBusConnection *left_client_conn;
+ DBusConnection *left_server_conn;
+
+ DBusConnection *right_server_conn;
+ DBusConnection *right_client_conn;
+ /* queue of DBusMessage received by right_client_conn */
+ GQueue messages;
+
+ int fd_before;
+} Fixture;
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+static void oom (const gchar *doing) G_GNUC_NORETURN;
+
+static void
+oom (const gchar *doing)
+{
+ g_error ("out of memory (%s)", doing);
+ abort ();
+}
+
+static void
+assert_no_error (const DBusError *e)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("expected success but got error: %s: %s", e->name, e->message);
+}
+
+static DBusHandlerResult
+left_server_message_cb (DBusConnection *server_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (server_conn == f->left_server_conn);
+ g_assert (f->right_server_conn != NULL);
+
+ dbus_connection_send (f->right_server_conn, message, NULL);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+right_client_message_cb (DBusConnection *client_conn,
+ DBusMessage *message,
+ void *data)
+{
+ Fixture *f = data;
+
+ g_assert (client_conn == f->right_client_conn);
+ g_queue_push_tail (&f->messages, dbus_message_ref (message));
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+new_conn_cb (DBusServer *server,
+ DBusConnection *server_conn,
+ void *data)
+{
+ Fixture *f = data;
+
+ dbus_connection_set_max_message_unix_fds (server_conn,
+ MAX_MESSAGE_UNIX_FDS);
+ dbus_connection_set_max_received_unix_fds (server_conn,
+ MAX_INCOMING_UNIX_FDS);
+
+ if (f->left_server_conn == NULL)
+ {
+ f->left_server_conn = dbus_connection_ref (server_conn);
+
+ if (!dbus_connection_add_filter (server_conn,
+ left_server_message_cb, f, NULL))
+ oom ("adding filter");
+ }
+ else
+ {
+ g_assert (f->right_server_conn == NULL);
+ f->right_server_conn = dbus_connection_ref (server_conn);
+ }
+
+ test_connection_setup (f->ctx, server_conn);
+}
+
+static void
+test_connect (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+ char *address;
+
+ g_assert (f->left_server_conn == NULL);
+ g_assert (f->right_server_conn == NULL);
+
+ address = dbus_server_get_address (f->server);
+ g_assert (address != NULL);
+
+ f->left_client_conn = dbus_connection_open_private (address, &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->left_client_conn != NULL);
+ test_connection_setup (f->ctx, f->left_client_conn);
+
+ /* The left client connection is allowed to behave abusively. */
+ dbus_connection_set_max_message_unix_fds (f->left_client_conn, 1000);
+ dbus_connection_set_max_received_unix_fds (f->left_client_conn, 1000000);
+
+ while (f->left_server_conn == NULL)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ f->right_client_conn = dbus_connection_open_private (address, &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->right_client_conn != NULL);
+ test_connection_setup (f->ctx, f->right_client_conn);
+
+ dbus_free (address);
+
+ while (f->right_server_conn == NULL)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ if (!dbus_connection_add_filter (f->right_client_conn,
+ right_client_message_cb, f, NULL))
+ oom ("adding filter");
+
+ /* The right client connection is allowed to queue all the messages. */
+ dbus_connection_set_max_message_unix_fds (f->right_client_conn, 1000);
+ dbus_connection_set_max_received_unix_fds (f->right_client_conn, 1000000);
+
+ while (!dbus_connection_get_is_authenticated (f->left_client_conn) ||
+ !dbus_connection_get_is_authenticated (f->right_client_conn) ||
+ !dbus_connection_get_is_authenticated (f->left_server_conn) ||
+ !dbus_connection_get_is_authenticated (f->right_server_conn))
+ {
+ test_progress ('*');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ if (!dbus_connection_can_send_type (f->left_client_conn,
+ DBUS_TYPE_UNIX_FD))
+ g_error ("left client connection cannot send Unix fds");
+
+ if (!dbus_connection_can_send_type (f->left_server_conn,
+ DBUS_TYPE_UNIX_FD))
+ g_error ("left server connection cannot send Unix fds");
+
+ if (!dbus_connection_can_send_type (f->right_client_conn,
+ DBUS_TYPE_UNIX_FD))
+ g_error ("right client connection cannot send Unix fds");
+
+ if (!dbus_connection_can_send_type (f->right_server_conn,
+ DBUS_TYPE_UNIX_FD))
+ g_error ("right server connection cannot send Unix fds");
+}
+#endif
+
+static void
+setup (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ /* We assume that anything with fd-passing supports the unix: transport */
+
+ f->ctx = test_main_context_get ();
+ dbus_error_init (&f->e);
+ g_queue_init (&f->messages);
+
+ f->server = dbus_server_listen ("unix:tmpdir=/tmp", &f->e);
+ assert_no_error (&f->e);
+ g_assert (f->server != NULL);
+
+ dbus_server_set_new_connection_function (f->server,
+ new_conn_cb, f, NULL);
+ test_server_setup (f->ctx, f->server);
+
+ f->fd_before = open ("/dev/null", O_RDONLY);
+
+ /* this should succeed on any reasonable Unix */
+ if (f->fd_before < 0)
+ g_error ("cannot open /dev/null for reading: %s", g_strerror (errno));
+
+ _dbus_fd_set_close_on_exec (f->fd_before);
+#endif
+}
+
+static void
+test_relay (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ /* We assume that any platform with working fd-passing is POSIX,
+ * and therefore has open() and fstat() */
+ dbus_uint32_t serial;
+ DBusMessage *outgoing, *incoming;
+ int fd_after;
+ struct stat stat_before;
+ struct stat stat_after;
+
+ test_connect (f, data);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ if (!dbus_message_append_args (outgoing,
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+
+ if (!dbus_connection_send (f->left_client_conn, outgoing, &serial))
+ oom ("sending message");
+
+ dbus_message_unref (outgoing);
+
+ while (g_queue_get_length (&f->messages) < 1)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
+
+ incoming = g_queue_pop_head (&f->messages);
+
+ g_assert (dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_signature (incoming), ==,
+ DBUS_TYPE_UNIX_FD_AS_STRING);
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
+ g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
+
+ if (!dbus_message_get_args (incoming,
+ &f->e,
+ DBUS_TYPE_UNIX_FD, &fd_after,
+ DBUS_TYPE_INVALID))
+ g_error ("%s: %s", f->e.name, f->e.message);
+
+ assert_no_error (&f->e);
+
+ if (fstat (f->fd_before, &stat_before) < 0)
+ g_error ("%s", g_strerror (errno));
+
+ if (fstat (fd_after, &stat_after) < 0)
+ g_error ("%s", g_strerror (errno));
+
+ /* this seems like enough to say "it's the same file" */
+ g_assert_cmpint (stat_before.st_dev, ==, stat_after.st_dev);
+ g_assert_cmpint (stat_before.st_ino, ==, stat_after.st_ino);
+ g_assert_cmpint (stat_before.st_rdev, ==, stat_after.st_rdev);
+
+ dbus_message_unref (incoming);
+
+ if (close (fd_after) < 0)
+ g_error ("%s", g_strerror (errno));
+
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_server_conn));
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+test_limit (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ dbus_uint32_t serial;
+ DBusMessage *outgoing, *incoming;
+ int i;
+
+ test_connect (f, data);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ for (i = 0; i < MAX_MESSAGE_UNIX_FDS; i++)
+ {
+ if (!dbus_message_append_args (outgoing,
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+ }
+
+ if (!dbus_connection_send (f->left_client_conn, outgoing, &serial))
+ oom ("sending message");
+
+ dbus_message_unref (outgoing);
+
+ while (g_queue_get_length (&f->messages) < 1)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
+
+ incoming = g_queue_pop_head (&f->messages);
+
+ g_assert (dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
+ g_assert_cmpuint (dbus_message_get_serial (incoming), ==, serial);
+
+ dbus_message_unref (incoming);
+
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_server_conn));
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+test_too_many (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ DBusMessage *outgoing;
+ unsigned int i;
+
+ test_connect (f, data);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ for (i = 0; i < MAX_MESSAGE_UNIX_FDS + GPOINTER_TO_UINT (data); i++)
+ {
+ if (!dbus_message_append_args (outgoing,
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+ }
+
+ if (!dbus_connection_send (f->left_client_conn, outgoing, NULL))
+ oom ("sending message");
+
+ dbus_message_unref (outgoing);
+
+ /* The sender is unceremoniously disconnected. */
+ while (dbus_connection_get_is_connected (f->left_client_conn) ||
+ dbus_connection_get_is_connected (f->left_server_conn))
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ /* The message didn't get through without its fds. */
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
+
+ /* The intended victim is unaffected by the left connection's
+ * misbehaviour. */
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+test_too_many_split (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ DBusMessage *outgoing;
+ int i;
+ DBusSocket left_client_socket;
+ char *payload;
+ int payload_len;
+ DBusString buffer;
+ int fds[TOO_MANY_FDS];
+ int done;
+
+ /* This test deliberately pushes up against OS limits, so skip it
+ * if we don't have enough fds. 4 times the maximum per message
+ * ought to be enough: that will cover the message, the dup'd fds
+ * we actually send, the copy that we potentially receive, and some
+ * spare capacity for everything else. */
+#ifdef HAVE_GETRLIMIT
+ struct rlimit lim;
+
+ if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
+ {
+ if (lim.rlim_cur != RLIM_INFINITY &&
+ lim.rlim_cur < 4 * TOO_MANY_FDS)
+ {
+ g_test_skip ("not enough RLIMIT_NOFILE");
+ return;
+ }
+ }
+#endif
+
+ test_connect (f, data);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ /* TOO_MANY_FDS fds are far too many: in particular, Linux doesn't allow
+ * sending this many in a single sendmsg(). libdbus never splits
+ * a message between two sendmsg() calls if it can help it, and
+ * in particular it always sends all the fds with the first sendmsg(),
+ * but malicious senders might not be so considerate. */
+ for (i = 0; i < TOO_MANY_FDS; i++)
+ {
+ if (!dbus_message_append_args (outgoing,
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+ }
+
+ /* This probably shouldn't work for messages with fds, but it does,
+ * which is convenient for this sort of trickery. */
+ if (!dbus_message_marshal (outgoing, &payload, &payload_len))
+ oom ("marshalling message");
+
+ _dbus_string_init_const_len (&buffer, payload, payload_len);
+
+ for (i = 0; i < TOO_MANY_FDS; i++)
+ {
+ fds[i] = dup (f->fd_before);
+
+ if (fds[i] < 0)
+ g_error ("could not dup fd: %s", g_strerror (errno));
+ }
+
+ /* This is blatant cheating, and the API documentation specifically
+ * tells you not use this function in this way. Never do this
+ * in application code. */
+ if (!dbus_connection_get_socket (f->left_client_conn,
+ &left_client_socket.fd))
+ g_error ("'unix:' DBusConnection should have had a socket");
+
+ /* Just to be sure that we're at a message boundary. */
+ dbus_connection_flush (f->left_client_conn);
+
+ /* We have too many fds for one sendmsg(), so send the first half
+ * (rounding down if odd) with the first byte... */
+ done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 0, 1,
+ &fds[0], TOO_MANY_FDS / 2);
+
+ if (done < 0)
+ g_error ("could not send first byte and first batch of fds: %s",
+ g_strerror (errno));
+
+ /* ... and the second half (rounding up if odd) with the rest of
+ * the message */
+ done = _dbus_write_socket_with_unix_fds (left_client_socket, &buffer, 1,
+ payload_len - 1, &fds[TOO_MANY_FDS / 2],
+ TOO_MANY_FDS - (TOO_MANY_FDS / 2));
+
+ if (done < 0)
+ {
+ g_error ("could not send rest of message and rest of fds: %s",
+ g_strerror (errno));
+ }
+ else if (done < payload_len - 1)
+ {
+ /* For simplicity, assume the socket buffer is big enough for the
+ * whole message, which should be < 2 KiB. If this fails on some
+ * OS, redo this test code to use a proper loop like the real
+ * libdbus does. */
+ g_error ("short write in sendmsg(), fix this test: %d/%d",
+ done, payload_len - 1);
+ }
+
+ dbus_free (payload);
+
+ for (i = 0; i < TOO_MANY_FDS; i++)
+ close (fds[i]);
+
+ dbus_message_unref (outgoing);
+
+ /* The sender is unceremoniously disconnected. */
+ while (dbus_connection_get_is_connected (f->left_client_conn) ||
+ dbus_connection_get_is_connected (f->left_server_conn))
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ /* The message didn't get through without its fds. */
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
+
+ /* The intended victim is unaffected by the left connection's
+ * misbehaviour. */
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+test_flood (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ unsigned int i, j;
+ DBusMessage *outgoing[SOME_MESSAGES];
+ dbus_uint32_t serial;
+
+ test_connect (f, data);
+
+ for (j = 0; j < SOME_MESSAGES; j++)
+ {
+ outgoing[j] = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing[j] != NULL);
+
+ for (i = 0; i < GPOINTER_TO_UINT (data); i++)
+ {
+ if (!dbus_message_append_args (outgoing[j],
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+ }
+ }
+
+ /* This is in its own loop so we do it as fast as possible */
+ for (j = 0; j < SOME_MESSAGES; j++)
+ {
+ if (!dbus_connection_send (f->left_client_conn, outgoing[j], &serial))
+ oom ("sending message");
+ }
+
+ for (j = 0; j < SOME_MESSAGES; j++)
+ {
+ dbus_message_unref (outgoing[j]);
+ }
+
+ while (g_queue_get_length (&f->messages) < SOME_MESSAGES)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, SOME_MESSAGES);
+
+ for (j = 0; j < SOME_MESSAGES; j++)
+ {
+ DBusMessage *incoming;
+
+ incoming = g_queue_pop_head (&f->messages);
+
+ g_assert (dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==, "/com/example/Hello");
+
+ dbus_message_unref (incoming);
+ }
+
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_server_conn));
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+test_odd_limit (Fixture *f,
+ gconstpointer data)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ DBusMessage *outgoing;
+ int i;
+
+ test_connect (f, data);
+ dbus_connection_set_max_message_unix_fds (f->left_server_conn, 7);
+ dbus_connection_set_max_message_unix_fds (f->right_server_conn, 7);
+
+ outgoing = dbus_message_new_signal ("/com/example/Hello",
+ "com.example.Hello", "Greeting");
+ g_assert (outgoing != NULL);
+
+ for (i = 0; i < 7 + GPOINTER_TO_INT (data); i++)
+ {
+ if (!dbus_message_append_args (outgoing,
+ DBUS_TYPE_UNIX_FD, &f->fd_before,
+ DBUS_TYPE_INVALID))
+ oom ("appending fd");
+ }
+
+ if (!dbus_connection_send (f->left_client_conn, outgoing, NULL))
+ oom ("sending message");
+
+ dbus_message_unref (outgoing);
+
+ if (GPOINTER_TO_INT (data) > 0)
+ {
+ /* The sender is unceremoniously disconnected. */
+ while (dbus_connection_get_is_connected (f->left_client_conn) ||
+ dbus_connection_get_is_connected (f->left_server_conn))
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ /* The message didn't get through without its fds. */
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 0);
+
+ /* The intended victim is unaffected by the left connection's
+ * misbehaviour. */
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+ }
+ else
+ {
+ DBusMessage *incoming;
+
+ /* We're at or under the limit. The message gets through intact. */
+ while (g_queue_get_length (&f->messages) < 1)
+ {
+ test_progress ('.');
+ test_main_context_iterate (f->ctx, TRUE);
+ }
+
+ g_assert_cmpuint (g_queue_get_length (&f->messages), ==, 1);
+
+ incoming = g_queue_pop_head (&f->messages);
+
+ g_assert (dbus_message_contains_unix_fds (incoming));
+ g_assert_cmpstr (dbus_message_get_destination (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_error_name (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_interface (incoming), ==,
+ "com.example.Hello");
+ g_assert_cmpstr (dbus_message_get_member (incoming), ==, "Greeting");
+ g_assert_cmpstr (dbus_message_get_sender (incoming), ==, NULL);
+ g_assert_cmpstr (dbus_message_get_path (incoming), ==,
+ "/com/example/Hello");
+
+ dbus_message_unref (incoming);
+
+ g_assert (dbus_connection_get_is_connected (f->right_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->right_server_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_client_conn));
+ g_assert (dbus_connection_get_is_connected (f->left_server_conn));
+ }
+#else
+ g_test_skip ("fd-passing not supported on this platform");
+#endif
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer data G_GNUC_UNUSED)
+{
+#ifdef HAVE_UNIX_FD_PASSING
+ if (f->left_client_conn != NULL)
+ {
+ dbus_connection_close (f->left_client_conn);
+ dbus_connection_unref (f->left_client_conn);
+ f->left_client_conn = NULL;
+ }
+
+ if (f->right_client_conn != NULL)
+ {
+ dbus_connection_close (f->right_client_conn);
+ dbus_connection_unref (f->right_client_conn);
+ f->right_client_conn = NULL;
+ }
+
+ if (f->left_server_conn != NULL)
+ {
+ dbus_connection_close (f->left_server_conn);
+ dbus_connection_unref (f->left_server_conn);
+ f->left_server_conn = NULL;
+ }
+
+ if (f->right_server_conn != NULL)
+ {
+ dbus_connection_close (f->right_server_conn);
+ dbus_connection_unref (f->right_server_conn);
+ f->right_server_conn = NULL;
+ }
+
+ if (f->server != NULL)
+ {
+ dbus_server_disconnect (f->server);
+ dbus_server_unref (f->server);
+ f->server = NULL;
+ }
+
+ test_main_context_unref (f->ctx);
+
+ if (f->fd_before >= 0 && close (f->fd_before) < 0)
+ g_error ("%s", g_strerror (errno));
+#endif
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ test_init (&argc, &argv);
+
+#ifdef HAVE_GETRLIMIT
+ {
+ struct rlimit lim;
+
+ if (getrlimit (RLIMIT_NOFILE, &lim) < 0)
+ g_error ("Failed to get RLIMIT_NOFILE limit: %s", g_strerror (errno));
+
+ if (lim.rlim_cur != RLIM_INFINITY &&
+ /* only run if we have a fairly generous margin of error
+ * for stdout, stderr, duplicates, the D-Bus connection, etc. */
+ lim.rlim_cur < 2 * MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES)
+ {
+ g_message ("not enough RLIMIT_NOFILE to run this test");
+ /* Autotools exit code for "all skipped" */
+ return 77;
+ }
+ }
+#endif
+
+ g_test_add ("/relay", Fixture, NULL, setup,
+ test_relay, teardown);
+ g_test_add ("/limit", Fixture, NULL, setup,
+ test_limit, teardown);
+
+ g_test_add ("/too-many/plus1", Fixture, GUINT_TO_POINTER (1), setup,
+ test_too_many, teardown);
+ g_test_add ("/too-many/plus2", Fixture, GUINT_TO_POINTER (2), setup,
+ test_too_many, teardown);
+ g_test_add ("/too-many/plus17", Fixture, GUINT_TO_POINTER (17), setup,
+ test_too_many, teardown);
+
+ g_test_add ("/too-many/split", Fixture, NULL, setup,
+ test_too_many_split, teardown);
+
+ g_test_add ("/flood/1", Fixture, GUINT_TO_POINTER (1),
+ setup, test_flood, teardown);
+#if MAX_MESSAGE_UNIX_FDS >= 2
+ g_test_add ("/flood/half-limit", Fixture,
+ GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS / 2),
+ setup, test_flood, teardown);
+#endif
+#if MAX_MESSAGE_UNIX_FDS >= 4
+ g_test_add ("/flood/over-half-limit", Fixture,
+ GUINT_TO_POINTER (3 * MAX_MESSAGE_UNIX_FDS / 4),
+ setup, test_flood, teardown);
+#endif
+ g_test_add ("/flood/limit", Fixture,
+ GUINT_TO_POINTER (MAX_MESSAGE_UNIX_FDS), setup, test_flood, teardown);
+
+ g_test_add ("/odd-limit/minus1", Fixture, GINT_TO_POINTER (-1), setup,
+ test_odd_limit, teardown);
+ g_test_add ("/odd-limit/at", Fixture, GINT_TO_POINTER (0), setup,
+ test_odd_limit, teardown);
+ g_test_add ("/odd-limit/plus1", Fixture, GINT_TO_POINTER (+1), setup,
+ test_odd_limit, teardown);
+ g_test_add ("/odd-limit/plus2", Fixture, GINT_TO_POINTER (+2), setup,
+ test_odd_limit, teardown);
+
+ return g_test_run ();
+}
diff --git a/test/glib-tap-test.sh b/test/glib-tap-test.sh
new file mode 100755
index 00000000..fcb73383
--- /dev/null
+++ b/test/glib-tap-test.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Wrapper to make GTest tests output TAP syntax, because Automake's test
+# drivers do not currently support passing the same command-line argument
+# to each test executable. All GTest tests produce TAP output if invoked
+# with the --tap option.
+#
+# Usage: "glib-tap-test.sh test-foo --verbose ..." is equivalent to
+# "test-foo --tap --verbose ..."
+
+set -e
+t="$1"
+shift
+exec "$t" --tap "$@"
diff --git a/test/internals/printf.c b/test/internals/printf.c
index db151518..2006f321 100644
--- a/test/internals/printf.c
+++ b/test/internals/printf.c
@@ -62,20 +62,32 @@ do_test (int minimum,
#define X_TIMES_512 X_TIMES_256 X_TIMES_256
#define X_TIMES_1024 X_TIMES_512 X_TIMES_512
+/* This test outputs TAP syntax: http://testanything.org/ */
int
main (int argc,
char **argv)
{
char buf[] = X_TIMES_1024 X_TIMES_1024 X_TIMES_1024 X_TIMES_1024;
int i;
+ int test_num = 0;
do_test (1, "%d", 0);
+ printf ("ok %d\n", ++test_num);
+
do_test (7, "%d", 1234567);
+ printf ("ok %d\n", ++test_num);
+
do_test (3, "%f", 3.5);
+ printf ("ok %d\n", ++test_num);
do_test (0, "%s", "");
+ printf ("ok %d\n", ++test_num);
+
do_test (1024, "%s", X_TIMES_1024);
+ printf ("ok %d\n", ++test_num);
+
do_test (1025, "%s", X_TIMES_1024 "Y");
+ printf ("ok %d\n", ++test_num);
for (i = 4096; i > 0; i--)
{
@@ -83,6 +95,11 @@ main (int argc,
do_test (i, "%s", buf);
do_test (i + 3, "%s:%d", buf, 42);
}
+ printf ("ok %d\n", ++test_num);
+ /* Tell the TAP driver that we have done all the tests we plan to do.
+ * This is how it can distinguish between an unexpected exit and
+ * successful completion. */
+ printf ("1..%d\n", test_num);
return 0;
}
diff --git a/test/internals/refs.c b/test/internals/refs.c
index 202dc043..7eae44e1 100644
--- a/test/internals/refs.c
+++ b/test/internals/refs.c
@@ -35,7 +35,7 @@
#include <dbus/dbus-message-internal.h>
#include <dbus/dbus-pending-call-internal.h>
#include <dbus/dbus-server-protected.h>
-#include "test-utils.h"
+#include "test-utils-glib.h"
static void
assert_no_error (const DBusError *e)
@@ -585,14 +585,7 @@ int
main (int argc,
char **argv)
{
- /* In GLib >= 2.24, < 2.31 this acts like g_thread_init() but avoids
- * the deprecation of that function. In GLib >= 2.32 this is not
- * necessary at all.
- */
- g_type_init ();
-
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
g_test_add ("/refs/connection", Fixture, NULL, setup_connection,
test_connection, teardown);
diff --git a/test/internals/syslog.c b/test/internals/syslog.c
index 7e0eae79..805c5784 100644
--- a/test/internals/syslog.c
+++ b/test/internals/syslog.c
@@ -33,6 +33,8 @@
#include <dbus/dbus.h>
#include <dbus/dbus-sysdeps.h>
+#include "test-utils-glib.h"
+
typedef struct {
int dummy;
} Fixture;
@@ -68,16 +70,18 @@ test_syslog (Fixture *f,
{
_dbus_init_system_log (FALSE);
_dbus_system_log (DBUS_SYSTEM_LOG_INFO, MESSAGE "%d", 42);
+ _dbus_system_log (DBUS_SYSTEM_LOG_WARNING, MESSAGE "%d", 45);
_dbus_system_log (DBUS_SYSTEM_LOG_SECURITY, MESSAGE "%d", 666);
exit (0);
}
g_test_trap_assert_passed ();
- g_test_trap_assert_stderr ("*" MESSAGE "42\n*" MESSAGE "666\n*");
+ g_test_trap_assert_stderr ("*" MESSAGE "42\n*" MESSAGE "45\n*" MESSAGE "666\n*");
#endif
/* manual test (this is the best we can do on Windows) */
_dbus_init_system_log (FALSE);
_dbus_system_log (DBUS_SYSTEM_LOG_INFO, MESSAGE "%d", 42);
+ _dbus_system_log (DBUS_SYSTEM_LOG_WARNING, MESSAGE "%d", 45);
_dbus_system_log (DBUS_SYSTEM_LOG_SECURITY, MESSAGE "%d", 666);
}
@@ -91,8 +95,7 @@ int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
g_test_add ("/syslog", Fixture, NULL, setup, test_syslog, teardown);
diff --git a/test/loopback.c b/test/loopback.c
index 7526d8d2..bf0542aa 100644
--- a/test/loopback.c
+++ b/test/loopback.c
@@ -2,6 +2,7 @@
*
* Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
* Copyright © 2010-2012 Nokia Corporation
+ * Copyright © 2015 Collabora Ltd.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
@@ -27,12 +28,14 @@
#include <config.h>
#include <glib.h>
+#include <glib/gstdio.h>
#include <dbus/dbus.h>
+#include <errno.h>
#include <string.h>
-#include "test-utils.h"
+#include "test-utils-glib.h"
typedef struct {
TestMainContext *ctx;
@@ -44,6 +47,9 @@ typedef struct {
GQueue server_messages;
DBusConnection *client_conn;
+
+ gchar *tmp_runtime_dir;
+ gchar *saved_runtime_dir;
} Fixture;
static void
@@ -100,6 +106,56 @@ setup (Fixture *f,
test_server_setup (f->ctx, f->server);
}
+#ifdef DBUS_UNIX
+static void
+setup_runtime (Fixture *f,
+ gconstpointer addr)
+{
+ char *listening_at;
+ GError *error = NULL;
+
+ /* this is chosen to be something needing escaping */
+ f->tmp_runtime_dir = g_dir_make_tmp ("dbus=daemon=test.XXXXXX", &error);
+ g_assert_no_error (error);
+
+ /* we're relying on being single-threaded for this to be safe */
+ f->saved_runtime_dir = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
+ g_setenv ("XDG_RUNTIME_DIR", f->tmp_runtime_dir, TRUE);
+
+ setup (f, addr);
+
+ listening_at = dbus_server_get_address (f->server);
+ g_test_message ("listening at %s", listening_at);
+ g_assert (g_str_has_prefix (listening_at, "unix:path="));
+ g_assert (strstr (listening_at, "dbus%3ddaemon%3dtest.") != NULL);
+ g_assert (strstr (listening_at, "/bus,") != NULL ||
+ g_str_has_suffix (listening_at, "/bus"));
+
+ dbus_free (listening_at);
+}
+
+static void
+setup_no_runtime (Fixture *f,
+ gconstpointer addr)
+{
+ char *listening_at;
+
+ /* we're relying on being single-threaded for this to be safe */
+ f->saved_runtime_dir = g_strdup (g_getenv ("XDG_RUNTIME_DIR"));
+ g_unsetenv ("XDG_RUNTIME_DIR");
+
+ setup (f, addr);
+
+ listening_at = dbus_server_get_address (f->server);
+ g_test_message ("listening at %s", listening_at);
+ /* we have fallen back to something in /tmp, either abstract or not */
+ g_assert (g_str_has_prefix (listening_at, "unix:"));
+ g_assert (strstr (listening_at, "=/tmp/") != NULL);
+
+ dbus_free (listening_at);
+}
+#endif
+
static void
test_connect (Fixture *f,
gconstpointer addr G_GNUC_UNUSED)
@@ -114,7 +170,7 @@ test_connect (Fixture *f,
while (f->server_conn == NULL)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
}
@@ -148,7 +204,7 @@ test_bad_guid (Fixture *f,
while (f->server_conn == NULL)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -156,7 +212,7 @@ test_bad_guid (Fixture *f,
while (g_queue_is_empty (&f->server_messages))
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -199,7 +255,7 @@ test_message (Fixture *f,
while (g_queue_is_empty (&f->server_messages))
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -251,12 +307,51 @@ teardown (Fixture *f,
test_main_context_unref (f->ctx);
}
+#ifdef DBUS_UNIX
+static void
+teardown_no_runtime (Fixture *f,
+ gconstpointer addr)
+{
+ teardown (f, addr);
+
+ /* we're relying on being single-threaded for this to be safe */
+ if (f->saved_runtime_dir != NULL)
+ g_setenv ("XDG_RUNTIME_DIR", f->saved_runtime_dir, TRUE);
+ else
+ g_unsetenv ("XDG_RUNTIME_DIR");
+ g_free (f->saved_runtime_dir);
+}
+
+static void
+teardown_runtime (Fixture *f,
+ gconstpointer addr)
+{
+ gchar *path;
+
+ teardown (f, addr);
+
+ /* the socket may exist */
+ path = g_strdup_printf ("%s/bus", f->tmp_runtime_dir);
+ g_assert (g_remove (path) == 0 || errno == ENOENT);
+ g_free (path);
+ /* there shouldn't be anything else in there */
+ g_assert_cmpint (g_rmdir (f->tmp_runtime_dir), ==, 0);
+
+ /* we're relying on being single-threaded for this to be safe */
+ if (f->saved_runtime_dir != NULL)
+ g_setenv ("XDG_RUNTIME_DIR", f->saved_runtime_dir, TRUE);
+ else
+ g_unsetenv ("XDG_RUNTIME_DIR");
+ g_free (f->saved_runtime_dir);
+ g_free (f->tmp_runtime_dir);
+}
+#endif
+
int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
g_test_add ("/connect/tcp", Fixture, "tcp:host=127.0.0.1", setup,
test_connect, teardown);
@@ -273,6 +368,13 @@ main (int argc,
test_connect, teardown);
g_test_add ("/message/unix", Fixture, "unix:tmpdir=/tmp", setup,
test_message, teardown);
+
+ g_test_add ("/connect/unix/runtime", Fixture,
+ "unix:runtime=yes;unix:tmpdir=/tmp", setup_runtime, test_connect,
+ teardown_runtime);
+ g_test_add ("/connect/unix/no-runtime", Fixture,
+ "unix:runtime=yes;unix:tmpdir=/tmp", setup_no_runtime, test_connect,
+ teardown_no_runtime);
#endif
g_test_add ("/message/bad-guid", Fixture, "tcp:host=127.0.0.1", setup,
diff --git a/test/manual-authz.c b/test/manual-authz.c
index f9e3688e..ee9bc52f 100644
--- a/test/manual-authz.c
+++ b/test/manual-authz.c
@@ -30,6 +30,7 @@
#include <dbus/dbus.h>
+#include <stdlib.h>
#ifdef G_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
@@ -57,6 +58,7 @@ static void
oom (void)
{
g_error ("out of memory");
+ abort ();
}
static void
diff --git a/test/manual-dir-iter.c b/test/manual-dir-iter.c
new file mode 100644
index 00000000..21ac0e95
--- /dev/null
+++ b/test/manual-dir-iter.c
@@ -0,0 +1,92 @@
+#include <config.h>
+#include "test-utils.h"
+
+#include "dbus/dbus-macros.h"
+#include "dbus/dbus-sysdeps.h"
+
+static void oom (const char *doing) _DBUS_GNUC_NORETURN;
+static void die (const char *message) _DBUS_GNUC_NORETURN;
+
+void
+oom (const char *doing)
+{
+ fprintf (stderr, "*** manual-dir-iter: OOM while %s\n", doing);
+ exit (1);
+}
+
+void
+die (const char *message)
+{
+ fprintf (stderr, "*** manual-dir-iter: %s\n", message);
+ exit (1);
+}
+
+static void
+debug (const char *message)
+{
+ fprintf (stdout, "+++ manual-dir-iter: %s\n", message);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ DBusString filename;
+ DBusString dirname;
+ DBusError tmp_error;
+ DBusDirIter *dir;
+
+ if (argc != 2)
+ die ("syntax: manual-dir-iter <path>");
+
+ dbus_error_init (&tmp_error);
+
+ if (!_dbus_string_init (&filename))
+ oom ("init filename");
+
+ if (!_dbus_string_init (&dirname))
+ oom ("init dirname");
+
+ _dbus_string_append (&dirname, argv[1]);
+ dir = _dbus_directory_open (&dirname, &tmp_error);
+
+ if (dir == NULL)
+ {
+ fprintf (stderr, "could not open directory: %s: %s\n",
+ tmp_error.name, tmp_error.message);
+ exit(1);
+ }
+
+ while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
+ {
+ DBusString full_path;
+ if (!_dbus_string_init (&full_path))
+ {
+ oom ("init full_path");
+ }
+
+ if (!_dbus_string_copy (&dirname, 0, &full_path, 0))
+ {
+ oom ("copying full_path to dirname");
+ }
+
+ if (!_dbus_concat_dir_and_file (&full_path, &filename))
+ {
+ oom ("concat full_path");
+ }
+ debug (_dbus_string_get_const_data (&filename));
+ _dbus_string_free (&full_path);
+ }
+
+ if (dbus_error_is_set (&tmp_error))
+ die (tmp_error.message);
+
+ _dbus_string_free (&filename);
+
+ if (dir)
+ _dbus_directory_close (dir);
+
+ _dbus_verbose ("*** Test dir name exiting\n");
+
+ return 0;
+}
diff --git a/test/manual-paths.c b/test/manual-paths.c
new file mode 100644
index 00000000..e392c5c3
--- /dev/null
+++ b/test/manual-paths.c
@@ -0,0 +1,73 @@
+/*
+ * Simple manual paths check
+ *
+ * syntax: manual-paths
+ *
+*/
+
+#include "config.h"
+#include "dbus/dbus-list.h"
+#include "dbus/dbus-internals.h"
+#include "dbus/dbus-sysdeps.h"
+
+#include <stdio.h>
+
+static dbus_bool_t print_install_root()
+{
+ char runtime_prefix[1000];
+
+ if (!_dbus_get_install_root(runtime_prefix, sizeof(runtime_prefix)))
+ {
+ fprintf(stderr, "dbus_get_install_root() failed\n");
+ return FALSE;
+ }
+ fprintf(stdout, "dbus_get_install_root() returned '%s'\n", runtime_prefix);
+ return TRUE;
+}
+
+static dbus_bool_t print_service_dirs()
+{
+ DBusList *dirs;
+ DBusList *link;
+ dirs = NULL;
+
+ if (!_dbus_get_standard_session_servicedirs (&dirs))
+ _dbus_assert_not_reached ("couldn't get standard dirs");
+
+ while ((link = _dbus_list_pop_first_link (&dirs)))
+ {
+ printf ("default service dir: %s\n", (char *)link->data);
+ dbus_free (link->data);
+ _dbus_list_free_link (link);
+ }
+ dbus_free (dirs);
+ return TRUE;
+}
+
+static dbus_bool_t print_replace_install_prefix(const char *s)
+{
+ const char *s2 = _dbus_replace_install_prefix(s);
+ if (!s2)
+ return FALSE;
+
+ fprintf(stdout, "replaced '%s' by '%s'\n", s, s2);
+ return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+ if (!print_install_root())
+ return -1;
+
+ if (!print_service_dirs())
+ return -2;
+
+ if (!print_replace_install_prefix(DBUS_BINDIR "/dbus-daemon"))
+ return -3;
+
+ if (!print_replace_install_prefix("c:\\Windows\\System32\\testfile"))
+ return -4;
+
+ return 0;
+}
diff --git a/test/marshal.c b/test/marshal.c
index d74e7671..3353ec00 100644
--- a/test/marshal.c
+++ b/test/marshal.c
@@ -31,6 +31,8 @@
#include <dbus/dbus.h>
+#include "test-utils-glib.h"
+
typedef struct {
DBusError e;
} Fixture;
@@ -248,7 +250,7 @@ main (int argc,
char *aligned_le_blob;
char *aligned_be_blob;
- g_test_init (&argc, &argv, NULL);
+ test_init (&argc, &argv);
/* We have to pass in a buffer that's at least "default aligned",
* i.e. on GNU systems to 8 or 16. The linker may have only given
diff --git a/test/monitor.c b/test/monitor.c
new file mode 100644
index 00000000..c099139a
--- /dev/null
+++ b/test/monitor.c
@@ -0,0 +1,1523 @@
+/* Integration tests for monitor-mode D-Bus connections
+ *
+ * Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "test-utils-glib.h"
+
+typedef struct {
+ const char *config_file;
+ const char * const *match_rules;
+ gboolean care_about_our_names;
+} Config;
+
+typedef struct {
+ const Config *config;
+ TestMainContext *ctx;
+ DBusError e;
+ GError *ge;
+
+ gchar *address;
+ GPid daemon_pid;
+
+ DBusConnection *monitor;
+ DBusConnection *sender;
+ DBusConnection *recipient;
+
+ GQueue monitored;
+
+ const char *monitor_name;
+ const char *sender_name;
+ const char *recipient_name;
+
+ DBusConnection *systemd;
+ const char *systemd_name;
+ DBusMessage *systemd_message;
+ DBusConnection *activated;
+ const char *activated_name;
+ DBusMessage *activated_message;
+} Fixture;
+
+static const char * const no_match_rules[] = {
+ NULL
+};
+
+static const char * const wildcard_match_rules[] = {
+ "",
+ NULL,
+ FALSE
+};
+
+static const char * const eavesdrop_match_rules[] = {
+ "eavesdrop=true",
+ NULL,
+ FALSE
+};
+
+static const char * const no_eavesdrop_match_rules[] = {
+ "eavesdrop=false",
+ NULL,
+ FALSE
+};
+
+static const char * const selective_match_rules[] = {
+ "interface='com.example.Interesting'",
+ "interface='com.example.Fun'",
+ NULL,
+ FALSE
+};
+
+static Config forbidding_config = {
+ "valid-config-files/forbidding.conf",
+ NULL,
+ FALSE
+};
+
+static Config wildcard_config = {
+ NULL,
+ wildcard_match_rules,
+ FALSE
+};
+
+static Config selective_config = {
+ NULL,
+ selective_match_rules,
+ FALSE
+};
+
+static Config no_rules_config = {
+ NULL,
+ no_match_rules,
+ FALSE
+};
+
+static Config eavesdrop_config = {
+ NULL,
+ eavesdrop_match_rules,
+ FALSE
+};
+
+static Config no_eavesdrop_config = {
+ NULL,
+ no_eavesdrop_match_rules,
+ FALSE
+};
+
+static Config fake_systemd_config = {
+ "valid-config-files/systemd-activation.conf",
+ NULL,
+ FALSE
+};
+
+static Config side_effects_config = {
+ NULL,
+ NULL,
+ TRUE
+};
+
+static inline const char *
+not_null2 (const char *x,
+ const char *fallback)
+{
+ if (x == NULL)
+ return fallback;
+
+ return x;
+}
+
+static inline const char *
+not_null (const char *x)
+{
+ return not_null2 (x, "(null)");
+}
+
+#define log_message(m) _log_message (m, __FILE__, __LINE__)
+
+G_GNUC_UNUSED
+static void
+_log_message (DBusMessage *m,
+ const char *file,
+ int line)
+{
+ g_test_message ("%s:%d: message type %d (%s)", file, line,
+ dbus_message_get_type (m),
+ dbus_message_type_to_string (dbus_message_get_type (m)));
+ g_test_message ("\tfrom: %s",
+ not_null2 (dbus_message_get_sender (m), "(dbus-daemon)"));
+ g_test_message ("\tto: %s",
+ not_null2 (dbus_message_get_destination (m), "(broadcast)"));
+ g_test_message ("\tpath: %s",
+ not_null (dbus_message_get_path (m)));
+ g_test_message ("\tinterface: %s",
+ not_null (dbus_message_get_interface (m)));
+ g_test_message ("\tmember: %s",
+ not_null (dbus_message_get_member (m)));
+ g_test_message ("\tsignature: %s",
+ not_null (dbus_message_get_signature (m)));
+ g_test_message ("\terror name: %s",
+ not_null (dbus_message_get_error_name (m)));
+
+ if (strcmp ("s", dbus_message_get_signature (m)) == 0)
+ {
+ DBusError e = DBUS_ERROR_INIT;
+ const char *s;
+
+ dbus_message_get_args (m, &e,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ test_assert_no_error (&e);
+ g_test_message ("\tstring payload: %s", s);
+ }
+}
+
+/* these are macros so they get the right line number */
+
+#define assert_hello(m) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_CALL)); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, DBUS_SERVICE_DBUS); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, DBUS_PATH_DBUS); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, DBUS_INTERFACE_DBUS); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, "Hello"); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, ""); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
+} while (0)
+
+#define assert_hello_reply(m) \
+do { \
+ DBusError _e = DBUS_ERROR_INIT; \
+ const char *_s; \
+ \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_RETURN)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, DBUS_SERVICE_DBUS); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
+ \
+ dbus_message_get_args (m, &_e, \
+ DBUS_TYPE_STRING, &_s, \
+ DBUS_TYPE_INVALID); \
+ test_assert_no_error (&_e); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, _s); \
+} while (0)
+
+#define assert_name_acquired(m) \
+do { \
+ DBusError _e = DBUS_ERROR_INIT; \
+ const char *_s; \
+ \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, DBUS_SERVICE_DBUS); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, DBUS_PATH_DBUS); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, DBUS_INTERFACE_DBUS); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, "NameAcquired"); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
+ \
+ dbus_message_get_args (m, &_e, \
+ DBUS_TYPE_STRING, &_s, \
+ DBUS_TYPE_INVALID); \
+ test_assert_no_error (&_e); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, _s); \
+} while (0)
+
+#define assert_method_call(m, sender, \
+ destination, path, iface, method, signature) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_CALL)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, path); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, method); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
+} while (0)
+
+#define assert_signal(m, \
+ sender, path, iface, member, signature, \
+ destination) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, path); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, member); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
+} while (0)
+
+#define assert_method_reply(m, sender, destination, signature) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_METHOD_RETURN)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
+} while (0)
+
+#define assert_error_reply(m, sender, destination, error_name) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_ERROR)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
+ g_assert_cmpstr (dbus_message_get_error_name (m), ==, error_name); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, NULL); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), !=, 0); \
+} while (0)
+
+/* This is called after processing pending replies to our own method
+ * calls, but before anything else.
+ */
+static DBusHandlerResult
+monitor_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+
+ g_assert_cmpstr (dbus_message_get_interface (message), !=,
+ "com.example.Tedious");
+
+ /* we are not interested in the monitor getting NameAcquired or NameLost
+ * for most tests */
+ if (f->config == NULL || !f->config->care_about_our_names)
+ {
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired") ||
+ dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ DBusError e = DBUS_ERROR_INIT;
+ const char *s;
+
+ dbus_message_get_args (message, &e,
+ DBUS_TYPE_STRING, &s,
+ DBUS_TYPE_INVALID);
+ test_assert_no_error (&e);
+
+ if (strcmp (s, f->monitor_name) == 0)
+ {
+ /* ignore */
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ }
+ }
+
+ g_queue_push_tail (&f->monitored, dbus_message_ref (message));
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+recipient_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ g_assert_cmpstr (dbus_message_get_interface (message), !=,
+ "com.example.CannotSend");
+ g_assert_cmpstr (dbus_message_get_interface (message), !=,
+ "com.example.CannotReceive");
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+systemd_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired") ||
+ dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ g_assert (f->systemd_message == NULL);
+ f->systemd_message = dbus_message_ref (message);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+activated_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired") ||
+ dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ g_assert (f->activated_message == NULL);
+ f->activated_message = dbus_message_ref (message);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer context)
+{
+ f->config = context;
+
+ f->ctx = test_main_context_get ();
+
+ f->ge = NULL;
+ dbus_error_init (&f->e);
+
+ f->address = test_get_dbus_daemon (f->config ? f->config->config_file : NULL,
+ TEST_USER_ME, &f->daemon_pid);
+
+ if (f->address == NULL)
+ return;
+
+ f->monitor = test_connect_to_bus (f->ctx, f->address);
+ f->monitor_name = dbus_bus_get_unique_name (f->monitor);
+ f->sender = test_connect_to_bus (f->ctx, f->address);
+ f->sender_name = dbus_bus_get_unique_name (f->sender);
+ f->recipient = test_connect_to_bus (f->ctx, f->address);
+ f->recipient_name = dbus_bus_get_unique_name (f->recipient);
+
+ if (!dbus_connection_add_filter (f->monitor, monitor_filter, f, NULL))
+ g_error ("OOM");
+
+ if (!dbus_connection_add_filter (f->recipient, recipient_filter, f, NULL))
+ g_error ("OOM");
+}
+
+static void
+become_monitor (Fixture *f)
+{
+ DBusMessage *m;
+ DBusPendingCall *pc;
+ dbus_bool_t ok;
+ DBusMessageIter appender, array_appender;
+ const char * const *match_rules;
+ int i;
+ dbus_uint32_t zero = 0;
+
+ dbus_connection_set_route_peer_messages (f->monitor, TRUE);
+
+ if (f->config != NULL && f->config->match_rules != NULL)
+ match_rules = f->config->match_rules;
+ else
+ match_rules = wildcard_match_rules;
+
+ m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor");
+
+ if (m == NULL)
+ g_error ("OOM");
+
+ dbus_message_iter_init_append (m, &appender);
+
+ if (!dbus_message_iter_open_container (&appender, DBUS_TYPE_ARRAY, "s",
+ &array_appender))
+ g_error ("OOM");
+
+ for (i = 0; match_rules[i] != NULL; i++)
+ {
+ if (!dbus_message_iter_append_basic (&array_appender, DBUS_TYPE_STRING,
+ &match_rules[i]))
+ g_error ("OOM");
+ }
+
+ if (!dbus_message_iter_close_container (&appender, &array_appender) ||
+ !dbus_message_iter_append_basic (&appender, DBUS_TYPE_UINT32, &zero))
+ g_error ("OOM");
+
+ if (!dbus_connection_send_with_reply (f->monitor, m, &pc,
+ DBUS_TIMEOUT_USE_DEFAULT) ||
+ pc == NULL)
+ g_error ("OOM");
+
+ dbus_message_unref (m);
+ m = NULL;
+
+ if (dbus_pending_call_get_completed (pc))
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
+ &m, NULL))
+ g_error ("OOM");
+
+ while (m == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ ok = dbus_message_get_args (m, &f->e,
+ DBUS_TYPE_INVALID);
+ test_assert_no_error (&f->e);
+ g_assert (ok);
+
+ dbus_pending_call_unref (pc);
+ dbus_message_unref (m);
+ m = NULL;
+}
+
+/*
+ * Test the side-effects of becoming a monitor.
+ */
+static void
+test_become_monitor (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+ int ret;
+ dbus_bool_t got_unique = FALSE, got_a = FALSE, got_b = FALSE, got_c = FALSE;
+ dbus_bool_t lost_unique = FALSE, lost_a = FALSE, lost_b = FALSE, lost_c = FALSE;
+
+ if (f->address == NULL)
+ return;
+
+ ret = dbus_bus_request_name (f->monitor, "com.example.A",
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+
+ ret = dbus_bus_request_name (f->monitor, "com.example.B",
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+
+ ret = dbus_bus_request_name (f->monitor, "com.example.C",
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+
+ while (!got_unique || !got_a || !got_b || !got_c)
+ {
+ if (g_queue_is_empty (&f->monitored))
+ test_main_context_iterate (f->ctx, TRUE);
+
+ while ((m = g_queue_pop_head (&f->monitored)) != NULL)
+ {
+ if (dbus_message_is_signal (m, DBUS_INTERFACE_DBUS,
+ "NameAcquired"))
+ {
+ const char *name;
+ dbus_bool_t ok = dbus_message_get_args (m, &f->e,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ g_assert_cmpstr (dbus_message_get_path (m), ==,
+ DBUS_PATH_DBUS);
+
+ test_assert_no_error (&f->e);
+ g_assert (ok);
+
+ if (g_str_equal (name, f->monitor_name))
+ {
+ g_assert (!got_unique);
+ got_unique = TRUE;
+ }
+ else if (g_str_equal (name, "com.example.A"))
+ {
+ g_assert (!got_a);
+ got_a = TRUE;
+ }
+ else if (g_str_equal (name, "com.example.B"))
+ {
+ g_assert (!got_b);
+ got_b = TRUE;
+ }
+ else
+ {
+ g_assert_cmpstr (name, ==, "com.example.C");
+ g_assert (!got_c);
+ got_c = TRUE;
+ }
+ }
+ else
+ {
+ g_error ("unexpected message %s.%s",
+ dbus_message_get_interface (m),
+ dbus_message_get_member (m));
+ }
+
+ dbus_message_unref (m);
+ }
+ }
+
+ become_monitor (f);
+
+ while (!lost_unique || !lost_a || !lost_b || !lost_c)
+ {
+ if (g_queue_is_empty (&f->monitored))
+ test_main_context_iterate (f->ctx, TRUE);
+
+ while ((m = g_queue_pop_head (&f->monitored)) != NULL)
+ {
+ if (dbus_message_is_signal (m, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ const char *name;
+ dbus_bool_t ok = dbus_message_get_args (m, &f->e,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ test_assert_no_error (&f->e);
+ g_assert (ok);
+
+ if (g_str_equal (name, f->monitor_name))
+ {
+ g_assert (!lost_unique);
+ lost_unique = TRUE;
+ }
+ else if (g_str_equal (name, "com.example.A"))
+ {
+ g_assert (!lost_a);
+ lost_a = TRUE;
+ }
+ else if (g_str_equal (name, "com.example.B"))
+ {
+ g_assert (!lost_b);
+ lost_b = TRUE;
+ }
+ else
+ {
+ g_assert_cmpstr (name, ==, "com.example.C");
+ g_assert (!lost_c);
+ lost_c = TRUE;
+ }
+ }
+ else
+ {
+ g_error ("unexpected message %s.%s",
+ dbus_message_get_interface (m),
+ dbus_message_get_member (m));
+ }
+
+ dbus_message_unref (m);
+ }
+ }
+
+ /* Calling methods is forbidden; we get disconnected. */
+ dbus_bus_add_match (f->monitor, "", &f->e);
+ g_assert_cmpstr (f->e.name, ==, DBUS_ERROR_NO_REPLY);
+ g_assert (!dbus_connection_get_is_connected (f->monitor));
+
+ while (TRUE)
+ {
+ if (g_queue_is_empty (&f->monitored))
+ test_main_context_iterate (f->ctx, TRUE);
+
+ /* When we iterate all the connection's messages, we see ourselves
+ * losing all our names, then we're disconnected. */
+ while ((m = g_queue_pop_head (&f->monitored)) != NULL)
+ {
+ if (dbus_message_is_signal (m, DBUS_INTERFACE_LOCAL, "Disconnected"))
+ {
+ dbus_message_unref (m);
+ goto disconnected;
+ }
+ else
+ {
+ g_error ("unexpected message %s.%s",
+ dbus_message_get_interface (m),
+ dbus_message_get_member (m));
+ }
+
+ dbus_message_unref (m);
+ }
+ }
+
+disconnected:
+
+ g_assert (lost_a);
+ g_assert (lost_b);
+ g_assert (lost_c);
+}
+
+static void
+test_broadcast (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ dbus_bus_add_match (f->recipient, "type='signal'", &f->e);
+ test_assert_no_error (&f->e);
+
+ become_monitor (f);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal1");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal2");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "BroadcastSignal3");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 3)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.bar",
+ "BroadcastSignal1", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.bar",
+ "BroadcastSignal2", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.bar",
+ "BroadcastSignal3", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_forbidden_broadcast (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ dbus_bus_add_match (f->recipient, "type='signal'", &f->e);
+ test_assert_no_error (&f->e);
+
+ become_monitor (f);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotSend",
+ "BroadcastSignal1");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotReceive",
+ "BroadcastSignal2");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotSend",
+ "BroadcastSignal3");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 6)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.CannotSend",
+ "BroadcastSignal1", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.CannotReceive",
+ "BroadcastSignal2", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo", "com.example.CannotSend",
+ "BroadcastSignal3", "", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_unicast_signal (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 3)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal1", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal2", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal3", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_forbidden (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotSend",
+ "UnicastSignal1");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotReceive",
+ "UnicastSignal2");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.CannotSend",
+ "UnicastSignal3");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 6)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.CannotSend", "UnicastSignal1", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.CannotReceive", "UnicastSignal2", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.CannotSend", "UnicastSignal3", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_method_call (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ /* regression test for
+ * https://bugs.freedesktop.org/show_bug.cgi?id=90952 */
+ m = dbus_message_new_method_call (f->recipient_name, "/foo",
+ DBUS_INTERFACE_PEER, "Ping");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, f->recipient_name, "/foo",
+ DBUS_INTERFACE_PEER, "Ping", "");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_reply (m, f->recipient_name, f->sender_name, "");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+
+ m = dbus_message_new_method_call (f->recipient_name, "/foo", "com.example.bar",
+ "Call1");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, f->recipient_name, "/foo",
+ "com.example.bar", "Call1", "");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, f->recipient_name, f->sender_name,
+ DBUS_ERROR_UNKNOWN_METHOD);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_forbidden_method_call (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ m = dbus_message_new_method_call (f->recipient_name, "/foo",
+ "com.example.CannotSend", "Call1");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, f->recipient_name, "/foo",
+ "com.example.CannotSend", "Call1", "");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+
+ m = dbus_message_new_method_call (f->recipient_name, "/foo",
+ "com.example.CannotReceive", "Call2");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, f->recipient_name, "/foo",
+ "com.example.CannotReceive", "Call2", "");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ DBUS_ERROR_ACCESS_DENIED);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_dbus_daemon (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+ int res;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ res = dbus_bus_request_name (f->sender, "com.example.Sender",
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (res, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+
+ res = dbus_bus_release_name (f->sender, "com.example.Sender", &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (res, ==, DBUS_RELEASE_NAME_REPLY_RELEASED);
+
+ while (g_queue_get_length (&f->monitored) < 8)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "RequestName", "su");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged", "sss", NULL);
+ dbus_message_unref (m);
+
+ /* FIXME: should we get this? */
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameAcquired", "s", f->sender_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_reply (m, DBUS_SERVICE_DBUS, f->sender_name, "u");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, f->sender_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "ReleaseName", "s");
+ dbus_message_unref (m);
+
+ /* FIXME: should we get this? */
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameLost", "s", f->sender_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged", "sss", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_reply (m, DBUS_SERVICE_DBUS, f->sender_name, "u");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+test_selective (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ /* Match rules added before becoming a monitor should be cleared:
+ * if they weren't, this test would get Interesting twice, then Tedious,
+ * and only see Fun after that. */
+ dbus_bus_add_match (f->monitor,
+ "eavesdrop='true',interface='com.example.Interesting'", &f->e);
+ test_assert_no_error (&f->e);
+ dbus_bus_add_match (f->monitor,
+ "eavesdrop='true',interface='com.example.Tedious'", &f->e);
+ test_assert_no_error (&f->e);
+
+ become_monitor (f);
+
+ m = dbus_message_new_signal ("/foo", "com.example.Interesting",
+ "UnicastSignal1");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.Tedious",
+ "UnicastSignal2");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ m = dbus_message_new_signal ("/foo", "com.example.Fun",
+ "UnicastSignal3");
+ if (!dbus_message_set_destination (m, f->recipient_name))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ /* We get the interesting signal and the fun signal, but not the tedious
+ * signal. */
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.Interesting", "UnicastSignal1", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.Fun", "UnicastSignal3", "", f->recipient_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ g_assert (m == NULL);
+}
+
+static void
+expect_new_connection (Fixture *f)
+{
+ DBusMessage *m;
+
+ while (g_queue_get_length (&f->monitored) < 4)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_hello (m);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_hello_reply (m);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged", "sss", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_name_acquired (m);
+ dbus_message_unref (m);
+}
+
+static void
+take_well_known_name (Fixture *f,
+ DBusConnection *connection,
+ const char *name)
+{
+ int ret;
+
+ ret = dbus_bus_request_name (connection, name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+}
+
+static void
+expect_take_well_known_name (Fixture *f,
+ DBusConnection *connection,
+ const char *name)
+{
+ DBusMessage *m;
+ const char *connection_name = dbus_bus_get_unique_name (connection);
+
+ while (g_queue_get_length (&f->monitored) < 4)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_call (m, connection_name, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS, "RequestName", "su");
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged", "sss", NULL);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS,
+ "NameAcquired", "s", connection_name);
+ dbus_message_unref (m);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_method_reply (m, DBUS_SERVICE_DBUS, connection_name, "u");
+ dbus_message_unref (m);
+}
+
+static void
+test_activation (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ become_monitor (f);
+
+ /* The sender sends a message to an activatable service. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable1"))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ /* We observe the activation request, and the message that caused it,
+ * before systemd has even joined the bus. */
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal1", "",
+ "com.example.SystemdActivatable1");
+ dbus_message_unref (m);
+
+ /* The fake systemd connects to the bus. */
+ f->systemd = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->systemd, systemd_filter, f, NULL))
+ g_error ("OOM");
+ f->systemd_name = dbus_bus_get_unique_name (f->systemd);
+
+ expect_new_connection (f);
+ take_well_known_name (f, f->systemd, "org.freedesktop.systemd1");
+ expect_take_well_known_name (f, f->systemd, "org.freedesktop.systemd1");
+
+ /* It gets its activation request. */
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* systemd starts the activatable service. */
+ f->activated = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->activated, activated_filter,
+ f, NULL))
+ g_error ("OOM");
+ f->activated_name = dbus_bus_get_unique_name (f->activated);
+
+ expect_new_connection (f);
+ take_well_known_name (f, f->activated, "com.example.SystemdActivatable1");
+ expect_take_well_known_name (f, f->activated,
+ "com.example.SystemdActivatable1");
+
+ /* The message is delivered to the activatable service. */
+ while (f->activated_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->activated_message;
+ f->activated_message = NULL;
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal1", "",
+ "com.example.SystemdActivatable1");
+ dbus_message_unref (m);
+
+ /* The sender sends a message to a different activatable service. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable2"))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ /* This time systemd is already ready for it. */
+ while (g_queue_get_length (&f->monitored) < 2 ||
+ f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* The monitor sees the activation request and the signal that
+ * prompted it.*/
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal2", "",
+ "com.example.SystemdActivatable2");
+ dbus_message_unref (m);
+
+ /* The activatable service takes its name. Here I'm faking it by using
+ * an existing connection. */
+ take_well_known_name (f, f->activated, "com.example.SystemdActivatable2");
+
+ /* The message is delivered to the activatable service.
+ * Implementation detail: the monitor sees this happen before it even
+ * sees that the name request happened, which is pretty odd. */
+ while (f->activated_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->activated_message;
+ f->activated_message = NULL;
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal2", "",
+ "com.example.SystemdActivatable2");
+ dbus_message_unref (m);
+
+ expect_take_well_known_name (f, f->activated,
+ "com.example.SystemdActivatable2");
+
+ /* A third activation. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable3"))
+ g_error ("OOM");
+ dbus_connection_send (f->sender, m, NULL);
+ dbus_message_unref (m);
+
+ /* Once again, we see the activation request and the reason. */
+ while (g_queue_get_length (&f->monitored) < 2)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+ m = g_queue_pop_head (&f->monitored);
+ assert_signal (m, f->sender_name, "/foo",
+ "com.example.bar", "UnicastSignal3", "",
+ "com.example.SystemdActivatable3");
+ dbus_message_unref (m);
+
+ /* systemd gets the request too. */
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* This time activation fails */
+ m = dbus_message_new_signal ("/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Activator", "ActivationFailure");
+
+ do
+ {
+ const char *unit = "dbus-com.example.SystemdActivatable3.service";
+ const char *error_name = "com.example.Nope";
+ const char *error_message = "Computer says no";
+
+ if (!dbus_message_append_args (m,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_STRING, &error_name,
+ DBUS_TYPE_STRING, &error_message,
+ DBUS_TYPE_INVALID))
+ g_error ("OOM");
+ }
+ while (0);
+
+ if (!dbus_message_set_destination (m, "org.freedesktop.DBus"))
+ g_error ("OOM");
+ dbus_connection_send (f->systemd, m, NULL);
+ dbus_message_unref (m);
+
+ /* The monitor sees activation fail */
+
+ /* Once again, we see the activation request and the reason. */
+ while (g_queue_get_length (&f->monitored) < 1)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = g_queue_pop_head (&f->monitored);
+ assert_error_reply (m, DBUS_SERVICE_DBUS, f->sender_name,
+ "com.example.Nope");
+ dbus_message_unref (m);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ dbus_error_free (&f->e);
+ g_clear_error (&f->ge);
+
+ if (f->monitor != NULL)
+ {
+ dbus_connection_remove_filter (f->monitor, monitor_filter, f);
+ dbus_connection_close (f->monitor);
+ dbus_connection_unref (f->monitor);
+ f->monitor = NULL;
+ }
+
+ if (f->sender != NULL)
+ {
+ dbus_connection_close (f->sender);
+ dbus_connection_unref (f->sender);
+ f->sender = NULL;
+ }
+
+ if (f->recipient != NULL)
+ {
+ dbus_connection_remove_filter (f->recipient, recipient_filter, f);
+ dbus_connection_close (f->recipient);
+ dbus_connection_unref (f->recipient);
+ f->recipient = NULL;
+ }
+
+ if (f->systemd != NULL)
+ {
+ dbus_connection_remove_filter (f->systemd, systemd_filter, f);
+ dbus_connection_close (f->systemd);
+ dbus_connection_unref (f->systemd);
+ f->systemd = NULL;
+ }
+
+ if (f->activated != NULL)
+ {
+ dbus_connection_remove_filter (f->activated, activated_filter, f);
+ dbus_connection_close (f->activated);
+ dbus_connection_unref (f->activated);
+ f->activated = NULL;
+ }
+
+ test_kill_pid (f->daemon_pid);
+ g_spawn_close_pid (f->daemon_pid);
+
+ test_main_context_unref (f->ctx);
+
+ g_queue_foreach (&f->monitored, (GFunc) dbus_message_unref, NULL);
+ g_queue_clear (&f->monitored);
+
+ g_free (f->address);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ test_init (&argc, &argv);
+
+ g_test_add ("/monitor/become", Fixture, &side_effects_config,
+ setup, test_become_monitor, teardown);
+ g_test_add ("/monitor/broadcast", Fixture, NULL,
+ setup, test_broadcast, teardown);
+ g_test_add ("/monitor/forbidden-broadcast", Fixture, &forbidding_config,
+ setup, test_forbidden_broadcast, teardown);
+ g_test_add ("/monitor/unicast-signal", Fixture, NULL,
+ setup, test_unicast_signal, teardown);
+ g_test_add ("/monitor/forbidden", Fixture, &forbidding_config,
+ setup, test_forbidden, teardown);
+ g_test_add ("/monitor/method-call", Fixture, NULL,
+ setup, test_method_call, teardown);
+ g_test_add ("/monitor/forbidden-method", Fixture, &forbidding_config,
+ setup, test_forbidden_method_call, teardown);
+ g_test_add ("/monitor/dbus-daemon", Fixture, NULL,
+ setup, test_dbus_daemon, teardown);
+ g_test_add ("/monitor/selective", Fixture, &selective_config,
+ setup, test_selective, teardown);
+ g_test_add ("/monitor/wildcard", Fixture, &wildcard_config,
+ setup, test_unicast_signal, teardown);
+ g_test_add ("/monitor/no-rule", Fixture, &no_rules_config,
+ setup, test_unicast_signal, teardown);
+ g_test_add ("/monitor/eavesdrop", Fixture, &eavesdrop_config,
+ setup, test_unicast_signal, teardown);
+ g_test_add ("/monitor/no-eavesdrop", Fixture, &no_eavesdrop_config,
+ setup, test_unicast_signal, teardown);
+ g_test_add ("/monitor/activation", Fixture, &fake_systemd_config,
+ setup, test_activation, teardown);
+
+ return g_test_run ();
+}
diff --git a/test/name-test/Makefile.am b/test/name-test/Makefile.am
index 8ed1e160..0922558f 100644
--- a/test/name-test/Makefile.am
+++ b/test/name-test/Makefile.am
@@ -1,25 +1,28 @@
-# Everything in this directory is statically-linked to libdbus-internal
AM_CPPFLAGS = \
-I$(top_srcdir) \
+ $(DBUS_STATIC_BUILD_CPPFLAGS) \
-DDBUS_COMPILATION \
- -DDBUS_STATIC_BUILD \
- -DDBUS_TEST_USE_INTERNAL \
$(NULL)
# if assertions are enabled, improve backtraces
AM_LDFLAGS = @R_DYNAMIC_LDFLAG@
+TEST_EXTENSIONS = .sh
+
+SH_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
+SH_LOG_COMPILER = $(SHELL)
+
## note that TESTS has special meaning (stuff to use in make check)
## so if adding tests not to be run in make check, don't add them to
## TESTS
if DBUS_ENABLE_EMBEDDED_TESTS
-TESTS_ENVIRONMENT = \
- DBUS_TOP_BUILDDIR=@abs_top_builddir@ \
- DBUS_TOP_SRCDIR=@abs_top_srcdir@ \
- PYTHON=@PYTHON@ \
- DBUS_TEST_DATA=@abs_top_builddir@/test/data \
- DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT) \
- XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR \
+AM_TESTS_ENVIRONMENT = \
+ export DBUS_TOP_BUILDDIR=@abs_top_builddir@; \
+ export DBUS_TOP_SRCDIR=@abs_top_srcdir@; \
+ export PYTHON=@PYTHON@; \
+ export DBUS_TEST_DATA=@abs_top_builddir@/test/data; \
+ export DBUS_TEST_DAEMON=@abs_top_builddir@/bus/dbus-daemon$(EXEEXT); \
+ export XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR; \
$(NULL)
TESTS=run-test.sh run-test-systemserver.sh
@@ -35,14 +38,14 @@ if DBUS_ENABLE_EMBEDDED_TESTS
## build even when not doing "make check"
noinst_PROGRAMS=test-pending-call-dispatch test-pending-call-timeout test-threads-init test-ids test-shutdown test-privserver test-privserver-client test-autolaunch
-test_pending_call_dispatch_LDADD=$(top_builddir)/dbus/libdbus-internal.la
-test_pending_call_timeout_LDADD=$(top_builddir)/dbus/libdbus-internal.la
-test_threads_init_LDADD=$(top_builddir)/dbus/libdbus-internal.la
-test_ids_LDADD=$(top_builddir)/dbus/libdbus-internal.la
+test_pending_call_dispatch_LDADD=$(top_builddir)/dbus/libdbus-1.la
+test_pending_call_timeout_LDADD=$(top_builddir)/dbus/libdbus-1.la
+test_threads_init_LDADD=$(top_builddir)/dbus/libdbus-1.la
+test_ids_LDADD=$(top_builddir)/dbus/libdbus-1.la
-test_shutdown_LDADD=../libdbus-testutils-internal.la
-test_privserver_LDADD=../libdbus-testutils-internal.la
-test_privserver_client_LDADD=../libdbus-testutils-internal.la
-test_autolaunch_LDADD=../libdbus-testutils-internal.la
+test_shutdown_LDADD=../libdbus-testutils.la
+test_privserver_LDADD=../libdbus-testutils.la
+test_privserver_client_LDADD=../libdbus-testutils.la
+test_autolaunch_LDADD=../libdbus-testutils.la
endif
diff --git a/test/name-test/run-test-systemserver.sh b/test/name-test/run-test-systemserver.sh
index 90c03723..9926cad6 100755
--- a/test/name-test/run-test-systemserver.sh
+++ b/test/name-test/run-test-systemserver.sh
@@ -1,14 +1,4 @@
#! /bin/sh
-die()
-{
- if ! test -z "$DBUS_SESSION_BUS_PID" ; then
- echo "killing message bus "$DBUS_SESSION_BUS_PID >&2
- kill -9 $DBUS_SESSION_BUS_PID
- fi
- echo $SCRIPTNAME: $* >&2
-
- exit 1
-}
SCRIPTNAME=$0
MODE=$1
@@ -27,7 +17,7 @@ if test -z "$DBUS_TEST_NAME_IN_SYS_RUN_TEST"; then
fi
if test -n "$DBUS_TEST_MONITOR"; then
- dbus-monitor --session &
+ dbus-monitor --session >&2 &
fi
XDG_RUNTIME_DIR="$DBUS_TOP_BUILDDIR"/test/XDG_RUNTIME_DIR
@@ -35,22 +25,65 @@ test -d "$XDG_RUNTIME_DIR" || mkdir "$XDG_RUNTIME_DIR"
chmod 0700 "$XDG_RUNTIME_DIR"
export XDG_RUNTIME_DIR
-echo "running test-expected-echo-fail"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/tools/dbus-send --print-reply --dest=org.freedesktop.DBus.TestSuiteEchoService /org/freedesktop/TestSuite org.freedesktop.TestSuite.Echo string:hi >echo-error-output.tmp 2>&1
-if ! grep -q 'DBus.Error' echo-error-output.tmp; then
- echo "Didn't get expected failure; output was:"
- echo "====="
- cat echo-error-output.tmp
- echo "====="
- exit 1
-fi
+# Translate a command and exit status into TAP syntax.
+# Usage: interpret_result $? description-of-test
+# Uses global variable $test_num.
+interpret_result () {
+ e="$1"
+ shift
+ case "$e" in
+ (0)
+ echo "ok $test_num $*"
+ ;;
+ (77)
+ echo "ok $test_num # SKIP $*"
+ ;;
+ (*)
+ echo "not ok $test_num $*"
+ ;;
+ esac
+ test_num=$(( $test_num + 1 ))
+}
-echo "running test echo signal"
-if test "x$PYTHON" = "x:"; then
- echo "Skipped test-echo-signal: Python interpreter not found"
-elif ! $PYTHON $DBUS_TOP_SRCDIR/test/name-test/test-wait-for-echo.py; then
- echo "Failed test-wait-for-echo"
- exit 1
-fi
+dbus_send_test () {
+ t="$1"
+ expected_exit="$2"
+ phrase="$3"
+ shift 3
+ e=0
+ echo "# running test $t"
+ "${DBUS_TOP_BUILDDIR}/libtool" --mode=execute $DEBUG "$DBUS_TOP_BUILDDIR/tools/dbus-send" "$@" > output.tmp 2>&1 || e=$?
+ if [ $e != $expected_exit ]; then
+ sed -e 's/^/# /' < output.tmp
+ interpret_result "1" "$t" "$@" "(expected exit status $expected_exit, got $e)"
+ return
+ fi
+ echo "# parsing results of test $t"
+ if ! grep -q "$phrase" output.tmp; then
+ sed -e 's/^/# /' < output.tmp
+ interpret_result "1" "$t" "$@" "(Did not see \"$phrase\" in output)"
+ return
+ fi
+ interpret_result "0" "$t" "$@" "(Saw \"$phrase\" in output as expected)"
+ rm -f output.tmp
+}
+
+py_test () {
+ t="$1"
+ shift
+ if test "x$PYTHON" = "x:"; then
+ interpret_result 77 "$t" "(Python interpreter not found)"
+ else
+ e=0
+ echo "# running test $t"
+ $PYTHON "$DBUS_TOP_SRCDIR/test/name-test/$t" "$@" >&2 || e=$?
+ interpret_result "$e" "$t" "$@"
+ fi
+}
+
+test_num=1
+# TAP syntax: we plan to run 2 tests
+echo "1..2"
-exit 0
+dbus_send_test test-expected-echo-fail 1 DBus.Error --print-reply --dest=org.freedesktop.DBus.TestSuiteEchoService /org/freedesktop/TestSuite org.freedesktop.TestSuite.Echo string:hi
+py_test test-wait-for-echo.py
diff --git a/test/name-test/run-test.sh b/test/name-test/run-test.sh
index 84379c4a..1e257a16 100755
--- a/test/name-test/run-test.sh
+++ b/test/name-test/run-test.sh
@@ -1,17 +1,5 @@
#! /bin/sh
-die()
-{
- if ! test -z "$DBUS_SESSION_BUS_PID" ; then
- echo "killing message bus "$DBUS_SESSION_BUS_PID >&2
- kill -9 $DBUS_SESSION_BUS_PID
- fi
- echo $SCRIPTNAME: $* >&2
-
- exit 1
-}
-
-
SCRIPTNAME=$0
MODE=$1
@@ -27,7 +15,7 @@ if test -z "$DBUS_TEST_NAME_IN_RUN_TEST"; then
fi
if test -n "$DBUS_TEST_MONITOR"; then
- dbus-monitor --session &
+ dbus-monitor --session >&2 &
fi
XDG_RUNTIME_DIR="$DBUS_TOP_BUILDDIR"/test/XDG_RUNTIME_DIR
@@ -35,31 +23,60 @@ test -d "$XDG_RUNTIME_DIR" || mkdir "$XDG_RUNTIME_DIR"
chmod 0700 "$XDG_RUNTIME_DIR"
export XDG_RUNTIME_DIR
-echo "running test-ids"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-ids || die "test-ids failed"
-
-echo "running test-pending-call-dispatch"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-pending-call-dispatch || die "test-pending-call-dispatch failed"
-
-echo "running test-pending-call-timeout"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-pending-call-timeout || die "test-pending-call-timeout failed"
-
-echo "running test-threads-init"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-threads-init || die "test-threads-init failed"
-
-echo "running test-privserver-client"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-privserver-client || die "test-privserver-client failed"
+# Translate a command and exit status into TAP syntax.
+# Usage: interpret_result $? description-of-test
+# Uses global variable $test_num.
+interpret_result () {
+ e="$1"
+ shift
+ case "$e" in
+ (0)
+ echo "ok $test_num $*"
+ ;;
+ (77)
+ echo "ok $test_num # SKIP $*"
+ ;;
+ (*)
+ echo "not ok $test_num $*"
+ ;;
+ esac
+ test_num=$(( $test_num + 1 ))
+}
-echo "running test-shutdown"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-shutdown || die "test-shutdown failed"
+c_test () {
+ t="$1"
+ shift
+ e=0
+ echo "# running test $t"
+ if ! "${DBUS_TOP_BUILDDIR}/libtool" --mode=execute $DEBUG "$DBUS_TOP_BUILDDIR/test/name-test/$t" "$@" >&2; then
+ e=$?
+ echo "# exit status $e"
+ fi
+ interpret_result "$e" "$t" "$@"
+}
-echo "running test activation forking"
-if test "x$PYTHON" = "x:"; then
- echo "Skipped test-activation-forking: Python interpreter not found"
-elif ! $PYTHON $DBUS_TOP_SRCDIR/test/name-test/test-activation-forking.py; then
- echo "Failed test-activation-forking"
- exit 1
-fi
+py_test () {
+ t="$1"
+ shift
+ if test "x$PYTHON" = "x:"; then
+ interpret_result 77 "$t" "(Python interpreter not found)"
+ else
+ e=0
+ echo "# running test $t"
+ $PYTHON "$DBUS_TOP_SRCDIR/test/name-test/$t" "$@" >&2 || e=$?
+ interpret_result "$e" "$t" "$@"
+ fi
+}
-echo "running test-autolaunch"
-${DBUS_TOP_BUILDDIR}/libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-autolaunch || die "test-autolaunch failed"
+test_num=1
+# TAP test plan: we will run 8 tests
+echo "1..8"
+
+c_test test-ids
+c_test test-pending-call-dispatch
+c_test test-pending-call-timeout
+c_test test-threads-init
+c_test test-privserver-client
+c_test test-shutdown
+py_test test-activation-forking.py
+c_test test-autolaunch
diff --git a/test/name-test/test-activation-forking.py b/test/name-test/test-activation-forking.py
index 0d820754..f98537eb 100644
--- a/test/name-test/test-activation-forking.py
+++ b/test/name-test/test-activation-forking.py
@@ -3,7 +3,7 @@
import os,sys
try:
- import gobject
+ from gi.repository import GObject
import dbus
import dbus.mainloop.glib
except:
@@ -11,7 +11,7 @@ except:
sys.exit(0)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-loop = gobject.MainLoop()
+loop = GObject.MainLoop()
exitcode = 0
@@ -54,7 +54,7 @@ def check_counter():
if counter == 0:
print "Failed to get NameOwnerChanged for TestSuiteForkingEchoService"
sys.exit(1)
-gobject.timeout_add(15000, check_counter)
+GObject.timeout_add(15000, check_counter)
loop.run()
sys.exit(0)
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/test/name-test/test-wait-for-echo.py b/test/name-test/test-wait-for-echo.py
index bd09e459..49ecbb46 100755
--- a/test/name-test/test-wait-for-echo.py
+++ b/test/name-test/test-wait-for-echo.py
@@ -3,15 +3,15 @@
import os,sys
try:
- import gobject
import dbus
import dbus.mainloop.glib
+ from gi.repository import GObject
except:
print "Failed import, aborting test"
sys.exit(0)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
-loop = gobject.MainLoop()
+loop = GObject.MainLoop()
exitcode = 0
@@ -21,7 +21,7 @@ def handle_noreceipt():
exitcode = 1
loop.quit()
-gobject.timeout_add(7000, handle_noreceipt)
+GObject.timeout_add(7000, handle_noreceipt)
bus = dbus.SessionBus()
diff --git a/test/relay.c b/test/relay.c
index 984fde10..e275c167 100644
--- a/test/relay.c
+++ b/test/relay.c
@@ -30,7 +30,7 @@
#include <dbus/dbus.h>
-#include "test-utils.h"
+#include "test-utils-glib.h"
/* This is basically a miniature dbus-daemon. We relay messages from the client
* on the left to the client on the right.
@@ -155,7 +155,7 @@ test_connect (Fixture *f,
while (f->left_server_conn == NULL)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -168,7 +168,7 @@ test_connect (Fixture *f,
while (f->right_server_conn == NULL)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -210,7 +210,7 @@ test_relay (Fixture *f,
while (g_queue_get_length (&f->messages) < 2)
{
- g_print (".");
+ test_progress ('.');
test_main_context_iterate (f->ctx, TRUE);
}
@@ -317,8 +317,7 @@ int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
- g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+ test_init (&argc, &argv);
g_test_add ("/connect", Fixture, NULL, setup,
test_connect, teardown);
diff --git a/test/sd-activation.c b/test/sd-activation.c
new file mode 100644
index 00000000..2a7366df
--- /dev/null
+++ b/test/sd-activation.c
@@ -0,0 +1,349 @@
+/* Unit tests for systemd activation.
+ *
+ * Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "test-utils-glib.h"
+
+typedef struct {
+ TestMainContext *ctx;
+ DBusError e;
+ GError *ge;
+
+ gchar *address;
+ GPid daemon_pid;
+
+ DBusConnection *caller;
+ const char *caller_name;
+ DBusConnection *systemd;
+ const char *systemd_name;
+ DBusMessage *systemd_message;
+ DBusConnection *activated;
+ const char *activated_name;
+ DBusMessage *activated_message;
+} Fixture;
+
+/* this is a macro so it gets the right line number */
+#define assert_signal(m, \
+ sender, path, iface, member, signature, \
+ destination) \
+do { \
+ g_assert_cmpstr (dbus_message_type_to_string (dbus_message_get_type (m)), \
+ ==, dbus_message_type_to_string (DBUS_MESSAGE_TYPE_SIGNAL)); \
+ g_assert_cmpstr (dbus_message_get_sender (m), ==, sender); \
+ g_assert_cmpstr (dbus_message_get_destination (m), ==, destination); \
+ g_assert_cmpstr (dbus_message_get_path (m), ==, path); \
+ g_assert_cmpstr (dbus_message_get_interface (m), ==, iface); \
+ g_assert_cmpstr (dbus_message_get_member (m), ==, member); \
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, signature); \
+ g_assert_cmpint (dbus_message_get_serial (m), !=, 0); \
+ g_assert_cmpint (dbus_message_get_reply_serial (m), ==, 0); \
+} while (0)
+
+static DBusHandlerResult
+systemd_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired") ||
+ dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ g_assert (f->systemd_message == NULL);
+ f->systemd_message = dbus_message_ref (message);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+activated_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ Fixture *f = user_data;
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameAcquired") ||
+ dbus_message_is_signal (message, DBUS_INTERFACE_DBUS,
+ "NameLost"))
+ {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ g_assert (f->activated_message == NULL);
+ f->activated_message = dbus_message_ref (message);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+setup (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ f->ctx = test_main_context_get ();
+
+ f->ge = NULL;
+ dbus_error_init (&f->e);
+
+ f->address = test_get_dbus_daemon (
+ "valid-config-files/systemd-activation.conf",
+ TEST_USER_ME, &f->daemon_pid);
+
+ if (f->address == NULL)
+ return;
+
+ f->caller = test_connect_to_bus (f->ctx, f->address);
+ f->caller_name = dbus_bus_get_unique_name (f->caller);
+}
+
+static void
+take_well_known_name (Fixture *f,
+ DBusConnection *connection,
+ const char *name)
+{
+ int ret;
+
+ ret = dbus_bus_request_name (connection, name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &f->e);
+ test_assert_no_error (&f->e);
+ g_assert_cmpint (ret, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER);
+}
+
+static void
+test_activation (Fixture *f,
+ gconstpointer context)
+{
+ DBusMessage *m;
+
+ if (f->address == NULL)
+ return;
+
+ /* The sender sends a message to an activatable service. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal1");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable1"))
+ g_error ("OOM");
+ dbus_connection_send (f->caller, m, NULL);
+ dbus_message_unref (m);
+
+ /* The fake systemd connects to the bus. */
+ f->systemd = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->systemd, systemd_filter, f, NULL))
+ g_error ("OOM");
+ f->systemd_name = dbus_bus_get_unique_name (f->systemd);
+ take_well_known_name (f, f->systemd, "org.freedesktop.systemd1");
+
+ /* It gets its activation request. */
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* systemd starts the activatable service. */
+ f->activated = test_connect_to_bus (f->ctx, f->address);
+ if (!dbus_connection_add_filter (f->activated, activated_filter,
+ f, NULL))
+ g_error ("OOM");
+ f->activated_name = dbus_bus_get_unique_name (f->activated);
+ take_well_known_name (f, f->activated, "com.example.SystemdActivatable1");
+
+ /* The message is delivered to the activatable service. */
+ while (f->activated_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->activated_message;
+ f->activated_message = NULL;
+ assert_signal (m, f->caller_name, "/foo",
+ "com.example.bar", "UnicastSignal1", "",
+ "com.example.SystemdActivatable1");
+ dbus_message_unref (m);
+
+ /* The sender sends a message to a different activatable service. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal2");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable2"))
+ g_error ("OOM");
+ dbus_connection_send (f->caller, m, NULL);
+ dbus_message_unref (m);
+
+ /* This time systemd is already ready for it. */
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* A malicious process tries to disrupt the activation.
+ * In a more realistic scenario this would be another parallel
+ * connection. */
+ m = dbus_message_new_signal ("/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Activator", "ActivationFailure");
+ if (!dbus_message_set_destination (m, "org.freedesktop.DBus"))
+ g_error ("OOM");
+
+ do
+ {
+ const char *unit = "dbus-com.example.SystemdActivatable2.service";
+ const char *error_name = "com.example.Malice";
+ const char *error_message = "I'm on yr bus, making yr activations fail";
+
+ if (!dbus_message_append_args (m,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_STRING, &error_name,
+ DBUS_TYPE_STRING, &error_message,
+ DBUS_TYPE_INVALID))
+ g_error ("OOM");
+ }
+ while (0);
+
+ dbus_connection_send (f->caller, m, NULL);
+ dbus_message_unref (m);
+
+ /* This is just to make sure that the malicious message has arrived and
+ * been processed by the dbus-daemon, i.e. @caller won the race
+ * with @activated. */
+ take_well_known_name (f, f->caller, "com.example.Sync");
+
+ /* The activatable service takes its name. Here I'm faking it by using
+ * an existing connection; in real life it would be yet another
+ * connection. */
+ take_well_known_name (f, f->activated, "com.example.SystemdActivatable2");
+
+ /* The message is delivered to the activatable service. */
+ while (f->activated_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->activated_message;
+ f->activated_message = NULL;
+ assert_signal (m, f->caller_name, "/foo",
+ "com.example.bar", "UnicastSignal2", "",
+ "com.example.SystemdActivatable2");
+ dbus_message_unref (m);
+
+ /* A third activation. */
+ m = dbus_message_new_signal ("/foo", "com.example.bar", "UnicastSignal3");
+ if (!dbus_message_set_destination (m, "com.example.SystemdActivatable3"))
+ g_error ("OOM");
+ dbus_connection_send (f->caller, m, NULL);
+ dbus_message_unref (m);
+
+ while (f->systemd_message == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ m = f->systemd_message;
+ f->systemd_message = NULL;
+ assert_signal (m, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
+ "org.freedesktop.systemd1.Activator", "ActivationRequest", "s",
+ "org.freedesktop.systemd1");
+ dbus_message_unref (m);
+
+ /* This time activation fails */
+ m = dbus_message_new_signal ("/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Activator", "ActivationFailure");
+
+ do
+ {
+ const char *unit = "dbus-com.example.SystemdActivatable3.service";
+ const char *error_name = "com.example.Nope";
+ const char *error_message = "Computer says no";
+
+ if (!dbus_message_append_args (m,
+ DBUS_TYPE_STRING, &unit,
+ DBUS_TYPE_STRING, &error_name,
+ DBUS_TYPE_STRING, &error_message,
+ DBUS_TYPE_INVALID))
+ g_error ("OOM");
+ }
+ while (0);
+
+ if (!dbus_message_set_destination (m, "org.freedesktop.DBus"))
+ g_error ("OOM");
+ dbus_connection_send (f->systemd, m, NULL);
+ dbus_message_unref (m);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ dbus_error_free (&f->e);
+ g_clear_error (&f->ge);
+
+ if (f->caller != NULL)
+ {
+ dbus_connection_close (f->caller);
+ dbus_connection_unref (f->caller);
+ f->caller = NULL;
+ }
+
+ if (f->systemd != NULL)
+ {
+ dbus_connection_remove_filter (f->systemd, systemd_filter, f);
+ dbus_connection_close (f->systemd);
+ dbus_connection_unref (f->systemd);
+ f->systemd = NULL;
+ }
+
+ if (f->activated != NULL)
+ {
+ dbus_connection_remove_filter (f->activated, activated_filter, f);
+ dbus_connection_close (f->activated);
+ dbus_connection_unref (f->activated);
+ f->activated = NULL;
+ }
+
+ test_kill_pid (f->daemon_pid);
+ g_spawn_close_pid (f->daemon_pid);
+ test_main_context_unref (f->ctx);
+ g_free (f->address);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ test_init (&argc, &argv);
+
+ g_test_add ("/sd-activation", Fixture, NULL,
+ setup, test_activation, teardown);
+
+ return g_test_run ();
+}
diff --git a/test/shell-test.c b/test/shell-test.c
index d1dc5b5b..61280d68 100644
--- a/test/shell-test.c
+++ b/test/shell-test.c
@@ -9,8 +9,13 @@
#include <dbus/dbus-string.h>
#include <dbus/dbus-sysdeps.h>
+static int test_num = 0;
+static int num_failed = 0;
+
static dbus_bool_t
-test_command_line (const char *arg1, ...)
+test_command_line_internal (dbus_bool_t should_work,
+ const char *arg1,
+ va_list var_args)
{
int i, original_argc, shell_argc;
char **shell_argv;
@@ -18,10 +23,8 @@ test_command_line (const char *arg1, ...)
char *command_line, *tmp;
DBusString str;
DBusList *list = NULL, *node;
- va_list var_args;
DBusError error;
- va_start (var_args, arg1);
_dbus_list_append (&list, (char *)arg1);
do
{
@@ -30,7 +33,6 @@ test_command_line (const char *arg1, ...)
break;
_dbus_list_append (&list, tmp);
} while (tmp);
- va_end (var_args);
original_argc = _dbus_list_get_length (&list);
original_argv = dbus_new (char *, original_argc);
@@ -46,23 +48,28 @@ test_command_line (const char *arg1, ...)
_dbus_list_clear (&list);
command_line = _dbus_string_get_data (&str);
- printf ("\n\nTesting command line '%s'\n", command_line);
+ printf ("# Testing command line '%s'\n", command_line);
dbus_error_init (&error);
if (!_dbus_shell_parse_argv (command_line, &shell_argc, &shell_argv, &error))
{
- fprintf (stderr, "Error parsing command line: %s\n", error.message ? error.message : "");
- return FALSE;
+ printf ("# Error%s parsing command line: %s\n",
+ should_work ? "" : " (as expected)",
+ error.message ? error.message : "");
+ dbus_free (original_argv);
+ return !should_work;
}
else
{
if (shell_argc != original_argc)
{
- printf ("Number of arguments returned (%d) don't match original (%d)\n",
+ printf ("# Number of arguments returned (%d) don't match original (%d)\n",
shell_argc, original_argc);
+ dbus_free (original_argv);
+ dbus_free_string_array (shell_argv);
return FALSE;
}
- printf ("Number of arguments: %d\n", shell_argc);
+ printf ("# Number of arguments: %d\n", shell_argc);
for (i = 0; i < shell_argc; i++)
{
char *unquoted;
@@ -74,6 +81,8 @@ test_command_line (const char *arg1, ...)
printf ("Position %d, returned argument (%s) does not match original (%s)\n",
i, shell_argv[i], unquoted);
dbus_free (unquoted);
+ dbus_free (original_argv);
+ dbus_free_string_array (shell_argv);
return FALSE;
}
dbus_free (unquoted);
@@ -83,27 +92,79 @@ test_command_line (const char *arg1, ...)
dbus_free_string_array (shell_argv);
}
-
+
_dbus_string_free (&str);
-
+ dbus_free (original_argv);
+
+ if (!should_work)
+ {
+ printf ("# Expected an error\n");
+ return FALSE;
+ }
+
return TRUE;
}
+static void
+test_command_line (const char *arg1, ...)
+{
+ va_list var_args;
+
+ va_start (var_args, arg1);
+
+ if (test_command_line_internal (TRUE, arg1, var_args))
+ {
+ printf ("ok %d\n", ++test_num);
+ }
+ else
+ {
+ printf ("not ok %d\n", ++test_num);
+ num_failed++;
+ }
+
+ va_end (var_args);
+}
+
+static void
+test_command_line_fails (const char *arg1, ...)
+{
+ va_list var_args;
+
+ va_start (var_args, arg1);
+
+ if (test_command_line_internal (FALSE, arg1, var_args))
+ {
+ printf ("ok %d\n", ++test_num);
+ }
+ else
+ {
+ printf ("not ok %d\n", ++test_num);
+ num_failed++;
+ }
+
+ va_end (var_args);
+}
+
+/* This test outputs TAP syntax: http://testanything.org/ */
int
main (int argc, char **argv)
{
- if (!test_command_line ("command", "-s", "--force-shutdown", "\"a string\"", "123", NULL)
- || !test_command_line ("command", "-s", NULL)
- || !test_command_line ("/opt/gnome/bin/service-start", NULL)
- || !test_command_line ("grep", "-l", "-r", "-i", "'whatever'", "files*.c", NULL)
- || !test_command_line ("/home/boston/johnp/devel-local/dbus/test/test-segfault", NULL)
- || !test_command_line ("ls", "-l", "-a", "--colors", _dbus_get_tmpdir(), NULL)
- || !test_command_line ("rsync-to-server", NULL)
- || !test_command_line ("test-segfault", "--no-segfault", NULL)
- || !test_command_line ("evolution", "mailto:pepe@cuco.com", NULL)
- || !test_command_line ("run", "\"a \n multiline\"", NULL)
- || test_command_line ("ls", "\"a wrong string'", NULL) /* invalid command line */ )
- return -1;
-
- return 0;
+ test_command_line ("command", "-s", "--force-shutdown", "\"a string\"", "123", NULL);
+ test_command_line ("command", "-s", NULL);
+ test_command_line ("/opt/gnome/bin/service-start", NULL);
+ test_command_line ("grep", "-l", "-r", "-i", "'whatever'", "files*.c", NULL);
+ test_command_line ("/home/boston/johnp/devel-local/dbus/test/test-segfault", NULL);
+ test_command_line ("ls", "-l", "-a", "--colors", _dbus_get_tmpdir(), NULL);
+ test_command_line ("rsync-to-server", NULL);
+ test_command_line ("test-segfault", "--no-segfault", NULL);
+ test_command_line ("evolution", "mailto:pepe@cuco.com", NULL);
+ test_command_line ("run", "\"a \n multiline\"", NULL);
+ test_command_line_fails ("ls", "\"a wrong string'", NULL);
+
+ /* Tell the TAP driver that we have done all the tests we plan to do.
+ * This is how it can distinguish between an unexpected exit and
+ * successful completion. */
+ printf ("1..%d\n", test_num);
+
+ return (num_failed != 0);
}
diff --git a/test/syntax.c b/test/syntax.c
index e26b3643..bf960c9e 100644
--- a/test/syntax.c
+++ b/test/syntax.c
@@ -30,6 +30,8 @@
#include <dbus/dbus.h>
+#include "test-utils-glib.h"
+
typedef struct {
DBusError e;
} Fixture;
@@ -269,7 +271,7 @@ int
main (int argc,
char **argv)
{
- g_test_init (&argc, &argv, NULL);
+ test_init (&argc, &argv);
g_test_add ("/syntax/path", Fixture, &paths, setup, test_syntax, teardown);
g_test_add ("/syntax/interface", Fixture, &interfaces,
diff --git a/test/tap-test.sh.in b/test/tap-test.sh.in
new file mode 100644
index 00000000..743cdf39
--- /dev/null
+++ b/test/tap-test.sh.in
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Wrapper to make an Automake-style test output TAP syntax:
+#
+# - arbitrary stdout/stderr is sent to stderr where it will not be
+# interpreted as TAP
+# - it is treated as a single test-case
+# - exit 77 is a skip
+# - exit 0 is a pass
+# - anything else is a failure
+#
+# Usage: use sed to replace @RUN@ with the shell command-line to be run.
+
+set -e
+
+# we plan to do 1 test-case
+echo "1..1"
+
+e=0
+@RUN@ >&2 || e=$?
+
+case "$e" in
+ (0)
+ echo "ok 1 @RUN@"
+ ;;
+ (77)
+ echo "ok 1 # SKIP @RUN@"
+ ;;
+ (*)
+ echo "not ok 1 @RUN@ (exit status $e)"
+ ;;
+esac
diff --git a/test/test-service.c b/test/test-service.c
index 7181fa38..c0bd2c60 100644
--- a/test/test-service.c
+++ b/test/test-service.c
@@ -400,20 +400,18 @@ main (int argc,
int result;
DBusConnection *connection;
const char *name;
- dbus_bool_t do_fork;
-
+#ifndef DBUS_WIN
+ dbus_bool_t do_fork = FALSE;
+#endif
if (argc != 3)
{
name = "org.freedesktop.DBus.TestSuiteEchoService";
- do_fork = FALSE;
}
else
{
name = argv[1];
#ifndef DBUS_WIN
do_fork = strcmp (argv[2], "fork") == 0;
-#else
- do_fork = FALSE;
#endif
}
diff --git a/test/test-utils-glib.c b/test/test-utils-glib.c
new file mode 100644
index 00000000..a40c30b7
--- /dev/null
+++ b/test/test-utils-glib.c
@@ -0,0 +1,452 @@
+/* Utility functions for tests that rely on GLib
+ *
+ * Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2013-2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+#include "test-utils-glib.h"
+
+#include <string.h>
+
+#ifdef DBUS_WIN
+# include <io.h>
+# include <windows.h>
+#else
+# include <errno.h>
+# include <signal.h>
+# include <unistd.h>
+# include <sys/types.h>
+# include <pwd.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <dbus/dbus.h>
+
+#ifdef G_OS_WIN
+# define isatty(x) _isatty(x)
+#endif
+
+void
+_test_assert_no_error (const DBusError *e,
+ const char *file,
+ int line)
+{
+ if (G_UNLIKELY (dbus_error_is_set (e)))
+ g_error ("%s:%d: expected success but got error: %s: %s",
+ file, line, e->name, e->message);
+}
+
+#ifdef DBUS_UNIX
+static void
+child_setup (gpointer user_data)
+{
+ const struct passwd *pwd = user_data;
+ uid_t uid = geteuid ();
+
+ if (pwd == NULL || (pwd->pw_uid == uid && getuid () == uid))
+ return;
+
+ if (uid != 0)
+ g_error ("not currently euid 0: %lu", (unsigned long) uid);
+
+ if (setuid (pwd->pw_uid) != 0)
+ g_error ("could not setuid (%lu): %s",
+ (unsigned long) pwd->pw_uid, g_strerror (errno));
+
+ uid = getuid ();
+
+ if (uid != pwd->pw_uid)
+ g_error ("after successful setuid (%lu) my uid is %ld",
+ (unsigned long) pwd->pw_uid, (unsigned long) uid);
+
+ uid = geteuid ();
+
+ if (uid != pwd->pw_uid)
+ g_error ("after successful setuid (%lu) my euid is %ld",
+ (unsigned long) pwd->pw_uid, (unsigned long) uid);
+}
+#endif
+
+static gchar *
+spawn_dbus_daemon (const gchar *binary,
+ const gchar *configuration,
+ TestUser user,
+ GPid *daemon_pid)
+{
+ GError *error = NULL;
+ GString *address;
+ gint address_fd;
+ const gchar *const argv[] = {
+ binary,
+ configuration,
+ "--nofork",
+ "--print-address=1", /* stdout */
+#ifdef DBUS_UNIX
+ "--systemd-activation",
+#endif
+ NULL
+ };
+#ifdef DBUS_UNIX
+ const struct passwd *pwd = NULL;
+#endif
+
+ if (user != TEST_USER_ME)
+ {
+#ifdef DBUS_UNIX
+ if (getuid () != 0)
+ {
+ g_test_skip ("cannot use alternative uid when not uid 0");
+ return NULL;
+ }
+
+ switch (user)
+ {
+ case TEST_USER_ROOT:
+ break;
+
+ case TEST_USER_MESSAGEBUS:
+ pwd = getpwnam (DBUS_USER);
+
+ if (pwd == NULL)
+ {
+ gchar *message = g_strdup_printf ("user '%s' does not exist",
+ DBUS_USER);
+
+ g_test_skip (message);
+ g_free (message);
+ return NULL;
+ }
+
+ break;
+
+ case TEST_USER_OTHER:
+ pwd = getpwnam (DBUS_TEST_USER);
+
+ if (pwd == NULL)
+ {
+ gchar *message = g_strdup_printf ("user '%s' does not exist",
+ DBUS_TEST_USER);
+
+ g_test_skip (message);
+ g_free (message);
+ return NULL;
+ }
+
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+#else
+ g_test_skip ("cannot use alternative uid on Windows");
+ return NULL;
+#endif
+ }
+
+ g_spawn_async_with_pipes (NULL, /* working directory */
+ (gchar **) argv, /* g_s_a_w_p() is not const-correct :-( */
+ NULL, /* envp */
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+#ifdef DBUS_UNIX
+ child_setup, (gpointer) pwd,
+#else
+ NULL, NULL,
+#endif
+ daemon_pid,
+ NULL, /* child's stdin = /dev/null */
+ &address_fd,
+ NULL, /* child's stderr = our stderr */
+ &error);
+ g_assert_no_error (error);
+
+ address = g_string_new (NULL);
+
+ /* polling until the dbus-daemon writes out its address is a bit stupid,
+ * but at least it's simple, unlike dbus-launch... in principle we could
+ * use select() here, but life's too short */
+ while (1)
+ {
+ gssize bytes;
+ gchar buf[4096];
+ gchar *newline;
+
+ bytes = read (address_fd, buf, sizeof (buf));
+
+ if (bytes > 0)
+ g_string_append_len (address, buf, bytes);
+
+ newline = strchr (address->str, '\n');
+
+ if (newline != NULL)
+ {
+ if ((newline > address->str) && ('\r' == newline[-1]))
+ newline -= 1;
+ g_string_truncate (address, newline - address->str);
+ break;
+ }
+
+ g_usleep (G_USEC_PER_SEC / 10);
+ }
+
+ g_close (address_fd, NULL);
+
+ return g_string_free (address, FALSE);
+}
+
+gchar *
+test_get_dbus_daemon (const gchar *config_file,
+ TestUser user,
+ GPid *daemon_pid)
+{
+ gchar *dbus_daemon;
+ gchar *arg;
+ gchar *address;
+
+ if (config_file != NULL)
+ {
+
+ if (g_getenv ("DBUS_TEST_DATA") == NULL)
+ {
+ g_test_message ("set DBUS_TEST_DATA to a directory containing %s",
+ config_file);
+ g_test_skip ("DBUS_TEST_DATA not set");
+ return NULL;
+ }
+
+ arg = g_strdup_printf (
+ "--config-file=%s/%s",
+ g_getenv ("DBUS_TEST_DATA"), config_file);
+ }
+ else if (g_getenv ("DBUS_TEST_DATADIR") != NULL)
+ {
+ arg = g_strdup_printf ("--config-file=%s/dbus-1/session.conf",
+ g_getenv ("DBUS_TEST_DATADIR"));
+ }
+ else if (g_getenv ("DBUS_TEST_DATA") != NULL)
+ {
+ arg = g_strdup_printf (
+ "--config-file=%s/valid-config-files/session.conf",
+ g_getenv ("DBUS_TEST_DATA"));
+ }
+ else
+ {
+ arg = g_strdup ("--session");
+ }
+
+ dbus_daemon = g_strdup (g_getenv ("DBUS_TEST_DAEMON"));
+
+ if (dbus_daemon == NULL)
+ dbus_daemon = g_strdup ("dbus-daemon");
+
+ if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL)
+ {
+ if (config_file != NULL || user != TEST_USER_ME)
+ {
+ g_test_skip ("cannot use DBUS_TEST_DAEMON_ADDRESS for "
+ "unusally-configured dbus-daemon");
+ address = NULL;
+ }
+ else
+ {
+ address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS"));
+ }
+ }
+ else
+ {
+ address = spawn_dbus_daemon (dbus_daemon, arg, user, daemon_pid);
+ }
+
+ g_free (dbus_daemon);
+ g_free (arg);
+ return address;
+}
+
+DBusConnection *
+test_connect_to_bus (TestMainContext *ctx,
+ const gchar *address)
+{
+ DBusConnection *conn;
+ DBusError error = DBUS_ERROR_INIT;
+ dbus_bool_t ok;
+
+ conn = dbus_connection_open_private (address, &error);
+ test_assert_no_error (&error);
+ g_assert (conn != NULL);
+
+ ok = dbus_bus_register (conn, &error);
+ test_assert_no_error (&error);
+ g_assert (ok);
+ g_assert (dbus_bus_get_unique_name (conn) != NULL);
+
+ test_connection_setup (ctx, conn);
+ return conn;
+}
+
+DBusConnection *
+test_connect_to_bus_as_user (TestMainContext *ctx,
+ const char *address,
+ TestUser user)
+{
+ /* For now we only do tests like this on Linux, because I don't know how
+ * safe this use of setresuid() is on other platforms */
+#if defined(HAVE_GETRESUID) && defined(HAVE_SETRESUID) && defined(__linux__)
+ uid_t ruid, euid, suid;
+ const struct passwd *pwd;
+ DBusConnection *conn;
+ const char *username;
+
+ switch (user)
+ {
+ case TEST_USER_ME:
+ return test_connect_to_bus (ctx, address);
+
+ case TEST_USER_ROOT:
+ username = "root";
+ break;
+
+ case TEST_USER_MESSAGEBUS:
+ username = DBUS_USER;
+ break;
+
+ case TEST_USER_OTHER:
+ username = DBUS_TEST_USER;
+ break;
+
+ default:
+ g_return_val_if_reached (NULL);
+ }
+
+ if (getresuid (&ruid, &euid, &suid) != 0)
+ g_error ("getresuid: %s", g_strerror (errno));
+
+ if (ruid != 0 || euid != 0 || suid != 0)
+ {
+ g_test_message ("not uid 0 (ruid=%ld euid=%ld suid=%ld)",
+ (unsigned long) ruid, (unsigned long) euid, (unsigned long) suid);
+ g_test_skip ("not uid 0");
+ return NULL;
+ }
+
+ pwd = getpwnam (username);
+
+ if (pwd == NULL)
+ {
+ g_test_message ("getpwnam(\"%s\"): %s", username, g_strerror (errno));
+ g_test_skip ("not uid 0");
+ return NULL;
+ }
+
+ /* Impersonate the desired user while we connect to the bus.
+ * This should work, because we're root. */
+ if (setresuid (pwd->pw_uid, pwd->pw_uid, 0) != 0)
+ g_error ("setresuid(%ld, (same), 0): %s",
+ (unsigned long) pwd->pw_uid, g_strerror (errno));
+
+ conn = test_connect_to_bus (ctx, address);
+
+ /* go back to our saved uid */
+ if (setresuid (0, 0, 0) != 0)
+ g_error ("setresuid(0, 0, 0): %s", g_strerror (errno));
+
+ return conn;
+
+#else
+
+ switch (user)
+ {
+ case TEST_USER_ME:
+ return test_connect_to_bus (ctx, address);
+
+ default:
+ g_test_skip ("setresuid() not available, or unsure about "
+ "credentials-passing semantics on this platform");
+ return NULL;
+ }
+
+#endif
+}
+
+void
+test_kill_pid (GPid pid)
+{
+#ifdef DBUS_WIN
+ if (pid != NULL)
+ TerminateProcess (pid, 1);
+#else
+ if (pid > 0)
+ kill (pid, SIGTERM);
+#endif
+}
+
+static gboolean
+time_out (gpointer data)
+{
+ g_error ("timed out");
+ return FALSE;
+}
+
+#ifdef G_OS_UNIX
+static void
+wrap_abort (int signal)
+{
+ abort ();
+}
+#endif
+
+void
+test_init (int *argcp, char ***argvp)
+{
+ g_test_init (argcp, argvp, NULL);
+ g_test_bug_base ("https://bugs.freedesktop.org/show_bug.cgi?id=");
+
+ /* Prevent tests from hanging forever. This is intended to be long enough
+ * that any reasonable regression test on any reasonable hardware would
+ * have finished. */
+#define TIMEOUT 60
+
+ g_timeout_add_seconds (TIMEOUT, time_out, NULL);
+#ifdef G_OS_UNIX
+ /* The GLib main loop might not be running (we don't use it in every
+ * test). Die with SIGALRM shortly after if necessary. */
+ alarm (TIMEOUT + 10);
+
+ /* Get a core dump from the SIGALRM. */
+ {
+ struct sigaction act = { };
+
+ act.sa_handler = wrap_abort;
+
+ sigaction (SIGALRM, &act, NULL);
+ }
+#endif
+}
+
+void
+test_progress (char symbol)
+{
+ if (g_test_verbose () && isatty (1))
+ g_print ("%c", symbol);
+}
diff --git a/test/test-utils-glib.h b/test/test-utils-glib.h
new file mode 100644
index 00000000..acacee0a
--- /dev/null
+++ b/test/test-utils-glib.h
@@ -0,0 +1,94 @@
+/* Utility functions for tests that rely on GLib
+ *
+ * Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2013-2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef TEST_UTILS_GLIB_H
+#define TEST_UTILS_GLIB_H
+
+#include <dbus/dbus.h>
+
+#include <glib.h>
+
+#include "test-utils.h"
+
+/*
+ * Multi-user support for regression tests run with root privileges in
+ * a continuous integration system.
+ *
+ * A developer would normally run the tests as their own uid. Tests run
+ * as TEST_USER_ME are run, and the others are skipped.
+ *
+ * In a CI system that has access to root privileges, most tests should still
+ * be run as an arbitrary non-root user, as above.
+ *
+ * Certain tests can usefully be run again, as root. When this is done,
+ * tests using TEST_USER_ROOT, TEST_USER_MESSAGEBUS and/or TEST_USER_OTHER
+ * can exercise situations that only arise when there's more than one uid.
+ */
+typedef enum {
+ /* Whatever user happens to be running the regression test;
+ * such tests also work on Windows */
+ TEST_USER_ME,
+ /* Must be uid 0 on Unix; the test is skipped on Windows */
+ TEST_USER_ROOT,
+ /* The user who would normally run the system bus. This is the DBUS_USER
+ * from configure.ac, usually 'messagebus' but perhaps 'dbus' or
+ * '_dbus'. */
+ TEST_USER_MESSAGEBUS,
+ /* An unprivileged user who is neither root nor DBUS_USER.
+ * This is DBUS_TEST_USER from configure.ac, usually 'nobody'. */
+ TEST_USER_OTHER
+} TestUser;
+
+#define test_assert_no_error(e) _test_assert_no_error (e, __FILE__, __LINE__)
+void _test_assert_no_error (const DBusError *e,
+ const char *file,
+ int line);
+
+gchar *test_get_dbus_daemon (const gchar *config_file,
+ TestUser user,
+ GPid *daemon_pid);
+
+DBusConnection *test_connect_to_bus (TestMainContext *ctx,
+ const gchar *address);
+DBusConnection *test_connect_to_bus_as_user (TestMainContext *ctx,
+ const char *address,
+ TestUser user);
+
+void test_kill_pid (GPid pid);
+
+void test_init (int *argcp, char ***argvp);
+
+void test_progress (char symbol);
+
+#if !GLIB_CHECK_VERSION (2, 38, 0)
+#define g_test_skip(s) my_test_skip (s)
+static inline void my_test_skip (const gchar *s)
+{
+ g_test_message ("SKIP: %s", s);
+}
+#endif
+
+#endif
diff --git a/test/test-utils.c b/test/test-utils.c
index 9a4f3584..cb6cf1fb 100644
--- a/test/test-utils.c
+++ b/test/test-utils.c
@@ -1,13 +1,6 @@
#include <config.h>
#include "test-utils.h"
-#ifndef DBUS_TEST_USE_INTERNAL
-# include <dbus/dbus.h>
-# include <dbus/dbus-glib-lowlevel.h>
-#endif
-
-#ifdef DBUS_TEST_USE_INTERNAL
-
typedef struct
{
DBusLoop *loop;
@@ -104,13 +97,10 @@ cdata_new (DBusLoop *loop,
return cd;
}
-#endif /* DBUS_TEST_USE_INTERNAL */
-
dbus_bool_t
test_connection_setup (TestMainContext *ctx,
DBusConnection *connection)
{
-#ifdef DBUS_TEST_USE_INTERNAL
DBusLoop *loop = ctx;
CData *cd;
@@ -159,12 +149,6 @@ test_connection_setup (TestMainContext *ctx,
dbus_connection_set_timeout_functions (connection, NULL, NULL, NULL, NULL, NULL);
return FALSE;
-#else /* !DBUS_TEST_USE_INTERNAL */
-
- dbus_connection_setup_with_g_main (connection, ctx);
- return TRUE;
-
-#endif /* !DBUS_TEST_USE_INTERNAL */
}
static void
@@ -195,8 +179,6 @@ test_connection_shutdown (TestMainContext *ctx,
dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL);
}
-#ifdef DBUS_TEST_USE_INTERNAL
-
typedef struct
{
DBusLoop *loop;
@@ -278,13 +260,10 @@ remove_server_timeout (DBusTimeout *timeout,
_dbus_loop_remove_timeout (context->loop, timeout);
}
-#endif /* DBUS_TEST_USE_INTERNAL */
-
dbus_bool_t
test_server_setup (TestMainContext *ctx,
DBusServer *server)
{
-#ifdef DBUS_TEST_USE_INTERNAL
DBusLoop *loop = ctx;
ServerData *sd;
@@ -323,13 +302,6 @@ test_server_setup (TestMainContext *ctx,
test_server_shutdown (loop, server);
return FALSE;
-
-#else /* !DBUS_TEST_USE_INTERNAL */
-
- dbus_server_setup_with_g_main (server, ctx);
- return TRUE;
-
-#endif /* !DBUS_TEST_USE_INTERNAL */
}
void
@@ -354,39 +326,32 @@ test_server_shutdown (TestMainContext *ctx,
TestMainContext *
test_main_context_get (void)
{
-#ifdef DBUS_TEST_USE_INTERNAL
return _dbus_loop_new ();
-#else
- /* I suspect dbus-glib relies the default main context in some places */
- return g_main_context_ref (g_main_context_default ());
-#endif
}
TestMainContext *
test_main_context_ref (TestMainContext *ctx)
{
-#ifdef DBUS_TEST_USE_INTERNAL
return _dbus_loop_ref (ctx);
-#else
- return g_main_context_ref (ctx);
-#endif
}
void test_main_context_unref (TestMainContext *ctx)
{
-#ifdef DBUS_TEST_USE_INTERNAL
_dbus_loop_unref (ctx);
-#else
- g_main_context_unref (ctx);
-#endif
}
void test_main_context_iterate (TestMainContext *ctx,
dbus_bool_t may_block)
{
-#ifdef DBUS_TEST_USE_INTERNAL
_dbus_loop_iterate (ctx, may_block);
-#else
- g_main_context_iteration (ctx, may_block);
-#endif
+}
+
+void
+test_pending_call_store_reply (DBusPendingCall *pc,
+ void *data)
+{
+ DBusMessage **message_p = data;
+
+ *message_p = dbus_pending_call_steal_reply (pc);
+ _dbus_assert (*message_p != NULL);
}
diff --git a/test/test-utils.h b/test/test-utils.h
index 0d3f3690..39fae77b 100644
--- a/test/test-utils.h
+++ b/test/test-utils.h
@@ -6,18 +6,9 @@
#include <dbus/dbus.h>
-#ifdef DBUS_TEST_USE_INTERNAL
-
-# include <dbus/dbus-mainloop.h>
-# include <dbus/dbus-internals.h>
- typedef DBusLoop TestMainContext;
-
-#else /* !DBUS_TEST_USE_INTERNAL */
-
-# include <glib.h>
- typedef GMainContext TestMainContext;
-
-#endif /* !DBUS_TEST_USE_INTERNAL */
+#include <dbus/dbus-mainloop.h>
+#include <dbus/dbus-internals.h>
+typedef DBusLoop TestMainContext;
TestMainContext *test_main_context_get (void);
TestMainContext *test_main_context_ref (TestMainContext *ctx);
@@ -34,5 +25,7 @@ dbus_bool_t test_server_setup (TestMainContext *ctx,
DBusServer *server);
void test_server_shutdown (TestMainContext *ctx,
DBusServer *server);
+void test_pending_call_store_reply (DBusPendingCall *pc,
+ void *data);
#endif
diff --git a/test/uid-permissions.c b/test/uid-permissions.c
new file mode 100644
index 00000000..2c62185a
--- /dev/null
+++ b/test/uid-permissions.c
@@ -0,0 +1,199 @@
+/* Integration tests for the dbus-daemon's uid-based hardening
+ *
+ * Author: Simon McVittie <simon.mcvittie@collabora.co.uk>
+ * Copyright © 2010-2011 Nokia Corporation
+ * Copyright © 2015 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <config.h>
+
+#include "test-utils-glib.h"
+
+typedef struct {
+ gboolean skip;
+
+ TestMainContext *ctx;
+
+ DBusError e;
+ GError *ge;
+
+ GPid daemon_pid;
+
+ DBusConnection *conn;
+} Fixture;
+
+typedef struct {
+ const char *config_file;
+ TestUser user;
+ gboolean expect_success;
+} Config;
+
+static void
+setup (Fixture *f,
+ gconstpointer context)
+{
+ const Config *config = context;
+ gchar *address;
+
+ f->ctx = test_main_context_get ();
+ f->ge = NULL;
+ dbus_error_init (&f->e);
+
+ address = test_get_dbus_daemon (config ? config->config_file : NULL,
+ TEST_USER_MESSAGEBUS,
+ &f->daemon_pid);
+
+ if (address == NULL)
+ {
+ f->skip = TRUE;
+ return;
+ }
+
+ f->conn = test_connect_to_bus_as_user (f->ctx, address,
+ config ? config->user : TEST_USER_ME);
+
+ if (f->conn == NULL)
+ f->skip = TRUE;
+
+ g_free (address);
+}
+
+static void
+test_uae (Fixture *f,
+ gconstpointer context)
+{
+ const Config *config = context;
+ DBusMessage *m;
+ DBusPendingCall *pc;
+ DBusMessageIter args_iter;
+ DBusMessageIter arr_iter;
+
+ if (f->skip)
+ return;
+
+ m = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "UpdateActivationEnvironment");
+
+ if (m == NULL)
+ g_error ("OOM");
+
+ dbus_message_iter_init_append (m, &args_iter);
+
+ /* Append an empty a{ss} (string => string dictionary). */
+ if (!dbus_message_iter_open_container (&args_iter, DBUS_TYPE_ARRAY,
+ "{ss}", &arr_iter) ||
+ !dbus_message_iter_close_container (&args_iter, &arr_iter))
+ g_error ("OOM");
+
+ if (!dbus_connection_send_with_reply (f->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))
+ test_pending_call_store_reply (pc, &m);
+ else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply,
+ &m, NULL))
+ g_error ("OOM");
+
+ while (m == NULL)
+ test_main_context_iterate (f->ctx, TRUE);
+
+ if (config->expect_success)
+ {
+ /* it succeeds */
+ g_assert_cmpint (dbus_message_get_type (m), ==,
+ DBUS_MESSAGE_TYPE_METHOD_RETURN);
+ }
+ else
+ {
+ /* it fails, yielding an error message with one string argument */
+ g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR);
+ g_assert_cmpstr (dbus_message_get_error_name (m), ==,
+ DBUS_ERROR_ACCESS_DENIED);
+ g_assert_cmpstr (dbus_message_get_signature (m), ==, "s");
+ }
+
+ dbus_message_unref (m);
+}
+
+static void
+teardown (Fixture *f,
+ gconstpointer context G_GNUC_UNUSED)
+{
+ dbus_error_free (&f->e);
+ g_clear_error (&f->ge);
+
+ if (f->conn != NULL)
+ {
+ dbus_connection_close (f->conn);
+ dbus_connection_unref (f->conn);
+ f->conn = NULL;
+ }
+
+ if (f->daemon_pid != 0)
+ {
+ test_kill_pid (f->daemon_pid);
+ g_spawn_close_pid (f->daemon_pid);
+ f->daemon_pid = 0;
+ }
+
+ test_main_context_unref (f->ctx);
+}
+
+static Config root_ok_config = {
+ "valid-config-files/multi-user.conf",
+ TEST_USER_ROOT,
+ TRUE
+};
+
+static Config messagebus_ok_config = {
+ "valid-config-files/multi-user.conf",
+ TEST_USER_MESSAGEBUS,
+ TRUE
+};
+
+static Config other_fail_config = {
+ "valid-config-files/multi-user.conf",
+ TEST_USER_OTHER,
+ FALSE
+};
+
+int
+main (int argc,
+ char **argv)
+{
+ test_init (&argc, &argv);
+
+ g_test_add ("/uid-permissions/uae/root", Fixture, &root_ok_config,
+ setup, test_uae, teardown);
+ g_test_add ("/uid-permissions/uae/messagebus", Fixture, &messagebus_ok_config,
+ setup, test_uae, teardown);
+ g_test_add ("/uid-permissions/uae/other", Fixture, &other_fail_config,
+ setup, test_uae, teardown);
+
+ return g_test_run ();
+}