public inbox for cygwin-patches@cygwin.com
 help / color / mirror / Atom feed
* [PATCH v7 0/4] Improve pseudo console support.
@ 2021-01-28  3:26 Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 1/4] Cygwin: pty: Inherit typeahead data between two input pipes Takashi Yano
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Takashi Yano @ 2021-01-28  3:26 UTC (permalink / raw)
  To: cygwin-patches

The new implementation of pseudo console support by commit bb428520
provides the important advantages, while there also has been several
disadvantages compared to the previous implementation.

These patches overturn some of them.

The disadvantage:
 1) The cygwin program which calls console API directly does not work.
is supposed to be able to be overcome as well, however, I am not sure
it is worth enough. This will need a lot of hooks for console APIs.
 --> Respecting Corinna's opinion, we decided not to implement this.

v3: Fix typeahead input issue in GDB. Several other bugs have also
    been fixed.
v4: Change the conditions for calling transfer_input() slightly in
    reset_switch_to_pcon() to avoid calling it if uncecessary or
    with no effect.
v5: Small bug fix in v4.
v6: Yet another bug fix.
    Add missing CloseHandle().
    Take into account when the master is running as a service (such
    as ssh session).
v7: Specify FILE_FLAG_OVERLAPPED for to_slave pipe to prevent
    PeekNamedPipe() from blocking in transfer_input().
    Simplify the code determining if the slave is reading.

Takashi Yano (4):
  Cygwin: pty: Inherit typeahead data between two input pipes.
  Cygwin: pty: Keep code page between non-cygwin apps.
  Cygwin: pty: Make apps using console APIs be able to debug with gdb.
  Cygwin: pty: Allow multiple apps to enable pseudo console
    simultaneously.

 winsup/cygwin/fhandler.h      |   22 +-
 winsup/cygwin/fhandler_tty.cc | 1123 +++++++++++++++++++++++++++------
 winsup/cygwin/select.cc       |    7 +-
 winsup/cygwin/spawn.cc        |  106 +++-
 winsup/cygwin/tty.cc          |   13 +-
 winsup/cygwin/tty.h           |   21 +-
 6 files changed, 1059 insertions(+), 233 deletions(-)

-- 
2.30.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 1/4] Cygwin: pty: Inherit typeahead data between two input pipes.
  2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
@ 2021-01-28  3:26 ` Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 2/4] Cygwin: pty: Keep code page between non-cygwin apps Takashi Yano
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Yano @ 2021-01-28  3:26 UTC (permalink / raw)
  To: cygwin-patches

- PTY has a problem that the key input, which is typed during windows
  native app is running, disappears when it returns to shell. This is
  beacuse pty has two input pipes, one is for cygwin apps and the other
  one is for native windows apps. The key input during windows native
  program is running is sent to the second input pipe while cygwin
  shell reads input from the first input pipe. This issue had been
  fixed once by commit 29431fcb, however, the new implementation of
  pseudo console support by commit bb428520 could not inherit this
  feature. This patch realize transfering input data between these
  two pipes bidirectionally by utilizing cygwin-console-helper process.
  The helper process is launched prior to starting the non-cygwin app,
  however, exits immediately unlike previous implementation.
---
 winsup/cygwin/fhandler.h      |  14 +-
 winsup/cygwin/fhandler_tty.cc | 545 +++++++++++++++++++++++++++-------
 winsup/cygwin/spawn.cc        |  82 +++--
 winsup/cygwin/tty.cc          |   2 -
 winsup/cygwin/tty.h           |   8 +-
 5 files changed, 504 insertions(+), 147 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index af1ef3a45..378e9c13b 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2291,6 +2291,13 @@ 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);
 
@@ -2340,7 +2347,7 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
-  bool setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon);
+  bool setup_pseudoconsole (bool nopcon);
   static void close_pseudoconsole (tty *ttyp);
   bool term_has_pcon_cap (const WCHAR *env);
   void set_switch_to_pcon (void);
@@ -2349,6 +2356,9 @@ 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);
+  HANDLE get_input_available_event (void) { return input_available_event; }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2361,6 +2371,8 @@ public:
     HANDLE from_master_cyg;
     HANDLE to_master;
     HANDLE to_master_cyg;
+    HANDLE to_slave;
+    HANDLE to_slave_cyg;
     HANDLE master_ctl;
     HANDLE input_available_event;
   };
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 7f0752614..f0b2cd60a 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -56,6 +56,8 @@ struct pipe_reply {
   HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
+  HANDLE to_slave;
+  HANDLE to_slave_cyg;
   DWORD error;
 };
 
@@ -848,8 +850,14 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
     return;
   if (isHybrid)
     return;
+  if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->h_pseudo_console)
+    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 ()->h_pseudo_console = NULL;
 }
 
 ssize_t __stdcall
@@ -891,6 +899,22 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 void
 fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
 {
+  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)
+    {
+      if (mask)
+	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+					    get_handle (),
+					    get_ttyp (), get_minor (),
+					    input_available_event);
+      else
+	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
+					    get_handle_cyg (),
+					    get_ttyp (), get_minor (),
+					    input_available_event);
+    }
   get_ttyp ()->mask_switch_to_pcon_in = mask;
 }
 
@@ -1591,7 +1615,7 @@ fhandler_pty_common::resize_pseudo_console (struct winsize *ws)
   size.X = ws->ws_col;
   size.Y = ws->ws_row;
   pinfo p (get_ttyp ()->pcon_pid);
-  if (p && !get_ttyp ()->do_not_resize_pcon)
+  if (p)
     {
       HPCON_INTERNAL hpcon_local;
       HANDLE pcon_owner =
@@ -1763,6 +1787,17 @@ fhandler_pty_master::write (const void *ptr, size_t len)
 	      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;
 	}
 
@@ -1983,7 +2018,7 @@ fhandler_pty_master::pty_master_thread (const master_thread_param_t *p)
   while (!exit && (ConnectNamedPipe (p->master_ctl, NULL)
 		   || GetLastError () == ERROR_PIPE_CONNECTED))
     {
-      pipe_reply repl = { NULL, NULL, NULL, NULL, 0 };
+      pipe_reply repl = { NULL, NULL, NULL, NULL, NULL, NULL, 0 };
       bool deimp = false;
       NTSTATUS allow = STATUS_ACCESS_DENIED;
       ACCESS_MASK access = EVENT_MODIFY_STATE;
@@ -2074,6 +2109,20 @@ fhandler_pty_master::pty_master_thread (const master_thread_param_t *p)
 	      termios_printf ("DuplicateHandle (to_master_cyg), %E");
 	      goto reply;
 	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), p->to_slave,
+				client, &repl.to_slave,
+				0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (to_slave), %E");
+	      goto reply;
+	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), p->to_slave_cyg,
+				client, &repl.to_slave_cyg,
+				0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (to_slave_cyg), %E");
+	      goto reply;
+	    }
 	}
 reply:
       repl.error = GetLastError ();
@@ -2130,22 +2179,44 @@ fhandler_pty_master::pty_master_fwd_thread (const master_fwd_thread_param_t *p)
       char *ptr = outbuf;
       if (p->ttyp->h_pseudo_console)
 	{
-	  if (!p->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))))
-		{
-		  memmove (p0, p1 + 1, rlen - (p1 + 1 - outbuf));
-		  rlen -= p1 + 1 - p0;
-		  wlen = rlen;
-		}
-	    }
-	  /* Remove CSI > Pm m */
+	  /* Avoid setting window title to "cygwin-console-helper.exe" */
 	  int state = 0;
 	  int start_at = 0;
+	  for (DWORD i=0; i<rlen; i++)
+	    if (outbuf[i] == '\033')
+	      {
+		start_at = i;
+		state = 1;
+		continue;
+	      }
+	    else if ((state == 1 && outbuf[i] == ']') ||
+		     (state == 2 && outbuf[i] == '0') ||
+		     (state == 3 && outbuf[i] == ';'))
+	      {
+		state ++;
+		continue;
+	      }
+	    else if (state == 4 && outbuf[i] == '\a')
+	      {
+		const char *helper_str = "\\bin\\cygwin-console-helper.exe";
+		if (memmem (&outbuf[start_at], i + 1 - start_at,
+			    helper_str, strlen (helper_str)))
+		  {
+		    memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+		    rlen = wlen = start_at + rlen - i - 1;
+		  }
+		state = 0;
+		continue;
+	      }
+	    else if (outbuf[i] == '\a')
+	      {
+		state = 0;
+		continue;
+	      }
+
+	  /* Remove CSI > Pm m */
+	  state = 0;
+	  start_at = 0;
 	  for (DWORD i = 0; i < rlen; i++)
 	    if (outbuf[i] == '\033')
 	      {
@@ -2419,6 +2490,8 @@ fhandler_pty_master::setup ()
   t.set_from_master_cyg (from_master_cyg);
   t.set_to_master (to_master);
   t.set_to_master_cyg (to_master_cyg);
+  t.set_to_slave (to_slave);
+  t.set_to_slave_cyg (get_output_handle ());
   t.winsize.ws_col = 80;
   t.winsize.ws_row = 25;
   t.master_pid = myself->pid;
@@ -2579,10 +2652,15 @@ fhandler_pty_common::process_opost_output (HANDLE h, const void *ptr,
   return res;
 }
 
+  /* 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(). */
 bool
-fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
+fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
 {
-
   /* Setting switch_to_pcon_in is necessary even if
      pseudo console will not be activated. */
   fhandler_base *fh = ::cygheap->fdtab[0];
@@ -2613,60 +2691,151 @@ fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
       return false;
     }
 
-  COORD size = {
-    (SHORT) get_ttyp ()->winsize.ws_col,
-    (SHORT) get_ttyp ()->winsize.ws_row
-  };
-  const DWORD inherit_cursor = 1;
-  SetLastError (ERROR_SUCCESS);
-  HRESULT res = CreatePseudoConsole (size, get_handle (), get_output_handle (),
-				     inherit_cursor,
-				     &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;
+  STARTUPINFOEXW si;
+  PROCESS_INFORMATION pi;
+  HANDLE hello, goodbye;
+  HANDLE hr, hw;
+  HANDLE hpConIn, hpConOut;
 
-  {
-    fhandler_base *fh0 = ::cygheap->fdtab[0];
-    if (fh0 && fh0->get_device () != get_device ())
-      si->StartupInfo.hStdInput = fh0->get_handle ();
-    fhandler_base *fh1 = ::cygheap->fdtab[1];
-    if (fh1 && fh1->get_device () != get_device ())
-      {
-	get_ttyp ()->do_not_resize_pcon = true;
-	si->StartupInfo.hStdOutput = fh1->get_output_handle ();
-      }
-    fhandler_base *fh2 = ::cygheap->fdtab[2];
-    if (fh2 && fh2->get_device () != get_device ())
-      si->StartupInfo.hStdError = fh2->get_output_handle ();
-  }
+  do
+    {
+      COORD size = {
+	(SHORT) get_ttyp ()->winsize.ws_col,
+	(SHORT) get_ttyp ()->winsize.ws_row
+      };
+      const DWORD inherit_cursor = 1;
+      SetLastError (ERROR_SUCCESS);
+      HRESULT res = CreatePseudoConsole (size, get_handle (),
+					 get_output_handle (),
+					 inherit_cursor,
+					 &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, 2, 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,
+					      2, 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;
+
+      hello = CreateEvent (&sec_none, true, false, NULL);
+      goodbye = CreateEvent (&sec_none, true, false, NULL);
+      CreatePipe (&hr, &hw, &sec_none, 0);
+
+      HANDLE handles_to_inherit[] = {hello, goodbye, hw};
+      if (!UpdateProcThreadAttribute (si.lpAttributeList,
+				      0,
+				      PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+				      handles_to_inherit,
+				      sizeof (handles_to_inherit),
+				      NULL, NULL))
+	goto cleanup_event_and_pipes;
+
+      /* Execute 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.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+      si.StartupInfo.hStdInput = NULL;
+      si.StartupInfo.hStdOutput = NULL;
+      si.StartupInfo.hStdError = NULL;
+
+      get_ttyp ()->pcon_start = true;
+      if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none,
+			   TRUE, EXTENDED_STARTUPINFO_PRESENT,
+			   NULL, NULL, &si.StartupInfo, &pi))
+	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.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.hThread);
+
+      /* Duplicate 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.hProcess, hpConIn,
+			    GetCurrentProcess (), &hpConIn, 0,
+			    TRUE, DUPLICATE_SAME_ACCESS))
+	goto cleanup_helper_process;
+      if (!DuplicateHandle (pi.hProcess, hpConOut,
+			    GetCurrentProcess (), &hpConOut, 0,
+			    TRUE, DUPLICATE_SAME_ACCESS))
+	goto cleanup_pcon_in;
+
+      CloseHandle (hr);
+      CloseHandle (hw);
+      DeleteProcThreadAttributeList (si.lpAttributeList);
+      HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
+
+      /* Attach to pseudo console */
+      FreeConsole ();
+      AttachConsole (pi.dwProcessId);
+
+      /* Terminate helper process */
+      SetEvent (goodbye);
+      WaitForSingleObject (pi.hProcess, INFINITE);
+      CloseHandle (goodbye);
+      CloseHandle (pi.hProcess);
+
+      /* Set handle */
+      HANDLE orig_input_handle = get_handle ();
+      HANDLE orig_output_handle = get_output_handle ();
+      cygheap_fdenum cfd (false);
+      while (cfd.next () >= 0)
+	if (cfd->get_device () == get_device ())
+	  {
+	    fhandler_base *fh = cfd;
+	    fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	    if (ptys->get_handle () == orig_input_handle)
+	      ptys->set_handle (hpConIn);
+	    if (ptys->get_output_handle () == orig_output_handle)
+	      ptys->set_output_handle (hpConOut);
+	  }
+      CloseHandle (orig_input_handle);
+      CloseHandle (orig_output_handle);
+    }
+  while (false);
 
   if (get_ttyp ()->pcon_pid == 0 || !pinfo (get_ttyp ()->pcon_pid))
     get_ttyp ()->pcon_pid = myself->pid;
@@ -2676,11 +2845,23 @@ fhandler_pty_slave::setup_pseudoconsole (STARTUPINFOEXW *si, bool nopcon)
       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_pcon_in:
+  CloseHandle (hpConIn);
+cleanup_helper_process:
+  SetEvent (goodbye);
+  WaitForSingleObject (pi.hProcess, INFINITE);
+  CloseHandle (pi.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->lpAttributeList);
+  HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
 cleanup_pseudo_console:
   if (get_ttyp ()->h_pseudo_console)
     {
@@ -2690,6 +2871,7 @@ cleanup_pseudo_console:
       CloseHandle (tmp);
     }
 fallback:
+  get_ttyp ()->pcon_start = false;
   get_ttyp ()->h_pseudo_console = NULL;
   return false;
 }
@@ -2702,6 +2884,8 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
   if (ttyp->h_pseudo_console)
     {
       ttyp->wait_pcon_fwd ();
+      FreeConsole ();
+      AttachConsole (ATTACH_PARENT_PROCESS);
       HPCON_INTERNAL *hp = (HPCON_INTERNAL *) ttyp->h_pseudo_console;
       HANDLE tmp = hp->hConHostProcess;
       ClosePseudoConsole (ttyp->h_pseudo_console);
@@ -2710,7 +2894,6 @@ fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
       ttyp->switch_to_pcon_in = false;
       ttyp->pcon_pid = 0;
       ttyp->pcon_start = false;
-      ttyp->do_not_resize_pcon = false;
     }
 }
 
@@ -2793,7 +2976,6 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
   char buf[1024];
   char *p;
   int len;
-  int x1, y1, x2, y2;
   int wait_cnt = 0;
 
   /* Check if terminal has ANSI escape sequence. */
@@ -2805,6 +2987,7 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
   /* Set h_pseudo_console and pcon_start so that the response
      will sent to io_handle rather than io_handle_cyg. */
   get_ttyp ()->h_pseudo_console = (HPCON *) -1; /* dummy */
+  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);
@@ -2820,8 +3003,9 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 	  len -= n;
 	  *p = '\0';
 	  char *p1 = strrchr (buf, '\033');
+	  int x, y;
 	  char c;
-	  if (p1 == NULL || sscanf (p1, "\033[%d;%d%c", &y1, &x1, &c) != 3
+	  if (p1 == NULL || sscanf (p1, "\033[%d;%d%c", &y, &x, &c) != 3
 	      || c != 'R')
 	    continue;
 	  wait_cnt = 0;
@@ -2833,48 +3017,14 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 	Sleep (1);
     }
   while (len);
+  get_ttyp ()->h_pseudo_console = NULL;
+  get_ttyp ()->pcon_pid = 0;
   if (len == 0)
     goto not_has_csi6n;
 
   get_ttyp ()->has_csi6n = true;
   get_ttyp ()->pcon_cap_checked = true;
 
-  /* Check if terminal has set-title capability */
-  WaitForSingleObject (input_mutex, INFINITE);
-  /* Set pcon_start again because it should be cleared
-     in master write(). */
-  get_ttyp ()->pcon_start = true;
-  WriteFile (get_output_handle_cyg (), "\033]0;\033\\\033[6n", 10, &n, NULL);
-  ReleaseMutex (input_mutex);
-  p = buf;
-  len = sizeof (buf) - 1;
-  do
-    {
-      ReadFile (get_handle (), p, len, &n, NULL);
-      p += n;
-      len -= n;
-      *p = '\0';
-      char *p2 = strrchr (buf, '\033');
-      char c;
-      if (p2 == NULL || sscanf (p2, "\033[%d;%d%c", &y2, &x2, &c) != 3
-	  || c != 'R')
-	continue;
-      break;
-    }
-  while (len);
-  get_ttyp ()->h_pseudo_console = NULL;
-
-  if (len == 0)
-    return true;
-
-  if (x2 == x1 && y2 == y1)
-    /* If "\033]0;\033\\" does not move cursor position,
-       set-title is supposed to be supported. */
-    get_ttyp ()->has_set_title = true;
-  else
-    /* Try to erase garbage string caused by "\033]0;\033\\" */
-    for (int i=0; i<x2-x1; i++)
-      WriteFile (get_output_handle_cyg (), "\b \b", 3, &n, NULL);
   return true;
 
 not_has_csi6n:
@@ -2914,6 +3064,8 @@ fhandler_pty_master::get_master_thread_param (master_thread_param_t *p)
   p->from_master_cyg = from_master_cyg;
   p->to_master = to_master;
   p->to_master_cyg = to_master_cyg;
+  p->to_slave = to_slave;
+  p->to_slave_cyg = get_output_handle ();
   p->master_ctl = master_ctl;
   p->input_available_event = input_available_event;
   SetEvent (thread_param_copied_event);
@@ -2928,3 +3080,172 @@ fhandler_pty_master::get_master_fwd_thread_param (master_fwd_thread_param_t *p)
   p->ttyp = get_ttyp ();
   SetEvent (thread_param_copied_event);
 }
+
+#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)
+{
+  HANDLE to;
+  if (dir == to_nat)
+    to = ttyp->to_slave ();
+  else
+    to = ttyp->to_slave_cyg ();
+
+  pinfo p (ttyp->master_pid);
+  HANDLE pty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->dwProcessId);
+  if (pty_owner)
+    {
+      DuplicateHandle (pty_owner, to, GetCurrentProcess (), &to,
+		       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, unit);
+      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)
+	to = repl.to_slave;
+      else
+	to = repl.to_slave_cyg;
+    }
+
+  UINT cp_from = 0, cp_to = 0;
+
+  if (dir == to_nat)
+    {
+      cp_from = ttyp->term_code_page;
+      if (ttyp->h_pseudo_console)
+	cp_to = CP_UTF8;
+      else
+	cp_to = GetConsoleCP ();
+    }
+  else
+    {
+      cp_from = GetConsoleCP ();
+      cp_to = ttyp->term_code_page;
+    }
+
+  tmp_pathbuf tp;
+  char *buf = tp.c_get ();
+
+  bool transfered = false;
+
+  if (dir == to_cyg && ttyp->h_pseudo_console)
+    { /* from handle is console handle */
+      INPUT_RECORD r[INREC_SIZE];
+      DWORD n;
+      while (PeekConsoleInputA (from, r, INREC_SIZE, &n) && n)
+	{
+	  ReadConsoleInputA (from, r, n, &n);
+	  int len = 0;
+	  char *ptr = buf;
+	  for (DWORD i = 0; i < n; i++)
+	    if (r[i].EventType == KEY_EVENT && r[i].Event.KeyEvent.bKeyDown)
+	      {
+		DWORD ctrl_key_state = r[i].Event.KeyEvent.dwControlKeyState;
+		if (r[i].Event.KeyEvent.uChar.AsciiChar)
+		  {
+		    if ((ctrl_key_state & ALT_PRESSED)
+			&& r[i].Event.KeyEvent.uChar.AsciiChar <= 0x7f)
+		      buf[len++] = '\033'; /* Meta */
+		    buf[len++] = r[i].Event.KeyEvent.uChar.AsciiChar;
+		  }
+		/* Allow Ctrl-Space to emit ^@ */
+		else if (r[i].Event.KeyEvent.wVirtualKeyCode == '2'
+			 && (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
+		       sequence rather than xterm sequence. */
+		    char tmp[16];
+		    const char *add =
+		      fhandler_console::get_nonascii_key (r[i], tmp);
+		    if (add)
+		      {
+			strcpy (buf + len, add);
+			len += strlen (add);
+		      }
+		  }
+	      }
+	  if (cp_to != cp_from)
+	    {
+	      static mbstate_t mbp;
+	      char *mbbuf = tp.c_get ();
+	      size_t nlen = NT_MAX_PATH;
+	      convert_mb_str (cp_to, mbbuf, &nlen, cp_from, buf, len, &mbp);
+	      ptr = mbbuf;
+	      len = nlen;
+	    }
+	  /* Call WriteFile() line by line */
+	  char *p0 = ptr;
+	  char *p1 = ptr;
+	  while ((p1 = (char *) memchr (p0, '\r', len - (p0 - ptr))))
+	    {
+	      *p1 = '\n';
+	      n = p1 - p0 + 1;
+	      if (n && WriteFile (to, p0, n, &n, NULL) && n)
+		transfered = true;
+	      p0 = p1 + 1;
+	    }
+	  n = len - (p0 - ptr);
+	  if (n && WriteFile (to, p0, n, &n, NULL) && n)
+	    transfered = true;
+	}
+    }
+  else
+    {
+      DWORD bytes_in_pipe;
+      while (::bytes_available (bytes_in_pipe, from) && bytes_in_pipe)
+	{
+	  DWORD n = MIN (bytes_in_pipe, NT_MAX_PATH);
+	  ReadFile (from, buf, n, &n, NULL);
+	  char *ptr = buf;
+	  if (dir == to_nat && ttyp->h_pseudo_console)
+	    {
+	      char *p = buf;
+	      while ((p = (char *) memchr (p, '\n', n - (p - buf))))
+		*p = '\r';
+	    }
+	  if (cp_to != cp_from)
+	    {
+	      static mbstate_t mbp;
+	      char *mbbuf = tp.c_get ();
+	      size_t nlen = NT_MAX_PATH;
+	      convert_mb_str (cp_to, mbbuf, &nlen, cp_from, buf, n, &mbp);
+	      ptr = mbbuf;
+	      n = nlen;
+	    }
+	  if (n && WriteFile (to, ptr, n, &n, NULL) && n)
+	    transfered = true;;
+	}
+    }
+
+  if (dir == to_nat)
+    ResetEvent (input_available_event);
+  else if (transfered)
+    SetEvent (input_available_event);
+}
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index d03492ee6..b80a20f13 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -653,6 +653,36 @@ 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;
+      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;
+	  if (ptys_primary->setup_pseudoconsole (nopcon))
+	    enable_pcon = true;
+	  ptys_ttyp = ptys_primary->get_ttyp ();
+	  ptys_unit = ptys_primary->get_minor ();
+	  ptys_from_master = ptys_primary->get_handle ();
+	  DuplicateHandle (GetCurrentProcess (), ptys_from_master,
+			   GetCurrentProcess (), &ptys_from_master,
+			   0, 0, DUPLICATE_SAME_ACCESS);
+	  ptys_input_available_event =
+	    ptys_primary->get_input_available_event ();
+	  DuplicateHandle (GetCurrentProcess (), ptys_input_available_event,
+			   GetCurrentProcess (), &ptys_input_available_event,
+			   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);
+	}
+
       /* Set up needed handles for stdio */
       si.dwFlags = STARTF_USESTDHANDLES;
       si.hStdInput = handle ((in__stdin < 0 ? 0 : in__stdin), false);
@@ -664,25 +694,6 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       if (!iscygwin ())
 	init_console_handler (myself->ctty > 0);
 
-      bool enable_pcon = false;
-      tty *ptys_ttyp = NULL;
-      STARTUPINFOEXW si_pcon;
-      ZeroMemory (&si_pcon, sizeof (si_pcon));
-      STARTUPINFOW *si_tmp = &si;
-      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;
-	  if (ptys_primary->setup_pseudoconsole (&si_pcon, nopcon))
-	    {
-	      c_flags |= EXTENDED_STARTUPINFO_PRESENT;
-	      si_tmp = &si_pcon.StartupInfo;
-	      enable_pcon = true;
-	      ptys_ttyp = ptys_primary->get_ttyp ();
-	    }
-	}
-
     loop:
       /* When ruid != euid we create the new process under the current original
 	 account and impersonate in child, this way maintaining the different
@@ -711,7 +722,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       si_tmp,
+			       &si,
 			       &pi);
 	}
       else
@@ -765,7 +776,7 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 			       c_flags,
 			       envblock,	/* environment */
 			       NULL,
-			       si_tmp,
+			       &si,
 			       &pi);
 	  if (hwst)
 	    {
@@ -778,11 +789,6 @@ 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,
@@ -954,14 +960,21 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	    }
 	  if (sem)
 	    __posix_spawn_sem_release (sem, 0);
-	  if (enable_pcon)
+	  if (enable_pcon || ptys_ttyp || cons_native)
+	    WaitForSingleObject (pi.hProcess, INFINITE);
+	  if (ptys_ttyp)
 	    {
-	      WaitForSingleObject (pi.hProcess, INFINITE);
-	      fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
+	      fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+						  ptys_from_master,
+						  ptys_ttyp, ptys_unit,
+						  ptys_input_available_event);
+	      CloseHandle (ptys_from_master);
+	      CloseHandle (ptys_input_available_event);
 	    }
+	  if (enable_pcon)
+	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
 	  else if (cons_native)
 	    {
-	      WaitForSingleObject (pi.hProcess, INFINITE);
 	      fhandler_console::request_xterm_mode_output (true,
 							   &cons_handle_set);
 	      fhandler_console::request_xterm_mode_input (true,
@@ -975,6 +988,15 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  system_call.arm ();
 	  if (waitpid (cygpid, &res, 0) != cygpid)
 	    res = -1;
+	  if (ptys_ttyp)
+	    {
+	      fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
+						  ptys_from_master,
+						  ptys_ttyp, ptys_unit,
+						  ptys_input_available_event);
+	      CloseHandle (ptys_from_master);
+	      CloseHandle (ptys_input_available_event);
+	    }
 	  if (enable_pcon)
 	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
 	  else if (cons_native)
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index c6e13f111..436f5c6c3 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -244,8 +244,6 @@ tty::init ()
   pcon_start = false;
   pcon_cap_checked = false;
   has_csi6n = false;
-  has_set_title = false;
-  do_not_resize_pcon = false;
   need_invisible_console = false;
   invisible_console_pid = 0;
 }
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index a975aba45..eb604588c 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -93,6 +93,8 @@ private:
   HANDLE _from_master_cyg;
   HANDLE _to_master;
   HANDLE _to_master_cyg;
+  HANDLE _to_slave;
+  HANDLE _to_slave_cyg;
   HPCON h_pseudo_console;
   bool pcon_start;
   bool switch_to_pcon_in;
@@ -103,8 +105,6 @@ private:
   HANDLE h_pcon_write_pipe;
   bool pcon_cap_checked;
   bool has_csi6n;
-  bool has_set_title;
-  bool do_not_resize_pcon;
   bool need_invisible_console;
   pid_t invisible_console_pid;
 
@@ -113,10 +113,14 @@ public:
   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; }
+  HANDLE to_slave () const { return _to_slave; }
+  HANDLE to_slave_cyg () const { return _to_slave_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; }
+  void set_to_slave (HANDLE h) { _to_slave = h; }
+  void set_to_slave_cyg (HANDLE h) { _to_slave_cyg = h; }
 
   int read_retval;
   bool was_opened;	/* True if opened at least once. */
-- 
2.30.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 2/4] Cygwin: pty: Keep code page between non-cygwin apps.
  2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 1/4] Cygwin: pty: Inherit typeahead data between two input pipes Takashi Yano
@ 2021-01-28  3:26 ` Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 3/4] Cygwin: pty: Make apps using console APIs be able to debug with gdb Takashi Yano
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Yano @ 2021-01-28  3:26 UTC (permalink / raw)
  To: cygwin-patches

- After commit bb428520, there has been the disadvantage:
  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.
  This patch clears this disadvantage.
---
 winsup/cygwin/fhandler_tty.cc | 7 +++++++
 winsup/cygwin/tty.cc          | 2 ++
 winsup/cygwin/tty.h           | 2 ++
 3 files changed, 11 insertions(+)

diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index f0b2cd60a..34cff2ae5 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -2845,6 +2845,11 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
       HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console;
       get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe;
     }
+
+  if (get_ttyp ()->previous_code_page)
+    SetConsoleCP (get_ttyp ()->previous_code_page);
+  if (get_ttyp ()->previous_output_code_page)
+    SetConsoleOutputCP (get_ttyp ()->previous_output_code_page);
   return true;
 
 cleanup_pcon_in:
@@ -2884,6 +2889,8 @@ 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 ();
       AttachConsole (ATTACH_PARENT_PROCESS);
       HPCON_INTERNAL *hp = (HPCON_INTERNAL *) ttyp->h_pseudo_console;
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index 436f5c6c3..908166a37 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -246,6 +246,8 @@ tty::init ()
   has_csi6n = false;
   need_invisible_console = false;
   invisible_console_pid = 0;
+  previous_code_page = 0;
+  previous_output_code_page = 0;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index eb604588c..061357437 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -107,6 +107,8 @@ private:
   bool has_csi6n;
   bool need_invisible_console;
   pid_t invisible_console_pid;
+  UINT previous_code_page;
+  UINT previous_output_code_page;
 
 public:
   HANDLE from_master () const { return _from_master; }
-- 
2.30.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 3/4] Cygwin: pty: Make apps using console APIs be able to debug with gdb.
  2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 1/4] Cygwin: pty: Inherit typeahead data between two input pipes Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 2/4] Cygwin: pty: Keep code page between non-cygwin apps Takashi Yano
@ 2021-01-28  3:26 ` Takashi Yano
  2021-01-28  3:26 ` [PATCH v7 4/4] Cygwin: pty: Allow multiple apps to enable pseudo console simultaneously Takashi Yano
  2021-01-28 10:25 ` [PATCH v7 0/4] Improve pseudo console support Corinna Vinschen
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Yano @ 2021-01-28  3:26 UTC (permalink / raw)
  To: cygwin-patches

- After commit bb428520, there has been the disadvantage:
  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 app, in which pseudo console is already
     activated, works.
  This patch clears this disadvantage.
---
 winsup/cygwin/fhandler.h      |   3 +
 winsup/cygwin/fhandler_tty.cc | 299 ++++++++++++++++++++++++++++++----
 winsup/cygwin/select.cc       |   5 +-
 winsup/cygwin/spawn.cc        |   2 +
 winsup/cygwin/tty.cc          |   7 +-
 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 378e9c13b..3db33ea78 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 34cff2ae5..c74e38c3d 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 d6c13241e..4e7946435 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 b80a20f13..383a5a03e 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 908166a37..e6c0b680e 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 061357437..47be8e4a0 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;
-- 
2.30.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 4/4] Cygwin: pty: Allow multiple apps to enable pseudo console simultaneously.
  2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
                   ` (2 preceding siblings ...)
  2021-01-28  3:26 ` [PATCH v7 3/4] Cygwin: pty: Make apps using console APIs be able to debug with gdb Takashi Yano
@ 2021-01-28  3:26 ` Takashi Yano
  2021-01-28 10:25 ` [PATCH v7 0/4] Improve pseudo console support Corinna Vinschen
  4 siblings, 0 replies; 6+ messages in thread
From: Takashi Yano @ 2021-01-28  3:26 UTC (permalink / raw)
  To: cygwin-patches

- After commit bb428520, there has been the disadvantage:
  7) Pseudo console cannot be activated if it is already activated for
     another process on same pty.
  This patch clears this disadvantage.
---
 winsup/cygwin/fhandler.h      |   7 +-
 winsup/cygwin/fhandler_tty.cc | 344 ++++++++++++++++++++++++++--------
 winsup/cygwin/select.cc       |   4 +-
 winsup/cygwin/spawn.cc        |  44 +++--
 winsup/cygwin/tty.cc          |   2 +-
 winsup/cygwin/tty.h           |   6 +-
 6 files changed, 314 insertions(+), 93 deletions(-)

diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 3db33ea78..a4c1a3816 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2273,6 +2273,9 @@ class fhandler_pty_common: public fhandler_termios
   }
 
   void resize_pseudo_console (struct winsize *);
+  static DWORD get_console_process_id (DWORD pid, bool match,
+				       bool cygwin = false,
+				       bool stub_only = false);
 
  protected:
   static BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len,
@@ -2354,14 +2357,14 @@ class fhandler_pty_slave: public fhandler_pty_common
   bool term_has_pcon_cap (const WCHAR *env);
   void set_switch_to_pcon (void);
   void reset_switch_to_pcon (void);
-  void mask_switch_to_pcon_in (bool mask);
+  void mask_switch_to_pcon_in (bool mask, bool xfer);
   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);
   HANDLE get_input_available_event (void) { return input_available_event; }
-  bool pcon_activated (void) { return get_ttyp ()->h_pseudo_console; }
+  bool pcon_activated (void) { return get_ttyp ()->pcon_activated; }
 };
 
 #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 c74e38c3d..06fc19ac2 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -64,8 +64,9 @@ struct pipe_reply {
 
 extern HANDLE attach_mutex; /* Defined in fhandler_console.cc */
 
-static DWORD
-get_console_process_id (DWORD pid, bool match)
+DWORD
+fhandler_pty_common::get_console_process_id (DWORD pid, bool match,
+					     bool cygwin, bool stub_only)
 {
   tmp_pathbuf tp;
   DWORD *list = (DWORD *) tp.c_get ();
@@ -75,16 +76,34 @@ get_console_process_id (DWORD pid, bool match)
   if (num == 0 || num > buf_size)
     return 0;
 
-  DWORD res = 0;
+  DWORD res_pri = 0, res = 0;
   /* Last one is the oldest. */
   /* https://github.com/microsoft/terminal/issues/95 */
   for (int i = (int) num - 1; i >= 0; i--)
     if ((match && list[i] == pid) || (!match && list[i] != pid))
       {
-	res = list[i];
-	break;
+	if (!cygwin)
+	  {
+	    res_pri = list[i];
+	    break;
+	  }
+	else
+	  {
+	    pinfo p (cygwin_pid (list[i]));
+	    if (!!p && p->dwProcessId && p->exec_dwProcessId
+		&& p->dwProcessId != p->exec_dwProcessId)
+	      {
+		res_pri = list[i];
+		break;
+	      }
+	    if (!!p && !res)
+	      res = list[i];
+	  }
       }
-  return res;
+  if (stub_only)
+    return res_pri;
+  else
+    return res_pri ?: res;
 }
 
 static bool isHybrid;
@@ -937,7 +956,7 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
 void
 fhandler_pty_slave::set_switch_to_pcon (void)
 {
-  if (!get_ttyp ()->switch_to_pcon_in)
+  if (!isHybrid)
     {
       isHybrid = true;
       setup_locale ();
@@ -964,9 +983,10 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
 	{
 	  CloseHandle (h_gdb_process);
 	  h_gdb_process = NULL;
-	  if (isHybrid && get_ttyp ()->pcon_pid == myself->pid)
+	  if (isHybrid)
 	    {
-	      if (get_ttyp ()->switch_to_pcon_in)
+	      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 (),
@@ -976,7 +996,7 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
 		   the console of the parent process will fail.
 		   Therefore, never close pseudo console here. */
 		return;
-	      bool need_restore_handles = !!get_ttyp ()->h_pseudo_console;
+	      bool need_restore_handles = get_ttyp ()->pcon_activated;
 	      close_pseudoconsole (get_ttyp ());
 	      if (need_restore_handles)
 		{
@@ -1027,11 +1047,8 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
 		  if (fix_err)
 		    SetStdHandle (STD_ERROR_HANDLE, get_output_handle ());
 		}
-	      get_ttyp ()->pcon_pid = 0;
-	      get_ttyp ()->switch_to_pcon_in = false;
+	      isHybrid = false;
 	    }
-	  isHybrid = false;
-	  return;
 	}
     }
   if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid
@@ -1040,14 +1057,14 @@ fhandler_pty_slave::reset_switch_to_pcon (void)
     return;
   if (isHybrid)
     return;
-  if (get_ttyp ()->switch_to_pcon_in && !get_ttyp ()->h_pseudo_console)
+  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 ()->h_pseudo_console = NULL;
+  get_ttyp ()->pcon_activated = false;
 }
 
 ssize_t __stdcall
@@ -1087,7 +1104,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 }
 
 void
-fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
+fhandler_pty_slave::mask_switch_to_pcon_in (bool mask, bool xfer)
 {
   char name[MAX_PATH];
   shared_name (name, TTY_SLAVE_READING, get_minor ());
@@ -1102,21 +1119,63 @@ fhandler_pty_slave::mask_switch_to_pcon_in (bool mask)
   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)
-      && !!masked != mask)
-    {
+  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)
+	{
+	  pinfo p (get_ttyp ()->pcon_pid);
+	  if (p)
+	    pcon_winpid = p->exec_dwProcessId ?: p->dwProcessId;
+	}
       if (mask)
-	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_cyg,
-					    get_handle (),
-					    get_ttyp (), get_minor (),
-					    input_available_event);
+	{
+	  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
-	fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
-					    get_handle_cyg (),
-					    get_ttyp (), get_minor (),
-					    input_available_event);
+	{
+	  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);
+	}
     }
   return;
 }
@@ -1143,6 +1202,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
   DWORD bytes_in_pipe;
   char buf[INP_BUFFER_SIZE];
   DWORD time_to_wait;
+  char *ptr0 = (char *) ptr;
 
   bg_check_types bg = bg_check (SIGTTIN);
   if (bg <= bg_eof)
@@ -1157,7 +1217,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 
   if (ptr) /* Indicating not tcflush(). */
     {
-      mask_switch_to_pcon_in (true);
+      mask_switch_to_pcon_in (true, true);
       reset_switch_to_pcon ();
     }
 
@@ -1271,7 +1331,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       if (ptr && !bytes_in_pipe && !vmin && !time_to_wait)
 	{
 	  ReleaseMutex (input_mutex);
-	  mask_switch_to_pcon_in (false);
+	  mask_switch_to_pcon_in (false, false);
 	  len = (size_t) bytes_in_pipe;
 	  return;
 	}
@@ -1376,7 +1436,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 out:
   termios_printf ("%d = read(%p, %lu)", totalread, ptr, len);
   len = (size_t) totalread;
-  mask_switch_to_pcon_in (false);
+  mask_switch_to_pcon_in (false, totalread > 0 && ptr0[totalread - 1] == '\n');
 }
 
 int
@@ -1417,7 +1477,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
       {
 	if (get_ttyp ()->pcon_start)
 	  t->c_lflag &= ~(ICANON | ECHO);
-	if (get_ttyp ()->h_pseudo_console)
+	if (get_ttyp ()->pcon_activated)
 	  t->c_iflag &= ~ICRNL;
 	break;
       }
@@ -1532,7 +1592,7 @@ fhandler_pty_slave::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)
 	{
-	  if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid)
+	  if (get_ttyp ()->pcon_activated && get_ttyp ()->pcon_pid)
 	    resize_pseudo_console ((struct winsize *) arg);
 	  get_ttyp ()->arg.winsize = *(struct winsize *) arg;
 	  get_ttyp ()->winsize = *(struct winsize *) arg;
@@ -1947,7 +2007,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 () && get_ttyp ()->h_pseudo_console)
+  if (to_be_read_from_pcon () && get_ttyp ()->pcon_activated)
     {
       tmp_pathbuf tp;
       char *buf = (char *) ptr;
@@ -2042,7 +2102,7 @@ fhandler_pty_master::tcgetattr (struct termios *t)
   /* Workaround for rlwrap v0.40 or later */
   if (get_ttyp ()->pcon_start)
     t->c_lflag &= ~(ICANON | ECHO);
-  if (get_ttyp ()->h_pseudo_console)
+  if (get_ttyp ()->pcon_activated)
     t->c_iflag &= ~ICRNL;
   return 0;
 }
@@ -2091,7 +2151,7 @@ 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)
 	{
-	  if (get_ttyp ()->h_pseudo_console && get_ttyp ()->pcon_pid)
+	  if (get_ttyp ()->pcon_activated && get_ttyp ()->pcon_pid)
 	    resize_pseudo_console ((struct winsize *) arg);
 	  get_ttyp ()->winsize = *(struct winsize *) arg;
 	  get_ttyp ()->kill_pgrp (SIGWINCH);
@@ -2385,7 +2445,7 @@ fhandler_pty_master::pty_master_fwd_thread (const master_fwd_thread_param_t *p)
 	}
       ssize_t wlen = rlen;
       char *ptr = outbuf;
-      if (p->ttyp->h_pseudo_console)
+      if (p->ttyp->pcon_activated)
 	{
 	  /* Avoid setting window title to "cygwin-console-helper.exe" */
 	  int state = 0;
@@ -2899,9 +2959,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
 
   if (nopcon)
     return false;
-  if (get_ttyp ()->pcon_pid && get_ttyp ()->pcon_pid != myself->pid
-      && !!pinfo (get_ttyp ()->pcon_pid))
-    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. */
@@ -2915,11 +2973,32 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
       return false;
     }
 
+  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)
+    {
+      /* Attach to the pseudo console which already exits. */
+      pinfo p (get_ttyp ()->pcon_pid);
+      HANDLE pcon_owner =
+	OpenProcess (PROCESS_DUP_HANDLE, FALSE, p->exec_dwProcessId);
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_in,
+		       GetCurrentProcess (), &hpConIn,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      DuplicateHandle (pcon_owner, get_ttyp ()->h_pcon_out,
+		       GetCurrentProcess (), &hpConOut,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      CloseHandle (pcon_owner);
+      FreeConsole ();
+      AttachConsole (p->dwProcessId);
+      goto skip_create;
+    }
+
   STARTUPINFOEXW si;
   PROCESS_INFORMATION pi;
   HANDLE hello, goodbye;
   HANDLE hr, hw;
-  HANDLE hpConIn, hpConOut;
+  HPCON hpcon;
 
   do
     {
@@ -2928,11 +3007,11 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
 	(SHORT) get_ttyp ()->winsize.ws_row
       };
       const DWORD inherit_cursor = 1;
+      hpcon = NULL;
       SetLastError (ERROR_SUCCESS);
       HRESULT res = CreatePseudoConsole (size, get_handle (),
 					 get_output_handle (),
-					 inherit_cursor,
-					 &get_ttyp ()->h_pseudo_console);
+					 inherit_cursor, &hpcon);
       if (res != S_OK || GetLastError () == ERROR_PROC_NOT_FOUND)
 	{
 	  if (res != S_OK)
@@ -2955,9 +3034,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
       if (!UpdateProcThreadAttribute (si.lpAttributeList,
 				      0,
 				      PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
-				      get_ttyp ()->h_pseudo_console,
-				      sizeof (get_ttyp ()->h_pseudo_console),
-				      NULL, NULL))
+				      hpcon, sizeof (hpcon), NULL, NULL))
 
 	goto cleanup_heap;
 
@@ -2986,6 +3063,7 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
       si.StartupInfo.hStdError = NULL;
 
       get_ttyp ()->pcon_start = true;
+      get_ttyp ()->pcon_activated = true;
       if (!CreateProcessW (NULL, cmd, &sec_none, &sec_none,
 			   TRUE, EXTENDED_STARTUPINFO_PRESENT,
 			   NULL, NULL, &si.StartupInfo, &pi))
@@ -3041,7 +3119,12 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
       WaitForSingleObject (pi.hProcess, INFINITE);
       CloseHandle (goodbye);
       CloseHandle (pi.hProcess);
+    }
+  while (false);
 
+skip_create:
+  do
+    {
       /* Set handle */
       if (GetStdHandle (STD_INPUT_HANDLE) == get_handle ())
 	SetStdHandle (STD_INPUT_HANDLE, hpConIn);
@@ -3072,16 +3155,29 @@ fhandler_pty_slave::setup_pseudoconsole (bool nopcon)
   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)
+  if (hpcon && get_ttyp ()->pcon_pid == myself->pid)
     {
-      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console;
+      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) hpcon;
       get_ttyp ()->h_pcon_write_pipe = hp->hWritePipe;
+      get_ttyp ()->h_pcon_condrv_reference = hp->hConDrvReference;
+      get_ttyp ()->h_pcon_conhost_process = hp->hConHostProcess;
+      DuplicateHandle (GetCurrentProcess (), hpConIn,
+		       GetCurrentProcess (), &get_ttyp ()->h_pcon_in,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      DuplicateHandle (GetCurrentProcess (), hpConOut,
+		       GetCurrentProcess (), &get_ttyp ()->h_pcon_out,
+		       0, TRUE, DUPLICATE_SAME_ACCESS);
+      /* Discard the pseudo console handler container here.
+	 Reconstruct it temporary when it is needed. */
+      HeapFree (GetProcessHeap (), 0, hp);
     }
 
   if (get_ttyp ()->previous_code_page)
     SetConsoleCP (get_ttyp ()->previous_code_page);
   if (get_ttyp ()->previous_output_code_page)
     SetConsoleOutputCP (get_ttyp ()->previous_output_code_page);
+
+  release_output_mutex ();
   return true;
 
 cleanup_pcon_in:
@@ -3093,6 +3189,8 @@ cleanup_helper_process:
   goto skip_close_hello;
 cleanup_event_and_pipes:
   CloseHandle (hello);
+  get_ttyp ()->pcon_start = false;
+  get_ttyp ()->pcon_activated = false;
 skip_close_hello:
   CloseHandle (goodbye);
   CloseHandle (hr);
@@ -3100,16 +3198,15 @@ skip_close_hello:
 cleanup_heap:
   HeapFree (GetProcessHeap (), 0, si.lpAttributeList);
 cleanup_pseudo_console:
-  if (get_ttyp ()->h_pseudo_console)
+  if (hpcon)
     {
-      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) get_ttyp ()->h_pseudo_console;
+      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) hpcon;
       HANDLE tmp = hp->hConHostProcess;
-      ClosePseudoConsole (get_ttyp ()->h_pseudo_console);
+      ClosePseudoConsole (hpcon);
       CloseHandle (tmp);
     }
 fallback:
-  get_ttyp ()->pcon_start = false;
-  get_ttyp ()->h_pseudo_console = NULL;
+  release_output_mutex ();
   return false;
 }
 
@@ -3118,20 +3215,121 @@ fallback:
 void
 fhandler_pty_slave::close_pseudoconsole (tty *ttyp)
 {
-  if (ttyp->h_pseudo_console)
+  DWORD switch_to_stub = 0, switch_to = 0;
+  DWORD new_pcon_pid = 0;
+  if (ttyp->pcon_pid == myself->pid)
+    {
+      /* Search another process which attaches to the pseudo console */
+      DWORD current_pid = myself->exec_dwProcessId ?: myself->dwProcessId;
+      switch_to =
+	get_console_process_id (current_pid, false, true);
+      if (switch_to)
+	{
+	  pinfo p (cygwin_pid (switch_to));
+	  if (p)
+	    {
+	      if (p->exec_dwProcessId)
+		switch_to_stub = p->exec_dwProcessId;
+	      new_pcon_pid = p->pid;
+	    }
+	}
+    }
+  if (ttyp->pcon_activated)
     {
       ttyp->previous_code_page = GetConsoleCP ();
       ttyp->previous_output_code_page = GetConsoleOutputCP ();
-      FreeConsole ();
-      AttachConsole (ATTACH_PARENT_PROCESS);
-      HPCON_INTERNAL *hp = (HPCON_INTERNAL *) ttyp->h_pseudo_console;
-      HANDLE tmp = hp->hConHostProcess;
-      ClosePseudoConsole (ttyp->h_pseudo_console);
-      CloseHandle (tmp);
-      ttyp->h_pseudo_console = NULL;
-      ttyp->switch_to_pcon_in = false;
-      ttyp->pcon_pid = 0;
-      ttyp->pcon_start = false;
+      if (ttyp->pcon_pid == myself->pid)
+	{
+	  switch_to = switch_to_stub ?: switch_to;
+	  if (switch_to)
+	    {
+	      /* Change pseudo console owner to another process */
+	      HANDLE new_owner =
+		OpenProcess (PROCESS_DUP_HANDLE, FALSE, switch_to);
+	      HANDLE new_write_pipe = NULL;
+	      HANDLE new_condrv_reference = NULL;
+	      HANDLE new_conhost_process = NULL;
+	      HANDLE new_pcon_in = NULL, new_pcon_out = NULL;
+	      DuplicateHandle (GetCurrentProcess (),
+			       ttyp->h_pcon_write_pipe,
+			       new_owner, &new_write_pipe,
+			       0, TRUE, DUPLICATE_SAME_ACCESS);
+	      DuplicateHandle (GetCurrentProcess (),
+			       ttyp->h_pcon_condrv_reference,
+			       new_owner, &new_condrv_reference,
+			       0, TRUE, DUPLICATE_SAME_ACCESS);
+	      DuplicateHandle (GetCurrentProcess (),
+			       ttyp->h_pcon_conhost_process,
+			       new_owner, &new_conhost_process,
+			       0, TRUE, DUPLICATE_SAME_ACCESS);
+	      DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_in,
+			       new_owner, &new_pcon_in,
+			       0, TRUE, DUPLICATE_SAME_ACCESS);
+	      DuplicateHandle (GetCurrentProcess (), ttyp->h_pcon_out,
+			       new_owner, &new_pcon_out,
+			       0, TRUE, DUPLICATE_SAME_ACCESS);
+	      CloseHandle (new_owner);
+	      CloseHandle (ttyp->h_pcon_write_pipe);
+	      CloseHandle (ttyp->h_pcon_condrv_reference);
+	      CloseHandle (ttyp->h_pcon_conhost_process);
+	      CloseHandle (ttyp->h_pcon_in);
+	      CloseHandle (ttyp->h_pcon_out);
+	      ttyp->pcon_pid = new_pcon_pid;
+	      ttyp->h_pcon_write_pipe = new_write_pipe;
+	      ttyp->h_pcon_condrv_reference = new_condrv_reference;
+	      ttyp->h_pcon_conhost_process = new_conhost_process;
+	      ttyp->h_pcon_in = new_pcon_in;
+	      ttyp->h_pcon_out = new_pcon_out;
+	    }
+	  else
+	    { /* Close pseudo console */
+	      FreeConsole ();
+	      pinfo p (myself->ppid);
+	      if (p)
+		{
+		  if (!AttachConsole (p->dwProcessId))
+		    AttachConsole (ATTACH_PARENT_PROCESS);
+		}
+	      else
+		AttachConsole (ATTACH_PARENT_PROCESS);
+	      /* Reconstruct pseudo console handler container here for close */
+	      HPCON_INTERNAL *hp =
+		(HPCON_INTERNAL *) HeapAlloc (GetProcessHeap (), 0,
+					      sizeof (*hp));
+	      hp->hWritePipe = ttyp->h_pcon_write_pipe;
+	      hp->hConDrvReference = ttyp->h_pcon_condrv_reference;
+	      hp->hConHostProcess = ttyp->h_pcon_conhost_process;
+	      /* HeapFree() will be called in ClosePseudoConsole() */
+	      ClosePseudoConsole ((HPCON) hp);
+	      CloseHandle (ttyp->h_pcon_conhost_process);
+	      ttyp->pcon_activated = false;
+	      ttyp->switch_to_pcon_in = false;
+	      ttyp->pcon_pid = 0;
+	      ttyp->pcon_start = false;
+	    }
+	}
+      else
+	{
+	  FreeConsole ();
+	  pinfo p (myself->ppid);
+	  if (p)
+	    {
+	      if (!AttachConsole (p->dwProcessId))
+		AttachConsole (ATTACH_PARENT_PROCESS);
+	    }
+	  else
+	    AttachConsole (ATTACH_PARENT_PROCESS);
+	}
+    }
+  else if (ttyp->pcon_pid == myself->pid)
+    {
+      if (switch_to_stub)
+	ttyp->pcon_pid = new_pcon_pid;
+      else
+	{
+	  ttyp->pcon_pid = 0;
+	  ttyp->switch_to_pcon_in = false;
+	}
     }
 }
 
@@ -3222,9 +3420,9 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 
   /* Check if terminal has CSI6n */
   WaitForSingleObject (input_mutex, INFINITE);
-  /* Set h_pseudo_console and pcon_start so that the response
+  /* Set pcon_activated and pcon_start so that the response
      will sent to io_handle rather than io_handle_cyg. */
-  get_ttyp ()->h_pseudo_console = (HPCON *) -1; /* dummy */
+  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;
@@ -3255,7 +3453,7 @@ fhandler_pty_slave::term_has_pcon_cap (const WCHAR *env)
 	Sleep (1);
     }
   while (len);
-  get_ttyp ()->h_pseudo_console = NULL;
+  get_ttyp ()->pcon_activated = false;
   get_ttyp ()->pcon_pid = 0;
   if (len == 0)
     goto not_has_csi6n;
@@ -3270,7 +3468,7 @@ not_has_csi6n:
   /* If CSI6n is not responded, pcon_start is not cleared
      in master write(). Therefore, clear it here manually. */
   get_ttyp ()->pcon_start = false;
-  get_ttyp ()->h_pseudo_console = NULL;
+  get_ttyp ()->pcon_activated = false;
   ReleaseMutex (input_mutex);
 maybe_dumb:
   get_ttyp ()->pcon_cap_checked = true;
@@ -3362,7 +3560,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
   if (dir == to_nat)
     {
       cp_from = ttyp->term_code_page;
-      if (ttyp->h_pseudo_console)
+      if (ttyp->pcon_activated)
 	cp_to = CP_UTF8;
       else
 	cp_to = GetConsoleCP ();
@@ -3378,7 +3576,7 @@ fhandler_pty_slave::transfer_input (xfer_dir dir, HANDLE from, tty *ttyp,
 
   bool transfered = false;
 
-  if (dir == to_cyg && ttyp->h_pseudo_console)
+  if (dir == to_cyg && ttyp->pcon_activated)
     { /* from handle is console handle */
       INPUT_RECORD r[INREC_SIZE];
       DWORD n;
@@ -3462,7 +3660,7 @@ 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->h_pseudo_console)
+	  if (dir == to_nat && ttyp->pcon_activated)
 	    {
 	      char *p = buf;
 	      while ((p = (char *) memchr (p, '\n', n - (p - buf))))
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 4e7946435..dc75a2dbf 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -1388,7 +1388,7 @@ pty_slave_startup (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 (true);
+    ptys->mask_switch_to_pcon_in (true, true);
 
   select_pipe_info *pi = stuff->device_specific_ptys;
   if (pi->start)
@@ -1415,7 +1415,7 @@ pty_slave_cleanup (select_record *me, select_stuff *stuff)
   if (!pi)
     return;
   if (me->read_selected && pi->start)
-    ptys->mask_switch_to_pcon_in (false);
+    ptys->mask_switch_to_pcon_in (false, false);
   if (pi->thread)
     {
       pi->stop_thread = true;
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 383a5a03e..c4b612815 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -656,6 +656,7 @@ 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;
       tty *ptys_ttyp = NULL;
       _minor_t ptys_unit = 0;
       if (!iscygwin () && ptys_primary && is_console_app (runpath))
@@ -676,6 +677,9 @@ 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,
+			   0, 0, DUPLICATE_SAME_ACCESS);
 	  if (!enable_pcon)
 	    fhandler_pty_slave::transfer_input (fhandler_pty_slave::to_nat,
 						ptys_primary->get_handle_cyg (),
@@ -965,16 +969,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  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,
-						  ptys_input_available_event);
+	      /* 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);
 	      CloseHandle (ptys_from_master);
 	      CloseHandle (ptys_input_available_event);
+	      fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
+	      ReleaseMutex (ptys_output_mutex);
+	      CloseHandle (ptys_output_mutex);
 	    }
-	  if (enable_pcon)
-	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
-	  else if (cons_native)
+	  if (cons_native)
 	    {
 	      fhandler_console::request_xterm_mode_output (true,
 							   &cons_handle_set);
@@ -992,16 +1002,22 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
 	  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,
-						  ptys_input_available_event);
+	      /* 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);
 	      CloseHandle (ptys_from_master);
 	      CloseHandle (ptys_input_available_event);
+	      fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
+	      ReleaseMutex (ptys_output_mutex);
+	      CloseHandle (ptys_output_mutex);
 	    }
-	  if (enable_pcon)
-	    fhandler_pty_slave::close_pseudoconsole (ptys_ttyp);
-	  else if (cons_native)
+	  if (cons_native)
 	    {
 	      fhandler_console::request_xterm_mode_output (true,
 							   &cons_handle_set);
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index e6c0b680e..fb4501c1b 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -235,7 +235,7 @@ tty::init ()
   master_pid = 0;
   is_console = false;
   column = 0;
-  h_pseudo_console = NULL;
+  pcon_activated = false;
   switch_to_pcon_in = false;
   pcon_pid = 0;
   term_code_page = 0;
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 47be8e4a0..e2e6dfeb6 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -96,13 +96,17 @@ private:
   HANDLE _to_master_cyg;
   HANDLE _to_slave;
   HANDLE _to_slave_cyg;
-  HPCON h_pseudo_console;
+  bool pcon_activated;
   bool pcon_start;
   bool switch_to_pcon_in;
   pid_t pcon_pid;
   UINT term_code_page;
   DWORD pcon_last_time;
   HANDLE h_pcon_write_pipe;
+  HANDLE h_pcon_condrv_reference;
+  HANDLE h_pcon_conhost_process;
+  HANDLE h_pcon_in;
+  HANDLE h_pcon_out;
   bool pcon_cap_checked;
   bool has_csi6n;
   bool need_invisible_console;
-- 
2.30.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v7 0/4] Improve pseudo console support.
  2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
                   ` (3 preceding siblings ...)
  2021-01-28  3:26 ` [PATCH v7 4/4] Cygwin: pty: Allow multiple apps to enable pseudo console simultaneously Takashi Yano
@ 2021-01-28 10:25 ` Corinna Vinschen
  4 siblings, 0 replies; 6+ messages in thread
From: Corinna Vinschen @ 2021-01-28 10:25 UTC (permalink / raw)
  To: cygwin-patches

On Jan 28 12:26, Takashi Yano via Cygwin-patches wrote:
> The new implementation of pseudo console support by commit bb428520
> provides the important advantages, while there also has been several
> disadvantages compared to the previous implementation.
> 
> These patches overturn some of them.
> 
> The disadvantage:
>  1) The cygwin program which calls console API directly does not work.
> is supposed to be able to be overcome as well, however, I am not sure
> it is worth enough. This will need a lot of hooks for console APIs.
>  --> Respecting Corinna's opinion, we decided not to implement this.
> 
> v3: Fix typeahead input issue in GDB. Several other bugs have also
>     been fixed.
> v4: Change the conditions for calling transfer_input() slightly in
>     reset_switch_to_pcon() to avoid calling it if uncecessary or
>     with no effect.
> v5: Small bug fix in v4.
> v6: Yet another bug fix.
>     Add missing CloseHandle().
>     Take into account when the master is running as a service (such
>     as ssh session).
> v7: Specify FILE_FLAG_OVERLAPPED for to_slave pipe to prevent
>     PeekNamedPipe() from blocking in transfer_input().
>     Simplify the code determining if the slave is reading.
> 
> Takashi Yano (4):
>   Cygwin: pty: Inherit typeahead data between two input pipes.
>   Cygwin: pty: Keep code page between non-cygwin apps.
>   Cygwin: pty: Make apps using console APIs be able to debug with gdb.
>   Cygwin: pty: Allow multiple apps to enable pseudo console
>     simultaneously.
> 
>  winsup/cygwin/fhandler.h      |   22 +-
>  winsup/cygwin/fhandler_tty.cc | 1123 +++++++++++++++++++++++++++------
>  winsup/cygwin/select.cc       |    7 +-
>  winsup/cygwin/spawn.cc        |  106 +++-
>  winsup/cygwin/tty.cc          |   13 +-
>  winsup/cygwin/tty.h           |   21 +-
>  6 files changed, 1059 insertions(+), 233 deletions(-)
> 
> -- 
> 2.30.0

Pushed.


Thanks,
Corinna

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-01-28 10:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-28  3:26 [PATCH v7 0/4] Improve pseudo console support Takashi Yano
2021-01-28  3:26 ` [PATCH v7 1/4] Cygwin: pty: Inherit typeahead data between two input pipes Takashi Yano
2021-01-28  3:26 ` [PATCH v7 2/4] Cygwin: pty: Keep code page between non-cygwin apps Takashi Yano
2021-01-28  3:26 ` [PATCH v7 3/4] Cygwin: pty: Make apps using console APIs be able to debug with gdb Takashi Yano
2021-01-28  3:26 ` [PATCH v7 4/4] Cygwin: pty: Allow multiple apps to enable pseudo console simultaneously Takashi Yano
2021-01-28 10:25 ` [PATCH v7 0/4] Improve pseudo console support Corinna Vinschen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).