From 5b8a20ed447f11cda2e0bf528dd78e41e4b76fa4 Mon Sep 17 00:00:00 2001 From: Steven Murdoch Date: Thu, 1 Sep 2011 01:06:12 +0100 Subject: [PATCH] Make a version of tor_read_all_handle() for non-Windows platforms Mainly used for testing reading from subprocesses. To be more generic we now pass in a pointer to a process_handle_t rather than a Windows- specific HANDLE. --- src/common/util.c | 72 ++++++++++++++++++++++++++++++++++++-------- src/common/util.h | 13 +++++--- src/test/test_util.c | 43 +++++++++++++++----------- 3 files changed, 94 insertions(+), 34 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index 09eb969c2c..30c298e0dc 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -3522,7 +3522,8 @@ tor_get_exit_code(const process_handle_t process_handle, * only once the process has exited, or count bytes are read. Returns * the number of bytes read, or -1 on error. */ ssize_t -tor_read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess) +tor_read_all_handle(HANDLE h, char *buf, size_t count, + const process_handle_t *process) { size_t numread = 0; BOOL retval; @@ -3544,7 +3545,7 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess) /* Nothing available: process exited or it is busy */ /* Exit if we don't know whether the process is running */ - if (NULL == hProcess) + if (NULL == process) break; /* The process exited and there's nothing left to read from it */ @@ -3554,7 +3555,8 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess) /* If process is not running, check for output one more time in case it wrote something after the peek was performed. Otherwise keep on waiting for output */ - byte_count = WaitForSingleObject(hProcess, 0); + tor_assert(process != NULL); + byte_count = WaitForSingleObject(process->pid.hProcess, 0); if (WAIT_TIMEOUT != byte_count) process_exited = TRUE; @@ -3576,31 +3578,77 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count, HANDLE hProcess) } return (ssize_t)numread; } +#else +/** Read from a handle h into buf, up to count bytes. If + * process is NULL, the function will return immediately if there is + * nothing more to read. Otherwise data will be read until end of file, or + * count bytes are read. Returns the number of bytes read, or -1 on + * error. */ +ssize_t +tor_read_all_handle(FILE *h, char *buf, size_t count, + const process_handle_t *process) +{ + size_t numread = 0; + char *retval; + + if (count > SIZE_T_CEILING || count > SSIZE_T_MAX) + return -1; + + while (numread != count) { + /* Use fgets because that is what we use in log_from_pipe() */ + retval = fgets(buf+numread, (int)(count-numread), h); + if (NULL == retval) { + if (feof(h)) { + fclose(h); + break; + } else { + if (EAGAIN == errno) { + if (process) + continue; + else + break; + } else { + log_warn(LD_GENERAL, "fgets() from handle failed: %s", + strerror(errno)); + fclose(h); + return -1; + } + } + } + tor_assert(retval != NULL); + numread += strlen(retval); + } + + log_debug(LD_GENERAL, "fgets read %d bytes from handle", (int)numread); + return (ssize_t)numread; +} #endif /** Read from stdout of a process until the process exits. */ ssize_t -tor_read_all_from_process_stdout(const process_handle_t process_handle, - char *buf, size_t count) +tor_read_all_from_process_stdout(const process_handle_t *process_handle, + char *buf, size_t count) { #ifdef MS_WINDOWS - return tor_read_all_handle(process_handle.stdout_pipe, buf, count, - process_handle.pid.hProcess); + return tor_read_all_handle(process_handle->stdout_pipe, buf, count, + process_handle); #else - return read_all(process_handle.stdout_pipe, buf, count, 0); + return tor_read_all_handle(process_handle->stdout_handle, buf, count, + process_handle); #endif } /** Read from stdout of a process until the process exits. */ ssize_t -tor_read_all_from_process_stderr(const process_handle_t process_handle, +tor_read_all_from_process_stderr(const process_handle_t *process_handle, char *buf, size_t count) { #ifdef MS_WINDOWS - return tor_read_all_handle(process_handle.stderr_pipe, buf, count, - process_handle.pid.hProcess); + return tor_read_all_handle(process_handle->stderr_pipe, buf, count, + process_handle); #else - return read_all(process_handle.stderr_pipe, buf, count, 0); + return tor_read_all_handle(process_handle->stderr_handle, buf, count, + process_handle); #endif } diff --git a/src/common/util.h b/src/common/util.h index 3029425ca1..6211267d05 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -388,12 +388,15 @@ int tor_get_exit_code(const process_handle_t process_handle, int tor_split_lines(struct smartlist_t *sl, char *buf, int len); #ifdef MS_WINDOWS ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count, - HANDLE hProcess); + const process_handle_t *process); +#else +ssize_t tor_read_all_handle(FILE *h, char *buf, size_t count, + const process_handle_t *process); #endif -ssize_t tor_read_all_from_process_stdout(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); +ssize_t tor_read_all_from_process_stdout( + 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); char *tor_join_win_cmdline(const char *argv[]); 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 b903cbfb9c..336f95b14f 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1404,8 +1404,8 @@ run_util_spawn_background(const char *argv[], const char *expected_out, tt_int_op(process_handle.stderr_pipe, >, 0); /* Check stdout */ - pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, - sizeof(stdout_buf) - 1); + pos = tor_read_all_from_process_stdout(&process_handle, stdout_buf, + sizeof(stdout_buf) - 1); tt_assert(pos >= 0); stdout_buf[pos] = '\0'; tt_str_op(stdout_buf, ==, expected_out); @@ -1418,7 +1418,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out, // TODO: Make test-child exit with something other than 0 /* 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); tt_assert(pos >= 0); stderr_buf[pos] = '\0'; @@ -1493,57 +1493,66 @@ test_util_spawn_background_partial_read(void *ptr) "DONE\r\n", NULL }; const char *expected_err = "ERR\r\n"; - int expected_out_ctr; #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\r\n"; + const char *expected_out[] = { "OUT\n--test\nSLEEPING\n", + "DONE\n", + NULL }; + const char *expected_err = "ERR\n"; #endif + int expected_out_ctr; (void)ptr; /* Start the program */ +#ifdef MS_WINDOWS tor_spawn_background(NULL, argv, &process_handle); +#else + tor_spawn_background(argv[0], argv, &process_handle); +#endif tt_int_op(process_handle.status, ==, expected_status); /* Check stdout */ -#ifdef MS_WINDOWS for (expected_out_ctr =0; expected_out[expected_out_ctr] != NULL;) { +#ifdef MS_WINDOWS pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, NULL); +#else + pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + sizeof(stdout_buf) - 1, NULL); +#endif log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos); /* We would have blocked, keep on trying */ if (0 == pos) continue; - tt_assert(pos >= 0); + tt_int_op(pos, >=, 0); stdout_buf[pos] = '\0'; tt_str_op(stdout_buf, ==, expected_out[expected_out_ctr]); tt_int_op(pos, ==, strlen(expected_out[expected_out_ctr])); expected_out_ctr++; } + /* The process should have exited without writing more */ +#ifdef MS_WINDOWS pos = tor_read_all_handle(process_handle.stdout_pipe, stdout_buf, sizeof(stdout_buf) - 1, - process_handle.pid.hProcess); - tt_int_op(pos, ==, 0); + &process_handle); #else - pos = tor_read_all_from_process_stdout(process_handle, stdout_buf, - sizeof(stdout_buf) - 1); - tt_assert(pos >= 0); - stdout_buf[pos] = '\0'; - tt_str_op(stdout_buf, ==, expected_out); - tt_int_op(pos, ==, strlen(expected_out)); + pos = tor_read_all_handle(process_handle.stdout_handle, stdout_buf, + sizeof(stdout_buf) - 1, + &process_handle); #endif /* Check it terminated correctly */ retval = tor_get_exit_code(process_handle, 1, &exit_code); tt_int_op(retval, ==, PROCESS_EXIT_EXITED); tt_int_op(exit_code, ==, expected_exit); + // TODO: Make test-child exit with something other than 0 /* 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); tt_assert(pos >= 0); stderr_buf[pos] = '\0';