Compare commits
4
Commits
50cf0bef1d
...
a66b1e6079
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a66b1e6079 | ||
|
|
5cd2117a94 | ||
|
|
f7901a3684 | ||
|
|
b0c0008d44 |
+16
-2
@@ -94,8 +94,17 @@ provision_install_packages() {
|
|||||||
|
|
||||||
provision_create_kiosk_user() {
|
provision_create_kiosk_user() {
|
||||||
echo "[2/10] Creating 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
|
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
|
echo "$KIOSK_USER:kiosk" | sudo chpasswd
|
||||||
log_success "Kiosk user created (default password: kiosk - change it)"
|
log_success "Kiosk user created (default password: kiosk - change it)"
|
||||||
else
|
else
|
||||||
@@ -129,7 +138,12 @@ provision_install_app() {
|
|||||||
echo "[4/10] Installing kiosk app..."
|
echo "[4/10] Installing kiosk app..."
|
||||||
# *.mjs: pdf-render.mjs (pdf.js only ships as a JavaScript module).
|
# *.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 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"
|
sudo chmod +x "$KIOSK_DIR/start.sh"
|
||||||
|
|
||||||
if offline_mode_active; then
|
if offline_mode_active; then
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ baresip_installed_version() {
|
|||||||
# `apt install baresip` with no configured extension isn't something
|
# `apt install baresip` with no configured extension isn't something
|
||||||
# this menu should call done.
|
# this menu should call done.
|
||||||
baresip_is_installed() {
|
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() {
|
baresip_is_running() {
|
||||||
@@ -78,9 +80,9 @@ addon_asterisk_intercom_status() {
|
|||||||
else
|
else
|
||||||
echo "Asterisk Intercom: Installed (v${ver:-unknown}) - Not running"
|
echo "Asterisk Intercom: Installed (v${ver:-unknown}) - Not running"
|
||||||
fi
|
fi
|
||||||
if [[ -f "$BARESIP_CONFIG_DIR/accounts" ]]; then
|
if sudo test -f "$BARESIP_CONFIG_DIR/accounts"; then
|
||||||
local account
|
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:}"
|
local extension="${account#<sip:}"
|
||||||
extension="${extension%%@*}"
|
extension="${extension%%@*}"
|
||||||
[[ -n "$extension" ]] && echo " Extension: $extension"
|
[[ -n "$extension" ]] && echo " Extension: $extension"
|
||||||
|
|||||||
+120
-38
@@ -20,13 +20,89 @@
|
|||||||
# is exercised by hand against real hardware only.
|
# is exercised by hand against real hardware only.
|
||||||
#
|
#
|
||||||
# Unlike the other migrated menus, this one has no sub-options - it's a
|
# 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
|
# single linear wizard, same as the legacy configure_wifi. wifi_menu runs
|
||||||
# IS the action, not a run_menu wrapper.
|
# 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.
|
# Depends on: lib/menu.sh, lib/config.sh being sourced first.
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
WIFI_NETPLAN_FILE="$NETPLAN_DIR/60-kiosk-wifi.yaml"
|
||||||
|
|
||||||
wifi_menu() {
|
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
|
||||||
echo " ═══ WIFI CONFIGURATION ═══"
|
echo " ═══ WIFI CONFIGURATION ═══"
|
||||||
echo
|
echo
|
||||||
@@ -55,11 +131,11 @@ wifi_menu() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local wifi_iface
|
local wifi_iface
|
||||||
wifi_iface=$(ls /sys/class/net 2>/dev/null | grep -E "^wl" | head -1)
|
if ! wifi_iface=$(wifi_detect_iface); then
|
||||||
|
|
||||||
if [[ -z "$wifi_iface" ]]; then
|
|
||||||
log_warning "No WiFi hardware detected"
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -74,9 +150,11 @@ wifi_menu() {
|
|||||||
|
|
||||||
ask_yes_no "Configure WiFi?" "n" || return 0
|
ask_yes_no "Configure WiFi?" "n" || return 0
|
||||||
|
|
||||||
|
wifi_unblock
|
||||||
echo "Bringing up interface..."
|
echo "Bringing up interface..."
|
||||||
if ! sudo ip link set "$wifi_iface" up 2>/dev/null; then
|
local link_err
|
||||||
log_error "Failed to bring up interface"
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
sleep 3
|
sleep 3
|
||||||
@@ -161,15 +239,9 @@ apply_wifi_config() {
|
|||||||
local ssid="$2"
|
local ssid="$2"
|
||||||
local password="$3"
|
local password="$3"
|
||||||
|
|
||||||
# `|| true`: under set -e + pipefail (this whole tool runs under both),
|
# Our own file, merged by netplan with the others (wired config etc).
|
||||||
# `ls` matching nothing exits non-zero even with stderr silenced, which
|
local netplan_file="$WIFI_NETPLAN_FILE"
|
||||||
# would abort this function outright instead of falling through to the
|
sudo mkdir -p "$NETPLAN_DIR"
|
||||||
# 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"
|
|
||||||
|
|
||||||
local backup=""
|
local backup=""
|
||||||
if [[ -f "$netplan_file" ]]; then
|
if [[ -f "$netplan_file" ]]; then
|
||||||
@@ -190,29 +262,31 @@ network:
|
|||||||
dhcp6: false
|
dhcp6: false
|
||||||
optional: true
|
optional: true
|
||||||
access-points:
|
access-points:
|
||||||
"$ssid":
|
$(wifi_yaml_quote "$ssid"):
|
||||||
password: "$password"
|
password: $(wifi_yaml_quote "$password")
|
||||||
EOF
|
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
|
local watchdog
|
||||||
watchdog=$(mktemp --suffix=.sh)
|
watchdog=$(mktemp --suffix=.sh)
|
||||||
cat > "$watchdog" <<'WATCHEOF'
|
cat > "$watchdog" <<'WATCHEOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# $1 = backup ("" if none), $2 = netplan file, $3 = WiFi interface
|
||||||
sleep 60
|
sleep 60
|
||||||
if [[ -f "$1" && -f "$2" ]]; then
|
if ! ip -4 addr show dev "$3" 2>/dev/null | grep -q "inet "; then
|
||||||
ip=$(hostname -I | awk '{print $1}')
|
if [[ -n "$1" && -f "$1" ]]; then cp "$1" "$2"; else rm -f "$2"; fi
|
||||||
if [[ -z "$ip" ]] || ! ping -c 2 8.8.8.8 >/dev/null 2>&1; then
|
netplan apply 2>/dev/null
|
||||||
cp "$1" "$2"
|
echo "WiFi config reverted - $3 got no address" | wall
|
||||||
netplan apply 2>/dev/null
|
|
||||||
echo "WiFi config reverted - connection failed" | wall
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
rm -f "$0"
|
rm -f "$0"
|
||||||
WATCHEOF
|
WATCHEOF
|
||||||
chmod +x "$watchdog"
|
chmod +x "$watchdog"
|
||||||
nohup sudo bash "$watchdog" "$backup" "$netplan_file" >/dev/null 2>&1 &
|
nohup sudo bash "$watchdog" "$backup" "$netplan_file" "$wifi_iface" >/dev/null 2>&1 &
|
||||||
echo "Watchdog started - will revert in 60s if the connection fails"
|
echo "Watchdog started - will revert in 60s if $wifi_iface gets no address"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sudo cp "$temp_plan" "$netplan_file"
|
sudo cp "$temp_plan" "$netplan_file"
|
||||||
@@ -224,22 +298,30 @@ WATCHEOF
|
|||||||
netplan_log=$(mktemp)
|
netplan_log=$(mktemp)
|
||||||
if sudo netplan apply > "$netplan_log" 2>&1; then
|
if sudo netplan apply > "$netplan_log" 2>&1; then
|
||||||
cat "$netplan_log"
|
cat "$netplan_log"
|
||||||
sleep 10
|
echo "Waiting for $wifi_iface to connect (up to 30 seconds)..."
|
||||||
local new_ip
|
local _
|
||||||
new_ip=$(get_ip_address)
|
for _ in {1..30}; do
|
||||||
if [[ -n "$new_ip" && "$new_ip" != "No IP" ]]; then
|
wifi_iface_has_ip "$wifi_iface" && break
|
||||||
log_success "Connected: $ssid ($new_ip)"
|
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"
|
[[ -n "${SSH_CONNECTION:-}" ]] && echo "Connection successful - watchdog will not revert"
|
||||||
else
|
else
|
||||||
log_warning "Config applied but no IP yet"
|
log_warning "Config applied but $wifi_iface has no address yet"
|
||||||
echo "Check: sudo journalctl -u systemd-networkd -f"
|
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
|
fi
|
||||||
else
|
else
|
||||||
log_error "netplan apply failed"
|
log_error "netplan apply failed"
|
||||||
echo "Error log:"
|
echo "Error log:"
|
||||||
cat "$netplan_log"
|
cat "$netplan_log"
|
||||||
if [[ -n "$backup" ]] && ask_yes_no "Restore backup?" "y"; then
|
if ask_yes_no "Undo this WiFi change?" "y"; then
|
||||||
sudo cp "$backup" "$netplan_file"
|
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
|
# Last resort after everything else failed: report, don't crash
|
||||||
# the session if even the restore-and-reapply doesn't work.
|
# the session if even the restore-and-reapply doesn't work.
|
||||||
if sudo netplan apply; then
|
if sudo netplan apply; then
|
||||||
|
|||||||
Reference in New Issue
Block a user