Skip to content

Commit

Permalink
[interactive] Put shell in its own process group.
Browse files Browse the repository at this point in the history
We still need to do it when creating processes and pipelines, but I seem
to be running into some EPERM errors there.  Let's see if this passes
tests.

- test/group-session: Compare shells, interactive vs. non-interactive
  - It's very clear that OSH is missing setpgid() and tcsetpgrp()!
  • Loading branch information
Andy C committed Feb 5, 2022
1 parent b7ed2ce commit 3f998dc
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 16 deletions.
14 changes: 12 additions & 2 deletions core/pyos.py
Expand Up @@ -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)
Expand All @@ -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."""
Expand Down
2 changes: 1 addition & 1 deletion core/shell.py
Expand Up @@ -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):
Expand Down
12 changes: 7 additions & 5 deletions spec/stateful/harness.py
Expand Up @@ -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)


Expand Down
64 changes: 56 additions & 8 deletions test/group-session.sh
Expand Up @@ -3,15 +3,63 @@
# Test kernel state: the process group and session leader.
#
# Usage:
# ./group-session.sh <function name>
# test/group-session.sh <function name>

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

0 comments on commit 3f998dc

Please sign in to comment.