From c1986c4e1a42bf26230f1138b74ef7c7bc7f173b Mon Sep 17 00:00:00 2001 From: Johnothan King Date: Fri, 5 Mar 2021 22:43:38 -0800 Subject: [PATCH] Fix Ctrl+D after ksh receives SIGWINCH (#208) src/cmd/ksh93/edit/edit.c: ed_read(): - The loop that handles SIGWINCH assumes sfpkrd will return and set errno to EINTR if ksh is sent SIGWINCH. This only occurs when select(2) is used to wait for input, so tell sfpkrd to use select if possible. This is only done if the last argument given to sfpkrd is '2', which should avoid regressions. src/lib/libast/sfio/sfpkrd.c: sfpkrd(): - Always use select if the last argument is 2. This allows sfpkrd() to intercept SIGWINCH when necessary. Fixes: https://github.com/ksh93/ksh/issues/202 --- NEWS | 3 +++ src/cmd/ksh93/edit/edit.c | 10 +++++----- src/lib/libast/sfio/sfpkrd.c | 12 +++++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 5097a7eea..3010c825d 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,9 @@ Any uppercase BUG_* names are modernish shell bug IDs. did not perform an assignment and yielded the value 0 if 'var' was typeset as numeric (integer or float) but had not yet been assigned a value. +- Fixed a bug introduced on 2020-08-19: Ctrl+D would break after an + interactive shell received SIGWINCH. + 2021-03-05: - Unbalanced quotes and backticks now correctly produce a syntax error diff --git a/src/cmd/ksh93/edit/edit.c b/src/cmd/ksh93/edit/edit.c index a890e56af..db00165b5 100644 --- a/src/cmd/ksh93/edit/edit.c +++ b/src/cmd/ksh93/edit/edit.c @@ -833,15 +833,14 @@ static void ed_nputchar(register Edit_t *ep, int n, int c) /* * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set - * Use sfpkrd() to poll() or select() to wait for input if possible + * Use select(2) (via sfpkrd()) to wait for input if possible * * The return value is the number of bytes read, or < 0 for EOF. * * Unfortunately, systems that get interrupted from slow reads update * this access time for the terminal (in violation of POSIX). * The fixtime() macro resets the time to the time at entry in - * this case. This is not necessary for systems that can handle - * sfpkrd() correctly (i.e., those that support poll() or select()). + * this case. This is not necessary for systems that have select(). */ int ed_read(void *context, int fd, char *buff, int size, int reedit) { @@ -851,11 +850,12 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit) Shell_t *shp = ep->sh; int mode = -1; int (*waitevent)(int,long,int) = shp->gd->waitevent; + /* sfpkrd must use select(2) to intercept SIGWINCH for ed_read */ if(ep->e_raw==ALTMODE) - mode = 1; + mode = 2; if(size < 0) { - mode = 1; + mode = 2; size = -size; } sh_onstate(SH_TTYWAIT); diff --git a/src/lib/libast/sfio/sfpkrd.c b/src/lib/libast/sfio/sfpkrd.c index 91f56974c..1295e5cd9 100644 --- a/src/lib/libast/sfio/sfpkrd.c +++ b/src/lib/libast/sfio/sfpkrd.c @@ -24,11 +24,12 @@ /* * The preferred method is POSIX recv(2) with MSG_PEEK, which is detected as 'socket_peek'. * On Solaris/Illumos (__sun), _stream_peek and _lib_select are needed, as _socket_peek doesn't work correctly. - * On at least macOS and Linux, sfpkrd() runs significantly faster if we disable these. + * On at least macOS and Linux, sfpkrd() runs significantly faster if we disable these. However, + * ed_read() still needs to use select to intercept SIGWINCH, so if the last argument given + # to sfpkrd is '2' select is always used when available. */ #if _socket_peek && !__sun #undef _stream_peek -#undef _lib_select #endif #if __APPLE__ && !_socket_peek @@ -55,6 +56,7 @@ long tm; /* time-out */ int action; /* >0: peeking, if rc>=0, get action records, <0: no peeking, if rc>=0, get -action records, =0: no peeking, if rc>=0, must get a single record + =2: same as >0, but always use select(2) */ #endif { @@ -118,7 +120,11 @@ int action; /* >0: peeking, if rc>=0, get action records, (t&SOCKET_PEEK) ) { r = -2; #if _lib_select - if(r == -2) + if(r == -2 +#if !__sun /* select(2) is always used on Solaris or if action == 2 on other OSes */ + && action == 2 +#endif + ) { fd_set rd; struct timeval tmb, *tmp; FD_ZERO(&rd);