feat(kernel): Add launch.sh for streamlined module loading and frontend execution
Introduce `launch.sh` as a single execution entry point for the IEC listener module and Python frontend. Automates kernel module selection, virtual environment setup, and cleanup to simplify usage. Ensures consistency by unloading modules on script exit.
This commit is contained in:
parent
b3d63664a0
commit
dafcdfbd7b
149
launch.sh
Executable file
149
launch.sh
Executable file
@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# launch.sh - load the IEC listener module, run the userspace frontend, unload.
|
||||
#
|
||||
# This is the "run the whole thing" entry point for a released package. Unlike
|
||||
# selftest.sh (a one-shot wiring check that loads and immediately unloads), this
|
||||
# keeps the module loaded for as long as the frontend runs. In order it:
|
||||
# 1. installs the iecpoc Python frontend into a local virtualenv (.venv),
|
||||
# 2. detects the running kernel and picks the matching iec_listener_*.ko,
|
||||
# 3. insmods it (creating /dev/iec0),
|
||||
# 4. runs `iecpoc`, streaming the decoded C64 traffic to the terminal,
|
||||
# 5. on exit (Ctrl-C / EOF / error) ALWAYS rmmods again - the Pi is left
|
||||
# exactly as before.
|
||||
#
|
||||
# The module is never left loaded by this script; the EXIT trap unloads it even
|
||||
# if the frontend crashes or you interrupt it.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./launch.sh [--address N] [--device PATH] [--ko FILE] [-- <iecpoc args>]
|
||||
#
|
||||
# Examples:
|
||||
# sudo ./launch.sh # address 4, /dev/iec0, annotated trace
|
||||
# sudo ./launch.sh --address 8 # listen as device 8
|
||||
# sudo ./launch.sh -- --raw # forward --raw to iecpoc
|
||||
# sudo ./launch.sh --ko modules/iec_listener_1-6.12.93-1+rpt1.ko
|
||||
#
|
||||
# Defaults: address=4 (printer), device=/dev/iec0.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MODULE="iec_listener"
|
||||
ADDRESS=4
|
||||
DEVICE="/dev/iec0"
|
||||
KO=""
|
||||
VENV=".venv"
|
||||
LOADED=0
|
||||
declare -a PASS=()
|
||||
|
||||
# Run relative to the script's own directory so modules/, iecpoc/, pyproject.toml
|
||||
# and .venv resolve the same way whether invoked as ./launch.sh or by full path.
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "$HERE"
|
||||
|
||||
# ---- pretty output (color only on a tty) --------------------------------
|
||||
if [ -t 1 ]; then
|
||||
C_OK=$'\033[32m'; C_ERR=$'\033[31m'; C_INFO=$'\033[36m'; C_RST=$'\033[0m'
|
||||
else
|
||||
C_OK=""; C_ERR=""; C_INFO=""; C_RST=""
|
||||
fi
|
||||
ok() { printf '%s OK %s %s\n' "$C_OK" "$C_RST" "$*"; }
|
||||
info() { printf '%s ==>%s %s\n' "$C_INFO" "$C_RST" "$*"; }
|
||||
die() { printf '%sFAIL%s %s\n' "$C_ERR" "$C_RST" "$*" >&2; exit 1; }
|
||||
|
||||
# ---- always unload on the way out ---------------------------------------
|
||||
cleanup() {
|
||||
if [ "$LOADED" = 1 ] && lsmod | grep -q "^${MODULE}\b"; then
|
||||
info "rmmod ${MODULE} (releases the bus, removes ${DEVICE})"
|
||||
rmmod "$MODULE" 2>/dev/null && ok "unloaded - Pi left as before" \
|
||||
|| info "rmmod failed; unload manually: sudo rmmod ${MODULE}"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ---- args ---------------------------------------------------------------
|
||||
# Anything unrecognised (and everything after a literal --) is forwarded to
|
||||
# iecpoc, so e.g. --raw / --logfile FILE just work.
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-a|--address) ADDRESS="${2:-}"; shift 2 ;;
|
||||
--address=*) ADDRESS="${1#*=}"; shift ;;
|
||||
-d|--device) DEVICE="${2:-}"; shift 2 ;;
|
||||
--device=*) DEVICE="${1#*=}"; shift ;;
|
||||
--ko) KO="${2:-}"; shift 2 ;;
|
||||
--ko=*) KO="${1#*=}"; shift ;;
|
||||
--) shift; PASS+=("$@"); break ;;
|
||||
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) PASS+=("$1"); shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---- preconditions ------------------------------------------------------
|
||||
[ "$(id -u)" -eq 0 ] || die "must run as root (use: sudo $0 ...)"
|
||||
case "$ADDRESS" in ''|*[!0-9]*) die "address must be 0-30 (got '$ADDRESS')" ;; esac
|
||||
{ [ "$ADDRESS" -ge 0 ] && [ "$ADDRESS" -le 30 ]; } || die "address out of range 0-30: $ADDRESS"
|
||||
command -v modinfo >/dev/null || die "modinfo not found (install kmod)"
|
||||
command -v python3 >/dev/null || die "python3 not found (sudo apt install python3)"
|
||||
|
||||
# ---- 1. python frontend: venv + install (once) --------------------------
|
||||
# iecpoc declares no third-party runtime deps, so this is offline-safe except
|
||||
# for the build backend (setuptools), which pip fetches the first time. The
|
||||
# install is skipped on later runs once the entry point exists.
|
||||
[ -f "$HERE/pyproject.toml" ] || die "pyproject.toml not found next to $0; package incomplete?"
|
||||
if [ ! -x "$VENV/bin/iecpoc" ]; then
|
||||
if [ ! -x "$VENV/bin/python" ]; then
|
||||
info "creating virtualenv: $VENV"
|
||||
python3 -m venv "$VENV" || die "could not create venv (sudo apt install python3-venv)"
|
||||
fi
|
||||
info "installing iecpoc frontend into $VENV"
|
||||
"$VENV/bin/pip" install --quiet --disable-pip-version-check . \
|
||||
|| die "pip install failed (needs network for the build backend, or: sudo apt install python3-pip)"
|
||||
ok "frontend installed"
|
||||
fi
|
||||
|
||||
# ---- 2. pick the module matching the running kernel ----------------------
|
||||
# Same vermagic-based selection as selftest.sh: match by what the .ko was built
|
||||
# against (uname -r), not by filename. See selftest.sh for the full rationale.
|
||||
RUNNING="$(uname -r)"
|
||||
if [ -z "$KO" ]; then
|
||||
info "running kernel: $RUNNING"
|
||||
for dir in "$HERE/modules" "$HERE" "$HERE/kernel"; do
|
||||
[ -d "$dir" ] || continue
|
||||
for cand in "$dir"/${MODULE}*.ko; do
|
||||
[ -f "$cand" ] || continue
|
||||
vm="$(modinfo -F vermagic "$cand" 2>/dev/null)" && [ -n "$vm" ] || continue
|
||||
if [ "${vm%% *}" = "$RUNNING" ]; then KO="$cand"; break 2; fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
[ -n "$KO" ] && [ -f "$KO" ] || die \
|
||||
"no module matching the running kernel ($RUNNING) found.
|
||||
Pass one with --ko PATH, or run ./selftest.sh to list what's available."
|
||||
|
||||
VERMAGIC="$(modinfo -F vermagic "$KO" 2>/dev/null)" || die "cannot read vermagic from $KO"
|
||||
[ "${VERMAGIC%% *}" = "$RUNNING" ] || die \
|
||||
"kernel mismatch: module built for '${VERMAGIC%% *}' but running '$RUNNING'."
|
||||
info "module: $KO"
|
||||
|
||||
# ---- 3. load (reload if a stale copy is already in) ---------------------
|
||||
if lsmod | grep -q "^${MODULE}\b"; then
|
||||
info "a copy is already loaded; removing it first"
|
||||
rmmod "$MODULE" || die "rmmod failed (is ${DEVICE} in use?)"
|
||||
fi
|
||||
info "insmod ${MODULE} address=${ADDRESS}"
|
||||
insmod "$KO" address="$ADDRESS" || die "insmod failed; check: dmesg | tail"
|
||||
LOADED=1
|
||||
[ -c "$DEVICE" ] || die "${DEVICE} was not created"
|
||||
ok "loaded; ${DEVICE} present"
|
||||
|
||||
# ---- 4. run the frontend (foreground; rmmod runs in the EXIT trap) -------
|
||||
info "running iecpoc (Ctrl-C to stop) - address ${ADDRESS}, device ${DEVICE}"
|
||||
echo
|
||||
set +e
|
||||
"$VENV/bin/iecpoc" --address "$ADDRESS" --device "$DEVICE" "${PASS[@]}"
|
||||
ST=$?
|
||||
set -e
|
||||
|
||||
echo
|
||||
info "frontend exited (status $ST); unloading module"
|
||||
exit "$ST"
|
||||
Loading…
x
Reference in New Issue
Block a user