Add modern Raspberry Pi OS compatibility

This commit is contained in:
v3d 2026-06-29 09:42:14 +02:00
parent 4636a4f4f6
commit 3b7a16564b
35 changed files with 725 additions and 15678 deletions

View file

@ -0,0 +1,113 @@
#!/bin/bash
# Shared configuration and small helpers for the Pivilion commands.
PIVILION_CONFIG=""
_pivilion_trim() {
local value=$1
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
_pivilion_config_value() {
local key=$1 file line name value
for file in /boot/firmware/pivilion.conf /boot/pivilion.conf /etc/pivilion.conf /etc/pivilion.defaults; do
[ -r "$file" ] || continue
while IFS= read -r line || [ -n "$line" ]; do
line=$(_pivilion_trim "$line")
case "$line" in ''|'#'*) continue ;; esac
name=$(_pivilion_trim "${line%%=*}")
[ "$name" = "$key" ] || continue
value=$(_pivilion_trim "${line#*=}")
case "$value" in
\"*\") value=${value#\"}; value=${value%\"} ;;
\'*\') value=${value#\'}; value=${value%\'} ;;
esac
printf '%s' "$value"
return 0
done < "$file"
done
return 1
}
pivilion_load_config() {
local default_user configured ssid_bytes
default_user=${SUDO_USER:-${USER:-}}
[ -n "$default_user" ] && [ "$default_user" != root ] || default_user=$(id -nu 1000 2>/dev/null || true)
configured=$(_pivilion_config_value PIVILION_USER || true)
PIVILION_USER=${configured:-$default_user}
[ -n "$PIVILION_USER" ] || { echo "Cannot determine PIVILION_USER" >&2; return 1; }
getent passwd "$PIVILION_USER" >/dev/null || { echo "Configured user '$PIVILION_USER' does not exist" >&2; return 1; }
PIVILION_GROUP=$(id -gn "$PIVILION_USER")
configured=$(_pivilion_config_value PIVILION_HOME || true)
PIVILION_HOME=${configured:-$(getent passwd "$PIVILION_USER" | cut -d: -f6)}
configured=$(_pivilion_config_value PIVILION_DIR || true)
PIVILION_DIR=${configured:-$PIVILION_HOME/pivilion}
configured=$(_pivilion_config_value PIVILION_WEBROOT || true)
PIVILION_WEBROOT=${configured:-/var/www/html/pivilion}
configured=$(_pivilion_config_value PIVILION_MODE || true)
PIVILION_MODE=${configured:-onion}
configured=$(_pivilion_config_value PIVILION_HOTSPOT_SSID || true)
PIVILION_HOTSPOT_SSID=${configured:-pivilion}
configured=$(_pivilion_config_value PIVILION_WIFI_INTERFACE || true)
PIVILION_WIFI_INTERFACE=${configured:-}
case "$PIVILION_MODE" in onion|hotspot) ;; *) echo "PIVILION_MODE must be onion or hotspot" >&2; return 1 ;; esac
ssid_bytes=$(LC_ALL=C printf '%s' "$PIVILION_HOTSPOT_SSID" | wc -c)
[ "$ssid_bytes" -ge 1 ] && [ "$ssid_bytes" -le 32 ] || {
echo "PIVILION_HOTSPOT_SSID must contain 1-32 bytes" >&2; return 1;
}
case "$PIVILION_HOME$PIVILION_DIR$PIVILION_WEBROOT$PIVILION_WIFI_INTERFACE$PIVILION_HOTSPOT_SSID" in
*$'\n'*|*$'\r'*) echo "Invalid control character in Pivilion configuration" >&2; return 1 ;;
esac
case "$PIVILION_HOME:$PIVILION_DIR:$PIVILION_WEBROOT" in
/*:/*:/*) ;; *) echo "Pivilion paths must be absolute" >&2; return 1 ;;
esac
[[ "$PIVILION_HOME" =~ ^/[A-Za-z0-9._/+:-]+$ && "$PIVILION_DIR" =~ ^/[A-Za-z0-9._/+:-]+$ && "$PIVILION_WEBROOT" =~ ^/[A-Za-z0-9._/+:-]+$ ]] || {
echo "Pivilion paths contain unsupported characters" >&2; return 1;
}
[ -z "$PIVILION_WIFI_INTERFACE" ] || [[ "$PIVILION_WIFI_INTERFACE" =~ ^[A-Za-z0-9._:-]+$ ]] || {
echo "Invalid PIVILION_WIFI_INTERFACE" >&2; return 1;
}
export PIVILION_USER PIVILION_GROUP PIVILION_HOME PIVILION_DIR PIVILION_WEBROOT PIVILION_MODE PIVILION_HOTSPOT_SSID PIVILION_WIFI_INTERFACE
}
pivilion_wifi_interface() {
local iface
if [ -n "${PIVILION_WIFI_INTERFACE:-}" ]; then printf '%s\n' "$PIVILION_WIFI_INTERFACE"; return; fi
if command -v nmcli >/dev/null 2>&1; then
iface=$(nmcli -t -f DEVICE,TYPE device status 2>/dev/null | awk -F: '$2=="wifi" {print $1; exit}')
[ -n "$iface" ] && { printf '%s\n' "$iface"; return; }
fi
if command -v iw >/dev/null 2>&1; then
iface=$(iw dev 2>/dev/null | awk '$1=="Interface" {print $2; exit}')
[ -n "$iface" ] && { printf '%s\n' "$iface"; return; }
fi
iface=$(find /sys/class/net -maxdepth 1 -type l -name 'wl*' -printf '%f\n' 2>/dev/null | head -n1)
[ -n "$iface" ] && printf '%s\n' "$iface"
}
pivilion_uses_networkmanager() {
command -v nmcli >/dev/null 2>&1 && systemctl is-active --quiet NetworkManager
}
pivilion_set_mode() {
local mode=$1
case "$mode" in onion|hotspot) ;; *) return 2 ;; esac
install -d -m 0755 -o "$PIVILION_USER" -g "$PIVILION_GROUP" "$PIVILION_DIR"
printf '%s\n' "$mode" | install -m 0644 -o "$PIVILION_USER" -g "$PIVILION_GROUP" /dev/stdin "$PIVILION_DIR/mode"
}
pivilion_current_mode() {
local mode
mode=$(head -n1 "$PIVILION_DIR/mode" 2>/dev/null || true)
case "$mode" in onion|hotspot) printf '%s\n' "$mode" ;; *) printf '%s\n' "$PIVILION_MODE" ;; esac
}
pivilion_require_root() {
[ "$(id -u)" -eq 0 ] || { echo "This operation must run as root (use sudo)." >&2; return 1; }
}

138
usr/local/lib/pivilion/mode.sh Executable file
View file

@ -0,0 +1,138 @@
#!/bin/bash
set -euo pipefail
source /usr/local/lib/pivilion/common.sh
pivilion_require_root
pivilion_load_config
MODE=${1:-$(pivilion_current_mode)}
IFACE=$(pivilion_wifi_interface)
[ -n "$IFACE" ] || { echo "No Wi-Fi interface found; set PIVILION_WIFI_INTERFACE" >&2; exit 1; }
RUN_DIR=/run/pivilion
DNSMASQ_CONF=/etc/dnsmasq.d/pivilion-runtime.conf
NM_HOTSPOT=pivilion-hotspot
stop_hotspot() {
systemctl stop pivilion-dnsmasq.service 2>/dev/null || true
rm -f "$DNSMASQ_CONF"
nft delete table inet pivilion 2>/dev/null || true
if pivilion_uses_networkmanager; then
nmcli connection down "$NM_HOTSPOT" 2>/dev/null || true
nmcli connection delete "$NM_HOTSPOT" 2>/dev/null || true
else
systemctl stop hostapd 2>/dev/null || true
ip address flush dev "$IFACE" 2>/dev/null || true
fi
}
write_dnsmasq_config() {
install -d -m 0755 /etc/dnsmasq.d
cat > "$DNSMASQ_CONF" <<EOF
interface=$IFACE
bind-dynamic
listen-address=10.1.1.1
dhcp-range=10.1.1.2,10.1.1.254,255.255.255.0,72h
dhcp-option=option:router,10.1.1.1
dhcp-authoritative
address=/#/10.1.1.1
EOF
}
start_firewall() {
install -d -m 0755 "$RUN_DIR"
cat > "$RUN_DIR/pivilion.nft" <<EOF
table inet pivilion {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "$IFACE" udp dport 53 redirect to :53
iifname "$IFACE" tcp dport 53 redirect to :53
iifname "$IFACE" tcp dport 80 redirect to :80
}
}
EOF
nft delete table inet pivilion 2>/dev/null || true
nft -f "$RUN_DIR/pivilion.nft"
}
start_hotspot() {
stop_hotspot
rfkill unblock wifi 2>/dev/null || true
systemctl start apache2
if pivilion_uses_networkmanager; then
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 \
ipv4.method manual ipv4.addresses 10.1.1.1/24 ipv4.never-default yes ipv6.method disabled
nmcli connection up "$NM_HOTSPOT"
else
systemctl stop "wpa_supplicant@$IFACE.service" 2>/dev/null || true
command -v dhcpcd >/dev/null 2>&1 && dhcpcd -k "$IFACE" 2>/dev/null || true
ip link set "$IFACE" down
ip address flush dev "$IFACE"
ip address add 10.1.1.1/24 dev "$IFACE"
ip link set "$IFACE" up
install -d -m 0755 /etc/hostapd
cat > /etc/hostapd/hostapd.conf <<EOF
interface=$IFACE
driver=nl80211
ssid=$PIVILION_HOTSPOT_SSID
hw_mode=g
channel=6
auth_algs=1
wmm_enabled=0
EOF
systemctl unmask hostapd 2>/dev/null || true
systemctl restart hostapd
fi
write_dnsmasq_config
dnsmasq --test --conf-file="$DNSMASQ_CONF"
systemctl restart pivilion-dnsmasq.service
start_firewall
echo "Pivilion hotspot '$PIVILION_HOTSPOT_SSID' is active on $IFACE (10.1.1.1)."
}
update_torname() {
local source=/var/lib/tor/pivilion/hostname hostname tmp i
for i in $(seq 1 30); do
if [ -s "$source" ]; then
hostname=$(tr -d '\r\n' < "$source")
if [[ ! "$hostname" =~ ^[a-z2-7]{56}\.onion$ ]]; then
echo "Tor produced an invalid v3 onion hostname." >&2
return 1
fi
tmp=$(mktemp "$PIVILION_DIR/.torname.XXXXXX")
printf '%s\n' "$hostname" > "$tmp"
chown "$PIVILION_USER:$PIVILION_GROUP" "$tmp"
chmod 0644 "$tmp"
mv -f "$tmp" "$PIVILION_DIR/torname"
return 0
fi
sleep 1
done
echo "Tor started, but no onion hostname was generated within 30 seconds." >&2
return 1
}
start_onion() {
stop_hotspot
if pivilion_uses_networkmanager; then
nmcli connection up pivilion-onion 2>/dev/null || echo "No saved Pivilion Wi-Fi connection; run pikey." >&2
else
systemctl restart dhcpcd 2>/dev/null || true
systemctl restart "wpa_supplicant@$IFACE.service" 2>/dev/null || systemctl restart wpa_supplicant 2>/dev/null || true
fi
rm -f "$PIVILION_WEBROOT/gen/.htaccess"
systemctl start apache2
if systemctl cat tor@default.service >/dev/null 2>&1; then
systemctl restart tor@default.service
else
systemctl restart tor.service
fi
update_torname || true
echo "Pivilion onion mode is active."
}
case "$MODE" in
hotspot) start_hotspot ;;
onion) start_onion ;;
*) echo "Unknown Pivilion mode: $MODE" >&2; exit 2 ;;
esac