This is the mail archive of the
cygwin-patches
mailing list for the Cygwin project.
[PATCH 1/1] Cygwin: pty: Fix select() with pseudo console support.
- From: Takashi Yano <takashi dot yano at nifty dot ne dot jp>
- To: cygwin-patches at cygwin dot com
- Cc: Takashi Yano <takashi dot yano at nifty dot ne dot jp>
- Date: Thu, 5 Sep 2019 13:22:54 +0900
- Subject: [PATCH 1/1] Cygwin: pty: Fix select() with pseudo console support.
- Dkim-filter: OpenDKIM Filter v2.10.3 conuserg-02.nifty.com x854N0rE007571
- References: <20190905042254.1954-1-takashi.yano@nifty.ne.jp>
- select() did not work correctly when both read and except are
polled simultaneously for the same fd and the r/w pipe is switched
to pseudo console side. This patch fixes this isseu.
---
winsup/cygwin/fhandler.h | 15 +++
winsup/cygwin/fhandler_tty.cc | 13 ++-
winsup/cygwin/select.cc | 192 ++++++++++++++++++++++++++++++++--
winsup/cygwin/select.h | 2 +
4 files changed, 207 insertions(+), 15 deletions(-)
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index e8c165100..e72e11f7a 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -2102,6 +2102,7 @@ class fhandler_pty_common: public fhandler_termios
{
return get_ttyp ()->hPseudoConsole;
}
+ bool to_be_read_from_pcon (void);
protected:
BOOL process_opost_output (HANDLE h,
@@ -2150,6 +2151,8 @@ class fhandler_pty_slave: public fhandler_pty_common
void fixup_after_exec ();
select_record *select_read (select_stuff *);
+ select_record *select_write (select_stuff *);
+ select_record *select_except (select_stuff *);
virtual char const *ttyname () { return pc.dev.name (); }
int __reg2 fstat (struct stat *buf);
int __reg3 facl (int, int, struct acl *);
@@ -2177,9 +2180,21 @@ class fhandler_pty_slave: public fhandler_pty_common
void push_to_pcon_screenbuffer (const char *ptr, size_t len);
void mask_switch_to_pcon (bool mask)
{
+ if (!mask && get_ttyp ()->pcon_pid &&
+ get_ttyp ()->pcon_pid != myself->pid &&
+ kill (get_ttyp ()->pcon_pid, 0) == 0)
+ return;
get_ttyp ()->mask_switch_to_pcon = mask;
}
void fixup_after_attach (bool native_maybe);
+ pid_t get_pcon_pid (void)
+ {
+ return get_ttyp ()->pcon_pid;
+ }
+ bool is_line_input (void)
+ {
+ return get_ttyp ()->ti.c_lflag & ICANON;
+ }
};
#define __ptsname(buf, unit) __small_sprintf ((buf), "/dev/pty%d", (unit))
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
index a6844832b..78c9c9128 100644
--- a/winsup/cygwin/fhandler_tty.cc
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -1223,6 +1223,13 @@ fhandler_pty_slave::write (const void *ptr, size_t len)
return towrite;
}
+bool
+fhandler_pty_common::to_be_read_from_pcon (void)
+{
+ return get_ttyp ()->switch_to_pcon &&
+ (!get_ttyp ()->mask_switch_to_pcon || ALWAYS_USE_PCON);
+}
+
void __reg3
fhandler_pty_slave::read (void *ptr, size_t& len)
{
@@ -1351,8 +1358,7 @@ fhandler_pty_slave::read (void *ptr, size_t& len)
}
goto out;
}
- if (get_ttyp ()->switch_to_pcon &&
- (!get_ttyp ()->mask_switch_to_pcon || ALWAYS_USE_PCON))
+ if (to_be_read_from_pcon ())
{
if (!try_reattach_pcon ())
{
@@ -2129,8 +2135,7 @@ fhandler_pty_master::write (const void *ptr, size_t len)
/* Write terminal input to to_slave pipe instead of output_handle
if current application is native console application. */
- if (get_ttyp ()->switch_to_pcon &&
- (!get_ttyp ()->mask_switch_to_pcon || ALWAYS_USE_PCON))
+ if (to_be_read_from_pcon ())
{
char *buf;
size_t nlen;
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
index d29f3d2f4..4efc302df 100644
--- a/winsup/cygwin/select.cc
+++ b/winsup/cygwin/select.cc
@@ -667,9 +667,6 @@ 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 ())
{
@@ -713,6 +710,7 @@ peek_pipe (select_record *s, bool from_select)
}
out:
+ h = fh->get_output_handle_cyg ();
if (s->write_selected && dev != FH_PIPER)
{
gotone += s->write_ready = pipe_data_available (s->fd, fh, h, true);
@@ -1176,33 +1174,173 @@ static int
verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds)
{
- if (IsEventSignalled (me->h))
+ fhandler_pty_slave *ptys = (fhandler_pty_slave *) me->fh;
+ if (me->read_selected && !ptys->to_be_read_from_pcon () &&
+ IsEventSignalled (ptys->input_available_event))
me->read_ready = true;
return set_bits (me, readfds, writefds, exceptfds);
}
static int
-pty_slave_startup (select_record *s, select_stuff *)
+peek_pty_slave (select_record *s, bool from_select)
{
+ int gotone = 0;
fhandler_base *fh = (fhandler_base *) s->fh;
- ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (true);
+ fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+
+ ptys->reset_switch_to_pcon ();
+
+ if (s->read_selected)
+ {
+ if (s->read_ready)
+ {
+ select_printf ("%s, already ready for read", fh->get_name ());
+ gotone = 1;
+ goto out;
+ }
+
+ if (fh->bg_check (SIGTTIN, true) <= bg_eof)
+ {
+ gotone = s->read_ready = true;
+ goto out;
+ }
+
+ if (ptys->to_be_read_from_pcon ())
+ {
+ if (ptys->is_line_input ())
+ {
+#define INREC_SIZE (65536 / sizeof (INPUT_RECORD))
+ INPUT_RECORD inp[INREC_SIZE];
+ DWORD n;
+ PeekConsoleInput (ptys->get_handle (), inp, INREC_SIZE, &n);
+ bool end_of_line = false;
+ while (n-- > 0)
+ if (inp[n].EventType == KEY_EVENT &&
+ inp[n].Event.KeyEvent.bKeyDown &&
+ inp[n].Event.KeyEvent.uChar.AsciiChar == '\r')
+ end_of_line = true;
+ if (end_of_line)
+ {
+ gotone = s->read_ready = true;
+ goto out;
+ }
+ else
+ goto out;
+ }
+ }
+
+ if (IsEventSignalled (ptys->input_available_event))
+ {
+ gotone = s->read_ready = true;
+ goto out;
+ }
+
+ if (!gotone && s->fh->hit_eof ())
+ {
+ select_printf ("read: %s, saw EOF", fh->get_name ());
+ if (s->except_selected)
+ gotone += s->except_ready = true;
+ if (s->read_selected)
+ gotone += s->read_ready = true;
+ }
+ }
+
+out:
+ HANDLE h = ptys->get_output_handle_cyg ();
+ if (s->write_selected)
+ {
+ gotone += s->write_ready = pipe_data_available (s->fd, fh, h, true);
+ select_printf ("write: %s, gotone %d", fh->get_name (), gotone);
+ }
+ return gotone;
+}
+
+static int pty_slave_startup (select_record *me, select_stuff *stuff);
+
+static DWORD WINAPI
+thread_pty_slave (void *arg)
+{
+ select_pipe_info *pi = (select_pipe_info *) arg;
+ DWORD sleep_time = 0;
+ bool looping = true;
+
+ while (looping)
+ {
+ for (select_record *s = pi->start; (s = s->next); )
+ if (s->startup == pty_slave_startup)
+ {
+ if (peek_pty_slave (s, true))
+ looping = false;
+ if (pi->stop_thread)
+ {
+ select_printf ("stopping");
+ looping = false;
+ break;
+ }
+ }
+ if (!looping)
+ break;
+ Sleep (sleep_time >> 3);
+ if (sleep_time < 80)
+ ++sleep_time;
+ if (pi->stop_thread)
+ break;
+ }
+ return 0;
+}
+
+static int
+pty_slave_startup (select_record *me, select_stuff *stuff)
+{
+ fhandler_base *fh = (fhandler_base *) me->fh;
+ fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+ if (me->read_selected && ptys->get_pcon_pid () != myself->pid)
+ ptys->mask_switch_to_pcon (true);
+
+ select_pipe_info *pi = stuff->device_specific_ptys;
+ if (pi->start)
+ me->h = *((select_pipe_info *) stuff->device_specific_ptys)->thread;
+ else
+ {
+ pi->start = &stuff->start;
+ pi->stop_thread = false;
+ pi->thread = new cygthread (thread_pty_slave, pi, "ptyssel");
+ me->h = *pi->thread;
+ if (!me->h)
+ return 0;
+ }
return 1;
}
static void
-pty_slave_cleanup (select_record *s, select_stuff *)
+pty_slave_cleanup (select_record *me, select_stuff *stuff)
{
- fhandler_base *fh = (fhandler_base *) s->fh;
- ((fhandler_pty_slave *) fh)->mask_switch_to_pcon (false);
+ fhandler_base *fh = (fhandler_base *) me->fh;
+ fhandler_pty_slave *ptys = (fhandler_pty_slave *) fh;
+ if (me->read_selected)
+ ptys->mask_switch_to_pcon (false);
+
+ select_pipe_info *pi = (select_pipe_info *) stuff->device_specific_ptys;
+ if (!pi)
+ return;
+ if (pi->thread)
+ {
+ pi->stop_thread = true;
+ pi->thread->detach ();
+ }
+ delete pi;
+ stuff->device_specific_ptys = NULL;
}
select_record *
fhandler_pty_slave::select_read (select_stuff *ss)
{
+ if (!ss->device_specific_ptys
+ && (ss->device_specific_ptys = new select_pipe_info) == NULL)
+ return NULL;
select_record *s = ss->start.next;
- s->h = input_available_event;
s->startup = pty_slave_startup;
- s->peek = peek_pipe;
+ s->peek = peek_pty_slave;
s->verify = verify_tty_slave;
s->read_selected = true;
s->read_ready = false;
@@ -1210,6 +1348,38 @@ fhandler_pty_slave::select_read (select_stuff *ss)
return s;
}
+select_record *
+fhandler_pty_slave::select_write (select_stuff *ss)
+{
+ if (!ss->device_specific_ptys
+ && (ss->device_specific_ptys = new select_pipe_info) == NULL)
+ return NULL;
+ select_record *s = ss->start.next;
+ s->startup = pty_slave_startup;
+ s->peek = peek_pty_slave;
+ s->verify = verify_tty_slave;
+ s->write_selected = true;
+ s->write_ready = false;
+ s->cleanup = pty_slave_cleanup;
+ return s;
+}
+
+select_record *
+fhandler_pty_slave::select_except (select_stuff *ss)
+{
+ if (!ss->device_specific_ptys
+ && (ss->device_specific_ptys = new select_pipe_info) == NULL)
+ return NULL;
+ select_record *s = ss->start.next;
+ s->startup = pty_slave_startup;
+ s->peek = peek_pty_slave;
+ s->verify = verify_tty_slave;
+ s->except_selected = true;
+ s->except_ready = false;
+ s->cleanup = pty_slave_cleanup;
+ return s;
+}
+
select_record *
fhandler_dev_null::select_read (select_stuff *ss)
{
diff --git a/winsup/cygwin/select.h b/winsup/cygwin/select.h
index 7d6dee753..ae98c658d 100644
--- a/winsup/cygwin/select.h
+++ b/winsup/cygwin/select.h
@@ -88,6 +88,7 @@ public:
select_record start;
select_pipe_info *device_specific_pipe;
+ select_pipe_info *device_specific_ptys;
select_fifo_info *device_specific_fifo;
select_socket_info *device_specific_socket;
select_serial_info *device_specific_serial;
@@ -101,6 +102,7 @@ public:
select_stuff (): return_on_signal (false), always_ready (false),
windows_used (false), start (),
device_specific_pipe (NULL),
+ device_specific_ptys (NULL),
device_specific_fifo (NULL),
device_specific_socket (NULL),
device_specific_serial (NULL)
--
2.21.0