diff --git a/core/pyos.py b/core/pyos.py index 215effb3cb..2454dabe56 100644 --- a/core/pyos.py +++ b/core/pyos.py @@ -308,8 +308,8 @@ def InitShell(self): """Always called when initializing the shell process.""" pass - def InitInteractiveShell(self, display): - # type: (_IDisplay) -> None + def InitInteractiveShell(self, display, my_pid): + # type: (_IDisplay, int) -> None """Called when initializing an interactive shell.""" # The shell itself should ignore Ctrl-\. signal.signal(signal.SIGQUIT, signal.SIG_IGN) @@ -331,6 +331,16 @@ def InitInteractiveShell(self, display): self.sigwinch_handler = SigwinchHandler(display, self) signal.signal(signal.SIGWINCH, self.sigwinch_handler) + try: + # Put the interactive shell in its own process group, named by its PID + posix.setpgid(my_pid, my_pid) + # Attach the terminal (stdin) to the progress group + posix.tcsetpgrp(0, my_pid) + + except (IOError, OSError) as e: + # For some reason setpgid() fails with Operation Not Permitted (EPERM) under pexpect? + pass + def AddUserTrap(self, sig_num, handler): # type: (int, Any) -> None """For user-defined handlers registered with the 'trap' builtin.""" diff --git a/core/shell.py b/core/shell.py index 301133c5df..7a57d5ad5e 100644 --- a/core/shell.py +++ b/core/shell.py @@ -630,7 +630,7 @@ def Main(lang, arg_r, environ, login_shell, loader, line_input): else: # Without readline module display = comp_ui.MinimalDisplay(comp_ui_state, prompt_state, debug_f) - sig_state.InitInteractiveShell(display) + sig_state.InitInteractiveShell(display, my_pid) # NOTE: called AFTER _InitDefaultCompletions. with state.ctx_ThisDir(mem, rc_path): diff --git a/spec/stateful/harness.py b/spec/stateful/harness.py index 3b90c0beab..f6cad25b73 100644 --- a/spec/stateful/harness.py +++ b/spec/stateful/harness.py @@ -38,12 +38,14 @@ def send_signal(name, sig_num): os.kill(get_pid_by_name(name), sig_num) -# XXX: osh.sendcontrol("z") does not suspend the foreground process :( -# -# why does osh.sendcontrol("c") generate SIGINT, while osh.sendcontrol("z") -# appears to do nothing? def stop_process__hack(name): - """Send sigstop to the most recent process matching `name`""" + """Send sigstop to the most recent process matching `name` + + Hack in place of sh.sendcontrol('z'), which sends SIGTSTP. Why doesn't OSH + respond to this, or why don't the child processes respond? + + TODO: Fix OSH and get rid of this hack. + """ send_signal(name, signal.SIGSTOP) diff --git a/test/group-session.sh b/test/group-session.sh index 6cc6951614..335869727e 100755 --- a/test/group-session.sh +++ b/test/group-session.sh @@ -3,15 +3,63 @@ # Test kernel state: the process group and session leader. # # Usage: -# ./group-session.sh +# test/group-session.sh -set -o nounset -set -o pipefail -set -o errexit +setup() { + mkdir -p _tmp + ln -s -f -v $(which cat) _tmp/cat2 +} + +show_group_session() { + # by default, it shows processes that use the same terminal + #ps -o pid,ppid,pgid,sid,tname,comm | cat | _tmp/cat2 + + # - bash is the parent of ps, cat, ca2 + # - the PGID is the same as bash? Oh this is for a NON-INTERACTIVE SHELL. + # - TPGID: controlling terminal's notion of foreground pgid? + # - it's always the same number, so it's misleading to associate with a process + # - see APUE section on setpgid(), tcsetgprp() + + if true; then + echo '[foreground pipeline]' + ps -o pid,ppid,pgid,sid,tpgid,comm | cat | _tmp/cat2 + fi + + # Test background pipeline + if false; then + # For interactive shells, the TPGID is different here. + # Foreground: it matches the PGID of 'ps | cat | cat2' + # Background: it matches the PGID of bash! + + echo '[background pipeline]' + ps -o pid,ppid,pgid,sid,tpgid,comm | cat | _tmp/cat2 & + wait + fi + + # Test plain process + if false; then + echo '[single process]' + ps -o pid,ppid,pgid,sid,comm + fi +} + +compare_shells() { + for sh in dash bash mksh zsh bin/osh; do + #for sh in dash bash; do + echo ----- + echo $sh + echo ----- + + echo 'NON-INTERACTIVE' + $sh $0 show_group_session + echo -show() { - # by deafult, it shows processes that use the same terminal - ps -o pid,ppid,pgid,sid,tname,comm + echo INTERACTIVE + $sh -i -c '. $0; show_group_session' $0 + echo + done } -"$@" +if test $(basename $0) = 'group-session.sh'; then + "$@" +fi