Merge pull request #119 from outis1one/claude/happy-knuth-hwnvbn

Claude/happy knuth hwnvbn
This commit is contained in:
Outis
2026-10-09 13:26:37 -04:00
committed by GitHub
3 changed files with 141 additions and 43 deletions
+16 -2
View File
@@ -94,8 +94,17 @@ provision_install_packages() {
provision_create_kiosk_user() {
echo "[2/10] Creating kiosk user..."
local kiosk_groups=(audio video input plugdev netdev)
# Not every Ubuntu release ships all of these - 26.04 Server has no
# netdev group - and useradd refuses to create the user at all if any
# -G group is missing. Create missing ones as plain system groups
# (no-op when they exist).
local g
for g in "${kiosk_groups[@]}"; do
sudo groupadd -f -r "$g"
done
if ! id "$KIOSK_USER" &>/dev/null; then
sudo useradd -m -s /bin/bash -G audio,video,input,plugdev,netdev "$KIOSK_USER"
sudo useradd -m -s /bin/bash -G "$(IFS=,; echo "${kiosk_groups[*]}")" "$KIOSK_USER"
echo "$KIOSK_USER:kiosk" | sudo chpasswd
log_success "Kiosk user created (default password: kiosk - change it)"
else
@@ -129,7 +138,12 @@ provision_install_app() {
echo "[4/10] Installing kiosk app..."
# *.mjs: pdf-render.mjs (pdf.js only ships as a JavaScript module).
sudo cp "$KIOSK_APP_SRC"/*.js "$KIOSK_APP_SRC"/*.mjs "$KIOSK_APP_SRC"/*.html "$KIOSK_APP_SRC/package.json" "$KIOSK_APP_SRC/start.sh" "$KIOSK_DIR/"
sudo chown "$KIOSK_USER:$KIOSK_USER" "$KIOSK_DIR"/*.js "$KIOSK_DIR"/*.mjs "$KIOSK_DIR"/*.html "$KIOSK_DIR/package.json" "$KIOSK_DIR/start.sh"
# Not `chown "$KIOSK_DIR"/*.js ...`: that glob is expanded by *this*
# (admin) shell before sudo runs, and the kiosk user's home is private
# (mode 750 on 26.04), so it can't be listed - the pattern stays literal
# and chown fails. find runs as root. Top level only: node_modules is
# already the kiosk user's, and chrome-sandbox inside it must stay root's.
sudo find "$KIOSK_DIR" -maxdepth 1 -type f -exec chown "$KIOSK_USER:$KIOSK_USER" {} +
sudo chmod +x "$KIOSK_DIR/start.sh"
if offline_mode_active; then
+5 -3
View File
@@ -62,7 +62,9 @@ baresip_installed_version() {
# `apt install baresip` with no configured extension isn't something
# this menu should call done.
baresip_is_installed() {
command -v baresip &>/dev/null && [[ -f "$BARESIP_CONFIG_DIR/accounts" ]]
# sudo: it's under the kiosk user's home, which the admin running this
# can't look inside (mode 750) - a plain [[ -f ]] is always false there.
command -v baresip &>/dev/null && sudo test -f "$BARESIP_CONFIG_DIR/accounts"
}
baresip_is_running() {
@@ -78,9 +80,9 @@ addon_asterisk_intercom_status() {
else
echo "Asterisk Intercom: Installed (v${ver:-unknown}) - Not running"
fi
if [[ -f "$BARESIP_CONFIG_DIR/accounts" ]]; then
if sudo test -f "$BARESIP_CONFIG_DIR/accounts"; then
local account
account=$(head -1 "$BARESIP_CONFIG_DIR/accounts" 2>/dev/null)
account=$(sudo head -1 "$BARESIP_CONFIG_DIR/accounts" 2>/dev/null)
local extension="${account#<sip:}"
extension="${extension%%@*}"
[[ -n "$extension" ]] && echo " Extension: $extension"
+118 -36
View File
@@ -20,13 +20,89 @@
# is exercised by hand against real hardware only.
#
# Unlike the other migrated menus, this one has no sub-options - it's a
# single linear wizard, same as the legacy configure_wifi - so wifi_menu
# IS the action, not a run_menu wrapper.
# single linear wizard, same as the legacy configure_wifi. wifi_menu runs
# it (wifi_configure) and then pauses: run_menu clears the screen as soon
# as a handler returns, which used to wipe every error message here
# before it could be read - the menu just seemed to bounce back.
#
# The WiFi config goes in its own file, $NETPLAN_DIR/60-kiosk-wifi.yaml,
# which netplan merges with the rest - not over the first *.yaml it finds,
# which on a fresh install is the installer's wired config.
#
# Depends on: lib/menu.sh, lib/config.sh being sourced first.
################################################################################
WIFI_NETPLAN_FILE="$NETPLAN_DIR/60-kiosk-wifi.yaml"
wifi_menu() {
local rc=0
wifi_configure || rc=$?
echo
pause
return "$rc"
}
# The first wireless interface, by what it is (it has a wireless/ or
# phy80211 entry in sysfs) rather than by name - adapters aren't always
# named wl*. Falls back to the wl* naming convention.
wifi_detect_iface() {
local d
for d in /sys/class/net/*; do
if [[ -d "$d/wireless" || -e "$d/phy80211" ]]; then
basename "$d"
return 0
fi
done
local name
for d in /sys/class/net/wl*; do
[[ -e "$d" ]] || continue
name=$(basename "$d")
echo "$name"
return 0
done
return 1
}
# Clear a software WiFi block (the rfkill "soft" switch) - with it set,
# `ip link set up` fails with "RF-kill". The rfkill tool isn't installed
# on a server, so via sysfs. A hard block (physical switch / BIOS) can't
# be cleared from here and is reported instead.
wifi_unblock() {
local r type
for r in /sys/class/rfkill/rfkill*; do
[[ -e "$r/type" ]] || continue
type=$(cat "$r/type" 2>/dev/null)
[[ "$type" == "wlan" ]] || continue
if [[ "$(cat "$r/soft" 2>/dev/null)" == "1" ]]; then
echo "WiFi is switched off in software (rfkill) - switching it on..."
echo 0 | sudo tee "$r/soft" >/dev/null || true
fi
if [[ "$(cat "$r/hard" 2>/dev/null)" == "1" ]]; then
log_warning "WiFi is switched off by a hardware switch or the BIOS/UEFI - turn it on there"
fi
done
}
# YAML single-quoted scalar: the only special character inside is the
# quote itself, doubled. Safe for any SSID/password (", \, #, : ...) -
# double quotes broke on a password containing " or \.
wifi_yaml_quote() {
local v="$1"
printf "'%s'" "${v//\'/\'\'}"
}
# IFACE's first IPv4 address (without the /prefix), or nothing.
wifi_iface_ip() {
ip -4 -o addr show dev "$1" 2>/dev/null \
| awk '{for (i = 1; i < NF; i++) if ($i == "inet") { sub("/.*", "", $(i+1)); print $(i+1); exit }}'
}
# True if IFACE has an IPv4 address.
wifi_iface_has_ip() {
ip -4 addr show dev "$1" 2>/dev/null | grep -q "inet "
}
wifi_configure() {
echo
echo " ═══ WIFI CONFIGURATION ═══"
echo
@@ -55,11 +131,11 @@ wifi_menu() {
fi
local wifi_iface
wifi_iface=$(ls /sys/class/net 2>/dev/null | grep -E "^wl" | head -1)
if [[ -z "$wifi_iface" ]]; then
if ! wifi_iface=$(wifi_detect_iface); then
log_warning "No WiFi hardware detected"
echo "If you have a USB WiFi adapter, ensure it's plugged in."
echo "Network interfaces found: $(ls /sys/class/net 2>/dev/null | tr '\n' ' ')"
echo "If you have a USB WiFi adapter, make sure it's plugged in. A built-in"
echo "adapter with no driver shows up in 'lspci -k' / 'lsusb' but not above."
return 1
fi
@@ -74,9 +150,11 @@ wifi_menu() {
ask_yes_no "Configure WiFi?" "n" || return 0
wifi_unblock
echo "Bringing up interface..."
if ! sudo ip link set "$wifi_iface" up 2>/dev/null; then
log_error "Failed to bring up interface"
local link_err
if ! link_err=$(sudo ip link set "$wifi_iface" up 2>&1); then
log_error "Failed to bring up $wifi_iface: ${link_err:-unknown error}"
return 1
fi
sleep 3
@@ -161,15 +239,9 @@ apply_wifi_config() {
local ssid="$2"
local password="$3"
# `|| true`: under set -e + pipefail (this whole tool runs under both),
# `ls` matching nothing exits non-zero even with stderr silenced, which
# would abort this function outright instead of falling through to the
# default filename below. Same class of bug as the run_menu fix in
# lib/menu.sh - masked here in practice because cloud-init almost
# always leaves a *.yaml file behind, but not guaranteed.
local netplan_file
netplan_file=$(ls "$NETPLAN_DIR"/*.yaml 2>/dev/null | head -1) || true
[[ -z "$netplan_file" ]] && netplan_file="$NETPLAN_DIR/50-cloud-init.yaml"
# Our own file, merged by netplan with the others (wired config etc).
local netplan_file="$WIFI_NETPLAN_FILE"
sudo mkdir -p "$NETPLAN_DIR"
local backup=""
if [[ -f "$netplan_file" ]]; then
@@ -190,29 +262,31 @@ network:
dhcp6: false
optional: true
access-points:
"$ssid":
password: "$password"
$(wifi_yaml_quote "$ssid"):
password: $(wifi_yaml_quote "$password")
EOF
if [[ -n "${SSH_CONNECTION:-}" ]] && [[ -n "$backup" ]]; then
if [[ -n "${SSH_CONNECTION:-}" ]]; then
# Reverts if the WiFi interface still has no address after 60s -
# not "can't ping 8.8.8.8", which on a network without internet
# would undo a perfectly good connection every time. No backup
# means there was no WiFi file before: revert = remove ours.
local watchdog
watchdog=$(mktemp --suffix=.sh)
cat > "$watchdog" <<'WATCHEOF'
#!/bin/bash
# $1 = backup ("" if none), $2 = netplan file, $3 = WiFi interface
sleep 60
if [[ -f "$1" && -f "$2" ]]; then
ip=$(hostname -I | awk '{print $1}')
if [[ -z "$ip" ]] || ! ping -c 2 8.8.8.8 >/dev/null 2>&1; then
cp "$1" "$2"
if ! ip -4 addr show dev "$3" 2>/dev/null | grep -q "inet "; then
if [[ -n "$1" && -f "$1" ]]; then cp "$1" "$2"; else rm -f "$2"; fi
netplan apply 2>/dev/null
echo "WiFi config reverted - connection failed" | wall
fi
echo "WiFi config reverted - $3 got no address" | wall
fi
rm -f "$0"
WATCHEOF
chmod +x "$watchdog"
nohup sudo bash "$watchdog" "$backup" "$netplan_file" >/dev/null 2>&1 &
echo "Watchdog started - will revert in 60s if the connection fails"
nohup sudo bash "$watchdog" "$backup" "$netplan_file" "$wifi_iface" >/dev/null 2>&1 &
echo "Watchdog started - will revert in 60s if $wifi_iface gets no address"
fi
sudo cp "$temp_plan" "$netplan_file"
@@ -224,22 +298,30 @@ WATCHEOF
netplan_log=$(mktemp)
if sudo netplan apply > "$netplan_log" 2>&1; then
cat "$netplan_log"
sleep 10
local new_ip
new_ip=$(get_ip_address)
if [[ -n "$new_ip" && "$new_ip" != "No IP" ]]; then
log_success "Connected: $ssid ($new_ip)"
echo "Waiting for $wifi_iface to connect (up to 30 seconds)..."
local _
for _ in {1..30}; do
wifi_iface_has_ip "$wifi_iface" && break
sleep 1
done
if wifi_iface_has_ip "$wifi_iface"; then
log_success "Connected to $ssid: $(wifi_iface_ip "$wifi_iface")"
[[ -n "${SSH_CONNECTION:-}" ]] && echo "Connection successful - watchdog will not revert"
else
log_warning "Config applied but no IP yet"
echo "Check: sudo journalctl -u systemd-networkd -f"
log_warning "Config applied but $wifi_iface has no address yet"
echo "Wrong password or network name is the usual cause. Details:"
echo " sudo journalctl -b -u 'netplan-wpa-*' -u systemd-networkd -n 30 --no-pager"
fi
else
log_error "netplan apply failed"
echo "Error log:"
cat "$netplan_log"
if [[ -n "$backup" ]] && ask_yes_no "Restore backup?" "y"; then
if ask_yes_no "Undo this WiFi change?" "y"; then
if [[ -n "$backup" ]]; then
sudo cp "$backup" "$netplan_file"
else
sudo rm -f "$netplan_file"
fi
# Last resort after everything else failed: report, don't crash
# the session if even the restore-and-reapply doesn't work.
if sudo netplan apply; then