5 #if !defined(SPAWN_DEBUG) || defined(_MSC_VER)
8 #define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr)
37 #include "dbus-spawn.h"
38 #include "dbus-sysdeps.h"
39 #include "dbus-sysdeps-win.h"
40 #include "dbus-internals.h"
41 #include "dbus-test.h"
42 #include "dbus-protocol.h"
44 #define WIN32_LEAN_AND_MEAN
65 HANDLE start_sync_event;
66 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
68 HANDLE end_sync_event;
72 DBusSpawnChildSetupFunc child_setup;
85 DBusBabysitterFinishedFunc finished_cb;
100 #ifdef DBUS_ENABLE_VERBOSE_MODE
101 static int enabled = -1;
103 _dbus_trace_ref (
"DBusBabysitter", sitter, old_refcount, new_refcount, why,
104 "DBUS_BABYSITTER_TRACE", &enabled);
109 _dbus_babysitter_new (
void)
112 dbus_int32_t old_refcount;
120 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
123 if (sitter->start_sync_event ==
NULL)
129 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
131 if (sitter->end_sync_event ==
NULL)
138 sitter->child_handle =
NULL;
153 sitter->have_spawn_errno =
FALSE;
154 sitter->have_child_status =
FALSE;
168 dbus_int32_t old_refcount;
174 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
182 _dbus_verbose (
"Closing babysitter\n");
209 dbus_int32_t old_refcount;
216 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount-1, __FUNCTION__);
218 if (old_refcount == 1)
220 close_socket_to_babysitter (sitter);
222 if (sitter->socket_to_main.sock != INVALID_SOCKET)
225 sitter->socket_to_main.sock = INVALID_SOCKET;
229 if (sitter->argv !=
NULL)
231 for (i = 0; i < sitter->argc; i++)
232 if (sitter->argv[i] !=
NULL)
235 sitter->argv[i] =
NULL;
241 if (sitter->envp !=
NULL)
243 char **e = sitter->envp;
251 if (sitter->child_handle !=
NULL)
253 CloseHandle (sitter->child_handle);
254 sitter->child_handle =
NULL;
267 if (sitter->start_sync_event !=
NULL)
270 CloseHandle (sitter->start_sync_event);
271 sitter->start_sync_event =
NULL;
274 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
275 if (sitter->end_sync_event !=
NULL)
277 CloseHandle (sitter->end_sync_event);
278 sitter->end_sync_event =
NULL;
292 if (sitter->child_handle ==
NULL)
296 TerminateProcess (sitter->child_handle, 12345);
308 return (sitter->child_handle ==
NULL);
330 if (!sitter->have_child_status ||
331 sitter->child_status == STILL_ACTIVE)
334 *status = sitter->child_status;
356 if (sitter->have_spawn_errno)
358 char *emsg = _dbus_win_error_string (sitter->spawn_errno);
360 "Failed to execute program %s: %s",
362 _dbus_win_free_error_string (emsg);
364 else if (sitter->have_child_status)
368 "Process %s exited with status %d",
369 sitter->
log_name, sitter->child_status);
375 "Process %s exited, status unknown",
400 unsigned int condition,
416 close_socket_to_babysitter (sitter);
420 sitter->finished_cb !=
NULL)
422 sitter->finished_cb (sitter, sitter->finished_data);
423 sitter->finished_cb =
NULL;
431 protect_argv (
char **argv,
439 *new_argv =
dbus_malloc ((argc + 1) *
sizeof (
char *));
440 if (*new_argv ==
NULL)
443 for (i = 0; i < argc; i++)
444 (*new_argv)[i] =
NULL;
457 for (i = 0; i < argc; i++)
462 int need_dblquotes =
FALSE;
465 if (*p ==
' ' || *p ==
'\t')
466 need_dblquotes =
TRUE;
472 while (*pp && *pp ==
'\\')
481 q = (*new_argv)[i] =
dbus_malloc (len + need_dblquotes*2 + 1);
499 while (*pp && *pp ==
'\\')
513 (*new_argv)[argc] =
NULL;
521 compose_string (
char **strings,
char separator)
528 if (!strings || !strings[0])
530 for (i = 0; strings[i]; i++)
531 n += strlen (strings[i]) + 1;
534 buf = p = malloc (n);
537 for (i = 0; strings[i]; i++)
539 strcpy (p, strings[i]);
540 p += strlen (strings[i]);
551 build_commandline (
char **argv)
553 return compose_string (argv,
' ');
557 build_env_string (
char** envp)
559 return compose_string (envp,
'\0');
563 spawn_program (
char* name,
char** argv,
char** envp)
565 PROCESS_INFORMATION pi = {
NULL, 0, 0, 0 };
567 char *arg_string, *env_string;
572 arg_string = build_commandline (argv + 1);
576 arg_string = build_commandline (argv);
579 return INVALID_HANDLE_VALUE;
581 env_string = build_env_string(envp);
583 memset (&si, 0,
sizeof (si));
586 result = CreateProcessA (name, arg_string,
NULL,
NULL,
FALSE, 0,
590 (LPVOID)env_string,
NULL, &si, &pi);
596 return INVALID_HANDLE_VALUE;
598 CloseHandle (pi.hThread);
603 static DWORD __stdcall
604 babysitter (
void *parameter)
611 if (sitter->child_setup)
614 (*sitter->child_setup) (sitter->user_data);
617 _dbus_verbose (
"babysitter: spawning %s\n", sitter->
log_name);
620 handle = spawn_program (sitter->
log_name, sitter->argv, sitter->envp);
623 if (handle != INVALID_HANDLE_VALUE)
625 sitter->child_handle = handle;
629 sitter->child_handle =
NULL;
630 sitter->have_spawn_errno =
TRUE;
631 sitter->spawn_errno = GetLastError();
635 SetEvent (sitter->start_sync_event);
637 if (sitter->child_handle !=
NULL)
643 WaitForSingleObject (sitter->child_handle, INFINITE);
646 ret = GetExitCodeProcess (sitter->child_handle, &status);
649 sitter->child_status = status;
650 sitter->have_child_status =
TRUE;
653 CloseHandle (sitter->child_handle);
654 sitter->child_handle =
NULL;
657 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
658 SetEvent (sitter->end_sync_event);
662 send (sitter->socket_to_main.sock,
" ", 1, 0);
671 const char *log_name,
674 DBusSpawnChildSetupFunc child_setup,
679 HANDLE sitter_thread;
680 DWORD sitter_thread_id;
682 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
685 if (sitter_p !=
NULL)
689 sitter = _dbus_babysitter_new ();
692 _DBUS_SET_OOM (error);
696 sitter->child_setup = child_setup;
697 sitter->user_data = user_data;
702 _DBUS_SET_OOM (error);
711 _DBUS_SET_OOM (error);
717 &sitter->socket_to_main,
727 _DBUS_SET_OOM (error);
740 _DBUS_SET_OOM (error);
744 sitter->argc = protect_argv (argv, &sitter->argv);
745 if (sitter->argc == -1)
747 _DBUS_SET_OOM (error);
753 sitter_thread = (HANDLE) CreateThread (
NULL, 0, babysitter,
756 if (sitter_thread == 0)
760 "Failed to create new thread");
763 CloseHandle (sitter_thread);
766 WaitForSingleObject (sitter->start_sync_event, INFINITE);
769 if (sitter_p !=
NULL)
774 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
787 DBusBabysitterFinishedFunc finished,
790 sitter->finished_cb = finished;
791 sitter->finished_data = user_data;
794 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
797 get_test_exec (
const char *exe,
800 const char *dbus_test_exec;
804 if (dbus_test_exec ==
NULL)
805 dbus_test_exec = DBUS_TEST_EXEC;
811 dbus_test_exec, exe, DBUS_EXEEXT))
817 return _dbus_string_get_data (scratch_space);
820 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
825 if (sitter->child_handle ==
NULL)
828 WaitForSingleObject (sitter->end_sync_event, INFINITE);
832 check_spawn_nonexistent (
void *data)
844 argv[0] =
"/this/does/not/exist/32542sdgafgafdg";
849 _dbus_babysitter_block_for_child_exit (sitter);
858 _dbus_warn (
"Did not get an error launching nonexistent executable\n");
865 _dbus_warn (
"Not expecting error when launching nonexistent executable: %s: %s\n",
877 check_spawn_segfault (
void *data)
890 argv[0] = get_test_exec (
"test-segfault", &argv0);
902 _dbus_babysitter_block_for_child_exit (sitter);
913 _dbus_warn (
"Did not get an error launching segfaulting binary\n");
920 _dbus_warn (
"Not expecting error when launching segfaulting executable: %s: %s\n",
932 check_spawn_exit (
void *data)
945 argv[0] = get_test_exec (
"test-exit", &argv0);
957 _dbus_babysitter_block_for_child_exit (sitter);
968 _dbus_warn (
"Did not get an error launching binary that exited with failure code\n");
975 _dbus_warn (
"Not expecting error when launching exiting executable: %s: %s\n",
987 check_spawn_and_kill (
void *data)
1000 argv[0] = get_test_exec (
"test-sleep-forever", &argv0);
1002 if (argv[0] ==
NULL)
1014 _dbus_babysitter_block_for_child_exit (sitter);
1026 _dbus_warn (
"Did not get an error after killing spawned binary\n");
1033 _dbus_warn (
"Not expecting error when killing executable: %s: %s\n",
1045 _dbus_spawn_test (
const char *test_data_dir)
1047 if (!_dbus_test_oom_handling (
"spawn_nonexistent",
1048 check_spawn_nonexistent,
1055 if (getenv (
"DO_SEGFAULT_TEST"))
1056 if (!_dbus_test_oom_handling (
"spawn_segfault",
1057 check_spawn_segfault,
1061 if (!_dbus_test_oom_handling (
"spawn_exit",
1066 if (!_dbus_test_oom_handling (
"spawn_and_kill",
1067 check_spawn_and_kill,
dbus_bool_t dbus_error_has_name(const DBusError *error, const char *name)
Checks whether the error is set and has the given name.
An atomic integer safe to increment or decrement from multiple threads.
const char * message
public error message field
DBusWatch * _dbus_watch_new(DBusPollable fd, unsigned int flags, dbus_bool_t enabled, DBusWatchHandler handler, void *data, DBusFreeFunction free_data_function)
Creates a new DBusWatch.
Implementation of DBusWatch.
#define NULL
A null pointer, defined appropriately for C or C++.
#define DBUS_ERROR_SPAWN_EXEC_FAILED
While starting a new process, the exec() call failed.
void(* DBusFreeFunction)(void *memory)
The type of a function which frees a block of memory.
void(* DBusRemoveWatchFunction)(DBusWatch *watch, void *data)
Called when libdbus no longer needs a watch to be monitored by the main loop.
void dbus_free(void *memory)
Frees a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
dbus_bool_t _dbus_socketpair(DBusSocket *fd1, DBusSocket *fd2, dbus_bool_t blocking, DBusError *error)
Creates pair of connect sockets (as in socketpair()).
#define DBUS_ERROR_SPAWN_CHILD_EXITED
While starting a new process, the child exited with a status code.
#define _dbus_assert(condition)
Aborts with an error message if the condition is false.
void dbus_error_free(DBusError *error)
Frees an error that's been set (or just initialized), then reinitializes the error as in dbus_error_i...
void _dbus_watch_list_free(DBusWatchList *watch_list)
Frees a DBusWatchList.
DBusWatchList * _dbus_watch_list_new(void)
Creates a new watch list.
dbus_bool_t _dbus_string_init(DBusString *str)
Initializes a string.
dbus_bool_t _dbus_close_socket(DBusSocket fd, DBusError *error)
Closes a socket.
dbus_bool_t _dbus_babysitter_get_child_exited(DBusBabysitter *sitter)
Checks whether the child has exited, without blocking.
dbus_bool_t _dbus_watch_list_set_functions(DBusWatchList *watch_list, DBusAddWatchFunction add_function, DBusRemoveWatchFunction remove_function, DBusWatchToggledFunction toggled_function, void *data, DBusFreeFunction free_data_function)
Sets the watch functions.
dbus_bool_t(* DBusAddWatchFunction)(DBusWatch *watch, void *data)
Called when libdbus needs a new watch to be monitored by the main loop.
DBusWatchList * watches
Watches.
void * dbus_malloc(size_t bytes)
Allocates the given number of bytes, as with standard malloc().
#define dbus_new0(type, count)
Safe macro for using dbus_malloc0().
dbus_uint32_t dbus_bool_t
A boolean, valid values are TRUE and FALSE.
DBusWatch * sitter_watch
Sitter pipe watch.
dbus_bool_t _dbus_babysitter_get_child_exit_status(DBusBabysitter *sitter, int *status)
Gets the exit status of the child.
void _dbus_babysitter_kill_child(DBusBabysitter *sitter)
Blocks until the babysitter process gives us the PID of the spawned grandchild, then kills the spawne...
Babysitter implementation details.
dbus_bool_t _dbus_spawn_async_with_babysitter(DBusBabysitter **sitter_p, const char *log_name, char **argv, char **env, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error)
Spawns a new process.
void _dbus_warn(const char *format,...)
Prints a warning message to stderr.
dbus_int32_t _dbus_atomic_inc(DBusAtomic *atomic)
Atomically increments an integer.
dbus_bool_t _dbus_string_append_printf(DBusString *str, const char *format,...)
Appends a printf-style formatted string to the DBusString.
void _dbus_watch_invalidate(DBusWatch *watch)
Clears the file descriptor from a now-invalid watch object so that no one tries to use it...
dbus_bool_t _dbus_babysitter_set_watch_functions(DBusBabysitter *sitter, DBusAddWatchFunction add_function, DBusRemoveWatchFunction remove_function, DBusWatchToggledFunction toggled_function, void *data, DBusFreeFunction free_data_function)
Sets watch functions to notify us when the babysitter object needs to read/write file descriptors...
Object representing an exception.
void dbus_set_error(DBusError *error, const char *name, const char *format,...)
Assigns an error name and message to a DBusError.
void _dbus_string_free(DBusString *str)
Frees a string created by _dbus_string_init().
#define TRUE
Expands to "1".
#define _dbus_assert_not_reached(explanation)
Aborts with an error message if called.
#define DBUS_ERROR_FAILED
A generic error; "something went wrong" - see the error message for more.
dbus_bool_t _dbus_watch_list_add_watch(DBusWatchList *watch_list, DBusWatch *watch)
Adds a new watch to the watch list, invoking the application DBusAddWatchFunction if appropriate...
const char * name
public error name field
DBusWatchList implementation details.
void _dbus_watch_list_remove_watch(DBusWatchList *watch_list, DBusWatch *watch)
Removes a watch from the watch list, invoking the application's DBusRemoveWatchFunction if appropriat...
void _dbus_babysitter_unref(DBusBabysitter *sitter)
Decrement the reference count on the babysitter object.
void dbus_error_init(DBusError *error)
Initializes a DBusError structure.
dbus_int32_t _dbus_atomic_dec(DBusAtomic *atomic)
Atomically decrement an integer.
#define DBUS_ERROR_SPAWN_FORK_FAILED
While starting a new process, the fork() call failed.
char * log_name
the name under which to log messages about this process being spawned
void _dbus_watch_unref(DBusWatch *watch)
Decrements the reference count of a DBusWatch object and finalizes the object if the count reaches ze...
#define DBUS_ERROR_NO_MEMORY
There was not enough memory to complete an operation.
#define FALSE
Expands to "0".
void dbus_set_error_const(DBusError *error, const char *name, const char *message)
Assigns an error name and message to a DBusError.
void(* DBusWatchToggledFunction)(DBusWatch *watch, void *data)
Called when dbus_watch_get_enabled() may return a different value than it did before.
DBusSocket socket_to_babysitter
Connection to the babysitter process.
char * _dbus_strdup(const char *str)
Duplicates a string.
void _dbus_babysitter_set_child_exit_error(DBusBabysitter *sitter, DBusError *error)
Sets the DBusError with an explanation of why the spawned child process exited (on a signal...
const char * _dbus_getenv(const char *varname)
Wrapper for getenv().
dbus_bool_t dbus_error_is_set(const DBusError *error)
Checks whether an error occurred (the error is set).
DBusBabysitter * _dbus_babysitter_ref(DBusBabysitter *sitter)
Increment the reference count on the babysitter object.