]> granicus.if.org Git - apache/blob - support/win32/wintty.c
fix name of The Apache Software Foundation
[apache] / support / win32 / wintty.c
1 /* Copyright 2001-2004 The Apache Software Foundation
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 /* --------------------------------------------------------------------
17  *
18  * wintty : a Apache/WinNT support utility for monitoring and 
19  *          reflecting user feedback from the Apache process via
20  *          stdin/stdout, even as running within the service context.
21  *
22  * Originally contributed by William Rowe <wrowe covalent.net>
23  *
24  * Note: this implementation is _very_ experimental, and error handling
25  * is far from complete.  Using it as a cgi or pipe process allows the
26  * programmer to discover if facilities such as reliable piped logs
27  * are working as expected, or answer operator prompts that would
28  * otherwise be discarded by the service process.
29  *
30  * Also note the isservice detection semantics, which far exceed any
31  * mechanism we have discovered thus far.
32  * 
33  * --------------------------------------------------------------------
34  */
35
36 #define WIN32_LEAN_AND_MEAN
37 #include <windows.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40
41 const char *options = 
42 "\nwintty: a utility for echoing the stdin stream to a new console window,\n"
43 "\teven when invoked from within a service (such as the Apache server.)\n"
44 "\tAlso reflects the console input back to the stdout stream, allowing\n"
45 "\tthe operator to respond to prompts from the context of a service.\n\n"
46 "Syntax: %s [opts] [-t \"Window Title\"]\n\n"
47 "  opts: -c{haracter}   or -l{ine} input\n"
48 "\t-q{uiet}       or -e{cho} input\n"
49 "\t-u{nprocessed} or -p{rocessed} input\n"
50 "\t-n{owrap}      or -w{rap} output lines\n"
51 "\t-f{ormatted}   or -r{aw} output lines\n"
52 "\t-O{output} [number of seconds]\n"
53 "\t-v{erbose} error reporting (for debugging)\n"
54 "\t-? for this message\n\n";
55
56 BOOL verbose = FALSE;
57
58 void printerr(char *fmt, ...) 
59 {
60     char str[1024];
61     va_list args;
62     if (!verbose)
63         return;
64     va_start(args, fmt);
65     wvsprintf(str, fmt, args);
66     OutputDebugString(str);
67 }
68
69 DWORD WINAPI feedback(LPVOID args);
70
71 typedef struct feedback_args_t {
72     HANDLE in;
73     HANDLE out;
74 } feedback_args_t;
75
76 int main(int argc, char** argv)
77 {
78     char str[1024], *contitle = NULL;
79     HANDLE hproc, thread;
80     HANDLE hwinsta = NULL, hsavewinsta;
81     HANDLE hdesk = NULL, hsavedesk = NULL;
82     HANDLE conin, conout;
83     HANDLE hstdin, hstdout, hstderr, hdup;
84     feedback_args_t feed;
85     DWORD conmode;
86     DWORD newinmode = 0, notinmode = 0;
87     DWORD newoutmode = 0, notoutmode = 0;
88     DWORD tid;
89     DWORD len;
90     DWORD timeout = INFINITE;
91     BOOL isservice = FALSE;
92     char *arg0 = argv[0];
93
94     while (--argc) {
95         ++argv;
96         if (**argv == '/' || **argv == '-') {
97             switch (tolower((*argv)[1])) {
98                 case 'c':
99                     notinmode |= ENABLE_LINE_INPUT;          break;
100                 case 'l':
101                     newinmode |= ENABLE_LINE_INPUT;          break;
102                 case 'q':
103                     notinmode |= ENABLE_ECHO_INPUT;          break;
104                 case 'e':
105                     newinmode |= ENABLE_ECHO_INPUT;          break;
106                 case 'u':
107                     notinmode |= ENABLE_PROCESSED_INPUT;     break;
108                 case 'p':
109                     newinmode |= ENABLE_PROCESSED_INPUT;     break;
110                 case 'n':
111                     notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
112                 case 'w':
113                     newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
114                 case 'r':
115                     notoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
116                 case 'f':
117                     newoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
118                 case 'o':
119                     if (*(argv + 1) && *(argv + 1)[0] != '-') {
120                         *(++argv);
121                         timeout = atoi(*argv) / 1000;
122                         --argc;
123                     }
124                     else {
125                         timeout = 0;
126                     }   
127                     break;
128                 case 'v':
129                     verbose = TRUE;
130                     break;
131                 case 't':
132                     contitle = *(++argv);
133                     --argc;
134                     break;
135                 case '?':
136                     printf(options, arg0);
137                     exit(1);
138                 default:
139                     printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
140                     exit(1);
141             }
142         }
143         else {
144             printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
145             exit(1);
146         }
147     }
148
149     hproc = GetCurrentProcess();
150     hsavewinsta = GetProcessWindowStation();
151     if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
152         printerr("GetProcessWindowStation() failed (%d)\n", GetLastError());
153     }
154     else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
155         printerr("GetUserObjectInfoformation(hWinSta) failed (%d)\n", GetLastError());
156     }
157     else if (strnicmp(str, "Service-", 8) == 0) {
158         printerr("WindowStation Name %s is a service\n", str);
159         isservice = TRUE;
160     }
161     SetLastError(0);
162
163     hstdin = GetStdHandle(STD_INPUT_HANDLE);
164     if (!hstdin || hstdin == INVALID_HANDLE_VALUE) {
165         printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", 
166                  GetLastError());
167     }
168     else if (DuplicateHandle(hproc, hstdin, hproc, &hdup, 0, 
169                              isservice, DUPLICATE_SAME_ACCESS)) {
170         CloseHandle(hstdin);
171         hstdin = hdup;
172     }
173     else {
174         printerr("DupHandle(stdin [%x]) failed (%d)\n", 
175                  hstdin, GetLastError());
176     }
177
178     hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
179     if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
180         printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", 
181                  GetLastError());
182     }
183     else if (DuplicateHandle(hproc, hstdout, hproc, &hdup, 0, 
184                              isservice, DUPLICATE_SAME_ACCESS)) {
185         CloseHandle(hstdout);
186         hstdout = hdup;
187     }
188     else {
189         printerr("DupHandle(stdout [%x]) failed (%d)\n", 
190                  hstdout, GetLastError());
191     }
192
193     hstderr = GetStdHandle(STD_ERROR_HANDLE);
194     if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
195         printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", 
196                  GetLastError());
197     }
198     else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0, 
199                              isservice, DUPLICATE_SAME_ACCESS)) {
200         CloseHandle(hstderr);
201         hstderr = hdup;
202     }
203     else {
204         printerr("DupHandle(stderr [%x]) failed (%d)\n", 
205                  hstderr, GetLastError());
206     }
207
208     /* You can't close the console till all the handles above were
209      * rescued by DuplicateHandle()
210      */
211     if (!FreeConsole())
212         printerr("FreeConsole() failed (%d)\n", GetLastError());
213         
214     if (isservice) {
215 #ifdef WE_EVER_FIGURE_OUT_WHY_THIS_DOESNT_WORK
216         hsavedesk = GetThreadDesktop(GetCurrentThreadId());
217         if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) {
218             printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError());
219         }
220         CloseWindowStation(hwinsta);
221         hwinsta = OpenWindowStation("WinSta0", TRUE, MAXIMUM_ALLOWED);
222         if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) {
223             printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError());
224         }
225         else if (!SetProcessWindowStation(hwinsta)) {
226             printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError());
227         }
228         hdesk = OpenDesktop("Default", 0, TRUE, MAXIMUM_ALLOWED);
229         if (!hdesk || hdesk == INVALID_HANDLE_VALUE) {
230             printerr("OpenDesktop(Default) failed (%d)\n", GetLastError());
231         } 
232         else if (!SetThreadDesktop(hdesk)) {
233             printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError());
234         }
235 #else
236         PROCESS_INFORMATION pi;
237         STARTUPINFO si;
238         DWORD exitcode = 1;
239         char appbuff[MAX_PATH];
240         char *appname = NULL;
241         char *cmdline = GetCommandLine();
242         
243         if (!GetModuleFileName(NULL, appbuff, sizeof(appbuff))) {
244             appname = appbuff;
245         }
246         
247         memset(&si, 0, sizeof(si));
248         si.cb = sizeof(si);
249         si.dwFlags     = STARTF_USESHOWWINDOW
250                        | STARTF_USESTDHANDLES;
251         si.lpDesktop   = "WinSta0\\Default";
252         si.wShowWindow = 1;  /* SW_SHOWNORMAL */
253         si.hStdInput   = hstdin;
254         si.hStdOutput  = hstdout;
255         si.hStdError   = hstderr;
256
257         /* Instantly, upon creating the new process, we will close our
258          * copies of the handles so our parent isn't confused when the
259          * child closes their copy of the handle.  Without this action,
260          * we would hold a copy of the handle, and the parent would not
261          * receive their EOF notification.
262          */
263         if (CreateProcess(appname, cmdline, NULL, NULL, TRUE,
264                           CREATE_SUSPENDED | CREATE_NEW_CONSOLE, 
265                           NULL, NULL, &si, &pi)) {
266             CloseHandle(si.hStdInput);
267             CloseHandle(si.hStdOutput);
268             CloseHandle(si.hStdError);
269             ResumeThread(pi.hThread);
270             CloseHandle(pi.hThread);
271             WaitForSingleObject(pi.hProcess, INFINITE);
272             GetExitCodeProcess(pi.hProcess, &exitcode);
273             CloseHandle(pi.hProcess);
274             return exitcode;
275         }
276         return 1;
277 #endif
278     }
279
280     if (!AllocConsole()) {
281         printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
282     }
283
284     if (contitle && !SetConsoleTitle(contitle)) {
285         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
286     }
287
288     conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, 
289                         FILE_SHARE_READ | FILE_SHARE_WRITE, 
290                         FALSE, OPEN_EXISTING, 0, NULL);
291     if (!conout || conout == INVALID_HANDLE_VALUE) {
292         printerr("CreateFile(CONOUT$) failed (%d)\n", GetLastError());
293     }
294     else if (!GetConsoleMode(conout, &conmode)) {
295         printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
296     }
297     else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode)
298                                                          & ~notoutmode))) {
299         printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n", 
300                  conmode, GetLastError());
301     }
302
303     conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, 
304                        FILE_SHARE_READ | FILE_SHARE_WRITE, 
305                        FALSE, OPEN_EXISTING, 0, NULL);
306     if (!conin || conin == INVALID_HANDLE_VALUE) {
307         printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError());
308     }
309     else if (!GetConsoleMode(conin, &conmode)) {
310         printerr("GetConsoleMode(CONIN) failed (%d)\n", GetLastError());
311     }
312     else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode) 
313                                                         & ~notinmode))) {
314         printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n", 
315                  conmode, GetLastError());
316     }
317
318     feed.in = conin;
319     feed.out = hstdout;
320     thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid);
321
322     while (ReadFile(hstdin, str, sizeof(str), &len, NULL))
323         if (!len || !WriteFile(conout, str, len, &len, NULL))
324            break;
325
326     printerr("[EOF] from stdin (%d)\n", GetLastError());
327
328     CloseHandle(stdout);
329     if (!GetConsoleTitle(str, sizeof(str))) {
330         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
331     }
332     else {
333         strcat(str, " - [Finished]");
334         if (!SetConsoleTitle(str)) {
335             printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
336         }
337     }
338
339     WaitForSingleObject(thread, timeout);
340     FreeConsole();
341     if (isservice) {
342         if (!SetProcessWindowStation(hsavewinsta)) {
343             len = GetLastError();
344         }
345         if (!SetThreadDesktop(hsavedesk)) {
346             len = GetLastError();
347         }
348         CloseDesktop(hdesk);
349         CloseWindowStation(hwinsta);
350     }
351     return 0;
352 }
353
354
355 DWORD WINAPI feedback(LPVOID arg)
356 {
357     feedback_args_t *feed = (feedback_args_t*)arg;
358     char *str[1024];
359     DWORD len;
360
361     while (ReadFile(feed->in, str, sizeof(str), &len, NULL))
362         if (!len || !WriteFile(feed->out, str, len, &len, NULL))
363             break;
364
365     printerr("[EOF] from Console (%d)\n", GetLastError());
366
367     return 0;
368 }