mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 00:26:29 +00:00
fix(on-device-install): persistent install path + syslog-based logging
Bundles the install-time hygiene work for issues #268 and #250. # Install location — #268 Stock SoundTouch rootfs has only a few MB free (~4 MB on the ST20 the reporter captured); the AfterTouch binary is ~12 MB. The previous flow downloaded into tmpfs (/media/aftertouch) and then `mv`'d the binary into /opt/aftertouch on rootfs — which fails with "No space left on device" on any speaker with the standard layout. install.sh now installs to /mnt/nv/aftertouch by default (the persistent partition, ~30 MB free on the same captures) and points /opt/aftertouch at it via a symlink so the init script's hardcoded DAEMON path keeps working unchanged. Power users can override with INSTALL_DIR=/some/other/path. The interactive prompt from the community patch in #268's thread is dropped — STDIN is the curl pipe under the documented `curl | sh` invocation, so a read prompt would hang or read garbage. uninstall.sh is updated to resolve the symlink and remove the target before unlinking, so the 12 MB binary doesn't get orphaned on /mnt/nv when users uninstall. # Logging — #250 Issue #250 surfaced a "running but unreachable" state: the install script reported AfterTouch as running, the init script's status agreed, but `curl :8000` returned connection-refused. start-stop- daemon's --background detaches stdout/stderr, so any panic the daemon emitted before dying went to /dev/null with no diagnostic trail. The fix is to route the daemon's stdout/stderr through `logger -t aftertouch` so output lands in BusyBox syslog — a bounded in-memory ring buffer that never grows on disk (writing to a file in /mnt/nv would have eaten the volume over months). Diagnostic flow is now: logread | grep aftertouch | tail -20 logread -f | grep aftertouch # live tail Matches the recipe already documented in TROUBLESHOOTING.md for the speaker's own logs (Curl 7 section). Tightening on top of the syslog change: - The init script's `status` case now also curls localhost:8000 when the PID is alive — distinguishes "PID alive, listener up" from "PID alive, listener silently died" (which is what fooled everyone on #250). A bare PID-liveness check returned "running" in both cases. - install.sh's post-install verification now does its own 10s curl probe after the init script returns; on failure it tails the aftertouch syslog so the user sees the actual error rather than the install script claiming success. - `exec` is added inside the start-stop-daemon's shell wrapper so --make-pidfile records the daemon's own PID (not the shell's), which keeps `stop` semantics correct. README updated to document the install location, INSTALL_DIR override, and the syslog tag. No automated tests — these are shell scripts the install pipeline runs once on the device. All three scripts pass `bash -n` / `sh -n` syntax checks. Real validation is end-user retest, gated on the next release. Refs #268, refs #250. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
554fa78c0b
commit
064fe80e18
@@ -16,10 +16,21 @@ If your device doesn't expose the port, you can still use the on-device installe
|
||||
|
||||
### Space Limitation
|
||||
|
||||
The storage space on the SoundTouch devices is very limited. At the moment only one AfterTouch installation barely fits on them with enough room for the data it needs to maintain. When installing, make sure that you have removed any binaries and folders of previous installation attempts.
|
||||
The storage space on the SoundTouch devices is very limited — stock rootfs typically has only a few MB free (e.g. ~4 MB on the ST20, see issue #268), well below the AfterTouch binary's ~12 MB. To work around this, the installer puts everything on `/mnt/nv/aftertouch` by default (the persistent partition, typically ~30 MB free) and points `/opt/aftertouch` at it via a symlink so the init script and runtime paths stay unchanged. Override the install target with `INSTALL_DIR=/some/path` if you've got room elsewhere.
|
||||
|
||||
The space limitation also means we are currently unsure on how to update the system, because two binaries are already too large. We are currently working on this - both by checking how we can make the binaries smaller, but also on how we can extend the storage space (e.g. by running AfterTouch from a USB drive).
|
||||
|
||||
### Logs
|
||||
|
||||
The daemon writes to BusyBox syslog (tagged `aftertouch`) rather than to a file. Disk usage stays bounded — the syslog ring buffer is in memory — and the same `logread` recipe used elsewhere in this project works:
|
||||
|
||||
```sh
|
||||
logread | grep aftertouch | tail -20 # recent entries
|
||||
logread -f | grep aftertouch # live tail
|
||||
```
|
||||
|
||||
If the install command reports "running but :8000 not responding" or `aftertouch status` reports the listener is down, the syslog tail is the first place to look.
|
||||
|
||||
## Installation
|
||||
|
||||
Enable SSH on your SoundTouch device using the usual "Stick with remote_services" method. Connect with the following command.
|
||||
|
||||
@@ -17,6 +17,7 @@ PIDFILE="/var/run/$NAME.pid"
|
||||
DATADIR="/opt/aftertouch/data"
|
||||
SCRIPTNAME="/etc/init.d/$NAME"
|
||||
USER="root"
|
||||
LOG_TAG="aftertouch"
|
||||
|
||||
|
||||
# Export PATH
|
||||
@@ -41,6 +42,17 @@ case "$1" in
|
||||
|
||||
mkdir -p "$DATADIR"
|
||||
|
||||
# Pipe stdout + stderr through `logger -t $LOG_TAG` so the
|
||||
# daemon's output lands in busybox syslog (bounded ring buffer,
|
||||
# never grows on disk). Users diagnose with:
|
||||
#
|
||||
# logread | grep aftertouch | tail -20
|
||||
# logread -f | grep aftertouch # live tail
|
||||
#
|
||||
# `exec` on the daemon replaces /bin/sh so --make-pidfile records
|
||||
# the daemon's own PID (not the shell wrapper). The `logger`
|
||||
# process sits on the read end of the pipe and exits cleanly
|
||||
# when the daemon dies and closes its end.
|
||||
start-stop-daemon --start \
|
||||
--quiet \
|
||||
--pidfile "$PIDFILE" \
|
||||
@@ -48,7 +60,7 @@ case "$1" in
|
||||
--make-pidfile \
|
||||
--chuid "$USER" \
|
||||
--startas "/bin/sh" \
|
||||
-- -c "\"$DAEMON\" --data-dir '$DATADIR' --record-interactions=false --discovery-interval=60m"
|
||||
-- -c "exec \"$DAEMON\" --data-dir '$DATADIR' --record-interactions=false --discovery-interval=60m 2>&1 | logger -t $LOG_TAG"
|
||||
|
||||
tries=0
|
||||
max_tries=60
|
||||
@@ -60,6 +72,9 @@ case "$1" in
|
||||
tries=$((tries + 1))
|
||||
done
|
||||
|
||||
echo "ERROR: daemon started but http://localhost:8000 never responded within $((max_tries * 2))s." >&2
|
||||
echo " Inspect the daemon's syslog output:" >&2
|
||||
echo " logread | grep $LOG_TAG | tail -20" >&2
|
||||
exit 1
|
||||
;;
|
||||
|
||||
@@ -86,12 +101,27 @@ case "$1" in
|
||||
if [ -f "$PIDFILE" ]; then
|
||||
PID=$(cat "$PIDFILE")
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
echo "$NAME is running."
|
||||
# PID is alive — does it actually serve HTTP? A live process
|
||||
# with a dead listener is the symptom behind issue #250
|
||||
# (Gustour's ST30: status said running, curl said
|
||||
# connection-refused). Distinguish the two states here so
|
||||
# status isn't a false-positive.
|
||||
if curl -fsS --max-time 3 http://localhost:8000 >/dev/null 2>&1; then
|
||||
echo "$NAME is running (PID $PID, http://localhost:8000 responding)."
|
||||
exit 0
|
||||
else
|
||||
echo "$NAME PID $PID is alive but http://localhost:8000 is not responding." >&2
|
||||
echo "Recent log:" >&2
|
||||
logread 2>/dev/null | grep "$LOG_TAG" | tail -10 >&2
|
||||
exit 3
|
||||
fi
|
||||
else
|
||||
echo "$NAME is not running (PID file exists but process is dead)."
|
||||
echo "$NAME is not running (PID file exists but process is dead)." >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "$NAME is not running."
|
||||
exit 3
|
||||
fi
|
||||
;;
|
||||
|
||||
@@ -102,4 +132,4 @@ case "$1" in
|
||||
esac
|
||||
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
@@ -5,22 +5,47 @@ VERSION=${VERSION:-0.79.0}
|
||||
GH_REPO=${GH_REPO:-gesellix/Bose-SoundTouch}
|
||||
BINARY_URL=${BINARY_URL:-https://github.com/$GH_REPO/releases/download/v$VERSION/soundtouch-service-v$VERSION-linux-armv7}
|
||||
INIT_SCRIPT_URL=${INIT_SCRIPT_URL:-https://raw.githubusercontent.com/$GH_REPO/v$VERSION/scripts/on-device-install/aftertouch}
|
||||
UPDATE_TMP_DIR=${UPDATE_TMP_DIR:-/media/aftertouch}
|
||||
|
||||
# Default install location is /mnt/nv/aftertouch (the persistent
|
||||
# partition), not /opt/aftertouch on rootfs. Stock SoundTouch rootfs
|
||||
# has ~4 MB free on devices like the ST20 (issue #268); the
|
||||
# AfterTouch binary is ~12 MB. /mnt/nv typically has tens of MB
|
||||
# free and persists across reboots the same way /opt would.
|
||||
#
|
||||
# /opt/aftertouch becomes a symlink into the install target so the
|
||||
# init script's hardcoded DAEMON path keeps working unchanged.
|
||||
#
|
||||
# Power users can override with INSTALL_DIR=/some/other/path.
|
||||
INSTALL_DIR=${INSTALL_DIR:-/mnt/nv/aftertouch}
|
||||
|
||||
# Scratch directory for the download. /media is tmpfs on most
|
||||
# SoundTouch firmware, fine for transient files but unrelated to
|
||||
# the persistent install target.
|
||||
UPDATE_TMP_DIR=${UPDATE_TMP_DIR:-/media/aftertouch}
|
||||
|
||||
rm -rf "$UPDATE_TMP_DIR" || true
|
||||
mkdir -p "$UPDATE_TMP_DIR"
|
||||
|
||||
echo "Installing Aftertouch $VERSION ..."
|
||||
mkdir -p /opt/aftertouch
|
||||
echo "Installing AfterTouch $VERSION to $INSTALL_DIR ..."
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# Wire /opt/aftertouch -> $INSTALL_DIR so the init script
|
||||
# (DAEMON=/opt/aftertouch/aftertouch-service) finds the binary
|
||||
# regardless of which target we picked. Replace any prior
|
||||
# /opt/aftertouch (directory or stale symlink) before re-creating.
|
||||
if [ "$INSTALL_DIR" != "/opt/aftertouch" ]; then
|
||||
rm -rf /opt/aftertouch
|
||||
ln -sf "$INSTALL_DIR" /opt/aftertouch
|
||||
fi
|
||||
|
||||
curl \
|
||||
-sSL \
|
||||
-o "$UPDATE_TMP_DIR/binary" \
|
||||
--fail \
|
||||
"$BINARY_URL"
|
||||
|
||||
mv "$UPDATE_TMP_DIR/binary" /opt/aftertouch/aftertouch-service
|
||||
chmod +x /opt/aftertouch/aftertouch-service
|
||||
mv "$UPDATE_TMP_DIR/binary" "$INSTALL_DIR/aftertouch-service"
|
||||
chmod +x "$INSTALL_DIR/aftertouch-service"
|
||||
|
||||
echo "Creating init script..."
|
||||
curl \
|
||||
@@ -33,12 +58,33 @@ mv "$UPDATE_TMP_DIR/init-script" /etc/init.d/aftertouch
|
||||
chmod +x /etc/init.d/aftertouch
|
||||
update-rc.d aftertouch defaults
|
||||
|
||||
echo "Installation complete. Running initial startup to accelerate future startups..."
|
||||
echo "Installation complete. Running initial startup..."
|
||||
/etc/init.d/aftertouch start
|
||||
|
||||
/etc/init.d/aftertouch status
|
||||
|
||||
echo "Installation complete. Aftertouch $VERSION is now running on your device."
|
||||
echo "You can try to connect to at http://<your-device-ip>:8000 ."
|
||||
echo "If the connection fails, reconnect ssh with port forwarding like:"
|
||||
echo "ssh -L 8000:localhost:8000 root@<IP_ADDRESS_OF_SPEAKER>"
|
||||
# Post-install verification: the init script's own poll loop only
|
||||
# checks that the daemon registered a PID file; that's not enough
|
||||
# evidence the listener is actually serving HTTP. Issue #250 shipped
|
||||
# with a "running but unreachable" state where status was green and
|
||||
# `curl :8000` got connection-refused. Re-check directly here and
|
||||
# surface the recent syslog if it fails — the init script pipes the
|
||||
# daemon's stdout/stderr through `logger -t aftertouch`, so panics
|
||||
# land in busybox syslog and `logread` reads them out.
|
||||
if curl -fsS --max-time 10 http://localhost:8000 >/dev/null 2>&1; then
|
||||
echo "Installation complete. AfterTouch $VERSION is now running on your device."
|
||||
echo "Connect to http://<your-device-ip>:8000 from another machine on the LAN."
|
||||
echo "If the device doesn't expose :8000 directly, port-forward via SSH:"
|
||||
echo " ssh -L 8000:localhost:8000 root@<IP_ADDRESS_OF_SPEAKER>"
|
||||
else
|
||||
echo "WARNING: the init script reports AfterTouch as running, but" >&2
|
||||
echo " http://localhost:8000 isn't responding. The daemon may have" >&2
|
||||
echo " panicked shortly after start. Recent aftertouch syslog:" >&2
|
||||
echo "" >&2
|
||||
logread 2>/dev/null | grep aftertouch | tail -20 >&2 || \
|
||||
echo " (logread returned nothing for tag 'aftertouch'; the daemon" >&2
|
||||
echo "" >&2
|
||||
echo " For a live view of the daemon's output, run:" >&2
|
||||
echo " logread -f | grep aftertouch" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,22 @@
|
||||
/etc/init.d/aftertouch stop
|
||||
rm -rf /etc/init.d/aftertouch
|
||||
#!/bin/sh
|
||||
# Uninstall AfterTouch on-device. Handles both the historical
|
||||
# layout (/opt/aftertouch as a directory) and the post-#268 layout
|
||||
# (/opt/aftertouch as a symlink into /mnt/nv/aftertouch).
|
||||
set -eu
|
||||
|
||||
/etc/init.d/aftertouch stop || true
|
||||
rm -f /etc/init.d/aftertouch
|
||||
update-rc.d -f aftertouch remove
|
||||
rm -rf /opt/aftertouch
|
||||
|
||||
# If /opt/aftertouch is a symlink, resolve it and remove the target
|
||||
# before unlinking, so we don't leave ~12 MB of orphan binary on
|
||||
# /mnt/nv. Tolerate either layout — readlink -f returns the same
|
||||
# path for a real directory, and rm -rf on a missing path with
|
||||
# set -eu would abort.
|
||||
target="$(readlink -f /opt/aftertouch 2>/dev/null || echo /opt/aftertouch)"
|
||||
if [ -e "$target" ]; then
|
||||
rm -rf "$target"
|
||||
fi
|
||||
if [ -L /opt/aftertouch ] || [ -e /opt/aftertouch ]; then
|
||||
rm -rf /opt/aftertouch
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user