Skip to content

Commit

Permalink
Pick an unused fd to capture subshell output
Browse files Browse the repository at this point in the history
  • Loading branch information
citrus-it committed Jan 8, 2021
1 parent a62cc24 commit d017817
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/cmd/ksh93/include/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ extern void sh_iorestore(Shell_t*,int,int);
extern Sfio_t *sh_iostream(Shell_t*,int);
extern int sh_redirect(Shell_t*,struct ionod*,int);
extern void sh_iosave(Shell_t *, int,int,char*);
extern int sh_get_unused_fd(Shell_t* shp, int min_fd);
extern int sh_iovalidfd(Shell_t*, int);
extern int sh_inuse(Shell_t*, int);
extern void sh_iounsave(Shell_t*);
Expand Down
18 changes: 18 additions & 0 deletions src/cmd/ksh93/sh/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -2764,3 +2764,21 @@ int sh_chdir(const char* dir)
return(r);
}

// Return the lowest numbered fd that is equal to or greater than the requested
// `min_fd` and which is not currently in use.
int sh_get_unused_fd(Shell_t* shp, int min_fd) {
int fd;

while (1) {
if (fcntl(min_fd, F_GETFD) == -1) {
for(fd = 0; fd < shp->topfd; fd++) {
if (filemap[fd].save_fd == min_fd || filemap[fd].orig_fd == min_fd) break;
}
if (fd == shp->topfd) break;
}
min_fd++;
}

return min_fd;
}

2 changes: 1 addition & 1 deletion src/cmd/ksh93/sh/subshell.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ Sfio_t *sh_subshell(Shell_t *shp,Shnode_t *t, volatile int flags, int comsub)
}
if(iop && sffileno(iop)==1)
{
int fd=sfsetfd(iop,3);
int fd=sfsetfd(iop,sh_get_unused_fd(shp, 3));
if(fd<0)
{
shp->toomany = 1;
Expand Down
49 changes: 49 additions & 0 deletions src/cmd/ksh93/tests/subshell.sh
Original file line number Diff line number Diff line change
Expand Up @@ -617,4 +617,53 @@ do if [[ -e $f ]]
fi
done

# ========================================
# Test that closing file descriptors don't affect capturing the output of a
# subshell. Regression test for issue #198.
tmpfile=$(mktemp)
expected='return value'

function get_value {
case=$1
(( case >= 1 )) && exec 3< $tmpfile
(( case >= 2 )) && exec 4< $tmpfile
(( case >= 3 )) && exec 6< $tmpfile

# To trigger the bug we have to spawn an external command. Why is a
# mystery but not really relevant.
$(whence -p true)

(( case >= 1 )) && exec 3<&-
(( case >= 2 )) && exec 4<&-
(( case >= 3 )) && exec 6<&-

print $expected
}

actual=$(get_value 0)
if [[ $actual != $expected ]]
then
err_exit -u2 "failed to capture subshell output when closing fd: case 0"
fi

actual=$(get_value 1)
if [[ $actual != $expected ]]
then
err_exit -u2 "failed to capture subshell output when closing fd: case 1"
fi

actual=$(get_value 2)
if [[ $actual != $expected ]]
then
err_exit -u2 "failed to capture subshell output when closing fd: case 2"
fi

actual=$(get_value 3)
if [[ $actual != $expected ]]
then
err_exit -u2 "failed to capture subshell output when closing fd: case 3"
fi

rm $tmpfile

exit $((Errors<125?Errors:125))

0 comments on commit d017817

Please sign in to comment.