Activate offline mode changes on next boot

This commit is contained in:
v3d 2026-06-30 17:02:54 +02:00
parent eda151065c
commit b5e19f08f6
9 changed files with 94 additions and 14 deletions

View file

@ -5,6 +5,7 @@
PIVILION_CONFIG=""
PIVILION_STATE_DIR=${PIVILION_STATE_DIR:-/run/pivilion}
PIVILION_STATE_FILE=${PIVILION_STATE_FILE:-$PIVILION_STATE_DIR/status.tsv}
PIVILION_SELECTED_MODE_FILE=${PIVILION_SELECTED_MODE_FILE:-$PIVILION_STATE_DIR/selected-mode}
# Stable command exit codes. Keep these public so scripts and monitoring can
# distinguish configuration, hardware, service, captive portal, and Tor faults.
@ -158,6 +159,12 @@ pivilion_current_mode() {
printf '%s\n' "$PIVILION_MODE"
}
pivilion_selected_mode() {
local mode
mode=$(head -n1 "$PIVILION_SELECTED_MODE_FILE" 2>/dev/null || true)
case "$mode" in hotspot|onion) printf '%s\n' "$mode" ;; *) return 1 ;; esac
}
pivilion_require_root() {
[ "$(id -u)" -eq 0 ] || { echo "This operation must run as root (use sudo)." >&2; return 1; }
}

View file

@ -125,7 +125,7 @@ EOF
}
write_canonical() {
local tmp
local tmp mode_tmp
require_writable_boot
tmp=$(mktemp "$BOOT_ROOT/.pivilion-conf.XXXXXX")
render_config > "$tmp"
@ -134,6 +134,10 @@ write_canonical() {
sync -f "$BOOT_ROOT" 2>/dev/null || sync
atomic_copy "$BOOT_CONFIG" "$LAST_GOOD" 0600
atomic_copy "$BOOT_CONFIG" /run/pivilion/effective.conf 0600
mode_tmp=$(mktemp)
printf '%s\n' "$PIVILION_MODE" > "$mode_tmp"
atomic_copy "$mode_tmp" "$PIVILION_SELECTED_MODE_FILE" 0644
rm -f "$mode_tmp"
rm -f "$BOOT_ERROR"
}
@ -255,11 +259,18 @@ set_value() {
case "$ACTION" in
apply|initialize)
if [ "$ACTION" = apply ]; then
rm -f /run/pivilion/effective.conf "$PIVILION_SELECTED_MODE_FILE"
fi
if ! load_boot_or_detect; then
fail_boot "Invalid pivilion.conf; the last-known-good settings were retained"
[ -r "$LAST_GOOD" ] || exit "$PIVILION_E_CONFIG"
PIVILION_CONFIG_PATHS=$LAST_GOOD pivilion_load_config || exit "$PIVILION_E_CONFIG"
atomic_copy "$LAST_GOOD" /run/pivilion/effective.conf 0600
mode_tmp=$(mktemp)
printf '%s\n' "$PIVILION_MODE" > "$mode_tmp"
atomic_copy "$mode_tmp" "$PIVILION_SELECTED_MODE_FILE" 0644
rm -f "$mode_tmp"
[ "$ACTION" = initialize ] || apply_runtime
exit 0
fi
@ -282,5 +293,9 @@ case "$ACTION" in
sync-status)
PIVILION_CONFIG_PATHS=$BOOT_CONFIG pivilion_load_config || exit "$PIVILION_E_CONFIG"
write_canonical ;;
*) echo "Usage: config-sync.sh apply|initialize|set KEY VALUE|set-wifi SSID PASSWORD|sync-status" >&2; exit 2 ;;
record-error)
shift
[ "$#" -gt 0 ] || { echo "Usage: config-sync.sh record-error MESSAGE" >&2; exit 2; }
fail_boot "$*" ;;
*) echo "Usage: config-sync.sh apply|initialize|set KEY VALUE|set-wifi SSID PASSWORD|sync-status|record-error MESSAGE" >&2; exit 2 ;;
esac

View file

@ -5,8 +5,30 @@ source /usr/local/lib/pivilion/common.sh
pivilion_require_root
pivilion_load_config || exit "$PIVILION_E_CONFIG"
MODE=${1:-$(pivilion_current_mode)}
if [ "${1:---boot-selected}" = --boot-selected ]; then
MODE=$(pivilion_selected_mode) || {
echo "No validated boot mode was produced by pivilion-config.service" >&2
/usr/local/lib/pivilion/config-sync.sh record-error "No validated boot mode was produced during startup" >/dev/null 2>&1 || true
exit "$PIVILION_E_CONFIG"
}
[ "$MODE" = "$(pivilion_current_mode)" ] || {
echo "Validated boot mode does not match the effective configuration" >&2
/usr/local/lib/pivilion/config-sync.sh record-error "Validated boot mode does not match the effective configuration" >/dev/null 2>&1 || true
exit "$PIVILION_E_CONFIG"
}
else
MODE=$1
fi
case "$MODE" in hotspot|onion) ;; *) echo "Unknown Pivilion mode: $MODE" >&2; exit 2 ;; esac
mode_exit() {
local rc=$? failure
[ "$rc" -eq 0 ] && return
failure=$(pivilion_state_value failure "activation exited with status $rc")
/usr/local/lib/pivilion/config-sync.sh record-error "Failed to activate $MODE mode: $failure (exit $rc)" >/dev/null 2>&1 || true
logger -t pivilion-mode "Failed to activate $MODE mode: $failure (exit $rc)" 2>/dev/null || true
}
trap mode_exit EXIT
IFACE=$(pivilion_wifi_interface)
[ -n "$IFACE" ] && [ -d "/sys/class/net/$IFACE" ] || {
pivilion_write_state none none failed "Wi-Fi interface not found"
@ -32,9 +54,12 @@ refresh_login_banner() {
}
activation_error() {
local rc=$? line=${BASH_LINENO[0]:-unknown}
local rc=$? line=${BASH_LINENO[0]:-unknown} existing
trap - ERR
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
existing=$(pivilion_state_value failure none)
if [ "$existing" = none ] || [ "$existing" = unknown ]; then
pivilion_write_state none "$BACKEND" failed "Activation failed at line $line (exit $rc)" || true
fi
refresh_login_banner || true
logger -t pivilion-mode "Failed to activate $MODE mode at line $line (exit $rc)" 2>/dev/null || true
exit "$rc"
@ -152,6 +177,15 @@ wait_for_default_route() {
return 1
}
wait_for_diagnostics() {
local mode=$1 i
for i in $(seq 1 10); do
/usr/local/lib/pivilion/diagnose.sh --mode "$mode" --quiet && return 0
sleep 1
done
return 1
}
restart_tor() {
if systemctl cat tor@default.service >/dev/null 2>&1; then systemctl restart tor@default.service; else systemctl restart tor.service; fi
}
@ -196,6 +230,7 @@ start_hotspot() {
rfkill unblock wifi 2>/dev/null || true
systemctl start apache2
if [ "$BACKEND" = networkmanager ]; then
nmcli device disconnect "$IFACE" >/dev/null 2>&1 || true
nmcli connection add type wifi ifname "$IFACE" con-name "$NM_HOTSPOT" ssid "$PIVILION_HOTSPOT_SSID"
nmcli connection modify "$NM_HOTSPOT" 802-11-wireless.mode ap 802-11-wireless.band bg \
802-11-wireless.channel "$PIVILION_HOTSPOT_CHANNEL" \
@ -209,11 +244,12 @@ start_hotspot() {
fi
systemctl restart pivilion-dnsmasq.service
apply_firewall
if /usr/local/lib/pivilion/diagnose.sh --mode hotspot --quiet; then
if wait_for_diagnostics hotspot; then
pivilion_write_state hotspot "$BACKEND" healthy none
else
pivilion_write_state hotspot "$BACKEND" degraded "Post-start captive portal diagnostics failed"
logger -t pivilion-mode "Hotspot active but degraded; run pivilion diagnose" || true
pivilion_write_state none "$BACKEND" failed "Post-start captive portal diagnostics failed"
refresh_login_banner
return "$PIVILION_E_CAPTIVE"
fi
refresh_login_banner
/usr/local/lib/pivilion/config-sync.sh sync-status || logger -t pivilion-mode "Could not mirror hotspot status to the boot partition"
@ -235,11 +271,12 @@ start_onion() {
systemctl start apache2
restart_tor
update_torname || failure="${failure:+$failure; }Tor did not produce a valid hostname; retained previous value"
if [ -z "$failure" ] && /usr/local/lib/pivilion/diagnose.sh --mode onion --quiet; then
if [ -z "$failure" ] && wait_for_diagnostics onion; then
pivilion_write_state onion "$BACKEND" healthy none
else
pivilion_write_state onion "$BACKEND" degraded "${failure:-Onion diagnostics failed}"
logger -t pivilion-mode "Onion mode active but degraded: ${failure:-diagnostics failed}" || true
pivilion_write_state none "$BACKEND" failed "${failure:-Onion diagnostics failed}"
refresh_login_banner
return "$PIVILION_E_TOR"
fi
refresh_login_banner
/usr/local/lib/pivilion/config-sync.sh sync-status || logger -t pivilion-mode "Could not mirror onion status to the boot partition"