1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 11:42:21 +00:00

Fix BUG_PUTIOERR: Check for and report I/O error in output builtins

This allows scripts to check for a nonzero exit status on the
'print', 'printf' and 'echo' builtins and prevent possible infinite
loops if SIGPIPE is ignored.

sfsync() was already returning a negative value on I/O error, so
all we need to do is add a check. The stream buffer will need to be
filled before an I/O error can be detected, but this is the same on
other shells. See manual page: src/lib/libast/man/sfio.3

Ref.: https://github.com/att/ast/issues/1093
      https://github.com/att/ast/pull/1363

src/cmd/ksh93/bltins/print.c: b_print():
- Make sure an error result from sfsync() is reflected in the
  output builtin's exit status (exitval).
- Write an I/O error message (e_io) if the exit status is nonzero.

src/cmd/ksh93/data/msg.c, src/cmd/ksh93/include/io.h:
- Add the e_io[] error message.

src/cmd/ksh93/tests/builtins.sh:
- Add I/O error regression test, checking for the correct error
  message and exit status. All three output builtins use the same
  b_print() function so we only need to test one.

src/cmd/ksh93/tests/basic.sh,
src/cmd/ksh93/tests/coprocess.sh:
- Redirect stderr on a few 'print' commands to /dev/null; these
  now issue an expected I/O error. This does not cause failures.

NEWS, TODO:
- Update.

(cherry picked from commit 9011fa933552e483dab460f7dd1593d64e059d94)
This commit is contained in:
Martijn Dekker 2020-05-16 16:04:35 +02:00
parent 846ad93272
commit 93e15a3035
8 changed files with 45 additions and 13 deletions

5
NEWS
View file

@ -12,6 +12,11 @@ Any uppercase BUG_* names are modernish shell bug IDs.
Ref.: https://github.com/att/ast/issues/425 Ref.: https://github.com/att/ast/issues/425
https://github.com/att/ast/pull/442 https://github.com/att/ast/pull/442
- Fix BUG_PUTIOERR: Output builtins now correctly detect and report
input/output errors. This allows scripts to check for a nonzero exit
status on the 'print', 'printf' and 'echo' builtins and prevent possible
infinite loops if SIGPIPE is ignored.
2020-05-13: 2020-05-13:
- Fix BUG_CASELIT: an undocumented 'case' pattern matching misbehaviour that - Fix BUG_CASELIT: an undocumented 'case' pattern matching misbehaviour that

5
TODO
View file

@ -92,10 +92,5 @@ https://github.com/modernish/modernish/tree/0.16/lib/modernish/cap/
example, "$*" joins positional parameters on the first byte of IFS instead example, "$*" joins positional parameters on the first byte of IFS instead
of the first character. of the first character.
- BUG_PUTIOERR: Shell builtins that output strings (echo, printf, ksh/zsh
print), and thus also modernish put and putln, do not check for I/O errors
on output. This means a script cannot check for them, and a script process
in a pipe can get stuck in an infinite loop if SIGPIPE is ignored.
- BUG_TESTERR1A: test/[ exits with a non-error false status (1) if an - BUG_TESTERR1A: test/[ exits with a non-error false status (1) if an
invalid argument is given to an operator. invalid argument is given to an operator.

View file

@ -336,9 +336,11 @@ skip2:
* https://github.com/att/ast/issues/425 * https://github.com/att/ast/issues/425
*/ */
if(!sflag && sffileno(outfile)!=sffileno(sfstderr)) if(!sflag && sffileno(outfile)!=sffileno(sfstderr))
sfsync(outfile); if (sfsync(outfile) < 0)
exitval = 1;
sfpool(sfstderr,pool,SF_WRITE); sfpool(sfstderr,pool,SF_WRITE);
exitval = pdata.err; if (pdata.err)
exitval = 1;
} }
else if(vflag) else if(vflag)
{ {
@ -353,7 +355,10 @@ skip2:
{ {
/* echo style print */ /* echo style print */
if(nflag && !argv[0]) if(nflag && !argv[0])
sfsync((Sfio_t*)0); {
if (sfsync((Sfio_t*)0) < 0)
exitval = 1;
}
else if(sh_echolist(shp,outfile,rflag,argv) && !nflag) else if(sh_echolist(shp,outfile,rflag,argv) && !nflag)
sfputc(outfile,'\n'); sfputc(outfile,'\n');
} }
@ -365,8 +370,11 @@ skip2:
else if(n&SF_SHARE) else if(n&SF_SHARE)
{ {
sfset(outfile,SF_SHARE|SF_PUBLIC,1); sfset(outfile,SF_SHARE|SF_PUBLIC,1);
sfsync(outfile); if (sfsync(outfile) < 0)
exitval = 1;
} }
if (exitval)
errormsg(SH_DICT, 2, e_io);
return(exitval); return(exitval);
} }

View file

@ -89,6 +89,7 @@ const char e_access[] = "permission denied";
const char e_direct[] = "bad directory"; const char e_direct[] = "bad directory";
const char e_file[] = "%s: bad file unit number"; const char e_file[] = "%s: bad file unit number";
const char e_redirect[] = "redirection failed"; const char e_redirect[] = "redirection failed";
const char e_io[] = "I/O error";
const char e_trap[] = "%s: bad trap"; const char e_trap[] = "%s: bad trap";
const char e_readonly[] = "%s: is read only"; const char e_readonly[] = "%s: is read only";
const char e_badfield[] = "%d: negative field size"; const char e_badfield[] = "%d: negative field size";

View file

@ -97,6 +97,7 @@ extern const char e_tmpcreate[];
extern const char e_exists[]; extern const char e_exists[];
extern const char e_file[]; extern const char e_file[];
extern const char e_redirect[]; extern const char e_redirect[];
extern const char e_io[];
extern const char e_formspec[]; extern const char e_formspec[];
extern const char e_badregexp[]; extern const char e_badregexp[];
extern const char e_open[]; extern const char e_open[];

View file

@ -431,7 +431,7 @@ done | while read sec; do ( $sleep $sec; $sleep $sec) done
s=SECONDS s=SECONDS
set -o pipefail set -o pipefail
for ((i=0; i < 30; i++)) for ((i=0; i < 30; i++))
do print hello do print hello 2>/dev/null
sleep .1 sleep .1
done | $sleep 1 done | $sleep 1
(( (SECONDS-s) < 2 )) || err_exit 'early termination not causing broken pipe' (( (SECONDS-s) < 2 )) || err_exit 'early termination not causing broken pipe'
@ -470,7 +470,7 @@ bintrue=$(whence -p true)
set -o pipefail set -o pipefail
float start=$SECONDS end float start=$SECONDS end
for ((i=0; i < 2; i++)) for ((i=0; i < 2; i++))
do print foo do print foo 2>/dev/null
sleep 1.5 sleep 1.5
done | { read; $bintrue; end=$SECONDS ;} done | { read; $bintrue; end=$SECONDS ;}
(( (SECONDS-start) < 1 )) && err_exit "pipefail not waiting for pipe to finish" (( (SECONDS-start) < 1 )) && err_exit "pipefail not waiting for pipe to finish"

View file

@ -639,4 +639,26 @@ read baz <<< 'foo\\\\bar'
: ~root : ~root
[[ $(builtin) == *.sh.tilde* ]] && err_exit 'builtin contains .sh.tilde' [[ $(builtin) == *.sh.tilde* ]] && err_exit 'builtin contains .sh.tilde'
# ======
# Check that I/O errors are detected <https://github.com/att/ast/issues/1093>
actual=$(
{
(
trap "" PIPE
for ((i = SECONDS + 1; SECONDS < i; )); do
print hi || {
print $? >&2
exit
}
done
) | true
} 2>&1
)
expect=$': print: I/O error\n1'
if [[ $actual != *"$expect" ]]
then
err_exit "I/O error not detected (expected '$expect', got '$actual')"
fi
# ======
exit $((Errors<125?Errors:125)) exit $((Errors<125?Errors:125))

View file

@ -266,7 +266,7 @@ _COSHELL_msgfd=5
function cop function cop
{ {
read read
print ok print ok 2>/dev/null
} }
exp=ok exp=ok
@ -342,7 +342,7 @@ wait
function cop function cop
{ {
read read
print ok print ok 2>/dev/null
} }
exp=ok exp=ok
cop |& cop |&