mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
Merge branch 'bug8746_v2_squashed'
Conflicts: src/common/include.am
This commit is contained in:
commit
a7cafb1ea9
4
changes/bug8746
Normal file
4
changes/bug8746
Normal file
@ -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.
|
@ -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 \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
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;
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
|
157
src/common/util_process.c
Normal file
157
src/common/util_process.c
Normal file
@ -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
|
||||
|
25
src/common/util_process.h
Normal file
25
src/common/util_process.h
Normal file
@ -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
|
@ -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: {
|
||||
|
@ -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
|
||||
|
@ -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]);
|
||||
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;
|
||||
}
|
||||
|
@ -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,15 +2635,9 @@ 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;
|
||||
|
||||
@ -2623,9 +2649,6 @@ test_util_spawn_background_ok(void *ptr)
|
||||
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];
|
||||
@ -2652,7 +2675,7 @@ test_util_spawn_background_fail(void *ptr)
|
||||
/** 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",
|
||||
|
||||
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\r\n";
|
||||
#else
|
||||
const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
|
||||
const char *expected_out[] = { "OUT\n--test\nSLEEPING\n",
|
||||
"DONE\n",
|
||||
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),
|
||||
|
Loading…
Reference in New Issue
Block a user