diff --git a/include/criterion/redirect.h b/include/criterion/redirect.h new file mode 100644 index 0000000..bb5bc6c --- /dev/null +++ b/include/criterion/redirect.h @@ -0,0 +1,41 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CRITERION_REDIRECT_H_ +# define CRITERION_REDIRECT_H_ + +# include "common.h" + +CR_BEGIN_C_API + +CR_API void cr_redirect_stdout(void); +CR_API void cr_redirect_stderr(void); +CR_API void cr_redirect_stdin(void); + +CR_API FILE* cr_get_redirected_stdout(void); +CR_API FILE* cr_get_redirected_stderr(void); +CR_API FILE* cr_get_redirected_stdin(void); + +CR_END_C_API + +#endif /* !CRITERION_REDIRECT_H_ */ diff --git a/src/posix-compat.c b/src/posix-compat.c index 291a727..d7894b7 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -24,6 +24,7 @@ #include #include "posix-compat.h" #include "process.h" +#include "criterion/assert.h" #ifdef VANILLA_WIN32 # define VC_EXTRALEAN @@ -262,15 +263,17 @@ void wait_process(s_proc_handle *handle, int *status) { #endif } -FILE *pipe_in(s_pipe_handle *p) { +FILE *pipe_in(s_pipe_handle *p, int do_close) { #ifdef VANILLA_WIN32 - CloseHandle(p->fhs[1]); + if (do_close) + CloseHandle(p->fhs[1]); int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY); if (fd == -1) return NULL; FILE *in = _fdopen(fd, "r"); #else - close(p->fds[1]); + if (do_close) + close(p->fds[1]); FILE *in = fdopen(p->fds[0], "r"); #endif if (!in) @@ -280,15 +283,17 @@ FILE *pipe_in(s_pipe_handle *p) { return in; } -FILE *pipe_out(s_pipe_handle *p) { +FILE *pipe_out(s_pipe_handle *p, int do_close) { #ifdef VANILLA_WIN32 - CloseHandle(p->fhs[0]); + if (do_close) + CloseHandle(p->fhs[0]); int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY); if (fd == -1) return NULL; FILE *out = _fdopen(fd, "w"); #else - close(p->fds[0]); + if (do_close) + close(p->fds[0]); FILE *out = fdopen(p->fds[1], "w"); #endif if (!out) @@ -298,8 +303,7 @@ FILE *pipe_out(s_pipe_handle *p) { return out; } -s_pipe_handle *stdpipe() { - s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); +int stdpipe_stack(s_pipe_handle *out) { #ifdef VANILLA_WIN32 HANDLE fhs[2]; SECURITY_ATTRIBUTES attr = { @@ -307,16 +311,22 @@ s_pipe_handle *stdpipe() { .bInheritHandle = TRUE }; if (!CreatePipe(fhs, fhs + 1, &attr, 0)) - return NULL; - *handle = (s_pipe_handle) {{ fhs[0], fhs[1] }}; - return handle; + return -1; + *out = (s_pipe_handle) {{ fhs[0], fhs[1] }}; #else int fds[2] = { -1, -1 }; if (pipe(fds) == -1) - return NULL; - *handle = (s_pipe_handle) {{ fds[0], fds[1] }}; - return handle; + return -1; + *out = (s_pipe_handle) {{ fds[0], fds[1] }}; #endif + return 0; +} + +s_pipe_handle *stdpipe() { + s_pipe_handle *handle = smalloc(sizeof (s_pipe_handle)); + if (stdpipe_stack(handle) < 0) + return NULL; + return handle; } s_proc_handle *get_current_process() { @@ -405,3 +415,97 @@ const char *basename_compat(const char *str) { start = c + 1; return start; } + +#ifdef VANILLA_WIN32 +typedef DWORD cr_std_fd; +#else +typedef int cr_std_fd; +#endif + +static s_pipe_handle stdout_redir; +static s_pipe_handle stderr_redir; +static s_pipe_handle stdin_redir; + +enum criterion_std_fd { + CR_STDIN, + CR_STDOUT, + CR_STDERR +}; + +enum criterion_pipe_end { + PIPE_READ = 0, + PIPE_WRITE = 1, +}; + +cr_std_fd get_std_fd(int fd_kind) { + static int kinds[] = { +#ifdef VANILLA_WIN32 + [CR_STDIN] = STD_INPUT_HANDLE, + [CR_STDOUT] = STD_OUTPUT_HANDLE, + [CR_STDERR] = STD_ERROR_HANDLE, +#else + [CR_STDIN] = STDIN_FILENO, + [CR_STDOUT] = STDOUT_FILENO, + [CR_STDERR] = STDERR_FILENO, +#endif + }; + + return kinds[fd_kind]; +} + +void cr_redirect(int fd_kind, s_pipe_handle *pipe, int fd_index) { + if (stdpipe_stack(pipe) < 0) + cr_assert_fail("Could not redirect standard file descriptor."); + + cr_std_fd fd = get_std_fd(fd_kind); +#ifdef VANILLA_WIN32 + CloseHandle(GetStdHandle(fd)); + SetStdHandle(fd, pipe->fds[fd_index]); +#else + close(fd); + dup2(fd, pipe->fds[fd_index]); + close(pipe->fds[fd_index]); +#endif +} + +void cr_redirect_stdout(void) { + cr_redirect(CR_STDOUT, &stdout_redir, PIPE_WRITE); +} + +void cr_redirect_stderr(void) { + cr_redirect(CR_STDERR, &stderr_redir, PIPE_WRITE); +} + +void cr_redirect_stdin(void) { + cr_redirect(CR_STDIN, &stdin_redir, PIPE_READ); +} + +FILE* cr_get_redirected_stdout(void) { + static FILE *f; + if (!f) { + f = pipe_in(&stdout_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdout read end."); + } + return f; +} + +FILE* cr_get_redirected_stderr(void) { + static FILE *f; + if (!f) { + f = pipe_in(&stderr_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stderr read end."); + } + return f; +} + +FILE* cr_get_redirected_stdin(void) { + static FILE *f; + if (!f) { + f = pipe_out(&stdin_redir, 0); + if (!f) + cr_assert_fail("Could not get redirected stdin write end."); + } + return f; +} diff --git a/src/posix-compat.h b/src/posix-compat.h index 9f938db..8ad38f3 100644 --- a/src/posix-compat.h +++ b/src/posix-compat.h @@ -67,8 +67,8 @@ extern struct worker_context g_worker_context; int resume_child(void); s_pipe_handle *stdpipe(); -FILE *pipe_in(s_pipe_handle *p); -FILE *pipe_out(s_pipe_handle *p); +FILE *pipe_in(s_pipe_handle *p, int do_close); +FILE *pipe_out(s_pipe_handle *p, int do_close); s_proc_handle *fork_process(); void wait_process(s_proc_handle *handle, int *status); diff --git a/src/process.c b/src/process.c index 036b790..dd0a0c7 100644 --- a/src/process.c +++ b/src/process.c @@ -62,7 +62,7 @@ struct event *worker_read_event(struct process *proc) { void run_worker(struct worker_context *ctx) { fclose(stdin); - g_event_pipe = pipe_out(ctx->pipe); + g_event_pipe = pipe_out(ctx->pipe, 1); ctx->func(ctx->test, ctx->suite); fclose(g_event_pipe); @@ -102,7 +102,7 @@ struct process *spawn_test_worker(struct criterion_test *test, .kind = UNIQUE, .dtor = close_process); - *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe) }; + *ptr = (struct process) { .proc = proc, .in = pipe_in(pipe, 1) }; cleanup: sfree(pipe); return ptr;