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 \
|
sysconf \
|
||||||
sysctl \
|
sysctl \
|
||||||
uname \
|
uname \
|
||||||
|
usleep \
|
||||||
vasprintf \
|
vasprintf \
|
||||||
_vscprintf
|
_vscprintf
|
||||||
)
|
)
|
||||||
@ -898,6 +899,7 @@ AC_CHECK_HEADERS(
|
|||||||
sys/param.h \
|
sys/param.h \
|
||||||
sys/prctl.h \
|
sys/prctl.h \
|
||||||
sys/resource.h \
|
sys/resource.h \
|
||||||
|
sys/select.h \
|
||||||
sys/socket.h \
|
sys/socket.h \
|
||||||
sys/sysctl.h \
|
sys/sysctl.h \
|
||||||
sys/syslimits.h \
|
sys/syslimits.h \
|
||||||
|
@ -114,6 +114,12 @@
|
|||||||
/* Only use the linux prctl; the IRIX prctl is totally different */
|
/* Only use the linux prctl; the IRIX prctl is totally different */
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#endif
|
#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 "torlog.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -3556,3 +3562,23 @@ get_total_system_memory(size_t *mem_out)
|
|||||||
return 0;
|
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
|
#endif
|
||||||
|
|
||||||
|
#ifdef TOR_UNIT_TESTS
|
||||||
|
void tor_sleep_msec(int msec);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef COMPAT_PRIVATE
|
#ifdef COMPAT_PRIVATE
|
||||||
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
|
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
|
||||||
#define NEED_ERSATZ_SOCKETPAIR
|
#define NEED_ERSATZ_SOCKETPAIR
|
||||||
|
@ -64,9 +64,9 @@ LIBOR_A_SOURCES = \
|
|||||||
src/common/di_ops.c \
|
src/common/di_ops.c \
|
||||||
src/common/log.c \
|
src/common/log.c \
|
||||||
src/common/memarea.c \
|
src/common/memarea.c \
|
||||||
src/common/procmon.c \
|
|
||||||
src/common/util.c \
|
src/common/util.c \
|
||||||
src/common/util_codedigest.c \
|
src/common/util_codedigest.c \
|
||||||
|
src/common/util_process.c \
|
||||||
src/common/sandbox.c \
|
src/common/sandbox.c \
|
||||||
src/ext/csiphash.c \
|
src/ext/csiphash.c \
|
||||||
$(libor_extra_source) \
|
$(libor_extra_source) \
|
||||||
@ -80,7 +80,9 @@ LIBOR_CRYPTO_A_SOURCES = \
|
|||||||
src/common/tortls.c \
|
src/common/tortls.c \
|
||||||
$(libcrypto_extra_source)
|
$(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_a_SOURCES = $(LIBOR_A_SOURCES)
|
||||||
src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
|
src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
|
||||||
@ -119,6 +121,7 @@ COMMONHEADERS = \
|
|||||||
src/common/torlog.h \
|
src/common/torlog.h \
|
||||||
src/common/tortls.h \
|
src/common/tortls.h \
|
||||||
src/common/util.h \
|
src/common/util.h \
|
||||||
|
src/common/util_process.h \
|
||||||
$(libor_mempool_header)
|
$(libor_mempool_header)
|
||||||
|
|
||||||
noinst_HEADERS+= $(COMMONHEADERS)
|
noinst_HEADERS+= $(COMMONHEADERS)
|
||||||
|
@ -162,6 +162,7 @@ tor_validate_process_specifier(const char *process_spec,
|
|||||||
return parse_process_specifier(process_spec, &ppspec, msg);
|
return parse_process_specifier(process_spec, &ppspec, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* XXXX we should use periodic_timer_new() for this stuff */
|
||||||
#ifdef HAVE_EVENT2_EVENT_H
|
#ifdef HAVE_EVENT2_EVENT_H
|
||||||
#define PERIODIC_TIMER_FLAGS EV_PERSIST
|
#define PERIODIC_TIMER_FLAGS EV_PERSIST
|
||||||
#else
|
#else
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "address.h"
|
#include "address.h"
|
||||||
#include "sandbox.h"
|
#include "sandbox.h"
|
||||||
#include "backtrace.h"
|
#include "backtrace.h"
|
||||||
|
#include "util_process.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
@ -3629,13 +3630,7 @@ tor_terminate_process(process_handle_t *process_handle)
|
|||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
|
if (tor_get_exit_code(process_handle, 0, NULL) == PROCESS_EXIT_RUNNING) {
|
||||||
HANDLE handle;
|
HANDLE handle = process_handle->pid.hProcess;
|
||||||
/* 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;
|
|
||||||
|
|
||||||
if (!TerminateProcess(handle, 0))
|
if (!TerminateProcess(handle, 0))
|
||||||
return -1;
|
return -1;
|
||||||
@ -3643,7 +3638,10 @@ tor_terminate_process(process_handle_t *process_handle)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else /* Unix */
|
#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
|
#endif
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -3692,6 +3690,23 @@ process_handle_new(void)
|
|||||||
return out;
|
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
|
* @name child-process states
|
||||||
*
|
*
|
||||||
@ -4008,6 +4023,10 @@ tor_spawn_background(const char *const filename, const char **argv,
|
|||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_handle->waitpid_cb = set_waitpid_callback(pid,
|
||||||
|
process_handle_waitpid_cb,
|
||||||
|
process_handle);
|
||||||
|
|
||||||
process_handle->stderr_pipe = stderr_pipe[0];
|
process_handle->stderr_pipe = stderr_pipe[0];
|
||||||
retval = close(stderr_pipe[1]);
|
retval = close(stderr_pipe[1]);
|
||||||
|
|
||||||
@ -4072,6 +4091,8 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
|
|||||||
|
|
||||||
if (process_handle->stderr_handle)
|
if (process_handle->stderr_handle)
|
||||||
fclose(process_handle->stderr_handle);
|
fclose(process_handle->stderr_handle);
|
||||||
|
|
||||||
|
clear_waitpid_callback(process_handle->waitpid_cb);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(process_handle, 0x0f, sizeof(process_handle_t));
|
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
|
* probably not work in Tor, because waitpid() is called in main.c to reap any
|
||||||
* terminated child processes.*/
|
* terminated child processes.*/
|
||||||
int
|
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)
|
int block, int *exit_code)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -4129,7 +4150,20 @@ tor_get_exit_code(const process_handle_t *process_handle,
|
|||||||
int stat_loc;
|
int stat_loc;
|
||||||
int retval;
|
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) {
|
if (!block && 0 == retval) {
|
||||||
/* Process has not exited */
|
/* Process has not exited */
|
||||||
return PROCESS_EXIT_RUNNING;
|
return PROCESS_EXIT_RUNNING;
|
||||||
|
@ -446,6 +446,7 @@ void set_environment_variable_in_smartlist(struct smartlist_t *env_vars,
|
|||||||
#define PROCESS_STATUS_ERROR -1
|
#define PROCESS_STATUS_ERROR -1
|
||||||
|
|
||||||
#ifdef UTIL_PRIVATE
|
#ifdef UTIL_PRIVATE
|
||||||
|
struct waitpid_callback_t;
|
||||||
/** Structure to represent the state of a process with which Tor is
|
/** Structure to represent the state of a process with which Tor is
|
||||||
* communicating. The contents of this structure are private to util.c */
|
* communicating. The contents of this structure are private to util.c */
|
||||||
struct process_handle_t {
|
struct process_handle_t {
|
||||||
@ -461,6 +462,12 @@ struct process_handle_t {
|
|||||||
FILE *stdout_handle;
|
FILE *stdout_handle;
|
||||||
FILE *stderr_handle;
|
FILE *stderr_handle;
|
||||||
pid_t pid;
|
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 // _WIN32
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -469,7 +476,7 @@ struct process_handle_t {
|
|||||||
#define PROCESS_EXIT_RUNNING 1
|
#define PROCESS_EXIT_RUNNING 1
|
||||||
#define PROCESS_EXIT_EXITED 0
|
#define PROCESS_EXIT_EXITED 0
|
||||||
#define PROCESS_EXIT_ERROR -1
|
#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 block, int *exit_code);
|
||||||
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
|
int tor_split_lines(struct smartlist_t *sl, char *buf, int len);
|
||||||
#ifdef _WIN32
|
#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 "routerparse.h"
|
||||||
#include "statefile.h"
|
#include "statefile.h"
|
||||||
#include "status.h"
|
#include "status.h"
|
||||||
|
#include "util_process.h"
|
||||||
#include "ext_orport.h"
|
#include "ext_orport.h"
|
||||||
#ifdef USE_DMALLOC
|
#ifdef USE_DMALLOC
|
||||||
#include <dmalloc.h>
|
#include <dmalloc.h>
|
||||||
@ -2109,8 +2110,7 @@ process_signal(uintptr_t sig)
|
|||||||
break;
|
break;
|
||||||
#ifdef SIGCHLD
|
#ifdef SIGCHLD
|
||||||
case SIGCHLD:
|
case SIGCHLD:
|
||||||
while (waitpid(-1,NULL,WNOHANG) > 0) ; /* keep reaping until no more
|
notify_pending_waitpid_callbacks();
|
||||||
zombies */
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case SIGNEWNYM: {
|
case SIGNEWNYM: {
|
||||||
|
@ -42,9 +42,6 @@
|
|||||||
#include <sys/param.h> /* FreeBSD needs this to know what version it is */
|
#include <sys/param.h> /* FreeBSD needs this to know what version it is */
|
||||||
#endif
|
#endif
|
||||||
#include "torint.h"
|
#include "torint.h"
|
||||||
#ifdef HAVE_SYS_WAIT_H
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_SYS_FCNTL_H
|
#ifdef HAVE_SYS_FCNTL_H
|
||||||
#include <sys/fcntl.h>
|
#include <sys/fcntl.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,6 +9,13 @@
|
|||||||
#else
|
#else
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#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
|
/** Trivial test program which prints out its command line arguments so we can
|
||||||
* check if tor_spawn_background() works */
|
* check if tor_spawn_background() works */
|
||||||
@ -16,27 +23,38 @@ int
|
|||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
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(stdout, "OUT\n");
|
||||||
fprintf(stderr, "ERR\n");
|
fprintf(stderr, "ERR\n");
|
||||||
for (i = 1; i < argc; i++)
|
for (i = 1; i < argc; i++)
|
||||||
fprintf(stdout, "%s\n", argv[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()
|
/* We need to flush stdout so that test_util_spawn_background_partial_read()
|
||||||
succeed. Otherwise ReadFile() will get the entire output in one */
|
succeed. Otherwise ReadFile() will get the entire output in one */
|
||||||
// XXX: Can we make stdio flush on newline?
|
// XXX: Can we make stdio flush on newline?
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#ifdef _WIN32
|
if (!fast)
|
||||||
Sleep(1000);
|
SLEEP(1);
|
||||||
#else
|
|
||||||
sleep(1);
|
|
||||||
#endif
|
|
||||||
fprintf(stdout, "DONE\n");
|
fprintf(stdout, "DONE\n");
|
||||||
#ifdef _WIN32
|
fflush(stdout);
|
||||||
Sleep(1000);
|
if (fast)
|
||||||
#else
|
return 0;
|
||||||
sleep(1);
|
|
||||||
#endif
|
while (--delay) {
|
||||||
|
SLEEP(1);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "mempool.h"
|
#include "mempool.h"
|
||||||
#endif /* ENABLE_MEMPOOLS */
|
#endif /* ENABLE_MEMPOOLS */
|
||||||
#include "memarea.h"
|
#include "memarea.h"
|
||||||
|
#include "util_process.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
@ -2538,6 +2539,19 @@ test_util_fgets_eagain(void *ptr)
|
|||||||
}
|
}
|
||||||
#endif
|
#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 */
|
/** Helper function for testing tor_spawn_background */
|
||||||
static void
|
static void
|
||||||
run_util_spawn_background(const char *argv[], const char *expected_out,
|
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);
|
status = tor_spawn_background(argv[0], argv, NULL, &process_handle);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
notify_pending_waitpid_callbacks();
|
||||||
|
|
||||||
test_eq(expected_status, status);
|
test_eq(expected_status, status);
|
||||||
if (status == PROCESS_STATUS_ERROR)
|
if (status == PROCESS_STATUS_ERROR) {
|
||||||
|
tt_ptr_op(process_handle, ==, NULL);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
test_assert(process_handle != NULL);
|
test_assert(process_handle != NULL);
|
||||||
test_eq(expected_status, process_handle->status);
|
test_eq(expected_status, process_handle->status);
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
notify_pending_waitpid_callbacks();
|
||||||
|
tt_ptr_op(process_handle->waitpid_cb, !=, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
|
test_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
|
||||||
test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
|
test_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
|
||||||
#else
|
#else
|
||||||
test_assert(process_handle->stdout_pipe > 0);
|
test_assert(process_handle->stdout_pipe >= 0);
|
||||||
test_assert(process_handle->stderr_pipe > 0);
|
test_assert(process_handle->stderr_pipe >= 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check stdout */
|
/* Check stdout */
|
||||||
@ -2580,12 +2603,19 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
|
|||||||
test_eq(strlen(expected_out), pos);
|
test_eq(strlen(expected_out), pos);
|
||||||
test_streq(expected_out, stdout_buf);
|
test_streq(expected_out, stdout_buf);
|
||||||
|
|
||||||
|
notify_pending_waitpid_callbacks();
|
||||||
|
|
||||||
/* Check it terminated correctly */
|
/* Check it terminated correctly */
|
||||||
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
retval = tor_get_exit_code(process_handle, 1, &exit_code);
|
||||||
test_eq(PROCESS_EXIT_EXITED, retval);
|
test_eq(PROCESS_EXIT_EXITED, retval);
|
||||||
test_eq(expected_exit, exit_code);
|
test_eq(expected_exit, exit_code);
|
||||||
// TODO: Make test-child exit with something other than 0
|
// 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 */
|
/* Check stderr */
|
||||||
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
pos = tor_read_all_from_process_stderr(process_handle, stderr_buf,
|
||||||
sizeof(stderr_buf) - 1);
|
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_streq(expected_err, stderr_buf);
|
||||||
test_eq(strlen(expected_err), pos);
|
test_eq(strlen(expected_err), pos);
|
||||||
|
|
||||||
|
notify_pending_waitpid_callbacks();
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (process_handle)
|
if (process_handle)
|
||||||
tor_process_handle_destroy(process_handle, 1);
|
tor_process_handle_destroy(process_handle, 1);
|
||||||
@ -2603,29 +2635,20 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
|
|||||||
static void
|
static void
|
||||||
test_util_spawn_background_ok(void *ptr)
|
test_util_spawn_background_ok(void *ptr)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||||
const char *argv[] = {"test-child.exe", "--test", NULL};
|
const char *expected_out = "OUT"EOL "--test"EOL "SLEEPING"EOL "DONE" EOL;
|
||||||
const char *expected_out = "OUT\r\n--test\r\nSLEEPING\r\nDONE\r\n";
|
const char *expected_err = "ERR"EOL;
|
||||||
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
|
|
||||||
|
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
|
|
||||||
run_util_spawn_background(argv, expected_out, expected_err, 0,
|
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 */
|
/** Check that failing to find the executable works as expected */
|
||||||
static void
|
static void
|
||||||
test_util_spawn_background_fail(void *ptr)
|
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 *argv[] = {BUILDDIR "/src/test/no-such-file", "--test", NULL};
|
||||||
const char *expected_err = "";
|
const char *expected_err = "";
|
||||||
char expected_out[1024];
|
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);
|
"ERR: Failed to spawn background process - code %s\n", code);
|
||||||
|
|
||||||
run_util_spawn_background(argv, expected_out, expected_err, 255,
|
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
|
/** Test that reading from a handle returns a partial read rather than
|
||||||
* blocking */
|
* blocking */
|
||||||
static void
|
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_exit = 0;
|
||||||
const int expected_status = PROCESS_STATUS_RUNNING;
|
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;
|
process_handle_t *process_handle=NULL;
|
||||||
int status;
|
int status;
|
||||||
char stdout_buf[100], stderr_buf[100];
|
char stdout_buf[100], stderr_buf[100];
|
||||||
#ifdef _WIN32
|
|
||||||
const char *argv[] = {"test-child.exe", "--test", NULL};
|
const char *argv[] = {TEST_CHILD, "--test", NULL};
|
||||||
const char *expected_out[] = { "OUT\r\n--test\r\nSLEEPING\r\n",
|
const char *expected_out[] = { "OUT" EOL "--test" EOL "SLEEPING" EOL,
|
||||||
"DONE\r\n",
|
"DONE" EOL,
|
||||||
NULL };
|
NULL };
|
||||||
const char *expected_err = "ERR\r\n";
|
const char *expected_err = "ERR" EOL;
|
||||||
#else
|
|
||||||
const char *argv[] = {BUILDDIR "/src/test/test-child", "--test", NULL};
|
#ifndef _WIN32
|
||||||
const char *expected_out[] = { "OUT\n--test\nSLEEPING\n",
|
|
||||||
"DONE\n",
|
|
||||||
NULL };
|
|
||||||
const char *expected_err = "ERR\n";
|
|
||||||
int eof = 0;
|
int eof = 0;
|
||||||
#endif
|
#endif
|
||||||
int expected_out_ctr;
|
int expected_out_ctr;
|
||||||
(void)ptr;
|
|
||||||
|
if (exit_early) {
|
||||||
|
argv[1] = "--hang";
|
||||||
|
expected_out[0] = "OUT"EOL "--hang"EOL "SLEEPING" EOL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Start the program */
|
/* Start the program */
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -2713,6 +2736,12 @@ test_util_spawn_background_partial_read(void *ptr)
|
|||||||
expected_out_ctr++;
|
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 */
|
/* The process should have exited without writing more */
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
|
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);
|
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()
|
* 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_ok, 0),
|
||||||
UTIL_TEST(spawn_background_fail, 0),
|
UTIL_TEST(spawn_background_fail, 0),
|
||||||
UTIL_TEST(spawn_background_partial_read, 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_hex_number, 0),
|
||||||
UTIL_TEST(format_dec_number, 0),
|
UTIL_TEST(format_dec_number, 0),
|
||||||
UTIL_TEST(join_win_cmdline, 0),
|
UTIL_TEST(join_win_cmdline, 0),
|
||||||
|
Loading…
Reference in New Issue
Block a user