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