diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index ae64086df..6da92ce60 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2264,6 +2264,7 @@ class fhandler_pty_common: public fhandler_termios return get_ttyp ()->h_pseudo_console; } bool to_be_read_from_pcon (void); + void resize_pseudo_console2 (struct winsize *); protected: BOOL process_opost_output (HANDLE h, @@ -2355,6 +2356,9 @@ class fhandler_pty_slave: public fhandler_pty_common void wait_pcon_fwd (void); void pull_pcon_input (void); void update_pcon_input_state (bool need_lock); + bool setup_pseudoconsole2 (STARTUPINFOEXW *si); + void close_pseudoconsole2 (void); + void wait_pcon_fwd2 (void); }; #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 8547ec7c4..a9a2fcdb7 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -41,6 +41,8 @@ extern "C" { VOID WINAPI ClosePseudoConsole (HPCON); } +#define USE_PCON_MODE2 true + #define close_maybe(h) \ do { \ if (h && h != INVALID_HANDLE_VALUE) \ @@ -524,22 +526,30 @@ fhandler_pty_master::accept_input () bytes_left = eat_readahead (-1); + HANDLE write_to = get_output_handle (); + pinfo p (get_ttyp ()->pcon_pid); + bool pcon2_switch_to_pcon_in = + USE_PCON_MODE2 && get_ttyp ()->switch_to_pcon_in + && p && p->ctty == get_ttyp ()->ntty && p->pgid == get_ttyp ()->getpgid (); + if (pcon2_switch_to_pcon_in) + write_to = to_slave; + if (!bytes_left) { termios_printf ("sending EOF to slave"); get_ttyp ()->read_retval = 0; } - else if (!to_be_read_from_pcon ()) + else if (!to_be_read_from_pcon () || USE_PCON_MODE2) { char *p = rabuf (); DWORD rc; DWORD written = 0; paranoid_printf ("about to write %u chars to slave", bytes_left); - rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL); + rc = WriteFile (write_to, p, bytes_left, &written, NULL); if (!rc) { - debug_printf ("error writing to pipe %p %E", get_output_handle ()); + debug_printf ("error writing to pipe %p %E", write_to); get_ttyp ()->read_retval = -1; ret = -1; } @@ -1295,6 +1305,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void) get_ttyp ()->switch_to_pcon_in = false; get_ttyp ()->switch_to_pcon_out = false; init_console_handler (true); + get_ttyp ()->h_pseudo_console2 = NULL; } void @@ -1554,8 +1565,13 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask) bool fhandler_pty_common::to_be_read_from_pcon (void) { - return !get_ttyp ()->pcon_in_empty || - (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in); + pinfo p (get_ttyp ()->pcon_pid); + bool pcon2_switch_to_pcon_in = + get_ttyp ()->h_pseudo_console2 && get_ttyp ()->switch_to_pcon_in + && p && p->ctty == get_ttyp ()->ntty && p->pgid == get_ttyp ()->getpgid (); + return !get_ttyp ()->pcon_in_empty + || pcon2_switch_to_pcon_in || get_ttyp ()->pcon2_start + || (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in); } void __reg3 @@ -2059,6 +2075,8 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) cleanup: restore_reattach_pcon (); } + if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid) + resize_pseudo_console2 ((struct winsize *) arg); if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) @@ -2348,6 +2366,27 @@ fhandler_pty_common::close () return 0; } +void +fhandler_pty_common::resize_pseudo_console2 (struct winsize *ws) +{ + COORD size; + size.X = ws->ws_col; + size.Y = ws->ws_row; + pinfo p (get_ttyp ()->pcon_pid); + if (p) + { + HPCON_INTERNAL hpcon_local; + HANDLE pcon_owner = + OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->exec_dwProcessId); + DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_write_pipe, + GetCurrentProcess (), &hpcon_local.hWritePipe, + 0, TRUE, DUPLICATE_SAME_ACCESS); + ResizePseudoConsole ((HPCON) &hpcon_local, size); + CloseHandle (pcon_owner); + CloseHandle (hpcon_local.hWritePipe); + } +} + void fhandler_pty_master::cleanup () { @@ -2485,7 +2524,8 @@ fhandler_pty_master::write (const void *ptr, size_t len) /* Write terminal input to to_slave pipe instead of output_handle if current application is native console application. */ - if (to_be_read_from_pcon ()) + if (to_be_read_from_pcon () + && (!USE_PCON_MODE2 || get_ttyp ()->h_pseudo_console2)) { size_t nlen; char *buf = convert_mb_str @@ -2497,11 +2537,52 @@ fhandler_pty_master::write (const void *ptr, size_t len) get_ttyp ()->req_flush_pcon_input = true; DWORD wLen; + + if (get_ttyp ()->pcon2_start) + { + /* Pseudo condole support mode-2 uses "CSI6n" to get cursor + position. If the reply for "CSI6n" is divided into multiple + writes, pseudo console sometimes does not recognize it. + Therefore, put them together into wpbuf and write all at once. */ + static const int wpbuf_len = 64; + static char wpbuf[wpbuf_len]; + static int ixput = 0; + + if (ixput == 0 && buf[0] != '\033') + { /* fail-safe */ + WriteFile (to_slave, "\033[1;1R", 6, &wLen, NULL); /* dummy */ + get_ttyp ()->pcon2_start = false; + } + else + { + if (ixput + nlen < wpbuf_len) + for (size_t i=0; ipcon2_start = false; + WriteFile (to_slave, buf, nlen, &wLen, NULL); + } + if (ixput && memchr (wpbuf, 'R', ixput)) + { + WriteFile (to_slave, wpbuf, ixput, &wLen, NULL); + ixput = 0; + get_ttyp ()->pcon2_start = false; + } + ReleaseMutex (input_mutex); + mb_str_free (buf); + return len; + } + } + WriteFile (to_slave, buf, nlen, &wLen, NULL); get_ttyp ()->pcon_in_empty = false; ReleaseMutex (input_mutex); +#if !USE_PCON_MODE2 /* Use line_edit () in order to set input_available_event. */ bool is_echo = tc ()->ti.c_lflag & ECHO; if (!get_ttyp ()->mask_switch_to_pcon_in) @@ -2527,6 +2608,7 @@ fhandler_pty_master::write (const void *ptr, size_t len) eat_readahead (-1); SetEvent (input_available_event); } +#endif /* USE_PCON_MODE2 */ mb_str_free (buf); return len; @@ -2608,6 +2690,8 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) size.Y = ((struct winsize *) arg)->ws_row; ResizePseudoConsole (get_pseudo_console (), size); } + if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid) + resize_pseudo_console2 ((struct winsize *) arg); if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) { @@ -2884,6 +2968,19 @@ fhandler_pty_slave::wait_pcon_fwd (void) cygwait (get_ttyp ()->fwd_done, INFINITE); } +void +fhandler_pty_slave::wait_pcon_fwd2 (void) +{ + const int sleep_in_pcon = 16; + const int time_to_wait = sleep_in_pcon * 2 + 1/* margine */; + get_ttyp ()->pcon_last_time = GetTickCount (); + while (GetTickCount () - get_ttyp ()->pcon_last_time < time_to_wait) + { + int tw = time_to_wait - (GetTickCount () - get_ttyp ()->pcon_last_time); + cygwait (tw); + } +} + void fhandler_pty_slave::trigger_redraw_screen (void) { @@ -3267,6 +3364,8 @@ fhandler_pty_master::pty_master_fwd_thread () Sleep (1); } } + if (USE_PCON_MODE2) + get_ttyp ()->pcon_last_time = GetTickCount (); if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL)) { termios_printf ("ReadFile for forwarding failed, %E"); @@ -3274,8 +3373,9 @@ fhandler_pty_master::pty_master_fwd_thread () } ssize_t wlen = rlen; char *ptr = outbuf; - if (get_pseudo_console ()) + if (get_pseudo_console () || get_ttyp ()->h_pseudo_console2) { +#if !USE_PCON_MODE2 /* Avoid duplicating slave output which is already sent to to_master_cyg */ if (!get_ttyp ()->switch_to_pcon_out) @@ -3328,6 +3428,7 @@ fhandler_pty_master::pty_master_fwd_thread () rlen -= 4; } wlen = rlen; +#endif /* USE_PCON_MODE2 */ size_t nlen; char *buf = convert_mb_str @@ -3697,7 +3798,10 @@ fhandler_pty_master::setup () t.winsize.ws_col = 80; t.winsize.ws_row = 25; - setup_pseudoconsole (); + if (!USE_PCON_MODE2) + setup_pseudoconsole (); + else + CreatePipe (&from_master, &to_slave, &sec_none, 0); t.set_from_master (from_master); t.set_from_master_cyg (from_master_cyg); @@ -3859,3 +3963,126 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l len -= towrite; return res; } + +bool +fhandler_pty_slave::setup_pseudoconsole2 (STARTUPINFOEXW *si) +{ + if (USE_PCON_MODE2) + { + fhandler_pty_slave *p0 = (fhandler_pty_slave *) ::cygheap->fdtab[0]; + if (p0 && p0->get_device () == get_device ()) + get_ttyp ()->switch_to_pcon_in = true; + if (get_ttyp ()->pcon_pid == 0 || + !pinfo (get_ttyp ()->pcon_pid)) + get_ttyp ()->pcon_pid = myself->pid; + } + if (!USE_PCON_MODE2) + return false; + if (disable_pcon) + return false; + /* If the legacy console mode is enabled, pseudo console seems + not to work as expected. To determine console mode, registry + key ForceV2 in HKEY_CURRENT_USER\Console is checked. */ + reg_key reg (HKEY_CURRENT_USER, KEY_READ, L"Console", NULL); + if (reg.error ()) + return false; + if (reg.get_dword (L"ForceV2", 1) == 0) + { + termios_printf ("Pseudo console is disabled " + "because the legacy console mode is enabled."); + return false; + } + + COORD size = { + (SHORT) get_ttyp ()->winsize.ws_col, + (SHORT) get_ttyp ()->winsize.ws_row + }; + SetLastError (ERROR_SUCCESS); + HRESULT res = CreatePseudoConsole (size, get_handle (), get_output_handle (), + 1, &get_ttyp ()->h_pseudo_console2); + if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND) + { + if (res != S_OK) + system_printf ("CreatePseudoConsole() failed. %08x %08x\n", + GetLastError (), res); + goto fallback; + } + + SIZE_T bytesRequired; + InitializeProcThreadAttributeList (NULL, 1, 0, &bytesRequired); + ZeroMemory (si, sizeof (*si)); + si->StartupInfo.cb = sizeof (STARTUPINFOEXW); + si->lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) + HeapAlloc (GetProcessHeap (), 0, bytesRequired); + if (si->lpAttributeList == NULL) + goto cleanup_pseudo_console; + if (!InitializeProcThreadAttributeList (si->lpAttributeList, + 1, 0, &bytesRequired)) + goto cleanup_heap; + if (!UpdateProcThreadAttribute (si->lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + get_ttyp ()->h_pseudo_console2, + sizeof (get_ttyp ()->h_pseudo_console2), + NULL, NULL)) + goto cleanup_heap; + si->StartupInfo.dwFlags = STARTF_USESTDHANDLES; + si->StartupInfo.hStdInput = NULL; + si->StartupInfo.hStdOutput = NULL; + si->StartupInfo.hStdError = NULL; + + { + fhandler_pty_slave *p0 = (fhandler_pty_slave *) ::cygheap->fdtab[0]; + if (p0 && p0->get_device () != get_device ()) + si->StartupInfo.hStdInput = p0->get_handle (); + fhandler_pty_slave *p1 = (fhandler_pty_slave *) ::cygheap->fdtab[1]; + if (p1 && p1->get_device () != get_device ()) + si->StartupInfo.hStdOutput = p1->get_output_handle (); + fhandler_pty_slave *p2 = (fhandler_pty_slave *) ::cygheap->fdtab[2]; + if (p2 && p2->get_device () != get_device ()) + si->StartupInfo.hStdError = p2->get_output_handle (); + } +#if 0 + get_ttyp ()->switch_to_pcon_in = (si->StartupInfo.hStdInput == NULL); + + if (get_ttyp ()->pcon_pid == 0 || + !pinfo (get_ttyp ()->pcon_pid)) + get_ttyp ()->pcon_pid = myself->pid; +#endif + if (get_ttyp ()->h_pseudo_console2 && get_ttyp ()->pcon_pid == myself->pid) + { + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2; + get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe; + } + get_ttyp ()->pcon2_start = true; + return true; + +cleanup_heap: + HeapFree (GetProcessHeap (), 0, si->lpAttributeList); +cleanup_pseudo_console: + if (get_ttyp ()->h_pseudo_console2) + { + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2; + HANDLE tmp = hp->hConHostProcess; + ClosePseudoConsole (get_ttyp ()->h_pseudo_console2); + CloseHandle (tmp); + } +fallback: + get_ttyp ()->h_pseudo_console2 = NULL; + return false; +} + +void +fhandler_pty_slave::close_pseudoconsole2 (void) +{ + if (get_ttyp ()->h_pseudo_console2) + { + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console2; + HANDLE tmp = hp->hConHostProcess; + ClosePseudoConsole (get_ttyp ()->h_pseudo_console2); + CloseHandle (tmp); + get_ttyp ()->h_pseudo_console2 = NULL; + get_ttyp ()->switch_to_pcon_in = false; + get_ttyp ()->pcon_pid = 0; + } +} diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 3e8c8367a..514125072 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -194,6 +194,24 @@ handle (int fd, bool writing) return h; } +static bool +is_console_app (WCHAR *filename) +{ + HANDLE h; + const int id_offset = 92; + h = CreateFileW (filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + char buf[1024]; + DWORD n; + ReadFile (h, buf, sizeof (buf), &n, 0); + CloseHandle (h); + char *p = (char *) memmem (buf, n, "PE\0\0", 4); + if (p && p + id_offset <= buf + n) + return p[id_offset] == '\003'; /* 02: GUI, 03: console */ + else + return false; +} + int iscmd (const char *argv0, const char *what) { @@ -583,6 +601,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, /* Attach to pseudo console if pty salve is used */ pid_restore = fhandler_console::get_console_process_id (GetCurrentProcessId (), false); + fhandler_pty_slave *ptys_primary = NULL; for (int i = 0; i < 3; i ++) { const int chk_order[] = {1, 0, 2}; @@ -591,6 +610,8 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (fh && fh->get_major () == DEV_PTYS_MAJOR) { fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; + if (ptys_primary == NULL) + ptys_primary = ptys; if (ptys->get_pseudo_console ()) { DWORD helper_process_id = ptys->get_helper_process_id (); @@ -632,6 +653,18 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (!iscygwin ()) init_console_handler (myself->ctty > 0); + bool use_pcon_mode2 = false; + STARTUPINFOEXW si_pcon; + ZeroMemory (&si_pcon, sizeof (si_pcon)); + STARTUPINFOW *si_tmp = &si; + if (!iscygwin () && ptys_primary && is_console_app (runpath)) + if (ptys_primary->setup_pseudoconsole2 (&si_pcon)) + { + c_flags |= EXTENDED_STARTUPINFO_PRESENT; + si_tmp = &si_pcon.StartupInfo; + use_pcon_mode2 = true; + } + loop: /* When ruid != euid we create the new process under the current original account and impersonate in child, this way maintaining the different @@ -660,7 +693,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, c_flags, envblock, /* environment */ NULL, - &si, + si_tmp, &pi); } else @@ -714,7 +747,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, c_flags, envblock, /* environment */ NULL, - &si, + si_tmp, &pi); if (hwst) { @@ -727,6 +760,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, CloseDesktop (hdsk); } } + if (use_pcon_mode2) + { + DeleteProcThreadAttributeList (si_pcon.lpAttributeList); + HeapFree (GetProcessHeap (), 0, si_pcon.lpAttributeList); + } if (mode != _P_OVERLAY) SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, @@ -897,6 +935,12 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) wait_for_myself (); } + if (use_pcon_mode2) + { + WaitForSingleObject (pi.hProcess, INFINITE); + ptys_primary->wait_pcon_fwd2 (); + ptys_primary->close_pseudoconsole2 (); + } myself.exit (EXITCODE_NOSET); break; case _P_WAIT: diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index 0663dc5ee..9bc1457d1 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -250,6 +250,8 @@ tty::init () pcon_in_empty = true; req_transfer_input_to_pcon = false; req_flush_pcon_input = false; + h_pseudo_console2 = NULL; + pcon2_start = false; } HANDLE diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index a24afad06..a7546715e 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -111,6 +111,9 @@ private: bool pcon_in_empty; bool req_transfer_input_to_pcon; bool req_flush_pcon_input; + HPCON h_pseudo_console2; + HANDLE h_pcon_write_pipe; + bool pcon2_start; public: HANDLE from_master () const { return _from_master; }