/* 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 * * 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 #include #include #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; }