Enhance QEMU interactive shell and exit flow#383
Conversation
The legacy QEMU shell does not support job control. It makes related commnand disabled, such as Ctrl-C/Ctrl-Z/fg. I use setsid and cttyhack to launch a new session with ttyS0. The job control commands are enabled after modification. In addition, I also found that kernel panic takes place when we exit the QEMU shell. The root cause of panic is init process killed. I have added command 'poweroff -f' in this commit. It will be executed when QEMU shell exit by shell command 'exit' or Ctrl-D. The command can correctly terminate QEMU shell without kernel panic after modification.
| # Launch interactive shell with designated PS1 format | ||
| export PS1='lkmpg:\w# ' | ||
| exec /bin/sh | ||
| setsid /bin/cttyhack /bin/sh |
There was a problem hiding this comment.
Conclusion (Before detailed description)
-
Since
cttyhackwill search active tty device automatically, we can remove redundant command:exec 0</dev/ttyS0 ; exec 1>/dev/ttyS0 ; exec 2>&1- Please refer to commit cfb85b3
-
An alternative solution to assign tty device for new session without
cttyhackis:setsid -c /bin/sh </dev/ttyS0 >/dev/ttyS0 2>&1.
-
Please feel free to discuss on better solution
Recap script init
After confimation, I have found that job control is turned off due to constraint of script
init.
This situation caused Ctrl-C / Ctrl-Z unavailable in legacy implementation.
-
Regard to the description in PR above, the constraint here is no any tty device is related to current session.
-
When user clicked Ctrl-C on keyboard, the tty driver will detect Ctrl-C clicked event from tty device.
-
When detected, tty driver sends SIGINT to all processes under foreground process group of session.
- Call Stack Tracing: n_tty_receive_signal_char(tty, SIGINT, c) -> isig() -> __isig() -> kill_pgrp(tty_pgrp, sig, 1);
-
In conclusion, a session related to a tty device is mandatory for sending SIGINT by Ctrl-C.
- Otherwise, the process under the session cannot receive SIGINT via clicking Ctrl-C.
About the command setsid /bin/cttyhack /bin/sh
-
setsid aims to run up a program in a new session. (Busybox Implementation Reference).
- In
init(pid == 1) , the script will call fork() to create a child process for executingsetsid /bin/cttyhack /bin/sh. - Meanwhile, script
initis blocked here as a daemon process until child process returned.
Hence, kernel panic will not be triggered here becauseinitis still alive. - The
setsidwill set child process (pid != 1) to process group leader, which represent a brand new session.
Then execute/bin/cttyhack /bin/shby execvp().
- In
-
The /bin/cttyhack is an user program provided by busybox.
-
Since the new session is not related to any tty device, we use
cttyhackto automatically find active tty device for current session. -
The
cttyhackwill search/sys/class/tty/console/activeto figure out active tty devices.
If sysfs is not mounted,cttyhackwill useioctl()to lookup tty devices.
Ifioctl()failed, use default tty as final tty device. (e.g./dev/tty0) -
If there are additional program attached in argv (e.g.
/bin/shin this command) of cttyhack,
cttyhackwill open target tty device and redirectstdin,stdoutandstderrto target tty device withdup2().
It also closes unnecessary fd and steals tty device from other shell (if any). -
Finally,
cttyhackuse execve() to launch designated program (i.e./bin/shin this command).
-
-
/bin/sh runs up the interactive shell with job control enabled.
-
This command refers to
cttyhackmanual:- Refer to 3rd command - Starting interactive shell from boot shell script
lkmpg:~# cttyhack --help
BusyBox v1.37.0 (2026-07-05 18:07:17 CST) multi-call binary.
Usage: cttyhack [PROG ARGS]
Give PROG a controlling tty if possible.
Example for /etc/inittab (for busybox init):
::respawn:/bin/cttyhack /bin/sh
Giving controlling tty to shell running with PID 1:
$ exec cttyhack sh
Starting interactive shell from boot shell script:
setsid cttyhack sh
lkmpg:~#
Recap QEMU configuration
- In
devtools/boot.sh, we have assigned active console in kernel command line.
Hence, we can obtain active tty via reading/sys/class/tty/console/active.
# Kernel command line
KCMD="console=ttyS0 loglevel=7 nokaslr"
if [ -n "$TEST_CMD" ]; then
# Base64-encode the command to survive kernel cmdline word-splitting.
# Strip newlines portably so this works with both GNU and BSD base64.
# The init script decodes it before execution.
KCMD="$KCMD lkmpg.cmd64=$(printf '%s' "$TEST_CMD" | base64_nowrap)"
fi
- Read
/sys/class/tty/console/activein QEMU guest shell:
lkmpg:~# cat /sys/class/tty/console/active
ttyS0Other reference:
1. Remove redundant tty device setting 2. Add description nearby command for launching shell
Enable Job Control in QEMU Interactive Shell
In LKMPG chapter 27, I was suffering on testing module
blkramunder QEMU interactive shell.When I execute
cat /dev/blkram0, I found that I cannot use Ctrl-C / Ctrl-Z to exit the command.The QEMU shell hung here unless using Ctrl-A and X to terminate QEMU.
After confimation, I have found that job control is turned off due to constraint of script
init.This situation caused Ctrl-C / Ctrl-Z unavailable in legacy implementation.
In proposed solution, I start a new session with setsid and get rid of impact by script
init.Then, we set current controlling terminal to ttyS0 and use cttyhack to run up /bin/sh.
The job control of shell is enabled under current implementation.
Exit flow enhancement
During tesing on QEMU shell, I found that kernel panic is taken place when we input exit.
Under this situation, we should use Ctrl-A and X to terminate the QEMU.
Refer to the output log, the root cause is trying to kill
initprocess.Refer to ./devtools/test-modules.sh, it used
poweroff -fto exit QEMU shell without kernel panic.Hence, I also imported this method into our init script. After testing, the QEMU shell can exit without
kernel panic.
Summary by cubic
Enables job control in the QEMU guest shell and makes exit clean to avoid kernel panic. Also removes a redundant TTY setup in
init.New Features
setsid /bin/cttyhack /bin/shso Ctrl-C/Ctrl-Z/fg work.PS1='lkmpg:\w# ', and the boot banner now documents “Exit: exit (or Ctrl-D). Will execute poweroff -f”.Bug Fixes
poweroff -fso QEMU powers down instead of panicking onexit/Ctrl-D`.Written for commit cfb85b3. Summary will update on new commits.