#!/bin/sh

# Scutarius: this is rootskel 1.138's reopen-console (upstream path
# src/usr/sbin/reopen-console-linux; reference copy in
# profiles/debian-installer/upstream/) with TWO changes, each marked
# "Scutarius:" below. scutarius-install-reopen-console puts it over the
# upstream file in the installer tree, and refuses to if the upstream file is
# not the one this copy was made from. Tested by
# profiles/debian-installer/tests/reopen-console-harness.sh.
# Spec: docs/superpowers/specs/2026-09-10-installer-any-console-design.md

# First find the enabled consoles from the kernel, noting if one is 'preferred'
# Record these.
# Run the startup scripts on the preferred console

# In order to have D-I appear on all consoles, modify the inittab to
# add one entry for each console, running debian-installer.
# Finally HUP init so that it runs those installers
# (but doesn't rerun the sysinit startup stuff, including this script)


NL="
"

LOGGER_UP=0
LOG_FILE=/var/log/reopen-console

# If we're running with preseeding, we have a problem with running d-i
# on multiple consoles. We'll end up running each of those d-i
# instances in parallel with all kinds of hilarious undefined
# behaviour as they trip over each other! If we detect that we're
# preseeding (via any of the possible preseed methods), DO NOT run d-i
# multiple times. Instead, fall back to the older, more simple
# behaviour and run it once. If the user wants to see or interact with
# their preseed on a specific console, they get to tell us which one
# they want to use.
PRESEEDING=0

log() {
	# In very early startup we don't have syslog. Log to file that
	# we can flush out later so we can at least see what happened
	# at early startup
	if [ $LOGGER_UP -eq 1 ]; then
	        logger -t reopen-console "$@"
	else
		echo "$@" >> $LOG_FILE
	fi
}

flush_logger () {
	cat $LOG_FILE | logger -t reopen-console
	rm $LOG_FILE
}

# Scutarius: a /preseed.cfg in the initramfs is NOT a reason to run on one
# console, so upstream's test for it is gone. Our build bakes one into every
# initrd (build/preseed.cfg.in), and it holds DEFAULTS ONLY: language,
# keyboard, network, disks and users are all still asked, so every d-i
# instance stops at its first question until a person drives it, and the
# parallel-install hazard described above cannot happen. Honouring the file
# put every Scutarius installer on a single console -- whichever one the
# kernel preferred. With no console= on a machine whose firmware names a
# serial port (ACPI SPCR: aarch64 VMs, GB10) that was the serial port, and
# the screen stayed blank.
#
# What DOES mean "use one console" is somebody naming one: a console=
# anywhere on the kernel command line, before or after d-i's "---". Only "--"
# ends the kernel's own parameters, so the kernel registers a console= on
# either side of "---", and /proc/consoles already marks the last one
# preferred. (netconsole= is not a console=, hence the word boundary.)
#
# If build/preseed.cfg.in ever grows answers that let d-i run to the end on
# its own, this rule must be revisited FIRST: every console would then run a
# complete install, in parallel, on the same disk. The harness named above
# fails if it does.
if grep -qE '(^|[[:space:]])console=' /proc/cmdline; then
    log "Found console= in the command line; running on the chosen console only"
    PRESEEDING=1
fi

# Have we been told to do preseeding stuff on the boot command line?
for WORD in auto url; do
    if (grep -qw "$WORD" /proc/cmdline); then
	log "Found \"$WORD\" in the command line; falling back to simple mode for preseeding"
	PRESEEDING=1
    fi
done

consoles=
preferred=
# Retrieve all enabled consoles from kernel; ignore those
# for which no device file exists

kernelconsoles="$(cat /proc/consoles)"
for cons in $(echo "$kernelconsoles" | sed -n -r -e 's/(^.*)  .*\((.*)\).*$/\1/p' )
do
	log "Looking at console $cons from /proc/consoles"
	# Scutarius: read the flags under the name /proc/consoles uses, and only
	# THEN map tty0 to tty1. Upstream renamed first (rootskel 22ceb372,
	# 2021-11-12, still so in 1.139) and then grepped /proc/consoles for
	# "tty1", which has no line there: tty0's C (preferred) flag was lost, no
	# console was preferred, and the first one listed won -- on an SPCR
	# machine the serial port. So console=tty0 put the installer on serial.
	status=$(echo "$kernelconsoles" | grep $cons | sed -n -r -e 's/(^.*) *.*\((.*)\).*$/\2/p' )
	# Using tty0 prevents ^C from working
	[ "$cons" != tty0 ] || cons=tty1

	if [ -e "/dev/$cons" ] && [ $(echo "$status" | grep -o 'E') ]; then
		consoles="${consoles:+$consoles$NL}$cons"
		log "   Adding $cons to possible consoles list"
	fi
	# 'C' console is 'most prefered'.
	if [ $(echo "$status" | grep -o 'C') ]; then
		preferred="$cons"
		log "   $cons is preferred"
	fi
done

# /dev/tty1 may not show up in /proc/consoles (at least on QEMU aarch64),
# debian-installer should run on it anyway if it exists.
if [ -c /dev/tty1 ] && ! { echo "$consoles" | grep -q "^tty1$"; }; then
	consoles="${consoles:+$consoles$NL}tty1"
	log "   Adding tty0 to possible consoles list"
fi

if { echo "$consoles" | grep -q "^tty1$"; } && grep -q speakup /proc/cmdline ; then
        # Speakup can only work on the VT, prefer getting boot messages there
        # (notably sound card selection).
	preferred=tty1
fi

if [ -z "$consoles" ]; then
	# Nothing found? Default to /dev/console.
	log "Found no consoles! Defaulting to /dev/console"
	consoles=console
fi
if [ -z "$preferred" ]; then
	#None marked preferred? Use the first one
	preferred=$(echo "$consoles" | head -n 1)
	log "Found no preferred console. Picking $preferred"
fi

# If we're preseeding, do simple stuff here (see above). We just
# want one console. Let's pick the preferred one ONLY
if [ $PRESEEDING = 1 ]; then
    log "Running with preseeding. Picking preferred $preferred ONLY"
    consoles=$preferred
fi

for cons in $consoles
do
	echo "/dev/$cons " >> /var/run/console-devices
done
echo "/dev/$preferred " > /var/run/console-preferred


# Add debian-installer lines into inittab - one per console
for cons in $consoles
do
	log "Adding inittab entry for $cons"
	echo "$cons::respawn:/sbin/debian-installer" >> /etc/inittab
done

# Run the startup scripts once, using the preferred console
cons=$(cat /var/run/console-preferred)
# Some other session may have that console as ctty. Steal it from them
/sbin/steal-ctty $cons "$@"

# Now we should have syslog running, so flush our log entries
LOGGER_UP=1
flush_logger

# Finally restart init to run debian-installer on discovered consoles
log "Restarting init to start d-i on the console(s) we found"
kill -HUP 1

exit 0
