Blame support/win32/wintty.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/* --------------------------------------------------------------------
Packit 90a5c9
 *
Packit 90a5c9
 * wintty : a Apache/WinNT support utility for monitoring and
Packit 90a5c9
 *          reflecting user feedback from the Apache process via
Packit 90a5c9
 *          stdin/stdout, even as running within the service context.
Packit 90a5c9
 *
Packit 90a5c9
 * Originally contributed by William Rowe <wrowe covalent.net>
Packit 90a5c9
 *
Packit 90a5c9
 * Note: this implementation is _very_ experimental, and error handling
Packit 90a5c9
 * is far from complete.  Using it as a cgi or pipe process allows the
Packit 90a5c9
 * programmer to discover if facilities such as reliable piped logs
Packit 90a5c9
 * are working as expected, or answer operator prompts that would
Packit 90a5c9
 * otherwise be discarded by the service process.
Packit 90a5c9
 *
Packit 90a5c9
 * Also note the isservice detection semantics, which far exceed any
Packit 90a5c9
 * mechanism we have discovered thus far.
Packit 90a5c9
 *
Packit 90a5c9
 * --------------------------------------------------------------------
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#define WIN32_LEAN_AND_MEAN
Packit 90a5c9
#include <windows.h>
Packit 90a5c9
#include <stdlib.h>
Packit 90a5c9
#include <stdio.h>
Packit 90a5c9
Packit 90a5c9
#if defined(_MSC_VER) && _MSC_VER >= 1400
Packit 90a5c9
#define _CRT_SECURE_NO_DEPRECATE
Packit 90a5c9
#pragma warning(disable: 4996)
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
const char *options =
Packit 90a5c9
"\nwintty: a utility for echoing the stdin stream to a new console window,\n"
Packit 90a5c9
"\teven when invoked from within a service (such as the Apache server.)\n"
Packit 90a5c9
"\tAlso reflects the console input back to the stdout stream, allowing\n"
Packit 90a5c9
"\tthe operator to respond to prompts from the context of a service.\n\n"
Packit 90a5c9
"Syntax: %s [opts] [-t \"Window Title\"]\n\n"
Packit 90a5c9
"  opts: -c{haracter}   or -l{ine} input\n"
Packit 90a5c9
"\t-q{uiet}       or -e{cho} input\n"
Packit 90a5c9
"\t-u{nprocessed} or -p{rocessed} input\n"
Packit 90a5c9
"\t-n{owrap}      or -w{rap} output lines\n"
Packit 90a5c9
"\t-f{ormatted}   or -r{aw} output lines\n"
Packit 90a5c9
"\t-O{output} [number of seconds]\n"
Packit 90a5c9
"\t-v{erbose} error reporting (for debugging)\n"
Packit 90a5c9
"\t-? for this message\n\n";
Packit 90a5c9
Packit 90a5c9
BOOL verbose = FALSE;
Packit 90a5c9
Packit 90a5c9
void printerr(char *fmt, ...)
Packit 90a5c9
{
Packit 90a5c9
    char str[1024];
Packit 90a5c9
    va_list args;
Packit 90a5c9
    if (!verbose)
Packit 90a5c9
        return;
Packit 90a5c9
    va_start(args, fmt);
Packit 90a5c9
    wvsprintf(str, fmt, args);
Packit 90a5c9
    OutputDebugString(str);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
DWORD WINAPI feedback(LPVOID args);
Packit 90a5c9
Packit 90a5c9
typedef struct feedback_args_t {
Packit 90a5c9
    HANDLE in;
Packit 90a5c9
    HANDLE out;
Packit 90a5c9
} feedback_args_t;
Packit 90a5c9
Packit 90a5c9
int main(int argc, char** argv)
Packit 90a5c9
{
Packit 90a5c9
    char str[1024], *contitle = NULL;
Packit 90a5c9
    HANDLE hproc, thread;
Packit 90a5c9
    HANDLE hwinsta = NULL, hsavewinsta;
Packit 90a5c9
    HANDLE hdesk = NULL, hsavedesk = NULL;
Packit 90a5c9
    HANDLE conin, conout;
Packit 90a5c9
    HANDLE hstdin, hstdout, hstderr, hdup;
Packit 90a5c9
    feedback_args_t feed;
Packit 90a5c9
    DWORD conmode;
Packit 90a5c9
    DWORD newinmode = 0, notinmode = 0;
Packit 90a5c9
    DWORD newoutmode = 0, notoutmode = 0;
Packit 90a5c9
    DWORD tid;
Packit 90a5c9
    DWORD len;
Packit 90a5c9
    DWORD timeout = INFINITE;
Packit 90a5c9
    BOOL isservice = FALSE;
Packit 90a5c9
    char *arg0 = argv[0];
Packit 90a5c9
Packit 90a5c9
    while (--argc) {
Packit 90a5c9
        ++argv;
Packit 90a5c9
        if (**argv == '/' || **argv == '-') {
Packit 90a5c9
            switch (tolower((*argv)[1])) {
Packit 90a5c9
                case 'c':
Packit 90a5c9
                    notinmode |= ENABLE_LINE_INPUT;          break;
Packit 90a5c9
                case 'l':
Packit 90a5c9
                    newinmode |= ENABLE_LINE_INPUT;          break;
Packit 90a5c9
                case 'q':
Packit 90a5c9
                    notinmode |= ENABLE_ECHO_INPUT;          break;
Packit 90a5c9
                case 'e':
Packit 90a5c9
                    newinmode |= ENABLE_ECHO_INPUT;          break;
Packit 90a5c9
                case 'u':
Packit 90a5c9
                    notinmode |= ENABLE_PROCESSED_INPUT;     break;
Packit 90a5c9
                case 'p':
Packit 90a5c9
                    newinmode |= ENABLE_PROCESSED_INPUT;     break;
Packit 90a5c9
                case 'n':
Packit 90a5c9
                    notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
Packit 90a5c9
                case 'w':
Packit 90a5c9
                    newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
Packit 90a5c9
                case 'r':
Packit 90a5c9
                    notoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
Packit 90a5c9
                case 'f':
Packit 90a5c9
                    newoutmode |= ENABLE_PROCESSED_OUTPUT;   break;
Packit 90a5c9
                case 'o':
Packit 90a5c9
                    if (*(argv + 1) && *(argv + 1)[0] != '-') {
Packit 90a5c9
                        *(++argv);
Packit 90a5c9
                        timeout = atoi(*argv) / 1000;
Packit 90a5c9
                        --argc;
Packit 90a5c9
                    }
Packit 90a5c9
                    else {
Packit 90a5c9
                        timeout = 0;
Packit 90a5c9
                    }
Packit 90a5c9
                    break;
Packit 90a5c9
                case 'v':
Packit 90a5c9
                    verbose = TRUE;
Packit 90a5c9
                    break;
Packit 90a5c9
                case 't':
Packit 90a5c9
                    contitle = *(++argv);
Packit 90a5c9
                    --argc;
Packit 90a5c9
                    break;
Packit 90a5c9
                case '?':
Packit 90a5c9
                    printf(options, arg0);
Packit 90a5c9
                    exit(1);
Packit 90a5c9
                default:
Packit 90a5c9
                    printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
Packit 90a5c9
                    exit(1);
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
        else {
Packit 90a5c9
            printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
Packit 90a5c9
            exit(1);
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    hproc = GetCurrentProcess();
Packit 90a5c9
    hsavewinsta = GetProcessWindowStation();
Packit 90a5c9
    if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("GetProcessWindowStation() failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) {
Packit 90a5c9
        printerr("GetUserObjectInfoformation(hWinSta) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (strnicmp(str, "Service-", 8) == 0) {
Packit 90a5c9
        printerr("WindowStation Name %s is a service\n", str);
Packit 90a5c9
        isservice = TRUE;
Packit 90a5c9
    }
Packit 90a5c9
    SetLastError(0);
Packit 90a5c9
Packit 90a5c9
    hstdin = GetStdHandle(STD_INPUT_HANDLE);
Packit 90a5c9
    if (!hstdin || hstdin == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n",
Packit 90a5c9
                 GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (DuplicateHandle(hproc, hstdin, hproc, &hdup, 0,
Packit 90a5c9
                             isservice, DUPLICATE_SAME_ACCESS)) {
Packit 90a5c9
        CloseHandle(hstdin);
Packit 90a5c9
        hstdin = hdup;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        printerr("DupHandle(stdin [%x]) failed (%d)\n",
Packit 90a5c9
                 hstdin, GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
Packit 90a5c9
    if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n",
Packit 90a5c9
                 GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (DuplicateHandle(hproc, hstdout, hproc, &hdup, 0,
Packit 90a5c9
                             isservice, DUPLICATE_SAME_ACCESS)) {
Packit 90a5c9
        CloseHandle(hstdout);
Packit 90a5c9
        hstdout = hdup;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        printerr("DupHandle(stdout [%x]) failed (%d)\n",
Packit 90a5c9
                 hstdout, GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    hstderr = GetStdHandle(STD_ERROR_HANDLE);
Packit 90a5c9
    if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n",
Packit 90a5c9
                 GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0,
Packit 90a5c9
                             isservice, DUPLICATE_SAME_ACCESS)) {
Packit 90a5c9
        CloseHandle(hstderr);
Packit 90a5c9
        hstderr = hdup;
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        printerr("DupHandle(stderr [%x]) failed (%d)\n",
Packit 90a5c9
                 hstderr, GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* You can't close the console till all the handles above were
Packit 90a5c9
     * rescued by DuplicateHandle()
Packit 90a5c9
     */
Packit 90a5c9
    if (!FreeConsole())
Packit 90a5c9
        printerr("FreeConsole() failed (%d)\n", GetLastError());
Packit 90a5c9
Packit 90a5c9
    if (isservice) {
Packit 90a5c9
#ifdef WE_EVER_FIGURE_OUT_WHY_THIS_DOESNT_WORK
Packit 90a5c9
        hsavedesk = GetThreadDesktop(GetCurrentThreadId());
Packit 90a5c9
        if (!hsavedesk || hsavedesk == INVALID_HANDLE_VALUE) {
Packit 90a5c9
            printerr("GetThreadDesktop(GetTID()) failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
        CloseWindowStation(hwinsta);
Packit 90a5c9
        hwinsta = OpenWindowStation("WinSta0", TRUE, MAXIMUM_ALLOWED);
Packit 90a5c9
        if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) {
Packit 90a5c9
            printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
        else if (!SetProcessWindowStation(hwinsta)) {
Packit 90a5c9
            printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
        hdesk = OpenDesktop("Default", 0, TRUE, MAXIMUM_ALLOWED);
Packit 90a5c9
        if (!hdesk || hdesk == INVALID_HANDLE_VALUE) {
Packit 90a5c9
            printerr("OpenDesktop(Default) failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
        else if (!SetThreadDesktop(hdesk)) {
Packit 90a5c9
            printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
#else
Packit 90a5c9
        PROCESS_INFORMATION pi;
Packit 90a5c9
        STARTUPINFO si;
Packit 90a5c9
        DWORD exitcode = 1;
Packit 90a5c9
        char appbuff[MAX_PATH];
Packit 90a5c9
        char *appname = NULL;
Packit 90a5c9
        char *cmdline = GetCommandLine();
Packit 90a5c9
Packit 90a5c9
        if (!GetModuleFileName(NULL, appbuff, sizeof(appbuff))) {
Packit 90a5c9
            appname = appbuff;
Packit 90a5c9
        }
Packit 90a5c9
Packit 90a5c9
        memset(&si, 0, sizeof(si));
Packit 90a5c9
        si.cb = sizeof(si);
Packit 90a5c9
        si.dwFlags     = STARTF_USESHOWWINDOW
Packit 90a5c9
                       | STARTF_USESTDHANDLES;
Packit 90a5c9
        si.lpDesktop   = "WinSta0\\Default";
Packit 90a5c9
        si.wShowWindow = 1;  /* SW_SHOWNORMAL */
Packit 90a5c9
        si.hStdInput   = hstdin;
Packit 90a5c9
        si.hStdOutput  = hstdout;
Packit 90a5c9
        si.hStdError   = hstderr;
Packit 90a5c9
Packit 90a5c9
        /* Instantly, upon creating the new process, we will close our
Packit 90a5c9
         * copies of the handles so our parent isn't confused when the
Packit 90a5c9
         * child closes their copy of the handle.  Without this action,
Packit 90a5c9
         * we would hold a copy of the handle, and the parent would not
Packit 90a5c9
         * receive their EOF notification.
Packit 90a5c9
         */
Packit 90a5c9
        if (CreateProcess(appname, cmdline, NULL, NULL, TRUE,
Packit 90a5c9
                          CREATE_SUSPENDED | CREATE_NEW_CONSOLE,
Packit 90a5c9
                          NULL, NULL, &si, &pi)) {
Packit 90a5c9
            CloseHandle(si.hStdInput);
Packit 90a5c9
            CloseHandle(si.hStdOutput);
Packit 90a5c9
            CloseHandle(si.hStdError);
Packit 90a5c9
            ResumeThread(pi.hThread);
Packit 90a5c9
            CloseHandle(pi.hThread);
Packit 90a5c9
            WaitForSingleObject(pi.hProcess, INFINITE);
Packit 90a5c9
            GetExitCodeProcess(pi.hProcess, &exitcode);
Packit 90a5c9
            CloseHandle(pi.hProcess);
Packit 90a5c9
            return exitcode;
Packit 90a5c9
        }
Packit 90a5c9
        return 1;
Packit 90a5c9
#endif
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!AllocConsole()) {
Packit 90a5c9
        printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (contitle && !SetConsoleTitle(contitle)) {
Packit 90a5c9
        printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    conout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
Packit 90a5c9
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
Packit 90a5c9
                        FALSE, OPEN_EXISTING, 0, NULL);
Packit 90a5c9
    if (!conout || conout == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("CreateFile(CONOUT$) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (!GetConsoleMode(conout, &conmode)) {
Packit 90a5c9
        printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode)
Packit 90a5c9
                                                         & ~notoutmode))) {
Packit 90a5c9
        printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n",
Packit 90a5c9
                 conmode, GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
Packit 90a5c9
                       FILE_SHARE_READ | FILE_SHARE_WRITE,
Packit 90a5c9
                       FALSE, OPEN_EXISTING, 0, NULL);
Packit 90a5c9
    if (!conin || conin == INVALID_HANDLE_VALUE) {
Packit 90a5c9
        printerr("CreateFile(CONIN$) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (!GetConsoleMode(conin, &conmode)) {
Packit 90a5c9
        printerr("GetConsoleMode(CONIN) failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode)
Packit 90a5c9
                                                        & ~notinmode))) {
Packit 90a5c9
        printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n",
Packit 90a5c9
                 conmode, GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    feed.in = conin;
Packit 90a5c9
    feed.out = hstdout;
Packit 90a5c9
    thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid;;
Packit 90a5c9
Packit 90a5c9
    while (ReadFile(hstdin, str, sizeof(str), &len, NULL))
Packit 90a5c9
        if (!len || !WriteFile(conout, str, len, &len, NULL))
Packit 90a5c9
           break;
Packit 90a5c9
Packit 90a5c9
    printerr("[EOF] from stdin (%d)\n", GetLastError());
Packit 90a5c9
Packit 90a5c9
    CloseHandle(stdout);
Packit 90a5c9
    if (!GetConsoleTitle(str, sizeof(str))) {
Packit 90a5c9
        printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
Packit 90a5c9
    }
Packit 90a5c9
    else {
Packit 90a5c9
        strcat(str, " - [Finished]");
Packit 90a5c9
        if (!SetConsoleTitle(str)) {
Packit 90a5c9
            printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    WaitForSingleObject(thread, timeout);
Packit 90a5c9
    FreeConsole();
Packit 90a5c9
    if (isservice) {
Packit 90a5c9
        if (!SetProcessWindowStation(hsavewinsta)) {
Packit 90a5c9
            len = GetLastError();
Packit 90a5c9
        }
Packit 90a5c9
        if (!SetThreadDesktop(hsavedesk)) {
Packit 90a5c9
            len = GetLastError();
Packit 90a5c9
        }
Packit 90a5c9
        CloseDesktop(hdesk);
Packit 90a5c9
        CloseWindowStation(hwinsta);
Packit 90a5c9
    }
Packit 90a5c9
    return 0;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
DWORD WINAPI feedback(LPVOID arg)
Packit 90a5c9
{
Packit 90a5c9
    feedback_args_t *feed = (feedback_args_t*)arg;
Packit 90a5c9
    char *str[1024];
Packit 90a5c9
    DWORD len;
Packit 90a5c9
Packit 90a5c9
    while (ReadFile(feed->in, str, sizeof(str), &len, NULL))
Packit 90a5c9
        if (!len || !WriteFile(feed->out, str, len, &len, NULL))
Packit 90a5c9
            break;
Packit 90a5c9
Packit 90a5c9
    printerr("[EOF] from Console (%d)\n", GetLastError());
Packit 90a5c9
Packit 90a5c9
    return 0;
Packit 90a5c9
}