dbus/dbus/dbus-spawn-win.c
Chengwei Yang ec6ea1a6a8 DBusBabysitter: change executable to log_name
DBusBabysitter->executable is defined as executable name to use in error
messages. However, if servicehelper used, then the executable name is
servicehelper. It's not much help because we couldn't figure out which
service we're trying to activated if error happens.

In the following patch, we'll use service name to be activated as the
child log identifier and add a parameter to
_dbus_spawn_async_with_babysitter() to pass the log identifier.  Since
this is not the case in test, so executable changed to log_name.

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=68559
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
2013-11-01 12:40:41 +00:00

1046 lines
25 KiB
C

#include <config.h>
//#define SPAWN_DEBUG
#if !defined(SPAWN_DEBUG) || defined(_MSC_VER)
#define PING()
#else
#define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr)
#endif
#include <stdio.h>
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* dbus-spawn-win32.c Wrapper around g_spawn
*
* Copyright (C) 2002, 2003, 2004 Red Hat, Inc.
* Copyright (C) 2003 CodeFactory AB
* Copyright (C) 2005 Novell, Inc.
*
* Licensed under the Academic Free License version 2.1
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "dbus-spawn.h"
#include "dbus-sysdeps.h"
#include "dbus-sysdeps-win.h"
#include "dbus-internals.h"
#include "dbus-test.h"
#include "dbus-protocol.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//#define STRICT
//#include <windows.h>
//#undef STRICT
#include <winsock2.h>
#undef interface
#include <stdlib.h>
#ifndef DBUS_WINCE
#include <process.h>
#endif
/**
* Babysitter implementation details
*/
struct DBusBabysitter
{
int refcount;
HANDLE start_sync_event;
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
HANDLE end_sync_event;
#endif
char *log_name;
DBusSpawnChildSetupFunc child_setup;
void *user_data;
int argc;
char **argv;
char **envp;
HANDLE child_handle;
int socket_to_babysitter; /* Connection to the babysitter thread */
int socket_to_main;
DBusWatchList *watches;
DBusWatch *sitter_watch;
DBusBabysitterFinishedFunc finished_cb;
void *finished_data;
dbus_bool_t have_spawn_errno;
int spawn_errno;
dbus_bool_t have_child_status;
int child_status;
};
static DBusBabysitter*
_dbus_babysitter_new (void)
{
DBusBabysitter *sitter;
sitter = dbus_new0 (DBusBabysitter, 1);
if (sitter == NULL)
return NULL;
sitter->refcount = 1;
sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
if (sitter->start_sync_event == NULL)
{
_dbus_babysitter_unref (sitter);
return NULL;
}
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
if (sitter->end_sync_event == NULL)
{
_dbus_babysitter_unref (sitter);
return NULL;
}
#endif
sitter->child_handle = NULL;
sitter->socket_to_babysitter = sitter->socket_to_main = -1;
sitter->argc = 0;
sitter->argv = NULL;
sitter->envp = NULL;
sitter->watches = _dbus_watch_list_new ();
if (sitter->watches == NULL)
{
_dbus_babysitter_unref (sitter);
return NULL;
}
sitter->have_spawn_errno = FALSE;
sitter->have_child_status = FALSE;
return sitter;
}
/**
* Increment the reference count on the babysitter object.
*
* @param sitter the babysitter
* @returns the babysitter
*/
DBusBabysitter *
_dbus_babysitter_ref (DBusBabysitter *sitter)
{
PING();
_dbus_assert (sitter != NULL);
_dbus_assert (sitter->refcount > 0);
sitter->refcount += 1;
return sitter;
}
static void
close_socket_to_babysitter (DBusBabysitter *sitter)
{
_dbus_verbose ("Closing babysitter\n");
if (sitter->sitter_watch != NULL)
{
_dbus_assert (sitter->watches != NULL);
_dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
if (sitter->socket_to_babysitter != -1)
{
_dbus_close_socket (sitter->socket_to_babysitter, NULL);
sitter->socket_to_babysitter = -1;
}
}
/**
* Decrement the reference count on the babysitter object.
*
* @param sitter the babysitter
*/
void
_dbus_babysitter_unref (DBusBabysitter *sitter)
{
int i;
PING();
_dbus_assert (sitter != NULL);
_dbus_assert (sitter->refcount > 0);
sitter->refcount -= 1;
if (sitter->refcount == 0)
{
close_socket_to_babysitter (sitter);
if (sitter->socket_to_main != -1)
{
_dbus_close_socket (sitter->socket_to_main, NULL);
sitter->socket_to_main = -1;
}
PING();
if (sitter->argv != NULL)
{
for (i = 0; i < sitter->argc; i++)
if (sitter->argv[i] != NULL)
{
dbus_free (sitter->argv[i]);
sitter->argv[i] = NULL;
}
dbus_free (sitter->argv);
sitter->argv = NULL;
}
if (sitter->envp != NULL)
{
char **e = sitter->envp;
while (*e)
dbus_free (*e++);
dbus_free (sitter->envp);
sitter->envp = NULL;
}
if (sitter->child_handle != NULL)
{
CloseHandle (sitter->child_handle);
sitter->child_handle = NULL;
}
if (sitter->sitter_watch)
{
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
}
if (sitter->watches)
_dbus_watch_list_free (sitter->watches);
if (sitter->start_sync_event != NULL)
{
PING();
CloseHandle (sitter->start_sync_event);
sitter->start_sync_event = NULL;
}
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
if (sitter->end_sync_event != NULL)
{
CloseHandle (sitter->end_sync_event);
sitter->end_sync_event = NULL;
}
#endif
dbus_free (sitter->log_name);
dbus_free (sitter);
}
}
void
_dbus_babysitter_kill_child (DBusBabysitter *sitter)
{
PING();
if (sitter->child_handle == NULL)
return; /* child is already dead, or we're so hosed we'll never recover */
PING();
TerminateProcess (sitter->child_handle, 12345);
}
/**
* Checks whether the child has exited, without blocking.
*
* @param sitter the babysitter
*/
dbus_bool_t
_dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
{
PING();
return (sitter->child_handle == NULL);
}
/**
* Gets the exit status of the child. We do this so implementation specific
* detail is not cluttering up dbus, for example the system launcher code.
* This can only be called if the child has exited, i.e. call
* _dbus_babysitter_get_child_exited(). It returns FALSE if the child
* did not return a status code, e.g. because the child was signaled
* or we failed to ever launch the child in the first place.
*
* @param sitter the babysitter
* @param status the returned status code
* @returns #FALSE on failure
*/
dbus_bool_t
_dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
int *status)
{
if (!_dbus_babysitter_get_child_exited (sitter))
_dbus_assert_not_reached ("Child has not exited");
if (!sitter->have_child_status ||
sitter->child_status == STILL_ACTIVE)
return FALSE;
*status = sitter->child_status;
return TRUE;
}
/**
* Sets the #DBusError with an explanation of why the spawned
* child process exited (on a signal, or whatever). If
* the child process has not exited, does nothing (error
* will remain unset).
*
* @param sitter the babysitter
* @param error an error to fill in
*/
void
_dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
DBusError *error)
{
PING();
if (!_dbus_babysitter_get_child_exited (sitter))
return;
PING();
if (sitter->have_spawn_errno)
{
char *emsg = _dbus_win_error_string (sitter->spawn_errno);
dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
"Failed to execute program %s: %s",
sitter->log_name, emsg);
_dbus_win_free_error_string (emsg);
}
else if (sitter->have_child_status)
{
PING();
dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
"Process %s exited with status %d",
sitter->log_name, sitter->child_status);
}
else
{
PING();
dbus_set_error (error, DBUS_ERROR_FAILED,
"Process %s exited, status unknown",
sitter->log_name);
}
PING();
}
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)
{
PING();
return _dbus_watch_list_set_functions (sitter->watches,
add_function,
remove_function,
toggled_function,
data,
free_data_function);
}
static dbus_bool_t
handle_watch (DBusWatch *watch,
unsigned int condition,
void *data)
{
DBusBabysitter *sitter = data;
/* On Unix dbus-spawn uses a babysitter *process*, thus it has to
* actually send the exit statuses, error codes and whatnot through
* sockets and/or pipes. On Win32, the babysitter is jus a thread,
* so it can set the status fields directly in the babysitter struct
* just fine. The socket pipe is used just so we can watch it with
* select(), as soon as anything is written to it we know that the
* babysitter thread has recorded the status in the babysitter
* struct.
*/
PING();
close_socket_to_babysitter (sitter);
PING();
if (_dbus_babysitter_get_child_exited (sitter) &&
sitter->finished_cb != NULL)
{
sitter->finished_cb (sitter, sitter->finished_data);
sitter->finished_cb = NULL;
}
return TRUE;
}
/* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
static int
protect_argv (char **argv,
char ***new_argv)
{
int i;
int argc = 0;
while (argv[argc])
++argc;
*new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
if (*new_argv == NULL)
return -1;
for (i = 0; i < argc; i++)
(*new_argv)[i] = NULL;
/* Quote each argv element if necessary, so that it will get
* reconstructed correctly in the C runtime startup code. Note that
* the unquoting algorithm in the C runtime is really weird, and
* rather different than what Unix shells do. See stdargv.c in the C
* runtime sources (in the Platform SDK, in src/crt).
*
* Note that an new_argv[0] constructed by this function should
* *not* be passed as the filename argument to a spawn* or exec*
* family function. That argument should be the real file name
* without any quoting.
*/
for (i = 0; i < argc; i++)
{
char *p = argv[i];
char *q;
int len = 0;
int need_dblquotes = FALSE;
while (*p)
{
if (*p == ' ' || *p == '\t')
need_dblquotes = TRUE;
else if (*p == '"')
len++;
else if (*p == '\\')
{
char *pp = p;
while (*pp && *pp == '\\')
pp++;
if (*pp == '"')
len++;
}
len++;
p++;
}
q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
if (q == NULL)
return -1;
p = argv[i];
if (need_dblquotes)
*q++ = '"';
while (*p)
{
if (*p == '"')
*q++ = '\\';
else if (*p == '\\')
{
char *pp = p;
while (*pp && *pp == '\\')
pp++;
if (*pp == '"')
*q++ = '\\';
}
*q++ = *p;
p++;
}
if (need_dblquotes)
*q++ = '"';
*q++ = '\0';
/* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
}
(*new_argv)[argc] = NULL;
return argc;
}
/* From GPGME, relicensed by g10 Code GmbH. */
static char *
compose_string (char **strings, char separator)
{
int i;
int n = 0;
char *buf;
char *p;
if (!strings || !strings[0])
return 0;
for (i = 0; strings[i]; i++)
n += strlen (strings[i]) + 1;
n++;
buf = p = malloc (n);
if (!buf)
return NULL;
for (i = 0; strings[i]; i++)
{
strcpy (p, strings[i]);
p += strlen (strings[i]);
*(p++) = separator;
}
p--;
*(p++) = '\0';
*p = '\0';
return buf;
}
static char *
build_commandline (char **argv)
{
return compose_string (argv, ' ');
}
static char *
build_env_string (char** envp)
{
return compose_string (envp, '\0');
}
static HANDLE
spawn_program (char* name, char** argv, char** envp)
{
PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
STARTUPINFOA si;
char *arg_string, *env_string;
BOOL result;
#ifdef DBUS_WINCE
if (argv && argv[0])
arg_string = build_commandline (argv + 1);
else
arg_string = NULL;
#else
arg_string = build_commandline (argv);
#endif
if (!arg_string)
return INVALID_HANDLE_VALUE;
env_string = build_env_string(envp);
memset (&si, 0, sizeof (si));
si.cb = sizeof (si);
#ifdef DBUS_WINCE
result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
#else
result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
#endif
(LPVOID)env_string, NULL, &si, &pi);
free (arg_string);
if (env_string)
free (env_string);
if (!result)
return INVALID_HANDLE_VALUE;
CloseHandle (pi.hThread);
return pi.hProcess;
}
static DWORD __stdcall
babysitter (void *parameter)
{
DBusBabysitter *sitter = (DBusBabysitter *) parameter;
PING();
_dbus_babysitter_ref (sitter);
if (sitter->child_setup)
{
PING();
(*sitter->child_setup) (sitter->user_data);
}
_dbus_verbose ("babysitter: spawning %s\n", sitter->log_name);
PING();
sitter->child_handle = spawn_program (sitter->log_name,
sitter->argv, sitter->envp);
PING();
if (sitter->child_handle == (HANDLE) -1)
{
sitter->child_handle = NULL;
sitter->have_spawn_errno = TRUE;
sitter->spawn_errno = GetLastError();
}
PING();
SetEvent (sitter->start_sync_event);
if (sitter->child_handle != NULL)
{
int ret;
DWORD status;
PING();
WaitForSingleObject (sitter->child_handle, INFINITE);
PING();
ret = GetExitCodeProcess (sitter->child_handle, &status);
sitter->child_status = status;
sitter->have_child_status = TRUE;
CloseHandle (sitter->child_handle);
sitter->child_handle = NULL;
}
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
SetEvent (sitter->end_sync_event);
#endif
PING();
send (sitter->socket_to_main, " ", 1, 0);
_dbus_babysitter_unref (sitter);
return 0;
}
dbus_bool_t
_dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
const char *log_name,
char **argv,
char **envp,
DBusSpawnChildSetupFunc child_setup,
void *user_data,
DBusError *error)
{
DBusBabysitter *sitter;
HANDLE sitter_thread;
DWORD sitter_thread_id;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
_dbus_assert (argv[0] != NULL);
*sitter_p = NULL;
PING();
sitter = _dbus_babysitter_new ();
if (sitter == NULL)
{
_DBUS_SET_OOM (error);
return FALSE;
}
sitter->child_setup = child_setup;
sitter->user_data = user_data;
sitter->log_name = _dbus_strdup (log_name);
if (sitter->log_name == NULL && log_name != NULL)
{
_DBUS_SET_OOM (error);
goto out0;
}
if (sitter->log_name == NULL)
sitter->log_name = _dbus_strdup (argv[0]);
if (sitter->log_name == NULL)
{
_DBUS_SET_OOM (error);
goto out0;
}
PING();
if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
&sitter->socket_to_main,
FALSE, error))
goto out0;
sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
DBUS_WATCH_READABLE,
TRUE, handle_watch, sitter, NULL);
PING();
if (sitter->sitter_watch == NULL)
{
_DBUS_SET_OOM (error);
goto out0;
}
PING();
if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
{
/* we need to free it early so the destructor won't try to remove it
* without it having been added, which DBusLoop doesn't allow */
_dbus_watch_invalidate (sitter->sitter_watch);
_dbus_watch_unref (sitter->sitter_watch);
sitter->sitter_watch = NULL;
_DBUS_SET_OOM (error);
goto out0;
}
sitter->argc = protect_argv (argv, &sitter->argv);
if (sitter->argc == -1)
{
_DBUS_SET_OOM (error);
goto out0;
}
sitter->envp = envp;
PING();
sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
sitter, 0, &sitter_thread_id);
if (sitter_thread == 0)
{
PING();
dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
"Failed to create new thread");
goto out0;
}
CloseHandle (sitter_thread);
PING();
WaitForSingleObject (sitter->start_sync_event, INFINITE);
PING();
if (sitter_p != NULL)
*sitter_p = sitter;
else
_dbus_babysitter_unref (sitter);
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
PING();
return TRUE;
out0:
_dbus_babysitter_unref (sitter);
return FALSE;
}
void
_dbus_babysitter_set_result_function (DBusBabysitter *sitter,
DBusBabysitterFinishedFunc finished,
void *user_data)
{
sitter->finished_cb = finished;
sitter->finished_data = user_data;
}
#ifdef DBUS_ENABLE_EMBEDDED_TESTS
static char *
get_test_exec (const char *exe,
DBusString *scratch_space)
{
const char *dbus_test_exec;
dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
if (dbus_test_exec == NULL)
dbus_test_exec = DBUS_TEST_EXEC;
if (!_dbus_string_init (scratch_space))
return NULL;
if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
dbus_test_exec, exe, DBUS_EXEEXT))
{
_dbus_string_free (scratch_space);
return NULL;
}
return _dbus_string_get_data (scratch_space);
}
#define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
static void
_dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
{
if (sitter->child_handle == NULL)
return;
WaitForSingleObject (sitter->end_sync_event, INFINITE);
}
static dbus_bool_t
check_spawn_nonexistent (void *data)
{
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
sitter = NULL;
dbus_error_init (&error);
/*** Test launching nonexistent binary */
argv[0] = "/this/does/not/exist/32542sdgafgafdg";
if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_nonexistent", argv, NULL,
NULL, NULL,
&error))
{
_dbus_babysitter_block_for_child_exit (sitter);
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
if (sitter)
_dbus_babysitter_unref (sitter);
if (!dbus_error_is_set (&error))
{
_dbus_warn ("Did not get an error launching nonexistent executable\n");
return FALSE;
}
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
{
_dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return FALSE;
}
dbus_error_free (&error);
return TRUE;
}
static dbus_bool_t
check_spawn_segfault (void *data)
{
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
DBusString argv0;
sitter = NULL;
dbus_error_init (&error);
/*** Test launching segfault binary */
argv[0] = get_test_exec ("test-segfault", &argv0);
if (argv[0] == NULL)
{
/* OOM was simulated, never mind */
return TRUE;
}
if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_segfault", argv, NULL,
NULL, NULL,
&error))
{
_dbus_babysitter_block_for_child_exit (sitter);
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
_dbus_string_free (&argv0);
if (sitter)
_dbus_babysitter_unref (sitter);
if (!dbus_error_is_set (&error))
{
_dbus_warn ("Did not get an error launching segfaulting binary\n");
return FALSE;
}
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
{
_dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return FALSE;
}
dbus_error_free (&error);
return TRUE;
}
static dbus_bool_t
check_spawn_exit (void *data)
{
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
DBusString argv0;
sitter = NULL;
dbus_error_init (&error);
/*** Test launching exit failure binary */
argv[0] = get_test_exec ("test-exit", &argv0);
if (argv[0] == NULL)
{
/* OOM was simulated, never mind */
return TRUE;
}
if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_exit", argv, NULL,
NULL, NULL,
&error))
{
_dbus_babysitter_block_for_child_exit (sitter);
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
_dbus_string_free (&argv0);
if (sitter)
_dbus_babysitter_unref (sitter);
if (!dbus_error_is_set (&error))
{
_dbus_warn ("Did not get an error launching binary that exited with failure code\n");
return FALSE;
}
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
{
_dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return FALSE;
}
dbus_error_free (&error);
return TRUE;
}
static dbus_bool_t
check_spawn_and_kill (void *data)
{
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
DBusString argv0;
sitter = NULL;
dbus_error_init (&error);
/*** Test launching sleeping binary then killing it */
argv[0] = get_test_exec ("test-sleep-forever", &argv0);
if (argv[0] == NULL)
{
/* OOM was simulated, never mind */
return TRUE;
}
if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_and_kill", argv, NULL,
NULL, NULL,
&error))
{
_dbus_babysitter_kill_child (sitter);
_dbus_babysitter_block_for_child_exit (sitter);
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
_dbus_string_free (&argv0);
if (sitter)
_dbus_babysitter_unref (sitter);
if (!dbus_error_is_set (&error))
{
_dbus_warn ("Did not get an error after killing spawned binary\n");
return FALSE;
}
if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
{
_dbus_warn ("Not expecting error when killing executable: %s: %s\n",
error.name, error.message);
dbus_error_free (&error);
return FALSE;
}
dbus_error_free (&error);
return TRUE;
}
dbus_bool_t
_dbus_spawn_test (const char *test_data_dir)
{
if (!_dbus_test_oom_handling ("spawn_nonexistent",
check_spawn_nonexistent,
NULL))
return FALSE;
/* Don't run the obnoxious segfault test by default,
* it's a pain to have to click all those error boxes.
*/
if (getenv ("DO_SEGFAULT_TEST"))
if (!_dbus_test_oom_handling ("spawn_segfault",
check_spawn_segfault,
NULL))
return FALSE;
if (!_dbus_test_oom_handling ("spawn_exit",
check_spawn_exit,
NULL))
return FALSE;
if (!_dbus_test_oom_handling ("spawn_and_kill",
check_spawn_and_kill,
NULL))
return FALSE;
return TRUE;
}
#endif