aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2014-06-14 11:46:38 -0400
committerNick Mathewson <nickm@torproject.org>2014-06-14 11:46:38 -0400
commita7cafb1ea9307864c94ad3e019af93d09d48350e (patch)
tree1c6cc26fb0a93f46d839b3cb46855c93aca135f4
parenta58d94fb7c6c304ecba930eaa7ebbade1d0686ab (diff)
parente07d328457b26babe89abdcc1d5a550ee8273462 (diff)
downloadtor-a7cafb1ea9307864c94ad3e019af93d09d48350e.tar
tor-a7cafb1ea9307864c94ad3e019af93d09d48350e.tar.gz
Merge branch 'bug8746_v2_squashed'
Conflicts: src/common/include.am
-rw-r--r--changes/bug87464
-rw-r--r--configure.ac2
-rw-r--r--src/common/compat.c26
-rw-r--r--src/common/compat.h4
-rw-r--r--src/common/include.am7
-rw-r--r--src/common/procmon.c1
-rw-r--r--src/common/util.c54
-rw-r--r--src/common/util.h9
-rw-r--r--src/common/util_process.c157
-rw-r--r--src/common/util_process.h25
-rw-r--r--src/or/main.c4
-rw-r--r--src/or/or.h3
-rw-r--r--src/test/test-child.c40
-rw-r--r--src/test/test_util.c160
14 files changed, 437 insertions, 59 deletions
diff --git a/changes/bug8746 b/changes/bug8746
new file mode 100644
index 000000000..b6e52ca43
--- /dev/null
+++ b/changes/bug8746
@@ -0,0 +1,4 @@
+ o Major bugfixes:
+ - When managing pluggable transports, use OS notification facilities to
+ learn if they have crashed, and do not attempt to kill any process
+ that has already exited. Fix for bug 8746; bugfix on 0.2.3.6-alpha.
diff --git a/configure.ac b/configure.ac
index 6e9e13d76..deaa93981 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,6 +367,7 @@ AC_CHECK_FUNCS(
sysconf \
sysctl \
uname \
+ usleep \
vasprintf \
_vscprintf
)
@@ -898,6 +899,7 @@ AC_CHECK_HEADERS(
sys/param.h \
sys/prctl.h \
sys/resource.h \
+ sys/select.h \
sys/socket.h \
sys/sysctl.h \
sys/syslimits.h \
diff --git a/src/common/compat.c b/src/common/compat.c
index 111070cc1..e25ecc462 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -114,6 +114,12 @@
/* Only use the linux prctl; the IRIX prctl is totally different */
#include <sys/prctl.h>
#endif
+#ifdef TOR_UNIT_TESTS
+#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
+/* as fallback implementation for tor_sleep_msec */
+#include <sys/select.h>
+#endif
+#endif
#include "torlog.h"
#include "util.h"
@@ -3556,3 +3562,23 @@ get_total_system_memory(size_t *mem_out)
return 0;
}
+#ifdef TOR_UNIT_TESTS
+/** Delay for <b>msec</b> milliseconds. Only used in tests. */
+void
+tor_sleep_msec(int msec)
+{
+#ifdef _WIN32
+ Sleep(msec);
+#elif defined(HAVE_USLEEP)
+ sleep(msec / 1000);
+ /* Some usleep()s hate sleeping more than 1 sec */
+ usleep((msec % 1000) * 1000);
+#elif defined(HAVE_SYS_SELECT_H)
+ struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
+ select(0, NULL, NULL, NULL, &tv);
+#else
+ sleep(CEIL_DIV(msec, 1000));
+#endif
+}
+#endif
+
diff --git a/src/common/compat.h b/src/common/compat.h
index 683c4d089..ec7d2415e 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -749,6 +749,10 @@ char *format_win32_error(DWORD err);
#endif
+#ifdef TOR_UNIT_TESTS
+void tor_sleep_msec(int msec);
+#endif
+
#ifdef COMPAT_PRIVATE
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
#define NEED_ERSATZ_SOCKETPAIR
diff --git a/src/common/include.am b/src/common/include.am
index 61a90cd35..68e0110c2 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -64,9 +64,9 @@ LIBOR_A_SOURCES = \
src/common/di_ops.c \
src/common/log.c \
src/common/memarea.c \
- src/common/procmon.c \
src/common/util.c \
src/common/util_codedigest.c \
+ src/common/util_process.c \
src/common/sandbox.c \
src/ext/csiphash.c \
$(libor_extra_source) \
@@ -80,7 +80,9 @@ LIBOR_CRYPTO_A_SOURCES = \
src/common/tortls.c \
$(libcrypto_extra_source)
-LIBOR_EVENT_A_SOURCES = src/common/compat_libevent.c
+LIBOR_EVENT_A_SOURCES = \
+ src/common/compat_libevent.c \
+ src/common/procmon.c
src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
@@ -119,6 +121,7 @@ COMMONHEADERS = \
src/common/torlog.h \
src/common/tortls.h \
src/common/util.h \
+ src/common/util_process.h \
$(libor_mempool_header)
noinst_HEADERS+= $(COMMONHEADERS)
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 0a49689e3..7c9b7c3c8 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -162,6 +162,7 @@ tor_validate_process_specifier(const char *process_spec,
return parse_process_specifier(process_spec, &ppspec, msg);
}
+/* XXXX we should use periodic_timer_new() for this stuff */
#ifdef HAVE_EVENT2_EVENT_H
#define PERIODIC_TIMER_FLAGS EV_PERSIST
#else
diff --git a/src/common/util.c b/src/common/util.c
index e27036a84..2d7893b38 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -26,6 +26,7 @@
#include "address.h"
#include "sandbox.h"
#include "backtrace.h"
+#include "util_process.h"
#ifdef _WIN32
#include <io.h>
@@ -3629,13 +3630,7 @@ tor_terminate_process(process_handle_t *process_handle)
{
#ifdef _WIN32
if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
- HANDLE handle;
- /* If the signal is outside of what GenerateConsoleCtrlEvent can use,
- attempt to open and terminate the process. */
- handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE,
- process_handle->pid.dwProcessId);
- if (!handle)
- return -1;
+ HANDLE handle = process_handle->pid.hProcess;
if (!TerminateProcess(handle, 0))
return -1;
@@ -3643,7 +3638,10 @@ tor_terminate_process(process_handle_t *process_handle)
return 0;
}
#else /* Unix */
- return kill(process_handle->pid, SIGTERM);
+ if (process_handle->waitpid_cb) {
+ /* We haven't got a waitpid yet, so we can just kill off the process. */
+ return kill(process_handle->pid, SIGTERM);
+ }
#endif
return -1;
@@ -3692,6 +3690,23 @@ process_handle_new(void)
return out;
}
+#ifndef _WIN32
+/** Invoked when a process that we've launched via tor_spawn_background() has
+ * been found to have terminated.
+ */
+static void
+process_handle_waitpid_cb(int status, void *arg)
+{
+ process_handle_t *process_handle = arg;
+
+ process_handle->waitpid_exit_status = status;
+ clear_waitpid_callback(process_handle->waitpid_cb);
+ if (process_handle->status == PROCESS_STATUS_RUNNING)
+ process_handle->status = PROCESS_STATUS_NOTRUNNING;
+ process_handle->waitpid_cb = 0;
+}
+#endif
+
/**
* @name child-process states
*
@@ -4008,6 +4023,10 @@ tor_spawn_background(const char *const filename, const char **argv,
strerror(errno));
}
+ process_handle->waitpid_cb = set_waitpid_callback(pid,
+ process_handle_waitpid_cb,
+ process_handle);
+
process_handle->stderr_pipe = stderr_pipe[0];
retval = close(stderr_pipe[1]);
@@ -4072,6 +4091,8 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stderr_handle)
fclose(process_handle->stderr_handle);
+
+ clear_waitpid_callback(process_handle->waitpid_cb);
#endif
memset(process_handle, 0x0f, sizeof(process_handle_t));
@@ -4089,7 +4110,7 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
* probably not work in Tor, because waitpid() is called in main.c to reap any
* terminated child processes.*/
int
-tor_get_exit_code(const process_handle_t *process_handle,
+tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code)
{
#ifdef _WIN32
@@ -4129,7 +4150,20 @@ tor_get_exit_code(const process_handle_t *process_handle,
int stat_loc;
int retval;
- retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
+ if (process_handle->waitpid_cb) {
+ /* We haven't processed a SIGCHLD yet. */
+ retval = waitpid(process_handle->pid, &stat_loc, block?0:WNOHANG);
+ if (retval == process_handle->pid) {
+ clear_waitpid_callback(process_handle->waitpid_cb);
+ process_handle->waitpid_cb = NULL;
+ process_handle->waitpid_exit_status = stat_loc;
+ }
+ } else {
+ /* We already got a SIGCHLD for this process, and handled it. */
+ retval = process_handle->pid;
+ stat_loc = process_handle->waitpid_exit_status;
+ }
+
if (!block && 0 == retval) {
/* Process has not exited */
return PROCESS_EXIT_RUNNING;
diff --git a/src/common/util.h b/src/common/util.h
index 18dc20639..97367a9a7 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -446,6 +446,7 @@ void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
#define PROCESS_STATUS_ERROR -1
#ifdef UTIL_PRIVATE
+struct waitpid_callback_t;
/** Structure to represent the state of a process with which Tor is
* communicating. The contents of this structure are private to util.c */
struct process_handle_t {
@@ -461,6 +462,12 @@ struct process_handle_t {
FILE *stdout_handle;
FILE *stderr_handle;
pid_t pid;
+ /** If the process has not given us a SIGCHLD yet, this has the
+ * waitpid_callback_t that gets invoked once it has. Otherwise this
+ * contains NULL. */
+ struct waitpid_callback_t *waitpid_cb;
+ /** The exit status reported by waitpid. */
+ int waitpid_exit_status;
#endif // _WIN32
};
#endif
@@ -469,7 +476,7 @@ struct process_handle_t {
#define PROCESS_EXIT_RUNNING 1
#define PROCESS_EXIT_EXITED 0
#define PROCESS_EXIT_ERROR -1
-int tor_get_exit_code(const process_handle_t *process_handle,
+int tor_get_exit_code(process_handle_t *process_handle,
int block, int *exit_code);
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
#ifdef _WIN32
diff --git a/src/common/util_process.c b/src/common/util_process.c
new file mode 100644
index 000000000..8ccf7f38f
--- /dev/null
+++ b/src/common/util_process.c
@@ -0,0 +1,157 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.c
+ * \brief utility functions for launching processes and checking their
+ * status. These functions are kept separately from procmon so that they
+ * won't require linking against libevent.
+ **/
+
+#include "orconfig.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+#include "compat.h"
+#include "util.h"
+#include "torlog.h"
+#include "util_process.h"
+#include "ht.h"
+
+/* ================================================== */
+/* Convenience structures for handlers for waitpid().
+ *
+ * The tor_process_monitor*() code above doesn't use them, since it is for
+ * monitoring a non-child process.
+ */
+
+#ifndef _WIN32
+
+/** Mapping from a PID to a userfn/userdata pair. */
+struct waitpid_callback_t {
+ HT_ENTRY(waitpid_callback_t) node;
+ pid_t pid;
+
+ void (*userfn)(int, void *userdata);
+ void *userdata;
+
+ unsigned running;
+};
+
+static INLINE unsigned int
+process_map_entry_hash_(const waitpid_callback_t *ent)
+{
+ return (unsigned) ent->pid;
+}
+
+static INLINE unsigned int
+process_map_entries_eq_(const waitpid_callback_t *a, const waitpid_callback_t *b)
+{
+ return a->pid == b->pid;
+}
+
+static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
+
+HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+ process_map_entries_eq_);
+HT_GENERATE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
+ process_map_entries_eq_, 0.6, malloc, realloc, free);
+
+/**
+ * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
+ * it. If we eventually do, call <b>fn</b>, passing it the exit status (as
+ * yielded by waitpid) and the pointer <b>arg</b>.
+ *
+ * To cancel this, or clean up after it has triggered, call
+ * clear_waitpid_callback().
+ */
+waitpid_callback_t *
+set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
+{
+ waitpid_callback_t *old_ent;
+ waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
+ ent->pid = pid;
+ ent->userfn = fn;
+ ent->userdata = arg;
+ ent->running = 1;
+
+ old_ent = HT_REPLACE(process_map, &process_map, ent);
+ if (old_ent) {
+ log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
+ "impossible.", (unsigned) pid);
+ old_ent->running = 0;
+ }
+
+ return ent;
+}
+
+/**
+ * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
+ * all storage held by <b>ent</b>.
+ */
+void
+clear_waitpid_callback(waitpid_callback_t *ent)
+{
+ waitpid_callback_t *old_ent;
+ if (ent == NULL)
+ return;
+
+ if (ent->running) {
+ old_ent = HT_REMOVE(process_map, &process_map, ent);
+ if (old_ent != ent) {
+ log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
+ (unsigned) ent->pid);
+ return;
+ }
+ }
+
+ tor_free(ent);
+}
+
+/** Helper: find the callack for <b>pid</b>; if there is one, run it,
+ * reporting the exit status as <b>status</b>. */
+static void
+notify_waitpid_callback_by_pid(pid_t pid, int status)
+{
+ waitpid_callback_t search, *ent;
+
+ search.pid = pid;
+ ent = HT_REMOVE(process_map, &process_map, &search);
+ if (!ent || !ent->running) {
+ log_info(LD_GENERAL, "Child process %u has exited; no callback was "
+ "registered", (unsigned)pid);
+ return;
+ }
+
+ log_info(LD_GENERAL, "Child process %u has exited; running callback.",
+ (unsigned)pid);
+
+ ent->running = 0;
+ ent->userfn(status, ent->userdata);
+}
+
+/** Use waitpid() to wait for all children that have exited, and invoke any
+ * callbacks registered for them. */
+void
+notify_pending_waitpid_callbacks(void)
+{
+ /* I was going to call this function reap_zombie_children(), but
+ * that makes it sound way more exciting than it really is. */
+ pid_t child;
+ int status = 0;
+
+ while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
+ notify_waitpid_callback_by_pid(child, status);
+ status = 0; /* should be needless */
+ }
+}
+
+#endif
+
diff --git a/src/common/util_process.h b/src/common/util_process.h
new file mode 100644
index 000000000..877702c68
--- /dev/null
+++ b/src/common/util_process.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2011-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_process.h
+ * \brief Headers for util_process.c
+ **/
+
+#ifndef TOR_UTIL_PROCESS_H
+#define TOR_UTIL_PROCESS_H
+
+#ifndef _WIN32
+/** A callback structure waiting for us to get a SIGCHLD informing us that a
+ * PID has been closed. Created by set_waitpid_callback. Cancelled or cleaned-
+ * up from clear_waitpid_callback(). Do not access outside of the main thread;
+ * do not access from inside a signal handler. */
+typedef struct waitpid_callback_t waitpid_callback_t;
+
+waitpid_callback_t *set_waitpid_callback(pid_t pid,
+ void (*fn)(int, void *), void *arg);
+void clear_waitpid_callback(waitpid_callback_t *ent);
+void notify_pending_waitpid_callbacks(void);
+#endif
+
+#endif
diff --git a/src/or/main.c b/src/or/main.c
index 090503e07..1c49ba148 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -54,6 +54,7 @@
#include "routerparse.h"
#include "statefile.h"
#include "status.h"
+#include "util_process.h"
#include "ext_orport.h"
#ifdef USE_DMALLOC
#include <dmalloc.h>
@@ -2109,8 +2110,7 @@ process_signal(uintptr_t sig)
break;
#ifdef SIGCHLD
case SIGCHLD:
- while (waitpid(-1,NULL,WNOHANG) > 0) ; /* keep reaping until no more
- zombies */
+ notify_pending_waitpid_callbacks();
break;
#endif
case SIGNEWNYM: {
diff --git a/src/or/or.h b/src/or/or.h
index f1d68b766..131bce3e1 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -42,9 +42,6 @@
#include <sys/param.h> /* FreeBSD needs this to know what version it is */
#endif
#include "torint.h"
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
#ifdef HAVE_SYS_FCNTL_H
#include <sys/fcntl.h>
#endif
diff --git a/src/test/test-child.c b/src/test/test-child.c
index ef10fbb92..756782e70 100644
--- a/src/test/test-child.c
+++ b/src/test/test-child.c
@@ -9,6 +9,13 @@
#else
#include <unistd.h>
#endif
+#include <string.h>
+
+#ifdef _WIN32
+#define SLEEP(sec) Sleep((sec)*1000)
+#else
+#define SLEEP(sec) sleep(sec)
+#endif
/** Trivial test program which prints out its command line arguments so we can
* check if tor_spawn_background() works */
@@ -16,27 +23,38 @@ int
main(int argc, char **argv)
{
int i;
+ int delay = 1;
+ int fast = 0;
+
+ if (argc > 1) {
+ if (!strcmp(argv[1], "--hang")) {
+ delay = 60;
+ } else if (!strcmp(argv[1], "--fast")) {
+ fast = 1;
+ delay = 0;
+ }
+ }
fprintf(stdout, "OUT\n");
fprintf(stderr, "ERR\n");
for (i = 1; i < argc; i++)
fprintf(stdout, "%s\n", argv[i]);
- fprintf(stdout, "SLEEPING\n");
+ if (!fast)
+ fprintf(stdout, "SLEEPING\n");
/* We need to flush stdout so that test_util_spawn_background_partial_read()
succeed. Otherwise ReadFile() will get the entire output in one */
// XXX: Can we make stdio flush on newline?
fflush(stdout);
-#ifdef _WIN32
- Sleep(1000);
-#else
- sleep(1);
-#endif
+ if (!fast)
+ SLEEP(1);
fprintf(stdout, "DONE\n");
-#ifdef _WIN32
- Sleep(1000);
-#else
- sleep(1);
-#endif
+ fflush(stdout);
+ if (fast)
+ return 0;
+
+ while (--delay) {
+ SLEEP(1);
+ }
return 0;
}
diff --git a/src/test/test_util.c b/src/test/test_util.c
index c7fa14118..151ec6912 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -16,6 +16,7 @@
#include "mempool.h"
#endif /* ENABLE_MEMPOOLS */
#include "memarea.h"
+#include "util_process.h"
#ifdef _WIN32
#include <tchar.h>
@@ -2538,6 +2539,19 @@ test_util_fgets_eagain(void *ptr)
}
#endif
+#ifndef BUILDDIR
+#define BUILDDIR "."
+#endif
+
+#ifdef _WIN32
+#define notify_pending_waitpid_callbacks() STMT_NIL
+#define TEST_CHILD "test-child.exe"
+#define EOL "\r\n"
+#else
+#define TEST_CHILD (BUILDDIR "/src/test/test-child")
+#define EOL "\n"
+#endif
+
/** Helper function for testing tor_spawn_background */
static void
run_util_spawn_background(const char *argv[], const char *expected_out,
@@ -2557,19 +2571,28 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
#endif
+ notify_pending_waitpid_callbacks();
+
test_eq(expected_status, status);
- if (status == PROCESS_STATUS_ERROR)
+ if (status == PROCESS_STATUS_ERROR) {
+ tt_ptr_op(process_handle, ==, NULL);
return;
+ }
test_assert(process_handle != NULL);
test_eq(expected_status, process_handle->status);
+#ifndef _WIN32
+ notify_pending_waitpid_callbacks();
+ tt_ptr_op(process_handle->waitpid_cb, !=, NULL);
+#endif
+
#ifdef _WIN32
test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
#else
- test_assert(process_handle->stdout_pipe > 0);
- test_assert(process_handle->stderr_pipe > 0);
+ test_assert(process_handle->stdout_pipe >= 0);
+ test_assert(process_handle->stderr_pipe >= 0);
#endif
/* Check stdout */
@@ -2580,12 +2603,19 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
test_eq(strlen(expected_out), pos);
test_streq(expected_out, stdout_buf);
+ notify_pending_waitpid_callbacks();
+
/* Check it terminated correctly */
retval = tor_get_exit_code(process_handle, 1, &exit_code);
test_eq(PROCESS_EXIT_EXITED, retval);
test_eq(expected_exit, exit_code);
// TODO: Make test-child exit with something other than 0
+#ifndef _WIN32
+ notify_pending_waitpid_callbacks();
+ tt_ptr_op(process_handle->waitpid_cb, ==, NULL);
+#endif
+
/* Check stderr */
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
sizeof(stderr_buf) - 1);
@@ -2594,6 +2624,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
test_streq(expected_err, stderr_buf);
test_eq(strlen(expected_err), pos);
+ notify_pending_waitpid_callbacks();
+
done:
if (process_handle)
tor_process_handle_destroy(process_handle, 1);
@@ -2603,29 +2635,20 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
static void
test_util_spawn_background_ok(void *ptr)
{
-#ifdef _WIN32
- const char *argv[] = {"test-child.exe", "--test", NULL};
- const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n";
- const char *expected_err = "ERR\r\n";
-#else
- const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
- const char *expected_out = "OUT\n--test\nSLEEPING\nDONE\n";
- const char *expected_err = "ERR\n";
-#endif
+ const char *argv[] = {TEST_CHILD, "--test", NULL};
+ const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
+ const char *expected_err = "ERR"EOL;
(void)ptr;
run_util_spawn_background(argv, expected_out, expected_err, 0,
- PROCESS_STATUS_RUNNING);
+ PROCESS_STATUS_RUNNING);
}
/** Check that failing to find the executable works as expected */
static void
test_util_spawn_background_fail(void *ptr)
{
-#ifndef BUILDDIR
-#define BUILDDIR "."
-#endif
const char *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
const char *expected_err = "";
char expected_out[1024];
@@ -2646,13 +2669,13 @@ test_util_spawn_background_fail(void *ptr)
"ERR: Failed to spawn background process - code %s\n", code);
run_util_spawn_background(argv, expected_out, expected_err, 255,
- expected_status);
+ expected_status);
}
/** Test that reading from a handle returns a partial read rather than
* blocking */
static void
-test_util_spawn_background_partial_read(void *ptr)
+test_util_spawn_background_partial_read_impl(int exit_early)
{
const int expected_exit = 0;
const int expected_status = PROCESS_STATUS_RUNNING;
@@ -2662,22 +2685,22 @@ test_util_spawn_background_partial_read(void *ptr)
process_handle_t *process_handle=NULL;
int status;
char stdout_buf[100], stderr_buf[100];
-#ifdef _WIN32
- const char *argv[] = {"test-child.exe", "--test", NULL};
- const char *expected_out[] = { "OUT\r\n--test\r\nSLEEPING\r\n",
- "DONE\r\n",
- NULL };
- const char *expected_err = "ERR\r\n";
-#else
- const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
- const char *expected_out[] = { "OUT\n--test\nSLEEPING\n",
- "DONE\n",
+
+ const char *argv[] = {TEST_CHILD, "--test", NULL};
+ const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
+ "DONE" EOL,
NULL };
- const char *expected_err = "ERR\n";
+ const char *expected_err = "ERR" EOL;
+
+#ifndef _WIN32
int eof = 0;
#endif
int expected_out_ctr;
- (void)ptr;
+
+ if (exit_early) {
+ argv[1] = "--hang";
+ expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
+ }
/* Start the program */
#ifdef _WIN32
@@ -2713,6 +2736,12 @@ test_util_spawn_background_partial_read(void *ptr)
expected_out_ctr++;
}
+ if (exit_early) {
+ tor_process_handle_destroy(process_handle, 1);
+ process_handle = NULL;
+ goto done;
+ }
+
/* The process should have exited without writing more */
#ifdef _WIN32
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
@@ -2750,6 +2779,75 @@ test_util_spawn_background_partial_read(void *ptr)
tor_process_handle_destroy(process_handle, 1);
}
+static void
+test_util_spawn_background_partial_read(void *arg)
+{
+ (void)arg;
+ test_util_spawn_background_partial_read_impl(0);
+}
+
+static void
+test_util_spawn_background_exit_early(void *arg)
+{
+ (void)arg;
+ test_util_spawn_background_partial_read_impl(1);
+}
+
+static void
+test_util_spawn_background_waitpid_notify(void *arg)
+{
+ int retval, exit_code;
+ process_handle_t *process_handle=NULL;
+ int status;
+ int ms_timer;
+
+ const char *argv[] = {TEST_CHILD, "--fast", NULL};
+
+ (void) arg;
+
+#ifdef _WIN32
+ status = tor_spawn_background(NULL, argv, NULL, &process_handle);
+#else
+ status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
+#endif
+
+ tt_int_op(status, ==, PROCESS_STATUS_RUNNING);
+ tt_ptr_op(process_handle, !=, NULL);
+
+ /* We're not going to look at the stdout/stderr output this time. Instead,
+ * we're testing whether notify_pending_waitpid_calbacks() can report the
+ * process exit (on unix) and/or whether tor_get_exit_code() can notice it
+ * (on windows) */
+
+#ifndef _WIN32
+ ms_timer = 30*1000;
+ tt_ptr_op(process_handle->waitpid_cb, !=, NULL);
+ while (process_handle->waitpid_cb && ms_timer > 0) {
+ tor_sleep_msec(100);
+ ms_timer -= 100;
+ notify_pending_waitpid_callbacks();
+ }
+ tt_int_op(ms_timer, >, 0);
+ tt_ptr_op(process_handle->waitpid_cb, ==, NULL);
+#endif
+
+ ms_timer = 30*1000;
+ while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
+ == PROCESS_EXIT_RUNNING) && ms_timer > 0) {
+ tor_sleep_msec(100);
+ ms_timer -= 100;
+ }
+ tt_int_op(ms_timer, >, 0);
+
+ tt_int_op(retval, ==, PROCESS_EXIT_EXITED);
+
+ done:
+ tor_process_handle_destroy(process_handle, 1);
+}
+
+#undef TEST_CHILD
+#undef EOL
+
/**
* Test for format_hex_number_sigsafe()
*/
@@ -3695,6 +3793,8 @@ struct testcase_t util_tests[] = {
UTIL_TEST(spawn_background_ok, 0),
UTIL_TEST(spawn_background_fail, 0),
UTIL_TEST(spawn_background_partial_read, 0),
+ UTIL_TEST(spawn_background_exit_early, 0),
+ UTIL_TEST(spawn_background_waitpid_notify, 0),
UTIL_TEST(format_hex_number, 0),
UTIL_TEST(format_dec_number, 0),
UTIL_TEST(join_win_cmdline, 0),