Move MOTD into TTY login banner

This commit is contained in:
v3d 2026-06-29 23:08:56 +02:00
parent ed0b168ab6
commit bbbf80c25f
11 changed files with 137 additions and 29 deletions

View file

@ -62,6 +62,12 @@ verified active mode and reports degraded activation. `pivilion diagnose
--json` provides the same health checks for monitoring. The legacy --json` provides the same health checks for monitoring. The legacy
`pivilion-diagnose` command remains an alias. `pivilion-diagnose` command remains an alias.
On the physical TTY, Pivilion appends its current IP, ASCII logo, and system
status through `/etc/issue.d/90-pivilion.issue`; Raspberry Pi OS retains its
normal Debian/hostname/TTY heading. The banner appears before `login:` rather
than among systemd boot messages. SSH sessions receive the same status once
after authentication.
Diagnostic exit codes are stable: `10` invalid configuration, `11` missing or Diagnostic exit codes are stable: `10` invalid configuration, `11` missing or
unsupported hardware, `12` service/port conflict, `13` core service failure, unsupported hardware, `12` service/port conflict, `13` core service failure,
`14` captive portal failure, `15` Tor/onion failure, and `20` installer failure. `14` captive portal failure, `15` Tor/onion failure, and `20` installer failure.

View file

@ -1,7 +1,8 @@
# Show the Pivilion status banner for the configured owner on interactive Bash # TTY consoles receive the banner from agetty before login. Keep the
# login shells. Other users and non-interactive sessions remain unchanged. # post-authentication banner only for interactive SSH sessions.
[ -n "${BASH_VERSION:-}" ] || return 0 [ -n "${BASH_VERSION:-}" ] || return 0
case $- in *i*) ;; *) return 0 ;; esac case $- in *i*) ;; *) return 0 ;; esac
[ -n "${SSH_CONNECTION:-}" ] || return 0
[ -r /usr/local/lib/pivilion/common.sh ] || return 0 [ -r /usr/local/lib/pivilion/common.sh ] || return 0
[ "${PIVILION_MOTD_SHOWN:-0}" != 1 ] || return 0 [ "${PIVILION_MOTD_SHOWN:-0}" != 1 ] || return 0

View file

@ -1,13 +0,0 @@
[Unit]
Description=Display the Pivilion boot greeting and system status
After=local-fs.target
Before=getty@tty1.service
[Service]
Type=oneshot
ExecStart=/usr/local/lib/pivilion/boot-greeter.sh
StandardOutput=journal+console
StandardError=journal+console
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,14 @@
[Unit]
Description=Generate the Pivilion TTY login banner
After=local-fs.target
Before=getty@tty1.service
[Service]
Type=oneshot
ExecStart=/usr/local/lib/pivilion/issue-banner.sh
TimeoutStartSec=5
StandardOutput=null
StandardError=journal
[Install]
WantedBy=multi-user.target

View file

@ -42,13 +42,14 @@ export DEBIAN_FRONTEND=noninteractive
BACKUP_DIR="" BACKUP_DIR=""
TRANSACTION_ACTIVE=0 TRANSACTION_ACTIVE=0
declare -a MANAGED_PATHS=( declare -a MANAGED_PATHS=(
/etc/pivilion.defaults /etc/pivilion.php /etc/profile.d/pivilion-motd.sh /etc/pivilion.defaults /etc/pivilion.php /etc/profile.d/pivilion-motd.sh /etc/issue.d/90-pivilion.issue
/etc/apache2/ports.conf /etc/apache2/conf-available/pivilion.conf /etc/apache2/ports.conf /etc/apache2/conf-available/pivilion.conf
/etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-upload.conf /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/000-upload.conf
/etc/apache2/conf-enabled/pivilion.conf /etc/apache2/sites-enabled/000-default.conf /etc/apache2/conf-enabled/pivilion.conf /etc/apache2/sites-enabled/000-default.conf
/etc/tor/torrc /etc/tor/torrc.d/pivilion.conf /etc/tor/torrc /etc/tor/torrc.d/pivilion.conf
/etc/systemd/system/pivilion-mode.service /etc/systemd/system/pivilion-dnsmasq.service /etc/systemd/system/pivilion-mode.service /etc/systemd/system/pivilion-dnsmasq.service
/etc/systemd/system/pivilion-hostapd.service /etc/systemd/system/pivilion-boot-greeter.service /etc/systemd/system/pivilion-hostapd.service /etc/systemd/system/pivilion-boot-greeter.service
/etc/systemd/system/pivilion-login-banner.service /etc/rc.local
/usr/local/lib/pivilion /usr/local/bin/pivilion /usr/local/bin/pivilion-diagnose /usr/local/lib/pivilion /usr/local/bin/pivilion /usr/local/bin/pivilion-diagnose
/usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey /usr/local/bin/generator /usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey /usr/local/bin/generator
/usr/local/bin/static /usr/local/bin/hotglue /usr/local/bin/htaccess /usr/local/bin/static /usr/local/bin/hotglue /usr/local/bin/htaccess
@ -117,7 +118,7 @@ backup_managed_files() {
BACKUP_DIR=$(mktemp -d /var/backups/pivilion/install-$(date +%Y%m%d-%H%M%S).XXXXXX) BACKUP_DIR=$(mktemp -d /var/backups/pivilion/install-$(date +%Y%m%d-%H%M%S).XXXXXX)
install -d -m 0700 "$BACKUP_DIR/files" install -d -m 0700 "$BACKUP_DIR/files"
: > "$BACKUP_DIR/present"; : > "$BACKUP_DIR/missing"; : > "$BACKUP_DIR/enabled-services"; : > "$BACKUP_DIR/active-services" : > "$BACKUP_DIR/present"; : > "$BACKUP_DIR/missing"; : > "$BACKUP_DIR/enabled-services"; : > "$BACKUP_DIR/active-services"
for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
systemctl is-enabled --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/enabled-services" || true systemctl is-enabled --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/enabled-services" || true
systemctl is-active --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/active-services" || true systemctl is-active --quiet "$service" 2>/dev/null && printf '%s\n' "$service" >> "$BACKUP_DIR/active-services" || true
done done
@ -146,9 +147,9 @@ rollback() {
relative=${path#/}; rm -rf -- "$path"; install -d -m 0755 "$(dirname "$path")"; cp -a "$BACKUP_DIR/files/$relative" "$path" relative=${path#/}; rm -rf -- "$path"; install -d -m 0755 "$(dirname "$path")"; cp -a "$BACKUP_DIR/files/$relative" "$path"
done < "$BACKUP_DIR/present" done < "$BACKUP_DIR/present"
systemctl daemon-reload 2>/dev/null || true systemctl daemon-reload 2>/dev/null || true
systemctl disable apache2 pivilion-mode.service pivilion-boot-greeter.service tor.service tor@default.service 2>/dev/null || true systemctl disable apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service tor.service tor@default.service 2>/dev/null || true
while IFS= read -r path; do [ -n "$path" ] && systemctl enable "$path" 2>/dev/null || true; done < "$BACKUP_DIR/enabled-services" while IFS= read -r path; do [ -n "$path" ] && systemctl enable "$path" 2>/dev/null || true; done < "$BACKUP_DIR/enabled-services"
for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do for service in apache2 pivilion-mode.service pivilion-boot-greeter.service pivilion-login-banner.service pivilion-dnsmasq.service pivilion-hostapd.service tor.service tor@default.service; do
grep -Fqx "$service" "$BACKUP_DIR/active-services" || systemctl stop "$service" 2>/dev/null || true grep -Fqx "$service" "$BACKUP_DIR/active-services" || systemctl stop "$service" 2>/dev/null || true
done done
while IFS= read -r service; do [ -n "$service" ] && systemctl start "$service" 2>/dev/null || true; done < "$BACKUP_DIR/active-services" while IFS= read -r service; do [ -n "$service" ] && systemctl start "$service" 2>/dev/null || true; done < "$BACKUP_DIR/active-services"
@ -171,7 +172,7 @@ deploy_files() {
install -d -m 0755 /usr/local/lib/pivilion /usr/local/bin /etc/systemd/system /etc/tor/torrc.d /etc/profile.d /var/lib/pivilion install -d -m 0755 /usr/local/lib/pivilion /usr/local/bin /etc/systemd/system /etc/tor/torrc.d /etc/profile.d /var/lib/pivilion
install -m 0644 "$SOURCE_DIR/usr/local/lib/pivilion/common.sh" /usr/local/lib/pivilion/common.sh install -m 0644 "$SOURCE_DIR/usr/local/lib/pivilion/common.sh" /usr/local/lib/pivilion/common.sh
install -m 0755 "$SOURCE_DIR/usr/local/lib/pivilion/mode.sh" "$SOURCE_DIR/usr/local/lib/pivilion/diagnose.sh" \ install -m 0755 "$SOURCE_DIR/usr/local/lib/pivilion/mode.sh" "$SOURCE_DIR/usr/local/lib/pivilion/diagnose.sh" \
"$SOURCE_DIR/usr/local/lib/pivilion/boot-greeter.sh" /usr/local/lib/pivilion/ "$SOURCE_DIR/usr/local/lib/pivilion/issue-banner.sh" /usr/local/lib/pivilion/
install -m 0755 "$SOURCE_DIR"/usr/local/bin/* /usr/local/bin/ install -m 0755 "$SOURCE_DIR"/usr/local/bin/* /usr/local/bin/
install -m 0644 "$SOURCE_DIR"/etc/systemd/system/*.service /etc/systemd/system/ install -m 0644 "$SOURCE_DIR"/etc/systemd/system/*.service /etc/systemd/system/
install -m 0644 "$SOURCE_DIR/etc/tor/torrc.d/pivilion.conf" /etc/tor/torrc.d/pivilion.conf install -m 0644 "$SOURCE_DIR/etc/tor/torrc.d/pivilion.conf" /etc/tor/torrc.d/pivilion.conf
@ -211,6 +212,43 @@ EOF
install -m 0644 "$SOURCE_DIR/etc/php/pivilion.ini" "/etc/php/$php_version/apache2/conf.d/99-pivilion.ini" install -m 0644 "$SOURCE_DIR/etc/php/pivilion.ini" "/etc/php/$php_version/apache2/conf.d/99-pivilion.ini"
} }
migrate_legacy_console_greeter() {
local tmp signatures=0
systemctl disable --now pivilion-boot-greeter.service 2>/dev/null || true
rm -f /etc/systemd/system/pivilion-boot-greeter.service /usr/local/lib/pivilion/boot-greeter.sh
[ -f /etc/rc.local ] || return 0
grep -q 'hostapd -B' /etc/rc.local && signatures=$((signatures + 1)) || true
grep -q 'ifconfig br0' /etc/rc.local && signatures=$((signatures + 1)) || true
grep -q 'iptables -t nat' /etc/rc.local && signatures=$((signatures + 1)) || true
grep -q 'service dnsmasq start' /etc/rc.local && signatures=$((signatures + 1)) || true
grep -q '/usr/local/lib/pivilion/mode.sh' /etc/rc.local && signatures=4 || true
if [ "$signatures" -ge 4 ]; then
cp -a /etc/rc.local "$BACKUP_DIR/legacy-rc.local"
atomic_from_stdin /etc/rc.local 0755 <<'EOF'
#!/bin/sh -e
# Pivilion startup is managed by pivilion-mode.service.
exit 0
EOF
log "Migrated legacy Pivilion rc.local; original saved in $BACKUP_DIR/legacy-rc.local"
elif grep -q '/var/lib/tor/hidden_service/hostname.*home/pi/torname' /etc/rc.local; then
cp -a /etc/rc.local "$BACKUP_DIR/legacy-rc.local"
if grep -Fq '_IP=$(hostname -I)' /etc/rc.local && grep -Fq 'My IP address is' /etc/rc.local; then
atomic_from_stdin /etc/rc.local 0755 <<'EOF'
#!/bin/sh -e
# Pivilion startup and the TTY banner are managed by systemd and agetty.
exit 0
EOF
else
tmp=$(mktemp)
grep -v '/var/lib/tor/hidden_service/hostname.*home/pi/torname' /etc/rc.local > "$tmp"
install -m 0755 "$tmp" /etc/rc.local
rm -f "$tmp"
fi
log "Migrated legacy Pivilion onion rc.local; original saved in $BACKUP_DIR/legacy-rc.local"
fi
}
configure_services() { configure_services() {
local tmp local tmp
log "Configuring services" log "Configuring services"
@ -228,10 +266,13 @@ EOF
if ! grep -Fqx '%include /etc/tor/torrc.d/*.conf' /etc/tor/torrc; then if ! grep -Fqx '%include /etc/tor/torrc.d/*.conf' /etc/tor/torrc; then
tmp=$(mktemp); cp /etc/tor/torrc "$tmp"; printf '\n%%include /etc/tor/torrc.d/*.conf\n' >> "$tmp"; install -m 0644 "$tmp" /etc/tor/torrc; rm -f "$tmp" tmp=$(mktemp); cp /etc/tor/torrc "$tmp"; printf '\n%%include /etc/tor/torrc.d/*.conf\n' >> "$tmp"; install -m 0644 "$tmp" /etc/tor/torrc; rm -f "$tmp"
fi fi
migrate_legacy_console_greeter
systemctl daemon-reload systemctl daemon-reload
systemctl enable apache2 pivilion-mode.service pivilion-boot-greeter.service systemctl enable apache2 pivilion-mode.service pivilion-login-banner.service
systemctl enable tor@default.service 2>/dev/null || systemctl enable tor.service systemctl enable tor@default.service 2>/dev/null || systemctl enable tor.service
systemctl disable pivilion-dnsmasq.service pivilion-hostapd.service 2>/dev/null || true systemctl disable pivilion-dnsmasq.service pivilion-hostapd.service 2>/dev/null || true
/usr/local/lib/pivilion/issue-banner.sh
command -v agetty >/dev/null 2>&1 && agetty --reload >/dev/null 2>&1 || true
} }
validate_install() { validate_install() {
@ -243,6 +284,9 @@ validate_install() {
dnsmasq --test || failed=1 dnsmasq --test || failed=1
php -l "$PIVILION_WEBROOT/gen.php" || failed=1 php -l "$PIVILION_WEBROOT/gen.php" || failed=1
systemd-analyze verify /etc/systemd/system/pivilion-*.service || failed=1 systemd-analyze verify /etc/systemd/system/pivilion-*.service || failed=1
grep -q '^My IP address is ' /etc/issue.d/90-pivilion.issue || failed=1
grep -q '^@@@@@@@@@@@@@@@@@@@' /etc/issue.d/90-pivilion.issue || failed=1
grep -q 'StandardOutput=null' /etc/systemd/system/pivilion-login-banner.service || failed=1
if command -v shellcheck >/dev/null 2>&1; then shellcheck /usr/local/lib/pivilion/*.sh /usr/local/bin/pivilion* /usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey || failed=1; fi if command -v shellcheck >/dev/null 2>&1; then shellcheck /usr/local/lib/pivilion/*.sh /usr/local/bin/pivilion* /usr/local/bin/hotspot /usr/local/bin/onion /usr/local/bin/pikey || failed=1; fi
[ "$failed" -eq 0 ] || return "$PIVILION_E_INSTALL" [ "$failed" -eq 0 ] || return "$PIVILION_E_INSTALL"
} }
@ -259,7 +303,10 @@ EOF
check_install() { check_install() {
local failed=0 file local failed=0 file
for file in /etc/pivilion.defaults /var/lib/pivilion/install-state /usr/local/lib/pivilion/common.sh /usr/local/lib/pivilion/mode.sh /usr/local/lib/pivilion/diagnose.sh /etc/systemd/system/pivilion-mode.service; do for file in /etc/pivilion.defaults /var/lib/pivilion/install-state /etc/issue.d/90-pivilion.issue \
/usr/local/lib/pivilion/common.sh /usr/local/lib/pivilion/mode.sh /usr/local/lib/pivilion/diagnose.sh \
/usr/local/lib/pivilion/issue-banner.sh /etc/systemd/system/pivilion-mode.service \
/etc/systemd/system/pivilion-login-banner.service; do
if [ ! -e "$file" ]; then echo "Missing: $file" >&2; failed=1; fi if [ ! -e "$file" ]; then echo "Missing: $file" >&2; failed=1; fi
done done
if [ "$failed" -eq 0 ]; then validate_install || failed=1; fi if [ "$failed" -eq 0 ]; then validate_install || failed=1; fi

View file

@ -49,6 +49,15 @@ state_contract() {
grep -q 'degraded' "$ROOT/usr/local/lib/pivilion/mode.sh" grep -q 'degraded' "$ROOT/usr/local/lib/pivilion/mode.sh"
} }
tty_banner_contract() {
grep -q 'StandardOutput=null' "$ROOT/etc/systemd/system/pivilion-login-banner.service" &&
! grep -q 'journal+console' "$ROOT/etc/systemd/system/pivilion-login-banner.service" &&
grep -q '/etc/issue.d/90-pivilion.issue' "$ROOT/usr/local/lib/pivilion/issue-banner.sh" &&
grep -q 'SSH_CONNECTION' "$ROOT/etc/profile.d/pivilion-motd.sh" &&
grep -q 'agetty --reload' "$ROOT/usr/local/lib/pivilion/mode.sh" &&
! grep -Eq '(^|[[:space:]])/etc/issue([[:space:]]|$)' "$ROOT/install.sh"
}
shellcheck_scripts() { shellcheck_scripts() {
command -v shellcheck >/dev/null 2>&1 || return 0 command -v shellcheck >/dev/null 2>&1 || return 0
shellcheck "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/pivilion* "$ROOT/usr/local/bin/hotspot" "$ROOT/usr/local/bin/onion" "$ROOT/usr/local/bin/pikey" shellcheck "$ROOT/install.sh" "$ROOT"/usr/local/lib/pivilion/*.sh "$ROOT"/usr/local/bin/pivilion* "$ROOT/usr/local/bin/hotspot" "$ROOT/usr/local/bin/onion" "$ROOT/usr/local/bin/pikey"
@ -61,6 +70,7 @@ run "systemd unit syntax" systemd_syntax
run "temporary HTTP-only captive rules" captive_rules run "temporary HTTP-only captive rules" captive_rules
run "transactional installer safety contract" installer_safety run "transactional installer safety contract" installer_safety
run "truthful runtime state contract" state_contract run "truthful runtime state contract" state_contract
run "agetty login-banner contract" tty_banner_contract
run "ShellCheck (when installed)" shellcheck_scripts run "ShellCheck (when installed)" shellcheck_scripts
[ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; } [ "$failures" -eq 0 ] || { echo "$failures test group(s) failed." >&2; exit 1; }

View file

@ -17,6 +17,7 @@ PIVILION_DIR=$TMP/pivilion
PIVILION_WEBROOT=/var/www/html/pivilion PIVILION_WEBROOT=/var/www/html/pivilion
PIVILION_MODE=onion PIVILION_MODE=onion
PIVILION_HOTSPOT_SSID=fixture-portal PIVILION_HOTSPOT_SSID=fixture-portal
PIVILION_WIFI_INTERFACE=wlan-test
EOF EOF
export PIVILION_CONFIG_PATHS="$TMP/primary.conf $TMP/fallback.conf" export PIVILION_CONFIG_PATHS="$TMP/primary.conf $TMP/fallback.conf"
@ -43,6 +44,11 @@ EOF
[ "$(pivilion_state_value health)" = degraded ] [ "$(pivilion_state_value health)" = degraded ]
[ "$(pivilion_state_value missing fallback)" = fallback ] [ "$(pivilion_state_value missing fallback)" = fallback ]
pivilion_print_issue > "$TMP/pivilion.issue"
grep -Fqx 'My IP address is \4{wlan-test}' "$TMP/pivilion.issue"
grep -q '^Welcome to Pivilion!$' "$TMP/pivilion.issue"
grep -q '^@@@@@@@@@@@@@@@@@@@' "$TMP/pivilion.issue"
mkdir "$TMP/bin" mkdir "$TMP/bin"
cat > "$TMP/bin/nmcli" <<'EOF' cat > "$TMP/bin/nmcli" <<'EOF'
#!/bin/sh #!/bin/sh

View file

@ -1,6 +0,0 @@
#!/bin/bash
set -euo pipefail
source /usr/local/lib/pivilion/common.sh
pivilion_load_config
pivilion_print_motd "Welcome to Pivilion!"

View file

@ -161,6 +161,7 @@ pivilion_service_state() {
pivilion_print_motd() { pivilion_print_motd() {
local heading=${1:-Welcome to Pivilion!} local heading=${1:-Welcome to Pivilion!}
local show_addresses=${2:-yes}
local uptime_text memory_free memory_total load_one load_five load_fifteen processes torname desired active backend health addresses local uptime_text memory_free memory_total load_one load_five load_fifteen processes torname desired active backend health addresses
uptime_text=$(uptime -p 2>/dev/null || printf 'unknown') uptime_text=$(uptime -p 2>/dev/null || printf 'unknown')
@ -191,6 +192,19 @@ pivilion_print_motd() {
@@, @ ,@@ Onion Address......: $torname @@, @ ,@@ Onion Address......: $torname
@@@@@#@@@@@@@#@@@@@ Desired / active....: $desired / $active @@@@@#@@@@@@@#@@@@@ Desired / active....: $desired / $active
Backend / health...: $backend / $health Backend / health...: $backend / $health
IP addresses.......: $addresses
EOF EOF
if [ "$show_addresses" = yes ]; then
printf ' IP addresses.......: %s\n' "$addresses"
fi
}
pivilion_print_issue() {
local iface
iface=$(pivilion_wifi_interface)
if [ -n "$iface" ]; then
printf 'My IP address is \\4{%s}\n' "$iface"
else
printf 'My IP address is unknown\n'
fi
pivilion_print_motd "Welcome to Pivilion!" no
} }

View file

@ -0,0 +1,16 @@
#!/bin/bash
set -euo pipefail
source /usr/local/lib/pivilion/common.sh
pivilion_require_root
pivilion_load_config
ISSUE_FILE=/etc/issue.d/90-pivilion.issue
ISSUE_DIR=$(dirname "$ISSUE_FILE")
install -d -m 0755 "$ISSUE_DIR"
tmp=$(mktemp "$ISSUE_DIR/.90-pivilion.XXXXXX")
trap 'rm -f "$tmp"' EXIT
pivilion_print_issue > "$tmp"
chmod 0644 "$tmp"
mv -f "$tmp" "$ISSUE_FILE"
trap - EXIT

View file

@ -24,10 +24,18 @@ CAPTIVE_LIVE=$PIVILION_WEBROOT/gen/.htaccess
BACKEND=legacy BACKEND=legacy
pivilion_uses_networkmanager && BACKEND=networkmanager pivilion_uses_networkmanager && BACKEND=networkmanager
refresh_login_banner() {
if [ -x /usr/local/lib/pivilion/issue-banner.sh ]; then
/usr/local/lib/pivilion/issue-banner.sh || logger -t pivilion-mode "Could not refresh TTY login banner"
fi
command -v agetty >/dev/null 2>&1 && agetty --reload >/dev/null 2>&1 || true
}
activation_error() { activation_error() {
local rc=$? line=${BASH_LINENO[0]:-unknown} local rc=$? line=${BASH_LINENO[0]:-unknown}
trap - ERR trap - ERR
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
refresh_login_banner || true
logger -t pivilion-mode "Failed to activate $MODE mode at line $line (exit $rc)" 2>/dev/null || true logger -t pivilion-mode "Failed to activate $MODE mode at line $line (exit $rc)" 2>/dev/null || true
exit "$rc" exit "$rc"
} }
@ -168,16 +176,19 @@ start_hotspot() {
pivilion_write_state none "$BACKEND" activating none pivilion_write_state none "$BACKEND" activating none
validate_ap_support || { validate_ap_support || {
pivilion_write_state none "$BACKEND" failed "$IFACE does not advertise Wi-Fi AP support" pivilion_write_state none "$BACKEND" failed "$IFACE does not advertise Wi-Fi AP support"
refresh_login_banner
echo "$IFACE does not advertise Wi-Fi AP support." >&2 echo "$IFACE does not advertise Wi-Fi AP support." >&2
exit "$PIVILION_E_HARDWARE" exit "$PIVILION_E_HARDWARE"
} }
cleanup_hotspot cleanup_hotspot
if ! check_hostapd_conflict; then if ! check_hostapd_conflict; then
pivilion_write_state none "$BACKEND" failed "System hostapd conflict" pivilion_write_state none "$BACKEND" failed "System hostapd conflict"
refresh_login_banner
exit "$PIVILION_E_CONFLICT" exit "$PIVILION_E_CONFLICT"
fi fi
if ! check_dns_conflict; then if ! check_dns_conflict; then
pivilion_write_state none "$BACKEND" failed "Port 53 conflict" pivilion_write_state none "$BACKEND" failed "Port 53 conflict"
refresh_login_banner
exit "$PIVILION_E_CONFLICT" exit "$PIVILION_E_CONFLICT"
fi fi
install_captive_redirect install_captive_redirect
@ -203,6 +214,7 @@ start_hotspot() {
pivilion_write_state hotspot "$BACKEND" degraded "Post-start captive portal diagnostics failed" pivilion_write_state hotspot "$BACKEND" degraded "Post-start captive portal diagnostics failed"
logger -t pivilion-mode "Hotspot active but degraded; run pivilion diagnose" || true logger -t pivilion-mode "Hotspot active but degraded; run pivilion diagnose" || true
fi fi
refresh_login_banner
echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE." echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE."
} }
@ -227,6 +239,7 @@ start_onion() {
pivilion_write_state onion "$BACKEND" degraded "${failure:-Onion diagnostics failed}" pivilion_write_state onion "$BACKEND" degraded "${failure:-Onion diagnostics failed}"
logger -t pivilion-mode "Onion mode active but degraded: ${failure:-diagnostics failed}" || true logger -t pivilion-mode "Onion mode active but degraded: ${failure:-diagnostics failed}" || true
fi fi
refresh_login_banner
echo "Pivilion onion mode activation completed." echo "Pivilion onion mode activation completed."
} }