[PATCH v2 1/1] Cygwin: pty: add pseudo console support.

Takashi Yano takashi.yano@nifty.ne.jp
Wed Apr 3 16:37:00 GMT 2019


- Support pseudo console in PTY. Pseudo console is a new feature
  in Windows 10 1809, which privides console APIs on virtual
  terminal. With this patch, native console applications can work
  in PTY such as mintty, ssh, gnu screen or tmux.
---
 winsup/cygwin/dtable.cc               |   7 +
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 ++
 winsup/cygwin/fhandler_tty.cc         | 583 ++++++++++++++++++++++++--
 winsup/cygwin/fork.cc                 |  20 +
 winsup/cygwin/select.cc               |   3 +
 winsup/cygwin/spawn.cc                |  32 ++
 winsup/cygwin/tty.cc                  |   4 +
 winsup/cygwin/tty.h                   |  20 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 10 files changed, 716 insertions(+), 41 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..3f5e86075 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,13 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+#if 1 /* Experimental code */
+  /* This is workaround for the issue:
+     https://www.cygwin.com/ml/cygwin-developers/2019-04/msg00000.html
+     when pseudo console is enabled. */
+  fhandler_console::need_invisible ();
+#endif
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index bc66377cd..75f07b275 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1994,6 +1994,7 @@ private:
   static bool need_invisible ();
   static void free_console ();
   static const char *get_nonascii_key (INPUT_RECORD& input_rec, char *);
+  static DWORD get_console_process_id (DWORD pid, bool match);
 
   fhandler_console (void *) {}
 
@@ -2026,14 +2027,17 @@ class fhandler_pty_common: public fhandler_termios
  public:
   fhandler_pty_common ()
     : fhandler_termios (),
-      output_mutex (NULL),
-    input_mutex (NULL), input_available_event (NULL)
+    output_mutex (NULL), input_mutex (NULL),
+    input_available_event (NULL), pcon_attached (false),
+    change_codepage_to_utf8 (true)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
   static const unsigned pipesize = 128 * 1024;
   HANDLE output_mutex, input_mutex;
   HANDLE input_available_event;
+  bool pcon_attached;
+  bool change_codepage_to_utf8;
 
   bool use_archetype () const {return true;}
   DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
@@ -2064,14 +2068,29 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_spawn (void)
+  {
+    return get_ttyp ()->attach_pcon_in_spawn;
+  }
+  DWORD getHelperProcessId (void)
+  {
+    return get_ttyp ()->HelperProcessId;
+  }
+  HPCON getPseudoConsole (void)
+  {
+    return get_ttyp ()->hPseudoConsole;
+  }
+
  protected:
-  BOOL process_opost_output (HANDLE h, const void *ptr, ssize_t& len, bool is_echo);
+  BOOL process_opost_output (HANDLE h,
+			     const void *ptr, ssize_t& len, bool is_echo);
+  void push_to_pcon_screenbuffer (HANDLE h, const char *ptr, size_t len);
 };
 
 class fhandler_pty_slave: public fhandler_pty_common
 {
   HANDLE inuse;			// used to indicate that a tty is in use
-  HANDLE output_handle_cyg;
+  HANDLE output_handle_cyg, io_handle_cyg;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2084,6 +2103,8 @@ class fhandler_pty_slave: public fhandler_pty_common
 
   void set_output_handle_cyg (HANDLE h) { output_handle_cyg = h; }
   HANDLE& get_output_handle_cyg () { return output_handle_cyg; }
+  void set_handle_cyg (HANDLE h) { io_handle_cyg = h; }
+  HANDLE& get_handle_cyg () { return io_handle_cyg; }
 
   int open (int flags, mode_t mode = 0);
   void open_setup (int flags);
@@ -2124,6 +2145,12 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void reset_switch_to_pcon (void)
+  {
+    if (get_ttyp ()->switch_to_pcon == 2)
+      Sleep (20);
+    get_ttyp ()->switch_to_pcon = 0;
+  }
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2132,15 +2159,14 @@ class fhandler_pty_master: public fhandler_pty_common
   int pktmode;			// non-zero if pty in a packet mode.
   HANDLE master_ctl;		// Control socket for handle duplication
   cygthread *master_thread;	// Master control thread
-  HANDLE from_master, to_master;
+  HANDLE from_master, to_master, from_slave, to_slave;
   HANDLE echo_r, echo_w;
   DWORD dwProcessId;		// Owner of master handles
-  HANDLE io_handle_cyg, to_master_cyg;
+  HANDLE to_master_cyg, from_master_cyg;
   cygthread *master_fwd_thread;	// Master forwarding thread
 
 public:
   HANDLE get_echo_handle () const { return echo_r; }
-  HANDLE& get_handle_cyg () { return io_handle_cyg; }
   /* Constructor */
   fhandler_pty_master (int);
 
@@ -2187,6 +2213,8 @@ public:
     copyto (fh);
     return fh;
   }
+
+  bool setup_pseudoconsole (void);
 };
 
 class fhandler_dev_null: public fhandler_base
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
index 6d42ed888..d2e3184a6 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1019,6 +1019,19 @@ fhandler_console::close ()
 
   CloseHandle (get_handle ());
   CloseHandle (get_output_handle ());
+
+  /* If already attached to pseudo console, don't call free_console () */
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR ||
+	cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_pty_common *t =
+	  (fhandler_pty_common *) (fhandler_base *) cfd;
+	if (get_console_process_id (t->getHelperProcessId (), true))
+	  return 0;
+      }
+
   if (!have_execed)
     free_console ();
   return 0;
@@ -3065,6 +3078,25 @@ fhandler_console::need_invisible ()
   return b;
 }
 
+DWORD
+fhandler_console::get_console_process_id (DWORD pid, bool match)
+{
+  DWORD tmp;
+  int num = GetConsoleProcessList (&tmp, 1);
+  DWORD *list = (DWORD *)
+	      HeapAlloc (GetProcessHeap (), 0, num * sizeof (DWORD));
+  num = GetConsoleProcessList (list, num);
+  tmp = 0;
+  for (int i=0; i<num; i++)
+    if ((match && list[i] == pid) || (!match && list[i] != pid))
+      {
+	tmp = list[i];
+	break;
+      }
+  HeapFree (GetProcessHeap (), 0, list);
+  return tmp;
+}
+
 DWORD
 fhandler_console::__acquire_input_mutex (const char *fn, int ln, DWORD ms)
 {
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index 312c2d083..0c7fd1d6c 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,18 @@ details. */
 #include "child_info.h"
 #include <asm/socket.h>
 #include "cygwait.h"
+#include "tls_pbuf.h"
+
+/* Not yet defined in Mingw-w64 */
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif /* ENABLE_VIRTUAL_TERMINAL_PROCESSING */
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +51,7 @@ struct pipe_request {
 
 struct pipe_reply {
   HANDLE from_master;
+  HANDLE from_master_cyg;
   HANDLE to_master;
   HANDLE to_master_cyg;
   DWORD error;
@@ -67,7 +80,7 @@ bytes_available (DWORD& n, HANDLE h)
 bool
 fhandler_pty_common::bytes_available (DWORD &n)
 {
-  return ::bytes_available (n, get_handle ());
+  return ::bytes_available (n, get_handle_cyg ());
 }
 
 #ifdef DEBUGGING
@@ -144,6 +157,11 @@ fhandler_pty_common::__release_output_mutex (const char *fn, int ln)
 void
 fhandler_pty_master::doecho (const void *str, DWORD len)
 {
+#if 0 /* Experimental code */
+  /* Push echo output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    push_to_pcon_screenbuffer (to_master, (char *)str, len);
+#endif
   ssize_t towrite = len;
   if (!process_opost_output (echo_w, str, towrite, true))
     termios_printf ("Write to echo pipe failed, %E");
@@ -235,7 +253,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on
 	  /* Check echo pipe first. */
 	  if (::bytes_available (echo_cnt, echo_r) && echo_cnt > 0)
 	    break;
-	  if (!::bytes_available (n, get_handle_cyg ()))
+	  if (!bytes_available (n))
 	    goto err;
 	  if (n)
 	    break;
@@ -296,7 +314,7 @@ fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on
 	      goto err;
 	    }
 	}
-      else if (!ReadFile (get_handle_cyg (), outbuf, rlen, &n, NULL))
+      else if (!ReadFile (get_handle (), outbuf, rlen, &n, NULL))
 	{
 	  termios_printf ("ReadFile failed, %E");
 	  goto err;
@@ -340,11 +358,14 @@ fhandler_pty_slave::fhandler_pty_slave (int unit)
 int
 fhandler_pty_slave::open (int flags, mode_t)
 {
-  HANDLE pty_owner, from_master_local, to_master_local, to_master_cyg_local;
+  HANDLE pty_owner;
+  HANDLE from_master_local, from_master_cyg_local;
+  HANDLE to_master_local, to_master_cyg_local;
   HANDLE *handles[] =
   {
     &from_master_local, &input_available_event, &input_mutex, &inuse,
     &output_mutex, &to_master_local, &pty_owner, &to_master_cyg_local,
+    &from_master_cyg_local,
     NULL
   };
 
@@ -396,7 +417,7 @@ fhandler_pty_slave::open (int flags, mode_t)
     release_output_mutex ();
   }
 
-  if (!get_ttyp ()->from_master () ||
+  if (!get_ttyp ()->from_master () || !get_ttyp ()->from_master_cyg () ||
       !get_ttyp ()->to_master () || !get_ttyp ()->to_master_cyg ())
     {
       errmsg = "pty handles have been closed";
@@ -441,6 +462,15 @@ fhandler_pty_slave::open (int flags, mode_t)
 	  __seterrno ();
 	  goto err_no_msg;
 	}
+      if (!DuplicateHandle (pty_owner, get_ttyp ()->from_master_cyg (),
+			    GetCurrentProcess (), &from_master_cyg_local, 0, TRUE,
+			    DUPLICATE_SAME_ACCESS))
+	{
+	  termios_printf ("can't duplicate input from %u/%p, %E",
+			  get_ttyp ()->master_pid, get_ttyp ()->from_master_cyg ());
+	  __seterrno ();
+	  goto err_no_msg;
+	}
       if (!DuplicateHandle (pty_owner, get_ttyp ()->to_master (),
 			  GetCurrentProcess (), &to_master_local, 0, TRUE,
 			  DUPLICATE_SAME_ACCESS))
@@ -474,9 +504,11 @@ fhandler_pty_slave::open (int flags, mode_t)
 	  goto err;
 	}
       from_master_local = repl.from_master;
+      from_master_cyg_local = repl.from_master_cyg;
       to_master_local = repl.to_master;
       to_master_cyg_local = repl.to_master_cyg;
-      if (!from_master_local || !to_master_local || !to_master_cyg_local)
+      if (!from_master_local || !from_master_cyg_local ||
+	  !to_master_local || !to_master_cyg_local)
 	{
 	  SetLastError (repl.error);
 	  errmsg = "error duplicating pipes, %E";
@@ -484,22 +516,28 @@ fhandler_pty_slave::open (int flags, mode_t)
 	}
     }
   VerifyHandle (from_master_local);
+  VerifyHandle (from_master_cyg_local);
   VerifyHandle (to_master_local);
   VerifyHandle (to_master_cyg_local);
 
   termios_printf ("duplicated from_master %p->%p from pty_owner",
 		  get_ttyp ()->from_master (), from_master_local);
+  termios_printf ("duplicated from_master_cyg %p->%p from pty_owner",
+		  get_ttyp ()->from_master_cyg (), from_master_cyg_local);
   termios_printf ("duplicated to_master %p->%p from pty_owner",
 		  get_ttyp ()->to_master (), to_master_local);
   termios_printf ("duplicated to_master_cyg %p->%p from pty_owner",
 		  get_ttyp ()->to_master_cyg (), to_master_cyg_local);
 
   set_handle (from_master_local);
+  set_handle_cyg (from_master_cyg_local);
   set_output_handle (to_master_local);
   set_output_handle_cyg (to_master_cyg_local);
 
   fhandler_console::need_invisible ();
   set_open_status ();
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   return 1;
 
 err:
@@ -548,6 +586,9 @@ fhandler_pty_slave::close ()
   if (!ForceCloseHandle (get_output_handle_cyg ()))
     termios_printf ("CloseHandle (get_output_handle_cyg ()<%p>), %E",
 	get_output_handle_cyg ());
+  if (!ForceCloseHandle (get_handle_cyg ()))
+    termios_printf ("CloseHandle (get_handle_cyg ()<%p>), %E",
+	get_handle_cyg ());
   if ((unsigned) myself->ctty == FHDEV (DEV_PTYS_MAJOR, get_minor ()))
     fhandler_console::free_console ();	/* assumes that we are the last pty closer */
   fhandler_pty_common::close ();
@@ -596,6 +637,95 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_common::push_to_pcon_screenbuffer (HANDLE h,
+						const char *ptr, size_t len)
+{
+  /* Switch to alternate screen buffer */
+  if (memmem (ptr, len, "\033[?1049h", 8))
+    get_ttyp ()->screen_alternated = true;
+  /* Switch to main screen buffer */
+  if (memmem (ptr, len, "\033[?1049l", 8))
+    get_ttyp ()->screen_alternated = false;
+  /* ALnternate screen buffer is not needed to synchronize */
+  if (get_ttyp ()->screen_alternated)
+    return;
+  DWORD pidRestore = 0;
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      pidRestore =
+	fhandler_console::get_console_process_id (GetCurrentProcessId (),
+						  false);
+      /* If pidRestore is not set, give up to push. */
+      if (!pidRestore)
+	return;
+      FreeConsole ();
+      AttachConsole (getHelperProcessId ());
+    }
+  char *buf;
+  size_t nlen;
+  if (!change_codepage_to_utf8 && strstr (setlocale (LC_CTYPE, NULL), "UTF-8"))
+    {
+      /* Code page conversion from UTF-8 to OEM code page */
+      /* FIXME: The terminal code may not be UTF-8. */
+      size_t wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, NULL, 0);
+      wchar_t *wbuf = (wchar_t *)
+	HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+      wlen =
+	MultiByteToWideChar (CP_UTF8, 0, (char *)ptr, len, wbuf, wlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, NULL, 0, NULL, NULL);
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+      nlen = WideCharToMultiByte (CP_OEMCP, 0,
+				  wbuf, wlen, buf, nlen, NULL, NULL);
+      HeapFree (GetProcessHeap (), 0, wbuf);
+    }
+  else
+    {
+      /* Just copy */
+      buf = (char *) HeapAlloc (GetProcessHeap (), 0, len);
+      memcpy (buf, (char *)ptr, len);
+      nlen = len;
+    }
+  /* Remove ESC sequence which returns results to console
+     input buffer. Without this, cursor position report
+     is put into the input buffer as a garbage. */
+  char *p0;
+  /* Remove ESC sequence to report cursor position. */
+  while ((p0 = (char *) memmem (buf, nlen, "\033[6n", 4)))
+    {
+      memmove (p0, p0+4, nlen - (p0+4 - buf));
+      nlen -= 4;
+    }
+  /* Remove ESC sequence to report terminal identity. */
+  while ((p0 = (char *) memmem (buf, nlen, "\033[0c", 4)))
+    {
+      memmove (p0, p0+4, nlen - (p0+4 - buf));
+      nlen -= 4;
+    }
+  DWORD dwMode;
+  GetConsoleMode (h, &dwMode);
+  SetConsoleMode (h, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
+  char *p = buf;
+  DWORD wLen, written = 0;
+  while (written <  nlen)
+    {
+      WriteFile (h, p, nlen - written, &wLen, NULL);
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (h, dwMode);
+  HeapFree (GetProcessHeap (), 0, buf);
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      AttachConsole (pidRestore);
+    }
+}
+
 ssize_t __stdcall
 fhandler_pty_slave::write (const void *ptr, size_t len)
 {
@@ -609,6 +739,12 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 
   push_process_state process_state (PID_TTYOU);
 
+  reset_switch_to_pcon ();
+
+  /* Push slave output to pseudo console screen buffer */
+  if (getPseudoConsole ())
+    push_to_pcon_screenbuffer (get_output_handle (), (char *)ptr, len);
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -644,10 +780,12 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       return;
     }
 
-  termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle ());
+  termios_printf ("read(%p, %lu) handle %p", ptr, len, get_handle_cyg ());
 
   push_process_state process_state (PID_TTYIN);
 
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -777,7 +915,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
       if (readlen)
 	{
 	  termios_printf ("reading %lu bytes (vtime %d)", readlen, vtime);
-	  if (!ReadFile (get_handle (), buf, readlen, &n, NULL))
+	  if (!ReadFile (get_handle_cyg (), buf, readlen, &n, NULL))
 	    {
 	      termios_printf ("read failed, %E");
 	      ReleaseMutex (input_mutex);
@@ -890,6 +1028,7 @@ fhandler_pty_master::dup (fhandler_base *child, int)
 int
 fhandler_pty_slave::tcgetattr (struct termios *t)
 {
+  reset_switch_to_pcon ();
   *t = get_ttyp ()->ti;
   return 0;
 }
@@ -897,6 +1036,7 @@ fhandler_pty_slave::tcgetattr (struct termios *t)
 int
 fhandler_pty_slave::tcsetattr (int, const struct termios *t)
 {
+  reset_switch_to_pcon ();
   acquire_output_mutex (INFINITE);
   get_ttyp ()->ti = *t;
   release_output_mutex ();
@@ -908,8 +1048,9 @@ fhandler_pty_slave::tcflush (int queue)
 {
   int ret = 0;
 
-  termios_printf ("tcflush(%d) handle %p", queue, get_handle ());
+  termios_printf ("tcflush(%d) handle %p", queue, get_handle_cyg ());
 
+  reset_switch_to_pcon ();
   if (queue == TCIFLUSH || queue == TCIOFLUSH)
     {
       size_t len = UINT_MAX;
@@ -929,6 +1070,7 @@ int
 fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
 {
   termios_printf ("ioctl (%x)", cmd);
+  reset_switch_to_pcon ();
   int res = fhandler_termios::ioctl (cmd, arg);
   if (res <= 0)
     return res;
@@ -995,6 +1137,52 @@ fhandler_pty_slave::ioctl (unsigned int cmd, void *arg)
       get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
       break;
     case TIOCSWINSZ:
+      if (getPseudoConsole ())
+	{
+	  /* If not attached pseudo console yet, try to attach
+	     temporally. */
+	  DWORD pidRestore = 0;
+	  if (!pcon_attached)
+	    {
+	      pidRestore = fhandler_console::get_console_process_id
+		(GetCurrentProcessId (), false);
+	      FreeConsole ();
+	      AttachConsole (getHelperProcessId ());
+	    }
+	  COORD size;
+	  size.X = ((struct winsize *) arg)->ws_col;
+	  size.Y = ((struct winsize *) arg)->ws_row;
+	  CONSOLE_SCREEN_BUFFER_INFO csbi;
+	  if (GetConsoleScreenBufferInfo (get_output_handle (), &csbi))
+	    if (size.X == csbi.srWindow.Right - csbi.srWindow.Left + 1 &&
+		size.Y == csbi.srWindow.Bottom - csbi.srWindow.Top + 1)
+	      goto detach_pcon;
+	  if (!SetConsoleScreenBufferSize (get_output_handle (), size))
+	    goto detach_pcon;
+	  SMALL_RECT rect;
+	  rect.Left = 0;
+	  rect.Top = 0;
+	  rect.Right = size.X-1;
+	  rect.Bottom = size.Y-1;
+	  SetConsoleWindowInfo (get_output_handle (), TRUE, &rect);
+detach_pcon:
+	  /* Detach from pseudo console and resume. */
+	  if (pidRestore)
+	    {
+	      FreeConsole ();
+	      AttachConsole (pidRestore);
+	    }
+	  else if (!pcon_attached)
+	    { /* If failed to resume, leave being attached. */
+	      if (change_codepage_to_utf8)
+		{
+		  /* FIXME: The terminal code may not be UTF-8. */
+		  SetConsoleCP (65001);
+		  SetConsoleOutputCP (65001);
+		}
+	      pcon_attached = true;
+	    }
+	}
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1228,8 +1416,9 @@ errout:
 fhandler_pty_master::fhandler_pty_master (int unit)
   : fhandler_pty_common (), pktmode (0), master_ctl (NULL),
     master_thread (NULL), from_master (NULL), to_master (NULL),
-    echo_r (NULL), echo_w (NULL), dwProcessId (0),
-    io_handle_cyg (NULL), to_master_cyg (NULL), master_fwd_thread (NULL)
+    from_slave (NULL), to_slave (NULL), echo_r (NULL), echo_w (NULL),
+    dwProcessId (0), to_master_cyg (NULL), from_master_cyg (NULL),
+    master_fwd_thread (NULL)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYM_MAJOR, unit);
@@ -1285,7 +1474,8 @@ fhandler_pty_master::cleanup ()
 {
   report_tty_counts (this, "closing master", "");
   if (archetype)
-    from_master = to_master = to_master_cyg = NULL;
+    from_master = from_master_cyg =
+      to_master = to_master_cyg = from_slave = to_slave = NULL;
   fhandler_base::cleanup ();
 }
 
@@ -1295,8 +1485,9 @@ fhandler_pty_master::close ()
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
 
-  termios_printf ("closing from_master(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)",
-		  from_master, to_master, to_master_cyg, dwProcessId);
+  termios_printf ("closing from_master(%p)/from_master_cyg(%p)/to_master(%p)/to_master_cyg(%p) since we own them(%u)",
+		  from_master, from_master_cyg,
+		  to_master, to_master_cyg, dwProcessId);
   if (cygwin_finished_initializing)
     {
       if (master_ctl && get_ttyp ()->master_pid == myself->pid)
@@ -1320,6 +1511,22 @@ fhandler_pty_master::close ()
 	    }
 	  release_output_mutex ();
 	  master_fwd_thread->terminate_thread ();
+
+	  /* Pseudo Console Support */
+	  if (getPseudoConsole ())
+	    {
+	      /* Terminate helper process */
+	      SetEvent (get_ttyp ()->hHelperGoodbye);
+	      WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	      /* Release pseudo console */
+	      HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	      FARPROC func = GetProcAddress (hModule, "ClosePseudoConsole");
+	      VOID (WINAPI *ClosePseudoConsole) (HPCON) = NULL;
+	      ClosePseudoConsole = (VOID (WINAPI *) (HPCON)) func;
+	      ClosePseudoConsole (getPseudoConsole ());
+	      get_ttyp ()->hPseudoConsole = NULL;
+	      get_ttyp ()->switch_to_pcon = 0;
+	    }
 	}
     }
 
@@ -1344,17 +1551,24 @@ fhandler_pty_master::close ()
 
   if (!ForceCloseHandle (from_master))
     termios_printf ("error closing from_master %p, %E", from_master);
+  if (from_master_cyg != from_master) /* Avoid double close. */
+    if (!ForceCloseHandle (from_master_cyg))
+      termios_printf ("error closing from_master_cyg %p, %E", from_master_cyg);
   if (!ForceCloseHandle (to_master))
     termios_printf ("error closing to_master %p, %E", to_master);
   from_master = to_master = NULL;
-  if (!ForceCloseHandle (get_handle_cyg ()))
-    termios_printf ("error closing io_handle_cyg %p, %E", get_handle_cyg ());
+  if (!ForceCloseHandle (from_slave))
+    termios_printf ("error closing from_slave %p, %E", from_slave);
+  from_slave = NULL;
   if (!ForceCloseHandle (to_master_cyg))
     termios_printf ("error closing to_master_cyg %p, %E", to_master_cyg);
-  get_handle_cyg () = to_master_cyg = NULL;
+  to_master_cyg = from_master_cyg = NULL;
   ForceCloseHandle (echo_r);
   ForceCloseHandle (echo_w);
   echo_r = echo_w = NULL;
+  if (to_slave)
+    ForceCloseHandle (to_slave);
+  to_slave = NULL;
 
   if (have_execed || get_ttyp ()->master_pid != myself->pid)
     termios_printf ("not clearing: %d, master_pid %d", have_execed, get_ttyp ()->master_pid);
@@ -1376,6 +1590,16 @@ fhandler_pty_master::write (const void *ptr, size_t len)
     return (ssize_t) bg;
 
   push_process_state process_state (PID_TTYOU);
+
+  /* Write terminal input to to_slave pipe instead of output_handle
+     if current application is native console application. */
+  if (get_ttyp ()->switch_to_pcon == 2)
+    {
+      DWORD wLen;
+      WriteFile (to_slave, ptr, len, &wLen, NULL);
+      return wLen;
+    }
+
   line_edit_status status = line_edit (p++, len, ti, &ret);
   if (status > line_edit_signalled && status != line_edit_pipe_full)
     ret = -1;
@@ -1443,6 +1667,17 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      if (getPseudoConsole ())
+	{
+	  HMODULE hModule = GetModuleHandle ("kernel32.dll");
+	  FARPROC func = GetProcAddress (hModule, "ResizePseudoConsole");
+	  HRESULT (WINAPI *ResizePseudoConsole) (HPCON, COORD) = NULL;
+	  ResizePseudoConsole = (HRESULT (WINAPI *) (HPCON, COORD)) func;
+	  COORD size;
+	  size.X = ((struct winsize *) arg)->ws_col;
+	  size.Y = ((struct winsize *) arg)->ws_row;
+	  ResizePseudoConsole (getPseudoConsole (), size);
+	}
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1458,7 +1693,7 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
     case FIONREAD:
       {
 	DWORD n;
-	if (!::bytes_available (n, get_handle_cyg ()))
+	if (!bytes_available (n))
 	  {
 	    set_errno (EINVAL);
 	    return -1;
@@ -1497,14 +1732,82 @@ fhandler_pty_common::set_close_on_exec (bool val)
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (change_codepage_to_utf8)
+	      {
+		/* FIXME: The terminal code may not be UTF-8. */
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#if 0 /* Experimental code */
+	    /* Clear screen to synchronize pseudo console screen buffer
+	       with real terminal. */
+	    /* FIXME: Clearing sequence may not be "^[[H^[[J"
+	       depending on the terminal type. However, it is
+	       "^[[H^[[J" for pseudo console. */
+	    if (!get_ttyp ()->screen_alternated)
+	      {
+#if 0
+		DWORD n;
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#else
+		this->write ("\033[H\033[J", 6);
+#endif
+	      }
+#endif
+	    pcon_attached = true;
+	  }
+    }
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   report_tty_counts (this, "inherited", "");
 }
 
 void
 fhandler_pty_slave::fixup_after_exec ()
 {
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	if (!pcon_attached)
+	  {
+	    if (change_codepage_to_utf8)
+	      {
+		/* FIXME: The terminal code may not be UTF-8. */
+		SetConsoleCP (65001);
+		SetConsoleOutputCP (65001);
+	      }
+#if 0 /* Experimental code */
+	    /* Clear screen to synchronize pseudo console screen buffer
+	       with real terminal. */
+	    /* FIXME: Clearing sequence may not be "^[[H^[[J"
+	       depending on the terminal type. However, it is
+	       "^[[H^[[J" for pseudo console. */
+	    if (!get_ttyp ()->screen_alternated)
+	      {
+#if 0
+		DWORD n;
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#else
+		this->write ("\033[H\033[J", 6);
+#endif
+	      }
+#endif
+	    pcon_attached = true;
+	  }
+    }
+  if (pcon_attached && get_ttyp ()->switch_to_pcon == 0)
+    get_ttyp ()->switch_to_pcon = 1;
   if (!close_on_exec ())
     fixup_after_fork (NULL);
 }
@@ -1544,7 +1847,7 @@ fhandler_pty_master::pty_master_thread ()
   while (!exit && (ConnectNamedPipe (master_ctl, NULL)
 		   || GetLastError () == ERROR_PIPE_CONNECTED))
     {
-      pipe_reply repl = { NULL, NULL, 0 };
+      pipe_reply repl = { NULL, NULL, NULL, 0 };
       bool deimp = false;
       NTSTATUS allow = STATUS_ACCESS_DENIED;
       ACCESS_MASK access = EVENT_MODIFY_STATE;
@@ -1614,6 +1917,13 @@ fhandler_pty_master::pty_master_thread ()
 	      termios_printf ("DuplicateHandle (from_master), %E");
 	      goto reply;
 	    }
+	  if (!DuplicateHandle (GetCurrentProcess (), from_master_cyg,
+			       client, &repl.from_master_cyg,
+			       0, TRUE, DUPLICATE_SAME_ACCESS))
+	    {
+	      termios_printf ("DuplicateHandle (from_master_cyg), %E");
+	      goto reply;
+	    }
 	  if (!DuplicateHandle (GetCurrentProcess (), to_master,
 				client, &repl.to_master,
 				0, TRUE, DUPLICATE_SAME_ACCESS))
@@ -1659,23 +1969,80 @@ fhandler_pty_master::pty_master_fwd_thread ()
   DWORD rlen;
   char outbuf[OUT_BUFFER_SIZE];
 
-  termios_printf("Started.");
+  termios_printf ("Started.");
   for (;;)
     {
-      if (!ReadFile (get_handle (), outbuf, sizeof outbuf, &rlen, NULL))
+      if (!ReadFile (from_slave, outbuf, sizeof outbuf, &rlen, NULL))
 	{
 	  termios_printf ("ReadFile for forwarding failed, %E");
 	  break;
 	}
       ssize_t wlen = rlen;
+      char *ptr = outbuf;
+      if (getPseudoConsole ())
+	{
+	  if (get_ttyp ()->switch_to_pcon == 1)
+	    get_ttyp ()->switch_to_pcon = 2;
+	  /* 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 == 4 && outbuf[i] == '\0'))
+	      {
+		state ++;
+		continue;
+	      }
+	    else if (state == 5 && outbuf[i] == '\a')
+	      {
+		memmove (&outbuf[start_at], &outbuf[i+1], rlen-i-1);
+		state = 0;
+		rlen = wlen = start_at + rlen - i - 1;
+		continue;
+	      }
+	    else if (state != 4 || outbuf[i] == '\a')
+	      {
+		state = 0;
+		continue;
+	      }
+
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (get_ttyp ()->switch_to_pcon != 2)
+	    continue;
+
+	  /* OPOST processing was already done in pseudo console,
+	     so just write it to to_master_cyg. */
+	  DWORD written;
+	  while (rlen>0)
+	    {
+	      if (!WriteFile (to_master_cyg, ptr, wlen, &written, NULL))
+		{
+		  termios_printf ("WriteFile for forwarding failed, %E");
+		  break;
+		}
+	      ptr += written;
+	      wlen = (rlen -= written);
+	    }
+	  continue;
+	}
       while (rlen>0)
 	{
-	  if (!process_opost_output (to_master_cyg, outbuf, wlen, false))
+	  if (!process_opost_output (to_master_cyg, ptr, wlen, false))
 	    {
 	      termios_printf ("WriteFile for forwarding failed, %E");
 	      break;
 	    }
-	  rlen -= wlen;
+	  ptr += wlen;
+	  wlen = (rlen -= wlen);
 	}
     }
   return 0;
@@ -1687,6 +2054,146 @@ pty_master_fwd_thread (VOID *arg)
   return ((fhandler_pty_master *) arg)->pty_master_fwd_thread ();
 }
 
+/* If master process is running as service, attaching to
+   pseudo console should be done in fork. If attaching
+   is done in spawn for inetd or sshd, it fails because
+   the helper process is running as privileged user while
+   slave process is not. This function is used to determine
+   if the process is running as a srvice or not. */
+static bool
+is_running_as_service (void)
+{
+  DWORD dwSize = 0;
+  PTOKEN_GROUPS pGroupInfo;
+  tmp_pathbuf tp;
+  pGroupInfo = (PTOKEN_GROUPS) tp.w_get ();
+  NtQueryInformationToken (hProcToken, TokenGroups, pGroupInfo,
+					2 * NT_MAX_PATH, &dwSize);
+  for (DWORD i=0; i<pGroupInfo->GroupCount; i++)
+    if (RtlEqualSid (well_known_service_sid, pGroupInfo->Groups[i].Sid))
+      return true;
+  for (DWORD i=0; i<pGroupInfo->GroupCount; i++)
+    if (RtlEqualSid (well_known_interactive_sid, pGroupInfo->Groups[i].Sid))
+      return false;
+  return true;
+}
+
+bool
+fhandler_pty_master::setup_pseudoconsole ()
+{
+  /* 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(). */
+  HMODULE hModule = GetModuleHandle ("kernel32.dll");
+  FARPROC func = GetProcAddress (hModule, "CreatePseudoConsole");
+  HRESULT (WINAPI *CreatePseudoConsole)
+    (COORD, HANDLE, HANDLE, DWORD, HPCON *) = NULL;
+  if (!func)
+    return false;
+  CreatePseudoConsole =
+    (HRESULT (WINAPI *) (COORD, HANDLE, HANDLE, DWORD, HPCON *)) func;
+  COORD size = {80, 25};
+  CreatePipe (&from_master, &to_slave, &sec_none, 0);
+  HRESULT res = CreatePseudoConsole (size, from_master, to_master,
+				     0, &get_ttyp ()->hPseudoConsole);
+  if (res != S_OK)
+    {
+      system_printf ("CreatePseudoConsole() failed. %08x\n",
+		     GetLastError());
+      CloseHandle (from_master);
+      CloseHandle (to_slave);
+      from_master = from_master_cyg;
+      to_slave = NULL;
+      get_ttyp ()->hPseudoConsole = NULL;
+      return false;
+    }
+#if 0
+  /* If master is running on real console,
+     attach to pseudo console in spawn. */
+  char tty[32];
+  for (int fd=0; fd<3; fd++)
+    {
+      ttyname_r (fd, tty, sizeof(tty));
+      if (strncmp (tty, "/dev/cons", 9) == 0)
+	get_ttyp ()->attach_pcon_in_spawn = true;
+    }
+#endif
+#if 1
+  /* If master process is running as service, attaching to
+     pseudo console should be done in fork. If attaching
+     is done in spawn for inetd or sshd, it fails because
+     the helper process is running as privileged user while
+     slave process is not. */
+  if (!is_running_as_service ())
+    get_ttyp ()->attach_pcon_in_spawn = true;
+#endif
+  SIZE_T bytesRequired;
+  InitializeProcThreadAttributeList (NULL, 1, 0, &bytesRequired);
+  STARTUPINFOEXW si_helper;
+  ZeroMemory (&si_helper, sizeof (si_helper));
+  si_helper.StartupInfo.cb = sizeof (STARTUPINFOEXW);
+  si_helper.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)
+    HeapAlloc (GetProcessHeap (), 0, bytesRequired);
+  InitializeProcThreadAttributeList (si_helper.lpAttributeList,
+				     1, 0, &bytesRequired);
+  UpdateProcThreadAttribute (si_helper.lpAttributeList,
+			     0,
+			     PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+			     get_ttyp ()->hPseudoConsole,
+			     sizeof (get_ttyp ()->hPseudoConsole),
+			     NULL, NULL);
+  HANDLE hello = CreateEvent (&sec_none, true, false, NULL);
+  HANDLE goodbye = CreateEvent (&sec_none, true, false, NULL);
+  /* Create a pipe for receiving pseudo console handles */
+  HANDLE hr, hw;
+  CreatePipe (&hr, &hw, &sec_none, 0);
+  /* Create helper process */
+  WCHAR cmd[256];
+  path_conv helper ("/bin/cygwin-console-helper.exe");
+  size_t len = helper.get_wide_win32_path_len ();
+  helper.get_wide_win32_path (cmd);
+  __small_swprintf (cmd + len, L" %p %p %p", hello, goodbye, hw);
+  si_helper.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+  si_helper.StartupInfo.hStdInput = NULL;
+  si_helper.StartupInfo.hStdOutput = NULL;
+  si_helper.StartupInfo.hStdError = NULL;
+  PROCESS_INFORMATION pi_helper;
+  CreateProcessW (NULL, cmd, &sec_none, &sec_none,
+		  TRUE, EXTENDED_STARTUPINFO_PRESENT,
+		  NULL, NULL, &si_helper.StartupInfo, &pi_helper);
+  WaitForSingleObject (hello, INFINITE);
+  /* Retrieve pseudo console handles */
+  DWORD rLen;
+  char buf[64];
+  ReadFile (hr, buf, sizeof (buf), &rLen, NULL);
+  buf[rLen] = '\0';
+  HANDLE hpConIn, hpConOut;
+  sscanf (buf, "StdHandles=%p,%p", &hpConIn, &hpConOut);
+  DuplicateHandle (pi_helper.hProcess, hpConIn,
+		   GetCurrentProcess (), &hpConIn, 0,
+		   TRUE, DUPLICATE_SAME_ACCESS);
+  DuplicateHandle (pi_helper.hProcess, hpConOut,
+		   GetCurrentProcess (), &hpConOut, 0,
+		   TRUE, DUPLICATE_SAME_ACCESS);
+  CloseHandle (hr);
+  CloseHandle (hw);
+  /* Clean up */
+  DeleteProcThreadAttributeList (si_helper.lpAttributeList);
+  HeapFree (GetProcessHeap (), 0, si_helper.lpAttributeList);
+  /* Setting information of stuffs regarding pseudo console */
+  get_ttyp ()->hHelperGoodbye = goodbye;
+  get_ttyp ()->hHelperProcess = pi_helper.hProcess;
+  get_ttyp ()->HelperProcessId = pi_helper.dwProcessId;
+  CloseHandle (from_master);
+  CloseHandle (to_master);
+  from_master = hpConIn;
+  to_master = hpConOut;
+  return true;
+}
+
 bool
 fhandler_pty_master::setup ()
 {
@@ -1695,10 +2202,15 @@ fhandler_pty_master::setup ()
   SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
 
   /* Find an unallocated pty to use. */
-  int unit = cygwin_shared->tty.allocate (from_master, get_output_handle ());
+  int unit = cygwin_shared->tty.allocate (from_master_cyg, get_output_handle ());
   if (unit < 0)
     return false;
 
+  /* from_master should be used for pseudo console.
+     Just copy from_master_cyg here for the case that
+     pseudo console is not available. */
+  from_master = from_master_cyg;
+
   ProtectHandle1 (get_output_handle (), to_pty);
 
   tty& t = *cygwin_shared->tty[unit];
@@ -1715,7 +2227,7 @@ fhandler_pty_master::setup ()
 
   char pipename[sizeof("ptyNNNN-to-master-cyg")];
   __small_sprintf (pipename, "pty%d-to-master", unit);
-  res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master,
+  res = fhandler_pipe::create (&sec_none, &from_slave, &to_master,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1724,7 +2236,7 @@ fhandler_pty_master::setup ()
     }
 
   __small_sprintf (pipename, "pty%d-to-master-cyg", unit);
-  res = fhandler_pipe::create (&sec_none, &get_handle_cyg (), &to_master_cyg,
+  res = fhandler_pipe::create (&sec_none, &get_handle (), &to_master_cyg,
 			       fhandler_pty_common::pipesize, pipename, 0);
   if (res)
     {
@@ -1732,7 +2244,7 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
-  ProtectHandle1 (get_handle (), from_pty);
+  ProtectHandle1 (from_slave, from_pty);
 
   __small_sprintf (pipename, "pty%d-echoloop", unit);
   res = fhandler_pipe::create (&sec_none, &echo_r, &echo_w,
@@ -1797,7 +2309,10 @@ fhandler_pty_master::setup ()
       goto err;
     }
 
+  setup_pseudoconsole ();
+
   t.set_from_master (from_master);
+  t.set_from_master_cyg (from_master_cyg);
   t.set_to_master (to_master);
   t.set_to_master_cyg (to_master_cyg);
   t.winsize.ws_col = 80;
@@ -1807,19 +2322,20 @@ fhandler_pty_master::setup ()
   dev ().parse (DEV_PTYM_MAJOR, unit);
 
   termios_printf ("this %p, pty%d opened - from_pty <%p,%p>, to_pty %p",
-	this, unit, get_handle (), get_handle_cyg (),
+	this, unit, from_slave, get_handle (),
 	get_output_handle ());
   return true;
 
 err:
   __seterrno ();
+  close_maybe (from_slave);
   close_maybe (get_handle ());
-  close_maybe (get_handle_cyg ());
   close_maybe (get_output_handle ());
   close_maybe (input_available_event);
   close_maybe (output_mutex);
   close_maybe (input_mutex);
   close_maybe (from_master);
+  close_maybe (from_master_cyg);
   close_maybe (to_master);
   close_maybe (to_master_cyg);
   close_maybe (echo_r);
@@ -1840,14 +2356,20 @@ fhandler_pty_master::fixup_after_fork (HANDLE parent)
       if (myself->pid == t.master_pid)
 	{
 	  t.set_from_master (arch->from_master);
+	  t.set_from_master_cyg (arch->from_master_cyg);
 	  t.set_to_master (arch->to_master);
 	  t.set_to_master_cyg (arch->to_master_cyg);
 	}
       arch->dwProcessId = wpid;
     }
   from_master = arch->from_master;
+  from_master_cyg = arch->from_master_cyg;
   to_master = arch->to_master;
   to_master_cyg = arch->to_master_cyg;
+#if 0 /* Not sure if this is necessary. */
+  from_slave = arch->from_slave;
+  to_slave = arch->to_slave;
+#endif
   report_tty_counts (this, "inherited master", "");
 }
 
@@ -1857,7 +2379,8 @@ fhandler_pty_master::fixup_after_exec ()
   if (!close_on_exec ())
     fixup_after_fork (spawn_info->parent);
   else
-    from_master = to_master = to_master_cyg = NULL;
+    from_master = from_master_cyg = to_master = to_master_cyg =
+      from_slave = to_slave = NULL;
 }
 
 BOOL
diff --git a/winsup/cygwin/fork.cc b/winsup/cygwin/fork.cc
index 7e1c08990..cb4da7330 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,26 @@ child_info::prefork (bool detached)
 int __stdcall
 frok::child (volatile char * volatile here)
 {
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	if (((fhandler_pty_master *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_master *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (!((fhandler_pty_master *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 85242ec06..04249eb94 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -667,6 +667,9 @@ peek_pipe (select_record *s, bool from_select)
 	    fhm->flush_to_slave ();
 	  }
 	  break;
+	case DEV_PTYS_MAJOR:
+	  ((fhandler_pty_slave *) fh)->reset_switch_to_pcon ();
+	  break;
 	default:
 	  if (fh->get_readahead_valid ())
 	    {
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index 4e549f7d4..cb4bdca06 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,31 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       return -1;
     }
 
+  DWORD pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+  cygheap_fdenum cfd (false);
+  bool attach_to_pcon = false;
+  int fd;
+  while ((fd = cfd.next ()) >= 0 && fd < 3)
+    if (cfd->get_major () == DEV_PTYS_MAJOR)
+      {
+	fhandler_base *fh = cfd;
+	if (((fhandler_pty_slave *) fh)->getPseudoConsole ())
+	  {
+	    DWORD dwHelperProcessId =
+	      ((fhandler_pty_slave *) fh)->getHelperProcessId ();
+	    debug_printf ("found a PTY slave %d: helper_PID=%d",
+			  cfd->get_minor (), dwHelperProcessId);
+	    if (((fhandler_pty_slave *) fh)->attach_pcon_in_spawn ())
+	      {
+		FreeConsole ();
+		AttachConsole (dwHelperProcessId);
+		attach_to_pcon = true;
+		break;
+	      }
+	  }
+      }
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -868,6 +893,13 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
   this->cleanup ();
   if (envblock)
     free (envblock);
+
+  if (attach_to_pcon && pidRestore)
+    {
+      FreeConsole ();
+      AttachConsole (pidRestore);
+    }
+
   return (int) res;
 }
 
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..06bae302e 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,11 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_spawn = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = 0;
+  screen_alternated = false;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..3268b668d 100644
--- a/winsup/cygwin/tty.h
+++ b/winsup/cygwin/tty.h
@@ -28,6 +28,8 @@ details. */
 #define MIN_CTRL_C_SLOP 50
 #endif
 
+typedef void *HPCON;
+
 #include <devices.h>
 class tty_min
 {
@@ -88,14 +90,24 @@ public:
 
 private:
   HANDLE _from_master;
+  HANDLE _from_master_cyg;
   HANDLE _to_master;
   HANDLE _to_master_cyg;
+  HPCON hPseudoConsole;
+  HANDLE hHelperProcess;
+  DWORD HelperProcessId;
+  HANDLE hHelperGoodbye;
+  bool attach_pcon_in_spawn;
+  int switch_to_pcon;
+  bool screen_alternated;
 
 public:
-  HANDLE from_master() const { return _from_master; }
-  HANDLE to_master() const { return _to_master; }
-  HANDLE to_master_cyg() const { return _to_master_cyg; }
+  HANDLE from_master () const { return _from_master; }
+  HANDLE from_master_cyg () const { return _from_master_cyg; }
+  HANDLE to_master () const { return _to_master; }
+  HANDLE to_master_cyg () const { return _to_master_cyg; }
   void set_from_master (HANDLE h) { _from_master = h; }
+  void set_from_master_cyg (HANDLE h) { _from_master_cyg = h; }
   void set_to_master (HANDLE h) { _to_master = h; }
   void set_to_master_cyg (HANDLE h) { _to_master_cyg = h; }
 
@@ -117,7 +129,9 @@ public:
   void set_master_ctl_closed () {master_pid = -1;}
   static void __stdcall create_master (int);
   static void __stdcall init_session ();
+  friend class fhandler_pty_common;
   friend class fhandler_pty_master;
+  friend class fhandler_pty_slave;
 };
 
 class tty_list
diff --git a/winsup/utils/cygwin-console-helper.cc b/winsup/utils/cygwin-console-helper.cc
index 8f62ed7e6..bef143f96 100644
--- a/winsup/utils/cygwin-console-helper.cc
+++ b/winsup/utils/cygwin-console-helper.cc
@@ -1,12 +1,24 @@
 #include <windows.h>
+#include <stdio.h>
 int
 main (int argc, char **argv)
 {
   char *end;
-  if (argc != 3)
+  if (argc < 3)
     exit (1);
   HANDLE h = (HANDLE) strtoul (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoul (argv[3], &end, 0);
+      char buf[64];
+      sprintf (buf, "StdHandles=%p,%p\n",
+	       GetStdHandle (STD_INPUT_HANDLE),
+	       GetStdHandle (STD_OUTPUT_HANDLE));
+      DWORD dwLen;
+      WriteFile (hPipe, buf, strlen (buf), &dwLen, NULL);
+      CloseHandle (hPipe);
+    }
   h = (HANDLE) strtoul (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.17.0



More information about the Cygwin-developers mailing list