diff --git a/src/common/util.c b/src/common/util.c index b95ee3a612..5bc7a8017b 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2978,14 +2978,15 @@ 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. */ -int -tor_spawn_background(const char *const filename, int *stdout_read, - int *stderr_read, const char **argv) +process_handle_t +tor_spawn_background(const char *const filename, const char **argv) { + process_handle_t process_handle; #ifdef MS_WINDOWS - (void) filename; (void) stdout_read; (void) stderr_read; (void) argv; + (void) filename; (void) argv; log_warn(LD_BUG, "not yet implemented on Windows."); - return -1; + process_handle.status = -1; + return process_handle; #else pid_t pid; int stdout_pipe[2]; @@ -3016,7 +3017,8 @@ tor_spawn_background(const char *const filename, int *stdout_read, log_warn(LD_GENERAL, "Failed to set up pipe for stdout communication with child process: %s", strerror(errno)); - return -1; + process_handle.status = -1; + return process_handle; } retval = pipe(stderr_pipe); @@ -3024,7 +3026,8 @@ tor_spawn_background(const char *const filename, int *stdout_read, log_warn(LD_GENERAL, "Failed to set up pipe for stderr communication with child process: %s", strerror(errno)); - return -1; + process_handle.status = -1; + return process_handle; } child_state = CHILD_STATE_MAXFD; @@ -3109,7 +3112,8 @@ tor_spawn_background(const char *const filename, int *stdout_read, (void) nbytes; _exit(255); - return -1; /* Never reached, but avoids compiler warning */ + process_handle.status = -1; + return process_handle; /* Never reached, but avoids compiler warning */ } /* In parent */ @@ -3120,11 +3124,12 @@ tor_spawn_background(const char *const filename, int *stdout_read, close(stdout_pipe[1]); close(stderr_pipe[0]); close(stderr_pipe[1]); - return -1; + process_handle.status = -1; + return process_handle; } /* Return read end of the pipes to caller, and close write end */ - *stdout_read = stdout_pipe[0]; + process_handle.stdout_pipe = stdout_pipe[0]; retval = close(stdout_pipe[1]); if (-1 == retval) { @@ -3135,7 +3140,7 @@ tor_spawn_background(const char *const filename, int *stdout_read, needs to know about the pid in order to reap it later */ } - *stderr_read = stderr_pipe[0]; + process_handle.stderr_pipe = stderr_pipe[0]; retval = close(stderr_pipe[1]); if (-1 == retval) { @@ -3146,7 +3151,56 @@ tor_spawn_background(const char *const filename, int *stdout_read, needs to know about the pid in order to reap it later */ } - return pid; + process_handle.status = 0; + process_handle.pid = pid; + return process_handle; +#endif +} + +int +tor_get_exit_code(const process_handle_t process_handle) +{ +#ifdef MS_WINDOWS + log_warn(LD_BUG, "not yet implemented on Windows."); + return -1; +#else + int stat_loc; + + retval = waitpid(process_handle.pid, &stat_loc, 0); + if (retval != process_handle.pid) { + log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s", process_handle.pid, + sterror(errno)); + return -1; + } + + if (!WIFEXITED(stat_loc)) { + log_warn(LD_GENERAL, "Process %d did not exit normally", process_handle.pid); + return -1; + } + + return WEXITSTATUS(stat_loc); +#endif // MS_WINDOWS +} + +ssize_t +tor_read_all_from_process_stdin(const process_handle_t process_handle, + char *buf, size_t count) +{ +#ifdef MS_WINDOWS + return -1; +#else + return read_all(process_handle.stdin_pipe, buf, count, 0); +#endif +} + +ssize_t +tor_read_all_from_process_stderr(const process_handle_t process_handle, + char *buf, size_t count) +{ +#ifdef MS_WINDOWS + return -1; +#else + return read_all(process_handle.stderr_pipe, buf, count, 0); #endif } diff --git a/src/common/util.h b/src/common/util.h index 6496c42db8..2cf57a125d 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -347,8 +347,27 @@ 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); + +typedef struct process_handle_s { + int status; +#ifdef MS_WINDOWS + HANDLE stdout_pipe; + HANDLE stderr_pipe; + HANDLE pid; +#else + int stdout_pipe; + int stderr_pipe; + int pid; +#endif // MS_WINDOWS +} process_handle_t; + +process_handle_t tor_spawn_background(const char *const filename, + const char **argv); +int tor_get_exit_code(const process_handle_t pid); +ssize_t tor_read_all_from_process_stdin(const process_handle_t process_handle, + char *buf, size_t count); +ssize_t tor_read_all_from_process_stderr(const process_handle_t process_handle, + char *buf, size_t count); void format_helper_exit_status(unsigned char child_state, int saved_errno, char *hex_errno); diff --git a/src/test/test_util.c b/src/test/test_util.c index c4769e6407..fce53b835e 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1376,42 +1376,38 @@ 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; + int retval; ssize_t pos; + process_handle_t process_handle; 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; + process_handle = tor_spawn_background(argv[0], argv); + + tt_int_op(process_handle.status, ==, 0); + tt_int_op(process_handle.stdout_pipe, >, 0); + tt_int_op(process_handle.stderr_pipe, >, 0); /* Check stdout */ - pos = read_all(stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, 0); + pos = tor_read_all_from_process_stdin(process_handle, stdout_buf, + sizeof(stdout_buf) - 1); tt_assert(pos >= 0); 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)); + retval = tor_get_exit_code(process_handle); + tt_int_op(retval, ==, expected_exit); /* Check stderr */ - pos = read_all(stderr_pipe, stderr_buf, sizeof(stderr_buf) - 1, 0); + pos = tor_read_all_from_process_stderr(process_handle, stderr_buf, + sizeof(stderr_buf) - 1); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; tt_int_op(pos, ==, strlen(expected_err)); @@ -1447,7 +1443,6 @@ test_util_spawn_background_fail(void *ptr) run_util_spawn_background(argv, expected_out, expected_err, 255); } -#endif static void test_util_di_ops(void) @@ -1533,9 +1528,9 @@ struct testcase_t util_tests[] = { UTIL_TEST(exit_status, 0), #ifndef MS_WINDOWS UTIL_TEST(fgets_eagain, TT_SKIP), +#endif UTIL_TEST(spawn_background_ok, 0), UTIL_TEST(spawn_background_fail, 0), -#endif END_OF_TESTCASES };