]> sourceware.org Git - newlib-cygwin.git/commitdiff
Cygwin: pty: Make apps using console APIs be able to debug with gdb.
authorTakashi Yano via Cygwin-patches <cygwin-patches@cygwin.com>
Thu, 28 Jan 2021 03:26:13 +0000 (12:26 +0900)
committerCorinna Vinschen <corinna@vinschen.de>
Thu, 28 Jan 2021 10:21:12 +0000 (11:21 +0100)
- After commit bb428520, there has been the disadvantage:
  2) The apps which use console API cannot be debugged with gdb. This
     is because pseudo console is not activated since gdb uses
     CreateProcess() rather than exec(). Even with this limitation,
     attaching gdb to native app, in which pseudo console is already
     activated, works.
  This patch clears this disadvantage.

winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_tty.cc
winsup/cygwin/select.cc
winsup/cygwin/spawn.cc
winsup/cygwin/tty.cc
winsup/cygwin/tty.h

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