1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* --------------------------------------------------------------------
19 * wintty : a Apache/WinNT support utility for monitoring and
20 * reflecting user feedback from the Apache process via
21 * stdin/stdout, even as running within the service context.
23 * Originally contributed by William Rowe <wrowe covalent.net>
25 * Note: this implementation is _very_ experimental, and error handling
26 * is far from complete. Using it as a cgi or pipe process allows the
27 * programmer to discover if facilities such as reliable piped logs
28 * are working as expected, or answer operator prompts that would
29 * otherwise be discarded by the service process.
31 * Also note the isservice detection semantics, which far exceed any
32 * mechanism we have discovered thus far.
34 * --------------------------------------------------------------------
37 #define WIN32_LEAN_AND_MEAN
39 #if defined(_MSC_VER) && _MSC_VER >= 1400
40 #define _CRT_SECURE_NO_DEPRECATE
41 #pragma warning(disable: 4996)
49 "\nwintty: a utility for echoing the stdin stream to a new console window,\n"
50 "\teven when invoked from within a service (such as the Apache server.)\n"
51 "\tAlso reflects the console input back to the stdout stream, allowing\n"
52 "\tthe operator to respond to prompts from the context of a service.\n\n"
53 "Syntax: %s [opts] [-t \"Window Title\"]\n\n"
54 " opts: -c{haracter} or -l{ine} input\n"
55 "\t-q{uiet} or -e{cho} input\n"
56 "\t-u{nprocessed} or -p{rocessed} input\n"
57 "\t-n{owrap} or -w{rap} output lines\n"
58 "\t-f{ormatted} or -r{aw} output lines\n"
59 "\t-O{output} [number of seconds]\n"
60 "\t-v{erbose} error reporting (for debugging)\n"
61 "\t-? for this message\n\n";
65 void printerr(char *fmt, ...)
72 wvsprintf(str, fmt, args);
73 OutputDebugString(str);
76 DWORD WINAPI feedback(LPVOID args);
78 typedef struct feedback_args_t {
83 int main(int argc, char** argv)
85 char str[1024], *contitle = NULL;
87 HANDLE hwinsta = NULL, hsavewinsta;
88 HANDLE hdesk = NULL, hsavedesk = NULL;
90 HANDLE hstdin, hstdout, hstderr, hdup;
93 DWORD newinmode = 0, notinmode = 0;
94 DWORD newoutmode = 0, notoutmode = 0;
97 DWORD timeout = INFINITE;
98 BOOL isservice = FALSE;
103 if (**argv == '/' || **argv == '-') {
104 switch (tolower((*argv)[1])) {
106 notinmode |= ENABLE_LINE_INPUT; break;
108 newinmode |= ENABLE_LINE_INPUT; break;
110 notinmode |= ENABLE_ECHO_INPUT; break;
112 newinmode |= ENABLE_ECHO_INPUT; break;
114 notinmode |= ENABLE_PROCESSED_INPUT; break;
116 newinmode |= ENABLE_PROCESSED_INPUT; break;
118 notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
120 newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
122 notoutmode |= ENABLE_PROCESSED_OUTPUT; break;
124 newoutmode |= ENABLE_PROCESSED_OUTPUT; break;
126 if (*(argv + 1) && *(argv + 1)[0] != '-') {
128 timeout = atoi(*argv) / 1000;
139 contitle = *(++argv);
143 printf(options, arg0);
146 printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
151 printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
156 hproc = GetCurrentProcess();
157 hsavewinsta = GetProcessWindowStation();
158 if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
159 printerr("GetProcessWindowStation() failed (%d)\n", GetLastError());
161 else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
162 printerr("GetUserObjectInfoformation(hWinSta) failed (%d)\n", GetLastError());
164 else if (strnicmp(str, "Service-", 8) == 0) {
165 printerr("WindowStation Name %s is a service\n", str);
170 hstdin = GetStdHandle(STD_INPUT_HANDLE);
171 if (!hstdin || hstdin == INVALID_HANDLE_VALUE) {
172 printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n",
175 else if (DuplicateHandle(hproc, hstdin, hproc, &hdup, 0,
176 isservice, DUPLICATE_SAME_ACCESS)) {
181 printerr("DupHandle(stdin [%x]) failed (%d)\n",
182 hstdin, GetLastError());
185 hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
186 if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
187 printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n",
190 else if (DuplicateHandle(hproc, hstdout, hproc, &hdup, 0,
191 isservice, DUPLICATE_SAME_ACCESS)) {
192 CloseHandle(hstdout);
196 printerr("DupHandle(stdout [%x]) failed (%d)\n",
197 hstdout, GetLastError());
200 hstderr = GetStdHandle(STD_ERROR_HANDLE);
201 if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
202 printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n",
205 else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0,
206 isservice, DUPLICATE_SAME_ACCESS)) {
207 CloseHandle(hstderr);
211 printerr("DupHandle(stderr [%x]) failed (%d)\n",
212 hstderr, GetLastError());
215 /* You can't close the console till all the handles above were
216 * rescued by DuplicateHandle()
219 printerr("FreeConsole() failed (%d)\n", GetLastError());
222 #ifdef WE_EVER_FIGURE_OUT_WHY_THIS_DOESNT_WORK
223 hsavedesk = GetThreadDesktop(GetCurrentThreadId());
224 if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) {
225 printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError());
227 CloseWindowStation(hwinsta);
228 hwinsta = OpenWindowStation("WinSta0", TRUE, MAXIMUM_ALLOWED);
229 if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) {
230 printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError());
232 else if (!SetProcessWindowStation(hwinsta)) {
233 printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError());
235 hdesk = OpenDesktop("Default", 0, TRUE, MAXIMUM_ALLOWED);
236 if (!hdesk || hdesk == INVALID_HANDLE_VALUE) {
237 printerr("OpenDesktop(Default) failed (%d)\n", GetLastError());
239 else if (!SetThreadDesktop(hdesk)) {
240 printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError());
243 PROCESS_INFORMATION pi;
246 char appbuff[MAX_PATH];
247 char *appname = NULL;
248 char *cmdline = GetCommandLine();
250 if (!GetModuleFileName(NULL, appbuff, sizeof(appbuff))) {
254 memset(&si, 0, sizeof(si));
256 si.dwFlags = STARTF_USESHOWWINDOW
257 | STARTF_USESTDHANDLES;
258 si.lpDesktop = "WinSta0\\Default";
259 si.wShowWindow = 1; /* SW_SHOWNORMAL */
260 si.hStdInput = hstdin;
261 si.hStdOutput = hstdout;
262 si.hStdError = hstderr;
264 /* Instantly, upon creating the new process, we will close our
265 * copies of the handles so our parent isn't confused when the
266 * child closes their copy of the handle. Without this action,
267 * we would hold a copy of the handle, and the parent would not
268 * receive their EOF notification.
270 if (CreateProcess(appname, cmdline, NULL, NULL, TRUE,
271 CREATE_SUSPENDED | CREATE_NEW_CONSOLE,
272 NULL, NULL, &si, &pi)) {
273 CloseHandle(si.hStdInput);
274 CloseHandle(si.hStdOutput);
275 CloseHandle(si.hStdError);
276 ResumeThread(pi.hThread);
277 CloseHandle(pi.hThread);
278 WaitForSingleObject(pi.hProcess, INFINITE);
279 GetExitCodeProcess(pi.hProcess, &exitcode);
280 CloseHandle(pi.hProcess);
287 if (!AllocConsole()) {
288 printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
291 if (contitle && !SetConsoleTitle(contitle)) {
292 printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
295 conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
296 FILE_SHARE_READ | FILE_SHARE_WRITE,
297 FALSE, OPEN_EXISTING, 0, NULL);
298 if (!conout || conout == INVALID_HANDLE_VALUE) {
299 printerr("CreateFile(CONOUT$) failed (%d)\n", GetLastError());
301 else if (!GetConsoleMode(conout, &conmode)) {
302 printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
304 else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode)
306 printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n",
307 conmode, GetLastError());
310 conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
311 FILE_SHARE_READ | FILE_SHARE_WRITE,
312 FALSE, OPEN_EXISTING, 0, NULL);
313 if (!conin || conin == INVALID_HANDLE_VALUE) {
314 printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError());
316 else if (!GetConsoleMode(conin, &conmode)) {
317 printerr("GetConsoleMode(CONIN) failed (%d)\n", GetLastError());
319 else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode)
321 printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n",
322 conmode, GetLastError());
327 thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid);
329 while (ReadFile(hstdin, str, sizeof(str), &len, NULL))
330 if (!len || !WriteFile(conout, str, len, &len, NULL))
333 printerr("[EOF] from stdin (%d)\n", GetLastError());
336 if (!GetConsoleTitle(str, sizeof(str))) {
337 printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
340 strcat(str, " - [Finished]");
341 if (!SetConsoleTitle(str)) {
342 printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
346 WaitForSingleObject(thread, timeout);
349 if (!SetProcessWindowStation(hsavewinsta)) {
350 len = GetLastError();
352 if (!SetThreadDesktop(hsavedesk)) {
353 len = GetLastError();
356 CloseWindowStation(hwinsta);
362 DWORD WINAPI feedback(LPVOID arg)
364 feedback_args_t *feed = (feedback_args_t*)arg;
368 while (ReadFile(feed->in, str, sizeof(str), &len, NULL))
369 if (!len || !WriteFile(feed->out, str, len, &len, NULL))
372 printerr("[EOF] from Console (%d)\n", GetLastError());