mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-14 07:03:44 +01:00
Merge remote branch 'sjmurdoch/bug1903'
This commit is contained in:
commit
544a8afe5a
1
.gitignore
vendored
1
.gitignore
vendored
@ -155,6 +155,7 @@
|
|||||||
/src/test/Makefile
|
/src/test/Makefile
|
||||||
/src/test/Makefile.in
|
/src/test/Makefile.in
|
||||||
/src/test/test
|
/src/test/test
|
||||||
|
/src/test/test-child
|
||||||
|
|
||||||
|
|
||||||
# /src/tools/
|
# /src/tools/
|
||||||
|
@ -2883,17 +2883,34 @@ load_windows_system_library(const TCHAR *library_name)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Format child_state and saved_errno as a hex string placed in hex_errno.
|
/** Format <b>child_state</b> and <b>saved_errno</b> as a hex string placed in
|
||||||
* Called between fork and _exit, so must be signal-handler safe */
|
* <b>hex_errno</b>. Called between fork and _exit, so must be signal-handler
|
||||||
|
* safe.
|
||||||
|
*
|
||||||
|
* <b>hex_errno</b> must have at least HEX_ERRNO_SIZE bytes available.
|
||||||
|
*
|
||||||
|
* The format of <b>hex_errno</b> is: "CHILD_STATE/ERRNO\n", left-padded
|
||||||
|
* with spaces. Note that there is no trailing \0. CHILD_STATE indicates where
|
||||||
|
* in the processs of starting the child process did the failure occur (see
|
||||||
|
* CHILD_STATE_* macros for definition), and SAVED_ERRNO is the value of
|
||||||
|
* errno when the failure occurred.
|
||||||
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
format_helper_exit_status(unsigned char child_state, int saved_errno,
|
format_helper_exit_status(unsigned char child_state, int saved_errno,
|
||||||
char *hex_errno)
|
char *hex_errno)
|
||||||
{
|
{
|
||||||
/* Convert errno to be unsigned for hex conversion */
|
|
||||||
unsigned int unsigned_errno;
|
unsigned int unsigned_errno;
|
||||||
char *cur;
|
char *cur;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
/* If errno is negative, negate it */
|
/* Fill hex_errno with spaces, and a trailing newline (memset may
|
||||||
|
not be signal handler safe, so we can't use it) */
|
||||||
|
for (i = 0; i < (HEX_ERRNO_SIZE - 1); i++)
|
||||||
|
hex_errno[i] = ' ';
|
||||||
|
hex_errno[HEX_ERRNO_SIZE - 1] = '\n';
|
||||||
|
|
||||||
|
/* Convert errno to be unsigned for hex conversion */
|
||||||
if (saved_errno < 0) {
|
if (saved_errno < 0) {
|
||||||
unsigned_errno = (unsigned int) -saved_errno;
|
unsigned_errno = (unsigned int) -saved_errno;
|
||||||
} else {
|
} else {
|
||||||
@ -2903,18 +2920,27 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|||||||
/* Convert errno to hex (start before \n) */
|
/* Convert errno to hex (start before \n) */
|
||||||
cur = hex_errno + HEX_ERRNO_SIZE - 2;
|
cur = hex_errno + HEX_ERRNO_SIZE - 2;
|
||||||
|
|
||||||
|
/* Check for overflow on first iteration of the loop */
|
||||||
|
if (cur < hex_errno)
|
||||||
|
return;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
*cur-- = "0123456789ABCDEF"[unsigned_errno % 16];
|
*cur-- = "0123456789ABCDEF"[unsigned_errno % 16];
|
||||||
unsigned_errno /= 16;
|
unsigned_errno /= 16;
|
||||||
} while (unsigned_errno != 0 && cur >= hex_errno);
|
} while (unsigned_errno != 0 && cur >= hex_errno);
|
||||||
|
|
||||||
/* Add on the minus side if errno was negative */
|
/* Prepend the minus sign if errno was negative */
|
||||||
if (saved_errno < 0)
|
if (saved_errno < 0 && cur >= hex_errno)
|
||||||
*cur-- = '-';
|
*cur-- = '-';
|
||||||
|
|
||||||
/* Leave a gap */
|
/* Leave a gap */
|
||||||
|
if (cur >= hex_errno)
|
||||||
*cur-- = '/';
|
*cur-- = '/';
|
||||||
|
|
||||||
|
/* Check for overflow on first iteration of the loop */
|
||||||
|
if (cur < hex_errno)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Convert child_state to hex */
|
/* Convert child_state to hex */
|
||||||
do {
|
do {
|
||||||
*cur-- = "0123456789ABCDEF"[child_state % 16];
|
*cur-- = "0123456789ABCDEF"[child_state % 16];
|
||||||
@ -2938,13 +2964,20 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
|
|||||||
|
|
||||||
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
|
||||||
|
|
||||||
/** Start a program in the background. If <b>filename</b> contains a '/', then
|
/** Start a program in the background. If <b>filename</b> contains a '/',
|
||||||
* it will be treated as an absolute or relative path. Otherwise the system
|
* then it will be treated as an absolute or relative path. Otherwise the
|
||||||
* path will be searched for <b>filename</b>. Returns pid on success, otherwise
|
* system path will be searched for <b>filename</b>. The strings in
|
||||||
* returns -1.
|
* <b>argv</b> will be passed as the command line arguments of the child
|
||||||
* Some parts of this code are based on the POSIX subprocess module from Python
|
* program (following convention, argv[0] should normally be the filename of
|
||||||
|
* the executable). The last element of argv must be NULL. If the child
|
||||||
|
* program is launched, the PID will be returned and <b>stdout_read</b> and
|
||||||
|
* <b>stdout_err</b> will be set to file descriptors from which the stdout
|
||||||
|
* and stderr, respectively, output of the child program can be read, and the
|
||||||
|
* stdin of the child process shall be set to /dev/null. Otherwise returns
|
||||||
|
* -1. Some parts of this code are based on the POSIX subprocess module from
|
||||||
|
* Python.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
tor_spawn_background(const char *const filename, int *stdout_read,
|
tor_spawn_background(const char *const filename, int *stdout_read,
|
||||||
int *stderr_read, const char **argv)
|
int *stderr_read, const char **argv)
|
||||||
{
|
{
|
||||||
@ -2974,16 +3007,12 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
and we are not allowed to use unsafe functions between fork and exec */
|
and we are not allowed to use unsafe functions between fork and exec */
|
||||||
error_message_length = strlen(error_message);
|
error_message_length = strlen(error_message);
|
||||||
|
|
||||||
/* Fill hex_errno with spaces, and a trailing newline */
|
|
||||||
memset(hex_errno, ' ', sizeof(hex_errno) - 1);
|
|
||||||
hex_errno[sizeof(hex_errno) - 1] = '\n';
|
|
||||||
|
|
||||||
child_state = CHILD_STATE_PIPE;
|
child_state = CHILD_STATE_PIPE;
|
||||||
|
|
||||||
/* Set up pipe for redirecting stdout and stderr of child */
|
/* Set up pipe for redirecting stdout and stderr of child */
|
||||||
retval = pipe(stdout_pipe);
|
retval = pipe(stdout_pipe);
|
||||||
if (-1 == retval) {
|
if (-1 == retval) {
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Failed to set up pipe for stdout communication with child process: %s",
|
"Failed to set up pipe for stdout communication with child process: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -2991,7 +3020,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
|
|
||||||
retval = pipe(stderr_pipe);
|
retval = pipe(stderr_pipe);
|
||||||
if (-1 == retval) {
|
if (-1 == retval) {
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Failed to set up pipe for stderr communication with child process: %s",
|
"Failed to set up pipe for stderr communication with child process: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -3043,7 +3072,8 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
child_state = CHILD_STATE_CLOSEFD;
|
child_state = CHILD_STATE_CLOSEFD;
|
||||||
|
|
||||||
/* Close all other fds, including the read end of the pipe */
|
/* Close all other fds, including the read end of the pipe */
|
||||||
/* TODO: use closefrom if available */
|
/* XXX: use closefrom if available, or better still set FD_CLOEXEC
|
||||||
|
on all of Tor's open files */
|
||||||
for (fd = STDERR_FILENO + 1; fd < max_fd; fd++)
|
for (fd = STDERR_FILENO + 1; fd < max_fd; fd++)
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
@ -3059,7 +3089,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
child_state = CHILD_STATE_FAILEXEC;
|
child_state = CHILD_STATE_FAILEXEC;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
/* TODO: are we leaking fds from the pipe? */
|
/* XXX: are we leaking fds from the pipe? */
|
||||||
|
|
||||||
format_helper_exit_status(child_state, errno, hex_errno);
|
format_helper_exit_status(child_state, errno, hex_errno);
|
||||||
|
|
||||||
@ -3075,7 +3105,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
/* In parent */
|
/* In parent */
|
||||||
|
|
||||||
if (-1 == pid) {
|
if (-1 == pid) {
|
||||||
log_err(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
|
log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
|
||||||
close(stdout_pipe[0]);
|
close(stdout_pipe[0]);
|
||||||
close(stdout_pipe[1]);
|
close(stdout_pipe[1]);
|
||||||
close(stderr_pipe[0]);
|
close(stderr_pipe[0]);
|
||||||
@ -3088,7 +3118,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
retval = close(stdout_pipe[1]);
|
retval = close(stdout_pipe[1]);
|
||||||
|
|
||||||
if (-1 == retval) {
|
if (-1 == retval) {
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Failed to close write end of stdout pipe in parent process: %s",
|
"Failed to close write end of stdout pipe in parent process: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
/* Do not return -1, because the child is running, so the parent
|
/* Do not return -1, because the child is running, so the parent
|
||||||
@ -3099,7 +3129,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
|
|||||||
retval = close(stderr_pipe[1]);
|
retval = close(stderr_pipe[1]);
|
||||||
|
|
||||||
if (-1 == retval) {
|
if (-1 == retval) {
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Failed to close write end of stderr pipe in parent process: %s",
|
"Failed to close write end of stderr pipe in parent process: %s",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
/* Do not return -1, because the child is running, so the parent
|
/* Do not return -1, because the child is running, so the parent
|
||||||
@ -3152,26 +3182,27 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
|
|||||||
} else {
|
} else {
|
||||||
/* No newline; check whether we overflowed the buffer */
|
/* No newline; check whether we overflowed the buffer */
|
||||||
if (!feof(stream))
|
if (!feof(stream))
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Line from port forwarding helper was truncated: %s", buf);
|
"Line from port forwarding helper was truncated: %s", buf);
|
||||||
/* TODO: What to do with this error? */
|
/* TODO: What to do with this error? */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
|
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
|
||||||
if (strstr(buf, SPAWN_ERROR_MESSAGE) == buf) {
|
if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
|
||||||
/* Parse error message */
|
/* Parse error message */
|
||||||
int retval, child_state, saved_errno;
|
int retval, child_state, saved_errno;
|
||||||
retval = sscanf(buf, SPAWN_ERROR_MESSAGE "%d/%d",
|
retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
|
||||||
&child_state, &saved_errno);
|
&child_state, &saved_errno);
|
||||||
if (retval == 2) {
|
if (retval == 2) {
|
||||||
log_err(LD_GENERAL,
|
log_warn(LD_GENERAL,
|
||||||
"Failed to start child process \"%s\" in state %d: %s",
|
"Failed to start child process \"%s\" in state %d: %s",
|
||||||
executable, child_state, strerror(saved_errno));
|
executable, child_state, strerror(saved_errno));
|
||||||
if (child_status)
|
if (child_status)
|
||||||
*child_status = 1;
|
*child_status = 1;
|
||||||
} else {
|
} else {
|
||||||
/* Failed to parse message from child process, log it as error */
|
/* Failed to parse message from child process, log it as a
|
||||||
log_err(LD_GENERAL,
|
warning */
|
||||||
|
log_warn(LD_GENERAL,
|
||||||
"Unexpected message from port forwarding helper \"%s\": %s",
|
"Unexpected message from port forwarding helper \"%s\": %s",
|
||||||
executable, buf);
|
executable, buf);
|
||||||
}
|
}
|
||||||
@ -3237,7 +3268,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|||||||
|
|
||||||
child_pid = tor_spawn_background(filename, &fd_out, &fd_err, argv);
|
child_pid = tor_spawn_background(filename, &fd_out, &fd_err, argv);
|
||||||
if (child_pid < 0) {
|
if (child_pid < 0) {
|
||||||
log_err(LD_GENERAL, "Failed to start port forwarding helper %s",
|
log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
|
||||||
filename);
|
filename);
|
||||||
child_pid = -1;
|
child_pid = -1;
|
||||||
return;
|
return;
|
||||||
@ -3258,7 +3289,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|||||||
/* Read from stdout/stderr and log result */
|
/* Read from stdout/stderr and log result */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
stdout_status = log_from_pipe(stdout_read, LOG_INFO, filename, &retval);
|
stdout_status = log_from_pipe(stdout_read, LOG_INFO, filename, &retval);
|
||||||
stderr_status = log_from_pipe(stderr_read, LOG_ERR, filename, &retval);
|
stderr_status = log_from_pipe(stderr_read, LOG_WARN, filename, &retval);
|
||||||
if (retval) {
|
if (retval) {
|
||||||
/* There was a problem in the child process */
|
/* There was a problem in the child process */
|
||||||
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
|
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
|
||||||
@ -3280,7 +3311,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
|
|||||||
if (1 == retval) {
|
if (1 == retval) {
|
||||||
log_info(LD_GENERAL, "Port forwarding helper terminated");
|
log_info(LD_GENERAL, "Port forwarding helper terminated");
|
||||||
} else {
|
} else {
|
||||||
log_err(LD_GENERAL, "Failed to read from port forwarding helper");
|
log_warn(LD_GENERAL, "Failed to read from port forwarding helper");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: The child might not actually be finished (maybe it failed or
|
/* TODO: The child might not actually be finished (maybe it failed or
|
||||||
|
@ -350,6 +350,8 @@ HANDLE load_windows_system_library(const TCHAR *library_name);
|
|||||||
|
|
||||||
#ifdef UTIL_PRIVATE
|
#ifdef UTIL_PRIVATE
|
||||||
/* Prototypes for private functions only used by util.c (and unit tests) */
|
/* Prototypes for private functions only used by util.c (and unit tests) */
|
||||||
|
int tor_spawn_background(const char *const filename, int *stdout_read,
|
||||||
|
int *stderr_read, const char **argv);
|
||||||
void format_helper_exit_status(unsigned char child_state,
|
void format_helper_exit_status(unsigned char child_state,
|
||||||
int saved_errno, char *hex_errno);
|
int saved_errno, char *hex_errno);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
TESTS = test
|
TESTS = test
|
||||||
|
|
||||||
noinst_PROGRAMS = test
|
noinst_PROGRAMS = test test-child
|
||||||
|
|
||||||
AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
|
AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
|
||||||
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
|
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
|
||||||
|
18
src/test/test-child.c
Normal file
18
src/test/test-child.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/** Trivial test program which prints out its command line arguments so we can
|
||||||
|
* check if tor_spawn_background() works */
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
fprintf(stdout, "OUT\n");
|
||||||
|
fprintf(stderr, "ERR\n");
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
fprintf(stdout, "%s\n", argv[i]);
|
||||||
|
fprintf(stdout, "DONE\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -1224,14 +1224,13 @@ test_util_load_win_lib(void *ptr)
|
|||||||
static void
|
static void
|
||||||
clear_hex_errno(char *hex_errno)
|
clear_hex_errno(char *hex_errno)
|
||||||
{
|
{
|
||||||
memset(hex_errno, ' ', HEX_ERRNO_SIZE - 2);
|
memset(hex_errno, '\0', HEX_ERRNO_SIZE + 1);
|
||||||
hex_errno[HEX_ERRNO_SIZE - 1] = '\n';
|
|
||||||
hex_errno[HEX_ERRNO_SIZE] = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_util_exit_status(void *ptr)
|
test_util_exit_status(void *ptr)
|
||||||
{
|
{
|
||||||
|
/* Leave an extra byte for a \0 so we can do string comparison */
|
||||||
char hex_errno[HEX_ERRNO_SIZE + 1];
|
char hex_errno[HEX_ERRNO_SIZE + 1];
|
||||||
|
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
@ -1260,6 +1259,164 @@ test_util_exit_status(void *ptr)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef MS_WINDOWS
|
||||||
|
/** Check that fgets waits until a full line, and not return a partial line, on
|
||||||
|
* a EAGAIN with a non-blocking pipe */
|
||||||
|
static void
|
||||||
|
test_util_fgets_eagain(void *ptr)
|
||||||
|
{
|
||||||
|
int test_pipe[2] = {-1, -1};
|
||||||
|
int retval;
|
||||||
|
ssize_t retlen;
|
||||||
|
char *retptr;
|
||||||
|
FILE *test_stream = NULL;
|
||||||
|
char buf[10];
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
/* Set up a pipe to test on */
|
||||||
|
retval = pipe(test_pipe);
|
||||||
|
tt_int_op(retval, >=, 0);
|
||||||
|
|
||||||
|
/* Set up the read-end to be non-blocking */
|
||||||
|
retval = fcntl(test_pipe[0], F_SETFL, O_NONBLOCK);
|
||||||
|
tt_int_op(retval, >=, 0);
|
||||||
|
|
||||||
|
/* Open it as a stdio stream */
|
||||||
|
test_stream = fdopen(test_pipe[0], "r");
|
||||||
|
tt_ptr_op(test_stream, !=, NULL);
|
||||||
|
|
||||||
|
/* Send in a partial line */
|
||||||
|
retlen = write(test_pipe[1], "A", 1);
|
||||||
|
tt_int_op(retlen, ==, 1);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_want(retptr == NULL);
|
||||||
|
tt_int_op(errno, ==, EAGAIN);
|
||||||
|
|
||||||
|
/* Send in the rest */
|
||||||
|
retlen = write(test_pipe[1], "B\n", 2);
|
||||||
|
tt_int_op(retlen, ==, 2);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, buf);
|
||||||
|
tt_str_op(buf, ==, "AB\n");
|
||||||
|
|
||||||
|
/* Send in a full line */
|
||||||
|
retlen = write(test_pipe[1], "CD\n", 3);
|
||||||
|
tt_int_op(retlen, ==, 3);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, buf);
|
||||||
|
tt_str_op(buf, ==, "CD\n");
|
||||||
|
|
||||||
|
/* Send in a partial line */
|
||||||
|
retlen = write(test_pipe[1], "E", 1);
|
||||||
|
tt_int_op(retlen, ==, 1);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, NULL);
|
||||||
|
tt_int_op(errno, ==, EAGAIN);
|
||||||
|
|
||||||
|
/* Send in the rest */
|
||||||
|
retlen = write(test_pipe[1], "F\n", 2);
|
||||||
|
tt_int_op(retlen, ==, 2);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, buf);
|
||||||
|
tt_str_op(buf, ==, "EF\n");
|
||||||
|
|
||||||
|
/* Send in a full line and close */
|
||||||
|
retlen = write(test_pipe[1], "GH", 2);
|
||||||
|
tt_int_op(retlen, ==, 2);
|
||||||
|
retval = close(test_pipe[1]);
|
||||||
|
test_pipe[1] = -1;
|
||||||
|
tt_int_op(retval, ==, 0);
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, buf);
|
||||||
|
tt_str_op(buf, ==, "GH");
|
||||||
|
|
||||||
|
/* Check for EOF */
|
||||||
|
retptr = fgets(buf, sizeof(buf), test_stream);
|
||||||
|
tt_ptr_op(retptr, ==, NULL);
|
||||||
|
tt_int_op(feof(test_stream), >, 0);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (test_stream != NULL)
|
||||||
|
fclose(test_stream);
|
||||||
|
if (test_pipe[0] != -1)
|
||||||
|
close(test_pipe[0]);
|
||||||
|
if (test_pipe[1] != -1)
|
||||||
|
close(test_pipe[1]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MS_WINDOWS
|
||||||
|
/** Helper function for testing tor_spawn_background */
|
||||||
|
static void
|
||||||
|
run_util_spawn_background(const char *argv[], const char *expected_out,
|
||||||
|
const char *expected_err, int expected_exit)
|
||||||
|
{
|
||||||
|
int stdout_pipe=-1, stderr_pipe=-1;
|
||||||
|
int retval, stat_loc;
|
||||||
|
pid_t pid;
|
||||||
|
ssize_t pos;
|
||||||
|
char stdout_buf[100], stderr_buf[100];
|
||||||
|
|
||||||
|
/* Start the program */
|
||||||
|
retval = tor_spawn_background(argv[0], &stdout_pipe, &stderr_pipe, argv);
|
||||||
|
tt_int_op(retval, >, 0);
|
||||||
|
tt_int_op(stdout_pipe, >, 0);
|
||||||
|
tt_int_op(stderr_pipe, >, 0);
|
||||||
|
pid = retval;
|
||||||
|
|
||||||
|
/* Check stdout */
|
||||||
|
pos = read(stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1);
|
||||||
|
stdout_buf[pos] = '\0';
|
||||||
|
tt_int_op(pos, ==, strlen(expected_out));
|
||||||
|
tt_str_op(stdout_buf, ==, expected_out);
|
||||||
|
|
||||||
|
/* Check it terminated correctly */
|
||||||
|
retval = waitpid(pid, &stat_loc, 0);
|
||||||
|
tt_int_op(retval, ==, pid);
|
||||||
|
tt_assert(WIFEXITED(stat_loc));
|
||||||
|
tt_int_op(WEXITSTATUS(stat_loc), ==, expected_exit);
|
||||||
|
tt_assert(!WIFSIGNALED(stat_loc));
|
||||||
|
tt_assert(!WIFSTOPPED(stat_loc));
|
||||||
|
|
||||||
|
/* Check stderr */
|
||||||
|
pos = read(stderr_pipe, stderr_buf, sizeof(stderr_buf) - 1);
|
||||||
|
stderr_buf[pos] = '\0';
|
||||||
|
tt_int_op(pos, ==, strlen(expected_err));
|
||||||
|
tt_str_op(stderr_buf, ==, expected_err);
|
||||||
|
|
||||||
|
done:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that we can launch a process and read the output */
|
||||||
|
static void
|
||||||
|
test_util_spawn_background_ok(void *ptr)
|
||||||
|
{
|
||||||
|
const char *argv[] = {"src/test/test-child", "--test", NULL};
|
||||||
|
const char *expected_out = "OUT\nsrc/test/test-child\n--test\nDONE\n";
|
||||||
|
const char *expected_err = "ERR\n";
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
run_util_spawn_background(argv, expected_out, expected_err, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check that failing to find the executable works as expected */
|
||||||
|
static void
|
||||||
|
test_util_spawn_background_fail(void *ptr)
|
||||||
|
{
|
||||||
|
const char *argv[] = {"src/test/no-such-file", "--test", NULL};
|
||||||
|
const char *expected_out = "ERR: Failed to spawn background process "
|
||||||
|
"- code 9/2\n";
|
||||||
|
const char *expected_err = "";
|
||||||
|
|
||||||
|
(void)ptr;
|
||||||
|
|
||||||
|
run_util_spawn_background(argv, expected_out, expected_err, 255);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#define UTIL_LEGACY(name) \
|
#define UTIL_LEGACY(name) \
|
||||||
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
|
{ #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name }
|
||||||
|
|
||||||
@ -1287,6 +1444,11 @@ struct testcase_t util_tests[] = {
|
|||||||
UTIL_TEST(load_win_lib, 0),
|
UTIL_TEST(load_win_lib, 0),
|
||||||
#endif
|
#endif
|
||||||
UTIL_TEST(exit_status, 0),
|
UTIL_TEST(exit_status, 0),
|
||||||
|
#ifndef MS_WINDOWS
|
||||||
|
UTIL_TEST(fgets_eagain, TT_SKIP),
|
||||||
|
UTIL_TEST(spawn_background_ok, 0),
|
||||||
|
UTIL_TEST(spawn_background_fail, 0),
|
||||||
|
#endif
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user