]> granicus.if.org Git - apache/blobdiff - support/win32/wintty.c
More splitting as suggested by minfrin.
[apache] / support / win32 / wintty.c
index acdde94b6daad18ee1a9f3afbc8d4ab5d02c5d5d..684ce5b52c421356f444db147dca6e32f7e950a1 100644 (file)
@@ -1,59 +1,26 @@
-/* ====================================================================
- * The Apache Software License, Version 1.1
+/* 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
  *
- * Copyright (c) 2000-2001 The Apache Software Foundation.  All rights
- * reserved.
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- *    if any, must include the following acknowledgment:
- *       "This product includes software developed by the
- *        Apache Software Foundation (http://www.apache.org/)."
- *    Alternately, this acknowledgment may appear in the software itself,
- *    if and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "Apache" and "Apache Software Foundation" must
- *    not be used to endorse or promote products derived from this
- *    software without prior written permission. For written
- *    permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- *    nor may "Apache" appear in their name, without prior written
- *    permission of the Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
+ * 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 
+ * 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>
+ * 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
@@ -63,7 +30,7 @@
  *
  * Also note the isservice detection semantics, which far exceed any
  * mechanism we have discovered thus far.
- * 
+ *
  * --------------------------------------------------------------------
  */
 
 #include <stdlib.h>
 #include <stdio.h>
 
-const char *options = 
+#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"
@@ -83,25 +55,24 @@ const char *options =
 "\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";
 
-HANDLE herrout = NULL;
 BOOL verbose = FALSE;
 
-void printerr(char *fmt, ...) 
+void printerr(char *fmt, ...)
 {
     char str[1024];
     va_list args;
-    DWORD len;
-    if (!verbose || !herrout)
+    if (!verbose)
         return;
     va_start(args, fmt);
     wvsprintf(str, fmt, args);
-    WriteFile(herrout, str, len = strlen(str), &len, NULL);
+    OutputDebugString(str);
 }
 
-DWORD WINAPI feedback(LPVOID pipeout);
+DWORD WINAPI feedback(LPVOID args);
 
 typedef struct feedback_args_t {
     HANDLE in;
@@ -110,19 +81,19 @@ typedef struct feedback_args_t {
 
 int main(int argc, char** argv)
 {
-    char str[1024], *contitle;
+    char str[1024], *contitle = NULL;
     HANDLE hproc, thread;
-    HANDLE hwinsta, hsavewinsta;
-    HANDLE hdesk, hsavedesk;
+    HANDLE hwinsta = NULL, hsavewinsta;
+    HANDLE hdesk = NULL, hsavedesk = NULL;
     HANDLE conin, conout;
-    HANDLE pipein, pipeout;
-    HANDLE hstdin, hstdout, hstderr;
+    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];
 
@@ -150,6 +121,16 @@ int main(int argc, char** argv)
                     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;
@@ -160,7 +141,7 @@ int main(int argc, char** argv)
                 case '?':
                     printf(options, arg0);
                     exit(1);
-               default:
+                default:
                     printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
                     exit(1);
             }
@@ -172,89 +153,134 @@ int main(int argc, char** argv)
     }
 
     hproc = GetCurrentProcess();
-    herrout = hstderr = GetStdHandle(STD_ERROR_HANDLE);
-    if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
-        printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", GetLastError());
-        herrout = NULL;
+    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 (!DuplicateHandle(hproc, hstderr,
-                         hproc, &herrout, 0, FALSE, 
-                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
-        printerr("DupHandle(stderr) 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());
+        printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n",
+                 GetLastError());
     }
-    else if (!DuplicateHandle(hproc, hstdin,
-                         hproc, &pipein, 0, FALSE, 
-                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
-        printerr("DupHandle(stdin) 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());
+        printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n",
+                 GetLastError());
     }
-    else if (!DuplicateHandle(hproc, hstdout,
-                         hproc, &pipeout, 0, FALSE, 
-                         DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
-        printerr("DupHandle(stdout) 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());
     }
 
-    hsavewinsta = GetProcessWindowStation();
-    if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
-        printerr("GetProcWinSta() failed (%d)\n", GetLastError());
+    hstderr = GetStdHandle(STD_ERROR_HANDLE);
+    if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
+        printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n",
+                 GetLastError());
     }
-    else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
-        printerr("GetUserObjectInfo(GetProcWinSta) failed (%d)\n", GetLastError());
-        CloseHandle(hsavewinsta);
+    else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0,
+                             isservice, DUPLICATE_SAME_ACCESS)) {
+        CloseHandle(hstderr);
+        hstderr = hdup;
     }
-    else if (strnicmp(str, "Service-", 8) == 0) {
-        isservice = TRUE;
+    else {
+        printerr("DupHandle(stderr [%x]) failed (%d)\n",
+                 hstderr, GetLastError());
     }
-    else
-        CloseHandle(hsavewinsta);
-    SetLastError(0);
 
+    /* You can't close the console till all the handles above were
+     * rescued by DuplicateHandle()
+     */
     if (!FreeConsole())
-        printerr("DupHandle(stdout) failed (%d)\n", GetLastError());
+        printerr("FreeConsole() failed (%d)\n", GetLastError());
 
     if (isservice) {
-        hwinsta = OpenWindowStation("WinSta0", TRUE, 
-                            WINSTA_ACCESSCLIPBOARD     
-                          | WINSTA_ACCESSGLOBALATOMS  
-                          | WINSTA_ENUMDESKTOPS
-                          | WINSTA_ENUMERATE     
-                          | WINSTA_READATTRIBUTES  
-                          | WINSTA_READSCREEN
-                          | WINSTA_WRITEATTRIBUTES);
+#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());
         }
-       hsavedesk = GetThreadDesktop(GetCurrentThreadId());
-        if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) {
-            printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError());
-        }
-        hdesk = OpenDesktop("Default", 0, TRUE, 
-                            DESKTOP_READOBJECTS     
-                          | DESKTOP_CREATEWINDOW    
-                          | DESKTOP_CREATEMENU      
-                          | DESKTOP_HOOKCONTROL     
-                          | DESKTOP_JOURNALRECORD   
-                          | DESKTOP_JOURNALPLAYBACK 
-                          | DESKTOP_ENUMERATE       
-                          | DESKTOP_WRITEOBJECTS);
+        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()) {
@@ -265,46 +291,47 @@ int main(int argc, char** argv)
         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
     }
 
-    conout = CreateFile("CONOUT$", GENERIC_WRITE, 
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, 
+    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("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", GetLastError());
-    }
-    else if (!herrout) {
-        herrout = conout;
+        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());
+    else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode)
+                                                         & ~notoutmode))) {
+        printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n",
+                 conmode, GetLastError());
     }
 
-    conin = CreateFile("CONIN$", GENERIC_READ
-                       FILE_SHARE_READ | FILE_SHARE_WRITE, 
+    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("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", GetLastError());
+        printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError());
     }
     else if (!GetConsoleMode(conin, &conmode)) {
-        printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
+        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());
+    else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode)
+                                                        & ~notinmode))) {
+        printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n",
+                 conmode, GetLastError());
     }
 
     feed.in = conin;
-    feed.out = pipeout;
+    feed.out = hstdout;
     thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid);
 
-    while (ReadFile(pipein, str, sizeof(str), &len, NULL))
+    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(pipeout);
+    CloseHandle(stdout);
     if (!GetConsoleTitle(str, sizeof(str))) {
         printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
     }
@@ -315,9 +342,8 @@ int main(int argc, char** argv)
         }
     }
 
-    WaitForSingleObject(thread, INFINITE);
+    WaitForSingleObject(thread, timeout);
     FreeConsole();
-    CloseHandle(herrout);
     if (isservice) {
         if (!SetProcessWindowStation(hsavewinsta)) {
             len = GetLastError();