diff --git a/.spdxignore b/.spdxignore index bb91ca940a..f3e8637e2b 100644 --- a/.spdxignore +++ b/.spdxignore @@ -14,3 +14,4 @@ pkg/kube/descheduler-job.yaml pkg/kube/descheduler_rbac.yaml pkg/kube/lh-cfg-v1.6.2.yaml pkg/vtpm/swtpm-vtpm/vendor/ +pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh diff --git a/.yetus/blanks-tabs.txt b/.yetus/blanks-tabs.txt index c2bcfc31b7..2776a3482b 100644 --- a/.yetus/blanks-tabs.txt +++ b/.yetus/blanks-tabs.txt @@ -3,3 +3,4 @@ .*\.dts .*\.md ^.codespellignorelines +pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh diff --git a/.yetus/excludes.txt b/.yetus/excludes.txt index 3532d23367..4313895c00 100644 --- a/.yetus/excludes.txt +++ b/.yetus/excludes.txt @@ -20,3 +20,4 @@ ^.+/testdata/.+ ^.codespellignorelines eve-tools/bpftrace-compiler/examples/.+\.bt +pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh diff --git a/docs/LOCAL-TUI.md b/docs/LOCAL-TUI.md index f2f5867e31..873657e58c 100644 --- a/docs/LOCAL-TUI.md +++ b/docs/LOCAL-TUI.md @@ -12,13 +12,17 @@ The client communicates with the server over UNIX socket The UI is rendered on a local TTY (/dev/tty2) only i.e. on a physical monitor attached to the system. Neither Serial Console nor SSH connection has access to TUI. It is done to ensure the physical presence of the operator. -## /dev/ttyX vs /dev/console +## /dev/ttyX vs /dev/console vs /dev/tty0 -There are two distinguishable console devices in Linux `/dev/console` and `/dev/ttyX`. The later points to a particular virtual terminal device and the former points to *currently active* TTY device. The user can switch between virtual terminals by using `Alt+Fx` or `Alt+<,>` keys. When the current TTY is set `/dev/console` tracks this change and always points to to the current terminal +There are three distinguishable console devices in Linux `/dev/console`, `/dev/tty0` and `/dev/ttyX` where X > 0. The latter points to a particular virtual terminal device i.e. a dedicated framebuffer for VGA console. `/dev/tty0` points to *currently active* TTY device. This can be proven by reading `/sys/devices/virtual/tty/tty0/active` file. This file exists only for `/dev/tty0`. On the other hand `/dev/console` may point to several devices at a time. These are devices user specifies in `console=` kernel command line parameters. The list can be obtained by reading `/sys/devices/virtual/tty/console/active` file. -Monitor application is spawned on a `/dev/tty2` using a `openvt` utility while the rest of the applications are spawned on the default kernel console defined by `console=` parameters on the kernel command line. When the application is in focus (`/dev/tty2` is an active console) writing to `/dev/console` which points to the same device corrupts TUI thus it cannot be used by other services in the system to produce the output. At least when `/dev/tty2` is a current console. +The user can switch between virtual terminals by using `Alt+Fx` or `Alt+<,>` keys. When the current TTY is set `/dev/tty0` tracks this change and always points to to the current terminal -From the other hand `/dev/tty` (no digit at the end!) device always points to a TTY *in the context of running process*. This device can be used instead of `/dev/console` by other services for the output. +Monitor application is spawned on a `/dev/tty2` using a `openvt` utility while the rest of the applications are spawned on the default kernel console defined by `console=` parameters on the kernel command line. When the application is in focus (`/dev/tty2` is an active console) writing to `/dev/console` or to `/dev/tty0` which points to the same device corrupts TUI thus it cannot be used by other services in the system to produce the output. At least when `/dev/tty2` is a current console. + +On the other hand `/dev/tty` (no digit at the end!) device always points to a TTY *in the context of running process*. This device can be used instead of `/dev/console` by other services for the output. + +Mode details can be found in [https://www.kernel.org/doc/Documentation/admin-guide/serial-console.rst](https://www.kernel.org/doc/Documentation/admin-guide/serial-console.rst), [https://www.kernel.org/doc/html/v6.11/admin-guide/devices.html#virtual-consoles-and-the-console-device](https://www.kernel.org/doc/html/v6.11/admin-guide/devices.html#virtual-consoles-and-the-console-device) and in this blog post [https://www.baeldung.com/linux/monitor-keyboard-drivers](https://www.baeldung.com/linux/monitor-keyboard-drivers) ## Limitations of linux terminal diff --git a/pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh b/pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh new file mode 100755 index 0000000000..e69bd13501 --- /dev/null +++ b/pkg/dom0-ztools/rootfs/usr/bin/rungetty.sh @@ -0,0 +1,94 @@ +#!/bin/sh + +infinite_loop() { + while true; do + $@ + done +} + +# run getty on all known consoles +start_getty() { + tty=${1%,*} + speed=${1#*,} + securetty="$2" + line= + term="linux" + [ "$speed" = "$1" ] && speed=115200 + + # if console=tty0 is specified on the kernel command line, we should not start a getty on tty0 + # but instead start it on the first available tty. tty0 points to the current console, so if + # we start a getty from pillar of debug.enable.console=true we may start it on current tty + # which may be occupied by TUI monitor application and tty will be taken away by getty. + # replacing tty0 with tty1 should be ok because this is the user's intention + if [ "$tty" = "tty0" ]; then + tty="tty1" + fi + + # did we already process this tty? + if $(echo "${PROCESSEDTTY}" | grep -q -w "$tty"); then + echo "getty: already processed tty for $tty, not starting twice" | tee /dev/tty + return + fi + # now indicate that we are processing it + PROCESSEDTTY="${PROCESSEDTTY} ${tty}" + + # does the device even exist? + if [ ! -c /dev/$tty ]; then + echo "getty: cmdline has console=$tty but /dev/$tty is not a character device; not starting getty for $tty" | tee /dev/tty + return + fi + + case "$tty" in + ttyS*|ttyAMA*|ttyUSB*|ttyMFD*) + line="-L" + term="vt100" + ;; + tty?) + line="" + speed="38400" + term="" + ;; + esac + + # are we secure or insecure? + loginargs= + if [ "$INSECURE" == "true" ]; then + loginargs="-a root" + fi + + if ! grep -q -w "$tty" "$securetty"; then + # we could not find the tty in securetty, so start a getty but warn that root login will not work + echo "getty: cmdline has console=$tty but does not exist in $securetty; will not be able to log in as root on this tty $tty." | tee /dev/$tty + fi + # respawn forever + echo "getty: starting getty for $tty" | tee /dev/$tty + infinite_loop setsid.getty -w /sbin/agetty $loginargs $line $speed $tty $term & +} + + +# check if we are namespaced, and, if so, indicate in the PS1 +if [ -z "$INITGETTY" ]; then + cat >/etc/profile.d/namespace.sh <<"EOF" +export PS1="(ns: getty) $PS1" +EOF +fi + +PROCESSEDTTY= + +# check if we have /etc/getty.shadow +ROOTSHADOW=/hostroot/etc/getty.shadow +if [ -f $ROOTSHADOW ]; then + cp $ROOTSHADOW /etc/shadow + # just in case someone forgot a newline + echo >> /etc/shadow +fi + +for opt in $(cat /proc/cmdline); do + case "$opt" in + console=*) + start_getty ${opt#console=} /etc/securetty + esac +done + +# if we are in a container (not in root init) wait for all our child process to exit; tini will handle subreaping, if necessary +[ -n "$INITGETTY" ] || wait