From 4ad82a1a6681edcbe79b0604546ae8999643bb08 Mon Sep 17 00:00:00 2001 From: Timothy Hofland Date: Mon, 16 Mar 2026 08:42:26 +0100 Subject: [PATCH] fix: refactor networking scripts for improved robustness and clarity --- backend/index.js | 6 +- scripts/setup.sh | 202 +++++++++++++++++++++++++----------- scripts/switch-to-ap.sh | 23 ++-- scripts/switch-to-client.sh | 19 ++-- 4 files changed, 167 insertions(+), 83 deletions(-) diff --git a/backend/index.js b/backend/index.js index 9561fd6..ce42491 100644 --- a/backend/index.js +++ b/backend/index.js @@ -9,6 +9,8 @@ const fs = require('fs'); const path = require('path'); const multer = require('multer'); +const scriptsDir = path.join(__dirname, '../scripts'); + const app = express(); app.use(express.json()); app.use(express.static(path.join(__dirname, '../frontend/dist'))); @@ -120,14 +122,14 @@ app.post('/media/assign', (req, res) => { // Networking API app.post('/network/ap', (req, res) => { - exec('bash scripts/switch-to-ap.sh', (err, stdout) => { + exec(`bash "${path.join(scriptsDir, 'switch-to-ap.sh')}"`, (err, stdout) => { if (err) return res.status(500).json({ error: err.message }); res.json({ status: 'switching to ap', output: stdout }); }); }); app.post('/network/client', (req, res) => { - exec('bash scripts/switch-to-client.sh', (err, stdout) => { + exec(`bash "${path.join(scriptsDir, 'switch-to-client.sh')}"`, (err, stdout) => { if (err) return res.status(500).json({ error: err.message }); res.json({ status: 'switching to client', output: stdout }); }); diff --git a/scripts/setup.sh b/scripts/setup.sh index cfa7b9b..7e02fb0 100644 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -3,9 +3,134 @@ # MPVJ Headless Setup Script (Final Polish & Robustness) # Usage: curl -sSL /setup.sh | sudo bash -s -- -set -e +set -euo pipefail -REPO_URL=$1 +REPO_URL=${1:-} + +AP_ADDRESS="192.168.4.1/24" +AP_GATEWAY="192.168.4.1" +AP_DHCP_RANGE_START="192.168.4.20" +AP_DHCP_RANGE_END="192.168.4.150" + +set_wifi_country() { + if command -v raspi-config > /dev/null 2>&1; then + raspi-config nonint do_wifi_country "$WIFI_COUNTRY" || true + fi + + mkdir -p /etc/wpa_supplicant + if [ -f /etc/wpa_supplicant/wpa_supplicant.conf ]; then + if grep -q '^country=' /etc/wpa_supplicant/wpa_supplicant.conf; then + sed -i "s/^country=.*/country=$WIFI_COUNTRY/" /etc/wpa_supplicant/wpa_supplicant.conf + else + sed -i "1icountry=$WIFI_COUNTRY" /etc/wpa_supplicant/wpa_supplicant.conf + fi + else + cat < /etc/wpa_supplicant/wpa_supplicant.conf +country=$WIFI_COUNTRY +ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev +update_config=1 +EOF + fi + + if [ -f /etc/default/crda ]; then + if grep -q '^REGDOMAIN=' /etc/default/crda; then + sed -i "s/^REGDOMAIN=.*/REGDOMAIN=$WIFI_COUNTRY/" /etc/default/crda + else + echo "REGDOMAIN=$WIFI_COUNTRY" >> /etc/default/crda + fi + fi + + if command -v iw > /dev/null 2>&1; then + iw reg set "$WIFI_COUNTRY" || true + fi +} + +configure_wlan0_static_profile() { + if [ -f /etc/dhcpcd.conf ]; then + sed -i '/# BEGIN MPVJ AP/,/# END MPVJ AP/d' /etc/dhcpcd.conf + cat <> /etc/dhcpcd.conf + +# BEGIN MPVJ AP +interface wlan0 + static ip_address=$AP_ADDRESS + nohook wpa_supplicant +# END MPVJ AP +EOF + fi +} + +configure_access_point_stack() { + mkdir -p /etc/hostapd /etc/systemd/system /etc/NetworkManager/conf.d + + cat < /etc/hostapd/hostapd.conf +country_code=$WIFI_COUNTRY +interface=wlan0 +driver=nl80211 +ssid=$WIFI_SSID +hw_mode=g +channel=7 +ieee80211n=1 +wmm_enabled=1 +auth_algs=1 +ignore_broadcast_ssid=0 +wpa=2 +wpa_passphrase=$WIFI_PASS +wpa_key_mgmt=WPA-PSK +rsn_pairwise=CCMP +EOF + + cat < /etc/default/hostapd +DAEMON_CONF="/etc/hostapd/hostapd.conf" +EOF + + if [ -f /etc/dnsmasq.conf ] && [ ! -f /etc/dnsmasq.conf.mpvj-backup ]; then + cp /etc/dnsmasq.conf /etc/dnsmasq.conf.mpvj-backup + fi + + cat < /etc/dnsmasq.conf +interface=wlan0 +bind-interfaces +listen-address=$AP_GATEWAY +domain-needed +bogus-priv +dhcp-range=$AP_DHCP_RANGE_START,$AP_DHCP_RANGE_END,255.255.255.0,24h +address=/$HOSTNAME.local/$AP_GATEWAY +EOF + + cat <<'EOF' > /etc/systemd/system/mpvj-ap-mode.service +[Unit] +Description=Prepare wlan0 for MPVJ access point mode +DefaultDependencies=no +After=systemd-udev-settle.service +Before=network-pre.target hostapd.service dnsmasq.service +Wants=network-pre.target hostapd.service dnsmasq.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/bash -c 'rfkill unblock wlan || true; systemctl stop wpa_supplicant.service wpa_supplicant@wlan0.service 2>/dev/null || true; ip link set wlan0 down || true; ip addr flush dev wlan0 || true; iw dev wlan0 set power_save off || true; ip link set wlan0 up; ip address add 192.168.4.1/24 dev wlan0' +ExecStop=/usr/bin/bash -c 'systemctl stop hostapd.service dnsmasq.service 2>/dev/null || true; ip addr flush dev wlan0 || true' + +[Install] +WantedBy=multi-user.target +EOF + + cat < /etc/NetworkManager/conf.d/99-mpvj-unmanage-wlan0.conf +[keyfile] +unmanaged-devices=interface-name:wlan0 +EOF + + cat < /etc/sysctl.d/90-mpvj-ap.conf +net.ipv4.ip_forward=1 +EOF + + configure_wlan0_static_profile + + systemctl daemon-reload + systemctl unmask hostapd dnsmasq 2>/dev/null || true + systemctl enable mpvj-ap-mode.service hostapd.service dnsmasq.service avahi-daemon.service + systemctl disable wpa_supplicant.service wpa_supplicant@wlan0.service 2>/dev/null || true +} if [ -z "$REPO_URL" ]; then echo "Usage: $0 " @@ -54,8 +179,15 @@ fi echo "Interrogating user for configuration..." # 1.1 Hostname -HOSTNAME=$(whiptail --inputbox "Enter Hostname (e.g. mpvj)" 8 45 "mpvj" --title "Hostname Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 -HOSTNAME=${HOSTNAME:-mpvj} +while true; do + HOSTNAME=$(whiptail --inputbox "Enter Hostname (e.g. mpvj)" 8 45 "mpvj" --title "Hostname Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 + HOSTNAME=${HOSTNAME:-mpvj} + if echo "$HOSTNAME" | grep -qE '^[a-zA-Z0-9-]+$'; then + break + fi + + whiptail --msgbox "Error: Hostname may only contain letters, numbers, and hyphens." 8 60 < /dev/tty +done # 1.2 SSID WIFI_SSID=$(whiptail --inputbox "Enter WiFi SSID (e.g. MPVJ-AP)" 8 45 "MPVJ-AP" --title "WiFi SSID Configuration" 3>&1 1>&2 2>&3 < /dev/tty) || exit 1 @@ -126,6 +258,7 @@ fi # 2.4 Hostname & WiFi Country hostnamectl set-hostname "$HOSTNAME" sed -i "s/127.0.1.1.*/127.0.1.1\t$HOSTNAME/g" /etc/hosts || true +set_wifi_country # 2.5 OS-Agnostic Swap Increase (2GB) echo "Ensuring 2GB Swap for build..." @@ -139,64 +272,7 @@ fi # 3. NETWORKING echo "Configuring Networking..." rfkill unblock wlan || true - -if command -v nmcli > /dev/null; then - echo "NetworkManager detected. Configuring stable hotspot..." - # Clean up existing profiles - nmcli con delete MPVJ-AP 2>/dev/null || true - - # Ensure wlan0 is not connected to anything else (prevents supplicant-timeout) - nmcli device set wlan0 autoconnect no - nmcli device disconnect wlan0 2>/dev/null || true - - # Create the Access Point with specific 2.4GHz settings for Pi 3B stability - nmcli con add type wifi ifname wlan0 mode ap con-name MPVJ-AP autoconnect yes ssid "$WIFI_SSID" - nmcli con modify MPVJ-AP 802-11-wireless.mode ap 802-11-wireless.band bg - nmcli con modify MPVJ-AP ipv4.method shared ipv4.addresses 192.168.4.1/24 - nmcli con modify MPVJ-AP wifi-sec.key-mgmt wpa-psk - nmcli con modify MPVJ-AP wifi-sec.psk "$WIFI_PASS" - nmcli con modify MPVJ-AP connection.autoconnect-priority 100 - - # Ensure legacy services are out of the way - systemctl stop hostapd dnsmasq 2>/dev/null || true - systemctl mask hostapd dnsmasq 2>/dev/null || true - - echo "Stable NetworkManager Hotspot configured." -else - echo "Using legacy hostapd/dnsmasq logic..." - # hostapd - cat < /etc/hostapd/hostapd.conf -interface=wlan0 -driver=nl80211 -ssid=$WIFI_SSID -hw_mode=g -channel=7 -wpa=2 -wpa_passphrase=$WIFI_PASS -wpa_key_mgmt=WPA-PSK -EOF - [ -f /etc/default/hostapd ] && sed -i 's/#DAEMON_CONF=""/DAEMON_CONF="\/etc\/hostapd\/hostapd.conf"/g' /etc/default/hostapd - - # dnsmasq - [ -f /etc/dnsmasq.conf ] && mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak || true - cat < /etc/dnsmasq.conf -interface=wlan0 -dhcp-range=192.168.4.2,192.168.4.20,255.255.255.0,24h -address=/$HOSTNAME.local/192.168.4.1 -EOF - - # dhcpcd - if [ -f /etc/dhcpcd.conf ]; then - if ! grep -q "interface wlan0" /etc/dhcpcd.conf; then - echo -e "\ninterface wlan0\n static ip_address=192.168.4.1/24\n nohook wpa_supplicant" >> /etc/dhcpcd.conf - fi - fi - - systemctl unmask hostapd dnsmasq 2>/dev/null || true - systemctl enable hostapd dnsmasq -fi - -systemctl enable avahi-daemon +configure_access_point_stack # 4. DEPLOYMENT echo "Deploying Application..." @@ -260,5 +336,5 @@ echo "Cleaning up..." swapoff /swapfile_mpvj 2>/dev/null || true rm /swapfile_mpvj 2>/dev/null || true -whiptail --title "Success" --msgbox "INSTALL COMPLETE!\n\nRebooting into AP Mode.\nSSID: $WIFI_SSID\nIP: 192.168.4.1" 12 45 < /dev/tty +whiptail --title "Success" --msgbox "INSTALL COMPLETE!\n\nRebooting into AP Mode.\nHostname: $HOSTNAME.local\nSSID: $WIFI_SSID\nPassword: $WIFI_PASS\nIP: $AP_GATEWAY" 14 60 < /dev/tty reboot diff --git a/scripts/switch-to-ap.sh b/scripts/switch-to-ap.sh index c373edf..88bf148 100644 --- a/scripts/switch-to-ap.sh +++ b/scripts/switch-to-ap.sh @@ -1,14 +1,19 @@ #!/bin/bash -echo "Switching to AP Mode..." -# Stop wpa_supplicant for client mode -systemctl stop wpa_supplicant +set -euo pipefail -# Configure static IP for wlan0 (normally handled by dhcpcd or systemd-networkd) -# ifconfig wlan0 192.168.4.1 +echo "Switching to AP mode..." -# Start AP services -systemctl start hostapd -systemctl start dnsmasq +rfkill unblock wlan || true +systemctl stop wpa_supplicant.service wpa_supplicant@wlan0.service 2>/dev/null || true -echo "AP Mode active: MPVJ-3B" +ip link set wlan0 down || true +ip addr flush dev wlan0 || true +ip link set wlan0 up +ip address add 192.168.4.1/24 dev wlan0 + +systemctl start mpvj-ap-mode.service +systemctl restart hostapd.service +systemctl restart dnsmasq.service + +echo "AP mode active on wlan0 at 192.168.4.1" diff --git a/scripts/switch-to-client.sh b/scripts/switch-to-client.sh index 035f76c..5252f36 100644 --- a/scripts/switch-to-client.sh +++ b/scripts/switch-to-client.sh @@ -1,14 +1,15 @@ #!/bin/bash -echo "Switching to Client Mode..." -# Stop AP services -systemctl stop hostapd -systemctl stop dnsmasq +set -euo pipefail -# Start wpa_supplicant -systemctl start wpa_supplicant +echo "Switching to client mode..." -# Normally would trigger a re-scan or wait for IP -# dhclient wlan0 +systemctl stop mpvj-ap-mode.service 2>/dev/null || true +systemctl stop hostapd.service dnsmasq.service -echo "Client Mode active. Connecting to configured SSID..." +ip addr flush dev wlan0 || true +ip link set wlan0 up || true + +systemctl start wpa_supplicant.service || systemctl start wpa_supplicant@wlan0.service + +echo "Client mode active. wlan0 returned to wpa_supplicant."