diff --git a/.gitignore b/.gitignore index 369d925a9..6a6c14e6a 100644 --- a/.gitignore +++ b/.gitignore @@ -155,6 +155,7 @@ /src/test/Makefile /src/test/Makefile.in /src/test/test +/src/test/test-child # /src/tools/ diff --git a/src/common/util.c b/src/common/util.c index 402cfbf78..cddc8a9a9 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2973,7 +2973,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno, * -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, int *stderr_read, const char **argv) { diff --git a/src/common/util.h b/src/common/util.h index 86555eeb1..8c2a9be32 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -350,6 +350,8 @@ HANDLE load_windows_system_library(const TCHAR *library_name); #ifdef UTIL_PRIVATE /* 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, int saved_errno, char *hex_errno); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index cfe330c74..976c2756c 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -1,6 +1,6 @@ TESTS = test -noinst_PROGRAMS = test +noinst_PROGRAMS = test test-child AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ diff --git a/src/test/test-child.c b/src/test/test-child.c new file mode 100644 index 000000000..100e8c0f3 --- /dev/null +++ b/src/test/test-child.c @@ -0,0 +1,18 @@ +#include + +/** 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; +} + diff --git a/src/test/test_util.c b/src/test/test_util.c index f2744ee7d..99b758713 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1334,6 +1334,77 @@ test_util_fgets_eagain(void *ptr) } #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) \ { #name, legacy_test_helper, 0, &legacy_setup, test_util_ ## name } @@ -1363,6 +1434,8 @@ struct testcase_t util_tests[] = { UTIL_TEST(exit_status, 0), #ifndef MS_WINDOWS UTIL_TEST(fgets_eagain, 0), + UTIL_TEST(spawn_background_ok, 0), + UTIL_TEST(spawn_background_fail, 0), #endif END_OF_TESTCASES };