Skip to content

Commit b7ef5ef

Browse files
committed
fix(lva-peripheral): re-enable linger for UID 1000 at boot
Background: PipeWire's PulseAudio compat socket lives under /run/user/1000. The LVA container bind-mounts that path so the audio thread inside the container can record from the mic. /run/user/1000 is a session-scoped tmpfs by default — only created while the user has an active session, torn down and re-mounted with a new inode each time. When the LVA container's bind-mount points at a stale inode, ``pactl info`` from inside the container returns "Connection refused" and the audio thread crashes with "Recording failed, stream is in status FAILED", which surfaces in Home Assistant as "the voice assistant is unable to connect to Home Assistant". The pipewire stage (01-stage-picompose/03-install-pipewire-audio) already addresses this by enabling linger for the ``pi`` user. But Pi Imager's first-boot wizard normally renames ``pi`` to whatever username the user supplied, which leaves the linger file orphaned under the old name and the actual runtime user without linger. Fix: ship a tiny oneshot systemd unit that runs at every boot (before picompose.service), reads the username currently owning UID 1000, and touches /var/lib/systemd/linger/$USER. Idempotent, covers both the renamed and un-renamed cases, and makes the linger setting survive re-flashes by being part of the image rather than something the operator has to remember to enable.
1 parent e9cca2d commit b7ef5ef

3 files changed

Lines changed: 57 additions & 1 deletion

File tree

03-stage-lva-2mic-peripheral/01-lva-peripheral/01-run.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,21 @@ install -v -D -m 755 files/lva-peripheral-gpiochip-fix.sh \
6060
install -v -D -m 644 files/lva-peripheral-gpiochip-fix.service \
6161
"${ROOTFS_DIR}/etc/systemd/system/lva-peripheral-gpiochip-fix.service"
6262

63-
# Group membership + SPI overlay + enable the gpiochip-fix unit inside the chroot.
63+
# Helper that enables systemd linger for whoever UID 1000 ends up being at
64+
# runtime. Pi Imager's first-boot wizard typically renames ``pi`` to a
65+
# user-supplied name, orphaning the linger file the pipewire stage already
66+
# created under /var/lib/systemd/linger/pi. Without linger, /run/user/1000
67+
# (where PipeWire's PulseAudio socket lives) is session-scoped: the moment
68+
# the user SSHs out, the tmpfs is torn down, and the LVA container's
69+
# bind-mount of /run/user/1000 goes stale — audio capture starts failing
70+
# with "Connection refused" and HA shows "voice assistant unable to
71+
# connect". Runs once at boot before picompose deploys.
72+
install -v -D -m 755 files/lva-peripheral-linger-fix.sh \
73+
"${ROOTFS_DIR}/usr/local/sbin/lva-peripheral-linger-fix.sh"
74+
install -v -D -m 644 files/lva-peripheral-linger-fix.service \
75+
"${ROOTFS_DIR}/etc/systemd/system/lva-peripheral-linger-fix.service"
76+
77+
# Group membership + SPI overlay + enable the helper units inside the chroot.
6478
on_chroot << 'EOF'
6579
getent group spi >/dev/null && usermod -aG spi pi || true
6680
getent group gpio >/dev/null && usermod -aG gpio pi || true
@@ -74,6 +88,7 @@ grep -q "^dtparam=spi=on$" "$CONFIG" || echo "dtparam=spi=on" >> "$CONFIG"
7488
7589
systemctl daemon-reload
7690
systemctl enable lva-peripheral-gpiochip-fix.service
91+
systemctl enable lva-peripheral-linger-fix.service
7792
EOF
7893

7994
rm -rf "$SRC"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[Unit]
2+
Description=Enable systemd linger for the UID 1000 runtime user (LVA peripheral)
3+
ConditionPathExists=/compose/lva-peripheral/docker-compose.yml
4+
After=local-fs.target
5+
Before=picompose.service
6+
7+
[Service]
8+
Type=oneshot
9+
ExecStart=/usr/local/sbin/lva-peripheral-linger-fix.sh
10+
RemainAfterExit=yes
11+
12+
[Install]
13+
WantedBy=multi-user.target
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
# Ensure linger is enabled for whoever ends up at UID 1000.
3+
#
4+
# The LVA peripheral compose runs the containers as the host's UID 1000
5+
# (compose's ``user: "${LVA_USER_ID:-1000}:..."``) and bind-mounts the host's
6+
# ``/run/user/1000`` so the container can reach PipeWire's PulseAudio socket
7+
# for microphone capture. Without linger, ``/run/user/1000`` is a tmpfs that
8+
# only exists while a session is active — meaning audio capture breaks the
9+
# moment the user SSHs out, and the next bind-mount inside the container goes
10+
# stale.
11+
#
12+
# PiCompose's pipewire stage enables linger for the built-in ``pi`` user, but
13+
# Pi Imager's first-boot wizard often renames ``pi`` → something else (e.g.
14+
# ``je``), orphaning the linger file under the old name. Do it again here at
15+
# boot time using whatever username currently owns UID 1000.
16+
17+
set -e
18+
19+
RUNTIME_USER="$(getent passwd 1000 | cut -d: -f1 || true)"
20+
if [ -z "$RUNTIME_USER" ]; then
21+
echo "lva-peripheral-linger-fix: no user at UID 1000, skipping" >&2
22+
exit 0
23+
fi
24+
25+
mkdir -p /var/lib/systemd/linger
26+
touch "/var/lib/systemd/linger/$RUNTIME_USER"
27+
chmod 644 "/var/lib/systemd/linger/$RUNTIME_USER"
28+
echo "lva-peripheral-linger-fix: linger enabled for $RUNTIME_USER"

0 commit comments

Comments
 (0)