diff options
Diffstat (limited to 'support/win32/wintty.c')
-rw-r--r-- | support/win32/wintty.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/support/win32/wintty.c b/support/win32/wintty.c new file mode 100644 index 0000000..684ce5b --- /dev/null +++ b/support/win32/wintty.c @@ -0,0 +1,374 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* -------------------------------------------------------------------- + * + * wintty : a Apache/WinNT support utility for monitoring and + * reflecting user feedback from the Apache process via + * stdin/stdout, even as running within the service context. + * + * Originally contributed by William Rowe <wrowe covalent.net> + * + * Note: this implementation is _very_ experimental, and error handling + * is far from complete. Using it as a cgi or pipe process allows the + * programmer to discover if facilities such as reliable piped logs + * are working as expected, or answer operator prompts that would + * otherwise be discarded by the service process. + * + * Also note the isservice detection semantics, which far exceed any + * mechanism we have discovered thus far. + * + * -------------------------------------------------------------------- + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdlib.h> +#include <stdio.h> + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +#define _CRT_SECURE_NO_DEPRECATE +#pragma warning(disable: 4996) +#endif + +const char *options = +"\nwintty: a utility for echoing the stdin stream to a new console window,\n" +"\teven when invoked from within a service (such as the Apache server.)\n" +"\tAlso reflects the console input back to the stdout stream, allowing\n" +"\tthe operator to respond to prompts from the context of a service.\n\n" +"Syntax: %s [opts] [-t \"Window Title\"]\n\n" +" opts: -c{haracter} or -l{ine} input\n" +"\t-q{uiet} or -e{cho} input\n" +"\t-u{nprocessed} or -p{rocessed} input\n" +"\t-n{owrap} or -w{rap} output lines\n" +"\t-f{ormatted} or -r{aw} output lines\n" +"\t-O{output} [number of seconds]\n" +"\t-v{erbose} error reporting (for debugging)\n" +"\t-? for this message\n\n"; + +BOOL verbose = FALSE; + +void printerr(char *fmt, ...) +{ + char str[1024]; + va_list args; + if (!verbose) + return; + va_start(args, fmt); + wvsprintf(str, fmt, args); + OutputDebugString(str); +} + +DWORD WINAPI feedback(LPVOID args); + +typedef struct feedback_args_t { + HANDLE in; + HANDLE out; +} feedback_args_t; + +int main(int argc, char** argv) +{ + char str[1024], *contitle = NULL; + HANDLE hproc, thread; + HANDLE hwinsta = NULL, hsavewinsta; + HANDLE hdesk = NULL, hsavedesk = NULL; + HANDLE conin, conout; + HANDLE hstdin, hstdout, hstderr, hdup; + feedback_args_t feed; + DWORD conmode; + DWORD newinmode = 0, notinmode = 0; + DWORD newoutmode = 0, notoutmode = 0; + DWORD tid; + DWORD len; + DWORD timeout = INFINITE; + BOOL isservice = FALSE; + char *arg0 = argv[0]; + + while (--argc) { + ++argv; + if (**argv == '/' || **argv == '-') { + switch (tolower((*argv)[1])) { + case 'c': + notinmode |= ENABLE_LINE_INPUT; break; + case 'l': + newinmode |= ENABLE_LINE_INPUT; break; + case 'q': + notinmode |= ENABLE_ECHO_INPUT; break; + case 'e': + newinmode |= ENABLE_ECHO_INPUT; break; + case 'u': + notinmode |= ENABLE_PROCESSED_INPUT; break; + case 'p': + newinmode |= ENABLE_PROCESSED_INPUT; break; + case 'n': + notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break; + case 'w': + newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break; + case 'r': + notoutmode |= ENABLE_PROCESSED_OUTPUT; break; + case 'f': + newoutmode |= ENABLE_PROCESSED_OUTPUT; break; + case 'o': + if (*(argv + 1) && *(argv + 1)[0] != '-') { + *(++argv); + timeout = atoi(*argv) / 1000; + --argc; + } + else { + timeout = 0; + } + break; + case 'v': + verbose = TRUE; + break; + case 't': + contitle = *(++argv); + --argc; + break; + case '?': + printf(options, arg0); + exit(1); + default: + printf("wintty option %s not recognized, use -? for help.\n\n", *argv); + exit(1); + } + } + else { + printf("wintty argument %s not understood, use -? for help.\n\n", *argv); + exit(1); + } + } + + hproc = GetCurrentProcess(); + hsavewinsta = GetProcessWindowStation(); + if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) { + printerr("GetProcessWindowStation() failed (%d)\n", GetLastError()); + } + else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) { + printerr("GetUserObjectInfoformation(hWinSta) failed (%d)\n", GetLastError()); + } + else if (strnicmp(str, "Service-", 8) == 0) { + printerr("WindowStation Name %s is a service\n", str); + isservice = TRUE; + } + SetLastError(0); + + hstdin = GetStdHandle(STD_INPUT_HANDLE); + if (!hstdin || hstdin == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", + GetLastError()); + } + else if (DuplicateHandle(hproc, hstdin, hproc, &hdup, 0, + isservice, DUPLICATE_SAME_ACCESS)) { + CloseHandle(hstdin); + hstdin = hdup; + } + else { + printerr("DupHandle(stdin [%x]) failed (%d)\n", + hstdin, GetLastError()); + } + + hstdout = GetStdHandle(STD_OUTPUT_HANDLE); + if (!hstdout || hstdout == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", + GetLastError()); + } + else if (DuplicateHandle(hproc, hstdout, hproc, &hdup, 0, + isservice, DUPLICATE_SAME_ACCESS)) { + CloseHandle(hstdout); + hstdout = hdup; + } + else { + printerr("DupHandle(stdout [%x]) failed (%d)\n", + hstdout, GetLastError()); + } + + hstderr = GetStdHandle(STD_ERROR_HANDLE); + if (!hstderr || hstderr == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", + GetLastError()); + } + else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0, + isservice, DUPLICATE_SAME_ACCESS)) { + CloseHandle(hstderr); + hstderr = hdup; + } + else { + printerr("DupHandle(stderr [%x]) failed (%d)\n", + hstderr, GetLastError()); + } + + /* You can't close the console till all the handles above were + * rescued by DuplicateHandle() + */ + if (!FreeConsole()) + printerr("FreeConsole() failed (%d)\n", GetLastError()); + + if (isservice) { +#ifdef WE_EVER_FIGURE_OUT_WHY_THIS_DOESNT_WORK + hsavedesk = GetThreadDesktop(GetCurrentThreadId()); + if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) { + printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError()); + } + CloseWindowStation(hwinsta); + hwinsta = OpenWindowStation("WinSta0", TRUE, MAXIMUM_ALLOWED); + if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) { + printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError()); + } + else if (!SetProcessWindowStation(hwinsta)) { + printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError()); + } + hdesk = OpenDesktop("Default", 0, TRUE, MAXIMUM_ALLOWED); + if (!hdesk || hdesk == INVALID_HANDLE_VALUE) { + printerr("OpenDesktop(Default) failed (%d)\n", GetLastError()); + } + else if (!SetThreadDesktop(hdesk)) { + printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError()); + } +#else + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD exitcode = 1; + char appbuff[MAX_PATH]; + char *appname = NULL; + char *cmdline = GetCommandLine(); + + if (!GetModuleFileName(NULL, appbuff, sizeof(appbuff))) { + appname = appbuff; + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW + | STARTF_USESTDHANDLES; + si.lpDesktop = "WinSta0\\Default"; + si.wShowWindow = 1; /* SW_SHOWNORMAL */ + si.hStdInput = hstdin; + si.hStdOutput = hstdout; + si.hStdError = hstderr; + + /* Instantly, upon creating the new process, we will close our + * copies of the handles so our parent isn't confused when the + * child closes their copy of the handle. Without this action, + * we would hold a copy of the handle, and the parent would not + * receive their EOF notification. + */ + if (CreateProcess(appname, cmdline, NULL, NULL, TRUE, + CREATE_SUSPENDED | CREATE_NEW_CONSOLE, + NULL, NULL, &si, &pi)) { + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + ResumeThread(pi.hThread); + CloseHandle(pi.hThread); + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, &exitcode); + CloseHandle(pi.hProcess); + return exitcode; + } + return 1; +#endif + } + + if (!AllocConsole()) { + printerr("AllocConsole(Default) failed (%d)\n", GetLastError()); + } + + if (contitle && !SetConsoleTitle(contitle)) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + + conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FALSE, OPEN_EXISTING, 0, NULL); + if (!conout || conout == INVALID_HANDLE_VALUE) { + printerr("CreateFile(CONOUT$) failed (%d)\n", GetLastError()); + } + else if (!GetConsoleMode(conout, &conmode)) { + printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError()); + } + else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode) + & ~notoutmode))) { + printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n", + conmode, GetLastError()); + } + + conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FALSE, OPEN_EXISTING, 0, NULL); + if (!conin || conin == INVALID_HANDLE_VALUE) { + printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError()); + } + else if (!GetConsoleMode(conin, &conmode)) { + printerr("GetConsoleMode(CONIN) failed (%d)\n", GetLastError()); + } + else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode) + & ~notinmode))) { + printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n", + conmode, GetLastError()); + } + + feed.in = conin; + feed.out = hstdout; + thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid); + + while (ReadFile(hstdin, str, sizeof(str), &len, NULL)) + if (!len || !WriteFile(conout, str, len, &len, NULL)) + break; + + printerr("[EOF] from stdin (%d)\n", GetLastError()); + + CloseHandle(stdout); + if (!GetConsoleTitle(str, sizeof(str))) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + else { + strcat(str, " - [Finished]"); + if (!SetConsoleTitle(str)) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + } + + WaitForSingleObject(thread, timeout); + FreeConsole(); + if (isservice) { + if (!SetProcessWindowStation(hsavewinsta)) { + len = GetLastError(); + } + if (!SetThreadDesktop(hsavedesk)) { + len = GetLastError(); + } + CloseDesktop(hdesk); + CloseWindowStation(hwinsta); + } + return 0; +} + + +DWORD WINAPI feedback(LPVOID arg) +{ + feedback_args_t *feed = (feedback_args_t*)arg; + char *str[1024]; + DWORD len; + + while (ReadFile(feed->in, str, sizeof(str), &len, NULL)) + if (!len || !WriteFile(feed->out, str, len, &len, NULL)) + break; + + printerr("[EOF] from Console (%d)\n", GetLastError()); + + return 0; +} |