Cygwin: pty: Allow multiple apps to enable pseudo console simultaneously.

- After commit bb428520, there has been the disadvantage:
  7) Pseudo console cannot be activated if it is already activated for
     another process on same pty.
  This patch clears this disadvantage.
This commit is contained in:
Takashi Yano via Cygwin-patches 2021-01-28 12:26:14 +09:00 committed by Corinna Vinschen
parent 8aeb3f3e50
commit 253352e796
6 changed files with 314 additions and 93 deletions

View file

@ -2273,6 +2273,9 @@ class fhandler_pty_common: public fhandler_termios
} }
void resize_pseudo_console (struct winsize *); void resize_pseudo_console (struct winsize *);
static DWORD get_console_process_id (DWORD pid, bool match,
bool cygwin = false,
bool stub_only = false);
protected: protected:
static BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len, static BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len,
@ -2354,14 +2357,14 @@ class fhandler_pty_slave: public fhandler_pty_common
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);
void mask_switch_to_pcon_in (bool mask); void mask_switch_to_pcon_in (bool mask, bool xfer);
void setup_locale (void); void setup_locale (void);
tty *get_ttyp () { return (tty *) tc (); } /* Override as public */ tty *get_ttyp () { return (tty *) tc (); } /* Override as public */
void create_invisible_console (void); void create_invisible_console (void);
static void transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, static void transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
_minor_t unit, HANDLE input_available_event); _minor_t unit, HANDLE input_available_event);
HANDLE get_input_available_event (void) { return input_available_event; } HANDLE get_input_available_event (void) { return input_available_event; }
bool pcon_activated (void) { return get_ttyp ()->h_pseudo_console; } bool pcon_activated (void) { return get_ttyp ()->pcon_activated; }
}; };
#define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))

View file

@ -64,8 +64,9 @@ struct pipe_reply {
extern HANDLE attach_mutex; /* Defined in fhandler_console.cc */ extern HANDLE attach_mutex; /* Defined in fhandler_console.cc */
static DWORD DWORD
get_console_process_id (DWORD pid, bool match) fhandler_pty_common::get_console_process_id (DWORD pid, bool match,
bool cygwin, bool stub_only)
{ {
tmp_pathbuf tp; tmp_pathbuf tp;
DWORD *list = (DWORD *) tp.c_get (); DWORD *list = (DWORD *) tp.c_get ();
@ -75,16 +76,34 @@ get_console_process_id (DWORD pid, bool match)
if (num == 0 || num > buf_size) if (num == 0 || num > buf_size)
return 0; return 0;
DWORD res = 0; DWORD res_pri = 0, res = 0;
/* Last one is the oldest. */ /* Last one is the oldest. */
/* https://github.com/microsoft/terminal/issues/95 */ /* https://github.com/microsoft/terminal/issues/95 */
for (int i = (int) num - 1; i >= 0; i--) for (int i = (int) num - 1; i >= 0; i--)
if ((match && list[i] == pid) || (!match && list[i] != pid)) if ((match && list[i] == pid) || (!match && list[i] != pid))
{ {
res = list[i]; if (!cygwin)
{
res_pri = list[i];
break; break;
} }
return res; else
{
pinfo p (cygwin_pid (list[i]));
if (!!p && p->dwProcessId && p->exec_dwProcessId
&& p->dwProcessId != p->exec_dwProcessId)
{
res_pri = list[i];
break;
}
if (!!p && !res)
res = list[i];
}
}
if (stub_only)
return res_pri;
else
return res_pri ?: res;
} }
static bool isHybrid; static bool isHybrid;
@ -937,7 +956,7 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
void void
fhandler_pty_slave::set_switch_to_pcon (void) fhandler_pty_slave::set_switch_to_pcon (void)
{ {
if (!get_ttyp ()->switch_to_pcon_in) if (!isHybrid)
{ {
isHybrid = true; isHybrid = true;
setup_locale (); setup_locale ();
@ -964,9 +983,10 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
{ {
CloseHandle (h_gdb_process); CloseHandle (h_gdb_process);
h_gdb_process = NULL; h_gdb_process = NULL;
if (isHybrid && get_ttyp ()->pcon_pid == myself->pid) if (isHybrid)
{ {
if (get_ttyp ()->switch_to_pcon_in) if (get_ttyp ()->switch_to_pcon_in
&& get_ttyp ()->pcon_pid == myself->pid)
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
get_handle (), get_handle (),
get_ttyp (), get_minor (), get_ttyp (), get_minor (),
@ -976,7 +996,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
the console of the parent process will fail. the console of the parent process will fail.
Therefore, never close pseudo console here. */ Therefore, never close pseudo console here. */
return; return;
bool need_restore_handles = !!get_ttyp ()->h_pseudo_console; bool need_restore_handles = get_ttyp ()->pcon_activated;
close_pseudoconsole (get_ttyp ()); close_pseudoconsole (get_ttyp ());
if (need_restore_handles) if (need_restore_handles)
{ {
@ -1027,11 +1047,8 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
if (fix_err) if (fix_err)
SetStdHandle (STD_ERROR_HANDLE, get_output_handle ()); SetStdHandle (STD_ERROR_HANDLE, get_output_handle ());
} }
get_ttyp ()->pcon_pid = 0;
get_ttyp ()->switch_to_pcon_in = false;
}
isHybrid = false; isHybrid = false;
return; }
} }
} }
if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid
@ -1040,14 +1057,14 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
return; return;
if (isHybrid) if (isHybrid)
return; return;
if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->h_pseudo_console) if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->pcon_activated)
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
get_handle (), get_handle (),
get_ttyp (), get_minor (), get_ttyp (), get_minor (),
input_available_event); input_available_event);
get_ttyp ()->pcon_pid = 0; get_ttyp ()->pcon_pid = 0;
get_ttyp ()->switch_to_pcon_in = false; get_ttyp ()->switch_to_pcon_in = false;
get_ttyp ()->h_pseudo_console = NULL; get_ttyp ()->pcon_activated = false;
} }
ssize_t __stdcall ssize_t __stdcall
@ -1087,7 +1104,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
} }
void void
fhandler_pty_slave::mask_switch_to_pcon_in (bool mask) fhandler_pty_slave::mask_switch_to_pcon_in (bool mask, bool xfer)
{ {
char name[MAX_PATH]; char name[MAX_PATH];
shared_name (name, TTY_SLAVE_READING, get_minor ()); shared_name (name, TTY_SLAVE_READING, get_minor ());
@ -1102,22 +1119,64 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
else if (InterlockedDecrement (&num_reader) == 0) else if (InterlockedDecrement (&num_reader) == 0)
CloseHandle (slave_reading); CloseHandle (slave_reading);
if (get_ttyp ()->switch_to_pcon_in if (get_ttyp ()->switch_to_pcon_in && !!masked != mask && xfer)
&& (get_ttyp ()->pcon_pid == myself->pid { /* Transfer input */
|| !get_ttyp ()->h_pseudo_console) bool attach_restore = false;
&& !!masked != mask) DWORD pcon_winpid = 0;
if (get_ttyp ()->pcon_pid)
{ {
pinfo p (get_ttyp ()->pcon_pid);
if (p)
pcon_winpid = p->exec_dwProcessId ?: p->dwProcessId;
}
if (mask) if (mask)
{
HANDLE from = get_handle ();
if (get_ttyp ()->pcon_activated && pcon_winpid
&& !get_console_process_id (pcon_winpid, true))
{
HANDLE pcon_owner =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, pcon_winpid);
DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
GetCurrentProcess (), &from,
0, TRUE, DUPLICATE_SAME_ACCESS);
CloseHandle (pcon_owner);
FreeConsole ();
AttachConsole (pcon_winpid);
attach_restore = true;
}
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
get_handle (), from,
get_ttyp (), get_minor (), get_ttyp (), get_minor (),
input_available_event); input_available_event);
}
else else
{
if (get_ttyp ()->pcon_activated && pcon_winpid
&& !get_console_process_id (pcon_winpid, true))
{
FreeConsole ();
AttachConsole (pcon_winpid);
attach_restore = true;
}
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
get_handle_cyg (), get_handle_cyg (),
get_ttyp (), get_minor (), get_ttyp (), get_minor (),
input_available_event); input_available_event);
} }
if (attach_restore)
{
FreeConsole ();
pinfo p (myself->ppid);
if (p)
{
if (!AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
}
else
AttachConsole (ATTACH_PARENT_PROCESS);
}
}
return; return;
} }
@ -1143,6 +1202,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
DWORD bytes_in_pipe; DWORD bytes_in_pipe;
char buf[INP_BUFFER_SIZE]; char buf[INP_BUFFER_SIZE];
DWORD time_to_wait; DWORD time_to_wait;
char *ptr0 = (char *) ptr;
bg_check_types bg = bg_check (SIGTTIN); bg_check_types bg = bg_check (SIGTTIN);
if (bg <= bg_eof) if (bg <= bg_eof)
@ -1157,7 +1217,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
if (ptr) /* Indicating not tcflush(). */ if (ptr) /* Indicating not tcflush(). */
{ {
mask_switch_to_pcon_in (true); mask_switch_to_pcon_in (true, true);
reset_switch_to_pcon (); reset_switch_to_pcon ();
} }
@ -1271,7 +1331,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
if (ptr && !bytes_in_pipe && !vmin && !time_to_wait) if (ptr && !bytes_in_pipe && !vmin && !time_to_wait)
{ {
ReleaseMutex (input_mutex); ReleaseMutex (input_mutex);
mask_switch_to_pcon_in (false); mask_switch_to_pcon_in (false, false);
len = (size_t) bytes_in_pipe; len = (size_t) bytes_in_pipe;
return; return;
} }
@ -1376,7 +1436,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
out: out:
termios_printf ("%d = read(%p, %lu)", totalread, ptr, len); termios_printf ("%d = read(%p, %lu)", totalread, ptr, len);
len = (size_t) totalread; len = (size_t) totalread;
mask_switch_to_pcon_in (false); mask_switch_to_pcon_in (false, totalread > 0 && ptr0[totalread - 1] == '\n');
} }
int int
@ -1417,7 +1477,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
{ {
if (get_ttyp ()->pcon_start) if (get_ttyp ()->pcon_start)
t->c_lflag &= ~(ICANON | ECHO); t->c_lflag &= ~(ICANON | ECHO);
if (get_ttyp ()->h_pseudo_console) if (get_ttyp ()->pcon_activated)
t->c_iflag &= ~ICRNL; t->c_iflag &= ~ICRNL;
break; break;
} }
@ -1532,7 +1592,7 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
|| get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
{ {
if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid) if (get_ttyp ()->pcon_activated && get_ttyp ()->pcon_pid)
resize_pseudo_console ((struct winsize *) arg); resize_pseudo_console ((struct winsize *) arg);
get_ttyp ()->arg.winsize = *(struct winsize *) arg; get_ttyp ()->arg.winsize = *(struct winsize *) arg;
get_ttyp ()->winsize = *(struct winsize *) arg; get_ttyp ()->winsize = *(struct winsize *) arg;
@ -1947,7 +2007,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
/* Write terminal input to to_slave pipe instead of output_handle /* Write terminal input to to_slave pipe instead of output_handle
if current application is native console application. */ if current application is native console application. */
if (to_be_read_from_pcon () && get_ttyp ()->h_pseudo_console) if (to_be_read_from_pcon () && get_ttyp ()->pcon_activated)
{ {
tmp_pathbuf tp; tmp_pathbuf tp;
char *buf = (char *) ptr; char *buf = (char *) ptr;
@ -2042,7 +2102,7 @@ fhandler_pty_master::tcgetattr (struct termios *t)
/* Workaround for rlwrap v0.40 or later */ /* Workaround for rlwrap v0.40 or later */
if (get_ttyp ()->pcon_start) if (get_ttyp ()->pcon_start)
t->c_lflag &= ~(ICANON | ECHO); t->c_lflag &= ~(ICANON | ECHO);
if (get_ttyp ()->h_pseudo_console) if (get_ttyp ()->pcon_activated)
t->c_iflag &= ~ICRNL; t->c_iflag &= ~ICRNL;
return 0; return 0;
} }
@ -2091,7 +2151,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
|| get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
{ {
if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid) if (get_ttyp ()->pcon_activated && get_ttyp ()->pcon_pid)
resize_pseudo_console ((struct winsize *) arg); resize_pseudo_console ((struct winsize *) arg);
get_ttyp ()->winsize = *(struct winsize *) arg; get_ttyp ()->winsize = *(struct winsize *) arg;
get_ttyp ()->kill_pgrp (SIGWINCH); get_ttyp ()->kill_pgrp (SIGWINCH);
@ -2385,7 +2445,7 @@ fhandler_pty_master::pty_master_fwd_thread (const master_fwd_thread_param_t *p)
} }
ssize_t wlen = rlen; ssize_t wlen = rlen;
char *ptr = outbuf; char *ptr = outbuf;
if (p->ttyp->h_pseudo_console) if (p->ttyp->pcon_activated)
{ {
/* Avoid setting window title to "cygwin-console-helper.exe" */ /* Avoid setting window title to "cygwin-console-helper.exe" */
int state = 0; int state = 0;
@ -2899,9 +2959,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
if (nopcon) if (nopcon)
return false; return false;
if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid
&& !!pinfo (get_ttyp ()->pcon_pid))
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. */
@ -2915,11 +2973,32 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
return false; return false;
} }
HANDLE hpConIn, hpConOut;
acquire_output_mutex (INFINITE);
if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid
&& !!pinfo (get_ttyp ()->pcon_pid) && get_ttyp ()->pcon_activated)
{
/* Attach to the pseudo console which already exits. */
pinfo p (get_ttyp ()->pcon_pid);
HANDLE pcon_owner =
OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->exec_dwProcessId);
DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
GetCurrentProcess (), &hpConIn,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_out,
GetCurrentProcess (), &hpConOut,
0, TRUE, DUPLICATE_SAME_ACCESS);
CloseHandle (pcon_owner);
FreeConsole ();
AttachConsole (p->dwProcessId);
goto skip_create;
}
STARTUPINFOEXW si; STARTUPINFOEXW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
HANDLE hello, goodbye; HANDLE hello, goodbye;
HANDLE hr, hw; HANDLE hr, hw;
HANDLE hpConIn, hpConOut; HPCON hpcon;
do do
{ {
@ -2928,11 +3007,11 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
(SHORT) get_ttyp ()->winsize.ws_row (SHORT) get_ttyp ()->winsize.ws_row
}; };
const DWORD inherit_cursor = 1; const DWORD inherit_cursor = 1;
hpcon = NULL;
SetLastError (ERROR_SUCCESS); SetLastError (ERROR_SUCCESS);
HRESULT res = CreatePseudoConsole (size, get_handle (), HRESULT res = CreatePseudoConsole (size, get_handle (),
get_output_handle (), get_output_handle (),
inherit_cursor, inherit_cursor, &hpcon);
&get_ttyp ()->h_pseudo_console);
if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND) if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND)
{ {
if (res != S_OK) if (res != S_OK)
@ -2955,9 +3034,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
if (!UpdateProcThreadAttribute (si.lpAttributeList, if (!UpdateProcThreadAttribute (si.lpAttributeList,
0, 0,
PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
get_ttyp ()->h_pseudo_console, hpcon, sizeof (hpcon), NULL, NULL))
sizeof (get_ttyp ()->h_pseudo_console),
NULL, NULL))
goto cleanup_heap; goto cleanup_heap;
@ -2986,6 +3063,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
si.StartupInfo.hStdError = NULL; si.StartupInfo.hStdError = NULL;
get_ttyp ()->pcon_start = true; get_ttyp ()->pcon_start = true;
get_ttyp ()->pcon_activated = true;
if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none, if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none,
TRUE, EXTENDED_STARTUPINFO_PRESENT, TRUE, EXTENDED_STARTUPINFO_PRESENT,
NULL, NULL, &si.StartupInfo, &pi)) NULL, NULL, &si.StartupInfo, &pi))
@ -3041,7 +3119,12 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
WaitForSingleObject (pi.hProcess, INFINITE); WaitForSingleObject (pi.hProcess, INFINITE);
CloseHandle (goodbye); CloseHandle (goodbye);
CloseHandle (pi.hProcess); CloseHandle (pi.hProcess);
}
while (false);
skip_create:
do
{
/* Set handle */ /* Set handle */
if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ()) if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ())
SetStdHandle (STD_INPUT_HANDLE, hpConIn); SetStdHandle (STD_INPUT_HANDLE, hpConIn);
@ -3072,16 +3155,29 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
if (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid)) if (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid))
get_ttyp ()->pcon_pid = myself->pid; get_ttyp ()->pcon_pid = myself->pid;
if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid == myself->pid) if (hpcon && get_ttyp ()->pcon_pid == myself->pid)
{ {
HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; HPCON_INTERNAL *hp = (HPCON_INTERNAL *) hpcon;
get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe; get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe;
get_ttyp ()->h_pcon_condrv_reference = hp->hConDrvReference;
get_ttyp ()->h_pcon_conhost_process = hp->hConHostProcess;
DuplicateHandle (GetCurrentProcess (), hpConIn,
GetCurrentProcess (), &get_ttyp ()->h_pcon_in,
0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), hpConOut,
GetCurrentProcess (), &get_ttyp ()->h_pcon_out,
0, TRUE, DUPLICATE_SAME_ACCESS);
/* Discard the pseudo console handler container here.
Reconstruct it temporary when it is needed. */
HeapFree (GetProcessHeap (), 0, hp);
} }
if (get_ttyp ()->previous_code_page) if (get_ttyp ()->previous_code_page)
SetConsoleCP (get_ttyp ()->previous_code_page); SetConsoleCP (get_ttyp ()->previous_code_page);
if (get_ttyp ()->previous_output_code_page) if (get_ttyp ()->previous_output_code_page)
SetConsoleOutputCP (get_ttyp ()->previous_output_code_page); SetConsoleOutputCP (get_ttyp ()->previous_output_code_page);
release_output_mutex ();
return true; return true;
cleanup_pcon_in: cleanup_pcon_in:
@ -3093,6 +3189,8 @@ cleanup_helper_process:
goto skip_close_hello; goto skip_close_hello;
cleanup_event_and_pipes: cleanup_event_and_pipes:
CloseHandle (hello); CloseHandle (hello);
get_ttyp ()->pcon_start = false;
get_ttyp ()->pcon_activated = false;
skip_close_hello: skip_close_hello:
CloseHandle (goodbye); CloseHandle (goodbye);
CloseHandle (hr); CloseHandle (hr);
@ -3100,16 +3198,15 @@ skip_close_hello:
cleanup_heap: cleanup_heap:
HeapFree (GetProcessHeap (), 0, si.lpAttributeList); HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
cleanup_pseudo_console: cleanup_pseudo_console:
if (get_ttyp ()->h_pseudo_console) if (hpcon)
{ {
HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; HPCON_INTERNAL *hp = (HPCON_INTERNAL *) hpcon;
HANDLE tmp = hp->hConHostProcess; HANDLE tmp = hp->hConHostProcess;
ClosePseudoConsole (get_ttyp ()->h_pseudo_console); ClosePseudoConsole (hpcon);
CloseHandle (tmp); CloseHandle (tmp);
} }
fallback: fallback:
get_ttyp ()->pcon_start = false; release_output_mutex ();
get_ttyp ()->h_pseudo_console = NULL;
return false; return false;
} }
@ -3118,21 +3215,122 @@ fallback:
void void
fhandler_pty_slave::close_pseudoconsole (tty *ttyp) fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
{ {
if (ttyp->h_pseudo_console) DWORD switch_to_stub = 0, switch_to = 0;
DWORD new_pcon_pid = 0;
if (ttyp->pcon_pid == myself->pid)
{
/* Search another process which attaches to the pseudo console */
DWORD current_pid = myself->exec_dwProcessId ?: myself->dwProcessId;
switch_to =
get_console_process_id (current_pid, false, true);
if (switch_to)
{
pinfo p (cygwin_pid (switch_to));
if (p)
{
if (p->exec_dwProcessId)
switch_to_stub = p->exec_dwProcessId;
new_pcon_pid = p->pid;
}
}
}
if (ttyp->pcon_activated)
{ {
ttyp->previous_code_page = GetConsoleCP (); ttyp->previous_code_page = GetConsoleCP ();
ttyp->previous_output_code_page = GetConsoleOutputCP (); ttyp->previous_output_code_page = GetConsoleOutputCP ();
if (ttyp->pcon_pid == myself->pid)
{
switch_to = switch_to_stub ?: 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 = new_pcon_pid;
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;
}
else
{ /* Close pseudo console */
FreeConsole (); FreeConsole ();
pinfo p (myself->ppid);
if (p)
{
if (!AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS); AttachConsole (ATTACH_PARENT_PROCESS);
HPCON_INTERNAL *hp = (HPCON_INTERNAL *) ttyp->h_pseudo_console; }
HANDLE tmp = hp->hConHostProcess; else
ClosePseudoConsole (ttyp->h_pseudo_console); AttachConsole (ATTACH_PARENT_PROCESS);
CloseHandle (tmp); /* Reconstruct pseudo console handler container here for close */
ttyp->h_pseudo_console = NULL; HPCON_INTERNAL *hp =
(HPCON_INTERNAL *) HeapAlloc (GetProcessHeap (), 0,
sizeof (*hp));
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_pid = 0;
ttyp->pcon_start = false; ttyp->pcon_start = false;
} }
}
else
{
FreeConsole ();
pinfo p (myself->ppid);
if (p)
{
if (!AttachConsole (p->dwProcessId))
AttachConsole (ATTACH_PARENT_PROCESS);
}
else
AttachConsole (ATTACH_PARENT_PROCESS);
}
}
else if (ttyp->pcon_pid == myself->pid)
{
if (switch_to_stub)
ttyp->pcon_pid = new_pcon_pid;
else
{
ttyp->pcon_pid = 0;
ttyp->switch_to_pcon_in = false;
}
}
} }
static bool static bool
@ -3222,9 +3420,9 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
/* Check if terminal has CSI6n */ /* Check if terminal has CSI6n */
WaitForSingleObject (input_mutex, INFINITE); WaitForSingleObject (input_mutex, INFINITE);
/* Set h_pseudo_console and pcon_start so that the response /* Set pcon_activated and pcon_start so that the response
will sent to io_handle rather than io_handle_cyg. */ will sent to io_handle rather than io_handle_cyg. */
get_ttyp ()->h_pseudo_console = (HPCON *) -1; /* dummy */ get_ttyp ()->pcon_activated = true;
get_ttyp ()->pcon_pid = myself->pid; get_ttyp ()->pcon_pid = myself->pid;
/* pcon_start will be cleared in master write() when CSI6n is responded. */ /* pcon_start will be cleared in master write() when CSI6n is responded. */
get_ttyp ()->pcon_start = true; get_ttyp ()->pcon_start = true;
@ -3255,7 +3453,7 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
Sleep (1); Sleep (1);
} }
while (len); while (len);
get_ttyp ()->h_pseudo_console = NULL; get_ttyp ()->pcon_activated = false;
get_ttyp ()->pcon_pid = 0; get_ttyp ()->pcon_pid = 0;
if (len == 0) if (len == 0)
goto not_has_csi6n; goto not_has_csi6n;
@ -3270,7 +3468,7 @@ not_has_csi6n:
/* If CSI6n is not responded, pcon_start is not cleared /* If CSI6n is not responded, pcon_start is not cleared
in master write(). Therefore, clear it here manually. */ in master write(). Therefore, clear it here manually. */
get_ttyp ()->pcon_start = false; get_ttyp ()->pcon_start = false;
get_ttyp ()->h_pseudo_console = NULL; get_ttyp ()->pcon_activated = false;
ReleaseMutex (input_mutex); ReleaseMutex (input_mutex);
maybe_dumb: maybe_dumb:
get_ttyp ()->pcon_cap_checked = true; get_ttyp ()->pcon_cap_checked = true;
@ -3362,7 +3560,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
if (dir == to_nat) if (dir == to_nat)
{ {
cp_from = ttyp->term_code_page; cp_from = ttyp->term_code_page;
if (ttyp->h_pseudo_console) if (ttyp->pcon_activated)
cp_to = CP_UTF8; cp_to = CP_UTF8;
else else
cp_to = GetConsoleCP (); cp_to = GetConsoleCP ();
@ -3378,7 +3576,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
bool transfered = false; bool transfered = false;
if (dir == to_cyg && ttyp->h_pseudo_console) if (dir == to_cyg && ttyp->pcon_activated)
{ /* from handle is console handle */ { /* from handle is console handle */
INPUT_RECORD r[INREC_SIZE]; INPUT_RECORD r[INREC_SIZE];
DWORD n; DWORD n;
@ -3462,7 +3660,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
DWORD n = MIN (bytes_in_pipe, NT_MAX_PATH); DWORD n = MIN (bytes_in_pipe, NT_MAX_PATH);
ReadFile (from, buf, n, &n, NULL); ReadFile (from, buf, n, &n, NULL);
char *ptr = buf; char *ptr = buf;
if (dir == to_nat && ttyp->h_pseudo_console) if (dir == to_nat && ttyp->pcon_activated)
{ {
char *p = buf; char *p = buf;
while ((p = (char *) memchr (p, '\n', n - (p - buf)))) while ((p = (char *) memchr (p, '\n', n - (p - buf))))

View file

@ -1388,7 +1388,7 @@ pty_slave_startup (select_record *me, select_stuff *stuff)
fhandler_base *fh = (fhandler_base *) me->fh; fhandler_base *fh = (fhandler_base *) me->fh;
fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
if (me->read_selected) if (me->read_selected)
ptys->mask_switch_to_pcon_in (true); ptys->mask_switch_to_pcon_in (true, true);
select_pipe_info *pi = stuff->device_specific_ptys; select_pipe_info *pi = stuff->device_specific_ptys;
if (pi->start) if (pi->start)
@ -1415,7 +1415,7 @@ pty_slave_cleanup (select_record *me, select_stuff *stuff)
if (!pi) if (!pi)
return; return;
if (me->read_selected && pi->start) if (me->read_selected && pi->start)
ptys->mask_switch_to_pcon_in (false); ptys->mask_switch_to_pcon_in (false, false);
if (pi->thread) if (pi->thread)
{ {
pi->stop_thread = true; pi->stop_thread = true;

View file

@ -656,6 +656,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
bool enable_pcon = false; bool enable_pcon = false;
HANDLE ptys_from_master = NULL; HANDLE ptys_from_master = NULL;
HANDLE ptys_input_available_event = NULL; HANDLE ptys_input_available_event = NULL;
HANDLE ptys_output_mutex = NULL;
tty *ptys_ttyp = NULL; tty *ptys_ttyp = NULL;
_minor_t ptys_unit = 0; _minor_t ptys_unit = 0;
if (!iscygwin () && ptys_primary && is_console_app (runpath)) if (!iscygwin () && ptys_primary && is_console_app (runpath))
@ -676,6 +677,9 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
DuplicateHandle (GetCurrentProcess (), ptys_input_available_event, DuplicateHandle (GetCurrentProcess (), ptys_input_available_event,
GetCurrentProcess (), &ptys_input_available_event, GetCurrentProcess (), &ptys_input_available_event,
0, 0, DUPLICATE_SAME_ACCESS); 0, 0, DUPLICATE_SAME_ACCESS);
DuplicateHandle (GetCurrentProcess (), ptys_primary->output_mutex,
GetCurrentProcess (), &ptys_output_mutex,
0, 0, DUPLICATE_SAME_ACCESS);
if (!enable_pcon) if (!enable_pcon)
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
ptys_primary->get_handle_cyg (), ptys_primary->get_handle_cyg (),
@ -965,16 +969,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
if (ptys_ttyp) if (ptys_ttyp)
{ {
ptys_ttyp->wait_pcon_fwd (); ptys_ttyp->wait_pcon_fwd ();
/* Do not transfer input if another process using pseudo
console exists. */
WaitForSingleObject (ptys_output_mutex, INFINITE);
if (!fhandler_pty_common::get_console_process_id
(myself->exec_dwProcessId, false, true, true))
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
ptys_from_master, ptys_from_master,
ptys_ttyp, ptys_unit, ptys_ttyp, ptys_unit,
ptys_input_available_event); ptys_input_available_event);
CloseHandle (ptys_from_master); CloseHandle (ptys_from_master);
CloseHandle (ptys_input_available_event); CloseHandle (ptys_input_available_event);
}
if (enable_pcon)
fhandler_pty_slave::close_pseudoconsole (ptys_ttyp); fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
else if (cons_native) ReleaseMutex (ptys_output_mutex);
CloseHandle (ptys_output_mutex);
}
if (cons_native)
{ {
fhandler_console::request_xterm_mode_output (true, fhandler_console::request_xterm_mode_output (true,
&cons_handle_set); &cons_handle_set);
@ -992,16 +1002,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
if (ptys_ttyp) if (ptys_ttyp)
{ {
ptys_ttyp->wait_pcon_fwd (); ptys_ttyp->wait_pcon_fwd ();
/* Do not transfer input if another process using pseudo
console exists. */
WaitForSingleObject (ptys_output_mutex, INFINITE);
if (!fhandler_pty_common::get_console_process_id
(myself->exec_dwProcessId, false, true, true))
fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
ptys_from_master, ptys_from_master,
ptys_ttyp, ptys_unit, ptys_ttyp, ptys_unit,
ptys_input_available_event); ptys_input_available_event);
CloseHandle (ptys_from_master); CloseHandle (ptys_from_master);
CloseHandle (ptys_input_available_event); CloseHandle (ptys_input_available_event);
}
if (enable_pcon)
fhandler_pty_slave::close_pseudoconsole (ptys_ttyp); fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
else if (cons_native) ReleaseMutex (ptys_output_mutex);
CloseHandle (ptys_output_mutex);
}
if (cons_native)
{ {
fhandler_console::request_xterm_mode_output (true, fhandler_console::request_xterm_mode_output (true,
&cons_handle_set); &cons_handle_set);

View file

@ -235,7 +235,7 @@ tty::init ()
master_pid = 0; master_pid = 0;
is_console = false; is_console = false;
column = 0; column = 0;
h_pseudo_console = NULL; pcon_activated = false;
switch_to_pcon_in = false; switch_to_pcon_in = false;
pcon_pid = 0; pcon_pid = 0;
term_code_page = 0; term_code_page = 0;

View file

@ -96,13 +96,17 @@ private:
HANDLE _to_master_cyg; HANDLE _to_master_cyg;
HANDLE _to_slave; HANDLE _to_slave;
HANDLE _to_slave_cyg; HANDLE _to_slave_cyg;
HPCON h_pseudo_console; bool pcon_activated;
bool pcon_start; bool pcon_start;
bool switch_to_pcon_in; bool switch_to_pcon_in;
pid_t pcon_pid; pid_t pcon_pid;
UINT term_code_page; UINT term_code_page;
DWORD pcon_last_time; DWORD pcon_last_time;
HANDLE h_pcon_write_pipe; HANDLE h_pcon_write_pipe;
HANDLE h_pcon_condrv_reference;
HANDLE h_pcon_conhost_process;
HANDLE h_pcon_in;
HANDLE h_pcon_out;
bool pcon_cap_checked; bool pcon_cap_checked;
bool has_csi6n; bool has_csi6n;
bool need_invisible_console; bool need_invisible_console;