From c5af197c8e4c19eb24d80ea8b2ca87241d3440a9 Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Sat, 30 Mar 2019 16:24:38 +0900 Subject: [PATCH 3/3] Cygwin: pty: support pseudo console. --- winsup/cygwin/dtable.cc | 28 ++ winsup/cygwin/fhandler.h | 30 +- winsup/cygwin/fhandler_console.cc | 31 ++ winsup/cygwin/fhandler_tty.cc | 399 ++++++++++++++++++++++++-- winsup/cygwin/fork.cc | 20 ++ winsup/cygwin/select.cc | 3 + winsup/cygwin/spawn.cc | 32 +++ winsup/cygwin/tty.cc | 3 + winsup/cygwin/tty.h | 19 +- winsup/utils/cygwin-console-helper.cc | 14 +- 10 files changed, 540 insertions(+), 39 deletions(-) diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc index 636221a77..cc0a52cb1 100644 --- a/winsup/cygwin/dtable.cc +++ b/winsup/cygwin/dtable.cc @@ -68,6 +68,24 @@ set_std_handle (int fd) SetStdHandle (std_consts[fd], fh ? fh->get_output_handle () : NULL); } +#if 0 +static bool +is_running_as_service (void) +{ + DWORD dwSize = 0; + PTOKEN_GROUPS pGroupInfo; + tmp_pathbuf tp; + bool ret = false; + pGroupInfo = (PTOKEN_GROUPS) tp.w_get (); + NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo, + 2 * NT_MAX_PATH, &dwSize); + for (DWORD i=0; iGroupCount && !ret; i++) + if (well_known_service_sid == pGroupInfo->Groups[i].Sid) + ret = true; + return ret; +} +#endif + int dtable::extend (size_t howmuch, size_t min) { @@ -147,6 +165,11 @@ dtable::get_debugger_info () void dtable::stdio_init () { +#if 1 + if (wcsstr (myself->progname, L"mintty")) + return; +#endif + if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT)) { tty_min *t = cygwin_shared->tty.get_cttyp (); @@ -159,6 +182,11 @@ dtable::stdio_init () HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE); HANDLE err = GetStdHandle (STD_ERROR_HANDLE); +#if 0 + if (!is_running_as_service () && !in && !out && !err) + return; +#endif + init_std_file_from_handle (0, in); /* STD_ERROR_HANDLE has been observed to be the same as diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 0477888d7..b5a6bbcc7 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -1995,6 +1995,7 @@ 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 *) {} @@ -2066,6 +2067,19 @@ class fhandler_pty_common: public fhandler_termios return fh; } + bool attach_pcon_in_spawn (void) + { + return get_ttyp ()->attach_pcon_in_spawn; + } + DWORD getHelperProcessId (void) + { + return get_ttyp ()->HelperProcessId; + } + HPCON getPseudoConsole (void) + { + return get_ttyp ()->hPseudoConsole; + } + protected: BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len, bool is_echo); }; @@ -2073,7 +2087,8 @@ class fhandler_pty_common: public fhandler_termios class fhandler_pty_slave: public fhandler_pty_common { HANDLE inuse; // used to indicate that a tty is in use - HANDLE output_handle_cyg; + HANDLE output_handle_cyg, io_handle_cyg; + bool pcon_attached; /* Helper functions for fchmod and fchown. */ bool fch_open_handles (bool chown); @@ -2086,6 +2101,8 @@ class fhandler_pty_slave: public fhandler_pty_common void set_output_handle_cyg (HANDLE h) { output_handle_cyg = h; } HANDLE& get_output_handle_cyg () { return output_handle_cyg; } + void set_handle_cyg (HANDLE h) { io_handle_cyg = h; } + HANDLE& get_handle_cyg () { return io_handle_cyg; } int open (int flags, mode_t mode = 0); void open_setup (int flags); @@ -2126,6 +2143,12 @@ class fhandler_pty_slave: public fhandler_pty_common copyto (fh); return fh; } + void reset_switch_to_pcon (void) + { + if (get_ttyp ()->switch_to_pcon == 2) + Sleep (20); + get_ttyp ()->switch_to_pcon = 0; + } }; #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) @@ -2134,15 +2157,14 @@ class fhandler_pty_master: public fhandler_pty_common int pktmode; // non-zero if pty in a packet mode. HANDLE master_ctl; // Control socket for handle duplication cygthread *master_thread; // Master control thread - HANDLE from_master, to_master; + HANDLE from_master, to_master, from_slave, to_slave; HANDLE echo_r, echo_w; DWORD dwProcessId; // Owner of master handles - HANDLE io_handle_cyg, to_master_cyg; + HANDLE to_master_cyg, from_master_cyg; cygthread *master_fwd_thread; // Master forwarding thread public: HANDLE get_echo_handle () const { return echo_r; } - HANDLE& get_handle_cyg () { return io_handle_cyg; } /* Constructor */ fhandler_pty_master (int); diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc index f7b50d0a8..bcd71a4d1 100644 --- a/winsup/cygwin/fhandler_console.cc +++ b/winsup/cygwin/fhandler_console.cc @@ -1011,6 +1011,18 @@ fhandler_console::close () CloseHandle (get_handle ()); CloseHandle (get_output_handle ()); + + 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->getHelperProcessId (), true)) + return 0; + } + if (!have_execed) free_console (); return 0; @@ -3040,6 +3052,25 @@ fhandler_console::need_invisible () return b; } +DWORD +fhandler_console::get_console_process_id (DWORD pid, bool match) +{ + DWORD tmp; + int num = GetConsoleProcessList (&tmp, 1); + DWORD *list = (DWORD *) + HeapAlloc (GetProcessHeap (), 0, num * sizeof (DWORD)); + num = GetConsoleProcessList (list, num); + tmp = 0; + for (int i=0; i #include "cygwait.h" +#include "tls_pbuf.h" + +#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE +#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016 +#endif + +extern "C" int sscanf (const char *, const char *, ...); +extern "C" int ttyname_r(int, char*, size_t); #define close_maybe(h) \ do { \ @@ -39,6 +47,7 @@ struct pipe_request { struct pipe_reply { HANDLE from_master; + HANDLE from_master_cyg; HANDLE to_master; HANDLE to_master_cyg; DWORD error; @@ -67,7 +76,7 @@ bytes_available (DWORD& n, HANDLE h) bool fhandler_pty_common::bytes_available (DWORD &n) { - return ::bytes_available (n, get_handle ()); + return ::bytes_available (n, get_handle_cyg ()); } #ifdef DEBUGGING @@ -235,7 +244,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on /* Check echo pipe first. */ if (::bytes_available (echo_cnt, echo_r) && echo_cnt > 0) break; - if (!::bytes_available (n, get_handle_cyg ())) + if (!bytes_available (n)) goto err; if (n) break; @@ -296,7 +305,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on goto err; } } - else if (!ReadFile (get_handle_cyg (), outbuf, rlen, &n, NULL)) + else if (!ReadFile (get_handle (), outbuf, rlen, &n, NULL)) { termios_printf ("ReadFile failed, %E"); goto err; @@ -331,7 +340,8 @@ out: /* pty slave stuff */ fhandler_pty_slave::fhandler_pty_slave (int unit) - : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL) + : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL), + pcon_attached (false) { if (unit >= 0) dev ().parse (DEV_PTYS_MAJOR, unit); @@ -340,11 +350,14 @@ fhandler_pty_slave::fhandler_pty_slave (int unit) int fhandler_pty_slave::open (int flags, mode_t) { - HANDLE pty_owner, from_master_local, to_master_local, to_master_cyg_local; + HANDLE pty_owner; + HANDLE from_master_local, from_master_cyg_local; + HANDLE to_master_local, to_master_cyg_local; HANDLE *handles[] = { &from_master_local, &input_available_event, &input_mutex, &inuse, &output_mutex, &to_master_local, &pty_owner, &to_master_cyg_local, + &from_master_cyg_local, NULL }; @@ -396,7 +409,7 @@ fhandler_pty_slave::open (int flags, mode_t) release_output_mutex (); } - if (!get_ttyp ()->from_master () || + if (!get_ttyp ()->from_master () || !get_ttyp ()->from_master_cyg () || !get_ttyp ()->to_master () || !get_ttyp ()->to_master_cyg ()) { errmsg = "pty handles have been closed"; @@ -441,6 +454,15 @@ fhandler_pty_slave::open (int flags, mode_t) __seterrno (); goto err_no_msg; } + if (!DuplicateHandle (pty_owner, get_ttyp ()->from_master_cyg (), + GetCurrentProcess (), &from_master_cyg_local, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + { + termios_printf ("can't duplicate input from %u/%p, %E", + get_ttyp ()->master_pid, get_ttyp ()->from_master_cyg ()); + __seterrno (); + goto err_no_msg; + } if (!DuplicateHandle (pty_owner, get_ttyp ()->to_master (), GetCurrentProcess (), &to_master_local, 0, TRUE, DUPLICATE_SAME_ACCESS)) @@ -474,9 +496,11 @@ fhandler_pty_slave::open (int flags, mode_t) goto err; } from_master_local = repl.from_master; + from_master_cyg_local = repl.from_master_cyg; to_master_local = repl.to_master; to_master_cyg_local = repl.to_master_cyg; - if (!from_master_local || !to_master_local || !to_master_cyg_local) + if (!from_master_local || !from_master_cyg_local || + !to_master_local || !to_master_cyg_local) { SetLastError (repl.error); errmsg = "error duplicating pipes, %E"; @@ -484,22 +508,28 @@ fhandler_pty_slave::open (int flags, mode_t) } } VerifyHandle (from_master_local); + VerifyHandle (from_master_cyg_local); VerifyHandle (to_master_local); VerifyHandle (to_master_cyg_local); termios_printf ("duplicated from_master %p->%p from pty_owner", get_ttyp ()->from_master (), from_master_local); + termios_printf ("duplicated from_master_cyg %p->%p from pty_owner", + get_ttyp ()->from_master_cyg (), from_master_cyg_local); termios_printf ("duplicated to_master %p->%p from pty_owner", get_ttyp ()->to_master (), to_master_local); termios_printf ("duplicated to_master_cyg %p->%p from pty_owner", get_ttyp ()->to_master_cyg (), to_master_cyg_local); set_handle (from_master_local); + set_handle_cyg (from_master_cyg_local); set_output_handle (to_master_local); set_output_handle_cyg (to_master_cyg_local); fhandler_console::need_invisible (); set_open_status (); + if (pcon_attached && get_ttyp ()->switch_to_pcon == 0) + get_ttyp ()->switch_to_pcon = 1; return 1; err: @@ -548,6 +578,9 @@ fhandler_pty_slave::close () if (!ForceCloseHandle (get_output_handle_cyg ())) termios_printf ("CloseHandle (get_output_handle_cyg ()<%p>), %E", get_output_handle_cyg ()); + if (!ForceCloseHandle (get_handle_cyg ())) + termios_printf ("CloseHandle (get_handle_cyg ()<%p>), %E", + get_handle_cyg ()); 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 (); @@ -609,6 +642,20 @@ fhandler_pty_slave::write (const void *ptr, size_t len) push_process_state process_state (PID_TTYOU); + reset_switch_to_pcon (); + + if (pcon_attached) + { /* Push slave output to pseudo console screen buffer */ + DWORD wLen, written = 0; + char *p = (char *) ptr; + while (written < len) + { + WriteFile (get_output_handle (), p, len - written, &wLen, NULL); + written += wLen; + p += wLen; + } + } + if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false)) { DWORD err = GetLastError (); @@ -644,10 +691,12 @@ fhandler_pty_slave::read (void *ptr, size_t& len) return; } - termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle ()); + termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle_cyg ()); push_process_state process_state (PID_TTYIN); + reset_switch_to_pcon (); + if (is_nonblocking () || !ptr) /* Indicating tcflush(). */ time_to_wait = 0; else if ((get_ttyp ()->ti.c_lflag & ICANON)) @@ -777,7 +826,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len) if (readlen) { termios_printf ("reading %lu bytes (vtime %d)", readlen, vtime); - if (!ReadFile (get_handle (), buf, readlen, &n, NULL)) + if (!ReadFile (get_handle_cyg (), buf, readlen, &n, NULL)) { termios_printf ("read failed, %E"); ReleaseMutex (input_mutex); @@ -890,6 +939,7 @@ fhandler_pty_master::dup (fhandler_base *child, int) int fhandler_pty_slave::tcgetattr (struct termios *t) { + reset_switch_to_pcon (); *t = get_ttyp ()->ti; return 0; } @@ -897,6 +947,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t) int fhandler_pty_slave::tcsetattr (int, const struct termios *t) { + reset_switch_to_pcon (); acquire_output_mutex (INFINITE); get_ttyp ()->ti = *t; release_output_mutex (); @@ -908,8 +959,9 @@ fhandler_pty_slave::tcflush (int queue) { int ret = 0; - termios_printf ("tcflush(%d) handle %p", queue, get_handle ()); + termios_printf ("tcflush(%d) handle %p", queue, get_handle_cyg ()); + reset_switch_to_pcon (); if (queue == TCIFLUSH || queue == TCIOFLUSH) { size_t len = UINT_MAX; @@ -929,6 +981,7 @@ int fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) { termios_printf ("ioctl (%x)", cmd); + reset_switch_to_pcon (); int res = fhandler_termios::ioctl (cmd, arg); if (res <= 0) return res; @@ -995,6 +1048,55 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg) get_ttyp ()->winsize = get_ttyp ()->arg.winsize; break; case TIOCSWINSZ: + HPCON hPseudoConsole = get_ttyp ()->hPseudoConsole; + COORD size; + size.X = ((struct winsize *) arg)->ws_col; + size.Y = ((struct winsize *) arg)->ws_row; +#if 0 + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (!GetConsoleScreenBufferInfo (get_output_handle (), &csbi)) + goto cyg_setsize; + if (size.X == csbi.srWindow.Right - csbi.srWindow.Left + 1 && + size.Y == csbi.srWindow.Bottom - csbi.srWindow.Top + 1) + goto cyg_setsize; +#endif + if (hPseudoConsole) + { + if (myself->pid == get_ttyp ()->master_pid) + { + HMODULE hModule = GetModuleHandle ("kernel32.dll"); + FARPROC func = GetProcAddress (hModule, "ResizePseudoConsole"); + HRESULT (WINAPI *ResizePseudoConsole) (HPCON, COORD) = NULL; + ResizePseudoConsole = (HRESULT (WINAPI *) (HPCON, COORD)) func; + if (!ResizePseudoConsole (hPseudoConsole, size)) + goto cyg_setsize; + } + else + { + DWORD pidRestore = + fhandler_console::get_console_process_id (GetCurrentProcessId (), false); + if (!pcon_attached) + { + FreeConsole (); + AttachConsole (getHelperProcessId ()); + } + SMALL_RECT rect; + if (!SetConsoleScreenBufferSize (get_output_handle (), size)) + goto detach_pcon; + rect.Left = 0; + rect.Top = 0; + rect.Right = size.X-1; + rect.Bottom = size.Y-1; + SetConsoleWindowInfo (get_output_handle (), TRUE, &rect); +detach_pcon: + if (!pcon_attached && pidRestore) + { + FreeConsole (); + AttachConsole (pidRestore); + } + } + } +cyg_setsize: if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) { @@ -1228,8 +1330,9 @@ errout: fhandler_pty_master::fhandler_pty_master (int unit) : fhandler_pty_common (), pktmode (0), master_ctl (NULL), master_thread (NULL), from_master (NULL), to_master (NULL), - echo_r (NULL), echo_w (NULL), dwProcessId (0), - io_handle_cyg (NULL), to_master_cyg (NULL), master_fwd_thread (NULL) + from_slave (NULL), to_slave (NULL), echo_r (NULL), echo_w (NULL), + dwProcessId (0), to_master_cyg (NULL), from_master_cyg (NULL), + master_fwd_thread (NULL) { if (unit >= 0) dev ().parse (DEV_PTYM_MAJOR, unit); @@ -1285,7 +1388,8 @@ fhandler_pty_master::cleanup () { report_tty_counts (this, "closing master", ""); if (archetype) - from_master = to_master = to_master_cyg = NULL; + from_master = from_master_cyg = + to_master = to_master_cyg = from_slave = to_slave = NULL; fhandler_base::cleanup (); } @@ -1295,8 +1399,9 @@ fhandler_pty_master::close () OBJECT_BASIC_INFORMATION obi; NTSTATUS status; - termios_printf ("closing from_master(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)", - from_master, to_master, to_master_cyg, dwProcessId); + 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, + to_master, to_master_cyg, dwProcessId); if (cygwin_finished_initializing) { if (master_ctl && get_ttyp ()->master_pid == myself->pid) @@ -1320,6 +1425,20 @@ fhandler_pty_master::close () } release_output_mutex (); master_fwd_thread->terminate_thread (); + + /* Pseudo Console Support */ + if (get_ttyp ()->hPseudoConsole) + { + SetEvent (get_ttyp ()->hHelperGoodbye); + WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE); + HMODULE hModule = GetModuleHandle ("kernel32.dll"); + FARPROC func = GetProcAddress (hModule, "ClosePseudoConsole"); + VOID (WINAPI *ClosePseudoConsole) (HPCON) = NULL; + ClosePseudoConsole = (VOID (WINAPI *) (HPCON)) func; + ClosePseudoConsole (get_ttyp ()->hPseudoConsole); + get_ttyp ()->hPseudoConsole = NULL; + get_ttyp ()->switch_to_pcon = 0; + } } } @@ -1344,17 +1463,22 @@ fhandler_pty_master::close () if (!ForceCloseHandle (from_master)) termios_printf ("error closing from_master %p, %E", from_master); + 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; - if (!ForceCloseHandle (get_handle_cyg ())) - termios_printf ("error closing io_handle_cyg %p, %E", get_handle_cyg ()); + if (!ForceCloseHandle (from_slave)) + termios_printf ("error closing from_slave %p, %E", from_slave); + from_slave = NULL; if (!ForceCloseHandle (to_master_cyg)) termios_printf ("error closing to_master_cyg %p, %E", to_master_cyg); - get_handle_cyg () = to_master_cyg = NULL; + to_master_cyg = from_master_cyg = NULL; ForceCloseHandle (echo_r); ForceCloseHandle (echo_w); echo_r = echo_w = NULL; + ForceCloseHandle (to_slave); + to_slave = NULL; if (have_execed || get_ttyp ()->master_pid != myself->pid) termios_printf ("not clearing: %d, master_pid %d", have_execed, get_ttyp ()->master_pid); @@ -1376,6 +1500,14 @@ fhandler_pty_master::write (const void *ptr, size_t len) return (ssize_t) bg; push_process_state process_state (PID_TTYOU); + + if (get_ttyp ()->switch_to_pcon == 2) + { + DWORD wLen; + WriteFile (to_slave, ptr, len, &wLen, NULL); + return wLen; + } + line_edit_status status = line_edit (p++, len, ti, &ret); if (status > line_edit_signalled && status != line_edit_pipe_full) ret = -1; @@ -1443,6 +1575,17 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) *(struct winsize *) arg = get_ttyp ()->winsize; break; case TIOCSWINSZ: + if (get_ttyp ()->hPseudoConsole) + { + HMODULE hModule = GetModuleHandle ("kernel32.dll"); + FARPROC func = GetProcAddress (hModule, "ResizePseudoConsole"); + HRESULT (WINAPI *ResizePseudoConsole) (HPCON, COORD) = NULL; + ResizePseudoConsole = (HRESULT (WINAPI *) (HPCON, COORD)) func; + COORD size; + size.X = ((struct winsize *) arg)->ws_col; + size.Y = ((struct winsize *) arg)->ws_row; + ResizePseudoConsole (get_ttyp ()->hPseudoConsole, size); + } if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) { @@ -1458,7 +1601,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg) case FIONREAD: { DWORD n; - if (!::bytes_available (n, get_handle_cyg ())) + if (!bytes_available (n)) { set_errno (EINVAL); return -1; @@ -1497,14 +1640,22 @@ fhandler_pty_common::set_close_on_exec (bool val) void fhandler_pty_slave::fixup_after_fork (HANDLE parent) { + if (fhandler_console::get_console_process_id (get_ttyp ()->HelperProcessId, true)) + pcon_attached = true; // fork_fixup (parent, inuse, "inuse"); // fhandler_pty_common::fixup_after_fork (parent); + if (pcon_attached && get_ttyp ()->switch_to_pcon == 0) + get_ttyp ()->switch_to_pcon = 1; report_tty_counts (this, "inherited", ""); } void fhandler_pty_slave::fixup_after_exec () { + if (fhandler_console::get_console_process_id (get_ttyp ()->HelperProcessId, true)) + pcon_attached = true; + if (pcon_attached && get_ttyp ()->switch_to_pcon == 0) + get_ttyp ()->switch_to_pcon = 1; if (!close_on_exec ()) fixup_after_fork (NULL); } @@ -1544,7 +1695,7 @@ fhandler_pty_master::pty_master_thread () while (!exit && (ConnectNamedPipe (master_ctl, NULL) || GetLastError () == ERROR_PIPE_CONNECTED)) { - pipe_reply repl = { NULL, NULL, 0 }; + pipe_reply repl = { NULL, NULL, NULL, 0 }; bool deimp = false; NTSTATUS allow = STATUS_ACCESS_DENIED; ACCESS_MASK access = EVENT_MODIFY_STATE; @@ -1614,6 +1765,13 @@ fhandler_pty_master::pty_master_thread () termios_printf ("DuplicateHandle (from_master), %E"); goto reply; } + if (!DuplicateHandle (GetCurrentProcess (), from_master_cyg, + client, &repl.from_master_cyg, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + { + termios_printf ("DuplicateHandle (from_master_cyg), %E"); + goto reply; + } if (!DuplicateHandle (GetCurrentProcess (), to_master, client, &repl.to_master, 0, TRUE, DUPLICATE_SAME_ACCESS)) @@ -1659,23 +1817,80 @@ fhandler_pty_master::pty_master_fwd_thread () DWORD rlen; char outbuf[OUT_BUFFER_SIZE]; - termios_printf("Started."); + termios_printf ("Started."); for (;;) { - if (!ReadFile (get_handle (), outbuf, sizeof outbuf, &rlen, NULL)) + if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL)) { termios_printf ("ReadFile for forwarding failed, %E"); break; } ssize_t wlen = rlen; + char *ptr = outbuf; + if (get_ttyp ()->hPseudoConsole) + { + if (get_ttyp ()->switch_to_pcon == 1) + get_ttyp ()->switch_to_pcon = 2; +#if 1 + /* Avoid setting window title to "cygwin-console-helper.exe" */ + int state = 0; + int start_at = 0; + for (DWORD i=0; iswitch_to_pcon != 2) + continue; + + DWORD written; + while (rlen>0) + { + if (!WriteFile (to_master_cyg, ptr, wlen, &written, NULL)) + { + termios_printf ("WriteFile for forwarding failed, %E"); + break; + } + ptr += written; + wlen = (rlen -= written); + } + continue; + } while (rlen>0) { - if (!process_opost_output (to_master_cyg, outbuf, wlen, false)) + if (!process_opost_output (to_master_cyg, ptr, wlen, false)) { termios_printf ("WriteFile for forwarding failed, %E"); break; } - rlen -= wlen; + ptr += wlen; + wlen = (rlen -= wlen); } } return 0; @@ -1687,6 +1902,22 @@ pty_master_fwd_thread (VOID *arg) return ((fhandler_pty_master *) arg)->pty_master_fwd_thread (); } +static bool +is_running_as_service (void) +{ + DWORD dwSize = 0; + PTOKEN_GROUPS pGroupInfo; + tmp_pathbuf tp; + bool ret = false; + pGroupInfo = (PTOKEN_GROUPS) tp.w_get (); + NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo, + 2 * NT_MAX_PATH, &dwSize); + for (DWORD i=0; iGroupCount && !ret; i++) + if (well_known_service_sid == pGroupInfo->Groups[i].Sid) + ret = true; + return ret; +} + bool fhandler_pty_master::setup () { @@ -1695,10 +1926,14 @@ fhandler_pty_master::setup () SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE }; /* Find an unallocated pty to use. */ - int unit = cygwin_shared->tty.allocate (from_master, get_output_handle ()); + int unit = cygwin_shared->tty.allocate (from_master_cyg, get_output_handle ()); if (unit < 0) return false; + /* from_master should be used for pseudo console. */ + /* FIXME: Currently just copy from_master_cyg */ + from_master = from_master_cyg; + ProtectHandle1 (get_output_handle (), to_pty); tty& t = *cygwin_shared->tty[unit]; @@ -1715,7 +1950,7 @@ fhandler_pty_master::setup () char pipename[sizeof("ptyNNNN-to-master-cyg")]; __small_sprintf (pipename, "pty%d-to-master", unit); - res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master, + res = fhandler_pipe::create (&sec_none, &from_slave, &to_master, fhandler_pty_common::pipesize, pipename, 0); if (res) { @@ -1724,7 +1959,7 @@ fhandler_pty_master::setup () } __small_sprintf (pipename, "pty%d-to-master-cyg", unit); - res = fhandler_pipe::create (&sec_none, &get_handle_cyg (), &to_master_cyg, + res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master_cyg, fhandler_pty_common::pipesize, pipename, 0); if (res) { @@ -1732,7 +1967,7 @@ fhandler_pty_master::setup () goto err; } - ProtectHandle1 (get_handle (), from_pty); + ProtectHandle1 (from_slave, from_pty); __small_sprintf (pipename, "pty%d-echoloop", unit); res = fhandler_pipe::create (&sec_none, &echo_r, &echo_w, @@ -1797,7 +2032,101 @@ fhandler_pty_master::setup () goto err; } + { /* Pseudo Console Support */ + HMODULE hModule = GetModuleHandle ("kernel32.dll"); + FARPROC func = GetProcAddress (hModule, "CreatePseudoConsole"); + HRESULT (WINAPI *CreatePseudoConsole) + (COORD, HANDLE, HANDLE, DWORD, HPCON *) = NULL; + if (func) + { + CreatePseudoConsole = + (HRESULT (WINAPI *) (COORD, HANDLE, HANDLE, DWORD, HPCON *)) func; + COORD size = {80, 25}; + CreatePipe (&from_master, &to_slave, &sec_none, 0); + HRESULT res = CreatePseudoConsole (size, from_master, to_master, + 0, &t.hPseudoConsole); + if (res != S_OK) + { + system_printf ("CreatePseudoConsole() failed. %08x\n", + GetLastError()); + t.hPseudoConsole = NULL; + } + else + { +#if 0 + char tty[32]; + for (int fd=0; fd<3; fd++) + { + ttyname_r (fd, tty, sizeof(tty)); + if (strncmp (tty, "/dev/cons", 9) == 0) + t.attach_pcon_in_spawn = true; + } +#else + if (!is_running_as_service ()) + t.attach_pcon_in_spawn = true; +#endif + SIZE_T bytesRequired; + InitializeProcThreadAttributeList (NULL, 1, 0, &bytesRequired); + STARTUPINFOEXW si_helper; + ZeroMemory (&si_helper, sizeof (si_helper)); + si_helper.StartupInfo.cb = sizeof (STARTUPINFOEXW); + si_helper.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST) + HeapAlloc (GetProcessHeap (), 0, bytesRequired); + InitializeProcThreadAttributeList (si_helper.lpAttributeList, + 1, 0, &bytesRequired); + UpdateProcThreadAttribute (si_helper.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + t.hPseudoConsole, + sizeof (t.hPseudoConsole), + NULL, NULL); + HANDLE hello = CreateEvent (&sec_none, true, false, NULL); + HANDLE goodbye = CreateEvent (&sec_none, true, false, NULL); + HANDLE hr, hw; + CreatePipe (&hr, &hw, &sec_none, 0); + WCHAR cmd[256]; + 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; + PROCESS_INFORMATION pi_helper; + CreateProcessW (NULL, cmd, &sec_none, &sec_none, + TRUE, EXTENDED_STARTUPINFO_PRESENT, + NULL, NULL, &si_helper.StartupInfo, &pi_helper); + WaitForSingleObject (hello, INFINITE); + DWORD rLen; + char buf[64]; + ReadFile (hr, buf, sizeof (buf), &rLen, NULL); + buf[rLen] = '\0'; + HANDLE hpConIn, hpConOut; + sscanf (buf, "StdHandles=%p,%p", &hpConIn, &hpConOut); + DuplicateHandle (pi_helper.hProcess, hpConIn, + GetCurrentProcess (), &hpConIn, 0, + TRUE, DUPLICATE_SAME_ACCESS); + DuplicateHandle (pi_helper.hProcess, hpConOut, + GetCurrentProcess (), &hpConOut, 0, + TRUE, DUPLICATE_SAME_ACCESS); + CloseHandle (hr); + CloseHandle (hw); + DeleteProcThreadAttributeList (si_helper.lpAttributeList); + HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList); + t.hHelperGoodbye = goodbye; + t.hHelperProcess = pi_helper.hProcess; + t.HelperProcessId = pi_helper.dwProcessId; + CloseHandle (from_master); + CloseHandle (to_master); + from_master = hpConIn; + to_master = hpConOut; + } + } + } + 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; @@ -1807,19 +2136,20 @@ fhandler_pty_master::setup () dev ().parse (DEV_PTYM_MAJOR, unit); termios_printf ("this %p, pty%d opened - from_pty <%p,%p>, to_pty %p", - this, unit, get_handle (), get_handle_cyg (), + this, unit, from_slave, get_handle (), get_output_handle ()); return true; err: __seterrno (); + close_maybe (from_slave); close_maybe (get_handle ()); - close_maybe (get_handle_cyg ()); close_maybe (get_output_handle ()); close_maybe (input_available_event); close_maybe (output_mutex); close_maybe (input_mutex); close_maybe (from_master); + close_maybe (from_master_cyg); close_maybe (to_master); close_maybe (to_master_cyg); close_maybe (echo_r); @@ -1840,14 +2170,20 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent) if (myself->pid == t.master_pid) { t.set_from_master (arch->from_master); + t.set_from_master_cyg (arch->from_master_cyg); t.set_to_master (arch->to_master); t.set_to_master_cyg (arch->to_master_cyg); } arch->dwProcessId = wpid; } from_master = arch->from_master; + from_master_cyg = arch->from_master_cyg; to_master = arch->to_master; to_master_cyg = arch->to_master_cyg; +#if 0 + from_slave = arch->from_slave; + to_slave = arch->to_slave; +#endif report_tty_counts (this, "inherited master", ""); } @@ -1857,7 +2193,8 @@ fhandler_pty_master::fixup_after_exec () if (!close_on_exec ()) fixup_after_fork (spawn_info->parent); else - from_master = to_master = to_master_cyg = NULL; + from_master = from_master_cyg = to_master = to_master_cyg = + from_slave = to_slave = NULL; } BOOL diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc index 7e1c08990..cb4da7330 100644 --- a/winsup/cygwin/fork.cc +++ b/winsup/cygwin/fork.cc @@ -134,6 +134,26 @@ 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; + if (((fhandler_pty_master *) fh)->getPseudoConsole ()) + { + DWORD dwHelperProcessId = + ((fhandler_pty_master *) fh)->getHelperProcessId (); + debug_printf ("found a PTY master %d: helper_PID=%d", + cfd->get_minor (), dwHelperProcessId); + if (!((fhandler_pty_master *) fh)->attach_pcon_in_spawn ()) + { + FreeConsole (); + AttachConsole (dwHelperProcessId); + break; + } + } + } + HANDLE& hParent = ch.parent; sync_with_parent ("after longjmp", true); diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index 85242ec06..04249eb94 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -667,6 +667,9 @@ peek_pipe (select_record *s, bool from_select) fhm->flush_to_slave (); } break; + case DEV_PTYS_MAJOR: + ((fhandler_pty_slave *) fh)->reset_switch_to_pcon (); + break; default: if (fh->get_readahead_valid ()) { diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index 4e549f7d4..cb4bdca06 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -288,6 +288,31 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, return -1; } + DWORD pidRestore = + fhandler_console::get_console_process_id (GetCurrentProcessId (), false); + cygheap_fdenum cfd (false); + bool attach_to_pcon = false; + int fd; + while ((fd = cfd.next ()) >= 0 && fd < 3) + if (cfd->get_major () == DEV_PTYS_MAJOR) + { + fhandler_base *fh = cfd; + if (((fhandler_pty_slave *) fh)->getPseudoConsole ()) + { + DWORD dwHelperProcessId = + ((fhandler_pty_slave *) fh)->getHelperProcessId (); + debug_printf ("found a PTY slave %d: helper_PID=%d", + cfd->get_minor (), dwHelperProcessId); + if (((fhandler_pty_slave *) fh)->attach_pcon_in_spawn ()) + { + FreeConsole (); + AttachConsole (dwHelperProcessId); + attach_to_pcon = true; + break; + } + } + } + av newargv; linebuf cmd; PWCHAR envblock = NULL; @@ -868,6 +893,13 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, this->cleanup (); if (envblock) free (envblock); + + if (attach_to_pcon && pidRestore) + { + FreeConsole (); + AttachConsole (pidRestore); + } + return (int) res; } diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index ad46cb312..92d646ff5 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -234,7 +234,10 @@ tty::init () was_opened = false; master_pid = 0; is_console = false; + attach_pcon_in_spawn = false; + hPseudoConsole = NULL; column = 0; + switch_to_pcon = 0; } HANDLE diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index 9aee43b9c..7c6ba15d7 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -28,6 +28,8 @@ details. */ #define MIN_CTRL_C_SLOP 50 #endif +typedef void *HPCON; + #include class tty_min { @@ -88,14 +90,23 @@ public: private: HANDLE _from_master; + HANDLE _from_master_cyg; HANDLE _to_master; HANDLE _to_master_cyg; + HPCON hPseudoConsole; + HANDLE hHelperProcess; + DWORD HelperProcessId; + HANDLE hHelperGoodbye; + bool attach_pcon_in_spawn; + int switch_to_pcon; public: - HANDLE from_master() const { return _from_master; } - HANDLE to_master() const { return _to_master; } - HANDLE to_master_cyg() const { return _to_master_cyg; } + HANDLE from_master () const { return _from_master; } + HANDLE from_master_cyg () const { return _from_master_cyg; } + HANDLE to_master () const { return _to_master; } + HANDLE to_master_cyg () const { return _to_master_cyg; } void set_from_master (HANDLE h) { _from_master = h; } + void set_from_master_cyg (HANDLE h) { _from_master_cyg = h; } void set_to_master (HANDLE h) { _to_master = h; } void set_to_master_cyg (HANDLE h) { _to_master_cyg = h; } @@ -117,7 +128,9 @@ public: void set_master_ctl_closed () {master_pid = -1;} static void __stdcall create_master (int); static void __stdcall init_session (); + friend class fhandler_pty_common; friend class fhandler_pty_master; + friend class fhandler_pty_slave; }; class tty_list diff --git a/winsup/utils/cygwin-console-helper.cc b/winsup/utils/cygwin-console-helper.cc index 8f62ed7e6..bef143f96 100644 --- a/winsup/utils/cygwin-console-helper.cc +++ b/winsup/utils/cygwin-console-helper.cc @@ -1,12 +1,24 @@ #include +#include int main (int argc, char **argv) { char *end; - if (argc != 3) + if (argc < 3) exit (1); HANDLE h = (HANDLE) strtoul (argv[1], &end, 0); SetEvent (h); + if (argc == 4) /* Pseudo console helper mode for PTY */ + { + HANDLE hPipe = (HANDLE) strtoul (argv[3], &end, 0); + char buf[64]; + sprintf (buf, "StdHandles=%p,%p\n", + GetStdHandle (STD_INPUT_HANDLE), + GetStdHandle (STD_OUTPUT_HANDLE)); + DWORD dwLen; + WriteFile (hPipe, buf, strlen (buf), &dwLen, NULL); + CloseHandle (hPipe); + } h = (HANDLE) strtoul (argv[2], &end, 0); WaitForSingleObject (h, INFINITE); exit (0); -- 2.17.0