Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
inithooks (2.3.6+keel20) trixie; urgency=medium

* postinst no longer restarts the console's getty on an upgrade. When
inithooks.service was not running it restarted getty@tty1.service and
container-getty@1.service "to make sure" a login prompt was there,
and the restart ended the session on that tty: the maintainer ran
apt upgrade from the container console (pct console, tty1) and was
logged out mid-upgrade (2026-10-03). A getty is now only ever
started, only when it is inactive, only one the machine has (a
container has no getty@tty1), and only once the first boot is over,
inithooks.service neither running nor queued, asked the way
libinithooks/init_lock.py asks. The branch for an inithooks that was
running before the upgrade is unchanged.

-- Marcos Mendez <mendez.foto@gmail.com> Sat, 03 Oct 2026 09:00:00 +0000

inithooks (2.3.6+keel19) trixie; urgency=medium

* The first boot asks the machine's fully qualified domain name, in a
Expand Down
29 changes: 26 additions & 3 deletions debian/postinst
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,32 @@ if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ] \
systemctl restart turnkey-init-fence.service
fi
else
# getty1 should already be running if inithooks wasn't, but let's
# make sure
systemctl restart getty@tty1.service container-getty@1.service 2>/dev/null || true
# A login prompt should be on the console already. Make sure
# there is one without touching the one there is: a restart of
# the getty ends the session on it, and an operator running apt
# upgrade from the console (tty1, pct console) was logged out
# mid-upgrade (2026-10-03). So a getty is only ever started,
# only when it is inactive, only one the machine has (a
# container has no getty@tty1), and only once the first boot is
# over: inithooks.service neither running nor queued, as
# libinithooks/init_lock.py asks (is-active, then a start job
# in list-jobs), since the wizard draws on that tty.
state=$(systemctl is-active inithooks.service 2>/dev/null || true)
case "$state" in
active|activating|reloading|deactivating) state=running ;;
*) if [ -n "$(systemctl list-jobs --no-legend inithooks.service 2>/dev/null)" ]; then
state=queued
else
state=stopped
fi ;;
esac
if [ "$state" = stopped ]; then
for unit in getty@tty1.service container-getty@1.service; do
systemctl cat "$unit" >/dev/null 2>&1 || continue
systemctl is-active --quiet "$unit" \
|| systemctl start "$unit" 2>/dev/null || true
done
fi
fi
else
# fresh install ($2 is empty) - enable services
Expand Down
102 changes: 102 additions & 0 deletions tests/test-packaging.bats
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,108 @@ prepare_postinst() {
[ -e "$CRON_HOURLY/other-job" ]
}

# getty_answers: systemctl answers from the environment, for the upgrade
# branch of postinst: INITHOOKS_STATE is what is-active says of
# inithooks.service, INITHOOKS_JOB a queued job, GETTY_UNITS the getty units
# the machine has (systemctl cat), GETTY_ACTIVE the ones running.
getty_answers() {
stub systemctl 'case "$1" in
is-active)
unit=${2#--quiet}; unit=${unit:-$3}
case "$unit" in
inithooks.service) echo "${INITHOOKS_STATE:-inactive}"
[ "${INITHOOKS_STATE:-inactive}" = active ] ;;
*) [[ " ${GETTY_ACTIVE-} " == *" $unit "* ]] ;;
esac ;;
list-jobs) printf "%s" "${INITHOOKS_JOB-}" ;;
cat) [[ " ${GETTY_UNITS-} " == *" $2 "* ]] ;;
*) exit 0 ;;
esac'
}

upgrade_postinst() {
run sh "$BATS_TEST_TMPDIR/postinst" configure 2.3.6+keel18
[ "$status" -eq 0 ]
}

# 2026-10-03: the maintainer ran apt upgrade from the container console and
# was logged out mid-upgrade; postinst restarted the getty of the console
# the upgrade was running on, "to make sure" it was running.
@test "an upgrade never restarts a getty" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service container-getty@1.service"
export GETTY_ACTIVE="getty@tty1.service container-getty@1.service"
upgrade_postinst
run ! grep -qE "^(start|restart)" "$STUBS/systemctl.calls"
}

@test "the packaged postinst has no getty restart left in it" {
packaged_scripts
run ! grep -E 'restart.*getty' "$DEBIAN/postinst"
}

@test "an upgrade starts a getty that is inactive, among the units the machine has" {
prepare_postinst
getty_answers
# a container: container-getty@1 only, not running
export GETTY_UNITS="container-getty@1.service"
upgrade_postinst
[ "$(grep -E '^start' "$STUBS/systemctl.calls")" = "start container-getty@1.service" ]
}

@test "an upgrade starts each getty the machine has and does not run" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service container-getty@1.service"
export GETTY_ACTIVE="container-getty@1.service"
upgrade_postinst
[ "$(grep -E '^start' "$STUBS/systemctl.calls")" = "start getty@tty1.service" ]
}

@test "an upgrade touches no getty while the first boot runs" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service"
export INITHOOKS_STATE=activating
upgrade_postinst
run ! grep -qE "^(start|restart)" "$STUBS/systemctl.calls"
}

@test "an upgrade touches no getty while the first boot is queued" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service"
export INITHOOKS_JOB="12 inithooks.service start waiting"
upgrade_postinst
run ! grep -qE "^(start|restart)" "$STUBS/systemctl.calls"
}

@test "an upgrade with inithooks running before it restarts inithooks, as before" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service"
# the marker preinst leaves: the copy run here reads the real /run, so
# the path is pointed at a scratch one, the way the cron path is
sed -i "s|/run/inithooks-was-active|$BATS_TEST_TMPDIR/inithooks-was-active|g" \
"$BATS_TEST_TMPDIR/postinst"
touch "$BATS_TEST_TMPDIR/inithooks-was-active"
upgrade_postinst
grep -qx "restart inithooks.service" "$STUBS/systemctl.calls"
[ ! -e "$BATS_TEST_TMPDIR/inithooks-was-active" ]
run ! grep -q getty "$STUBS/systemctl.calls"
}

@test "a fresh install touches no getty" {
prepare_postinst
getty_answers
export GETTY_UNITS="getty@tty1.service"
run sh "$BATS_TEST_TMPDIR/postinst" configure
[ "$status" -eq 0 ]
run ! grep -q getty "$STUBS/systemctl.calls"
grep -qx "enable inithooks.service" "$STUBS/systemctl.calls"
}

@test "a system that never had the job installs cleanly" {
prepare_postinst
run sh "$BATS_TEST_TMPDIR/postinst" configure
Expand Down
Loading