Frediano, On Fri, Apr 29, 2016 at 4:09 PM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: > This program attempt multiple redirection combination: > - passing handles using either CreateProcess or SetStdHandle; > - having a console or not; > - redirection stdout/stderr yes or not. > > Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> > --- > src/Makefile.am | 4 + > src/redirect-test.c | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 333 insertions(+) > create mode 100644 src/redirect-test.c > > diff --git a/src/Makefile.am b/src/Makefile.am > index ff487ba..52f80de 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -213,6 +213,10 @@ bin_PROGRAMS += debug-helper > debug_helper_SOURCES = debug-helper.c > debug_helper_LDFLAGS = $(GLIB2_LIBS) -Wl,--subsystem,windows > debug_helper_CFLAGS = $(GLIB2_CFLAGS) > + > +bin_PROGRAMS += redirect-test > +redirect_test_SOURCES = redirect-test.c > +redirect_test_LDFLAGS = -Wl,--subsystem,windows > endif > > -include $(top_srcdir)/git.mk > diff --git a/src/redirect-test.c b/src/redirect-test.c > new file mode 100644 > index 0000000..ea6327a > --- /dev/null > +++ b/src/redirect-test.c > @@ -0,0 +1,329 @@ > +/* > + * Copyright (C) 2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +/* This small program test redirection inside Remote Viewer > + */ > + > +#undef NDEBUG > +#undef DEBUG > +#define DEBUG 1 You're missing the config.h's include here and it breaks our autobuild.sh script. Also, I'd like to have all tests under tests instead of src. > +#include <windows.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <assert.h> > +#include <io.h> > + > +#define CMD_SEPS " \t\r\n\v" > + > +static BOOL has_console = FALSE; > +static HANDLE h_console = INVALID_HANDLE_VALUE; > +static FILE *log_f = NULL; > +static int num_test = 0; > + > +static int > +has_inherit(HANDLE h) > +{ > + DWORD flags; > + if (GetHandleInformation(h, &flags)) > + return flags & HANDLE_FLAG_INHERIT; > + return 0; > +} > + > +static BOOL is_handle_valid(HANDLE h) > +{ > + if (h == INVALID_HANDLE_VALUE || h == NULL) > + return FALSE; > + > + DWORD flags; > + return GetHandleInformation(h, &flags); > +} > + > +static int > +called_test(int argc, char **argv) > +{ > + STARTUPINFO si; > + memset(&si, 0, sizeof(si)); > + si.cb = sizeof(si); > + si.dwFlags = STARTF_USESTDHANDLES; > + GetStartupInfo(&si); > + fprintf(log_f, "handles %p %p - %p %p %d %d\n", si.hStdOutput, si.hStdError, > + GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE), > + has_inherit(GetStdHandle(STD_OUTPUT_HANDLE)), has_inherit(GetStdHandle(STD_ERROR_HANDLE))); > + > + /* Get redirection from parent */ > + BOOL out_valid = is_handle_valid(GetStdHandle(STD_OUTPUT_HANDLE)); > + BOOL err_valid = is_handle_valid(GetStdHandle(STD_ERROR_HANDLE)); > + > + /* > + * If not all output are redirected try to redirect to parent console. > + * If parent has no console (for instance as launched from GUI) just > + * rely on default (no output). > + */ > + if ((!out_valid || !err_valid) && AttachConsole(ATTACH_PARENT_PROCESS)) { > + fprintf(log_f, "attached\n"); > + if (!out_valid) { > + freopen("CONOUT$", "w", stdout); > + dup2(fileno(stdout), STDOUT_FILENO); > + } > + if (!err_valid) { > + freopen("CONOUT$", "w", stderr); > + dup2(fileno(stderr), STDERR_FILENO); > + } > + } > + > + printf("stdout %s line\n", argv[0]); > + fflush(stdout); > + fprintf(stderr, "stderr %s line\n", argv[0]); > + fflush(stderr); > + return 0; > +} > + > +static enum { > + METHOD_CREATEPROCESS, > + METHOD_STDHANDLE > +} redir_method = METHOD_CREATEPROCESS; > + > +static void > +exec(HANDLE redir_out, HANDLE redir_err) > +{ > + char program[MAX_PATH+128]; > + GetModuleFileName(NULL, program+1, MAX_PATH); > + program[0] = '\"'; > + sprintf(strchr(program, 0) , "\" %d", num_test); > + > + HANDLE old_out = GetStdHandle(STD_OUTPUT_HANDLE); > + HANDLE old_err = GetStdHandle(STD_ERROR_HANDLE); > + > + BOOL inherit = FALSE; > + STARTUPINFO si; > + memset(&si, 0, sizeof(si)); > + si.cb = sizeof(si); > + si.dwFlags = 0; > + si.hStdOutput = si.hStdError = si.hStdInput = INVALID_HANDLE_VALUE; > + if (redir_out != INVALID_HANDLE_VALUE || redir_err != INVALID_HANDLE_VALUE) { > + if (redir_method == METHOD_CREATEPROCESS) > + si.dwFlags |= STARTF_USESTDHANDLES; > + inherit = TRUE; > + if (redir_out != INVALID_HANDLE_VALUE) { > + SetHandleInformation(redir_out, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); > + if (redir_method == METHOD_CREATEPROCESS) { > + si.hStdOutput = redir_out; > + } else { > + SetStdHandle(STD_OUTPUT_HANDLE, redir_out); > + } > + } > + if (redir_err != INVALID_HANDLE_VALUE) { > + SetHandleInformation(redir_err, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); > + if (redir_method == METHOD_CREATEPROCESS) { > + si.hStdError = redir_err; > + } else { > + SetStdHandle(STD_ERROR_HANDLE, redir_err); > + } > + } > + } > + > + fprintf(log_f, "handles %p %p - %p %p %d %d\n", si.hStdOutput, si.hStdError, > + GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE), > + has_inherit(GetStdHandle(STD_OUTPUT_HANDLE)), has_inherit(GetStdHandle(STD_ERROR_HANDLE))); > + > + fprintf(log_f, "sub command ---->\n"); > + fclose(log_f); > + log_f = NULL; > + > + PROCESS_INFORMATION pi = { 0, }; > + assert(CreateProcess(NULL, program, NULL, NULL, inherit, 0, NULL, NULL, &si, &pi)); > + CloseHandle(pi.hThread); > + WaitForSingleObject(pi.hProcess, INFINITE); > + CloseHandle(pi.hProcess); > + > + log_f = fopen("log.txt", "a"); > + assert(log_f); > + setbuf(log_f, NULL); > + fprintf(log_f, "<---- sub command\n"); > + > + SetStdHandle(STD_OUTPUT_HANDLE, old_out); > + SetStdHandle(STD_ERROR_HANDLE, old_err); > +} > + > +// simple dirty function to read first line in a file > +static char * > +read_file(const char *fn) > +{ > + FILE *f = fopen(fn, "r"); > + assert(f); > + > + // dirty but fast > + static char buf[1024]; > + > + memset(buf, 0, sizeof(buf)); > + if (!fgets(buf, sizeof(buf), f)) > + memset(buf, 0, sizeof(buf)); > + > + fclose(f); > + return buf; > +} > + > +static char * > +read_console(void) > +{ > + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; > + assert(GetConsoleScreenBufferInfo(h_console, &csbiInfo)); > + > + fprintf(log_f, "size %d %d\n", csbiInfo.dwSize.X, csbiInfo.dwSize.Y); > + fprintf(log_f, "win %d %d %d %d\n", csbiInfo.srWindow.Left, csbiInfo.srWindow.Top, csbiInfo.srWindow.Right, csbiInfo.srWindow.Bottom); > + > + COORD size; > + size.Y = csbiInfo.srWindow.Bottom + 1; > + size.X = csbiInfo.srWindow.Right + 1; > + > + COORD coord = { 0, 0 }; > + > + CHAR_INFO *buf = calloc(size.Y * size.X, sizeof(CHAR_INFO)); > + assert(buf); > + > + SMALL_RECT rect; > + rect.Top = 0; > + rect.Left = 0; > + rect.Bottom = size.Y - 1; > + rect.Right = size.X - 1; > + > + // read console content > + assert(ReadConsoleOutput(h_console, buf, size, coord, &rect)); > + > + // convert to just text > + unsigned n; > + char *char_buf = (char *) buf; > + for (n = 0; n < size.X * size.Y; ++n) > + char_buf[n] = buf[n].Char.AsciiChar; > + char_buf[n] = 0; > + > + // remove additional spaces > + char *p; > + while ((p=strstr(char_buf, " ")) != NULL) > + memmove(p, p+1, strlen(p)); > + > + return char_buf; > +} > + > +static void > +check(BOOL redir_out, BOOL redir_err) > +{ > + ++num_test; > + fprintf(log_f, "check method %d console %d redir_out %d redir_err %d\n", > + (int) redir_method, has_console, redir_out, redir_err); > + > + char stdout_line[64], stderr_line[64]; > + > + sprintf(stdout_line, "stdout %d line", num_test); > + sprintf(stderr_line, "stderr %d line", num_test); > + > + DeleteFile("stdout.txt"); > + DeleteFile("stderr.txt"); > + > + HANDLE out = CreateFile("stdout.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); > + assert(out != INVALID_HANDLE_VALUE); > + HANDLE err = CreateFile("stderr.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); > + assert(err != INVALID_HANDLE_VALUE); > + > + exec(redir_out ? out : INVALID_HANDLE_VALUE, > + redir_err ? err : INVALID_HANDLE_VALUE); > + > + CloseHandle(out); > + CloseHandle(err); > + > + // check file output > + if (redir_out) { > + char *data = read_file("stdout.txt"); > + assert(strstr(data, stdout_line) != NULL); > + } > + if (redir_err) { > + char *data = read_file("stderr.txt"); > + assert(strstr(data, stderr_line) != NULL); > + } > + > + DeleteFile("stdout.txt"); > + DeleteFile("stderr.txt"); > + > + // check console output > + if (!has_console) > + return; > + > + char *data = read_console(); > + fprintf(log_f, "\nconsole: %s\nstdout expected: %s\nstderr expected: %s\n", data, stdout_line, stderr_line); > + if (!redir_out) > + assert(strstr(data, stdout_line) != NULL); > + > + if (!redir_err) > + assert(strstr(data, stderr_line) != NULL); > + free(data); > +} > + > +static void > +all_checks(void) > +{ > + check(TRUE, FALSE); > + check(FALSE, TRUE); > + check(TRUE, TRUE); > + > + assert(AllocConsole()); > + has_console = TRUE; > + h_console = GetStdHandle(STD_OUTPUT_HANDLE); > + fprintf(log_f, "got console handles %p %p\n", h_console, GetStdHandle(STD_ERROR_HANDLE)); > + > + check(FALSE, FALSE); > + check(TRUE, FALSE); > + check(FALSE, TRUE); > + check(TRUE, TRUE); > + > + assert(FreeConsole()); > + has_console = FALSE; > + h_console = INVALID_HANDLE_VALUE; > +} > + > +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) > +{ > + char *argv[10], *p; > + int argc = 0; > + > + // parse arguments > + for (p = strtok(lpCmdLine, CMD_SEPS); p && argc < 10; p = strtok(NULL, CMD_SEPS)) > + argv[argc++] = p; > + argv[argc] = NULL; > + > + log_f = fopen("log.txt", argc >= 1 ? "a" : "w"); > + assert(log_f); > + setbuf(log_f, NULL); > + > + fprintf(log_f, "argc %d argv[0] %s \n", argc, argv[0]); > + > + if (argc >= 1) { > + return called_test(argc, argv); > + } > + > + // main program, call ourself with different arguments and settings > + redir_method = METHOD_CREATEPROCESS; > + all_checks(); > + > + redir_method = METHOD_STDHANDLE; > + all_checks(); > + > + fprintf(log_f, "everything ok\n"); > + > + fclose(log_f); > + return 0; > +} > -- > 2.5.5 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/spice-devel -- Fabiano Fidêncio _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel