diff options
-rw-r--r-- | winsup/cygwin/fhandler.h | 3 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_tty.cc | 299 | ||||
-rw-r--r-- | winsup/cygwin/select.cc | 5 | ||||
-rw-r--r-- | winsup/cygwin/spawn.cc | 2 | ||||
-rw-r--r-- | winsup/cygwin/tty.cc | 7 | ||||
-rw-r--r-- | winsup/cygwin/tty.h | 5 |
6 files changed, 279 insertions, 42 deletions
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 378e9c1..3db33ea 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2284,6 +2284,8 @@ 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; + HANDLE slave_reading; + LONG num_reader; /* Helper functions for fchmod and fchown. */ bool fch_open_handles (bool chown); @@ -2359,6 +2361,7 @@ class fhandler_pty_slave: public fhandler_pty_common static void transfer_input (xfer_dir dir, HANDLE from, tty *ttyp, _minor_t unit, HANDLE input_available_event); HANDLE get_input_available_event (void) { return input_available_event; } + bool pcon_activated (void) { return get_ttyp ()->h_pseudo_console; } }; #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit)) diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 34cff2a..c74e38c 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -27,6 +27,7 @@ details. */ #include "cygwait.h" #include "registry.h" #include "tls_pbuf.h" +#include "winf.h" #ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE #define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016 @@ -87,21 +88,47 @@ get_console_process_id (DWORD pid, bool match) } static bool isHybrid; +static HANDLE h_gdb_process; static void -set_switch_to_pcon (HANDLE h) +set_switch_to_pcon (HANDLE *in, HANDLE *out, HANDLE *err, bool iscygwin) { cygheap_fdenum cfd (false); int fd; + fhandler_base *replace_in = NULL, *replace_out = NULL, *replace_err = NULL; + fhandler_pty_slave *ptys_pcon = NULL; while ((fd = cfd.next ()) >= 0) - if (cfd->get_major () == DEV_PTYS_MAJOR) - { - fhandler_base *fh = cfd; - fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; - if (h == ptys->get_handle ()) - ptys->set_switch_to_pcon (); - return; - } + { + if (*in == cfd->get_handle () || + (fd == 0 && *in == GetStdHandle (STD_INPUT_HANDLE))) + replace_in = (fhandler_base *) cfd; + if (*out == cfd->get_output_handle () || + (fd == 1 && *out == GetStdHandle (STD_OUTPUT_HANDLE))) + replace_out = (fhandler_base *) cfd; + 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) + { + fhandler_base *fh = cfd; + fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; + if (*in == ptys->get_handle ()) + ptys_pcon = ptys; + } + } + if (!iscygwin && ptys_pcon) + ptys_pcon->set_switch_to_pcon (); + if (replace_in) + { + if (iscygwin && ptys_pcon->pcon_activated ()) + *in = replace_in->get_handle_cyg (); + else + *in = replace_in->get_handle (); + } + if (replace_out) + *out = replace_out->get_output_handle (); + if (replace_err) + *err = replace_err->get_output_handle (); } #define DEF_HOOK(name) static __typeof__ (name) *name##_Orig @@ -115,16 +142,55 @@ CreateProcessA_Hooked BOOL inh, DWORD f, LPVOID e, LPCSTR d, LPSTARTUPINFOA si, LPPROCESS_INFORMATION pi) { - HANDLE h; - if (!isHybrid) + STARTUPINFOEXA siex = {0, }; + if (si->cb == sizeof (STARTUPINFOEXA)) + siex = *(STARTUPINFOEXA *)si; + else + siex.StartupInfo = *si; + STARTUPINFOA *siov = &siex.StartupInfo; + if (!(si->dwFlags & STARTF_USESTDHANDLES)) { - if (si->dwFlags & STARTF_USESTDHANDLES) - h = si->hStdInput; - else - h = GetStdHandle (STD_INPUT_HANDLE); - set_switch_to_pcon (h); + siov->dwFlags |= STARTF_USESTDHANDLES; + siov->hStdInput = GetStdHandle (STD_INPUT_HANDLE); + siov->hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + siov->hStdError = GetStdHandle (STD_ERROR_HANDLE); } - return CreateProcessA_Orig (n, c, pa, ta, inh, f, e, d, si, pi); + path_conv path; + tmp_pathbuf tp; + char *prog =tp.c_get (); + if (n) + __small_sprintf (prog, "%s", n); + else + { + __small_sprintf (prog, "%s", c); + char *p = prog; + char *p1; + do + if ((p1 = strstr (p, ".exe")) || (p1 = strstr (p, ".com"))) + { + p = p1 + 4; + if (*p == ' ') + { + *p = '\0'; + path.check (prog); + *p = ' '; + } + else if (*p == '\0') + path.check (prog); + } + while (!path.exists() && p1); + } + const char *argv[] = {"", NULL}; /* Dummy */ + av av1; + av1.setup ("", path, "", 1, argv, false); + set_switch_to_pcon (&siov->hStdInput, &siov->hStdOutput, &siov->hStdError, + path.iscygexec ()); + BOOL ret = CreateProcessA_Orig (n, c, pa, ta, inh, f, e, d, siov, pi); + h_gdb_process = pi->hProcess; + DuplicateHandle (GetCurrentProcess (), h_gdb_process, + GetCurrentProcess (), &h_gdb_process, + 0, 0, DUPLICATE_SAME_ACCESS); + return ret; } static BOOL WINAPI CreateProcessW_Hooked @@ -132,16 +198,55 @@ CreateProcessW_Hooked BOOL inh, DWORD f, LPVOID e, LPCWSTR d, LPSTARTUPINFOW si, LPPROCESS_INFORMATION pi) { - HANDLE h; - if (!isHybrid) + STARTUPINFOEXW siex = {0, }; + if (si->cb == sizeof (STARTUPINFOEXW)) + siex = *(STARTUPINFOEXW *)si; + else + siex.StartupInfo = *si; + STARTUPINFOW *siov = &siex.StartupInfo; + if (!(si->dwFlags & STARTF_USESTDHANDLES)) { - if (si->dwFlags & STARTF_USESTDHANDLES) - h = si->hStdInput; - else - h = GetStdHandle (STD_INPUT_HANDLE); - set_switch_to_pcon (h); + siov->dwFlags |= STARTF_USESTDHANDLES; + siov->hStdInput = GetStdHandle (STD_INPUT_HANDLE); + siov->hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + siov->hStdError = GetStdHandle (STD_ERROR_HANDLE); } - return CreateProcessW_Orig (n, c, pa, ta, inh, f, e, d, si, pi); + path_conv path; + tmp_pathbuf tp; + char *prog =tp.c_get (); + if (n) + __small_sprintf (prog, "%W", n); + else + { + __small_sprintf (prog, "%W", c); + char *p = prog; + char *p1; + do + if ((p1 = strstr (p, ".exe")) || (p1 = strstr (p, ".com"))) + { + p = p1 + 4; + if (*p == ' ') + { + *p = '\0'; + path.check (prog); + *p = ' '; + } + else if (*p == '\0') + path.check (prog); + } + while (!path.exists() && p1); + } + const char *argv[] = {"", NULL}; /* Dummy */ + av av1; + av1.setup ("", path, "", 1, argv, false); + set_switch_to_pcon (&siov->hStdInput, &siov->hStdOutput, &siov->hStdError, + path.iscygexec ()); + BOOL ret = CreateProcessW_Orig (n, c, pa, ta, inh, f, e, d, siov, pi); + h_gdb_process = pi->hProcess; + DuplicateHandle (GetCurrentProcess (), h_gdb_process, + GetCurrentProcess (), &h_gdb_process, + 0, 0, DUPLICATE_SAME_ACCESS); + return ret; } static void @@ -529,7 +634,7 @@ out: fhandler_pty_slave::fhandler_pty_slave (int unit) : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL), - io_handle_cyg (NULL) + io_handle_cyg (NULL), slave_reading (NULL), num_reader (0) { if (unit >= 0) dev ().parse (DEV_PTYS_MAJOR, unit); @@ -835,15 +940,100 @@ fhandler_pty_slave::set_switch_to_pcon (void) if (!get_ttyp ()->switch_to_pcon_in) { 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; + setup_locale (); + 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); } } void fhandler_pty_slave::reset_switch_to_pcon (void) { + if (h_gdb_process) + { + if (WaitForSingleObject (h_gdb_process, 0) == WAIT_TIMEOUT) + { + if (isHybrid) + get_ttyp ()->wait_pcon_fwd (false); + } + else + { + CloseHandle (h_gdb_process); + h_gdb_process = NULL; + if (isHybrid && get_ttyp ()->pcon_pid == myself->pid) + { + if (get_ttyp ()->switch_to_pcon_in) + 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 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 ()->h_pseudo_console; + close_pseudoconsole (get_ttyp ()); + if (need_restore_handles) + { + pinfo p (get_ttyp ()->master_pid); + HANDLE pty_owner = + OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->dwProcessId); + bool fix_in, fix_out, fix_err; + fix_in = + GetStdHandle (STD_INPUT_HANDLE) == get_handle (); + fix_out = + GetStdHandle (STD_OUTPUT_HANDLE) == get_output_handle (); + fix_err = + GetStdHandle (STD_ERROR_HANDLE) == get_output_handle (); + if (pty_owner) + { + CloseHandle (get_handle ()); + DuplicateHandle (pty_owner, get_ttyp ()->from_master (), + GetCurrentProcess (), &get_handle (), + 0, TRUE, DUPLICATE_SAME_ACCESS); + CloseHandle (get_output_handle ()); + DuplicateHandle (pty_owner, get_ttyp ()->to_master (), + GetCurrentProcess (), + &get_output_handle (), + 0, TRUE, DUPLICATE_SAME_ACCESS); + CloseHandle (pty_owner); + } + else + { + char pipe[MAX_PATH]; + __small_sprintf (pipe, + "\\\\.\\pipe\\cygwin-%S-pty%d-master-ctl", + &cygheap->installation_key, 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? */ + CloseHandle (get_handle ()); + set_handle (repl.from_master); + CloseHandle (get_output_handle ()); + set_output_handle (repl.to_master); + } + if (fix_in) + SetStdHandle (STD_INPUT_HANDLE, get_handle ()); + if (fix_out) + SetStdHandle (STD_OUTPUT_HANDLE, get_output_handle ()); + if (fix_err) + SetStdHandle (STD_ERROR_HANDLE, get_output_handle ()); + } + get_ttyp ()->pcon_pid = 0; + get_ttyp ()->switch_to_pcon_in = false; + } + isHybrid = false; + return; + } + } if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid && !!pinfo (get_ttyp ()->pcon_pid)) /* There is a process which is grabbing pseudo console. */ @@ -899,10 +1089,23 @@ fhandler_pty_slave::write (const void *ptr, size_t len) void fhandler_pty_slave::mask_switch_to_pcon_in (bool mask) { + char name[MAX_PATH]; + shared_name (name, TTY_SLAVE_READING, get_minor ()); + HANDLE masked = OpenEvent (READ_CONTROL, FALSE, name); + CloseHandle (masked); + + if (mask) + { + if (InterlockedIncrement (&num_reader) == 1) + slave_reading = CreateEvent (&sec_none_nih, TRUE, FALSE, name); + } + else if (InterlockedDecrement (&num_reader) == 0) + CloseHandle (slave_reading); + if (get_ttyp ()->switch_to_pcon_in && (get_ttyp ()->pcon_pid == myself->pid || !get_ttyp ()->h_pseudo_console) - && get_ttyp ()->mask_switch_to_pcon_in != mask) + && !!masked != mask) { if (mask) fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, @@ -915,14 +1118,19 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask) get_ttyp (), get_minor (), input_available_event); } - get_ttyp ()->mask_switch_to_pcon_in = mask; + return; } bool fhandler_pty_master::to_be_read_from_pcon (void) { + 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 && !get_ttyp ()->mask_switch_to_pcon_in); + || (get_ttyp ()->switch_to_pcon_in && !masked); } void __reg3 @@ -2360,6 +2568,13 @@ pty_master_fwd_thread (VOID *arg) return fhandler_pty_master::pty_master_fwd_thread (&p); } +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 () { @@ -2406,8 +2621,15 @@ fhandler_pty_master::setup () } __small_sprintf (pipename, "pty%d-to-slave", unit); + /* FILE_FLAG_OVERLAPPED is specified here in order to prevent + PeekNamedPipe() from blocking in transfer_input(). + Accordig to the official document, in order to access the handle + opened with FILE_FLAG_OVERLAPPED, it is mandatory to pass the + OVERLAPP structure, but in fact, it seems that the access will + fallback to the blocking access if it is not specified. */ res = fhandler_pipe::create (&sec_none, &from_master, &to_slave, - fhandler_pty_common::pipesize, pipename, 0); + fhandler_pty_common::pipesize, pipename, + FILE_FLAG_OVERLAPPED); if (res) { errstr = "input pipe"; @@ -2498,6 +2720,8 @@ fhandler_pty_master::setup () dev ().parse (DEV_PTYM_MAJOR, unit); + t.master_is_running_as_service = is_running_as_service (); + termios_printf ("this %p, pty%d opened - from_pty <%p,%p>, to_pty %p", this, unit, from_slave, get_handle (), get_output_handle ()); @@ -2819,6 +3043,14 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon) CloseHandle (pi.hProcess); /* Set handle */ + if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ()) + SetStdHandle (STD_INPUT_HANDLE, hpConIn); + if (GetStdHandle (STD_OUTPUT_HANDLE) == get_output_handle ()) + SetStdHandle (STD_OUTPUT_HANDLE, hpConOut); + if (GetStdHandle (STD_ERROR_HANDLE) == get_output_handle ()) + SetStdHandle (STD_ERROR_HANDLE, hpConOut); + + /* Fixup handles */ HANDLE orig_input_handle = get_handle (); HANDLE orig_output_handle = get_output_handle (); cygheap_fdenum cfd (false); @@ -2888,7 +3120,6 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp) { if (ttyp->h_pseudo_console) { - ttyp->wait_pcon_fwd (); ttyp->previous_code_page = GetConsoleCP (); ttyp->previous_output_code_page = GetConsoleOutputCP (); FreeConsole (); diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index d6c1324..4e79464 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -1411,12 +1411,11 @@ pty_slave_cleanup (select_record *me, select_stuff *stuff) { fhandler_base *fh = (fhandler_base *) me->fh; fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh; - if (me->read_selected) - ptys->mask_switch_to_pcon_in (false); - select_pipe_info *pi = (select_pipe_info *) stuff->device_specific_ptys; if (!pi) return; + if (me->read_selected && pi->start) + ptys->mask_switch_to_pcon_in (false); if (pi->thread) { pi->stop_thread = true; diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index b80a20f..383a5a0 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -964,6 +964,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, WaitForSingleObject (pi.hProcess, INFINITE); if (ptys_ttyp) { + ptys_ttyp->wait_pcon_fwd (); fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, ptys_from_master, ptys_ttyp, ptys_unit, @@ -990,6 +991,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, res = -1; if (ptys_ttyp) { + ptys_ttyp->wait_pcon_fwd (); fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg, ptys_from_master, ptys_ttyp, ptys_unit, diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index 908166a..e6c0b68 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -237,7 +237,6 @@ tty::init () column = 0; h_pseudo_console = NULL; switch_to_pcon_in = false; - mask_switch_to_pcon_in = false; pcon_pid = 0; term_code_page = 0; pcon_last_time = 0; @@ -248,6 +247,7 @@ tty::init () invisible_console_pid = 0; previous_code_page = 0; previous_output_code_page = 0; + master_is_running_as_service = false; } HANDLE @@ -294,7 +294,7 @@ tty_min::ttyname () } void -tty::wait_pcon_fwd (void) +tty::wait_pcon_fwd (bool init) { /* The forwarding in pseudo console sometimes stops for 16-32 msec even if it already has data to transfer. @@ -304,7 +304,8 @@ tty::wait_pcon_fwd (void) thread when the last data is transfered. */ const int sleep_in_pcon = 16; const int time_to_wait = sleep_in_pcon * 2 + 1/* margine */; - pcon_last_time = GetTickCount (); + if (init) + pcon_last_time = GetTickCount (); while (GetTickCount () - pcon_last_time < time_to_wait) { int tw = time_to_wait - (GetTickCount () - pcon_last_time); diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index 0613574..47be8e4 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -21,6 +21,7 @@ details. */ #define OUTPUT_MUTEX "cygtty.output.mutex" #define INPUT_MUTEX "cygtty.input.mutex" #define TTY_SLAVE_ALIVE "cygtty.slave_alive" +#define TTY_SLAVE_READING "cygtty.slave_reading" #include <sys/termios.h> @@ -98,7 +99,6 @@ private: HPCON h_pseudo_console; bool pcon_start; bool switch_to_pcon_in; - bool mask_switch_to_pcon_in; pid_t pcon_pid; UINT term_code_page; DWORD pcon_last_time; @@ -109,6 +109,7 @@ private: pid_t invisible_console_pid; UINT previous_code_page; UINT previous_output_code_page; + bool master_is_running_as_service; public: HANDLE from_master () const { return _from_master; } @@ -142,7 +143,7 @@ public: void set_master_ctl_closed () {master_pid = -1;} static void __stdcall create_master (int); static void __stdcall init_session (); - void wait_pcon_fwd (void); + void wait_pcon_fwd (bool init = true); friend class fhandler_pty_common; friend class fhandler_pty_master; friend class fhandler_pty_slave; |