summaryrefslogtreecommitdiffstats
path: root/support/win32/wintty.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 06:33:50 +0000
commitfe39ffb8b90ae4e002ed73fe98617cd590abb467 (patch)
treeb80e5956907d8aeaaffe4e4f0c068c0e6157ce8b /support/win32/wintty.c
parentInitial commit. (diff)
downloadapache2-upstream/2.4.56.tar.xz
apache2-upstream/2.4.56.zip
Adding upstream version 2.4.56.upstream/2.4.56
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'support/win32/wintty.c')
-rw-r--r--support/win32/wintty.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/support/win32/wintty.c b/support/win32/wintty.c
new file mode 100644
index 0000000..684ce5b
--- /dev/null
+++ b/support/win32/wintty.c
@@ -0,0 +1,374 @@
+/* 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
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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
+ * 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>
+ *
+ * Note: this implementation is _very_ experimental, and error handling
+ * is far from complete. Using it as a cgi or pipe process allows the
+ * programmer to discover if facilities such as reliable piped logs
+ * are working as expected, or answer operator prompts that would
+ * otherwise be discarded by the service process.
+ *
+ * Also note the isservice detection semantics, which far exceed any
+ * mechanism we have discovered thus far.
+ *
+ * --------------------------------------------------------------------
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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"
+"\tthe operator to respond to prompts from the context of a service.\n\n"
+"Syntax: %s [opts] [-t \"Window Title\"]\n\n"
+" opts: -c{haracter} or -l{ine} input\n"
+"\t-q{uiet} or -e{cho} input\n"
+"\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";
+
+BOOL verbose = FALSE;
+
+void printerr(char *fmt, ...)
+{
+ char str[1024];
+ va_list args;
+ if (!verbose)
+ return;
+ va_start(args, fmt);
+ wvsprintf(str, fmt, args);
+ OutputDebugString(str);
+}
+
+DWORD WINAPI feedback(LPVOID args);
+
+typedef struct feedback_args_t {
+ HANDLE in;
+ HANDLE out;
+} feedback_args_t;
+
+int main(int argc, char** argv)
+{
+ char str[1024], *contitle = NULL;
+ HANDLE hproc, thread;
+ HANDLE hwinsta = NULL, hsavewinsta;
+ HANDLE hdesk = NULL, hsavedesk = NULL;
+ HANDLE conin, conout;
+ 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];
+
+ while (--argc) {
+ ++argv;
+ if (**argv == '/' || **argv == '-') {
+ switch (tolower((*argv)[1])) {
+ case 'c':
+ notinmode |= ENABLE_LINE_INPUT; break;
+ case 'l':
+ newinmode |= ENABLE_LINE_INPUT; break;
+ case 'q':
+ notinmode |= ENABLE_ECHO_INPUT; break;
+ case 'e':
+ newinmode |= ENABLE_ECHO_INPUT; break;
+ case 'u':
+ notinmode |= ENABLE_PROCESSED_INPUT; break;
+ case 'p':
+ newinmode |= ENABLE_PROCESSED_INPUT; break;
+ case 'n':
+ notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
+ case 'w':
+ newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break;
+ case 'r':
+ 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;
+ case 't':
+ contitle = *(++argv);
+ --argc;
+ break;
+ case '?':
+ printf(options, arg0);
+ exit(1);
+ default:
+ printf("wintty option %s not recognized, use -? for help.\n\n", *argv);
+ exit(1);
+ }
+ }
+ else {
+ printf("wintty argument %s not understood, use -? for help.\n\n", *argv);
+ exit(1);
+ }
+ }
+
+ hproc = GetCurrentProcess();
+ 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 (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());
+ }
+ 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());
+ }
+ 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());
+ }
+
+ hstderr = GetStdHandle(STD_ERROR_HANDLE);
+ if (!hstderr || hstderr == INVALID_HANDLE_VALUE) {
+ printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n",
+ GetLastError());
+ }
+ else if (DuplicateHandle(hproc, hstderr, hproc, &hdup, 0,
+ isservice, DUPLICATE_SAME_ACCESS)) {
+ CloseHandle(hstderr);
+ hstderr = hdup;
+ }
+ else {
+ printerr("DupHandle(stderr [%x]) failed (%d)\n",
+ hstderr, GetLastError());
+ }
+
+ /* You can't close the console till all the handles above were
+ * rescued by DuplicateHandle()
+ */
+ if (!FreeConsole())
+ printerr("FreeConsole() failed (%d)\n", GetLastError());
+
+ if (isservice) {
+#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());
+ }
+ 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()) {
+ printerr("AllocConsole(Default) failed (%d)\n", GetLastError());
+ }
+
+ if (contitle && !SetConsoleTitle(contitle)) {
+ printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+ }
+
+ 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("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());
+ }
+
+ 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("CreateFile(CONIN$) failed (%d)\n", GetLastError());
+ }
+ else if (!GetConsoleMode(conin, &conmode)) {
+ 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());
+ }
+
+ feed.in = conin;
+ feed.out = hstdout;
+ thread = CreateThread(NULL, 0, feedback, (LPVOID)&feed, 0, &tid);
+
+ 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(stdout);
+ if (!GetConsoleTitle(str, sizeof(str))) {
+ printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+ }
+ else {
+ strcat(str, " - [Finished]");
+ if (!SetConsoleTitle(str)) {
+ printerr("SetConsoleTitle() failed (%d)\n", GetLastError());
+ }
+ }
+
+ WaitForSingleObject(thread, timeout);
+ FreeConsole();
+ if (isservice) {
+ if (!SetProcessWindowStation(hsavewinsta)) {
+ len = GetLastError();
+ }
+ if (!SetThreadDesktop(hsavedesk)) {
+ len = GetLastError();
+ }
+ CloseDesktop(hdesk);
+ CloseWindowStation(hwinsta);
+ }
+ return 0;
+}
+
+
+DWORD WINAPI feedback(LPVOID arg)
+{
+ feedback_args_t *feed = (feedback_args_t*)arg;
+ char *str[1024];
+ DWORD len;
+
+ while (ReadFile(feed->in, str, sizeof(str), &len, NULL))
+ if (!len || !WriteFile(feed->out, str, len, &len, NULL))
+ break;
+
+ printerr("[EOF] from Console (%d)\n", GetLastError());
+
+ return 0;
+}