[PATCH v4] Cygwin: console: Fix undesired mode change at exit of non-cygwin apps
Takashi Yano <[email protected]> Thu, 16 Jul 2026 16:36:18 +0900
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <[email protected]> |
Previously, if two non-cygwin apps are started and one of them
exits first, the other one loosed appropriate console mode, since
the first one restored it to tty::cygwin. This patch counts the
active console process whose pgid is pgid of the tty and if the
result is zero (means the last non-cygwin foreground process),
restore console mode. To avoid race issue between apps modifying
console mode simultaneously, this patch also introduce a mutex
named `cons_mode_mutex`.
Fixes: 48285aa36c2c ("Cygwin: console: Fix handling of Ctrl-S in Win7.")
Signed-off-by: Takashi Yano <[email protected]>
Reviewed-by: Johannes Schindelin <[email protected]>
---
v2: Stop counting up/down the counter by itself.
Use num_active_non_cygwin_apps() instead.
v3: Guard setup_for_non_cygwin_app() by cons_mode_mutex as well.
v4: Guard all mode changes in console by cons_mode_mutex.
winsup/cygwin/fhandler/console.cc | 87 ++++++++++++++++++++++++-
winsup/cygwin/local_includes/fhandler.h | 2 +
2 files changed, 86 insertions(+), 3 deletions(-)
diff --git a/winsup/cygwin/fhandler/console.cc b/winsup/cygwin/fhandler/console.cc
index d4c87f29f..5b9a87ebd 100644
--- a/winsup/cygwin/fhandler/console.cc
+++ b/winsup/cygwin/fhandler/console.cc
@@ -977,15 +977,59 @@ fhandler_console::setup_for_non_cygwin_app ()
console mode. */
if (get_ttyp ()->getpgid () == myself->pgid)
{
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
set_disable_master_thread (true, this);
set_input_mode (tty::native, &tc ()->ti, get_handle_set ());
set_output_mode (tty::native, &tc ()->ti, get_handle_set ());
+ ReleaseMutex (cons_mode_mutex);
}
}
+static int
+num_active_non_cygwin_apps (pid_t pgid)
+{
+ tmp_pathbuf tp;
+ DWORD *list = (DWORD *) tp.c_get ();
+ const DWORD buf_size = NT_MAX_PATH / sizeof (DWORD);
+
+ DWORD buf_size1 = 1;
+ DWORD num;
+ /* The buffer of too large size does not seem to be expected by new condrv.
+ https://github.com/microsoft/terminal/issues/18264#issuecomment-2515448548
+ Use the minimum buffer size in the loop. */
+ while ((num = GetConsoleProcessList (list, buf_size1)) > buf_size1)
+ {
+ if (num > buf_size)
+ return 0;
+ buf_size1 = num;
+ }
+ if (num == 0)
+ return 0;
+
+ int cnt = 0;
+ for (DWORD i = 0; i < num; i++)
+ {
+ pinfo p (cygwin_pid (list[i]));
+ if (!!p && p->pgid == pgid && ISSTATE (p, PID_NOTCYGWIN))
+ cnt++;
+ }
+ return cnt;
+}
+
void
fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p)
{
+ if (cygheap->ctty->tc()->pgid != myself->pgid)
+ return;
+
+ WaitForSingleObject (p->cons_mode_mutex, INFINITE);
+ if (num_active_non_cygwin_apps (cygheap->ctty->tc()->pgid))
+ {
+ ReleaseMutex (p->cons_mode_mutex);
+ CloseHandle (p->cons_mode_mutex);
+ return;
+ }
+
const _minor_t unit = p->unit;
termios dummy = {0, };
termios *ti = shared_console_info[unit] ?
@@ -999,6 +1043,7 @@ fhandler_console::cleanup_for_non_cygwin_app (handle_set_t *p)
set_output_mode (conmode, ti, p);
if (con.curr_input_mode != conmode)
set_input_mode (conmode, ti, p);
+ ReleaseMutex (p->cons_mode_mutex);
}
/* Return the tty structure associated with a given tty number. If the
@@ -1055,6 +1100,10 @@ fhandler_console::setup_io_mutex (void)
if (res == WAIT_OBJECT_0)
release_output_mutex ();
+ shared_name (buf, "cygcons.cons_mode.mutex", get_minor ());
+ if (!cons_mode_mutex)
+ cons_mode_mutex = CreateMutex (&sec_none, FALSE, buf);
+
extern HANDLE attach_mutex;
if (!attach_mutex)
attach_mutex = CreateMutex (&sec_none_nih, FALSE, NULL);
@@ -1189,6 +1238,7 @@ fhandler_console::bg_check (int sig, bool dontsignal)
/* Setting-up console mode for cygwin app. This is necessary if the
cygwin app and other non-cygwin apps are started simultaneously
in the same process group. */
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
if (sig == SIGTTIN && con.curr_input_mode != tty::cygwin)
{
set_disable_master_thread (false, this);
@@ -1196,6 +1246,7 @@ fhandler_console::bg_check (int sig, bool dontsignal)
}
if (sig == SIGTTOU && con.curr_output_mode != tty::cygwin)
set_output_mode (tty::cygwin, &tc ()->ti, get_handle_set ());
+ ReleaseMutex (cons_mode_mutex);
return fhandler_termios::bg_check (sig, dontsignal);
}
@@ -2010,6 +2061,7 @@ fhandler_console::open (int flags, mode_t)
if (in_is_console)
CloseHandle (h_in);
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
if (in_is_console && con.curr_input_mode != tty::cygwin)
{
prev_input_mode_backup = con.prev_input_mode;
@@ -2022,6 +2074,7 @@ fhandler_console::open (int flags, mode_t)
GetConsoleMode (get_output_handle (), &con.prev_output_mode);
set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set);
}
+ ReleaseMutex (cons_mode_mutex);
debug_printf ("opened conin$ %p, conout$ %p", get_handle (),
get_output_handle ());
@@ -2105,6 +2158,7 @@ fhandler_console::open_setup (int flags)
handle_set.output_handle = get_output_handle ();
handle_set.input_mutex = input_mutex;
handle_set.output_mutex = output_mutex;
+ handle_set.cons_mode_mutex = cons_mode_mutex;
handle_set.unit = unit;
}
return fhandler_base::open_setup (flags);
@@ -2114,6 +2168,7 @@ void
fhandler_console::post_open_setup (int fd)
{
/* Setting-up console mode for cygwin app started from non-cygwin app. */
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
if (fd == 0)
{
set_disable_master_thread (false, this);
@@ -2121,6 +2176,7 @@ fhandler_console::post_open_setup (int fd)
}
else if (fd == 1 || fd == 2)
set_output_mode (tty::cygwin, &get_ttyp ()->ti, &handle_set);
+ ReleaseMutex (cons_mode_mutex);
fhandler_base::post_open_setup (fd);
}
@@ -2135,11 +2191,13 @@ fhandler_console::close (int flag)
if (shared_console_info[unit] && (dev_t) myself->ctty == get_device ()
&& cons_mode_on_close (&handle_set) == tty::restore)
{
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
set_disable_master_thread (true, this);
if (con.curr_output_mode != tty::restore)
set_output_mode (tty::restore, &get_ttyp ()->ti, &handle_set);
if (con.curr_input_mode != tty::restore)
set_input_mode (tty::restore, &get_ttyp ()->ti, &handle_set);
+ ReleaseMutex (cons_mode_mutex);
}
if (shared_console_info[unit] && con.owner == GetCurrentProcessId ())
@@ -2196,6 +2254,8 @@ fhandler_console::close (int flag)
input_mutex = NULL;
CloseHandle (output_mutex);
output_mutex = NULL;
+ CloseHandle (cons_mode_mutex);
+ cons_mode_mutex = NULL;
pcon_hand_over_proc ();
@@ -2369,10 +2429,12 @@ int
fhandler_console::tcsetattr (int a, struct termios const *t)
{
get_ttyp ()->ti = *t;
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
if (con.curr_input_mode == tty::cygwin)
set_input_mode (tty::cygwin, t, &handle_set);
if (con.curr_output_mode == tty::cygwin)
set_output_mode (tty::cygwin, t, &handle_set);
+ ReleaseMutex (cons_mode_mutex);
return 0;
}
@@ -3140,10 +3202,12 @@ fhandler_console::char_command (char c)
con.cursor_key_app_mode = (c == 'h');
if (con.args[i] == 9001) /* win32-input-mode (https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md) */
{
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
set_disable_master_thread (c == 'h', this);
if (con.curr_input_mode == tty::cygwin)
set_input_mode (tty::cygwin,
&tc ()->ti, get_handle_set ());
+ ReleaseMutex (cons_mode_mutex);
}
}
/* Call fix_tab_position() if screen has been alternated. */
@@ -4475,10 +4539,13 @@ fhandler_console::set_console_mode_to_native ()
fhandler_console *cons = (fhandler_console *) (fhandler_base *) cfd;
if (cons->get_device () == cons->tc ()->getntty ())
{
+ const fhandler_console::handle_set_t *p = cons->get_handle_set ();
+ WaitForSingleObject (p->cons_mode_mutex, INFINITE);
set_disable_master_thread (true, cons);
termios *cons_ti = &cons->tc ()->ti;
- set_input_mode (tty::native, cons_ti, cons->get_handle_set ());
- set_output_mode (tty::native, cons_ti, cons->get_handle_set ());
+ set_input_mode (tty::native, cons_ti, p);
+ set_output_mode (tty::native, cons_ti, p);
+ ReleaseMutex (p->cons_mode_mutex);
break;
}
}
@@ -4536,7 +4603,16 @@ static FARPROC
GetProcAddress_Hooked (HMODULE h, LPCSTR n)
{
if (strcmp(n, "RequestTermConnector") == 0)
- fhandler_console::set_disable_master_thread (true);
+ {
+ char buf[MAX_PATH];
+ const _minor_t unit = cygheap->ctty->get_minor ();
+ shared_name (buf, "cygcons.cons_mode.mutex", unit);
+ HANDLE cons_mode_mutex = CreateMutex (&sec_none, FALSE, buf);
+ WaitForSingleObject (cons_mode_mutex, INFINITE);
+ fhandler_console::set_disable_master_thread (true);
+ ReleaseMutex (cons_mode_mutex);
+ CloseHandle (cons_mode_mutex);
+ }
return GetProcAddress_Orig (h, n);
}
@@ -4817,6 +4893,9 @@ fhandler_console::get_duplicated_handle_set (handle_set_t *p)
DuplicateHandle (GetCurrentProcess (), output_mutex,
GetCurrentProcess (), &p->output_mutex,
0, FALSE, DUPLICATE_SAME_ACCESS);
+ DuplicateHandle (GetCurrentProcess (), cons_mode_mutex,
+ GetCurrentProcess (), &p->cons_mode_mutex,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
p->unit = unit;
}
@@ -4833,6 +4912,8 @@ fhandler_console::close_handle_set (handle_set_t *p)
p->input_mutex = NULL;
CloseHandle (p->output_mutex);
p->output_mutex = NULL;
+ CloseHandle (p->cons_mode_mutex);
+ p->cons_mode_mutex = NULL;
}
bool
diff --git a/winsup/cygwin/local_includes/fhandler.h b/winsup/cygwin/local_includes/fhandler.h
index d11b3ec4f..9c891863f 100644
--- a/winsup/cygwin/local_includes/fhandler.h
+++ b/winsup/cygwin/local_includes/fhandler.h
@@ -2023,6 +2023,7 @@ class fhandler_termios: public fhandler_base
HANDLE output_handle;
HANDLE input_mutex;
HANDLE output_mutex;
+ HANDLE cons_mode_mutex;
_minor_t unit;
};
class spawn_worker
@@ -2199,6 +2200,7 @@ private:
static console_state *shared_console_info[MAX_CONS_DEV + 1];
static bool invisible_console;
HANDLE input_mutex, output_mutex;
+ HANDLE cons_mode_mutex;
handle_set_t handle_set;
_minor_t unit;
size_t num_input_events_processed;
--
2.51.0