From 08144b6ed85178785cfc075fda4be9f66f2378e8 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Thu, 16 Dec 2021 14:42:27 +0100 Subject: [PATCH 1/6] Fix indentation of functions that are later changed --- dbus/dbus-sysdeps-win.c | 43 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 5b1feb90..30f06691 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -3243,22 +3243,23 @@ _dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name) int i; // read shm - for(i=0;i<20;++i) { + for (i = 0; i < 20; ++i) + { // we know that dbus-daemon is available, so we wait until shm is available sharedMem = OpenFileMappingA (FILE_MAP_READ, FALSE, _dbus_string_get_const_data (shm_name)); if (sharedMem == 0) - Sleep (100); - if ( sharedMem != 0) - break; - } + Sleep (100); + if (sharedMem != 0) + break; + } if (sharedMem == 0) - return FALSE; + return FALSE; shared_addr = MapViewOfFile (sharedMem, FILE_MAP_READ, 0, 0, 0); if (!shared_addr) - return FALSE; + return FALSE; _dbus_string_init (address); @@ -3283,7 +3284,7 @@ _dbus_daemon_already_runs (DBusString *address, DBusString *shm_name, const char if (!_dbus_string_init (&mutex_name)) return FALSE; - if (!_dbus_get_mutex_name (&mutex_name,scope) || + if (!_dbus_get_mutex_name (&mutex_name, scope) || /* not determinable */ _dbus_string_get_length (&mutex_name) == 0) { @@ -3300,7 +3301,7 @@ _dbus_daemon_already_runs (DBusString *address, DBusString *shm_name, const char // do checks daemon = CreateMutexA (NULL, FALSE, _dbus_string_get_const_data (&mutex_name)); - if(WaitForSingleObject (daemon, 10) != WAIT_TIMEOUT) + if (WaitForSingleObject (daemon, 10) != WAIT_TIMEOUT) { ReleaseMutex (daemon); CloseHandle (daemon); @@ -3333,14 +3334,14 @@ _dbus_get_autolaunch_address (const char *scope, LPSTR lpFile; char dbus_exe_path[MAX_PATH]; DBusString dbus_args = _DBUS_STRING_INIT_INVALID; - const char * daemon_name = DBUS_DAEMON_NAME ".exe"; + const char *daemon_name = DBUS_DAEMON_NAME ".exe"; DBusString shm_name; _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (!_dbus_string_init (&shm_name)) { - _DBUS_SET_OOM(error); + _DBUS_SET_OOM (error); return FALSE; } @@ -3365,12 +3366,12 @@ _dbus_get_autolaunch_address (const char *scope, if (_dbus_daemon_already_runs (address, &shm_name, scope)) { _dbus_verbose ("found running dbus daemon for scope '%s' at %s\n", - scope ? scope : "", _dbus_string_get_const_data (&shm_name) ); + scope ? scope : "", _dbus_string_get_const_data (&shm_name)); retval = TRUE; goto out; } - if (!SearchPathA (NULL, daemon_name, NULL, sizeof(dbus_exe_path), dbus_exe_path, &lpFile)) + if (!SearchPathA (NULL, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile)) { // Look in directory containing dbus shared library HMODULE hmod; @@ -3381,7 +3382,7 @@ _dbus_get_autolaunch_address (const char *scope, "trying path where dbus shared library is located"); hmod = _dbus_win_get_dll_hmodule (); - rc = GetModuleFileNameA (hmod, dbus_module_path, sizeof(dbus_module_path)); + rc = GetModuleFileNameA (hmod, dbus_module_path, sizeof (dbus_module_path)); if (rc <= 0) { dbus_set_error_const (error, DBUS_ERROR_FAILED, "could not retrieve dbus shared library file name"); @@ -3393,7 +3394,7 @@ _dbus_get_autolaunch_address (const char *scope, char *ext_idx = strrchr (dbus_module_path, '\\'); if (ext_idx) *ext_idx = '\0'; - if (!SearchPathA (dbus_module_path, daemon_name, NULL, sizeof(dbus_exe_path), dbus_exe_path, &lpFile)) + if (!SearchPathA (dbus_module_path, daemon_name, NULL, sizeof (dbus_exe_path), dbus_exe_path, &lpFile)) { dbus_set_error (error, DBUS_ERROR_FAILED, "Could not find dbus-daemon executable. " @@ -3407,11 +3408,10 @@ _dbus_get_autolaunch_address (const char *scope, } } - // Create process - ZeroMemory (&si, sizeof(si)); + ZeroMemory (&si, sizeof (si)); si.cb = sizeof (si); - ZeroMemory (&pi, sizeof(pi)); + ZeroMemory (&pi, sizeof (pi)); if (!_dbus_string_init (&dbus_args)) { @@ -3427,8 +3427,8 @@ _dbus_get_autolaunch_address (const char *scope, goto out; } -// argv[i] = "--config-file=bus\\session.conf"; - if(CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + // argv[i] = "--config-file=bus\\session.conf"; + if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { CloseHandle (pi.hThread); CloseHandle (pi.hProcess); @@ -3450,8 +3450,7 @@ out: _dbus_string_free (&dbus_args); return retval; - } - +} /** Makes the file readable by every user in the system. * From f20a05f60f6f545757de98f0998dd2054ff3aac9 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Mon, 20 Dec 2021 14:40:38 +0100 Subject: [PATCH 2/6] sysdeps-win: Add support to give auto launched dbus daemon custom parameter For that purpose the function _dbus_test_win_set_autolaunch_command_line_parameter() has been added. Signed-off-by: Ralf Habacker --- dbus/dbus-sysdeps-win.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 30f06691..a7396427 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -2895,6 +2895,26 @@ static const char *cDBusDaemonMutex = "DBusDaemonMutex"; // named shm for dbus adress info (per user) static const char *cDBusDaemonAddressInfo = "DBusDaemonAddressInfo"; +/* custom command line parameter for autolaunching daemon */ +static const char *autolaunch_custom_command_line_parameter = ""; + +/** + * Set command line parameters for the dbus daemon to start + * for an autolaunch session. + * + * The specified instance must be valid until the dbus-daemon + * is started. + * + * This function is not thread-safe, and can only be called from a + * single-threaded unit test. + * + * @param path string to use as command line parameter + */ +void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path) +{ + autolaunch_custom_command_line_parameter = path; +} + /** * Return the hash of the installation root directory, which can be * used to construct a per-installation-root scope for autolaunching @@ -3420,7 +3440,8 @@ _dbus_get_autolaunch_address (const char *scope, goto out; } - if (!_dbus_string_append_printf (&dbus_args, "\"%s\" --session", dbus_exe_path)) + if (!_dbus_string_append_printf (&dbus_args, "\"%s\" %s", dbus_exe_path, + autolaunch_custom_command_line_parameter ? autolaunch_custom_command_line_parameter : "--session")) { dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, "Failed to append string to argument buffer"); retval = FALSE; From 62ccb35996449a8786beacc343646480556d66a5 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Mon, 20 Dec 2021 14:41:06 +0100 Subject: [PATCH 3/6] sysdeps-win: Let tests access autostarted dbus-daemon's handle This will allow automated tests to kill the autostarted dbus-daemon before continuing to the next test-case, which wasn't previously possible. Signed-off-by: Ralf Habacker --- dbus/dbus-sysdeps-win.c | 28 +++++++++++++++++++++++++++- dbus/dbus-sysdeps-win.h | 4 ++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index a7396427..19e832b4 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -2915,6 +2915,24 @@ void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path) autolaunch_custom_command_line_parameter = path; } +static HANDLE *autolaunch_handle_location; + +/** + * Set location where to store process handle of an autostarted server + * + * This function is not thread-safe, and can only be called from a + * single-threaded unit test. + * + * After using the handle it must be closed with @ref CloseHandle(). + * + * @param location Pointer where to store the handle + */ +void +_dbus_test_win_set_autolaunch_handle_location (HANDLE *location) +{ + autolaunch_handle_location = location; +} + /** * Return the hash of the installation root directory, which can be * used to construct a per-installation-root scope for autolaunching @@ -3452,7 +3470,15 @@ _dbus_get_autolaunch_address (const char *scope, if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { CloseHandle (pi.hThread); - CloseHandle (pi.hProcess); + if (autolaunch_handle_location != NULL) + { + *autolaunch_handle_location = pi.hProcess; + _dbus_verbose ("Returning process handle of started server (handle=%p)\n", pi.hProcess); + } + else + { + CloseHandle (pi.hProcess); + } retval = _dbus_get_autolaunch_shm (address, &shm_name); if (retval == FALSE) dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to get autolaunch address from launched dbus-daemon"); diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h index 284bc796..5027f180 100644 --- a/dbus/dbus-sysdeps-win.h +++ b/dbus/dbus-sysdeps-win.h @@ -117,6 +117,10 @@ dbus_bool_t _dbus_daemon_publish_session_bus_address (const char *address, DBUS_PRIVATE_EXPORT DBusRMutex *_dbus_win_rmutex_named_new (const char* name); +DBUS_PRIVATE_EXPORT +void _dbus_test_win_autolaunch_set_command_line_parameter (const char *path); +DBUS_PRIVATE_EXPORT +void _dbus_test_win_set_autolaunch_handle_location (HANDLE *location); #endif /** @} end of sysdeps-win.h */ From 6bced858cbe0a793b2eb116ceefdd47277ebb6c6 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Mon, 20 Dec 2021 12:34:14 +0100 Subject: [PATCH 4/6] sysdeps-win: Use the `connection readiness` event when starting dbus-daemon In _dbus_get_autolaunch_address() instead of polling at intervals to see if the shared memory segment is present, the named signal is now used to reliably get the state of dbus-daemon. In _dbus_get_autolaunch_shm() the unnecessary initialization of `address` has been removed, error handling has been improved and the `wait` parameter has been added to select waiting. Signed-off-by: Ralf Habacker --- dbus/dbus-sysdeps-win.c | 98 ++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 16 deletions(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 19e832b4..7d4b1bf8 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -3273,15 +3273,29 @@ _dbus_daemon_unpublish_session_bus_address (void) return TRUE; } +/** + * Get server bus address from shared memory segment provided by running dbus-daemon + * + * @param address initialized DBusString instance to store the retrieved address + * @param shm_name the name of the shared memory segment + * @param wait if TRUE wait maximum 2 seconds for the presence of the shared memory segment + * @return #TRUE the bus address was fetched from the shared memory segment + * @return #FALSE error during execution + */ static dbus_bool_t -_dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name) +_dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name, dbus_bool_t wait) { - HANDLE sharedMem; + HANDLE sharedMem = NULL; char *shared_addr; int i; + int max = 20; /* max 2 seconds */ + dbus_bool_t retval = FALSE; + + if (!wait) + max = 1; // read shm - for (i = 0; i < 20; ++i) + for (i = 0; i < max; ++i) { // we know that dbus-daemon is available, so we wait until shm is available sharedMem = OpenFileMappingA (FILE_MAP_READ, FALSE, _dbus_string_get_const_data (shm_name)); @@ -3297,18 +3311,15 @@ _dbus_get_autolaunch_shm (DBusString *address, DBusString *shm_name) shared_addr = MapViewOfFile (sharedMem, FILE_MAP_READ, 0, 0, 0); if (!shared_addr) - return FALSE; + goto out; - _dbus_string_init (address); + retval = _dbus_string_append (address, shared_addr); - _dbus_string_append (address, shared_addr); - - // cleanup UnmapViewOfFile (shared_addr); +out: CloseHandle (sharedMem); - - return TRUE; + return retval; } static dbus_bool_t @@ -3346,8 +3357,8 @@ _dbus_daemon_already_runs (DBusString *address, DBusString *shm_name, const char goto out; } - // read shm - retval = _dbus_get_autolaunch_shm (address, shm_name); + // read shm, wait max 2 seconds + retval = _dbus_get_autolaunch_shm (address, shm_name, TRUE); // cleanup CloseHandle (daemon); @@ -3374,6 +3385,7 @@ _dbus_get_autolaunch_address (const char *scope, DBusString dbus_args = _DBUS_STRING_INIT_INVALID; const char *daemon_name = DBUS_DAEMON_NAME ".exe"; DBusString shm_name; + HANDLE ready_event_handle = NULL; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -3461,15 +3473,64 @@ _dbus_get_autolaunch_address (const char *scope, if (!_dbus_string_append_printf (&dbus_args, "\"%s\" %s", dbus_exe_path, autolaunch_custom_command_line_parameter ? autolaunch_custom_command_line_parameter : "--session")) { - dbus_set_error_const (error, DBUS_ERROR_NO_MEMORY, "Failed to append string to argument buffer"); + _DBUS_SET_OOM (error); retval = FALSE; goto out; } - // argv[i] = "--config-file=bus\\session.conf"; - if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + ready_event_handle = _dbus_win_event_create_inheritable (error); + if (ready_event_handle == NULL) + goto out; + + _dbus_verbose ("Creating connection readiness event: handle=%p\n", ready_event_handle); + if (!_dbus_string_append_printf (&dbus_args, " \"--ready-event-handle=%p\"", ready_event_handle)) { + _DBUS_SET_OOM (error); + goto out; + } + + _dbus_verbose ("Starting dbus daemon with args: '%s'\n", _dbus_string_get_const_data (&dbus_args)); + if (CreateProcessA (dbus_exe_path, _dbus_string_get_data (&dbus_args), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + DWORD status; + HANDLE events[2]; + CloseHandle (pi.hThread); + + _dbus_verbose ("Wait until dbus-daemon is ready for connections (event handle %p)\n", ready_event_handle); + + events[0] = ready_event_handle; + events[1] = pi.hProcess; + status = WaitForMultipleObjects (2, events, FALSE, 30000); + + switch (status) + { + case WAIT_OBJECT_0: + /* ready event signalled, everything is okay */ + retval = TRUE; + break; + + case WAIT_OBJECT_0 + 1: + /* dbus-daemon process has exited */ + dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED, "dbus-daemon exited before signalling ready"); + goto out; + + case WAIT_FAILED: + _dbus_win_set_error_from_last_error (error, "Unable to wait for server readiness (handle %p)", ready_event_handle); + goto out; + + case WAIT_TIMEOUT: + /* GetLastError() is not set */ + dbus_set_error (error, DBUS_ERROR_TIMEOUT, "Timed out waiting for server readiness or exit (handle %p)", ready_event_handle); + goto out; + + default: + /* GetLastError() is probably not set? */ + dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for server readiness (handle %p)", status, ready_event_handle); + goto out; + } + _dbus_verbose ("Got signal that dbus-daemon is ready for connections\n"); + if (autolaunch_handle_location != NULL) { *autolaunch_handle_location = pi.hProcess; @@ -3479,7 +3540,9 @@ _dbus_get_autolaunch_address (const char *scope, { CloseHandle (pi.hProcess); } - retval = _dbus_get_autolaunch_shm (address, &shm_name); + + /* do not wait for the appearance of shm, we can assume that it is present */ + retval = _dbus_get_autolaunch_shm (address, &shm_name, FALSE); if (retval == FALSE) dbus_set_error_const (error, DBUS_ERROR_FAILED, "Failed to get autolaunch address from launched dbus-daemon"); } @@ -3495,7 +3558,10 @@ out: _dbus_platform_rmutex_free (lock); _dbus_string_free (&shm_name); _dbus_string_free (&dbus_args); + if (ready_event_handle) + _dbus_win_event_free (ready_event_handle, NULL); + _DBUS_ASSERT_ERROR_XOR_BOOL (error, retval); return retval; } From 556eb2ea1ac2c24022313cf2b70bda2f242ba4a1 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Tue, 21 Dec 2021 08:50:00 +0100 Subject: [PATCH 5/6] sysdeps-win: display process id of started dbus-daemon When verbose mode is enabled, this information is useful for debugging. --- dbus/dbus-sysdeps-win.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index 7d4b1bf8..508057e2 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -3529,7 +3529,7 @@ _dbus_get_autolaunch_address (const char *scope, dbus_set_error (error, DBUS_ERROR_FAILED, "Unknown result '%lu' while waiting for server readiness (handle %p)", status, ready_event_handle); goto out; } - _dbus_verbose ("Got signal that dbus-daemon is ready for connections\n"); + _dbus_verbose ("Got signal that dbus-daemon with process id '%ld' is ready for connections\n", GetProcessId (pi.hProcess)); if (autolaunch_handle_location != NULL) { From b1dd6fa7a62e42d9f38d73d28a3bf1c1618e8b32 Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Wed, 17 Nov 2021 10:51:39 +0100 Subject: [PATCH 6/6] Add a test application for autostart on Windows implementation The test application performs several individual tests to detect possible problems with the autostart support under Windows. Connections are tested with the standard scope, a 'custom' scope, the 'install path' scope and the 'user' scope. Signed-off-by: Ralf Habacker --- .../listen-autolaunch-win.conf.in | 11 + test/name-test/CMakeLists.txt | 6 +- test/name-test/Makefile.am | 16 +- test/name-test/test-autolaunch-win.c | 363 ++++++++++++++++++ 4 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 test/data/valid-config-files/listen-autolaunch-win.conf.in create mode 100644 test/name-test/test-autolaunch-win.c diff --git a/test/data/valid-config-files/listen-autolaunch-win.conf.in b/test/data/valid-config-files/listen-autolaunch-win.conf.in new file mode 100644 index 00000000..7557e526 --- /dev/null +++ b/test/data/valid-config-files/listen-autolaunch-win.conf.in @@ -0,0 +1,11 @@ + + + session + @TEST_LISTEN@ + + + + + + diff --git a/test/name-test/CMakeLists.txt b/test/name-test/CMakeLists.txt index 1b3c0e5c..098993ae 100644 --- a/test/name-test/CMakeLists.txt +++ b/test/name-test/CMakeLists.txt @@ -1,6 +1,10 @@ add_definitions(${DBUS_INTERNAL_CLIENT_DEFINITIONS}) -add_helper_executable(test-autolaunch test-autolaunch.c dbus-testutils) +if(WIN32) + add_test_executable(test-autolaunch-win test-autolaunch-win.c ${DBUS_INTERNAL_LIBRARIES} dbus-testutils) +else() + add_helper_executable(test-autolaunch test-autolaunch.c dbus-testutils) +endif() add_session_test_executable(test-ids test-ids.c ${DBUS_INTERNAL_LIBRARIES}) add_session_test_executable(test-pending-call-disconnected test-pending-call-disconnected.c ${DBUS_INTERNAL_LIBRARIES}) diff --git a/test/name-test/Makefile.am b/test/name-test/Makefile.am index 81938778..527dbbff 100644 --- a/test/name-test/Makefile.am +++ b/test/name-test/Makefile.am @@ -72,7 +72,7 @@ if DBUS_ENABLE_EMBEDDED_TESTS ## we use noinst_PROGRAMS not check_PROGRAMS for TESTS so that we ## build even when not doing "make check" -noinst_PROGRAMS=test-pending-call-dispatch test-pending-call-timeout test-pending-call-disconnected test-threads-init test-ids test-shutdown test-privserver-client test-autolaunch +noinst_PROGRAMS=test-pending-call-dispatch test-pending-call-timeout test-pending-call-disconnected test-threads-init test-ids test-shutdown test-privserver-client test_pending_call_dispatch_LDADD = \ $(CODE_COVERAGE_LIBS) \ @@ -103,9 +103,21 @@ test_privserver_client_LDADD = \ $(CODE_COVERAGE_LIBS) \ ../libdbus-testutils.la \ $(NULL) + +if DBUS_WIN +noinst_PROGRAMS += test-autolaunch-win +test_autolaunch_win_SOURCES = test-autolaunch-win.c +test_autolaunch_win_LDADD = \ + $(CODE_COVERAGE_LIBS) \ + ../libdbus-testutils.la \ + $(NULL) +TESTS += test-autolaunch-win +else +noinst_PROGRAMS += test-autolaunch +test_autolaunch_SOURCES = test-autolaunch.c test_autolaunch_LDADD = \ $(CODE_COVERAGE_LIBS) \ ../libdbus-testutils.la \ $(NULL) - +endif endif diff --git a/test/name-test/test-autolaunch-win.c b/test/name-test/test-autolaunch-win.c new file mode 100644 index 00000000..8a434e2c --- /dev/null +++ b/test/name-test/test-autolaunch-win.c @@ -0,0 +1,363 @@ +/* + * Copyright © 2018-2022 Ralf Habacker + * SPDX-License-Identifier: MIT + */ + +/** + * This test checks whether a client can connect to a dbus daemon configured + * for a default, user-defined and installation path related autostart and + * whether it can connect to a server having a different autolaunch + * configuration. + */ + +#include "config.h" + +#include "dbus/dbus-file.h" +#include "dbus/dbus-internals.h" +#include "dbus/dbus-sysdeps.h" +#include "dbus/dbus-test-tap.h" +#include "dbus/dbus-test.h" +#include "dbus/dbus.h" +#include "test/test-utils.h" + +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +/* dbus_bus_get does not work yet */ +static dbus_bool_t use_bus_get = FALSE; + +static int add_wait_time = 0; + +#define oom() _dbus_test_fatal ("Out of memory") + +/** + * helper function + */ +#define _dbus_error_set_from_message_with_location(a, b) __dbus_error_set_from_message_with_location (__FILE__, __LINE__, __FUNCTION__, a, b) + +static void +__dbus_error_set_from_message_with_location (const char *file, int line, const char *function, DBusError *error, DBusMessage *message) +{ + char *str = NULL; + dbus_message_get_args (message, NULL, + DBUS_TYPE_STRING, &str, + DBUS_TYPE_INVALID); + dbus_set_error (error, dbus_message_get_error_name (message), "[%s(%d):%s] %s", file, line, function, str ? str : ""); +} + +static dbus_bool_t +call_method (DBusConnection *conn, + DBusError *error, + int timeout, + const char *interface, + const char *method_str) +{ + DBusMessage *method; + DBusMessage *reply; + dbus_bool_t result = TRUE; + + dbus_error_init (error); + + method = dbus_message_new_method_call (DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + interface, + method_str); + + reply = dbus_connection_send_with_reply_and_block (conn, method, timeout, error); + dbus_message_unref (method); + if (reply == NULL) + { + dbus_set_error (error, DBUS_ERROR_FAILED, "Got no reply"); + result = FALSE; + goto out; + } + + if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR) + { + _dbus_error_set_from_message_with_location (error, reply); + result = FALSE; + goto out; + } + result = TRUE; + +out: + _DBUS_ASSERT_ERROR_XOR_BOOL (error, result); + dbus_message_unref (reply); + return result; +} + +static dbus_bool_t +_server_check_connection (DBusConnection *conn, + DBusError *error) +{ + if (use_bus_get) + return call_method (conn, error, -1, DBUS_INTERFACE_PEER, "GetMachineId"); + else + return call_method (conn, error, -1, DBUS_INTERFACE_DBUS, "Hello"); +} + +static HANDLE autolaunch_handle = NULL; + +static dbus_bool_t +_server_shutdown (DBusConnection *conn, + const char *scope, + int timeout, + DBusError *error) +{ + _dbus_assert (autolaunch_handle != NULL); + + _dbus_test_diag ("Shutting down dbus-daemon (handle=%p)", autolaunch_handle); + if (!TerminateProcess (autolaunch_handle, 1)) + _dbus_test_fatal ("Unable to terminate dbus-daemon (handle=%p) : %s", + /* this string is leaked, but we're crashing anyway */ + autolaunch_handle, _dbus_win_error_string (GetLastError ())); + + _dbus_test_diag ("Return value from closing autolaunch_handle is %d", CloseHandle (autolaunch_handle)); + autolaunch_handle = NULL; + _dbus_test_win_set_autolaunch_handle_location (NULL); + return TRUE; +} + +typedef enum +{ + RUN_TEST_DEFAULT = 0, + RUN_TEST_EXPECT_CONNECTION_TO_FAIL = (1 << 0), +} RunTestFlags; + +static dbus_bool_t +check_results (DBusConnection *conn, + DBusString *server_address, + DBusString *address, + const char *scope, + RunTestFlags flags, + DBusError *error) +{ + if (add_wait_time) + _dbus_sleep_milliseconds (add_wait_time); + + if (dbus_error_is_set (error)) + _dbus_test_diag ("Error is set: %s %s", error->name, error->message); + + if (conn == NULL) + { + if (!dbus_error_is_set (error)) + _dbus_test_fatal ("Failed to autolaunch session bus and no error was set"); + + if (flags & RUN_TEST_EXPECT_CONNECTION_TO_FAIL) + return TRUE; + + _dbus_test_diag ("autolaunch unexpectedly failed: %s: %s", error->name, error->message); + return FALSE; + } + else + { + if (dbus_error_is_set (error)) + _dbus_test_fatal ("Successfully autolaunched session bus but error was set: %s: %s", error->name, error->message); + + if (flags & RUN_TEST_EXPECT_CONNECTION_TO_FAIL) + { + _dbus_test_diag ("autolaunch unexpectedly succeeded"); + return FALSE; + } + _dbus_test_diag ("Client connection succeeded - uses '%s'", _dbus_string_get_const_data (address)); + } + + if (add_wait_time) + _dbus_sleep_milliseconds (add_wait_time); + + _dbus_test_diag ("Server returned bus address '%s'", _dbus_string_get_const_data (server_address)); + if (!_server_check_connection (conn, error)) + { + _dbus_test_diag ("Could not execute server function"); + return FALSE; + } + else + _dbus_test_diag ("Calling server function succeeded"); + + return TRUE; +} + +static dbus_bool_t +run_test (const char *server_scope, const char *scope, const char *test_data_dir, RunTestFlags flags) +{ + DBusConnection *conn = NULL; + DBusError error; + DBusString server_address = _DBUS_STRING_INIT_INVALID; + DBusString address = _DBUS_STRING_INIT_INVALID; + DBusString session_parameter = _DBUS_STRING_INIT_INVALID; + dbus_bool_t result = FALSE; + TestMainContext *ctx; + _dbus_assert (test_data_dir); + + ctx = test_main_context_get (); + + dbus_error_init (&error); + + if (!_dbus_string_init (&server_address)) + oom (); + + if (!_dbus_string_init (&address)) + oom (); + + _dbus_test_diag ("run test"); + + if (*server_scope != '\0') + { + if (!_dbus_string_append_printf (&server_address, "autolaunch:scope=%s", server_scope)) + oom (); + } + else if (!_dbus_string_append_printf (&server_address, "autolaunch:")) + { + oom (); + } + + if (*scope != '\0') + { + if (!_dbus_string_append_printf (&address, "autolaunch:scope=%s", scope)) + oom (); + } + else if (!_dbus_string_append_printf (&address, "autolaunch:")) + { + oom (); + } + + if (!_dbus_string_init (&session_parameter)) + oom (); + + /* We haven't implemented any form of escaping quotes, + * but Windows doesn't allow filenames to contain quotes + * so it shouldn't matter. */ + _dbus_test_check (strchr (test_data_dir, '"') == NULL); + + _dbus_test_check (strchr (_dbus_string_get_const_data (&server_address), '"') == NULL); + + if (!_dbus_string_append_printf (&session_parameter, "\"--config-file=%s/%s\" \"--address=%s\"", test_data_dir, "valid-config-files/listen-autolaunch-win.conf", _dbus_string_get_const_data (&server_address))) + { + oom (); + } + + _dbus_test_win_autolaunch_set_command_line_parameter (_dbus_string_get_const_data (&session_parameter)); + + _dbus_test_diag ("Autolaunch handle initially %p", autolaunch_handle); + _dbus_test_win_set_autolaunch_handle_location (&autolaunch_handle); + + if (use_bus_get) + { + dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", _dbus_string_get_const_data (&address)); + _dbus_test_diag ("got env %s", getenv ("DBUS_SESSION_BUS_ADDRESS")); + conn = dbus_bus_get_private (DBUS_BUS_SESSION, &error); + dbus_connection_set_exit_on_disconnect (conn, FALSE); + } + else + { + conn = dbus_connection_open_private (_dbus_string_get_const_data (&address), &error); + } + + _dbus_test_diag ("After attempting to connect: autolaunch handle is %p", autolaunch_handle); + if (conn) + test_connection_setup (ctx, conn); + + result = check_results (conn, &server_address, &address, scope, flags, &error); + + if (conn) + { + _dbus_test_diag("Shutdown connection '%p'", conn); + test_connection_shutdown (ctx, conn); + dbus_connection_close (conn); + dbus_connection_unref (conn); + } + + _server_shutdown (conn, scope, -1, &error); + _dbus_test_diag ("server has been shut down"); + + _dbus_string_free (&address); + _dbus_string_free (&server_address); + _dbus_string_free (&session_parameter); + + test_main_context_unref (ctx); + + return result; +} + +static dbus_bool_t +run_test_okay (const char *scope, const char *test_data_dir) +{ + return run_test (scope, scope, test_data_dir, RUN_TEST_DEFAULT); +} + +static dbus_bool_t +_dbus_autolaunch_default_test (const char *test_data_dir) +{ + return run_test_okay ("", test_data_dir); +} + +static dbus_bool_t +_dbus_autolaunch_custom_scope_test (const char *test_data_dir) +{ + return run_test_okay ("123", test_data_dir); +} + +static dbus_bool_t +_dbus_autolaunch_install_path_scope_test (const char *test_data_dir) +{ + return run_test_okay ("*install-path", test_data_dir); +} + +static dbus_bool_t +_dbus_autolaunch_user_scope_test (const char *test_data_dir) +{ + return run_test_okay ("*user", test_data_dir); +} + +static dbus_bool_t +_dbus_autolaunch_loop_test (const char *test_data_dir, dbus_bool_t same_scope) +{ + int i; + int max = 10; + + for (i = 0; i < max; i++) + { + char s[2] = { i+'A', 0 }; + if (!run_test_okay (same_scope ? "A" : s, test_data_dir)) + _dbus_test_not_ok ("%d", max); + else + _dbus_test_ok ("%d", max); + if (add_wait_time) + _dbus_sleep_milliseconds (add_wait_time); + } + return TRUE; +} + +static dbus_bool_t +_dbus_autolaunch_same_scope_loop_test (const char *test_data_dir) +{ + return _dbus_autolaunch_loop_test (test_data_dir, TRUE); +} + +static dbus_bool_t +_dbus_autolaunch_different_scope_loop_test (const char *test_data_dir) +{ + return _dbus_autolaunch_loop_test (test_data_dir, FALSE); +} + +static DBusTestCase tests[] = { + { "default", _dbus_autolaunch_default_test }, + { "custom", _dbus_autolaunch_custom_scope_test }, + { "install-path", _dbus_autolaunch_install_path_scope_test }, + { "user", _dbus_autolaunch_user_scope_test }, + { "loop", _dbus_autolaunch_same_scope_loop_test }, + { "different-scope-loop", _dbus_autolaunch_different_scope_loop_test }, +}; + +int +main (int argc, + char **argv) +{ + return _dbus_test_main (argc, argv, _DBUS_N_ELEMENTS (tests), tests, + DBUS_TEST_FLAGS_CHECK_MEMORY_LEAKS, + NULL, NULL); +}