From 3f1e8eb9ed563b510dae766a9091507541d16b4a Mon Sep 17 00:00:00 2001 From: Takashi Yano Date: Sun, 30 Aug 2020 05:07:52 +0900 Subject: [PATCH v5] Cygwin: pty: Disable pseudo console if TERM does not have CSI6n. - Pseudo console internally sends escape sequence CSI6n (query cursor position) on startup of non-cygwin apps. If the terminal does not support CSI6n, CreateProcess() hangs waiting for response. To prevent hang, this patch disables pseudo console if the terminal does not have CSI6n in terminfo database. Also, removes escape sequence for setting window title if the terminal does not have the set-title capability. --- winsup/cygwin/fhandler.h | 1 + winsup/cygwin/fhandler_tty.cc | 124 ++++++++++++++++++++++++++++++++++ winsup/cygwin/spawn.cc | 19 ++++-- winsup/cygwin/tty.cc | 1 + winsup/cygwin/tty.h | 1 + 5 files changed, 139 insertions(+), 7 deletions(-) diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 9fd95c098..f55bcf9d1 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -2332,6 +2332,7 @@ class fhandler_pty_slave: public fhandler_pty_common } bool setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon); void close_pseudoconsole (void); + bool term_has_pcon_cap (const WCHAR *env, bool bg); void set_switch_to_pcon (void); void reset_switch_to_pcon (void); void mask_switch_to_pcon_in (bool mask); diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc index 0865c1fac..0a6d211cd 100644 --- a/winsup/cygwin/fhandler_tty.cc +++ b/winsup/cygwin/fhandler_tty.cc @@ -2169,6 +2169,22 @@ fhandler_pty_master::pty_master_fwd_thread () char *ptr = outbuf; if (get_ttyp ()->h_pseudo_console) { + if (!get_ttyp ()->has_set_title) + { + /* Remove Set title sequence */ + char *p0, *p1; + p0 = outbuf; + while ((p0 = (char *) memmem (p0, rlen, "\033]0;", 4))) + { + p1 = (char *) memchr (p0, '\007', rlen - (p0 - outbuf)); + if (p1) + { + memmove (p0, p1 + 1, rlen - (p1 + 1 - outbuf)); + rlen -= p1 + 1 - p0; + wlen = rlen; + } + } + } /* Remove CSI > Pm m */ int state = 0; int start_at = 0; @@ -2659,3 +2675,111 @@ fhandler_pty_slave::close_pseudoconsole (void) get_ttyp ()->pcon_start = false; } } + +bool +fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env, bool background) +{ + const char *term = NULL; + char term_str[260]; + if (env) + { + for (const WCHAR *p = env; *p != L'\0'; p += wcslen (p) + 1) + if (swscanf (p, L"TERM=%236s", term_str) == 1) + { + term = term_str; + break; + } + } + else + term = getenv ("TERM"); + + if (!term) + return false; + + /* Check if terminal has capability which pusedo console needs */ + char tinfo[260]; + __small_sprintf (tinfo, "/usr/share/terminfo/%02x/%s", term[0], term); + path_conv path (tinfo); + WCHAR wtinfo[260]; + path.get_wide_win32_path (wtinfo); + HANDLE h; + h = CreateFileW (wtinfo, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, 0, NULL); + if (h == NULL) + return false; + char terminfo[4096]; + DWORD n; + ReadFile (h, terminfo, sizeof (terminfo), &n, 0); + CloseHandle (h); + + int num_size = 2; + if (*(int16_t *)terminfo == 01036 /* MAGIC2 */) + num_size = 4; + const int name_pos = 12; /* Position of terminal name */ + const int name_size = *(int16_t *) (terminfo + 2); + const int bool_count = *(int16_t *) (terminfo + 4); + const int num_count = *(int16_t *) (terminfo + 6); + const int str_count = *(int16_t *) (terminfo + 8); + const int str_size = *(int16_t *) (terminfo + 10); + const int user7 = 294; /* u7 (query cursor position) entry index */ + if (user7 >= str_count) + return false; + int str_idx_pos = name_pos + name_size + bool_count + num_size * num_count; + if (str_idx_pos & 1) + str_idx_pos ++; + const int16_t *str_idx = (int16_t *) (terminfo + str_idx_pos); + const char *str_table = (const char *) (str_idx + str_count); + if (str_idx + user7 >= (int16_t *) (terminfo + n)) + return false; + if (str_idx[user7] == -1) + return false; + const char *user7_str = str_table + str_idx[user7]; + if (user7_str >= str_table + str_size) + return false; + if (user7_str >= terminfo + n) + return false; + if (strcmp (user7_str, "\033[6n") == 0) + { + /* If the process is background, or another process is already + started under pseudo console, responce for CSI6n may be eaten + by the other process. Therefore, checking set-title capability + should be skipped. */ + if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid + && !!pinfo (get_ttyp ()->pcon_pid)) + ; /* Do nothing */ + else if (!background) + { + /* Check if terminal has set-title capability */ + tcflag_t c_lflag = get_ttyp ()->ti.c_lflag; + get_ttyp ()->ti.c_lflag &= ~ICANON; + write ("\033[6n\033]0;\007\033[6n", 13); + char buf[] = "\033[32768;32768R\033[32768;32768R"; + char *p = buf; + int len = sizeof (buf) - 1; + int x1, y1, x2, y2; + do + { + size_t n = len; + read (p, n); + p += n; + len -= n; + *p = '\0'; + } + while (sscanf (buf, "\033[%d;%dR\033[%d;%dR", + &y1, &x1, &y2, &x2) != 4); + get_ttyp ()->ti.c_lflag = c_lflag; + if (x2 == x1 && y2 == y1) + /* If "\033]0;\007" does not move cursor position, + set-title is supposed to be supported. */ + get_ttyp ()->has_set_title = true; + else + { + for (int i=0; ihas_set_title = false; + } + } + return true; + } + return false; +} diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc index a2f7697d7..9a5e3f6ef 100644 --- a/winsup/cygwin/spawn.cc +++ b/winsup/cygwin/spawn.cc @@ -647,13 +647,18 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv, 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, - mode != _P_OVERLAY && mode != _P_WAIT)) - { - c_flags |= EXTENDED_STARTUPINFO_PRESENT; - si_tmp = &si_pcon.StartupInfo; - enable_pcon = true; - } + { + bool nopcon = mode != _P_OVERLAY && mode != _P_WAIT; + bool background = ctty_pgid && ctty_pgid != myself->pgid; + if (!ptys_primary->term_has_pcon_cap (envblock, background)) + nopcon = true; + if (ptys_primary->setup_pseudoconsole (&si_pcon, nopcon)) + { + 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 diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc index d60f27545..e6d57ff6e 100644 --- a/winsup/cygwin/tty.cc +++ b/winsup/cygwin/tty.cc @@ -242,6 +242,7 @@ tty::init () term_code_page = 0; pcon_last_time = 0; pcon_start = false; + has_set_title = false; } HANDLE diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h index c491d3891..13af95687 100644 --- a/winsup/cygwin/tty.h +++ b/winsup/cygwin/tty.h @@ -101,6 +101,7 @@ private: UINT term_code_page; DWORD pcon_last_time; HANDLE h_pcon_write_pipe; + bool has_set_title; public: HANDLE from_master () const { return _from_master; } -- 2.28.0