From 3831043eff9ed412428150537374facb609e5b33 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 19 Nov 2020 16:49:24 -0500 Subject: [PATCH] tool_doswin: Restore original console settings on CTRL signal - Move Windows terminal init code from tool_main to tool_doswin. - Restore the original console settings on CTRL+C and CTRL+BREAK. Background: On Windows the curl tool changes the console settings to enable virtual terminal processing (eg color output) if supported (ie Win 10). The original settings are restored on exit but prior to this change were not restored in the case of the CTRL signals. Windows VT behavior varies depending on console/powershell/terminal; refer to the discussion in #6226. Assisted-by: Rich Turner Closes https://github.com/curl/curl/pull/6226 --- src/tool_doswin.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ src/tool_main.c | 57 +++---------------------------------------- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 2ae6ba788..b70100abe 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -28,6 +28,7 @@ #endif #ifdef WIN32 +# include # include # include "tool_cfgable.h" # include "tool_libinfo.h" @@ -702,6 +703,64 @@ cleanup: return slist; } +/* The terminal settings to restore on exit */ +static struct TerminalSettings { + HANDLE hStdOut; + DWORD dwOutputMode; + LONG valid; +} TerminalSettings; + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif + +static void restore_terminal(void) +{ + if(InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE)) + SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); +} + +/* This is the console signal handler. + * The system calls it in a separate thread. + */ +static BOOL WINAPI signal_handler(DWORD type) +{ + if(type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) + restore_terminal(); + return FALSE; +} + +static void init_terminal(void) +{ + TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + /* + * Enable VT (Virtual Terminal) output. + * Note: VT mode flag can be set on any version of Windows, but VT + * processing only performed on Win10 >= Creators Update) + */ + if((TerminalSettings.hStdOut != INVALID_HANDLE_VALUE) && + GetConsoleMode(TerminalSettings.hStdOut, + &TerminalSettings.dwOutputMode) && + !(TerminalSettings.dwOutputMode & + ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { + /* The signal handler is set before attempting to change the console mode + because otherwise a signal would not be caught after the change but + before the handler was installed. */ + (void)InterlockedExchange(&TerminalSettings.valid, (LONG)TRUE); + if(SetConsoleCtrlHandler(signal_handler, TRUE)) { + if(SetConsoleMode(TerminalSettings.hStdOut, + (TerminalSettings.dwOutputMode | + ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { + atexit(restore_terminal); + } + else { + SetConsoleCtrlHandler(signal_handler, FALSE); + (void)InterlockedExchange(&TerminalSettings.valid, (LONG)FALSE); + } + } + } +} + LARGE_INTEGER tool_freq; bool tool_isVistaOrGreater; @@ -714,6 +773,9 @@ CURLcode win32_init(void) tool_isVistaOrGreater = false; QueryPerformanceFrequency(&tool_freq); + + init_terminal(); + return CURLE_OK; } diff --git a/src/tool_main.c b/src/tool_main.c index 16f513a43..1c4b07e7c 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -23,6 +23,10 @@ #include +#ifdef WIN32 +#include +#endif + #ifdef HAVE_SIGNAL_H #include #endif @@ -225,51 +229,6 @@ static void main_free(struct GlobalConfig *config) config->last = NULL; } -#ifdef WIN32 -/* TerminalSettings for Windows */ -static struct TerminalSettings { - HANDLE hStdOut; - DWORD dwOutputMode; -} TerminalSettings; - -static void configure_terminal(void) -{ - /* - * If we're running Windows, enable VT output. - * Note: VT mode flag can be set on any version of Windows, but VT - * processing only performed on Win10 >= Creators Update) - */ - - /* Define the VT flags in case we're building with an older SDK */ -#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING - #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 -#endif - - memset(&TerminalSettings, 0, sizeof(TerminalSettings)); - - /* Enable VT output */ - TerminalSettings.hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); - if((TerminalSettings.hStdOut != INVALID_HANDLE_VALUE) - && (GetConsoleMode(TerminalSettings.hStdOut, - &TerminalSettings.dwOutputMode))) { - SetConsoleMode(TerminalSettings.hStdOut, - TerminalSettings.dwOutputMode - | ENABLE_VIRTUAL_TERMINAL_PROCESSING); - } -} -#else -#define configure_terminal() -#endif - -static void restore_terminal(void) -{ -#ifdef WIN32 - /* Restore Console output mode and codepage to whatever they were - * when Curl started */ - SetConsoleMode(TerminalSettings.hStdOut, TerminalSettings.dwOutputMode); -#endif -} - /* ** curl tool main function. */ @@ -284,7 +243,6 @@ int main(int argc, char *argv[]) memset(&global, 0, sizeof(global)); #ifdef WIN32 -#ifdef _tcscmp /* Undocumented diagnostic option to list the full paths of all loaded modules. This is purposely pre-init. */ if(argc == 2 && !_tcscmp(argv[1], _T("--dump-module-paths"))) { @@ -294,7 +252,6 @@ int main(int argc, char *argv[]) curl_slist_free_all(head); return head ? 0 : 1; } -#endif /* _tcscmp */ /* win32_init must be called before other init routines. */ result = win32_init(); if(result) { @@ -303,9 +260,6 @@ int main(int argc, char *argv[]) } #endif - /* Perform any platform-specific terminal configuration */ - configure_terminal(); - main_checkfds(); #if defined(HAVE_SIGNAL) && defined(SIGPIPE) @@ -326,9 +280,6 @@ int main(int argc, char *argv[]) main_free(&global); } - /* Return the terminal to its original state */ - restore_terminal(); - #ifdef __NOVELL_LIBC__ if(getenv("_IN_NETWARE_BASH_") == NULL) tool_pressanykey();