From a21b1b27e4b57968e14ffe07177951fd1bc7a06b Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Wed, 22 Jul 2020 12:39:45 +0900 Subject: [PATCH] Cygwin: pty: Implement new pseudo console support. - In this implementation, pseudo console is created for each native console app. Advantages and disadvantages of this implementation over the previous implementation are as follows. Advantages: 1) No performance degradation in pty output for cygwin process. https://cygwin.com/pipermail/cygwin/2020-February/243858.html 2) Free from the problem caused by difference of behaviour of control sequences between real terminal and pseudo console. https://cygwin.com/pipermail/cygwin/2019-December/243281.html https://cygwin.com/pipermail/cygwin/2020-February/243855.html 3) Free from the problem in cgdb and emacs gud. https://cygwin.com/pipermail/cygwin/2020-January/243601.html https://cygwin.com/pipermail/cygwin/2020-March/244146.html 4) Redrawing screen on executing native console apps is not necessary. 5) cygwin-console-helper is not necessary for the pseudo console support. 6) The codes for pseudo console support are much simpler than that of the previous one. Disadvantages: 1) The cygwin program which calls console API directly does not work. 2) The apps which use console API cannot be debugged with gdb. This is because pseudo console is not activated since gdb uses CreateProcess() rather than exec(). Even with this limitation, attaching gdb to native apps, in which pseudo console is already activated, works. 3) Typeahead key inputs are discarded while native console app is executed. Simirally, typeahead key inputs while cygwin app is executed are not inherited to native console app. 4) Code page cannot be changed by chcp.com. Acctually, chcp works itself and changes code page of its own pseudo console. However, since pseudo console is recreated for another process, it cannot inherit the code page. 5) system_printf() does not work after stderr is closed. (Same with cygwin 3.0.7) 6) Startup time of native console apps is about 3 times slower than previous implemenation. --- winsup/cygwin/dtable.cc | 32 - winsup/cygwin/fhandler.h | 51 +- winsup/cygwin/fhandler_console.cc | 43 - winsup/cygwin/fhandler_tty.cc | 1733 +++++------------------------ winsup/cygwin/fork.cc | 30 - winsup/cygwin/select.cc | 2 - winsup/cygwin/smallprint.cc | 2 - winsup/cygwin/spawn.cc | 87 +- winsup/cygwin/strace.cc | 2 - winsup/cygwin/tty.cc | 20 +- winsup/cygwin/tty.h | 13 +- winsup/cygwin/winsup.h | 3 - 12 files changed, 317 insertions(+), 1701 deletions(-) diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index e9e005b08..6a91e33bc 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -147,38 +147,6 @@ dtable::get_debugger_info () void dtable::stdio_init () { - for (int i = 0; i < 3; i ++) - { - const int chk_order[] = {1, 0, 2}; - int fd = chk_order[i]; - fhandler_base *fh = cygheap->fdtab[fd]; - if (fh && fh->get_major () == DEV_PTYS_MAJOR) - { - fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; - if (ptys->get_pseudo_console ()) - { - bool attached = !!fhandler_console::get_console_process_id - (ptys->get_helper_process_id (), true); - if (attached) - break; - else - { - /* Not attached to pseudo console in fork() or spawn() - by some reason. This happens if the executable is - a windows GUI binary, such as mintty. */ - FreeConsole (); - if (AttachConsole (ptys->get_helper_process_id ())) - { - ptys->fixup_after_attach (false, fd); - break; - } - } - } - } - else if (fh && fh->get_major () == DEV_CONS_MAJOR) - break; - } - if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT)) { tty_min *t = cygwin_shared->tty.get_cttyp (); diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 60bd27e00..544108a23 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -326,16 +326,16 @@ class fhandler_base virtual size_t &raixput () { return ra.raixput; }; virtual size_t &rabuflen () { return ra.rabuflen; }; - virtual bool get_readahead_valid () { return raixget () < ralen (); } + bool get_readahead_valid () { return raixget () < ralen (); } int puts_readahead (const char *s, size_t len = (size_t) -1); - virtual int put_readahead (char value); + int put_readahead (char value); int get_readahead (); int peek_readahead (int queryput = 0); void set_readahead_valid (int val, int ch = -1); - virtual int get_readahead_into_buffer (char *buf, size_t buflen); + int get_readahead_into_buffer (char *buf, size_t buflen); bool has_acls () const { return pc.has_acls (); } @@ -1909,7 +1909,7 @@ class fhandler_termios: public fhandler_base int ioctl (int, void *); tty_min *_tc; tty *get_ttyp () {return (tty *) tc ();} - virtual int eat_readahead (int n); + int eat_readahead (int n); public: tty_min*& tc () {return _tc;} @@ -2188,7 +2188,6 @@ private: static bool need_invisible (); static void free_console (); static const char *get_nonascii_key (INPUT_RECORD& input_rec, char *); - static DWORD get_console_process_id (DWORD pid, bool match); fhandler_console (void *) {} @@ -2268,19 +2267,8 @@ class fhandler_pty_common: public fhandler_termios return fh; } - bool attach_pcon_in_fork (void) - { - return get_ttyp ()->attach_pcon_in_fork; - } - DWORD get_helper_process_id (void) - { - return get_ttyp ()->helper_process_id; - } - HPCON get_pseudo_console (void) - { - return get_ttyp ()->h_pseudo_console; - } bool to_be_read_from_pcon (void); + void resize_pseudo_console (struct winsize *); protected: BOOL process_opost_output (HANDLE h, @@ -2291,23 +2279,15 @@ class fhandler_pty_slave: public fhandler_pty_common { HANDLE inuse; // used to indicate that a tty is in use HANDLE output_handle_cyg, io_handle_cyg; - DWORD pid_restore; - int fd; /* Helper functions for fchmod and fchown. */ bool fch_open_handles (bool chown); int fch_set_sd (security_descriptor &sd, bool chown); void fch_close_handles (); - bool try_reattach_pcon (); - void restore_reattach_pcon (); - inline void free_attached_console (); - public: /* Constructor */ fhandler_pty_slave (int); - /* Destructor */ - ~fhandler_pty_slave (); void set_output_handle_cyg (HANDLE h) { output_handle_cyg = h; } HANDLE& get_output_handle_cyg () { return output_handle_cyg; } @@ -2319,9 +2299,6 @@ class fhandler_pty_slave: public fhandler_pty_common ssize_t __stdcall write (const void *ptr, size_t len); void __reg3 read (void *ptr, size_t& len); int init (HANDLE, DWORD, mode_t); - int eat_readahead (int n); - int get_readahead_into_buffer (char *buf, size_t buflen); - bool get_readahead_valid (void); int tcsetattr (int a, const struct termios *t); int tcgetattr (struct termios *t); @@ -2358,20 +2335,12 @@ class fhandler_pty_slave: public fhandler_pty_common copyto (fh); return fh; } - void set_switch_to_pcon (int fd); + bool setup_pseudoconsole (STARTUPINFOEXW *si); + void close_pseudoconsole (void); + void set_switch_to_pcon (void); void reset_switch_to_pcon (void); - void push_to_pcon_screenbuffer (const char *ptr, size_t len, bool is_echo); void mask_switch_to_pcon_in (bool mask); - void fixup_after_attach (bool native_maybe, int fd); - bool is_line_input (void) - { - return get_ttyp ()->ti.c_lflag & ICANON; - } void setup_locale (void); - void set_freeconsole_on_close (bool val); - void trigger_redraw_screen (void); - void pull_pcon_input (void); - void update_pcon_input_state (bool need_lock); }; #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) @@ -2396,7 +2365,6 @@ public: int process_slave_output (char *buf, size_t len, int pktmode_on); void doecho (const void *str, DWORD len); int accept_input (); - int put_readahead (char value); int open (int flags, mode_t mode = 0); void open_setup (int flags); ssize_t __stdcall write (const void *ptr, size_t len); @@ -2435,9 +2403,6 @@ public: copyto (fh); return fh; } - - bool setup_pseudoconsole (void); - void transfer_input_to_pcon (void); }; class fhandler_dev_null: public fhandler_base diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc index 52741ce8b..02a5996a1 100644 --- a/winsup/cygwin/fhandler_console.cc +++ b/winsup/cygwin/fhandler_console.cc @@ -1206,18 +1206,6 @@ fhandler_console::close () if (con_ra.rabuf) free (con_ra.rabuf); - /* If already attached to pseudo console, don't call free_console () */ - cygheap_fdenum cfd (false); - while (cfd.next () >= 0) - if (cfd->get_major () == DEV_PTYM_MAJOR || - cfd->get_major () == DEV_PTYS_MAJOR) - { - fhandler_pty_common *t = - (fhandler_pty_common *) (fhandler_base *) cfd; - if (get_console_process_id (t->get_helper_process_id (), true)) - return 0; - } - if (!have_execed) free_console (); return 0; @@ -3611,37 +3599,6 @@ fhandler_console::need_invisible () return b; } -DWORD -fhandler_console::get_console_process_id (DWORD pid, bool match) -{ - DWORD tmp; - DWORD num, num_req; - num = 1; - num_req = GetConsoleProcessList (&tmp, num); - DWORD *list; - while (true) - { - list = (DWORD *) - HeapAlloc (GetProcessHeap (), 0, num_req * sizeof (DWORD)); - num = num_req; - num_req = GetConsoleProcessList (list, num); - if (num_req > num) - HeapFree (GetProcessHeap (), 0, list); - else - break; - } - num = num_req; - - tmp = 0; - for (DWORD i=0; i #include "cygwait.h" -#include "tls_pbuf.h" #include "registry.h" #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE @@ -33,7 +32,6 @@ details. */ #endif /* PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE */ extern "C" int sscanf (const char *, const char *, ...); -extern "C" int ttyname_r (int, char*, size_t); extern "C" { HRESULT WINAPI CreatePseudoConsole (COORD, HANDLE, HANDLE, DWORD, HPCON *); @@ -60,20 +58,10 @@ struct pipe_reply { DWORD error; }; -static int pcon_attached_to = -1; static bool isHybrid; -static bool do_not_reset_switch_to_pcon; -static bool freeconsole_on_close = true; -static tty *last_ttyp = NULL; - -void -clear_pcon_attached_to (void) -{ - pcon_attached_to = -1; -} static void -set_switch_to_pcon (void) +set_switch_to_pcon (HANDLE h) { cygheap_fdenum cfd (false); int fd; @@ -82,280 +70,17 @@ set_switch_to_pcon (void) { fhandler_base *fh = cfd; fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; - if (ptys->get_pseudo_console ()) - { - ptys->set_switch_to_pcon (fd); - ptys->trigger_redraw_screen (); - DWORD mode = - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT; - SetConsoleMode (ptys->get_handle (), mode); - } + if (h == ptys->get_handle ()) + ptys->set_switch_to_pcon (); return; } - /* No pty slave opened */ - if (last_ttyp) /* Make system_printf() work after closing pty slave */ - last_ttyp->set_switch_to_pcon_out (true); -} - -static void -force_attach_to_pcon (HANDLE h) -{ - bool attach_done = false; - for (int n = 0; n < 2; n ++) - { - /* First time, attach to the pty whose handle value is match. - Second time, try to attach to any pty. */ - cygheap_fdenum cfd (false); - while (cfd.next () >= 0) - if (cfd->get_major () == DEV_PTYS_MAJOR) - { - fhandler_base *fh = cfd; - fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; - if (!ptys->get_pseudo_console ()) - continue; - if (n != 0 - || h == ptys->get_handle () - || h == ptys->get_output_handle ()) - { - if (fhandler_console::get_console_process_id - (ptys->get_helper_process_id (), true)) - attach_done = true; - else - { - FreeConsole (); - if (AttachConsole (ptys->get_helper_process_id ())) - { - pcon_attached_to = ptys->get_minor (); - attach_done = true; - } - else - pcon_attached_to = -1; - } - break; - } - } - else if (cfd->get_major () == DEV_CONS_MAJOR) - { - fhandler_base *fh = cfd; - fhandler_console *cons = (fhandler_console *) fh; - if (n != 0 - || h == cons->get_handle () - || h == cons->get_output_handle ()) - { - /* If the process is running on a console, - the parent process should be attached - to the same console. */ - DWORD attach_wpid; - if (myself->ppid == 1) - attach_wpid = ATTACH_PARENT_PROCESS; - else - { - pinfo p (myself->ppid); - attach_wpid = p->dwProcessId; - } - FreeConsole (); - if (AttachConsole (attach_wpid)) - { - pcon_attached_to = -1; - attach_done = true; - } - else - pcon_attached_to = -1; - break; - } - } - if (attach_done) - break; - } -} - -void -set_ishybrid_and_switch_to_pcon (HANDLE h) -{ - if (GetFileType (h) == FILE_TYPE_CHAR) - { - force_attach_to_pcon (h); - DWORD dummy; - if (!isHybrid && (GetConsoleMode (h, &dummy) - || GetLastError () != ERROR_INVALID_HANDLE)) - { - isHybrid = true; - set_switch_to_pcon (); - } - } -} - -inline void -fhandler_pty_slave::free_attached_console () -{ - bool attached = get_ttyp () ? - fhandler_console::get_console_process_id (get_helper_process_id (), true) - : (get_minor () == pcon_attached_to); - if (freeconsole_on_close && attached) - { - FreeConsole (); - pcon_attached_to = -1; - } } #define DEF_HOOK(name) static __typeof__ (name) *name##_Orig -DEF_HOOK (WriteFile); -DEF_HOOK (WriteConsoleA); -DEF_HOOK (WriteConsoleW); -DEF_HOOK (ReadFile); -DEF_HOOK (ReadConsoleA); -DEF_HOOK (ReadConsoleW); -DEF_HOOK (WriteConsoleOutputA); -DEF_HOOK (WriteConsoleOutputW); -DEF_HOOK (WriteConsoleOutputCharacterA); -DEF_HOOK (WriteConsoleOutputCharacterW); -DEF_HOOK (WriteConsoleOutputAttribute); -DEF_HOOK (SetConsoleCursorPosition); -DEF_HOOK (SetConsoleTextAttribute); -DEF_HOOK (WriteConsoleInputA); -DEF_HOOK (WriteConsoleInputW); -DEF_HOOK (ReadConsoleInputA); -DEF_HOOK (ReadConsoleInputW); -DEF_HOOK (PeekConsoleInputA); -DEF_HOOK (PeekConsoleInputW); /* CreateProcess() is hooked for GDB etc. */ DEF_HOOK (CreateProcessA); DEF_HOOK (CreateProcessW); -static BOOL WINAPI -WriteFile_Hooked - (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPOVERLAPPED o) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteFile_Orig (h, p, l, n, o); -} -static BOOL WINAPI -WriteConsoleA_Hooked - (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPVOID o) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleA_Orig (h, p, l, n, o); -} -static BOOL WINAPI -WriteConsoleW_Hooked - (HANDLE h, LPCVOID p, DWORD l, LPDWORD n, LPVOID o) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleW_Orig (h, p, l, n, o); -} -static BOOL WINAPI -ReadFile_Hooked - (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPOVERLAPPED o) -{ - set_ishybrid_and_switch_to_pcon (h); - return ReadFile_Orig (h, p, l, n, o); -} -static BOOL WINAPI -ReadConsoleA_Hooked - (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPVOID o) -{ - set_ishybrid_and_switch_to_pcon (h); - return ReadConsoleA_Orig (h, p, l, n, o); -} -static BOOL WINAPI -ReadConsoleW_Hooked - (HANDLE h, LPVOID p, DWORD l, LPDWORD n, LPVOID o) -{ - set_ishybrid_and_switch_to_pcon (h); - return ReadConsoleW_Orig (h, p, l, n, o); -} -static BOOL WINAPI -WriteConsoleOutputA_Hooked - (HANDLE h, CONST CHAR_INFO *p, COORD s, COORD c, PSMALL_RECT r) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleOutputA_Orig (h, p, s, c, r); -} -static BOOL WINAPI -WriteConsoleOutputW_Hooked - (HANDLE h, CONST CHAR_INFO *p, COORD s, COORD c, PSMALL_RECT r) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleOutputW_Orig (h, p, s, c, r); -} -static BOOL WINAPI -WriteConsoleOutputCharacterA_Hooked - (HANDLE h, LPCSTR p, DWORD l, COORD c, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleOutputCharacterA_Orig (h, p, l, c, n); -} -static BOOL WINAPI -WriteConsoleOutputCharacterW_Hooked - (HANDLE h, LPCWSTR p, DWORD l, COORD c, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleOutputCharacterW_Orig (h, p, l, c, n); -} -static BOOL WINAPI -WriteConsoleOutputAttribute_Hooked - (HANDLE h, CONST WORD *a, DWORD l, COORD c, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleOutputAttribute_Orig (h, a, l, c, n); -} -static BOOL WINAPI -SetConsoleCursorPosition_Hooked - (HANDLE h, COORD c) -{ - set_ishybrid_and_switch_to_pcon (h); - return SetConsoleCursorPosition_Orig (h, c); -} -static BOOL WINAPI -SetConsoleTextAttribute_Hooked - (HANDLE h, WORD a) -{ - set_ishybrid_and_switch_to_pcon (h); - return SetConsoleTextAttribute_Orig (h, a); -} -static BOOL WINAPI -WriteConsoleInputA_Hooked - (HANDLE h, CONST INPUT_RECORD *r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleInputA_Orig (h, r, l, n); -} -static BOOL WINAPI -WriteConsoleInputW_Hooked - (HANDLE h, CONST INPUT_RECORD *r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return WriteConsoleInputW_Orig (h, r, l, n); -} -static BOOL WINAPI -ReadConsoleInputA_Hooked - (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return ReadConsoleInputA_Orig (h, r, l, n); -} -static BOOL WINAPI -ReadConsoleInputW_Hooked - (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return ReadConsoleInputW_Orig (h, r, l, n); -} -static BOOL WINAPI -PeekConsoleInputA_Hooked - (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return PeekConsoleInputA_Orig (h, r, l, n); -} -static BOOL WINAPI -PeekConsoleInputW_Hooked - (HANDLE h, PINPUT_RECORD r, DWORD l, LPDWORD n) -{ - set_ishybrid_and_switch_to_pcon (h); - return PeekConsoleInputW_Orig (h, r, l, n); -} -/* CreateProcess() is hooked for GDB etc. */ static BOOL WINAPI CreateProcessA_Hooked (LPCSTR n, LPSTR c, LPSECURITY_ATTRIBUTES pa, LPSECURITY_ATTRIBUTES ta, @@ -363,11 +88,14 @@ CreateProcessA_Hooked LPSTARTUPINFOA si, LPPROCESS_INFORMATION pi) { HANDLE h; - if (si->dwFlags & STARTF_USESTDHANDLES) - h = si->hStdOutput; - else - h = GetStdHandle (STD_OUTPUT_HANDLE); - set_ishybrid_and_switch_to_pcon (h); + if (!isHybrid) + { + if (si->dwFlags & STARTF_USESTDHANDLES) + h = si->hStdInput; + else + h = GetStdHandle (STD_INPUT_HANDLE); + set_switch_to_pcon (h); + } return CreateProcessA_Orig (n, c, pa, ta, inh, f, e, d, si, pi); } static BOOL WINAPI @@ -377,11 +105,14 @@ CreateProcessW_Hooked LPSTARTUPINFOW si, LPPROCESS_INFORMATION pi) { HANDLE h; - if (si->dwFlags & STARTF_USESTDHANDLES) - h = si->hStdOutput; - else - h = GetStdHandle (STD_OUTPUT_HANDLE); - set_ishybrid_and_switch_to_pcon (h); + if (!isHybrid) + { + if (si->dwFlags & STARTF_USESTDHANDLES) + h = si->hStdInput; + else + h = GetStdHandle (STD_INPUT_HANDLE); + set_switch_to_pcon (h); + } return CreateProcessW_Orig (n, c, pa, ta, inh, f, e, d, si, pi); } @@ -464,10 +195,6 @@ fhandler_pty_master::flush_to_slave () { if (get_readahead_valid () && !(get_ttyp ()->ti.c_lflag & ICANON)) accept_input (); - WaitForSingleObject (input_mutex, INFINITE); - if (!get_ttyp ()->pcon_in_empty && !(get_ttyp ()->ti.c_lflag & ICANON)) - SetEvent (input_available_event); - ReleaseMutex (input_mutex); } DWORD @@ -532,14 +259,6 @@ fhandler_pty_master::doecho (const void *str, DWORD len) release_output_mutex (); } -int -fhandler_pty_master::put_readahead (char value) -{ - if (to_be_read_from_pcon ()) - return 1; - return fhandler_base::put_readahead (value); -} - int fhandler_pty_master::accept_input () { @@ -551,9 +270,11 @@ fhandler_pty_master::accept_input () char *p = rabuf () + raixget (); bytes_left = eat_readahead (-1); + HANDLE write_to = get_output_handle (); if (to_be_read_from_pcon ()) - ; /* Do nothing */ - else if (!bytes_left) + write_to = to_slave; + + if (!bytes_left) { termios_printf ("sending EOF to slave"); get_ttyp ()->read_retval = 0; @@ -564,10 +285,10 @@ fhandler_pty_master::accept_input () 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; } @@ -725,52 +446,12 @@ out: fhandler_pty_slave::fhandler_pty_slave (int unit) : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL), - io_handle_cyg (NULL), pid_restore (0), fd (-1) + io_handle_cyg (NULL) { if (unit >= 0) dev ().parse (DEV_PTYS_MAJOR, unit); } -fhandler_pty_slave::~fhandler_pty_slave () -{ - if (!get_ttyp ()) - { - /* Why comes here? Who clears _tc? */ - free_attached_console (); - return; - } - if (get_pseudo_console ()) - { - int used = 0; - int attached = 0; - cygheap_fdenum cfd (false); - while (cfd.next () >= 0) - { - if (cfd->get_major () == DEV_PTYS_MAJOR || - cfd->get_major () == DEV_CONS_MAJOR) - used ++; - if (cfd->get_major () == DEV_PTYS_MAJOR && - cfd->get_minor () == pcon_attached_to) - attached ++; - } - - /* Call FreeConsole() if no tty is opened and the process - is attached to console corresponding to tty. This is - needed to make GNU screen and tmux work in Windows 10 - 1903. */ - if (attached == 0) - { - pcon_attached_to = -1; - last_ttyp = get_ttyp (); - } - if (used == 0) - { - init_console_handler (false); - free_attached_console (); - } - } -} - int fhandler_pty_slave::open (int flags, mode_t) { @@ -950,24 +631,7 @@ fhandler_pty_slave::open (int flags, mode_t) set_output_handle (to_master_local); set_output_handle_cyg (to_master_cyg_local); - if (!get_pseudo_console ()) - { - fhandler_console::need_invisible (); - pcon_attached_to = -1; - } - else if (!fhandler_console::get_console_process_id - (GetCurrentProcessId (), true)) - { - fhandler_console::need_invisible (); - pcon_attached_to = -1; - } - else if (fhandler_console::get_console_process_id - (get_helper_process_id (), true)) - /* Attached to pcon of this pty */ - { - pcon_attached_to = get_minor (); - init_console_handler (true); - } + fhandler_console::need_invisible (); set_open_status (); return 1; @@ -1021,8 +685,7 @@ fhandler_pty_slave::close () if (!ForceCloseHandle (get_handle_cyg ())) termios_printf ("CloseHandle (get_handle_cyg ()<%p>), %E", get_handle_cyg ()); - if (!get_pseudo_console () && - (unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ())) + if ((unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ())) fhandler_console::free_console (); /* assumes that we are the last pty closer */ fhandler_pty_common::close (); if (!ForceCloseHandle (output_mutex)) @@ -1070,234 +733,17 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t) return ret; } -bool -fhandler_pty_slave::try_reattach_pcon (void) -{ - pid_restore = 0; - - /* Do not detach from the console because re-attaching will - fail if helper process is running as service account. */ - if (get_ttyp()->attach_pcon_in_fork) - return false; - if (pcon_attached_to >= 0 && - cygwin_shared->tty[pcon_attached_to]->attach_pcon_in_fork) - return false; - - pid_restore = - fhandler_console::get_console_process_id (GetCurrentProcessId (), - false); - /* If pid_restore is not set, give up. */ - if (!pid_restore) - return false; - - FreeConsole (); - if (!AttachConsole (get_helper_process_id ())) - { - system_printf ("pty%d: AttachConsole(helper=%d) failed. 0x%08lx", - get_minor (), get_helper_process_id (), GetLastError ()); - return false; - } - return true; -} - void -fhandler_pty_slave::restore_reattach_pcon (void) +fhandler_pty_slave::set_switch_to_pcon (void) { - if (pid_restore) + if (!get_ttyp ()->switch_to_pcon_in) { - FreeConsole (); - if (!AttachConsole (pid_restore)) - { - system_printf ("pty%d: AttachConsole(restore=%d) failed. 0x%08lx", - get_minor (), pid_restore, GetLastError ()); - pcon_attached_to = -1; - } - } - pid_restore = 0; -} - -/* This function requests transfering the input data from the input - pipe for cygwin apps to the other input pipe for native apps. */ -void -fhandler_pty_slave::pull_pcon_input (void) -{ - get_ttyp ()->req_transfer_input_to_pcon = true; - while (get_ttyp ()->req_transfer_input_to_pcon) - Sleep (1); -} - -void -fhandler_pty_slave::update_pcon_input_state (bool need_lock) -{ - if (need_lock) - WaitForSingleObject (input_mutex, INFINITE); - /* Flush input buffer if it is requested by master. - This happens if ^C is pressed in pseudo console side. */ - if (get_ttyp ()->req_flush_pcon_input) - { - FlushConsoleInputBuffer (get_handle ()); - get_ttyp ()->req_flush_pcon_input = false; - } - /* Peek console input buffer and update state. */ - INPUT_RECORD inp[INREC_SIZE]; - DWORD n; - BOOL (WINAPI *PeekFunc) - (HANDLE, PINPUT_RECORD, DWORD, LPDWORD); - PeekFunc = - PeekConsoleInputA_Orig ? : PeekConsoleInput; - PeekFunc (get_handle (), inp, INREC_SIZE, &n); - bool saw_accept = false; - bool pipe_empty = true; - while (n-- > 0) - if (inp[n].EventType == KEY_EVENT && inp[n].Event.KeyEvent.bKeyDown && - inp[n].Event.KeyEvent.uChar.AsciiChar) - { - pipe_empty = false; - char c = inp[n].Event.KeyEvent.uChar.AsciiChar; - const char sigs[] = { - (char) get_ttyp ()->ti.c_cc[VINTR], - (char) get_ttyp ()->ti.c_cc[VQUIT], - (char) get_ttyp ()->ti.c_cc[VSUSP], - }; - const char eols[] = { - (char) get_ttyp ()->ti.c_cc[VEOF], - (char) get_ttyp ()->ti.c_cc[VEOL], - (char) get_ttyp ()->ti.c_cc[VEOL2], - '\n', - '\r' - }; - if (is_line_input () && c && memchr (eols, c, sizeof (eols))) - saw_accept = true; - if ((get_ttyp ()->ti.c_lflag & ISIG) - && c && memchr (sigs, c, sizeof (sigs))) - saw_accept = true; - } - get_ttyp ()->pcon_in_empty = pipe_empty && !(ralen () > raixget ()); - if (!get_readahead_valid () && - (pipe_empty || (is_line_input () && !saw_accept)) && - get_ttyp ()->read_retval == 1 && bytes_available (n) && n == 0) - ResetEvent (input_available_event); - if (need_lock) - ReleaseMutex (input_mutex); -} - -int -fhandler_pty_slave::eat_readahead (int n) -{ - int oralen = ralen (); - if (n < 0) - n = ralen () - raixget (); - if (n > 0 && ralen () > raixget ()) - { - const char eols[] = { - (char) get_ttyp ()->ti.c_cc[VEOF], - (char) get_ttyp ()->ti.c_cc[VEOL], - (char) get_ttyp ()->ti.c_cc[VEOL2], - '\n' - }; - while (n > 0 && ralen () > raixget ()) - { - if (is_line_input () && rabuf ()[ralen ()-1] - && memchr (eols, rabuf ()[ralen ()-1], sizeof (eols))) - break; - -- n; - -- ralen (); - } - - /* If IUTF8 is set, the terminal is in UTF-8 mode. If so, we erase - a complete UTF-8 multibyte sequence on VERASE/VWERASE. Otherwise, - if we only erase a single byte, invalid unicode chars are left in - the input. */ - if (get_ttyp ()->ti.c_iflag & IUTF8) - while (ralen () > raixget () && - ((unsigned char) rabuf ()[ralen ()] & 0xc0) == 0x80) - --ralen (); - } - oralen = oralen - ralen (); - if (raixget () >= ralen ()) - raixget () = raixput () = ralen () = 0; - else if (raixput () > ralen ()) - raixput () = ralen (); - - return oralen; -} - -int -fhandler_pty_slave::get_readahead_into_buffer (char *buf, size_t buflen) -{ - int ch; - int copied_chars = 0; - - while (buflen) - if ((ch = get_readahead ()) < 0) - break; - else - { - const char eols[] = { - (char) get_ttyp ()->ti.c_cc[VEOF], - (char) get_ttyp ()->ti.c_cc[VEOL], - (char) get_ttyp ()->ti.c_cc[VEOL2], - '\n' - }; - buf[copied_chars++] = (unsigned char)(ch & 0xff); - buflen--; - if (is_line_input () && ch && memchr (eols, ch & 0xff, sizeof (eols))) - break; - } - - return copied_chars; -} - -bool -fhandler_pty_slave::get_readahead_valid (void) -{ - if (is_line_input ()) - { - const char eols[] = { - (char) get_ttyp ()->ti.c_cc[VEOF], - (char) get_ttyp ()->ti.c_cc[VEOL], - (char) get_ttyp ()->ti.c_cc[VEOL2], - '\n' - }; - for (size_t i=raixget (); i raixget (); -} - -void -fhandler_pty_slave::set_switch_to_pcon (int fd_set) -{ - if (fd < 0) - fd = fd_set; - acquire_output_mutex (INFINITE); - if (fd == 0 && !get_ttyp ()->switch_to_pcon_in) - { - pull_pcon_input (); + isHybrid = true; if (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid)) get_ttyp ()->pcon_pid = myself->pid; get_ttyp ()->switch_to_pcon_in = true; - if (isHybrid && !get_ttyp ()->switch_to_pcon_out) - { - get_ttyp ()->wait_pcon_fwd (); - get_ttyp ()->switch_to_pcon_out = true; - } } - else if ((fd == 1 || fd == 2) && !get_ttyp ()->switch_to_pcon_out) - { - get_ttyp ()->wait_pcon_fwd (); - if (get_ttyp ()->pcon_pid == 0 || - !pinfo (get_ttyp ()->pcon_pid)) - get_ttyp ()->pcon_pid = myself->pid; - get_ttyp ()->switch_to_pcon_out = true; - if (isHybrid) - get_ttyp ()->switch_to_pcon_in = true; - } - release_output_mutex (); } void @@ -1310,183 +756,10 @@ fhandler_pty_slave::reset_switch_to_pcon (void) return; if (isHybrid) return; - if (do_not_reset_switch_to_pcon) - return; - acquire_output_mutex (INFINITE); - if (get_ttyp ()->switch_to_pcon_out) - /* Wait for pty_master_fwd_thread() */ - get_ttyp ()->wait_pcon_fwd (); get_ttyp ()->pcon_pid = 0; get_ttyp ()->switch_to_pcon_in = false; - get_ttyp ()->switch_to_pcon_out = false; - release_output_mutex (); - init_console_handler (true); -} - -void -fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len, - bool is_echo) -{ - bool attached = - !!fhandler_console::get_console_process_id (get_helper_process_id (), true); - if (!attached && pcon_attached_to == get_minor ()) - { - for (DWORD t0 = GetTickCount (); GetTickCount () - t0 < 100; ) - { - Sleep (1); - attached = fhandler_console::get_console_process_id - (get_helper_process_id (), true); - if (attached) - break; - } - if (!attached) - { - system_printf ("pty%d: pcon_attach_to mismatch??????", get_minor ()); - return; - } - } - - /* If not attached to this pseudo console, try to attach temporarily. */ - pid_restore = 0; - if (pcon_attached_to != get_minor ()) - if (!try_reattach_pcon ()) - goto detach; - - char *buf; - size_t nlen; - DWORD origCP; - origCP = GetConsoleOutputCP (); - SetConsoleOutputCP (get_ttyp ()->term_code_page); - /* Just copy */ - buf = (char *) HeapAlloc (GetProcessHeap (), 0, len); - memcpy (buf, (char *)ptr, len); - nlen = len; - char *p0, *p1; - p0 = p1 = buf; - /* Remove alternate screen buffer drawing */ - while (p0 && p1) - { - if (!get_ttyp ()->screen_alternated) - { - /* Check switching to alternate screen buffer */ - p0 = (char *) memmem (p1, nlen - (p1-buf), "\033[?1049h", 8); - if (p0) - { - //p0 += 8; - get_ttyp ()->screen_alternated = true; - if (get_ttyp ()->switch_to_pcon_out) - do_not_reset_switch_to_pcon = true; - } - } - if (get_ttyp ()->screen_alternated) - { - /* Check switching to main screen buffer */ - p1 = (char *) memmem (p0, nlen - (p0-buf), "\033[?1049l", 8); - if (p1) - { - p1 += 8; - get_ttyp ()->screen_alternated = false; - do_not_reset_switch_to_pcon = false; - memmove (p0, p1, buf+nlen - p1); - nlen -= p1 - p0; - } - else - nlen = p0 - buf; - } - } - if (!nlen) /* Nothing to be synchronized */ - goto cleanup; - if (get_ttyp ()->switch_to_pcon_out && !is_echo) - goto cleanup; - /* Remove ESC sequence which returns results to console - input buffer. Without this, cursor position report - is put into the input buffer as a garbage. */ - /* Remove ESC sequence to report cursor position. */ - while ((p0 = (char *) memmem (buf, nlen, "\033[6n", 4))) - { - memmove (p0, p0+4, nlen - (p0+4 - buf)); - nlen -= 4; - } - /* Remove ESC sequence to report terminal identity. */ - while ((p0 = (char *) memmem (buf, nlen, "\033[0c", 4))) - { - memmove (p0, p0+4, nlen - (p0+4 - buf)); - nlen -= 4; - } - - /* If the ESC sequence ESC[?3h or ESC[?3l which clears console screen - buffer is pushed, set need_redraw_screen to trigger redraw screen. */ - p0 = buf; - while ((p0 = (char *) memmem (p0, nlen - (p0 - buf), "\033[?", 3))) - { - p0 += 3; - bool exist_arg_3 = false; - while (p0 < buf + nlen && (isdigit (*p0) || *p0 == ';')) - { - int arg = 0; - while (p0 < buf + nlen && isdigit (*p0)) - arg = arg * 10 + (*p0 ++) - '0'; - if (arg == 3) - exist_arg_3 = true; - if (p0 < buf + nlen && *p0 == ';') - p0 ++; - } - if (p0 < buf + nlen && exist_arg_3 && (*p0 == 'h' || *p0 == 'l')) - get_ttyp ()->need_redraw_screen = true; - p0 ++; - if (p0 >= buf + nlen) - break; - } - - int retry_count; - retry_count = 0; - DWORD dwMode, flags; - flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING; - while (!GetConsoleMode (get_output_handle (), &dwMode)) - { - termios_printf ("GetConsoleMode failed, %E"); - int errno_save = errno; - /* Re-open handles */ - this->open (0, 0); - /* Fix pseudo console window size */ - this->ioctl (TIOCSWINSZ, &get_ttyp ()->winsize); - if (errno != errno_save) - set_errno (errno_save); - if (++retry_count > 3) - goto cleanup; - } - if (!(get_ttyp ()->ti.c_oflag & OPOST) || - !(get_ttyp ()->ti.c_oflag & ONLCR)) - flags |= DISABLE_NEWLINE_AUTO_RETURN; - SetConsoleMode (get_output_handle (), dwMode | flags); - char *p; - p = buf; - DWORD wLen, written; - written = 0; - BOOL (WINAPI *WriteFunc) - (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED); - WriteFunc = WriteFile_Orig ? WriteFile_Orig : WriteFile; - while (written < nlen) - { - if (!WriteFunc (get_output_handle (), p, nlen - written, &wLen, NULL)) - { - termios_printf ("WriteFile failed, %E"); - break; - } - else - { - written += wLen; - p += wLen; - } - } - /* Detach from pseudo console and resume. */ - flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode (get_output_handle (), dwMode | flags); -cleanup: - SetConsoleOutputCP (origCP); - HeapFree (GetProcessHeap (), 0, buf); -detach: - restore_reattach_pcon (); + get_ttyp ()->h_pseudo_console = NULL; + get_ttyp ()->pcon_start = false; } ssize_t __stdcall @@ -1505,44 +778,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len) reset_switch_to_pcon (); acquire_output_mutex (INFINITE); - bool output_to_pcon = get_ttyp ()->switch_to_pcon_out; - release_output_mutex (); - - UINT target_code_page = output_to_pcon ? - GetConsoleOutputCP () : get_ttyp ()->term_code_page; - ssize_t nlen; - char *buf = convert_mb_str (target_code_page, (size_t *) &nlen, - get_ttyp ()->term_code_page, (const char *) ptr, len); - - /* If not attached to this pseudo console, try to attach temporarily. */ - pid_restore = 0; - bool fallback = false; - if (output_to_pcon && pcon_attached_to != get_minor ()) - if (!try_reattach_pcon ()) - fallback = true; - - if (output_to_pcon && !fallback && - (memmem (buf, nlen, "\033[6n", 4) || memmem (buf, nlen, "\033[0c", 4))) - { - get_ttyp ()->pcon_in_empty = false; - if (!is_line_input ()) - SetEvent (input_available_event); - } - - DWORD dwMode, flags; - flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (!(get_ttyp ()->ti.c_oflag & OPOST) || - !(get_ttyp ()->ti.c_oflag & ONLCR)) - flags |= DISABLE_NEWLINE_AUTO_RETURN; - if (output_to_pcon && !fallback) - { - GetConsoleMode (get_output_handle (), &dwMode); - SetConsoleMode (get_output_handle (), dwMode | flags); - } - HANDLE to = (output_to_pcon && !fallback) ? - get_output_handle () : get_output_handle_cyg (); - acquire_output_mutex (INFINITE); - if (!process_opost_output (to, buf, nlen, false)) + if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false)) { DWORD err = GetLastError (); termios_printf ("WriteFile failed, %E"); @@ -1557,20 +793,6 @@ fhandler_pty_slave::write (const void *ptr, size_t len) towrite = -1; } release_output_mutex (); - mb_str_free (buf); - flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (output_to_pcon && !fallback) - SetConsoleMode (get_output_handle (), dwMode | flags); - - restore_reattach_pcon (); - - /* Push slave output to pseudo console screen buffer */ - if (get_pseudo_console ()) - { - acquire_output_mutex (INFINITE); - push_to_pcon_screenbuffer ((char *)ptr, len, false); - release_output_mutex (); - } return towrite; } @@ -1584,14 +806,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); + return get_ttyp ()->pcon_start + || (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->mask_switch_to_pcon_in); } void __reg3 fhandler_pty_slave::read (void *ptr, size_t& len) { - char *ptr0 = (char *)ptr; ssize_t totalread = 0; int vmin = 0; int vtime = 0; /* Initialized to prevent -Wuninitialized warning */ @@ -1616,8 +837,6 @@ fhandler_pty_slave::read (void *ptr, size_t& len) mask_switch_to_pcon_in (true); reset_switch_to_pcon (); } - if (to_be_read_from_pcon ()) - update_pcon_input_state (true); if (is_nonblocking () || !ptr) /* Indicating tcflush(). */ time_to_wait = 0; @@ -1700,101 +919,24 @@ fhandler_pty_slave::read (void *ptr, size_t& len) is an infinitely blocking read, restart the loop. */ if (time_to_wait != INFINITE) { - if (!totalread) - { - set_sig_errno (EAGAIN); - totalread = -1; - } - goto out; - } - continue; - default: - termios_printf ("wait for input mutex failed, %E"); - if (!totalread) - { - __seterrno (); - totalread = -1; - } - goto out; - } - if (ptr && to_be_read_from_pcon ()) - { - if (get_readahead_valid ()) - { - ReleaseMutex (input_mutex); - totalread = get_readahead_into_buffer ((char *) ptr, len); - } - else - { - if (!try_reattach_pcon ()) - { - restore_reattach_pcon (); - goto do_read_cyg; - } - - DWORD dwMode; - GetConsoleMode (get_handle (), &dwMode); - DWORD flags = ENABLE_VIRTUAL_TERMINAL_INPUT; - if (dwMode != flags) - SetConsoleMode (get_handle (), flags); - /* Read get_handle() instad of get_handle_cyg() */ - BOOL (WINAPI *ReadFunc) - (HANDLE, LPVOID, DWORD, LPDWORD, LPVOID); - ReadFunc = ReadConsoleA_Orig ? ReadConsoleA_Orig : ReadConsoleA; - DWORD rlen; - readlen = MIN (len, sizeof (buf)); - if (!ReadFunc (get_handle (), buf, readlen, &rlen, NULL)) - { - termios_printf ("read failed, %E"); - SetConsoleMode (get_handle (), dwMode); - restore_reattach_pcon (); - ReleaseMutex (input_mutex); - set_errno (EIO); - totalread = -1; - goto out; - } - SetConsoleMode (get_handle (), dwMode); - restore_reattach_pcon (); - ReleaseMutex (input_mutex); - - ssize_t nlen; - char *nbuf = convert_mb_str (get_ttyp ()->term_code_page, - (size_t *) &nlen, GetConsoleCP (), buf, rlen); - - ssize_t ret; - line_edit_status res = - line_edit (nbuf, nlen, get_ttyp ()->ti, &ret); - - mb_str_free (nbuf); - - if (res == line_edit_input_done || res == line_edit_ok) - totalread = get_readahead_into_buffer ((char *) ptr, len); - else if (res > line_edit_signalled) + if (!totalread) { - set_sig_errno (EINTR); + set_sig_errno (EAGAIN); totalread = -1; } - else - { - update_pcon_input_state (true); - continue; - } + goto out; + } + continue; + default: + termios_printf ("wait for input mutex failed, %E"); + if (!totalread) + { + __seterrno (); + totalread = -1; } - - update_pcon_input_state (true); - mask_switch_to_pcon_in (false); goto out; } - if (!ptr && len == UINT_MAX && !get_ttyp ()->pcon_in_empty) - { - FlushConsoleInputBuffer (get_handle ()); - get_ttyp ()->pcon_in_empty = true; - DWORD n; - if (bytes_available (n) && n == 0) - ResetEvent (input_available_event); - } -do_read_cyg: if (!bytes_available (bytes_in_pipe)) { ReleaseMutex (input_mutex); @@ -1911,16 +1053,6 @@ do_read_cyg: out: termios_printf ("%d = read(%p, %lu)", totalread, ptr, len); len = (size_t) totalread; - /* Push slave read as echo to pseudo console screen buffer. */ - if (get_pseudo_console () && ptr0 && totalread > 0 && - (get_ttyp ()->ti.c_lflag & ECHO)) - { - acquire_output_mutex (INFINITE); - push_to_pcon_screenbuffer (ptr0, len, true); - if (get_ttyp ()->switch_to_pcon_out) - trigger_redraw_screen (); - release_output_mutex (); - } mask_switch_to_pcon_in (false); } @@ -2061,38 +1193,11 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) get_ttyp ()->winsize = get_ttyp ()->arg.winsize; break; case TIOCSWINSZ: - if (get_pseudo_console ()) - { - /* If not attached to this pseudo console, - try to attach temporarily. */ - pid_restore = 0; - if (pcon_attached_to != get_minor ()) - if (!try_reattach_pcon ()) - goto cleanup; - - COORD size; - size.X = ((struct winsize *) arg)->ws_col; - size.Y = ((struct winsize *) arg)->ws_row; - CONSOLE_SCREEN_BUFFER_INFO csbi; - if (GetConsoleScreenBufferInfo (get_output_handle (), &csbi)) - if (size.X == csbi.srWindow.Right - csbi.srWindow.Left + 1 && - size.Y == csbi.srWindow.Bottom - csbi.srWindow.Top + 1) - goto cleanup; - if (!SetConsoleScreenBufferSize (get_output_handle (), size)) - goto cleanup; - SMALL_RECT rect; - rect.Left = 0; - rect.Top = 0; - rect.Right = size.X-1; - rect.Bottom = size.Y-1; - SetConsoleWindowInfo (get_output_handle (), TRUE, &rect); -cleanup: - restore_reattach_pcon (); - } - if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) { + if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid) + resize_pseudo_console ((struct winsize *) arg); get_ttyp ()->arg.winsize = *(struct winsize *) arg; get_ttyp ()->winsize = *(struct winsize *) arg; get_ttyp ()->kill_pgrp (SIGWINCH); @@ -2378,6 +1483,27 @@ fhandler_pty_common::close () return 0; } +void +fhandler_pty_common::resize_pseudo_console (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 () { @@ -2393,7 +1519,6 @@ fhandler_pty_master::close () { OBJECT_BASIC_INFORMATION obi; NTSTATUS status; - pid_t master_pid_tmp = get_ttyp ()->master_pid; termios_printf ("closing from_master(%p)/from_master_cyg(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)", from_master, from_master_cyg, @@ -2438,30 +1563,6 @@ fhandler_pty_master::close () else if (obi.HandleCount == 1) { termios_printf ("Closing last master of pty%d", get_minor ()); - /* Close Pseudo Console */ - if (get_pseudo_console ()) - { - /* Terminate helper process */ - SetEvent (get_ttyp ()->h_helper_goodbye); - WaitForSingleObject (get_ttyp ()->h_helper_process, INFINITE); - CloseHandle (get_ttyp ()->h_helper_goodbye); - CloseHandle (get_ttyp ()->h_helper_process); - /* FIXME: Pseudo console can be accessed via its handle - only in the process which created it. What else can we do? */ - if (master_pid_tmp == myself->pid) - { - /* ClosePseudoConsole() seems to have a bug that one - internal handle remains opened. This causes handle leak. - This is a workaround for this problem. */ - HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_pseudo_console (); - HANDLE tmp = hp->hConHostProcess; - /* Release pseudo console */ - ClosePseudoConsole (get_pseudo_console ()); - CloseHandle (tmp); - } - get_ttyp ()->switch_to_pcon_in = false; - get_ttyp ()->switch_to_pcon_out = false; - } if (get_ttyp ()->getsid () > 0) kill (get_ttyp ()->getsid (), SIGHUP); SetEvent (input_available_event); @@ -2469,9 +1570,8 @@ fhandler_pty_master::close () if (!ForceCloseHandle (from_master)) termios_printf ("error closing from_master %p, %E", from_master); - if (from_master_cyg != from_master) /* Avoid double close. */ - if (!ForceCloseHandle (from_master_cyg)) - termios_printf ("error closing from_master_cyg %p, %E", from_master_cyg); + if (!ForceCloseHandle (from_master_cyg)) + termios_printf ("error closing from_master_cyg %p, %E", from_master_cyg); if (!ForceCloseHandle (to_master)) termios_printf ("error closing to_master %p, %E", to_master); from_master = to_master = NULL; @@ -2513,7 +1613,7 @@ 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 () && get_ttyp ()->h_pseudo_console) { size_t nlen; char *buf = convert_mb_str @@ -2521,40 +1621,50 @@ fhandler_pty_master::write (const void *ptr, size_t len) WaitForSingleObject (input_mutex, INFINITE); - if (memchr (buf, '\003', nlen)) /* ^C intr key in pcon */ - get_ttyp ()->req_flush_pcon_input = true; - DWORD wLen; - WriteFile (to_slave, buf, nlen, &wLen, NULL); - get_ttyp ()->pcon_in_empty = false; - - ReleaseMutex (input_mutex); - /* 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) + if (get_ttyp ()->pcon_start) { - tc ()->ti.c_lflag &= ~ECHO; - ti.c_lflag &= ~ECHO; - } - ti.c_lflag &= ~ISIG; - line_edit (buf, nlen, ti, &ret); - if (is_echo) - tc ()->ti.c_lflag |= ECHO; - get_ttyp ()->read_retval = 1; - - const char sigs[] = { - (char) ti.c_cc[VINTR], - (char) ti.c_cc[VQUIT], - (char) ti.c_cc[VSUSP], - }; - if (tc ()->ti.c_lflag & ISIG) - for (size_t i=0; ipcon_start = false; + } + else { - eat_readahead (-1); - SetEvent (input_available_event); + if (ixput + nlen < wpbuf_len) + for (size_t i=0; ipcon_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 ()->pcon_start = false; + } + ReleaseMutex (input_mutex); + mb_str_free (buf); + return len; } + } + + WriteFile (to_slave, buf, nlen, &wLen, NULL); + + ReleaseMutex (input_mutex); mb_str_free (buf); return len; @@ -2630,15 +1740,8 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) { - /* FIXME: Pseudo console can be accessed via its handle - only in the process which created it. What else can we do? */ - if (get_pseudo_console () && get_ttyp ()->master_pid == myself->pid) - { - COORD size; - size.X = ((struct winsize *) arg)->ws_col; - size.Y = ((struct winsize *) arg)->ws_row; - ResizePseudoConsole (get_pseudo_console (), size); - } + if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid) + resize_pseudo_console ((struct winsize *) arg); get_ttyp ()->winsize = *(struct winsize *) arg; get_ttyp ()->kill_pgrp (SIGWINCH); } @@ -2763,7 +1866,7 @@ get_locale_from_env (char *locale) strcpy (locale, env); } -static LCID +static void get_langinfo (char *locale_out, char *charset_out) { /* Get locale from environment */ @@ -2776,11 +1879,6 @@ get_langinfo (char *locale_out, char *charset_out) if (!locale) locale = "C"; - char tmp_locale[ENCODING_LEN + 1]; - char *ret = __set_locale_from_locale_alias (locale, tmp_locale); - if (ret) - locale = tmp_locale; - const char *charset; struct lc_ctype_T *lc_ctype = (struct lc_ctype_T *) loc.lc_cat[LC_CTYPE].ptr; if (!lc_ctype) @@ -2830,23 +1928,9 @@ get_langinfo (char *locale_out, char *charset_out) charset = "CP932"; } - wchar_t lc[ENCODING_LEN + 1]; - wchar_t *p; - mbstowcs (lc, locale, ENCODING_LEN); - p = wcschr (lc, L'.'); - if (p) - *p = L'\0'; - p = wcschr (lc, L'_'); - if (p) - *p = L'-'; - LCID lcid = LocaleNameToLCID (lc, 0); - if (!lcid && !strcmp (charset, "ASCII")) - return 0; - /* Set results */ strcpy (locale_out, new_locale); strcpy (charset_out, charset); - return lcid; } void @@ -2854,21 +1938,7 @@ fhandler_pty_slave::setup_locale (void) { char locale[ENCODING_LEN + 1] = "C"; char charset[ENCODING_LEN + 1] = "ASCII"; - LCID lcid = get_langinfo (locale, charset); - - /* Set console code page form locale */ - if (get_pseudo_console ()) - { - UINT code_page; - if (lcid == 0 || lcid == (LCID) -1) - code_page = 20127; /* ASCII */ - else if (!GetLocaleInfo (lcid, - LOCALE_IDEFAULTCODEPAGE | LOCALE_RETURN_NUMBER, - (char *) &code_page, sizeof (code_page))) - code_page = 20127; /* ASCII */ - SetConsoleCP (code_page); - SetConsoleOutputCP (code_page); - } + get_langinfo (locale, charset); /* Set terminal code page from locale */ /* This code is borrowed from mintty: charset.c */ @@ -2896,90 +1966,9 @@ fhandler_pty_slave::setup_locale (void) } } -void -fhandler_pty_slave::set_freeconsole_on_close (bool val) -{ - freeconsole_on_close = val; -} - -void -fhandler_pty_slave::trigger_redraw_screen (void) -{ - /* Forcibly redraw screen based on console screen buffer. */ - /* The following code triggers redrawing the screen. */ - CONSOLE_SCREEN_BUFFER_INFO sbi; - GetConsoleScreenBufferInfo (get_output_handle (), &sbi); - SMALL_RECT rect = {0, 0, - (SHORT) (sbi.dwSize.X -1), (SHORT) (sbi.dwSize.Y - 1)}; - COORD dest = {0, 0}; - CHAR_INFO fill = {' ', 0}; - ScrollConsoleScreenBuffer (get_output_handle (), &rect, NULL, dest, &fill); - get_ttyp ()->need_redraw_screen = false; -} - -void -fhandler_pty_slave::fixup_after_attach (bool native_maybe, int fd_set) -{ - if (fd < 0) - fd = fd_set; - if (get_pseudo_console ()) - { - if (fhandler_console::get_console_process_id (get_helper_process_id (), - true)) - if (pcon_attached_to != get_minor ()) - { - pcon_attached_to = get_minor (); - init_console_handler (true); - } - -#if 0 /* This is for debug only. */ - isHybrid = true; - get_ttyp ()->switch_to_pcon_in = true; - get_ttyp ()->switch_to_pcon_out = true; -#endif - - if (pcon_attached_to == get_minor () && native_maybe) - { - if (fd == 0) - { - pull_pcon_input (); - DWORD mode = - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT; - SetConsoleMode (get_handle (), mode); - if (get_ttyp ()->pcon_pid == 0 || - !pinfo (get_ttyp ()->pcon_pid)) - get_ttyp ()->pcon_pid = myself->pid; - get_ttyp ()->switch_to_pcon_in = true; - } - else if (fd == 1 || fd == 2) - { - DWORD mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; - SetConsoleMode (get_output_handle (), mode); - acquire_output_mutex (INFINITE); - if (!get_ttyp ()->switch_to_pcon_out) - get_ttyp ()->wait_pcon_fwd (); - if (get_ttyp ()->pcon_pid == 0 || - !pinfo (get_ttyp ()->pcon_pid)) - get_ttyp ()->pcon_pid = myself->pid; - get_ttyp ()->switch_to_pcon_out = true; - release_output_mutex (); - - if (get_ttyp ()->need_redraw_screen) - trigger_redraw_screen (); - } - init_console_handler (false); - } - else if (fd == 0 && native_maybe) - /* Read from unattached pseudo console cause freeze, - therefore, fallback to legacy pty. */ - set_handle (get_handle_cyg ()); - } -} - void fhandler_pty_slave::fixup_after_fork (HANDLE parent) { - fixup_after_attach (false, -1); // fork_fixup (parent, inuse, "inuse"); // fhandler_pty_common::fixup_after_fork (parent); report_tty_counts (this, "inherited", ""); @@ -2992,71 +1981,22 @@ fhandler_pty_slave::fixup_after_exec () if (!close_on_exec ()) fixup_after_fork (NULL); /* No parent handle required. */ - else if (get_pseudo_console ()) - { - int used = 0; - int attached = 0; - cygheap_fdenum cfd (false); - while (cfd.next () >= 0) - { - if (cfd->get_major () == DEV_PTYS_MAJOR || - cfd->get_major () == DEV_CONS_MAJOR) - used ++; - if (cfd->get_major () == DEV_PTYS_MAJOR && - cfd->get_minor () == pcon_attached_to) - attached ++; - } - - /* Call FreeConsole() if no tty is opened and the process - is attached to console corresponding to tty. This is - needed to make GNU screen and tmux work in Windows 10 - 1903. */ - if (attached == 1 && get_minor () == pcon_attached_to) - pcon_attached_to = -1; - if (used == 1 /* About to close this tty */) - { - init_console_handler (false); - free_attached_console (); - } - } /* Set locale */ if (get_ttyp ()->term_code_page == 0) setup_locale (); /* Hook Console API */ - if (get_pseudo_console ()) - { #define DO_HOOK(module, name) \ - if (!name##_Orig) \ - { \ - void *api = hook_api (module, #name, (void *) name##_Hooked); \ - name##_Orig = (__typeof__ (name) *) api; \ - /*if (api) system_printf (#name " hooked.");*/ \ - } - DO_HOOK (NULL, WriteFile); - DO_HOOK (NULL, WriteConsoleA); - DO_HOOK (NULL, WriteConsoleW); - DO_HOOK (NULL, ReadFile); - DO_HOOK (NULL, ReadConsoleA); - DO_HOOK (NULL, ReadConsoleW); - DO_HOOK (NULL, WriteConsoleOutputA); - DO_HOOK (NULL, WriteConsoleOutputW); - DO_HOOK (NULL, WriteConsoleOutputCharacterA); - DO_HOOK (NULL, WriteConsoleOutputCharacterW); - DO_HOOK (NULL, WriteConsoleOutputAttribute); - DO_HOOK (NULL, SetConsoleCursorPosition); - DO_HOOK (NULL, SetConsoleTextAttribute); - DO_HOOK (NULL, WriteConsoleInputA); - DO_HOOK (NULL, WriteConsoleInputW); - DO_HOOK (NULL, ReadConsoleInputA); - DO_HOOK (NULL, ReadConsoleInputW); - DO_HOOK (NULL, PeekConsoleInputA); - DO_HOOK (NULL, PeekConsoleInputW); - /* CreateProcess() is hooked for GDB etc. */ - DO_HOOK (NULL, CreateProcessA); - DO_HOOK (NULL, CreateProcessW); + if (!name##_Orig) \ + { \ + void *api = hook_api (module, #name, (void *) name##_Hooked); \ + name##_Orig = (__typeof__ (name) *) api; \ + /*if (api) system_printf (#name " hooked.");*/ \ } + /* CreateProcess() is hooked for GDB etc. */ + DO_HOOK (NULL, CreateProcessA); + DO_HOOK (NULL, CreateProcessW); } /* This thread function handles the master control pipe. It waits for a @@ -3204,27 +2144,6 @@ reply: return 0; } -void -fhandler_pty_master::transfer_input_to_pcon (void) -{ - WaitForSingleObject (input_mutex, INFINITE); - DWORD n; - size_t transfered = 0; - while (::bytes_available (n, from_master_cyg) && n) - { - char buf[1024]; - ReadFile (from_master_cyg, buf, sizeof (buf), &n, 0); - char *p = buf; - while ((p = (char *) memchr (p, '\n', n - (p - buf)))) - *p = '\r'; - if (WriteFile (to_slave, buf, n, &n, 0)) - transfered += n; - } - if (transfered) - get_ttyp ()->pcon_in_empty = false; - ReleaseMutex (input_mutex); -} - static DWORD WINAPI pty_master_thread (VOID *arg) { @@ -3240,23 +2159,7 @@ fhandler_pty_master::pty_master_fwd_thread () termios_printf ("Started."); for (;;) { - if (get_pseudo_console ()) - { - get_ttyp ()->pcon_last_time = GetTickCount (); - while (::bytes_available (rlen, from_slave) && rlen == 0) - { - /* Forcibly transfer input if it is requested by slave. - This happens when input data should be transfered - from the input pipe for cygwin apps to the input pipe - for native apps. */ - if (get_ttyp ()->req_transfer_input_to_pcon) - { - transfer_input_to_pcon (); - get_ttyp ()->req_transfer_input_to_pcon = false; - } - Sleep (1); - } - } + get_ttyp ()->pcon_last_time = GetTickCount (); if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL)) { termios_printf ("ReadFile for forwarding failed, %E"); @@ -3264,51 +2167,11 @@ fhandler_pty_master::pty_master_fwd_thread () } ssize_t wlen = rlen; char *ptr = outbuf; - if (get_pseudo_console ()) + if (get_ttyp ()->h_pseudo_console) { - /* Avoid duplicating slave output which is already sent to - to_master_cyg */ - if (!get_ttyp ()->switch_to_pcon_out) - continue; - - /* Avoid setting window title to "cygwin-console-helper.exe" */ + /* Remove CSI > Pm m */ int state = 0; int start_at = 0; - for (DWORD i=0; i Pm m */ - state = 0; - start_at = 0; for (DWORD i=0; ipty_master_fwd_thread (); } -/* If master process is running as service, attaching to - pseudo console should be done in fork. If attaching - is done in spawn for inetd or sshd, it fails because - the helper process is running as privileged user while - slave process is not. This function is used to determine - if the process is running as a srvice or not. */ -inline static bool -is_running_as_service (void) -{ - return check_token_membership (well_known_service_sid) - || cygheap->user.saved_sid () == well_known_system_sid; -} - -bool -fhandler_pty_master::setup_pseudoconsole () -{ - 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; - } - - /* Pseudo console supprot is realized using a tricky technic. - PTY need the pseudo console handles, however, they cannot - be retrieved by normal procedure. Therefore, run a helper - process in a pseudo console and get them from the helper. - Slave process will attach to the pseudo console in the - helper process using AttachConsole(). */ - COORD size = { - (SHORT) get_ttyp ()->winsize.ws_col, - (SHORT) get_ttyp ()->winsize.ws_row - }; - CreatePipe (&from_master, &to_slave, &sec_none, 0); - SetLastError (ERROR_SUCCESS); - HRESULT res = CreatePseudoConsole (size, from_master, to_master, - 0, &get_ttyp ()->h_pseudo_console); - if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND) - { - if (res != S_OK) - system_printf ("CreatePseudoConsole() failed. %08x\n", - GetLastError ()); - goto fallback; - } - - /* If master process is running as service, attaching to - pseudo console should be done in fork. If attaching - is done in spawn for inetd or sshd, it fails because - the helper process is running as privileged user while - slave process is not. */ - if (is_running_as_service ()) - get_ttyp ()->attach_pcon_in_fork = true; - - STARTUPINFOEXW si_helper; - HANDLE hello, goodbye; - HANDLE hr, hw; - PROCESS_INFORMATION pi_helper; - HANDLE hpConIn, hpConOut; - { - SIZE_T bytesRequired; - InitializeProcThreadAttributeList (NULL, 2, 0, &bytesRequired); - ZeroMemory (&si_helper, sizeof (si_helper)); - si_helper.StartupInfo.cb = sizeof (STARTUPINFOEXW); - si_helper.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) - HeapAlloc (GetProcessHeap (), 0, bytesRequired); - if (si_helper.lpAttributeList == NULL) - goto cleanup_pseudo_console; - if (!InitializeProcThreadAttributeList (si_helper.lpAttributeList, - 2, 0, &bytesRequired)) - goto cleanup_heap; - if (!UpdateProcThreadAttribute (si_helper.lpAttributeList, - 0, - PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, - get_ttyp ()->h_pseudo_console, - sizeof (get_ttyp ()->h_pseudo_console), - NULL, NULL)) - goto cleanup_heap; - /* Create events for start/stop helper process. */ - hello = CreateEvent (&sec_none, true, false, NULL); - goodbye = CreateEvent (&sec_none, true, false, NULL); - /* Create a pipe for receiving pseudo console handles */ - CreatePipe (&hr, &hw, &sec_none, 0); - /* Inherit only handles which are needed by helper. */ - HANDLE handles_to_inherit[] = {hello, goodbye, hw}; - if (!UpdateProcThreadAttribute (si_helper.lpAttributeList, - 0, - PROC_THREAD_ATTRIBUTE_HANDLE_LIST, - handles_to_inherit, - sizeof (handles_to_inherit), - NULL, NULL)) - goto cleanup_event_and_pipes; - /* Create helper process */ - WCHAR cmd[MAX_PATH]; - path_conv helper ("/bin/cygwin-console-helper.exe"); - size_t len = helper.get_wide_win32_path_len (); - helper.get_wide_win32_path (cmd); - __small_swprintf (cmd + len, L" %p %p %p", hello, goodbye, hw); - si_helper.StartupInfo.dwFlags = STARTF_USESTDHANDLES; - si_helper.StartupInfo.hStdInput = NULL; - si_helper.StartupInfo.hStdOutput = NULL; - si_helper.StartupInfo.hStdError = NULL; - if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none, - TRUE, EXTENDED_STARTUPINFO_PRESENT, - NULL, NULL, &si_helper.StartupInfo, &pi_helper)) - goto cleanup_event_and_pipes; - for (;;) - { - DWORD wait_result = WaitForSingleObject (hello, 500); - if (wait_result == WAIT_OBJECT_0) - break; - if (wait_result != WAIT_TIMEOUT) - goto cleanup_helper_process; - DWORD exit_code; - if (!GetExitCodeProcess(pi_helper.hProcess, &exit_code)) - goto cleanup_helper_process; - if (exit_code == STILL_ACTIVE) - continue; - if (exit_code != 0 || - WaitForSingleObject (hello, 500) != WAIT_OBJECT_0) - goto cleanup_helper_process; - break; - } - CloseHandle (hello); - CloseHandle (pi_helper.hThread); - /* Retrieve pseudo console handles */ - DWORD rLen; - char buf[64]; - if (!ReadFile (hr, buf, sizeof (buf), &rLen, NULL)) - goto cleanup_helper_process; - buf[rLen] = '\0'; - sscanf (buf, "StdHandles=%p,%p", &hpConIn, &hpConOut); - if (!DuplicateHandle (pi_helper.hProcess, hpConIn, - GetCurrentProcess (), &hpConIn, 0, - TRUE, DUPLICATE_SAME_ACCESS)) - goto cleanup_helper_process; - if (!DuplicateHandle (pi_helper.hProcess, hpConOut, - GetCurrentProcess (), &hpConOut, 0, - TRUE, DUPLICATE_SAME_ACCESS)) - goto cleanup_pcon_in; - CloseHandle (hr); - CloseHandle (hw); - /* Clean up */ - DeleteProcThreadAttributeList (si_helper.lpAttributeList); - HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList); - } - /* Setting information of stuffs regarding pseudo console */ - get_ttyp ()->h_helper_goodbye = goodbye; - get_ttyp ()->h_helper_process = pi_helper.hProcess; - get_ttyp ()->helper_process_id = pi_helper.dwProcessId; - CloseHandle (from_master); - CloseHandle (to_master); - from_master = hpConIn; - to_master = hpConOut; - ResizePseudoConsole (get_ttyp ()->h_pseudo_console, size); - return true; - -cleanup_pcon_in: - CloseHandle (hpConIn); -cleanup_helper_process: - SetEvent (goodbye); - WaitForSingleObject (pi_helper.hProcess, INFINITE); - CloseHandle (pi_helper.hProcess); - goto skip_close_hello; -cleanup_event_and_pipes: - CloseHandle (hello); -skip_close_hello: - CloseHandle (goodbye); - CloseHandle (hr); - CloseHandle (hw); -cleanup_heap: - HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList); -cleanup_pseudo_console: - { - HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; - HANDLE tmp = hp->hConHostProcess; - ClosePseudoConsole (get_pseudo_console ()); - CloseHandle (tmp); - } -fallback: - CloseHandle (from_master); - CloseHandle (to_slave); - from_master = from_master_cyg; - to_slave = NULL; - get_ttyp ()->h_pseudo_console = NULL; - return false; -} - bool fhandler_pty_master::setup () { @@ -3593,11 +2261,6 @@ fhandler_pty_master::setup () if (unit < 0) return false; - /* from_master should be used for pseudo console. - Just copy from_master_cyg here for the case that - pseudo console is not available. */ - from_master = from_master_cyg; - ProtectHandle1 (get_output_handle (), to_pty); tty& t = *cygwin_shared->tty[unit]; @@ -3696,15 +2359,21 @@ fhandler_pty_master::setup () goto err; } - t.winsize.ws_col = 80; - t.winsize.ws_row = 25; - - setup_pseudoconsole (); + __small_sprintf (pipename, "pty%d-to-slave", unit); + res = fhandler_pipe::create (&sec_none, &from_master, &to_slave, + fhandler_pty_common::pipesize, pipename, 0); + if (res) + { + errstr = "input pipe"; + goto err; + } t.set_from_master (from_master); t.set_from_master_cyg (from_master_cyg); t.set_to_master (to_master); t.set_to_master_cyg (to_master_cyg); + t.winsize.ws_col = 80; + t.winsize.ws_row = 25; t.master_pid = myself->pid; dev ().parse (DEV_PTYM_MAJOR, unit); @@ -3717,6 +2386,7 @@ fhandler_pty_master::setup () err: __seterrno (); close_maybe (from_slave); + close_maybe (to_slave); close_maybe (get_handle ()); close_maybe (get_output_handle ()); close_maybe (input_available_event); @@ -3776,9 +2446,6 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l { ssize_t towrite = len; BOOL res = TRUE; - BOOL (WINAPI *WriteFunc) - (HANDLE, LPCVOID, DWORD, LPDWORD, LPOVERLAPPED); - WriteFunc = WriteFile_Orig ? WriteFile_Orig : WriteFile; while (towrite) { if (!is_echo) @@ -3801,7 +2468,7 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l if (!(get_ttyp ()->ti.c_oflag & OPOST)) // raw output mode { DWORD n = MIN (OUT_BUFFER_SIZE, towrite); - res = WriteFunc (h, ptr, n, &n, NULL); + res = WriteFile (h, ptr, n, &n, NULL); if (!res) break; ptr = (char *) ptr + n; @@ -3851,7 +2518,7 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l break; } } - res = WriteFunc (h, outbuf, n, &n, NULL); + res = WriteFile (h, outbuf, n, &n, NULL); if (!res) break; ptr = (char *) ptr + rc; @@ -3861,3 +2528,123 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr, ssize_t& l len -= towrite; return res; } + +bool +fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si) +{ + fhandler_base *fh = (fhandler_pty_slave *) ::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 (ptys->get_ttyp ()->pcon_pid == 0 || + !pinfo (ptys->get_ttyp ()->pcon_pid)) + ptys->get_ttyp ()->pcon_pid = myself->pid; + } + 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_console); + 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_console, + sizeof (get_ttyp ()->h_pseudo_console), + 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 (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid)) + get_ttyp ()->pcon_pid = myself->pid; + + if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid == myself->pid) + { + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; + get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe; + } + get_ttyp ()->pcon_start = true; + return true; + +cleanup_heap: + HeapFree (GetProcessHeap (), 0, si->lpAttributeList); +cleanup_pseudo_console: + if (get_ttyp ()->h_pseudo_console) + { + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; + HANDLE tmp = hp->hConHostProcess; + ClosePseudoConsole (get_ttyp ()->h_pseudo_console); + CloseHandle (tmp); + } +fallback: + get_ttyp ()->h_pseudo_console = NULL; + return false; +} + +void +fhandler_pty_slave::close_pseudoconsole (void) +{ + if (get_ttyp ()->h_pseudo_console) + { + get_ttyp ()->wait_pcon_fwd (); + HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console; + HANDLE tmp = hp->hConHostProcess; + ClosePseudoConsole (get_ttyp ()->h_pseudo_console); + CloseHandle (tmp); + get_ttyp ()->h_pseudo_console = NULL; + get_ttyp ()->switch_to_pcon_in = false; + get_ttyp ()->pcon_pid = 0; + get_ttyp ()->pcon_start = false; + } +} diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 691d08137..6ca4b5f29 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -134,36 +134,6 @@ child_info::prefork (bool detached) int __stdcall frok::child (volatile char * volatile here) { - cygheap_fdenum cfd (false); - while (cfd.next () >= 0) - if (cfd->get_major () == DEV_PTYM_MAJOR) - { - fhandler_base *fh = cfd; - fhandler_pty_master *ptym = (fhandler_pty_master *) fh; - if (ptym->get_pseudo_console ()) - { - debug_printf ("found a PTY master %d: helper_PID=%d", - ptym->get_minor (), ptym->get_helper_process_id ()); - if (fhandler_console::get_console_process_id ( - ptym->get_helper_process_id (), true)) - /* Already attached */ - break; - else - { - if (ptym->attach_pcon_in_fork ()) - { - FreeConsole (); - if (!AttachConsole (ptym->get_helper_process_id ())) - /* Error */; - else - break; - } - } - } - } - extern void clear_pcon_attached_to (void); /* fhandler_tty.cc */ - clear_pcon_attached_to (); - HANDLE& hParent = ch.parent; sync_with_parent ("after longjmp", true); diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 3f3f33fb5..56e6390af 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1227,8 +1227,6 @@ peek_pty_slave (select_record *s, bool from_select) fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; ptys->reset_switch_to_pcon (); - if (ptys->to_be_read_from_pcon ()) - ptys->update_pcon_input_state (true); if (s->read_selected) { diff --git a/winsup/cygwin/smallprint.cc b/winsup/cygwin/smallprint.cc index 26d34d9e4..f208a057a 100644 --- a/winsup/cygwin/smallprint.cc +++ b/winsup/cygwin/smallprint.cc @@ -405,7 +405,6 @@ small_printf (const char *fmt, ...) count = __small_vsprintf (buf, fmt, ap); va_end (ap); - set_ishybrid_and_switch_to_pcon (GetStdHandle (STD_ERROR_HANDLE)); WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, count, &done, NULL); FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE)); } @@ -432,7 +431,6 @@ console_printf (const char *fmt, ...) count = __small_vsprintf (buf, fmt, ap); va_end (ap); - set_ishybrid_and_switch_to_pcon (console_handle); WriteFile (console_handle, buf, count, &done, NULL); FlushFileBuffers (console_handle); } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 3e8c8367a..a3071884e 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) { @@ -259,8 +277,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, { bool rc; int res = -1; - DWORD pid_restore = 0; - bool attach_to_console = false; pid_t ctty_pgid = 0; /* Search for CTTY and retrieve its PGID */ @@ -580,9 +596,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, PROCESS_QUERY_LIMITED_INFORMATION)) sa = &sec_none_nih; - /* 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,29 +605,11 @@ 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->get_pseudo_console ()) - { - DWORD helper_process_id = ptys->get_helper_process_id (); - debug_printf ("found a PTY slave %d: helper_PID=%d", - fh->get_minor (), helper_process_id); - if (fhandler_console::get_console_process_id - (helper_process_id, true)) - /* Already attached */ - attach_to_console = true; - else if (!attach_to_console) - { - FreeConsole (); - if (AttachConsole (helper_process_id)) - attach_to_console = true; - } - ptys->fixup_after_attach (!iscygwin (), fd); - if (mode == _P_OVERLAY) - ptys->set_freeconsole_on_close (iscygwin ()); - } + if (ptys_primary == NULL) + ptys_primary = ptys; } else if (fh && fh->get_major () == DEV_CONS_MAJOR) { - attach_to_console = true; fhandler_console *cons = (fhandler_console *) fh; if (wincap.has_con_24bit_colors () && !iscygwin ()) if (fd == 1 || fd == 2) @@ -632,6 +628,18 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (!iscygwin ()) init_console_handler (myself->ctty > 0); + bool enable_pcon = 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_pseudoconsole (&si_pcon)) + { + c_flags |= EXTENDED_STARTUPINFO_PRESENT; + si_tmp = &si_pcon.StartupInfo; + enable_pcon = 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 +668,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 +722,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 +735,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, CloseDesktop (hdsk); } } + if (enable_pcon) + { + DeleteProcThreadAttributeList (si_pcon.lpAttributeList); + HeapFree (GetProcessHeap (), 0, si_pcon.lpAttributeList); + } if (mode != _P_OVERLAY) SetHandleInformation (my_wr_proc_pipe, HANDLE_FLAG_INHERIT, @@ -897,6 +910,11 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, && WaitForSingleObject (pi.hProcess, 0) == WAIT_TIMEOUT) wait_for_myself (); } + if (enable_pcon) + { + WaitForSingleObject (pi.hProcess, INFINITE); + ptys_primary->close_pseudoconsole (); + } myself.exit (EXITCODE_NOSET); break; case _P_WAIT: @@ -930,21 +948,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (envblock) free (envblock); - if (attach_to_console && pid_restore) - { - FreeConsole (); - AttachConsole (pid_restore); - cygheap_fdenum cfd (false); - int fd; - while ((fd = cfd.next ()) >= 0) - if (cfd->get_major () == DEV_PTYS_MAJOR) - { - fhandler_pty_slave *ptys = - (fhandler_pty_slave *) (fhandler_base *) cfd; - ptys->fixup_after_attach (false, fd); - } - } - return (int) res; } diff --git a/winsup/cygwin/strace.cc b/winsup/cygwin/strace.cc index f0aef3a36..35f8a59ae 100644 --- a/winsup/cygwin/strace.cc +++ b/winsup/cygwin/strace.cc @@ -264,7 +264,6 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap if (category & _STRACE_SYSTEM) { DWORD done; - set_ishybrid_and_switch_to_pcon (GetStdHandle (STD_ERROR_HANDLE)); WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0); FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE)); /* Make sure that the message shows up on the screen, too, since this is @@ -276,7 +275,6 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap &sec_none, OPEN_EXISTING, 0, 0); if (h != INVALID_HANDLE_VALUE) { - set_ishybrid_and_switch_to_pcon (h); WriteFile (h, buf, len, &done, 0); CloseHandle (h); } diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index 4cb68f776..d60f27545 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -234,20 +234,14 @@ tty::init () was_opened = false; master_pid = 0; is_console = false; - attach_pcon_in_fork = false; - h_pseudo_console = NULL; column = 0; + h_pseudo_console = NULL; switch_to_pcon_in = false; - switch_to_pcon_out = false; - screen_alternated = false; mask_switch_to_pcon_in = false; pcon_pid = 0; term_code_page = 0; - need_redraw_screen = true; pcon_last_time = 0; - pcon_in_empty = true; - req_transfer_input_to_pcon = false; - req_flush_pcon_input = false; + pcon_start = false; } HANDLE @@ -293,16 +287,6 @@ tty_min::ttyname () return d.name (); } -void -tty::set_switch_to_pcon_out (bool v) -{ - if (switch_to_pcon_out != v) - { - wait_pcon_fwd (); - switch_to_pcon_out = v; - } -} - void tty::wait_pcon_fwd (void) { diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index 920e32b16..c491d3891 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -94,21 +94,13 @@ private: HANDLE _to_master; HANDLE _to_master_cyg; HPCON h_pseudo_console; - HANDLE h_helper_process; - DWORD helper_process_id; - HANDLE h_helper_goodbye; - bool attach_pcon_in_fork; + bool pcon_start; bool switch_to_pcon_in; - bool switch_to_pcon_out; - bool screen_alternated; bool mask_switch_to_pcon_in; pid_t pcon_pid; UINT term_code_page; - bool need_redraw_screen; DWORD pcon_last_time; - bool pcon_in_empty; - bool req_transfer_input_to_pcon; - bool req_flush_pcon_input; + HANDLE h_pcon_write_pipe; public: HANDLE from_master () const { return _from_master; } @@ -138,7 +130,6 @@ public: void set_master_ctl_closed () {master_pid = -1;} static void __stdcall create_master (int); static void __stdcall init_session (); - void set_switch_to_pcon_out (bool v); void wait_pcon_fwd (void); friend class fhandler_pty_common; friend class fhandler_pty_master; diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h index fff7d18f3..458fb2a23 100644 --- a/winsup/cygwin/winsup.h +++ b/winsup/cygwin/winsup.h @@ -222,9 +222,6 @@ void init_console_handler (bool); extern bool wsock_started; -/* PTY related */ -void set_ishybrid_and_switch_to_pcon (HANDLE h); - /* Printf type functions */ extern "C" void vapi_fatal (const char *, va_list ap) __attribute__ ((noreturn)); extern "C" void api_fatal (const char *, ...) __attribute__ ((noreturn)); -- 2.27.0