diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index b2c814cc2..166ade414 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2255,13 +2255,13 @@ class fhandler_pty_common: public fhandler_termios public: fhandler_pty_common () : fhandler_termios (), - output_mutex (NULL), input_mutex (NULL), + output_mutex (NULL), input_mutex (NULL), pcon_mutex (NULL), input_available_event (NULL) { pc.file_attributes (FILE_ATTRIBUTE_NORMAL); } static const unsigned pipesize = 128 * 1024; - HANDLE output_mutex, input_mutex; + HANDLE output_mutex, input_mutex, pcon_mutex; HANDLE input_available_event; bool use_archetype () const {return true;} @@ -2317,13 +2317,6 @@ class fhandler_pty_slave: public fhandler_pty_common void fch_close_handles (); public: - /* Transfer direction for transfer_input() */ - enum xfer_dir - { - to_nat, - to_cyg - }; - /* Constructor */ fhandler_pty_slave (int); @@ -2382,8 +2375,8 @@ class fhandler_pty_slave: public fhandler_pty_common void setup_locale (void); tty *get_ttyp () { return (tty *) tc (); } /* Override as public */ void create_invisible_console (void); - static void transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, - _minor_t unit, HANDLE input_available_event); + static void transfer_input (tty::xfer_dir dir, HANDLE from, tty *ttyp, + HANDLE input_available_event); HANDLE get_input_available_event (void) { return input_available_event; } bool pcon_activated (void) { return get_ttyp ()->pcon_activated; } }; diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 48b89ae77..f6eb3ae4d 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -127,7 +127,7 @@ set_switch_to_pcon (HANDLE *in, HANDLE *out, HANDLE *err, bool iscygwin) if (*err == cfd->get_output_handle () || (fd == 2 && *err == GetStdHandle (STD_ERROR_HANDLE))) replace_err = (fhandler_base *) cfd; - if (cfd->get_major () == DEV_PTYS_MAJOR) + if (cfd->get_device () == (dev_t) myself->ctty) { fhandler_base *fh = cfd; fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; @@ -154,6 +154,7 @@ set_switch_to_pcon (HANDLE *in, HANDLE *out, HANDLE *err, bool iscygwin) /* CreateProcess() is hooked for GDB etc. */ DEF_HOOK (CreateProcessA); DEF_HOOK (CreateProcessW); +DEF_HOOK (exit); static BOOL WINAPI CreateProcessA_Hooked @@ -268,6 +269,34 @@ CreateProcessW_Hooked return ret; } +void +exit_Hooked (int e) +{ + if (isHybrid) + { + cygheap_fdenum cfd (false); + while (cfd.next () >= 0) + if (cfd->get_device () == (dev_t) myself->ctty) + { + fhandler_base *fh = cfd; + fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; + tty *ttyp = ptys->get_ttyp (); + HANDLE from = ptys->get_handle (); + HANDLE input_available_event = ptys->get_input_available_event (); + if (ttyp->getpgid () == myself->pgid + && ttyp->pcon_input_state_eq (tty::to_nat)) + { + WaitForSingleObject (ptys->input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_cyg, from, ttyp, + input_available_event); + ReleaseMutex (ptys->input_mutex); + } + break; + } + } + exit_Orig (e); +} + static void convert_mb_str (UINT cp_to, char *ptr_to, size_t *len_to, UINT cp_from, const char *ptr_from, size_t len_from, @@ -438,7 +467,8 @@ fhandler_pty_master::accept_input () HANDLE write_to = get_output_handle (); tmp_pathbuf tp; - if (to_be_read_from_pcon ()) + if (to_be_read_from_pcon () + && get_ttyp ()->pcon_input_state == tty::to_nat) { write_to = to_slave; @@ -487,11 +517,27 @@ fhandler_pty_master::accept_input () } else { - DWORD rc; + BOOL rc = TRUE; DWORD written = 0; paranoid_printf ("about to write %u chars to slave", bytes_left); - rc = WriteFile (write_to, p, bytes_left, &written, NULL); + /* Write line by line for transfer input. */ + char *p0 = p; + char *p1 = p; + DWORD n; + while ((p1 = (char *) memchr (p0, '\n', bytes_left - (p0 - p))) + || (p1 = (char *) memchr (p0, '\r', bytes_left - (p0 - p)))) + { + n = p1 - p0 + 1; + rc = WriteFile (write_to, p0, n, &n, NULL); + written += n; + p0 = p1 + 1; + } + if ((n = bytes_left - (p0 - p))) + { + rc = WriteFile (write_to, p0, n, &n, NULL); + written += n; + } if (!rc) { debug_printf ("error writing to pipe %p %E", write_to); @@ -669,7 +715,7 @@ fhandler_pty_slave::open (int flags, mode_t) { &from_master_local, &input_available_event, &input_mutex, &inuse, &output_mutex, &to_master_local, &pty_owner, &to_master_cyg_local, - &from_master_cyg_local, + &from_master_cyg_local, &pcon_mutex, NULL }; @@ -697,6 +743,11 @@ fhandler_pty_slave::open (int flags, mode_t) errmsg = "open input mutex failed, %E"; goto err; } + if (!(pcon_mutex = get_ttyp ()->open_mutex (PCON_MUTEX, MAXIMUM_ALLOWED))) + { + errmsg = "open pcon mutex failed, %E"; + goto err; + } shared_name (buf, INPUT_AVAILABLE_EVENT, get_minor ()); if (!(input_available_event = OpenEvent (MAXIMUM_ALLOWED, TRUE, buf))) { @@ -960,12 +1011,19 @@ fhandler_pty_slave::set_switch_to_pcon (void) { isHybrid = true; setup_locale (); + myself->exec_dwProcessId = myself->dwProcessId; bool nopcon = (disable_pcon || !term_has_pcon_cap (NULL)); - if (!setup_pseudoconsole (nopcon)) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, - get_handle_cyg (), - get_ttyp (), get_minor (), - input_available_event); + WaitForSingleObject (pcon_mutex, INFINITE); + bool pcon_enabled = setup_pseudoconsole (nopcon); + ReleaseMutex (pcon_mutex); + if (!pcon_enabled && get_ttyp ()->getpgid () == myself->pgid + && get_ttyp ()->pcon_input_state_eq (tty::to_cyg)) + { + WaitForSingleObject (input_mutex, INFINITE); + transfer_input (tty::to_nat, get_handle_cyg (), get_ttyp (), + input_available_event); + ReleaseMutex (input_mutex); + } } } @@ -985,19 +1043,24 @@ fhandler_pty_slave::reset_switch_to_pcon (void) h_gdb_process = NULL; if (isHybrid) { - if (get_ttyp ()->switch_to_pcon_in - && get_ttyp ()->pcon_pid == myself->pid) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, - get_handle (), - get_ttyp (), get_minor (), - input_available_event); - if (get_ttyp ()->master_is_running_as_service) + if (get_ttyp ()->getpgid () == myself->pgid + && get_ttyp ()->pcon_input_state_eq (tty::to_nat)) + { + WaitForSingleObject (input_mutex, INFINITE); + transfer_input (tty::to_cyg, get_handle (), get_ttyp (), + input_available_event); + ReleaseMutex (input_mutex); + } + if (get_ttyp ()->master_is_running_as_service + && get_ttyp ()->pcon_activated) /* If the master is running as service, re-attaching to the console of the parent process will fail. Therefore, never close pseudo console here. */ return; bool need_restore_handles = get_ttyp ()->pcon_activated; + WaitForSingleObject (pcon_mutex, INFINITE); close_pseudoconsole (get_ttyp ()); + ReleaseMutex (pcon_mutex); if (need_restore_handles) { pinfo p (get_ttyp ()->master_pid); @@ -1047,6 +1110,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void) if (fix_err) SetStdHandle (STD_ERROR_HANDLE, get_output_handle ()); } + myself->exec_dwProcessId = 0; isHybrid = false; } } @@ -1057,11 +1121,6 @@ fhandler_pty_slave::reset_switch_to_pcon (void) return; if (isHybrid) return; - if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->pcon_activated) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, - get_handle (), - get_ttyp (), get_minor (), - input_available_event); get_ttyp ()->pcon_pid = 0; get_ttyp ()->switch_to_pcon_in = false; get_ttyp ()->pcon_activated = false; @@ -1119,77 +1178,47 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask, bool xfer) else if (InterlockedDecrement (&num_reader) == 0) CloseHandle (slave_reading); - if (get_ttyp ()->switch_to_pcon_in && !!masked != mask && xfer) - { /* Transfer input */ - bool attach_restore = false; - DWORD pcon_winpid = 0; - if (get_ttyp ()->pcon_pid) + /* In GDB, transfer input based on setpgid() does not work because + GDB may not set terminal process group properly. Therefore, + transfer input here if isHybrid is set. */ + if (get_ttyp ()->switch_to_pcon_in && !!masked != mask && xfer && isHybrid) + { + if (mask && get_ttyp ()->pcon_input_state_eq (tty::to_nat)) { - pinfo p (get_ttyp ()->pcon_pid); - if (p) - pcon_winpid = p->exec_dwProcessId ?: p->dwProcessId; + WaitForSingleObject (input_mutex, INFINITE); + transfer_input (tty::to_cyg, get_handle (), get_ttyp (), + input_available_event); + ReleaseMutex (input_mutex); } - if (mask) + else if (!mask && get_ttyp ()->pcon_input_state_eq (tty::to_cyg)) { - HANDLE from = get_handle (); - if (get_ttyp ()->pcon_activated && pcon_winpid - && !get_console_process_id (pcon_winpid, true)) - { - HANDLE pcon_owner = - OpenProcess (PROCESS_DUP_HANDLE, FALSE, pcon_winpid); - DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in, - GetCurrentProcess (), &from, - 0, TRUE, DUPLICATE_SAME_ACCESS); - CloseHandle (pcon_owner); - FreeConsole (); - AttachConsole (pcon_winpid); - attach_restore = true; - } - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, - from, - get_ttyp (), get_minor (), - input_available_event); - } - else - { - if (get_ttyp ()->pcon_activated && pcon_winpid - && !get_console_process_id (pcon_winpid, true)) - { - FreeConsole (); - AttachConsole (pcon_winpid); - attach_restore = true; - } - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, - get_handle_cyg (), - get_ttyp (), get_minor (), - input_available_event); - } - if (attach_restore) - { - FreeConsole (); - pinfo p (myself->ppid); - if (p) - { - if (!AttachConsole (p->dwProcessId)) - AttachConsole (ATTACH_PARENT_PROCESS); - } - else - AttachConsole (ATTACH_PARENT_PROCESS); + WaitForSingleObject (input_mutex, INFINITE); + transfer_input (tty::to_nat, get_handle_cyg (), get_ttyp (), + input_available_event); + ReleaseMutex (input_mutex); } } - return; } bool fhandler_pty_master::to_be_read_from_pcon (void) { + if (!get_ttyp ()->switch_to_pcon_in) + return false; + char name[MAX_PATH]; shared_name (name, TTY_SLAVE_READING, get_minor ()); HANDLE masked = OpenEvent (READ_CONTROL, FALSE, name); CloseHandle (masked); - return get_ttyp ()->pcon_start - || (get_ttyp ()->switch_to_pcon_in && !masked); + if (masked) /* The foreground process is cygwin process */ + return false; + + if (!pinfo (get_ttyp ()->getpgid ())) + /* GDB may set invalid process group for non-cygwin process. */ + return true; + + return get_ttyp ()->pcon_fg (get_ttyp ()->getpgid ()); } void __reg3 @@ -1720,6 +1749,7 @@ fhandler_pty_slave::fch_open_handles (bool chown) TRUE, buf); output_mutex = get_ttyp ()->open_output_mutex (write_access); input_mutex = get_ttyp ()->open_input_mutex (write_access); + pcon_mutex = get_ttyp ()->open_mutex (PCON_MUTEX, write_access); inuse = get_ttyp ()->open_inuse (write_access); if (!input_available_event || !output_mutex || !input_mutex || !inuse) { @@ -1873,6 +1903,8 @@ fhandler_pty_common::close () get_minor (), get_handle (), get_output_handle ()); if (!ForceCloseHandle (input_mutex)) termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex); + if (!ForceCloseHandle (pcon_mutex)) + termios_printf ("CloseHandle (pcon_mutex<%p>), %E", pcon_mutex); if (!ForceCloseHandle1 (get_handle (), from_pty)) termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ()); if (!ForceCloseHandle1 (get_output_handle (), to_pty)) @@ -2011,9 +2043,86 @@ fhandler_pty_master::write (const void *ptr, size_t len) push_process_state process_state (PID_TTYOU); + if (get_ttyp ()->pcon_start) + { + /* Pseudo condole support uses "CSI6n" to get cursor position. + If the reply for "CSI6n" is divided into multiple writes, + pseudo console sometimes does not recognize it. Therefore, + put them together into wpbuf and write all at once. */ + static const int wpbuf_len = strlen ("\033[32768;32868R"); + static char wpbuf[wpbuf_len]; + static int ixput = 0; + static int state = 0; + + DWORD n; + WaitForSingleObject (input_mutex, INFINITE); + for (size_t i = 0; i < len; i++) + { + if (p[i] == '\033') + { + if (ixput) + line_edit (wpbuf, ixput, ti, &ret); + ixput = 0; + state = 1; + } + if (state == 1) + { + if (ixput < wpbuf_len) + wpbuf[ixput++] = p[i]; + else + { + if (!get_ttyp ()->req_xfer_input) + WriteFile (to_slave, wpbuf, ixput, &n, NULL); + ixput = 0; + wpbuf[ixput++] = p[i]; + } + } + else + line_edit (p + i, 1, ti, &ret); + if (state == 1 && p[i] == 'R') + state = 2; + } + if (state == 2) + { + if (!get_ttyp ()->req_xfer_input) + WriteFile (to_slave, wpbuf, ixput, &n, NULL); + ixput = 0; + state = 0; + get_ttyp ()->req_xfer_input = false; + get_ttyp ()->pcon_start = false; + } + ReleaseMutex (input_mutex); + + if (!get_ttyp ()->pcon_start) + { + pinfo pp (get_ttyp ()->pcon_start_pid); + bool pcon_fg = (pp && get_ttyp ()->getpgid () == pp->pgid); + /* GDB may set WINPID rather than cygwin PID to process group + when the debugged process is a non-cygwin process.*/ + pcon_fg |= !pinfo (get_ttyp ()->getpgid ()); + if (get_ttyp ()->switch_to_pcon_in && pcon_fg + && get_ttyp ()->pcon_input_state_eq (tty::to_cyg)) + { + /* This accept_input() call is needed in order to transfer input + which is not accepted yet to non-cygwin pipe. */ + if (get_readahead_valid ()) + accept_input (); + WaitForSingleObject (input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_nat, from_master_cyg, + get_ttyp (), + input_available_event); + ReleaseMutex (input_mutex); + } + get_ttyp ()->pcon_start_pid = 0; + } + + return 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 () && get_ttyp ()->pcon_activated) + if (to_be_read_from_pcon () && get_ttyp ()->pcon_activated + && get_ttyp ()->pcon_input_state == tty::to_nat) { tmp_pathbuf tp; char *buf = (char *) ptr; @@ -2029,54 +2138,8 @@ fhandler_pty_master::write (const void *ptr, size_t len) } WaitForSingleObject (input_mutex, INFINITE); - - DWORD wLen; - - if (get_ttyp ()->pcon_start) - { - /* Pseudo condole support uses "CSI6n" to get cursor position. - If the reply for "CSI6n" is divided into multiple writes, - pseudo console sometimes does not recognize it. Therefore, - put them together into wpbuf and write all at once. */ - static const int wpbuf_len = 64; - static char wpbuf[wpbuf_len]; - static int ixput = 0; - - if (ixput + nlen < wpbuf_len) - { - memcpy (wpbuf + ixput, buf, nlen); - ixput += nlen; - } - else - { - WriteFile (to_slave, wpbuf, ixput, &wLen, NULL); - ixput = 0; - get_ttyp ()->pcon_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); - if (get_ttyp ()->switch_to_pcon_in) - { - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, - from_master_cyg, - get_ttyp (), get_minor (), - input_available_event); - /* This accept_input() call is needed in order to transfer input - which is not accepted yet to non-cygwin pipe. */ - if (get_readahead_valid ()) - accept_input (); - } - return len; - } - - WriteFile (to_slave, buf, nlen, &wLen, NULL); - + DWORD n; + WriteFile (to_slave, buf, nlen, &n, NULL); ReleaseMutex (input_mutex); return len; @@ -2248,6 +2311,8 @@ fhandler_pty_slave::fixup_after_exec () /* CreateProcess() is hooked for GDB etc. */ DO_HOOK (NULL, CreateProcessA); DO_HOOK (NULL, CreateProcessW); + if (CreateProcessA_Orig || CreateProcessW_Orig) + DO_HOOK (NULL, exit); } /* This thread function handles the master control pipe. It waits for a @@ -2739,6 +2804,10 @@ fhandler_pty_master::setup () if (!(input_mutex = CreateMutex (&sa, FALSE, buf))) goto err; + errstr = shared_name (buf, PCON_MUTEX, unit); + if (!(pcon_mutex = CreateMutex (&sa, FALSE, buf))) + goto err; + attach_mutex = CreateMutex (&sa, FALSE, NULL); /* Create master control pipe which allows the master to duplicate @@ -2980,10 +3049,17 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon) } HANDLE hpConIn, hpConOut; - acquire_output_mutex (INFINITE); if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid && !!pinfo (get_ttyp ()->pcon_pid) && get_ttyp ()->pcon_activated) { + /* Send CSI6n just for requesting transfer input. */ + DWORD n; + WaitForSingleObject (input_mutex, INFINITE); + get_ttyp ()->req_xfer_input = true; + get_ttyp ()->pcon_start = true; + get_ttyp ()->pcon_start_pid = myself->pid; + WriteFile (get_output_handle_cyg (), "\033[6n", 4, &n, NULL); + ReleaseMutex (input_mutex); /* Attach to the pseudo console which already exits. */ pinfo p (get_ttyp ()->pcon_pid); HANDLE pcon_owner = @@ -3068,8 +3144,9 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon) si.StartupInfo.hStdOutput = NULL; si.StartupInfo.hStdError = NULL; - get_ttyp ()->pcon_start = true; get_ttyp ()->pcon_activated = true; + get_ttyp ()->pcon_start = true; + get_ttyp ()->pcon_start_pid = myself->pid; if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, &si.StartupInfo, &pi)) @@ -3183,7 +3260,6 @@ skip_create: if (get_ttyp ()->previous_output_code_page) SetConsoleOutputCP (get_ttyp ()->previous_output_code_page); - release_output_mutex (); return true; cleanup_pcon_in: @@ -3196,6 +3272,7 @@ cleanup_helper_process: cleanup_event_and_pipes: CloseHandle (hello); get_ttyp ()->pcon_start = false; + get_ttyp ()->pcon_start_pid = 0; get_ttyp ()->pcon_activated = false; skip_close_hello: CloseHandle (goodbye); @@ -3212,7 +3289,6 @@ cleanup_pseudo_console: CloseHandle (tmp); } fallback: - release_output_mutex (); return false; } @@ -3312,6 +3388,7 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp) ttyp->switch_to_pcon_in = false; ttyp->pcon_pid = 0; ttyp->pcon_start = false; + ttyp->pcon_start_pid = 0; } } else @@ -3429,7 +3506,6 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env) /* Set pcon_activated and pcon_start so that the response will sent to io_handle rather than io_handle_cyg. */ get_ttyp ()->pcon_activated = true; - get_ttyp ()->pcon_pid = myself->pid; /* pcon_start will be cleared in master write() when CSI6n is responded. */ get_ttyp ()->pcon_start = true; WriteFile (get_output_handle_cyg (), "\033[6n", 4, &n, NULL); @@ -3526,11 +3602,11 @@ fhandler_pty_master::get_master_fwd_thread_param (master_fwd_thread_param_t *p) #define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) #define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) void -fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, - _minor_t unit, HANDLE input_available_event) +fhandler_pty_slave::transfer_input (tty::xfer_dir dir, HANDLE from, tty *ttyp, + HANDLE input_available_event) { HANDLE to; - if (dir == to_nat) + if (dir == tty::to_nat) to = ttyp->to_slave (); else to = ttyp->to_slave_cyg (); @@ -3548,14 +3624,14 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, char pipe[MAX_PATH]; __small_sprintf (pipe, "\\\\.\\pipe\\cygwin-%S-pty%d-master-ctl", - &cygheap->installation_key, unit); + &cygheap->installation_key, ttyp->get_minor ()); pipe_request req = { GetCurrentProcessId () }; pipe_reply repl; DWORD len; if (!CallNamedPipe (pipe, &req, sizeof req, &repl, sizeof repl, &len, 500)) return; /* What can we do? */ - if (dir == to_nat) + if (dir == tty::to_nat) to = repl.to_slave; else to = repl.to_slave_cyg; @@ -3563,7 +3639,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, UINT cp_from = 0, cp_to = 0; - if (dir == to_nat) + if (dir == tty::to_nat) { cp_from = ttyp->term_code_page; if (ttyp->pcon_activated) @@ -3582,7 +3658,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, bool transfered = false; - if (dir == to_cyg && ttyp->pcon_activated) + if (dir == tty::to_cyg && ttyp->pcon_activated) { /* from handle is console handle */ INPUT_RECORD r[INREC_SIZE]; DWORD n; @@ -3607,18 +3683,6 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, && (ctrl_key_state & CTRL_PRESSED) && !(ctrl_key_state & ALT_PRESSED)) buf[len++] = '\0'; - else if (r[i].Event.KeyEvent.wVirtualKeyCode == VK_F3) - { - /* If the cursor position report for CSI6n matches - with e.g. "ESC[1;2R", pseudo console translates - it to Shift-F3. This is a workaround for that. */ - int ctrl = 1; - if (ctrl_key_state & SHIFT_PRESSED) ctrl += 1; - if (ctrl_key_state & ALT_PRESSED) ctrl += 2; - if (ctrl_key_state & CTRL_PRESSED) ctrl += 4; - __small_sprintf (buf + len, "\033[1;%1dR", ctrl); - len += 6; - } else { /* arrow/function keys */ /* FIXME: The current code generates cygwin terminal @@ -3666,11 +3730,15 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, DWORD n = MIN (bytes_in_pipe, NT_MAX_PATH); ReadFile (from, buf, n, &n, NULL); char *ptr = buf; - if (dir == to_nat && ttyp->pcon_activated) + if (dir == tty::to_nat) { char *p = buf; - while ((p = (char *) memchr (p, '\n', n - (p - buf)))) - *p = '\r'; + if (ttyp->pcon_activated) + while ((p = (char *) memchr (p, '\n', n - (p - buf)))) + *p = '\r'; + else + while ((p = (char *) memchr (p, '\r', n - (p - buf)))) + *p = '\n'; } if (cp_to != cp_from) { @@ -3686,8 +3754,9 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, } } - if (dir == to_nat) + if (dir == tty::to_nat) ResetEvent (input_available_event); else if (transfered) SetEvent (input_available_event); + ttyp->pcon_input_state = dir; } diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index c4b612815..4d4d599ca 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -656,18 +656,19 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, bool enable_pcon = false; HANDLE ptys_from_master = NULL; HANDLE ptys_input_available_event = NULL; - HANDLE ptys_output_mutex = NULL; + HANDLE ptys_pcon_mutex = NULL; + HANDLE ptys_input_mutex = NULL; tty *ptys_ttyp = NULL; - _minor_t ptys_unit = 0; if (!iscygwin () && ptys_primary && is_console_app (runpath)) { bool nopcon = mode != _P_OVERLAY && mode != _P_WAIT; if (disable_pcon || !ptys_primary->term_has_pcon_cap (envblock)) nopcon = true; + ptys_ttyp = ptys_primary->get_ttyp (); + WaitForSingleObject (ptys_primary->pcon_mutex, INFINITE); if (ptys_primary->setup_pseudoconsole (nopcon)) enable_pcon = true; - ptys_ttyp = ptys_primary->get_ttyp (); - ptys_unit = ptys_primary->get_minor (); + ReleaseMutex (ptys_primary->pcon_mutex); ptys_from_master = ptys_primary->get_handle (); DuplicateHandle (GetCurrentProcess (), ptys_from_master, GetCurrentProcess (), &ptys_from_master, @@ -677,14 +678,21 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, DuplicateHandle (GetCurrentProcess (), ptys_input_available_event, GetCurrentProcess (), &ptys_input_available_event, 0, 0, DUPLICATE_SAME_ACCESS); - DuplicateHandle (GetCurrentProcess (), ptys_primary->output_mutex, - GetCurrentProcess (), &ptys_output_mutex, + DuplicateHandle (GetCurrentProcess (), ptys_primary->pcon_mutex, + GetCurrentProcess (), &ptys_pcon_mutex, 0, 0, DUPLICATE_SAME_ACCESS); - if (!enable_pcon) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat, - ptys_primary->get_handle_cyg (), - ptys_ttyp, ptys_unit, - ptys_input_available_event); + DuplicateHandle (GetCurrentProcess (), ptys_primary->input_mutex, + GetCurrentProcess (), &ptys_input_mutex, + 0, 0, DUPLICATE_SAME_ACCESS); + if (!enable_pcon && ptys_ttyp->getpgid () == myself->pgid + && ptys_ttyp->pcon_input_state_eq (tty::to_cyg)) + { + WaitForSingleObject (ptys_input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_nat, + ptys_primary->get_handle_cyg (), + ptys_ttyp, ptys_input_available_event); + ReleaseMutex (ptys_input_mutex); + } } /* Set up needed handles for stdio */ @@ -969,20 +977,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (ptys_ttyp) { ptys_ttyp->wait_pcon_fwd (); - /* Do not transfer input if another process using pseudo - console exists. */ - WaitForSingleObject (ptys_output_mutex, INFINITE); - if (!fhandler_pty_common::get_console_process_id - (myself->exec_dwProcessId, false, true, true)) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, - ptys_from_master, - ptys_ttyp, ptys_unit, - ptys_input_available_event); + if (ptys_ttyp->getpgid () == myself->pgid + && ptys_ttyp->pcon_input_state_eq (tty::to_nat)) + { + WaitForSingleObject (ptys_input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_cyg, + ptys_from_master, ptys_ttyp, + ptys_input_available_event); + ReleaseMutex (ptys_input_mutex); + } CloseHandle (ptys_from_master); + CloseHandle (ptys_input_mutex); CloseHandle (ptys_input_available_event); + WaitForSingleObject (ptys_pcon_mutex, INFINITE); fhandler_pty_slave::close_pseudoconsole (ptys_ttyp); - ReleaseMutex (ptys_output_mutex); - CloseHandle (ptys_output_mutex); + ReleaseMutex (ptys_pcon_mutex); + CloseHandle (ptys_pcon_mutex); } if (cons_native) { @@ -1002,20 +1012,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, if (ptys_ttyp) { ptys_ttyp->wait_pcon_fwd (); - /* Do not transfer input if another process using pseudo - console exists. */ - WaitForSingleObject (ptys_output_mutex, INFINITE); - if (!fhandler_pty_common::get_console_process_id - (myself->exec_dwProcessId, false, true, true)) - fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, - ptys_from_master, - ptys_ttyp, ptys_unit, - ptys_input_available_event); + if (ptys_ttyp->getpgid () == myself->pgid + && ptys_ttyp->pcon_input_state_eq (tty::to_nat)) + { + WaitForSingleObject (ptys_input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_cyg, + ptys_from_master, ptys_ttyp, + ptys_input_available_event); + ReleaseMutex (ptys_input_mutex); + } CloseHandle (ptys_from_master); + CloseHandle (ptys_input_mutex); CloseHandle (ptys_input_available_event); + WaitForSingleObject (ptys_pcon_mutex, INFINITE); fhandler_pty_slave::close_pseudoconsole (ptys_ttyp); - ReleaseMutex (ptys_output_mutex); - CloseHandle (ptys_output_mutex); + ReleaseMutex (ptys_pcon_mutex); + CloseHandle (ptys_pcon_mutex); } if (cons_native) { diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index fb4501c1b..41f81f694 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -241,6 +241,7 @@ tty::init () term_code_page = 0; pcon_last_time = 0; pcon_start = false; + pcon_start_pid = 0; pcon_cap_checked = false; has_csi6n = false; need_invisible_console = false; @@ -248,6 +249,8 @@ tty::init () previous_code_page = 0; previous_output_code_page = 0; master_is_running_as_service = false; + req_xfer_input = false; + pcon_input_state = to_cyg; } HANDLE @@ -293,6 +296,77 @@ tty_min::ttyname () return d.name (); } +void +tty_min::setpgid (int pid) +{ + fhandler_pty_slave *ptys = NULL; + cygheap_fdenum cfd (false); + while (cfd.next () >= 0 && ptys == NULL) + if (cfd->get_device () == getntty ()) + ptys = (fhandler_pty_slave *) (fhandler_base *) cfd; + + if (ptys) + { + tty *ttyp = ptys->get_ttyp (); + WaitForSingleObject (ptys->pcon_mutex, INFINITE); + bool was_pcon_fg = ttyp->pcon_fg (pgid); + bool pcon_fg = ttyp->pcon_fg (pid); + if (!was_pcon_fg && pcon_fg && ttyp->switch_to_pcon_in + && ttyp->pcon_input_state_eq (tty::to_cyg)) + { + WaitForSingleObject (ptys->input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_nat, + ptys->get_handle_cyg (), ttyp, + ptys->get_input_available_event ()); + ReleaseMutex (ptys->input_mutex); + } + else if (was_pcon_fg && !pcon_fg && ttyp->switch_to_pcon_in + && ttyp->pcon_input_state_eq (tty::to_nat)) + { + bool attach_restore = false; + DWORD pcon_winpid = 0; + if (ttyp->pcon_pid) + { + pinfo p (ttyp->pcon_pid); + if (p) + pcon_winpid = p->exec_dwProcessId ?: p->dwProcessId; + } + HANDLE from = ptys->get_handle (); + if (ttyp->pcon_activated && pcon_winpid + && !ptys->get_console_process_id (pcon_winpid, true)) + { + HANDLE pcon_owner = + OpenProcess (PROCESS_DUP_HANDLE, FALSE, pcon_winpid); + DuplicateHandle (pcon_owner, ttyp->h_pcon_in, + GetCurrentProcess (), &from, + 0, TRUE, DUPLICATE_SAME_ACCESS); + CloseHandle (pcon_owner); + FreeConsole (); + AttachConsole (pcon_winpid); + attach_restore = true; + } + WaitForSingleObject (ptys->input_mutex, INFINITE); + fhandler_pty_slave::transfer_input (tty::to_cyg, from, ttyp, + ptys->get_input_available_event ()); + ReleaseMutex (ptys->input_mutex); + if (attach_restore) + { + FreeConsole (); + pinfo p (myself->ppid); + if (p) + { + if (!AttachConsole (p->dwProcessId)) + AttachConsole (ATTACH_PARENT_PROCESS); + } + else + AttachConsole (ATTACH_PARENT_PROCESS); + } + } + ReleaseMutex (ptys->pcon_mutex); + } + pgid = pid; +} + void tty::wait_pcon_fwd (bool init) { @@ -312,3 +386,18 @@ tty::wait_pcon_fwd (bool init) cygwait (tw); } } + +bool +tty::pcon_fg (pid_t pgid) +{ + /* Check if the terminal pgid matches with the pgid of the + non-cygwin process. */ + winpids pids ((DWORD) 0); + for (unsigned i = 0; i < pids.npids; i++) + { + _pinfo *p = pids[i]; + if (p->ctty == ntty && p->pgid == pgid && p->exec_dwProcessId) + return true; + } + return false; +} diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index e2e6dfeb6..e1de7ab46 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -20,6 +20,7 @@ details. */ #define INPUT_AVAILABLE_EVENT "cygtty.input.avail" #define OUTPUT_MUTEX "cygtty.output.mutex" #define INPUT_MUTEX "cygtty.input.mutex" +#define PCON_MUTEX "cygtty.pcon.mutex" #define TTY_SLAVE_ALIVE "cygtty.slave_alive" #define TTY_SLAVE_READING "cygtty.slave_reading" @@ -72,7 +73,7 @@ public: dev_t getntty () const {return ntty;} _minor_t get_minor () const {return device::minor (ntty);} pid_t getpgid () const {return pgid;} - void setpgid (int pid) {pgid = pid;} + void setpgid (int pid); int getsid () const {return sid;} void setsid (pid_t tsid) {sid = tsid;} void kill_pgrp (int); @@ -89,6 +90,13 @@ class tty: public tty_min public: pid_t master_pid; /* PID of tty master process */ + /* Transfer direction for fhandler_pty_slave::transfer_input() */ + enum xfer_dir + { + to_cyg, + to_nat + }; + private: HANDLE _from_master; HANDLE _from_master_cyg; @@ -98,6 +106,7 @@ private: HANDLE _to_slave_cyg; bool pcon_activated; bool pcon_start; + pid_t pcon_start_pid; bool switch_to_pcon_in; pid_t pcon_pid; UINT term_code_page; @@ -114,6 +123,8 @@ private: UINT previous_code_page; UINT previous_output_code_page; bool master_is_running_as_service; + bool req_xfer_input; + xfer_dir pcon_input_state; public: HANDLE from_master () const { return _from_master; } @@ -148,9 +159,12 @@ public: static void __stdcall create_master (int); static void __stdcall init_session (); void wait_pcon_fwd (bool init = true); + bool pcon_input_state_eq (xfer_dir x) { return pcon_input_state == x; } + bool pcon_fg (pid_t pgid); friend class fhandler_pty_common; friend class fhandler_pty_master; friend class fhandler_pty_slave; + friend class tty_min; }; class tty_list