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

Takashi Yano takashi.yano@nifty.ne.jp
Thu Aug 15 16:09:00 GMT 2019


- Support pseudo console in PTY. Pseudo console is a new feature
  in Windows 10 1809, which provides 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               |  50 ++
 winsup/cygwin/fhandler.h              |  42 +-
 winsup/cygwin/fhandler_console.cc     |  32 +
 winsup/cygwin/fhandler_tty.cc         | 843 +++++++++++++++++++++++++-
 winsup/cygwin/fork.cc                 |  24 +
 winsup/cygwin/select.cc               |  22 +-
 winsup/cygwin/spawn.cc                |  55 ++
 winsup/cygwin/strace.cc               |  24 +
 winsup/cygwin/tty.cc                  |   8 +
 winsup/cygwin/tty.h                   |  24 +-
 winsup/utils/cygwin-console-helper.cc |  14 +-
 11 files changed, 1093 insertions(+), 45 deletions(-)

diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
index 636221a77..63961648c 100644
--- a/winsup/cygwin/dtable.cc
+++ b/winsup/cygwin/dtable.cc
@@ -147,6 +147,35 @@ dtable::get_debugger_info ()
 void
 dtable::stdio_init ()
 {
+  bool need_fixup_handle = false;
+  fhandler_pty_slave *ptys = NULL;
+  bool is_pty[3] = {false, false, false};
+  for (int fd = 0; fd < 3; fd ++)
+    {
+      fhandler_base *fh = cygheap->fdtab[fd];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole ())
+	    {
+	      is_pty[fd] = true;
+	      bool attached = !!fhandler_console::get_console_process_id
+		(ptys->getHelperProcessId (), true);
+	      if (!attached)
+		{
+		  /* Not attached to pseudo console in fork() or spawn()
+		     by some reason. This happens if the executable is
+		     a windows GUI binary, such as mintty. */
+		  FreeConsole ();
+		  AttachConsole (ptys->getHelperProcessId ());
+		  need_fixup_handle = true;
+		}
+	    }
+	}
+    }
+  if (need_fixup_handle)
+    goto fixup_handle;
+
   if (myself->cygstarted || ISSTATE (myself, PID_CYGPARENT))
     {
       tty_min *t = cygwin_shared->tty.get_cttyp ();
@@ -155,6 +184,27 @@ dtable::stdio_init ()
       return;
     }
 
+fixup_handle:
+  if (need_fixup_handle)
+    {
+      HANDLE h;
+      h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_READ,
+		       NULL, OPEN_EXISTING, 0, 0);
+      if (is_pty[0])
+	{
+	  SetStdHandle (STD_INPUT_HANDLE, h);
+	  ptys->set_handle (h);
+	}
+      h = CreateFile ("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
+		       NULL, OPEN_EXISTING, 0, 0);
+      if (is_pty[1])
+	SetStdHandle (STD_OUTPUT_HANDLE, h);
+      if (is_pty[2])
+	SetStdHandle (STD_ERROR_HANDLE, h);
+      if (is_pty[1] || is_pty[2])
+	ptys->set_output_handle (h);
+    }
+
   HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
   HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
   HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 794948dba..a1eeffc49 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2019,6 +2019,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 *) {}
 
@@ -2051,8 +2052,8 @@ 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)
   {
     pc.file_attributes (FILE_ATTRIBUTE_NORMAL);
   }
@@ -2089,14 +2090,29 @@ class fhandler_pty_common: public fhandler_termios
     return fh;
   }
 
+  bool attach_pcon_in_fork (void)
+  {
+    return get_ttyp ()->attach_pcon_in_fork;
+  }
+  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);
 };
 
 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;
+  bool pcon_attached;
 
   /* Helper functions for fchmod and fchown. */
   bool fch_open_handles (bool chown);
@@ -2109,6 +2125,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);
@@ -2149,6 +2167,14 @@ class fhandler_pty_slave: public fhandler_pty_common
     copyto (fh);
     return fh;
   }
+  void reset_switch_to_pcon (void);
+  void push_to_pcon_screenbuffer (const char *ptr, size_t len);
+  bool has_master_opened (void);
+  void mask_switch_to_pcon (bool mask)
+  {
+    get_ttyp ()->mask_switch_to_pcon = mask;
+  }
+  void fixup_after_attach (bool native_maybe);
 };
 
 #define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
@@ -2157,17 +2183,17 @@ 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);
+  ~fhandler_pty_master ();
 
   DWORD pty_master_thread ();
   DWORD pty_master_fwd_thread ();
@@ -2212,6 +2238,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 67638055e..997c50d23 100644
--- a/winsup/cygwin/fhandler_console.cc
+++ b/winsup/cygwin/fhandler_console.cc
@@ -1056,6 +1056,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;
@@ -3119,6 +3132,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..f2c9138dd 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -25,6 +25,21 @@ 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 DISABLE_NEWLINE_AUTO_RETURN
+#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
+#endif /* DISABLE_NEWLINE_AUTO_RETURN */
+#ifndef PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
+#define PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE 0x00020016
+#endif /* PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE */
+
+extern "C" int sscanf (const char *, const char *, ...);
+extern "C" int ttyname_r(int, char*, size_t);
 
 #define close_maybe(h) \
   do { \
@@ -39,6 +54,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 +83,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
@@ -235,7 +251,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 +312,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;
@@ -331,7 +347,8 @@ out:
 /* pty slave stuff */
 
 fhandler_pty_slave::fhandler_pty_slave (int unit)
-  : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL)
+  : fhandler_pty_common (), inuse (NULL), output_handle_cyg (NULL),
+  io_handle_cyg (NULL), pcon_attached (false)
 {
   if (unit >= 0)
     dev ().parse (DEV_PTYS_MAJOR, unit);
@@ -340,11 +357,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 +416,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 +461,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 +503,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,17 +515,21 @@ 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);
 
@@ -548,11 +583,20 @@ 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 */
+#if 1
+  if (pcon_attached)
+    init_console_handler (false);
+#endif
   fhandler_pty_common::close ();
   if (!ForceCloseHandle (output_mutex))
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+  if (pcon_attached)
+    get_ttyp ()->num_pcon_attached_slaves --;
   return 0;
 }
 
@@ -596,6 +640,166 @@ fhandler_pty_slave::init (HANDLE h, DWORD a, mode_t)
   return ret;
 }
 
+void
+fhandler_pty_slave::reset_switch_to_pcon (void)
+{
+  if (get_ttyp ()->pcon_pid &&
+      get_ttyp ()->pcon_pid != myself->pid &&
+      kill (get_ttyp ()->pcon_pid, 0) == 0)
+    /* There is a process which is grabbing pseudo console. */
+    return;
+  if (get_ttyp ()->switch_to_pcon &&
+      get_ttyp ()->pcon_pid != myself->pid)
+    Sleep (20);
+  get_ttyp ()->pcon_pid = 0;
+  get_ttyp ()->switch_to_pcon = false;
+}
+
+void
+fhandler_pty_slave::push_to_pcon_screenbuffer (const char *ptr, size_t len)
+{
+  DWORD pidRestore = 0;
+  if (!fhandler_console::get_console_process_id (getHelperProcessId (), true))
+    if (pcon_attached)
+      {
+	system_printf ("pty%d: pcon_attach mismatch?????? (%p)", get_minor (), this);
+	pcon_attached = false;
+      }
+  /* If not attached pseudo console yet, try to attach temporally. */
+  if (!pcon_attached)
+    {
+      if (has_master_opened ())
+	return;
+
+      pidRestore =
+	fhandler_console::get_console_process_id (GetCurrentProcessId (),
+						  false);
+      /* If pidRestore is not set, give up to push. */
+      if (!pidRestore)
+	return;
+
+      FreeConsole ();
+      if (!AttachConsole (getHelperProcessId ()))
+	{
+	  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), getHelperProcessId (), this, GetLastError ());
+	  goto detach;
+	}
+    }
+  char *buf;
+  size_t nlen;
+  DWORD origCP;
+  origCP = GetConsoleOutputCP ();
+  SetConsoleOutputCP (get_ttyp ()->TermCodePage);
+  /* Just copy */
+  buf = (char *) HeapAlloc (GetProcessHeap (), 0, len);
+  memcpy (buf, (char *)ptr, len);
+  nlen = len;
+  /* Remove alternate screen buffer drawing */
+  char *p0, *p1;
+  p0 = p1 = buf;
+  while (p0 && p1)
+    {
+      if (!get_ttyp ()->screen_alternated)
+	{
+	  /* Check switching to alternate screen buffer */
+	  p0 = (char *) memmem (p1, nlen - (p1-buf), "\033[?1049h", 8);
+	  if (p0)
+	    {
+	      //p0 += 8;
+	      get_ttyp ()->screen_alternated = true;
+	    }
+	}
+      if (get_ttyp ()->screen_alternated)
+	{
+	  /* Check switching to main screen buffer */
+	  p1 = (char *) memmem (p0, nlen - (p0-buf), "\033[?1049l", 8);
+	  if (p1)
+	    {
+	      p1 += 8;
+	      get_ttyp ()->screen_alternated = false;
+	      memmove (p0, p1, buf+nlen - p1);
+	      nlen -= p1 - p0;
+	    }
+	  else
+	    nlen = p0 - buf;
+	}
+    }
+  if (!nlen) /* Nothing to be synchronized */
+    goto cleanup;
+  if (get_ttyp ()->switch_to_pcon)
+    goto cleanup;
+  /* Remove ESC sequence which returns results to console
+     input buffer. Without this, cursor position report
+     is put into the input buffer as a garbage. */
+  /* 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, flags;
+  flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+  GetConsoleMode (get_output_handle (), &dwMode);
+  if (!(get_ttyp ()->ti.c_oflag & OPOST) ||
+      !(get_ttyp ()->ti.c_oflag & ONLCR))
+    flags |= DISABLE_NEWLINE_AUTO_RETURN;
+  SetConsoleMode (get_output_handle (), dwMode | flags);
+  char *p;
+  p = buf;
+  DWORD wLen, written;
+  written = 0;
+  int retry_count;
+  retry_count = 0;
+  while (written <  nlen)
+    {
+      if (!WriteFile (get_output_handle (), p, nlen - written, &wLen, NULL))
+	{
+	  termios_printf ("WriteFile failed, %E");
+	  this->open (0, 0); /* Re-open handles */
+	  /* Fix pseudo console window size */
+	  struct winsize win;
+	  this->ioctl (TIOCGWINSZ, &win);
+	  this->ioctl (TIOCSWINSZ, &win);
+	  if (++retry_count > 3)
+	    break;
+	}
+      written += wLen;
+      p += wLen;
+    }
+  /* Detach from pseudo console and resume. */
+  SetConsoleMode (get_output_handle (), dwMode);
+cleanup:
+  SetConsoleOutputCP (origCP);
+  HeapFree (GetProcessHeap (), 0, buf);
+detach:
+  if (!pcon_attached)
+    {
+      FreeConsole ();
+      if (!AttachConsole (pidRestore))
+	{
+	  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), pidRestore, this, GetLastError ());
+	  pcon_attached = false;
+	}
+    }
+}
+
+bool
+fhandler_pty_slave::has_master_opened (void)
+{
+  cygheap_fdenum cfd (false);
+  while (cfd.next () >= 0)
+    if (cfd->get_major () == DEV_PTYM_MAJOR &&
+	cfd->get_minor () == get_minor ())
+      return true;
+  return false;
+}
+
 ssize_t __stdcall
 fhandler_pty_slave::write (const void *ptr, size_t len)
 {
@@ -609,6 +813,16 @@ 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 ())
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer ((char *)ptr, len);
+      release_output_mutex ();
+    }
+
   if (!process_opost_output (get_output_handle_cyg (), ptr, towrite, false))
     {
       DWORD err = GetLastError ();
@@ -629,6 +843,7 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
 void __reg3
 fhandler_pty_slave::read (void *ptr, size_t& len)
 {
+  char *ptr0 = (char *)ptr;
   ssize_t totalread = 0;
   int vmin = 0;
   int vtime = 0;	/* Initialized to prevent -Wuninitialized warning */
@@ -644,10 +859,13 @@ 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);
 
+  mask_switch_to_pcon (true);
+  reset_switch_to_pcon ();
+
   if (is_nonblocking () || !ptr) /* Indicating tcflush(). */
     time_to_wait = 0;
   else if ((get_ttyp ()->ti.c_lflag & ICANON))
@@ -757,6 +975,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 (false);
 	  len = (size_t) bytes_in_pipe;
 	  return;
 	}
@@ -777,7 +996,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);
@@ -861,6 +1080,16 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
 out:
   termios_printf ("%d = read(%p, %lu)", totalread, ptr, len);
   len = (size_t) totalread;
+#if 1 /* Experimenta code */
+  /* Push slave read as echo to pseudo console screen buffer. */
+  if (getPseudoConsole () && ptr0 && (get_ttyp ()->ti.c_lflag & ECHO))
+    {
+      acquire_output_mutex (INFINITE);
+      push_to_pcon_screenbuffer (ptr0, len);
+      release_output_mutex ();
+    }
+#endif
+  mask_switch_to_pcon (false);
 }
 
 int
@@ -890,6 +1119,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 +1127,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,7 +1139,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)
     {
@@ -929,6 +1162,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 +1229,60 @@ 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)
+	    {
+	      if (has_master_opened () && get_ttyp ()->attach_pcon_in_fork)
+		goto resize_cyg;
+
+	      pidRestore = fhandler_console::get_console_process_id
+		(GetCurrentProcessId (), false);
+
+	      /* This happens at mintty startup if fhandler_console::
+		 need_invisible() is called in stdio_init() in dtable.cc */
+	      if (!pidRestore) /* Give up to resize pseudo console */
+		goto resize_cyg;
+
+	      FreeConsole ();
+	      if (!AttachConsole (getHelperProcessId ()))
+		{
+		  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor(), getHelperProcessId (), this, GetLastError ());
+		  goto cleanup;
+		}
+	    }
+	  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 cleanup;
+	  if (!SetConsoleScreenBufferSize (get_output_handle (), size))
+	    goto cleanup;
+	  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);
+cleanup:
+	  /* Detach from pseudo console and resume. */
+	  if (pidRestore)
+	    {
+	      FreeConsole ();
+	      if (!AttachConsole (pidRestore))
+		{
+		  system_printf ("pty%d: AttachConsole(%d) failed. (%p) %08lx", get_minor (), pidRestore, this, GetLastError ());
+		  pcon_attached = false;
+		}
+	    }
+	}
+resize_cyg:
       if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
 	  || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
 	{
@@ -1228,8 +1516,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);
@@ -1241,6 +1530,15 @@ fhandler_pty_master::fhandler_pty_master (int unit)
   set_name ("/dev/ptmx");
 }
 
+fhandler_pty_master::~fhandler_pty_master ()
+{
+  /* Without this wait, helper process for pseudo console
+     sometimes remains running after the pty session is
+     closed. The reason is not clear. */
+  if (to_master && from_master)
+    Sleep (20);
+}
+
 int
 fhandler_pty_master::open (int flags, mode_t)
 {
@@ -1285,7 +1583,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 ();
 }
 
@@ -1294,9 +1593,11 @@ fhandler_pty_master::close ()
 {
   OBJECT_BASIC_INFORMATION obi;
   NTSTATUS status;
+  pid_t master_pid_tmp = get_ttyp ()->master_pid;
 
-  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)
@@ -1334,9 +1635,30 @@ fhandler_pty_master::close ()
     termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
   if (!NT_SUCCESS (status))
     debug_printf ("NtQueryObject: %y", status);
-  else if (obi.HandleCount == 1)
+  else if (obi.HandleCount == (getPseudoConsole () ? 2 : 1))
+			      /* Helper process has inherited one. */
     {
       termios_printf("Closing last master of pty%d", get_minor ());
+      /* Close Pseudo Console */
+      if (getPseudoConsole ())
+	{
+	  /* Terminate helper process */
+	  SetEvent (get_ttyp ()->hHelperGoodbye);
+	  WaitForSingleObject (get_ttyp ()->hHelperProcess, INFINITE);
+	  /* FIXME: Pseudo console can be accessed via its handle
+	     only in the process which created it. What else can we do? */
+	  if (master_pid_tmp == myself->pid)
+	    {
+	      /* 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 = false;
+	}
       if (get_ttyp ()->getsid () > 0)
 	kill (get_ttyp ()->getsid (), SIGHUP);
       SetEvent (input_available_event);
@@ -1344,17 +1666,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 +1705,44 @@ 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 &&
+      !get_ttyp ()->mask_switch_to_pcon)
+    {
+      char *buf;
+      size_t nlen;
+      if (get_ttyp ()->TermCodePage != 65001)
+	{
+	  size_t wlen =
+	    MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+				 (char *)ptr, len, NULL, 0);
+	  wchar_t *wbuf = (wchar_t *)
+	    HeapAlloc (GetProcessHeap (), 0, wlen * sizeof (wchar_t));
+	  wlen =
+	    MultiByteToWideChar (get_ttyp ()->TermCodePage, 0,
+				 (char *)ptr, len, wbuf, wlen);
+	  nlen = WideCharToMultiByte (CP_UTF8, 0,
+				      wbuf, wlen, NULL, 0, NULL, NULL);
+	  buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+	  nlen = WideCharToMultiByte (CP_UTF8, 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;
+	}
+      DWORD wLen;
+      WriteFile (to_slave, buf, nlen, &wLen, NULL);
+      HeapFree (GetProcessHeap (), 0, buf);
+      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 +1810,19 @@ fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
       *(struct winsize *) arg = get_ttyp ()->winsize;
       break;
     case TIOCSWINSZ:
+      /* FIXME: Pseudo console can be accessed via its handle
+	 only in the process which created it. What else can we do? */
+      if (getPseudoConsole () && get_ttyp ()->master_pid == myself->pid)
+	{
+	  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 +1838,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;
@@ -1494,9 +1874,166 @@ fhandler_pty_common::set_close_on_exec (bool val)
   close_on_exec (val);
 }
 
+void
+fhandler_pty_slave::fixup_after_attach (bool native_maybe)
+{
+  if (getPseudoConsole ())
+    {
+      if (fhandler_console::get_console_process_id (getHelperProcessId (),
+						    true))
+	{
+	  if (!pcon_attached)
+	    {
+	      setlocale (LC_CTYPE, "");
+	      /* Set console code page form locale */
+	      wchar_t lc[32];
+	      wchar_t *p;
+	      mbstowcs (lc, setlocale (LC_CTYPE, NULL), 32);
+	      p = wcschr (lc, L'.');
+	      if (p)
+		*p = L'\0';
+	      p = wcschr (lc, L'_');
+	      if (p)
+		*p = L'-';
+	      LCID lcid = LocaleNameToLCID (lc, 0);
+	      UINT CodePage;
+	      if (lcid == 0 || lcid == (LCID) -1)
+		CodePage = 65001;
+	      else if (!GetLocaleInfo (lcid,
+			  LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
+			  (char *) &CodePage, sizeof (CodePage)))
+		CodePage = 65001;
+	      SetConsoleCP (CodePage);
+	      SetConsoleOutputCP (CodePage);
+	      /* Set terminal code page from locale */
+	      /* FIXME: How the code page corresponding to charset
+		 can be retrieved? */
+	      char *p1;
+	      p1 = strchr (setlocale (LC_CTYPE, NULL), '.');
+	      if (p1)
+		{
+		  if (strcasecmp (p1+1, "UTF-8") == 0)
+		    get_ttyp ()->TermCodePage = 65001;
+		  else if (strcasecmp (p1+1, "ASCII") == 0)
+		    get_ttyp ()->TermCodePage = 20127;
+		  else if (strcasecmp (p1+1, "EUC-JP") == 0)
+		    get_ttyp ()->TermCodePage = 20932;
+		  else if (strcasecmp (p1+1, "EUCJP") == 0)
+		    get_ttyp ()->TermCodePage = 20932;
+		  else if (strcasecmp (p1+1, "SJIS") == 0)
+		    get_ttyp ()->TermCodePage = 932;
+		  else if (strcasecmp (p1+1, "GB2312") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "GBK") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUC-CN") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUCCN") == 0)
+		    get_ttyp ()->TermCodePage = 936;
+		  else if (strcasecmp (p1+1, "EUC-KR") == 0)
+		    get_ttyp ()->TermCodePage = 949 /*51949*/;
+		  else if (strcasecmp (p1+1, "EUCKR") == 0)
+		    get_ttyp ()->TermCodePage = 949 /*51949*/;
+		  else if (strcasecmp (p1+1, "BIG5") == 0)
+		    get_ttyp ()->TermCodePage = 950;
+		  else if (strcasecmp (p1+1, "ISO-8859-1") == 0)
+		    {
+		      if (lcid == 0x041c)
+			get_ttyp ()->TermCodePage = 1250;
+		      else if (lcid == 0x0443)
+			get_ttyp ()->TermCodePage = 1254;
+		      else
+			get_ttyp ()->TermCodePage = 1252;
+		    }
+		  else if (strcasecmp (p1+1, "ISO-8859-2") == 0)
+		    {
+		      if (lcid == 0x042e)
+			get_ttyp ()->TermCodePage = 1252;
+		      else
+			get_ttyp ()->TermCodePage = 1250;
+		    }
+		  else if (strcasecmp (p1+1, "KOI8-U") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "KOI8U") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "KOI8-R") == 0)
+		    get_ttyp ()->TermCodePage = 20866;
+		  else if (strcasecmp (p1+1, "KOI8R") == 0)
+		    get_ttyp ()->TermCodePage = 20866;
+		  else if (strcasecmp (p1+1, "ISO-8859-5") == 0)
+		    get_ttyp ()->TermCodePage = 1251;
+		  else if (strcasecmp (p1+1, "ISO-8859-14") == 0)
+		    get_ttyp ()->TermCodePage = 1252;
+		  else if (strcasecmp (p1+1, "ISO-8859-15") == 0)
+		    {
+		      if (lcid == 0x0425)
+			get_ttyp ()->TermCodePage = 1257;
+		      else
+			get_ttyp ()->TermCodePage = 1252;
+		    }
+		  else if (strcasecmp (p1+1, "ISO-8859-7") == 0)
+		    get_ttyp ()->TermCodePage = 1253;
+		  else if (strcasecmp (p1+1, "ISO-8859-9") == 0)
+		    get_ttyp ()->TermCodePage = 1254;
+		  else if (strcasecmp (p1+1, "ISO-8859-8") == 0)
+		    get_ttyp ()->TermCodePage = 1255;
+		  else if (strcasecmp (p1+1, "ISO-8859-6") == 0)
+		    get_ttyp ()->TermCodePage = 1256;
+		  else if (strcasecmp (p1+1, "ISO-8859-13") == 0)
+		    get_ttyp ()->TermCodePage = 1257;
+		  else if (strcasecmp (p1+1, "ISO-8859-3") == 0)
+		    get_ttyp ()->TermCodePage = 28593;
+		  else if (strcasecmp (p1+1, "ISO-8859-4") == 0)
+		    get_ttyp ()->TermCodePage = 28594;
+		  else if (strcasecmp (p1+1, "ISO-8859-10") == 0)
+		    get_ttyp ()->TermCodePage = 28600;
+		  else if (strcasecmp (p1+1, "ISO-8859-11") == 0)
+		    get_ttyp ()->TermCodePage = 28601;
+		  else if (strcasecmp (p1+1, "ISO-8859-16") == 0)
+		    get_ttyp ()->TermCodePage = 28606;
+		  else if (strcasecmp (p1+1, "TIS620") == 0)
+		    get_ttyp ()->TermCodePage = 874;
+		  else if (strcasecmp (p1+1, "TIS-620") == 0)
+		    get_ttyp ()->TermCodePage = 874;
+		  else if (strncasecmp (p1+1, "CP", 2) == 0)
+		    get_ttyp ()->TermCodePage = strtoul (p1+3, NULL, 0);
+		}
+	      if (get_ttyp ()->TermCodePage == 0)
+		get_ttyp ()->TermCodePage = 65001; /* Fallback */
+#if 1 /* 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. */
+	      DWORD n;
+	      if (get_ttyp ()->num_pcon_attached_slaves == 0)
+		/* Assume this is the first process using this pty slave. */
+		WriteFile (get_output_handle_cyg (),
+			   "\033[H\033[J", 6, &n, NULL);
+#endif
+	      pcon_attached = true;
+	      get_ttyp ()->num_pcon_attached_slaves ++;
+	    }
+	}
+      else
+	pcon_attached = false;
+    }
+  if (pcon_attached && native_maybe)
+    get_ttyp ()->switch_to_pcon = true;
+  if (pcon_attached && native_maybe &&
+      (get_ttyp ()->pcon_pid == 0 || kill (get_ttyp ()->pcon_pid, 0) != 0))
+    get_ttyp ()->pcon_pid = myself->pid;
+#if 1
+  if (pcon_attached)
+    init_console_handler (true);
+#endif
+}
+
 void
 fhandler_pty_slave::fixup_after_fork (HANDLE parent)
 {
+  fixup_after_attach (false);
   // fork_fixup (parent, inuse, "inuse");
   // fhandler_pty_common::fixup_after_fork (parent);
   report_tty_counts (this, "inherited", "");
@@ -1507,6 +2044,11 @@ fhandler_pty_slave::fixup_after_exec ()
 {
   if (!close_on_exec ())
     fixup_after_fork (NULL);
+  if (get_ttyp ()->pcon_pid == myself->pid)
+    {
+      get_ttyp ()->switch_to_pcon = false;
+      get_ttyp ()->pcon_pid = 0;
+    }
 }
 
 /* This thread function handles the master control pipe.  It waits for a
@@ -1544,7 +2086,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 +2156,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 +2208,108 @@ 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 ())
+	{
+	  /* Avoid duplicating slave output which is already sent to
+	     to_master_cyg */
+	  if (!get_ttyp ()->switch_to_pcon)
+	    continue;
+
+	  /* 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;
+	      }
+
+	  char *buf;
+	  size_t nlen;
+	  if (get_ttyp ()->TermCodePage != 65001)
+	    {
+	      size_t wlen2 =
+		MultiByteToWideChar (CP_UTF8, 0,
+				     (char *)ptr, wlen, NULL, 0);
+	      wchar_t *wbuf = (wchar_t *)
+		HeapAlloc (GetProcessHeap (), 0, wlen2 * sizeof (wchar_t));
+	      wlen2 =
+		MultiByteToWideChar (CP_UTF8, 0,
+				     (char *)ptr, wlen, wbuf, wlen2);
+	      nlen = WideCharToMultiByte (get_ttyp ()->TermCodePage, 0,
+					  wbuf, wlen2, NULL, 0, NULL, NULL);
+	      buf = (char *) HeapAlloc (GetProcessHeap (), 0, nlen);
+	      nlen = WideCharToMultiByte (get_ttyp ()->TermCodePage, 0,
+					  wbuf, wlen2, buf, nlen, NULL, NULL);
+	      HeapFree (GetProcessHeap (), 0, wbuf);
+	    }
+	  else
+	    {
+	      /* Just copy */
+	      buf = (char *) HeapAlloc (GetProcessHeap (), 0, wlen);
+	      memcpy (buf, (char *)ptr, wlen);
+	      nlen = wlen;
+	    }
+	  ptr = buf;
+	  wlen = rlen = nlen;
+
+	  /* 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);
+	    }
+	  HeapFree (GetProcessHeap (), 0, buf);
+	  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 +2321,135 @@ 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 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_fork = true;
+
+  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 +2458,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 +2483,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 +2492,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 +2500,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 +2565,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 +2578,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 +2612,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 +2635,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 7080144b9..0c089dbe9 100644
--- a/winsup/cygwin/fork.cc
+++ b/winsup/cygwin/fork.cc
@@ -134,6 +134,30 @@ 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;
+	fhandler_pty_master *ptym = (fhandler_pty_master *) fh;
+	if (ptym->getPseudoConsole () &&
+	    !fhandler_console::get_console_process_id (
+				ptym->getHelperProcessId (), true))
+
+	  {
+	    debug_printf ("found a PTY master %d: helper_PID=%d",
+			  ptym->get_minor (), ptym->getHelperProcessId ());
+	    if (ptym->attach_pcon_in_fork ())
+	      {
+		FreeConsole ();
+		if (!AttachConsole (ptym->getHelperProcessId ()))
+		  /* Error */;
+		else
+		  break;
+	      }
+	  }
+      }
+
   HANDLE& hParent = ch.parent;
 
   sync_with_parent ("after longjmp", true);
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index 4e9256b9f..688859b50 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 ())
 	    {
@@ -1178,17 +1181,32 @@ verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds,
   return set_bits (me, readfds, writefds, exceptfds);
 }
 
+static int
+pty_slave_startup (select_record *s, select_stuff *)
+{
+  fhandler_base *fh = (fhandler_base *) s->fh;
+  ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (true);
+  return 1;
+}
+
+static void
+pty_slave_cleanup (select_record *s, select_stuff *)
+{
+  fhandler_base *fh = (fhandler_base *) s->fh;
+  ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (false);
+}
+
 select_record *
 fhandler_pty_slave::select_read (select_stuff *ss)
 {
   select_record *s = ss->start.next;
   s->h = input_available_event;
-  s->startup = no_startup;
+  s->startup = pty_slave_startup;
   s->peek = peek_pipe;
   s->verify = verify_tty_slave;
   s->read_selected = true;
   s->read_ready = false;
-  s->cleanup = NULL;
+  s->cleanup = pty_slave_cleanup;
   return s;
 }
 
diff --git a/winsup/cygwin/spawn.cc b/winsup/cygwin/spawn.cc
index d95772802..b4045fb12 100644
--- a/winsup/cygwin/spawn.cc
+++ b/winsup/cygwin/spawn.cc
@@ -288,6 +288,54 @@ child_info_spawn::worker (const char *prog_arg, const char *const *argv,
       return -1;
     }
 
+  DWORD pidRestore =
+    fhandler_console::get_console_process_id (GetCurrentProcessId (), false);
+  bool attach_to_pcon = false;
+  fhandler_pty_slave *ptys = NULL;
+  for (int fd = 0; fd < 3; fd ++)
+    {
+      fhandler_base *fh = ::cygheap->fdtab[fd];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole () &&
+	      !fhandler_console::get_console_process_id (
+				  ptys->getHelperProcessId (), true))
+	    {
+	      DWORD dwHelperProcessId = ptys->getHelperProcessId ();
+	      debug_printf ("found a PTY slave %d: helper_PID=%d",
+			    fh->get_minor (), dwHelperProcessId);
+	      FreeConsole ();
+	      if (!AttachConsole (dwHelperProcessId))
+		{
+		  /* Fallback */
+		  DWORD target[3] = {
+		    STD_INPUT_HANDLE,
+		    STD_OUTPUT_HANDLE,
+		    STD_ERROR_HANDLE
+		  };
+		  if (fd == 0)
+		    {
+		      ptys->set_handle (ptys->get_handle_cyg ());
+		      SetStdHandle (target[fd], ptys->get_handle ());
+		    }
+		  else
+		    {
+		      ptys->set_output_handle (ptys->get_output_handle_cyg ());
+		      SetStdHandle (target[fd], ptys->get_output_handle ());
+		    }
+		}
+	      else
+		{
+		  attach_to_pcon = true;
+		  break;
+		}
+	    }
+	}
+    }
+  if (ptys)
+    ptys->fixup_after_attach (true);
+
   av newargv;
   linebuf cmd;
   PWCHAR envblock = NULL;
@@ -869,6 +917,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/strace.cc b/winsup/cygwin/strace.cc
index 35f8a59ae..b1eb5f3e4 100644
--- a/winsup/cygwin/strace.cc
+++ b/winsup/cygwin/strace.cc
@@ -279,6 +279,30 @@ strace::vprntf (unsigned category, const char *func, const char *fmt, va_list ap
 	      CloseHandle (h);
 	    }
 	}
+#if 1 /* Experimental code */
+      /* PTY with pseudo console cannot display data written to
+	 STD_ERROR_HANDLE (output_handle) if the process is cygwin
+	 process. output_handle works only in native console apps.
+	 Therefore the data should be written to output_handle_cyg
+	 as well. */
+      fhandler_base *fh = ::cygheap->fdtab[2];
+      if (fh && fh->get_major () == DEV_PTYS_MAJOR)
+	{
+	  fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+	  if (ptys->getPseudoConsole ())
+	    {
+	      HANDLE h_cyg = ptys->get_output_handle_cyg ();
+	      if (buf[len-1] == '\n' && len < NT_MAX_PATH - 1)
+		{
+		  buf[len-1] = '\r';
+		  buf[len] = '\n';
+		  len ++;
+		}
+	      WriteFile (h_cyg, buf, len, &done, 0);
+	      FlushFileBuffers (h_cyg);
+	    }
+	}
+#endif
     }
 
 #ifndef NOSTRACE
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
index ad46cb312..006f07b1a 100644
--- a/winsup/cygwin/tty.cc
+++ b/winsup/cygwin/tty.cc
@@ -234,7 +234,15 @@ tty::init ()
   was_opened = false;
   master_pid = 0;
   is_console = false;
+  attach_pcon_in_fork = false;
+  hPseudoConsole = NULL;
   column = 0;
+  switch_to_pcon = false;
+  screen_alternated = false;
+  mask_switch_to_pcon = false;
+  pcon_pid = 0;
+  num_pcon_attached_slaves = 0;
+  TermCodePage = 65001;
 }
 
 HANDLE
diff --git a/winsup/cygwin/tty.h b/winsup/cygwin/tty.h
index 9aee43b9c..d59b2027d 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,28 @@ 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_fork;
+  bool switch_to_pcon;
+  bool screen_alternated;
+  bool mask_switch_to_pcon;
+  pid_t pcon_pid;
+  int num_pcon_attached_slaves;
+  UINT TermCodePage;
 
 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 +133,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 0e04f4d18..ad451ecf5 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) strtoull (argv[1], &end, 0);
   SetEvent (h);
+  if (argc == 4) /* Pseudo console helper mode for PTY */
+    {
+      HANDLE hPipe = (HANDLE) strtoull (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) strtoull (argv[2], &end, 0);
   WaitForSingleObject (h, INFINITE);
   exit (0);
-- 
2.21.0



More information about the Cygwin-developers mailing list