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