> > Frediano, > > On Tue, May 3, 2016 at 11:26 AM, 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. > > Would worth to mention (even if it looks obvious) that the test needs > to be executed in a native MingW (no cross compilation). > > If you agree, I'll add this to the commit log and push both patches. > Agreed. Note that this apply to all tests in tests directory so it's not a new issue. To execute binaries compiled with MingW you need either native MingW or Wine (with .exe executables enabled in Linux binfmt). Frediano > > > > Signed-off-by: Frediano Ziglio <fziglio@xxxxxxxxxx> > > --- > > tests/Makefile.am | 7 ++ > > tests/redirect-test.c | 335 > > ++++++++++++++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 342 insertions(+) > > create mode 100644 tests/redirect-test.c > > > > Changes since v3: > > - moved source to tests directory; > > - added missing config.h include; > > - remove some unused warnings. > > > > diff --git a/tests/Makefile.am b/tests/Makefile.am > > index 15907eb..a1bdf85 100644 > > --- a/tests/Makefile.am > > +++ b/tests/Makefile.am > > @@ -26,4 +26,11 @@ test_monitor_mapping_SOURCES = \ > > test-monitor-mapping.c \ > > $(NULL) > > > > +if OS_WIN32 > > +TESTS += redirect-test > > +redirect_test_SOURCES = redirect-test.c > > +redirect_test_LDFLAGS = -Wl,--subsystem,windows > > +redirect_test_CPPFLAGS = $(GLIB2_CFLAGS) > > +endif > > + > > -include $(top_srcdir)/git.mk > > diff --git a/tests/redirect-test.c b/tests/redirect-test.c > > new file mode 100644 > > index 0000000..be96cd0 > > --- /dev/null > > +++ b/tests/redirect-test.c > > @@ -0,0 +1,335 @@ > > +/* > > + * 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 > > + */ > > + > > +#include <config.h> > > + > > +/* force assert to be compiler, on Windows are usually disabled */ > > +#undef NDEBUG > > +#undef DEBUG > > +#define DEBUG 1 > > + > > +#include <windows.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <assert.h> > > +#include <io.h> > > +#include <glib.h> > > + > > +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 G_GNUC_UNUSED, 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 G_GNUC_UNUSED, HINSTANCE > > hPrevInstance G_GNUC_UNUSED, > > + LPSTR lpCmdLine, int nShowCmd G_GNUC_UNUSED) > > +{ > > + static const char cmd_seps[] = " \t\r\n\v"; > > + > > + 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