#!/bin/sh
# wpa_supplicant, as s6 supervises it. Exec'd by s6-supervise, never by hand.
#
# A LONGRUN, AND NO -B, and that pair is the whole point of this file.
#
# This was a oneshot whose `up` backgrounded a subshell and returned at once,
# so that the 15s wait for wlan0 and the 20s wait to associate did not stall
# the boot transition firmwareExe blocks on. The comment there was explicit
# that nothing could depend on it, which was true and not the problem. The
# problem is that the subshell it left behind was a child of
# s6rc-oneshot-runner with its stdout on a pipe s6-rc closes the moment the up
# script returns -- so a `sleep 1` in the wlan0 wait, then an `echo`, is a
# write to a closed pipe.
#
# OBSERVED: a printer that booted with a healthy supervision tree, `s6-rc`
# reporting wifi up, no wlan0 configured, no lease, and not one line anywhere
# saying why -- HelixScreen calling it "wifi system is not ready" because
# there was no supplicant to hold a control socket. The subshell was never
# caught in the act, and could not be: once its pipe is gone it has no way to
# report its own death. Which is the argument. A oneshot that forks and
# returns cannot be relied on and cannot be debugged when it isn't.
#
# A longrun has neither half of that. Blocking IS the job, s6-supervise holds
# the process, and a supplicant that dies is restarted rather than mourned.
MODDIR=/usr/data/anvil
IFACE=wlan0

# The saved networks. FlashForge's file: the mod neither ships nor touches it,
# so whatever their UI saved is still what this reads.
CONF=/usr/prog/wifi/wpa_supplicant.conf

# anvil-env.sh is read HERE, not inherited. s6-supervise starts this script
# from the SCANNER's environment, which is firmwareExe's at boot but whatever
# an ssh session had when the service is restarted by hand.
[ -f $MODDIR/anvil-env.sh ] && . $MODDIR/anvil-env.sh

# The same log the action script writes, so association and address are one
# story in one file. Appending, so a respawn cannot erase the reason for it.
mkdir -p /usr/data/logs
exec >>/usr/data/logs/anvil-wifi.log 2>&1
echo "=== wifi `date 2>/dev/null` ==="

# app_startup.sh insmods 8821cu.ko just before it runs firmwareExe, so on a
# normal boot wlan0 is already here; the wait covers a slow USB enumeration
# and the restart typed over ssh. A run script cannot decline to run -- s6
# respawns it -- so a missing interface is reported and slept on rather than
# exec'd into a loop that fills the log once a second.
n=0
while [ $n -lt 30 ] && [ ! -d /sys/class/net/$IFACE ]; do
    sleep 1
    n=$((n + 1))
done
if [ ! -d /sys/class/net/$IFACE ]; then
    echo "wifi: !! $IFACE never appeared -- is 8821cu.ko loaded?"
    sleep 5
    exit 1
fi

# A MISSING CONFIG IS NOT PERMANENT, so this retries slowly instead of giving
# up the way the oneshot did. It appears the first time a network is saved.
# Note the standing chicken-and-egg, unchanged by this file and not ours to
# fix here: a printer with no config has no supplicant, so it has no control
# socket for HelixScreen to add a network through either.
if [ ! -f "$CONF" ]; then
    echo "wifi: !! no $CONF -- no saved network to join"
    sleep 60
    exit 1
fi

# s6-supervise runs one copy of this at a time, so any supplicant answering
# now is a leftover -- one started by hand over ssh, or by the rescue block an
# older firmwareExe carried. Two supplicants on one interface fight over the
# association and the loser's retries look like a flapping AP.
killall wpa_supplicant 2>/dev/null
n=0
while [ $n -lt 15 ] && { pgrep -f wpa_supplicant >/dev/null 2>&1 \
      || ps 2>/dev/null | grep wpa_supplican[t] >/dev/null; }; do
    sleep 1
    n=$((n + 1))
done

/sbin/ifconfig $IFACE up 2>/dev/null
mkdir -p /var/run/wpa_supplicant

# NO -B: with it, s6 would hold the parent that forked and exited, call the
# service dead and respawn it for ever beside a real supplicant supervised by
# nobody -- the same trap nginx's `daemon off;` avoids.
#
# nl80211 first, wext as the fallback: the 8821cu is a Realtek USB part and
# which one it registers varies by build.
#
# And `exec`, so the SIGTERM behind `s6-rc -d change wifi` reaches the
# supplicant rather than a shell that ignores it.
exec /usr/sbin/wpa_supplicant -i $IFACE -c "$CONF" -D nl80211,wext
