From 7c3a5bd7dd3df2ab34a9539fd503e9ac0ce697fd Mon Sep 17 00:00:00 2001 From: Ralf Habacker Date: Wed, 18 Nov 2015 23:35:46 +0100 Subject: [PATCH] Add windows implementation of dbus-run-session helper tool. The implementation requires a dbus-daemon configured with 'autolaunch:scope=*install-path' listen address, which let the client find the session bus address from shared memory. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=92899 --- cmake/tools/CMakeLists.txt | 3 + dbus/dbus-spawn-win.c | 19 ++- dbus/dbus-spawn.h | 3 + dbus/dbus-sysdeps-win.h | 1 + tools/dbus-run-session.c | 331 ++++++++++++++++++++++++++++----------------- 5 files changed, 229 insertions(+), 128 deletions(-) diff --git a/cmake/tools/CMakeLists.txt b/cmake/tools/CMakeLists.txt index 6a2e999..75cc240 100644 --- a/cmake/tools/CMakeLists.txt +++ b/cmake/tools/CMakeLists.txt @@ -77,5 +77,8 @@ add_executable(dbus-monitor ${dbus_monitor_SOURCES}) target_link_libraries(dbus-monitor ${DBUS_LIBRARIES}) install_targets(/bin dbus-monitor ) +add_executable(dbus-run-session ../../tools/dbus-run-session.c) +target_link_libraries(dbus-run-session ${DBUS_LIBRARIES} dbus-internal) + # create the /var/lib/dbus directory for dbus-uuidgen install(DIRECTORY DESTINATION var/lib/dbus) diff --git a/dbus/dbus-spawn-win.c b/dbus/dbus-spawn-win.c index c58bf3c..054c3f2 100644 --- a/dbus/dbus-spawn-win.c +++ b/dbus/dbus-spawn-win.c @@ -538,8 +538,9 @@ build_env_string (char** envp) return compose_string (envp, '\0'); } -static HANDLE -spawn_program (char* name, char** argv, char** envp) + +HANDLE +_dbus_spawn_program (const char* name, char** argv, char** envp) { PROCESS_INFORMATION pi = { NULL, 0, 0, 0 }; STARTUPINFOA si; @@ -561,6 +562,15 @@ spawn_program (char* name, char** argv, char** envp) memset (&si, 0, sizeof (si)); si.cb = sizeof (si); + +#ifdef DBUS_ENABLE_VERBOSE_MODE + { + char *s = compose_string (envp, ';'); + _dbus_verbose ("spawning '%s'' with args: '%s' env: '%s'\n", name, arg_string, s); + free (s); + } +#endif + #ifdef DBUS_WINCE result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0, #else @@ -578,6 +588,11 @@ spawn_program (char* name, char** argv, char** envp) return pi.hProcess; } +static HANDLE +spawn_program (char* name, char** argv, char** envp) +{ + return _dbus_spawn_program (name, argv, envp); +} static DWORD __stdcall babysitter (void *parameter) diff --git a/dbus/dbus-spawn.h b/dbus/dbus-spawn.h index e6baae9..71777f7 100644 --- a/dbus/dbus-spawn.h +++ b/dbus/dbus-spawn.h @@ -63,6 +63,9 @@ dbus_bool_t _dbus_babysitter_set_watch_functions (DBusBabysitter *si void *data, DBusFreeFunction free_data_function); +HANDLE +_dbus_spawn_program (const char* name, char** argv, char** envp); + DBUS_END_DECLS #endif /* DBUS_SPAWN_H */ diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h index 0b5d8f0..f77bde1 100644 --- a/dbus/dbus-sysdeps-win.h +++ b/dbus/dbus-sysdeps-win.h @@ -44,6 +44,7 @@ DBUS_PRIVATE_EXPORT const char* _dbus_win_error_from_last_error (void); dbus_bool_t _dbus_win_startup_winsock (void); +DBUS_PRIVATE_EXPORT void _dbus_win_warn_win_error (const char *message, unsigned long code); diff --git a/tools/dbus-run-session.c b/tools/dbus-run-session.c index 105ab3b..3a9dd49 100644 --- a/tools/dbus-run-session.c +++ b/tools/dbus-run-session.c @@ -4,6 +4,7 @@ * Copyright © 2003-2006 Red Hat, Inc. * Copyright © 2006 Thiago Macieira * Copyright © 2011-2012 Nokia Corporation + * Copyright © 2015 Ralf Habacker * * Licensed under the Academic Free License version 2.1 * @@ -33,10 +34,12 @@ #include #include +#ifdef DBUS_UNIX #include #include - +#endif #include "dbus/dbus.h" +#include #define MAX_ADDR_LEN 512 #define PIPE_READ_END 0 @@ -88,6 +91,7 @@ version (void) "Copyright (C) 2003-2006 Red Hat, Inc.\n" "Copyright (C) 2006 Thiago Macieira\n" "Copyright © 2011-2012 Nokia Corporation\n" + "Copyright © 2015 Ralf Habacker\n" "\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", @@ -102,6 +106,7 @@ oom (void) exit (1); } +#ifndef DBUS_WIN typedef enum { READ_STATUS_OK, /**< Read succeeded */ @@ -200,20 +205,215 @@ exec_app (int prog_arg, char **argv) strerror (errno)); exit (1); } +#endif + +static int +run_session(const char *dbus_daemon, const char *config_file, const char *bus_address, char **argv, int prog_arg) +{ +#ifdef DBUS_UNIX + pid_t bus_pid; + pid_t app_pid; + int bus_address_pipe[2] = { 0, 0 }; + + if (pipe (bus_address_pipe) < 0) + { + fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno)); + return 127; + } + + bus_pid = fork (); + + if (bus_pid < 0) + { + fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno)); + return 127; + } + + if (bus_pid == 0) + { + /* child */ + exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file); + /* not reached */ + return 127; + } + + close (bus_address_pipe[PIPE_WRITE_END]); + + switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN)) + { + case READ_STATUS_OK: + break; + + case READ_STATUS_EOF: + fprintf (stderr, "%s: EOF reading address from bus daemon\n", me); + return 127; + break; + + case READ_STATUS_ERROR: + fprintf (stderr, "%s: error reading address from bus daemon: %s\n", + me, strerror (errno)); + return 127; + break; + } + + close (bus_address_pipe[PIPE_READ_END]); + + if (!dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address) || + !dbus_setenv ("DBUS_SESSION_BUS_PID", NULL) || + !dbus_setenv ("DBUS_SESSION_BUS_WINDOWID", NULL) || + !dbus_setenv ("DBUS_STARTER_ADDRESS", NULL) || + !dbus_setenv ("DBUS_STARTER_BUS_TYPE", NULL)) + oom (); + + app_pid = fork (); + + if (app_pid < 0) + { + fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno)); + return 127; + } + + if (app_pid == 0) + { + /* child */ + exec_app (prog_arg, argv); + /* not reached */ + return 127; + } + + while (1) + { + int child_status; + pid_t child_pid = waitpid (-1, &child_status, 0); + + if (child_pid == (pid_t) -1) + { + int errsv = errno; + + if (errsv == EINTR) + continue; + + /* shouldn't happen: the only other documented errors are ECHILD, + * which shouldn't happen because we terminate when all our children + * have died, and EINVAL, which would indicate programming error */ + fprintf (stderr, "%s: waitpid() failed: %s\n", me, strerror (errsv)); + return 127; + } + else if (child_pid == bus_pid) + { + /* no need to kill it, now */ + bus_pid = 0; + + if (WIFEXITED (child_status)) + fprintf (stderr, "%s: dbus-daemon exited with code %d\n", + me, WEXITSTATUS (child_status)); + else if (WIFSIGNALED (child_status)) + fprintf (stderr, "%s: dbus-daemon terminated by signal %d\n", + me, WTERMSIG (child_status)); + else + fprintf (stderr, "%s: dbus-daemon died or something\n", me); + } + else if (child_pid == app_pid) + { + if (bus_pid != 0) + kill (bus_pid, SIGTERM); + + if (WIFEXITED (child_status)) + return WEXITSTATUS (child_status); + + /* if it died from a signal, behave like sh(1) */ + if (WIFSIGNALED (child_status)) + return 128 + WTERMSIG (child_status); + + /* I give up (this should never be reached) */ + fprintf (stderr, "%s: child process died or something\n", me); + return 127; + } + else + { + fprintf (stderr, "%s: ignoring unknown child process %ld\n", me, + (long) child_pid); + } + } + + return 0; +#else + char *argv2[5]; + int ret = 127; + HANDLE server_handle; + HANDLE app_handle; + DWORD exit_code; + DBusString string[4]; + char **env = NULL; + + _dbus_string_init (&string[0]); + _dbus_string_init (&string[1]); + _dbus_string_init (&string[2]); + _dbus_string_init (&string[3]); + + /* run dbus daemon */ + _dbus_string_append_printf (&string[3], "autolaunch:scope=dbus-tmp-session-%d", getpid()); + _dbus_string_append_printf (&string[0], "%s", dbus_daemon); + _dbus_string_append_printf (&string[1], "--config-file=%s", config_file); + _dbus_string_append_printf (&string[2], "--address=%s", _dbus_string_get_const_data (&string[3])); + argv2[0] = _dbus_string_get_data (&string[0]); + argv2[1] = _dbus_string_get_data (&string[1]); + argv2[2] = _dbus_string_get_data (&string[2]); + argv2[3] = 0; + + server_handle = _dbus_spawn_program (dbus_daemon, argv2, 0); + if (!server_handle) + { + _dbus_win_warn_win_error ("Could not start dbus daemon", GetLastError ()); + goto out; + } + + /* run app */ + _dbus_string_append_printf (&string[0], "DBUS_SESSION_BUS_ADDRESS=%s", _dbus_string_get_data (&string[3])); + + env = _dbus_get_environment (); + if (!_dbus_string_array_replace ((const char **) env, "DBUS_SESSION_BUS_ADDRESS", _dbus_string_get_data (&string[0]))) + if (!_dbus_string_array_append (env, _dbus_string_get_data (&string[0]))) + oom(); + + app_handle = _dbus_spawn_program (argv[prog_arg], argv + prog_arg, env); + if (!app_handle) + { + _dbus_win_warn_win_error ("child process died or something", GetLastError ()); + goto out; + } + + WaitForSingleObject (app_handle, INFINITE); + if (!GetExitCodeProcess (app_handle, &exit_code)) + { + _dbus_win_warn_win_error ("could not fetch exit code", GetLastError ()); + goto out; + } + ret = exit_code; + +out: + TerminateProcess (server_handle, 0); + CloseHandle (server_handle); + CloseHandle (app_handle); + _dbus_string_free (&string[0]); + _dbus_string_free (&string[1]); + _dbus_string_free (&string[2]); + _dbus_string_free (&string[3]); + dbus_free_string_array (env); + return ret; +#endif +} int main (int argc, char **argv) { int prog_arg = 0; - int bus_address_pipe[2] = { 0, 0 }; const char *config_file = NULL; const char *dbus_daemon = NULL; char bus_address[MAX_ADDR_LEN] = { 0 }; const char *prev_arg = NULL; int i = 1; int requires_arg = 0; - pid_t bus_pid; - pid_t app_pid; while (i < argc) { @@ -339,126 +539,5 @@ main (int argc, char **argv) if (dbus_daemon == NULL) dbus_daemon = "dbus-daemon"; - if (pipe (bus_address_pipe) < 0) - { - fprintf (stderr, "%s: failed to create pipe: %s\n", me, strerror (errno)); - return 127; - } - - bus_pid = fork (); - - if (bus_pid < 0) - { - fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno)); - return 127; - } - - if (bus_pid == 0) - { - /* child */ - exec_dbus_daemon (dbus_daemon, bus_address_pipe, config_file); - /* not reached */ - return 127; - } - - close (bus_address_pipe[PIPE_WRITE_END]); - - switch (read_line (bus_address_pipe[PIPE_READ_END], bus_address, MAX_ADDR_LEN)) - { - case READ_STATUS_OK: - break; - - case READ_STATUS_EOF: - fprintf (stderr, "%s: EOF reading address from bus daemon\n", me); - return 127; - break; - - case READ_STATUS_ERROR: - fprintf (stderr, "%s: error reading address from bus daemon: %s\n", - me, strerror (errno)); - return 127; - break; - } - - close (bus_address_pipe[PIPE_READ_END]); - - if (!dbus_setenv ("DBUS_SESSION_BUS_ADDRESS", bus_address) || - !dbus_setenv ("DBUS_SESSION_BUS_PID", NULL) || - !dbus_setenv ("DBUS_SESSION_BUS_WINDOWID", NULL) || - !dbus_setenv ("DBUS_STARTER_ADDRESS", NULL) || - !dbus_setenv ("DBUS_STARTER_BUS_TYPE", NULL)) - oom (); - - app_pid = fork (); - - if (app_pid < 0) - { - fprintf (stderr, "%s: failed to fork: %s\n", me, strerror (errno)); - return 127; - } - - if (app_pid == 0) - { - /* child */ - exec_app (prog_arg, argv); - /* not reached */ - return 127; - } - - while (1) - { - int child_status; - pid_t child_pid = waitpid (-1, &child_status, 0); - - if (child_pid == (pid_t) -1) - { - int errsv = errno; - - if (errsv == EINTR) - continue; - - /* shouldn't happen: the only other documented errors are ECHILD, - * which shouldn't happen because we terminate when all our children - * have died, and EINVAL, which would indicate programming error */ - fprintf (stderr, "%s: waitpid() failed: %s\n", me, strerror (errsv)); - return 127; - } - else if (child_pid == bus_pid) - { - /* no need to kill it, now */ - bus_pid = 0; - - if (WIFEXITED (child_status)) - fprintf (stderr, "%s: dbus-daemon exited with code %d\n", - me, WEXITSTATUS (child_status)); - else if (WIFSIGNALED (child_status)) - fprintf (stderr, "%s: dbus-daemon terminated by signal %d\n", - me, WTERMSIG (child_status)); - else - fprintf (stderr, "%s: dbus-daemon died or something\n", me); - } - else if (child_pid == app_pid) - { - if (bus_pid != 0) - kill (bus_pid, SIGTERM); - - if (WIFEXITED (child_status)) - return WEXITSTATUS (child_status); - - /* if it died from a signal, behave like sh(1) */ - if (WIFSIGNALED (child_status)) - return 128 + WTERMSIG (child_status); - - /* I give up (this should never be reached) */ - fprintf (stderr, "%s: child process died or something\n", me); - return 127; - } - else - { - fprintf (stderr, "%s: ignoring unknown child process %ld\n", me, - (long) child_pid); - } - } - - return 0; + return run_session (dbus_daemon, config_file, bus_address, argv, prog_arg); } -- 1.8.4.5