]> granicus.if.org Git - apache/blob - support/win32/wintty.c
Update our copyright for this year.
[apache] / support / win32 / wintty.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  */
49
50 /* --------------------------------------------------------------------
51  *
52  * wintty : a Apache/WinNT support utility for monitoring and 
53  *          reflecting user feedback from the Apache process via
54  *          stdin/stdout, even as running within the service context.
55  *
56  * Originally contributed by William Rowe <wrowe@covalent.net>
57  *
58  * Note: this implementation is _very_ experimental, and error handling
59  * is far from complete.  Using it as a cgi or pipe process allows the
60  * programmer to discover if facilities such as reliable piped logs
61  * are working as expected, or answer operator prompts that would
62  * otherwise be discarded by the service process.
63  *
64  * Also note the isservice detection semantics, which far exceed any
65  * mechanism we have discovered thus far.
66  * 
67  * --------------------------------------------------------------------
68  */
69
70 #define WIN32_LEAN_AND_MEAN
71 #include <windows.h>
72 #include <stdlib.h>
73 #include <stdio.h>
74
75 const char *options = 
76 "\nwintty: a utility for echoing the stdin stream to a new console window,\n"
77 "\teven when invoked from within a service (such as the Apache server.)\n"
78 "\tAlso reflects the console input back to the stdout stream, allowing\n"
79 "\tthe operator to respond to prompts from the context of a service.\n\n"
80 "Syntax: %s [opts] [-t \"Window Title\"]\n\n"
81 "  opts: -c{haracter}   or -l{ine} input\n"
82 "\t-q{uiet}       or -e{cho} input\n"
83 "\t-u{nprocessed} or -p{rocessed} input\n"
84 "\t-n{owrap}      or -w{rap} output lines\n"
85 "\t-f{ormatted}   or -r{aw} output lines\n"
86 "\t-v{erbose} error reporting (for debugging)\n"
87 "\t-? for this message\n\n";
88
89 BOOL verbose = FALSE;
90
91 void printerr(char *fmt, ...) 
92 {
93     char str[1024];
94     va_list args;
95     if (!verbose)
96         return;
97     va_start(args, fmt);
98     wvsprintf(str, fmt, args);
99     OutputDebugString(str);
100 }
101
102 DWORD WINAPI feedback(LPVOID args);
103
104 typedef struct feedback_args_t {
105     HANDLE in;
106     HANDLE out;
107 } feedback_args_t;
108
109 int main(int argc, char** argv)
110 {
111     char str[1024], *contitle;
112     HANDLE hproc, thread;
113     HANDLE hwinsta, hsavewinsta;
114     HANDLE hdesk, hsavedesk;
115     HANDLE conin, conout;
116     HANDLE hstdin, hstdout, hstderr, hdup;
117     feedback_args_t feed;
118     DWORD conmode;
119     DWORD newinmode = 0, notinmode = 0;
120     DWORD newoutmode = 0, notoutmode = 0;
121     DWORD tid;
122     DWORD len;
123     BOOL isservice = FALSE;
124     char *arg0 = argv[0];
125
126     while (--argc) {
127         ++argv;
128         if (**argv == '/' || **argv == '-') {
129             switch (tolower((*argv)[1])) {
130                 case 'c':
131                     notinmode |= ENABLE_LINE_INPUT;          break;
132                 case 'l':
133                     newinmode |= ENABLE_LINE_INPUT;          break;
134                 case 'q':
135                     notinmode |= ENABLE_ECHO_INPUT;          break;
136                 case 'e':
137                     newinmode |= ENABLE_ECHO_INPUT;          break;
138                 case 'u':
139                     notinmode |= ENABLE_PROCESSED_INPUT;     break;
140                 case 'p':
141                     newinmode |= ENABLE_PROCESSED_INPUT;     break;
142                 case 'n':
143                     notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
144                 case 'w':
145                     newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
146                 case 'r':
147                     notoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
148                 case 'f':
149                     newoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
150                 case 'v':
151                     verbose = TRUE;
152                     break;
153                 case 't':
154                     contitle = *(++argv);
155                     --argc;
156                     break;
157                 case '?':
158                     printf(options, arg0);
159                     exit(1);
160                 default:
161                     printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
162                     exit(1);
163             }
164         }
165         else {
166             printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
167             exit(1);
168         }
169     }
170
171     hproc = GetCurrentProcess();
172     hsavewinsta = GetProcessWindowStation();
173     if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
174         printerr("GetProcessWindowStation() failed (%d)\n", GetLastError());
175     }
176     else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
177         printerr("GetUserObjectInfoformation(hWinSta) failed (%d)\n", GetLastError());
178     }
179     else if (strnicmp(str, "Service-", 8) == 0) {
180         printerr("WindowStation Name %s is a service\n", str);
181         isservice = TRUE;
182     }
183     SetLastError(0);
184
185     hstdin = GetStdHandle(STD_INPUT_HANDLE);
186     if (!hstdin || hstdin == INVALID_HANDLE_VALUE) {
187         printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", 
188                  GetLastError());
189     }
190     else if (DuplicateHandle(hproc, hstdin, hproc, &hdup, 0, 
191                              isservice, DUPLICATE_SAME_ACCESS)) {
192         CloseHandle(hstdin);
193         hstdin = hdup;
194     }
195     else {
196         printerr("DupHandle(stdin [%x]) failed (%d)\n", 
197                  hstdin, GetLastError());
198     }
199
200     hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
201     if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
202         printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", 
203                  GetLastError());
204     }
205     else if (DuplicateHandle(hproc, hstdout, hproc, &hdup, 0, 
206                              isservice, DUPLICATE_SAME_ACCESS)) {
207         CloseHandle(hstdout);
208         hstdout = hdup;
209     }
210     else {
211         printerr("DupHandle(stdout [%x]) failed (%d)\n", 
212                  hstdout, GetLastError());
213     }
214
215     hstderr = GetStdHandle(STD_ERROR_HANDLE);
216     if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
217         printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", 
218                  GetLastError());
219     }
220     else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0, 
221                              isservice, DUPLICATE_SAME_ACCESS)) {
222         CloseHandle(hstderr);
223         hstderr = hdup;
224     }
225     else {
226         printerr("DupHandle(stderr [%x]) failed (%d)\n", 
227                  hstderr, GetLastError());
228     }
229
230     /* You can't close the console till all the handles above were
231      * rescued by DuplicateHandle()
232      */
233     if (!FreeConsole())
234         printerr("FreeConsole() failed (%d)\n", GetLastError());
235         
236     if (isservice) {
237 #ifdef WE_EVER_FIGURE_OUT_WHY_THIS_DOESNT_WORK
238         hsavedesk = GetThreadDesktop(GetCurrentThreadId());
239         if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) {
240             printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError());
241         }
242         CloseWindowStation(hwinsta);
243         hwinsta = OpenWindowStation("WinSta0", TRUE, MAXIMUM_ALLOWED);
244         if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) {
245             printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError());
246         }
247         else if (!SetProcessWindowStation(hwinsta)) {
248             printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError());
249         }
250         hdesk = OpenDesktop("Default", 0, TRUE, MAXIMUM_ALLOWED);
251         if (!hdesk || hdesk == INVALID_HANDLE_VALUE) {
252             printerr("OpenDesktop(Default) failed (%d)\n", GetLastError());
253         } 
254         else if (!SetThreadDesktop(hdesk)) {
255             printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError());
256         }
257 #else
258         PROCESS_INFORMATION pi;
259         STARTUPINFO si;
260         DWORD exitcode = 1;
261         char appbuff[MAX_PATH];
262         char *appname = NULL;
263         char *cmdline = GetCommandLine();
264         
265         if (!GetModuleFileName(NULL, appbuff, sizeof(appbuff))) {
266             appname = appbuff;
267         }
268         
269         memset(&si, 0, sizeof(si));
270         si.cb = sizeof(si);
271         si.dwFlags     = STARTF_USESHOWWINDOW
272                        | STARTF_USESTDHANDLES;
273         si.lpDesktop   = "WinSta0\\Default";
274         si.wShowWindow = 1;  /* SW_SHOWNORMAL */
275         si.hStdInput   = hstdin;
276         si.hStdOutput  = hstdout;
277         si.hStdError   = hstderr;
278
279         /* Instantly, upon creating the new process, we will close our
280          * copies of the handles so our parent isn't confused when the
281          * child closes their copy of the handle.  Without this action,
282          * we would hold a copy of the handle, and the parent would not
283          * receive their EOF notification.
284          */
285         if (CreateProcess(appname, cmdline, NULL, NULL, TRUE,
286                           CREATE_SUSPENDED | CREATE_NEW_CONSOLE, 
287                           NULL, NULL, &si, &pi)) {
288             CloseHandle(si.hStdInput);
289             CloseHandle(si.hStdOutput);
290             CloseHandle(si.hStdError);
291             ResumeThread(pi.hThread);
292             CloseHandle(pi.hThread);
293             WaitForSingleObject(pi.hProcess, INFINITE);
294             GetExitCodeProcess(pi.hProcess, &exitcode);
295             CloseHandle(pi.hProcess);
296             return exitcode;
297         }
298         return 1;
299 #endif
300     }
301
302     if (!AllocConsole()) {
303         printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
304     }
305
306     if (contitle && !SetConsoleTitle(contitle)) {
307         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
308     }
309
310     conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, 
311                         FILE_SHARE_READ | FILE_SHARE_WRITE, 
312                         FALSE, OPEN_EXISTING, 0, NULL);
313     if (!conout || conout == INVALID_HANDLE_VALUE) {
314         printerr("CreateFile(CONOUT$) failed (%d)\n", GetLastError());
315     }
316     else if (!GetConsoleMode(conout, &conmode)) {
317         printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
318     }
319     else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode)
320                                                          & ~notoutmode))) {
321         printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n", 
322                  conmode, GetLastError());
323     }
324
325     conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, 
326                        FILE_SHARE_READ | FILE_SHARE_WRITE, 
327                        FALSE, OPEN_EXISTING, 0, NULL);
328     if (!conin || conin == INVALID_HANDLE_VALUE) {
329         printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError());
330     }
331     else if (!GetConsoleMode(conin, &conmode)) {
332         printerr("GetConsoleMode(CONIN) failed (%d)\n", GetLastError());
333     }
334     else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode) 
335                                                         & ~notinmode))) {
336         printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n", 
337                  conmode, GetLastError());
338     }
339
340     feed.in = conin;
341     feed.out = hstdout;
342     thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid);
343
344     while (ReadFile(hstdin, str, sizeof(str), &len, NULL))
345         if (!len || !WriteFile(conout, str, len, &len, NULL))
346            break;
347
348     printerr("[EOF] from stdin (%d)\n", GetLastError());
349
350     CloseHandle(stdout);
351     if (!GetConsoleTitle(str, sizeof(str))) {
352         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
353     }
354     else {
355         strcat(str, " - [Finished]");
356         if (!SetConsoleTitle(str)) {
357             printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
358         }
359     }
360
361     WaitForSingleObject(thread, INFINITE);
362     FreeConsole();
363     if (isservice) {
364         if (!SetProcessWindowStation(hsavewinsta)) {
365             len = GetLastError();
366         }
367         if (!SetThreadDesktop(hsavedesk)) {
368             len = GetLastError();
369         }
370         CloseDesktop(hdesk);
371         CloseWindowStation(hwinsta);
372     }
373     return 0;
374 }
375
376
377 DWORD WINAPI feedback(LPVOID arg)
378 {
379     feedback_args_t *feed = (feedback_args_t*)arg;
380     char *str[1024];
381     DWORD len;
382
383     while (ReadFile(feed->in, str, sizeof(str), &len, NULL))
384         if (!len || !WriteFile(feed->out, str, len, &len, NULL))
385             break;
386
387     printerr("[EOF] from Console (%d)\n", GetLastError());
388
389     return 0;
390 }