Cygwin: pty: Reorganize the code path of setting up and closing pcon.

- This patch reorganizes the code path of setting-up and cleaning-up
  of the pseudo console to improve readability and maintainability
  of pty code.
This commit is contained in:
Takashi Yano 2022-03-01 18:04:35 +09:00
parent fbfea31dd9
commit 1e6c51d741
3 changed files with 154 additions and 143 deletions

View File

@ -2389,8 +2389,10 @@ class fhandler_pty_slave: public fhandler_pty_common
fh->copy_from (this); fh->copy_from (this);
return fh; return fh;
} }
bool setup_pseudoconsole (bool nopcon); bool setup_pseudoconsole ();
static DWORD get_winpid_to_hand_over (tty *ttyp, DWORD force_switch_to);
static void close_pseudoconsole (tty *ttyp, DWORD force_switch_to = 0); static void close_pseudoconsole (tty *ttyp, DWORD force_switch_to = 0);
static void hand_over_only (tty *ttyp, DWORD force_switch_to = 0);
bool term_has_pcon_cap (const WCHAR *env); bool term_has_pcon_cap (const WCHAR *env);
void set_switch_to_pcon (void); void set_switch_to_pcon (void);
void reset_switch_to_pcon (void); void reset_switch_to_pcon (void);
@ -2407,10 +2409,10 @@ class fhandler_pty_slave: public fhandler_pty_common
void setup_for_non_cygwin_app (bool nopcon, PWCHAR envblock, void setup_for_non_cygwin_app (bool nopcon, PWCHAR envblock,
bool stdin_is_ptys); bool stdin_is_ptys);
static void cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp, static void cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp,
bool stdin_is_ptys); bool stdin_is_ptys,
DWORD force_switch_to = 0);
void setpgid_aux (pid_t pid); void setpgid_aux (pid_t pid);
static void close_pseudoconsole_if_necessary (tty *ttyp, static void release_ownership_of_nat_pipe (tty *ttyp, fhandler_termios *fh);
fhandler_termios *fh);
}; };
#define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))

View File

@ -397,7 +397,7 @@ fhandler_termios::process_sigs (char c, tty* ttyp, fhandler_termios *fh)
pseudo console because this process attached to it pseudo console because this process attached to it
before sending CTRL_C_EVENT. In this case, closing before sending CTRL_C_EVENT. In this case, closing
pseudo console is necessary. */ pseudo console is necessary. */
fhandler_pty_slave::close_pseudoconsole_if_necessary (ttyp, fh); fhandler_pty_slave::release_ownership_of_nat_pipe (ttyp, fh);
FreeConsole (); FreeConsole ();
if (resume_pid && console_exists) if (resume_pid && console_exists)
AttachConsole (resume_pid); AttachConsole (resume_pid);

View File

@ -236,20 +236,18 @@ atexit_func (void)
fhandler_base *fh = cfd; fhandler_base *fh = cfd;
fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
tty *ttyp = (tty *) ptys->tc (); tty *ttyp = (tty *) ptys->tc ();
HANDLE from = ptys->get_handle_nat (); bool stdin_is_ptys =
HANDLE input_available_event = ptys->get_input_available_event (); GetStdHandle (STD_INPUT_HANDLE) == ptys->get_handle ();
if (ttyp->getpgid () == myself->pgid struct fhandler_pty_slave::handle_set_t handles =
&& GetStdHandle (STD_INPUT_HANDLE) == ptys->get_handle ()
&& ttyp->pcon_input_state_eq (tty::to_nat) && !force_switch_to)
{ {
WaitForSingleObject (ptys->input_mutex, mutex_timeout); ptys->get_handle_nat (),
fhandler_pty_slave::transfer_input (tty::to_cyg, from, ttyp, ptys->get_input_available_event (),
input_available_event); ptys->input_mutex,
ReleaseMutex (ptys->input_mutex); ptys->pcon_mutex
} };
WaitForSingleObject (ptys->pcon_mutex, INFINITE); fhandler_pty_slave::cleanup_for_non_cygwin_app (&handles, ttyp,
ptys->close_pseudoconsole (ttyp, force_switch_to); stdin_is_ptys,
ReleaseMutex (ptys->pcon_mutex); force_switch_to);
break; break;
} }
CloseHandle (h_gdb_process); CloseHandle (h_gdb_process);
@ -1089,19 +1087,8 @@ fhandler_pty_slave::set_switch_to_pcon (void)
setup_locale (); setup_locale ();
myself->exec_dwProcessId = myself->dwProcessId; myself->exec_dwProcessId = myself->dwProcessId;
myself->process_state |= PID_NEW_PG; /* Marker for pcon_fg */ myself->process_state |= PID_NEW_PG; /* Marker for pcon_fg */
bool nopcon = (disable_pcon || !term_has_pcon_cap (NULL)); bool stdin_is_ptys = GetStdHandle (STD_INPUT_HANDLE) == get_handle ();
WaitForSingleObject (pcon_mutex, INFINITE); setup_for_non_cygwin_app (false, NULL, stdin_is_ptys);
bool pcon_enabled = setup_pseudoconsole (nopcon);
ReleaseMutex (pcon_mutex);
if (!pcon_enabled && get_ttyp ()->getpgid () == myself->pgid
&& GetStdHandle (STD_INPUT_HANDLE) == get_handle ()
&& get_ttyp ()->pcon_input_state_eq (tty::to_cyg))
{
WaitForSingleObject (input_mutex, mutex_timeout);
transfer_input (tty::to_nat, get_handle (), get_ttyp (),
input_available_event);
ReleaseMutex (input_mutex);
}
} }
} }
@ -1161,7 +1148,10 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
return; return;
bool need_restore_handles = get_ttyp ()->pcon_activated; bool need_restore_handles = get_ttyp ()->pcon_activated;
WaitForSingleObject (pcon_mutex, INFINITE); WaitForSingleObject (pcon_mutex, INFINITE);
close_pseudoconsole (get_ttyp ()); if (get_ttyp ()->pcon_activated)
close_pseudoconsole (get_ttyp ());
else
hand_over_only (get_ttyp ());
ReleaseMutex (pcon_mutex); ReleaseMutex (pcon_mutex);
if (need_restore_handles) if (need_restore_handles)
{ {
@ -3218,22 +3208,8 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
Slave process will attach to the pseudo console in the Slave process will attach to the pseudo console in the
helper process using AttachConsole(). */ helper process using AttachConsole(). */
bool bool
fhandler_pty_slave::setup_pseudoconsole (bool nopcon) fhandler_pty_slave::setup_pseudoconsole ()
{ {
/* Setting switch_to_pcon_in is necessary even if
pseudo console will not be activated. */
fhandler_base *fh = ::cygheap->fdtab[0];
if (fh && fh->get_major () == DEV_PTYS_MAJOR)
{
fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
ptys->get_ttyp ()->switch_to_pcon_in = true;
if (!pcon_pid_alive (ptys->get_ttyp ()->pcon_pid))
ptys->get_ttyp ()->pcon_pid = myself->exec_dwProcessId;
}
if (nopcon)
return false;
/* If the legacy console mode is enabled, pseudo console seems /* If the legacy console mode is enabled, pseudo console seems
not to work as expected. To determine console mode, registry not to work as expected. To determine console mode, registry
key ForceV2 in HKEY_CURRENT_USER\Console is checked. */ key ForceV2 in HKEY_CURRENT_USER\Console is checked. */
@ -3249,7 +3225,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
HANDLE hpConIn, hpConOut; HANDLE hpConIn, hpConOut;
if (get_ttyp ()->pcon_activated) if (get_ttyp ()->pcon_activated)
{ { /* The pseudo console is already activated. */
if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ()) if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ())
{ /* Send CSI6n just for requesting transfer input. */ { /* Send CSI6n just for requesting transfer input. */
DWORD n; DWORD n;
@ -3287,7 +3263,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
HPCON hpcon; HPCON hpcon;
do do
{ { /* Create new pseudo console */
COORD size = { COORD size = {
(SHORT) get_ttyp ()->winsize.ws_col, (SHORT) get_ttyp ()->winsize.ws_col,
(SHORT) get_ttyp ()->winsize.ws_row (SHORT) get_ttyp ()->winsize.ws_row
@ -3519,10 +3495,10 @@ fallback:
return false; return false;
} }
/* The function close_pseudoconsole() should be static so that it can /* Find a process to which the ownership of nat pipe should be handed over */
be called even after the fhandler_pty_slave instance is deleted. */ DWORD
void fhandler_pty_slave::get_winpid_to_hand_over (tty *ttyp,
fhandler_pty_slave::close_pseudoconsole (tty *ttyp, DWORD force_switch_to) DWORD force_switch_to)
{ {
DWORD switch_to = 0; DWORD switch_to = 0;
if (force_switch_to) if (force_switch_to)
@ -3532,106 +3508,120 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp, DWORD force_switch_to)
} }
else if (pcon_pid_self (ttyp->pcon_pid)) else if (pcon_pid_self (ttyp->pcon_pid))
{ {
/* Search another process which attaches to the pseudo console */ /* Search another native process which attaches to the same console */
DWORD current_pid = myself->exec_dwProcessId ?: myself->dwProcessId; DWORD current_pid = myself->exec_dwProcessId ?: myself->dwProcessId;
switch_to = get_console_process_id (current_pid, false, true, true); switch_to = get_console_process_id (current_pid, false, true, true);
if (!switch_to) if (!switch_to)
switch_to = get_console_process_id (current_pid, false, true, false); switch_to = get_console_process_id (current_pid, false, true, false);
} }
if (ttyp->pcon_activated) return switch_to;
}
void
fhandler_pty_slave::hand_over_only (tty *ttyp, DWORD force_switch_to)
{
if (pcon_pid_self (ttyp->pcon_pid))
{ {
ttyp->previous_code_page = GetConsoleCP (); DWORD switch_to = get_winpid_to_hand_over (ttyp, force_switch_to);
ttyp->previous_output_code_page = GetConsoleOutputCP (); if (switch_to)
if (pcon_pid_self (ttyp->pcon_pid)) /* The process switch_to takes over the ownership of the nat pipe. */
{ ttyp->pcon_pid = switch_to;
if (switch_to)
{
/* Change pseudo console owner to another process */
HANDLE new_owner =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, switch_to);
HANDLE new_write_pipe = NULL;
HANDLE new_condrv_reference = NULL;
HANDLE new_conhost_process = NULL;
HANDLE new_pcon_in = NULL, new_pcon_out = NULL;
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_write_pipe,
new_owner, &new_write_pipe,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_condrv_reference,
new_owner, &new_condrv_reference,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_conhost_process,
new_owner, &new_conhost_process,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_in,
new_owner, &new_pcon_in,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_out,
new_owner, &new_pcon_out,
0, TRUE, DUPLICATE_SAME_ACCESS);
CloseHandle (new_owner);
CloseHandle (ttyp->h_pcon_write_pipe);
CloseHandle (ttyp->h_pcon_condrv_reference);
CloseHandle (ttyp->h_pcon_conhost_process);
CloseHandle (ttyp->h_pcon_in);
CloseHandle (ttyp->h_pcon_out);
ttyp->pcon_pid = switch_to;
ttyp->h_pcon_write_pipe = new_write_pipe;
ttyp->h_pcon_condrv_reference = new_condrv_reference;
ttyp->h_pcon_conhost_process = new_conhost_process;
ttyp->h_pcon_in = new_pcon_in;
ttyp->h_pcon_out = new_pcon_out;
FreeConsole ();
pinfo p (myself->ppid);
if (!p || !AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
init_console_handler (false);
}
else
{ /* Close pseudo console */
FreeConsole ();
pinfo p (myself->ppid);
if (!p || !AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
init_console_handler (false);
/* Reconstruct pseudo console handler container here for close */
HPCON_INTERNAL *hp =
(HPCON_INTERNAL *) HeapAlloc (GetProcessHeap (), 0,
sizeof (HPCON_INTERNAL));
hp->hWritePipe = ttyp->h_pcon_write_pipe;
hp->hConDrvReference = ttyp->h_pcon_condrv_reference;
hp->hConHostProcess = ttyp->h_pcon_conhost_process;
/* HeapFree() will be called in ClosePseudoConsole() */
ClosePseudoConsole ((HPCON) hp);
CloseHandle (ttyp->h_pcon_conhost_process);
ttyp->pcon_activated = false;
ttyp->switch_to_pcon_in = false;
ttyp->pcon_pid = 0;
ttyp->pcon_start = false;
ttyp->pcon_start_pid = 0;
}
}
else else
{ {
/* Abandon the ownership of the nat pipe */
ttyp->pcon_pid = 0;
ttyp->switch_to_pcon_in = false;
}
}
}
/* The function close_pseudoconsole() should be static so that it can
be called even after the fhandler_pty_slave instance is deleted. */
void
fhandler_pty_slave::close_pseudoconsole (tty *ttyp, DWORD force_switch_to)
{
DWORD switch_to = get_winpid_to_hand_over (ttyp, force_switch_to);
ttyp->previous_code_page = GetConsoleCP ();
ttyp->previous_output_code_page = GetConsoleOutputCP ();
if (pcon_pid_self (ttyp->pcon_pid))
{ /* I am owner of the nat pipe. */
if (switch_to)
{
/* Change pseudo console owner to another process (switch_to). */
HANDLE new_owner =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, switch_to);
HANDLE new_write_pipe = NULL;
HANDLE new_condrv_reference = NULL;
HANDLE new_conhost_process = NULL;
HANDLE new_pcon_in = NULL, new_pcon_out = NULL;
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_write_pipe,
new_owner, &new_write_pipe,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_condrv_reference,
new_owner, &new_condrv_reference,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (),
ttyp->h_pcon_conhost_process,
new_owner, &new_conhost_process,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_in,
new_owner, &new_pcon_in,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_out,
new_owner, &new_pcon_out,
0, TRUE, DUPLICATE_SAME_ACCESS);
CloseHandle (new_owner);
CloseHandle (ttyp->h_pcon_write_pipe);
CloseHandle (ttyp->h_pcon_condrv_reference);
CloseHandle (ttyp->h_pcon_conhost_process);
CloseHandle (ttyp->h_pcon_in);
CloseHandle (ttyp->h_pcon_out);
ttyp->pcon_pid = switch_to;
ttyp->h_pcon_write_pipe = new_write_pipe;
ttyp->h_pcon_condrv_reference = new_condrv_reference;
ttyp->h_pcon_conhost_process = new_conhost_process;
ttyp->h_pcon_in = new_pcon_in;
ttyp->h_pcon_out = new_pcon_out;
FreeConsole (); FreeConsole ();
pinfo p (myself->ppid); pinfo p (myself->ppid);
if (!p || !AttachConsole (p->dwProcessId)) if (!p || !AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS); AttachConsole (ATTACH_PARENT_PROCESS);
init_console_handler (false); init_console_handler (false);
} }
}
else if (pcon_pid_self (ttyp->pcon_pid))
{
if (switch_to)
ttyp->pcon_pid = switch_to;
else else
{ { /* Close pseudo console and abandon the ownership of the nat pipe. */
ttyp->pcon_pid = 0; FreeConsole ();
pinfo p (myself->ppid);
if (!p || !AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
init_console_handler (false);
/* Reconstruct pseudo console handler container here for close */
HPCON_INTERNAL *hp =
(HPCON_INTERNAL *) HeapAlloc (GetProcessHeap (), 0,
sizeof (HPCON_INTERNAL));
hp->hWritePipe = ttyp->h_pcon_write_pipe;
hp->hConDrvReference = ttyp->h_pcon_condrv_reference;
hp->hConHostProcess = ttyp->h_pcon_conhost_process;
/* HeapFree() will be called in ClosePseudoConsole() */
ClosePseudoConsole ((HPCON) hp);
CloseHandle (ttyp->h_pcon_conhost_process);
ttyp->pcon_activated = false;
ttyp->switch_to_pcon_in = false; ttyp->switch_to_pcon_in = false;
ttyp->pcon_pid = 0;
ttyp->pcon_start = false;
ttyp->pcon_start_pid = 0;
} }
} }
else
{ /* Just detach from the pseudo console if I am not owner. */
FreeConsole ();
pinfo p (myself->ppid);
if (!p || !AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
init_console_handler (false);
}
} }
static bool static bool
@ -4032,10 +4022,22 @@ fhandler_pty_slave::setup_for_non_cygwin_app (bool nopcon, PWCHAR envblock,
if (disable_pcon || !term_has_pcon_cap (envblock)) if (disable_pcon || !term_has_pcon_cap (envblock))
nopcon = true; nopcon = true;
WaitForSingleObject (pcon_mutex, INFINITE); WaitForSingleObject (pcon_mutex, INFINITE);
bool enable_pcon = setup_pseudoconsole (nopcon); /* Setting switch_to_pcon_in is necessary even if pseudo console
will not be activated. */
fhandler_base *fh = ::cygheap->fdtab[0];
if (fh && fh->get_major () == DEV_PTYS_MAJOR)
{
fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
ptys->get_ttyp ()->switch_to_pcon_in = true;
if (!pcon_pid_alive (ptys->get_ttyp ()->pcon_pid))
ptys->get_ttyp ()->pcon_pid = myself->exec_dwProcessId;
}
bool pcon_enabled = false;
if (!nopcon)
pcon_enabled = setup_pseudoconsole ();
ReleaseMutex (pcon_mutex); ReleaseMutex (pcon_mutex);
/* For pcon enabled case, transfer_input() is called in master::write() */ /* For pcon enabled case, transfer_input() is called in master::write() */
if (!enable_pcon && get_ttyp ()->getpgid () == myself->pgid if (!pcon_enabled && get_ttyp ()->getpgid () == myself->pgid
&& stdin_is_ptys && get_ttyp ()->pcon_input_state_eq (tty::to_cyg)) && stdin_is_ptys && get_ttyp ()->pcon_input_state_eq (tty::to_cyg))
{ {
WaitForSingleObject (input_mutex, mutex_timeout); WaitForSingleObject (input_mutex, mutex_timeout);
@ -4047,7 +4049,8 @@ fhandler_pty_slave::setup_for_non_cygwin_app (bool nopcon, PWCHAR envblock,
void void
fhandler_pty_slave::cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp, fhandler_pty_slave::cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp,
bool stdin_is_ptys) bool stdin_is_ptys,
DWORD force_switch_to)
{ {
ttyp->wait_pcon_fwd (); ttyp->wait_pcon_fwd ();
if (ttyp->getpgid () == myself->pgid && stdin_is_ptys if (ttyp->getpgid () == myself->pgid && stdin_is_ptys
@ -4059,7 +4062,10 @@ fhandler_pty_slave::cleanup_for_non_cygwin_app (handle_set_t *p, tty *ttyp,
ReleaseMutex (p->input_mutex); ReleaseMutex (p->input_mutex);
} }
WaitForSingleObject (p->pcon_mutex, INFINITE); WaitForSingleObject (p->pcon_mutex, INFINITE);
close_pseudoconsole (ttyp); if (ttyp->pcon_activated)
close_pseudoconsole (ttyp, force_switch_to);
else
hand_over_only (ttyp, force_switch_to);
ReleaseMutex (p->pcon_mutex); ReleaseMutex (p->pcon_mutex);
} }
@ -4123,14 +4129,17 @@ fhandler_pty_master::need_send_ctrl_c_event ()
} }
void void
fhandler_pty_slave::close_pseudoconsole_if_necessary (tty *ttyp, fhandler_pty_slave::release_ownership_of_nat_pipe (tty *ttyp,
fhandler_termios *fh) fhandler_termios *fh)
{ {
if (fh->get_major () == DEV_PTYM_MAJOR && ttyp->pcon_activated) if (fh->get_major () == DEV_PTYM_MAJOR)
{ {
fhandler_pty_master *ptym = (fhandler_pty_master *) fh; fhandler_pty_master *ptym = (fhandler_pty_master *) fh;
WaitForSingleObject (ptym->pcon_mutex, INFINITE); WaitForSingleObject (ptym->pcon_mutex, INFINITE);
close_pseudoconsole (ttyp); if (ttyp->pcon_activated)
close_pseudoconsole (ttyp);
else
hand_over_only (ttyp);
ReleaseMutex (ptym->pcon_mutex); ReleaseMutex (ptym->pcon_mutex);
} }
} }