Overhaul job and terminal control

* Instead of reaping all child processes when we receive a SIGCHLD, try
reaping only processes belonging to process groups from fully-
constructed jobs, which should eliminate the need for the keepalive
process entirely (WSL's lack of zombies not withstanding) as now
completed processes are not reaped until the job has been fully
constructed (i.e.  all processes launched), which means their process
group should still be around for new processes to join.

* When `tcgetpgrp()` calls return 0, attempt to `tcsetpgrp()` before
invoking failure handling code.

* When forking a builtin and not running interactively, do not bail if
unable to set/restore terminal attributes.

Fixes #4178. Fixes #3805. Fixes #5210.
This commit is contained in:
Mahmoud Al-Qudsi
2018-09-28 23:13:13 -05:00
parent 9454397e4c
commit af0c8d51e0
4 changed files with 89 additions and 39 deletions

View File

@@ -1742,8 +1742,23 @@ static void reader_interactive_init() {
// Eventually we just give up and assume we're orphaend.
for (unsigned long loop_count = 0;; loop_count++) {
pid_t owner = tcgetpgrp(STDIN_FILENO);
shell_pgid = getpgrp();
// 0 is a valid return code from `tcgetpgrp()` under at least FreeBSD and testing
// indicates that a subsequent call to `tcsetpgrp()` will succeed. 0 is the
// pid of the top-level kernel process, so I'm not sure if this means ownership
// of the terminal has gone back to the kernel (i.e. it's not owned) or if it is
// just an "invalid" pid for all intents and purposes.
if (owner == 0) {
tcsetpgrp(STDIN_FILENO, shell_pgid);
// Since we expect the above to work, call `tcgetpgrp()` immediately to
// avoid a second pass through this loop.
owner = tcgetpgrp(STDIN_FILENO);
}
if (owner == -1 && errno == ENOTTY) {
if (!is_interactive_session) {
// It's OK if we're not able to take control of the terminal. We handle
// the fallout from this in a few other places.
break;
}
// No TTY, cannot be interactive?
redirect_tty_output();
debug(1, _(L"No TTY for interactive shell (tcgetpgrp failed)"));
@@ -2437,9 +2452,11 @@ const wchar_t *reader_readline(int nchars) {
// Get the current terminal modes. These will be restored when the function returns.
if (tcgetattr(STDIN_FILENO, &old_modes) == -1 && errno == EIO) redirect_tty_output();
// Set the new modes.
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
if (errno == EIO) redirect_tty_output();
wperror(L"tcsetattr");
if (is_interactive_session) {
if (tcsetattr(0, TCSANOW, &shell_modes) == -1) {
if (errno == EIO) redirect_tty_output();
wperror(L"tcsetattr");
}
}
while (!finished && !data->end_loop) {
@@ -3317,7 +3334,9 @@ const wchar_t *reader_readline(int nchars) {
}
if (!reader_exit_forced()) {
if (tcsetattr(0, TCSANOW, &old_modes) == -1) {
// The order of the two conditions below is important. Try to restore the mode
// in all cases, but only complain if interactive.
if (tcsetattr(0, TCSANOW, &old_modes) == -1 && is_interactive_session) {
if (errno == EIO) redirect_tty_output();
wperror(L"tcsetattr"); // return to previous mode
}