-#include "config.h"
+#include <config.h>
//#define SPAWN_DEBUG
#endif
#include <stdio.h>
-#ifdef DBUS_WINCE
-#include <process.h>
-#endif
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- 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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "dbus-spawn.h"
#include "dbus-protocol.h"
#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
//#define STRICT
//#include <windows.h>
//#undef STRICT
#include <stdlib.h>
+#ifndef DBUS_WINCE
#include <process.h>
+#endif
/**
* Babysitter implementation details
DBusWatchList *watches;
DBusWatch *sitter_watch;
+ DBusBabysitterFinishedFunc finished_cb;
+ void *finished_data;
dbus_bool_t have_spawn_errno;
int spawn_errno;
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.
*
if (sitter->refcount == 0)
{
- if (sitter->socket_to_babysitter != -1)
- {
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
- sitter->socket_to_babysitter = -1;
- }
+ close_socket_to_babysitter (sitter);
if (sitter->socket_to_main != -1)
{
{
PING();
CloseHandle (sitter->start_sync_event);
- sitter->end_sync_event = NULL;
+ sitter->start_sync_event = NULL;
}
#ifdef DBUS_BUILD_TESTS
}
/**
+ * 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
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->executable, _dbus_strerror (sitter->spawn_errno));
+ sitter->executable, emsg);
+ _dbus_win_free_error_string (emsg);
}
else if (sitter->have_child_status)
{
*/
PING();
- _dbus_close_socket (sitter->socket_to_babysitter, NULL);
+ close_socket_to_babysitter (sitter);
PING();
- sitter->socket_to_babysitter = -1;
+
+ if (_dbus_babysitter_get_child_exited (sitter) &&
+ sitter->finished_cb != NULL)
+ {
+ sitter->finished_cb (sitter, sitter->finished_data);
+ sitter->finished_cb = NULL;
+ }
return TRUE;
}
return argc;
}
-static unsigned __stdcall
-babysitter (void *parameter)
+
+/* From GPGME, relicensed by g10 Code GmbH. */
+static char *
+compose_string (char **strings, char separator)
{
- DBusBabysitter *sitter = (DBusBabysitter *) parameter;
-#ifdef ENABLE_DBUSSOCKET
- DBusSocket *sock;
+ 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
- int fd;
+ 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);
_dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
PING();
- if (sitter->envp != NULL)
- sitter->child_handle = (HANDLE) spawnve (P_NOWAIT, sitter->executable,
- (const char * const *) sitter->argv,
- (const char * const *) sitter->envp);
- else
- sitter->child_handle = (HANDLE) spawnv (P_NOWAIT, sitter->executable,
- (const char * const *) sitter->argv);
+ sitter->child_handle = spawn_program (sitter->executable,
+ sitter->argv, sitter->envp);
PING();
if (sitter->child_handle == (HANDLE) -1)
{
sitter->child_handle = NULL;
sitter->have_spawn_errno = TRUE;
- sitter->spawn_errno = errno;
+ sitter->spawn_errno = GetLastError();
}
-
+
PING();
SetEvent (sitter->start_sync_event);
#endif
PING();
-#ifdef ENABLE_DBUSSOCKET
- _dbus_handle_to_socket (sitter->socket_to_main, &sock);
- send (sock->fd, " ", 1, 0);
-#else
send (sitter->socket_to_main, " ", 1, 0);
-#endif
_dbus_babysitter_unref (sitter);
{
DBusBabysitter *sitter;
HANDLE sitter_thread;
- int sitter_thread_id;
-
+ DWORD sitter_thread_id;
+
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
*sitter_p = NULL;
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->envp = envp;
PING();
- sitter_thread = (HANDLE) _beginthreadex (NULL, 0, babysitter,
+ sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
sitter, 0, &sitter_thread_id);
if (sitter_thread == 0)
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_BUILD_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
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
+ DBusString argv0;
sitter = NULL;
/*** Test launching segfault binary */
- argv[0] = TEST_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, argv, NULL,
NULL, NULL,
&error))
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
+ _dbus_string_free (&argv0);
+
if (sitter)
_dbus_babysitter_unref (sitter);
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
+ DBusString argv0;
sitter = NULL;
/*** Test launching exit failure binary */
- argv[0] = TEST_EXIT_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, argv, NULL,
NULL, NULL,
&error))
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
+ _dbus_string_free (&argv0);
+
if (sitter)
_dbus_babysitter_unref (sitter);
char *argv[4] = { NULL, NULL, NULL, NULL };
DBusBabysitter *sitter;
DBusError error;
+ DBusString argv0;
sitter = NULL;
/*** Test launching sleeping binary then killing it */
- argv[0] = TEST_SLEEP_FOREVER_BINARY;
+ 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, argv, NULL,
NULL, NULL,
&error))
_dbus_babysitter_set_child_exit_error (sitter, &error);
}
+ _dbus_string_free (&argv0);
+
if (sitter)
_dbus_babysitter_unref (sitter);