fix(installer): avoid unnecessary gadget resets

This commit is contained in:
Brad Stein 2026-04-30 15:47:07 -03:00
parent 3be2d485b0
commit e213dad9ce
6 changed files with 51 additions and 16 deletions

6
Cargo.lock generated
View File

@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]] [[package]]
name = "lesavka_client" name = "lesavka_client"
version = "0.16.5" version = "0.16.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@ -1686,7 +1686,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_common" name = "lesavka_common"
version = "0.16.5" version = "0.16.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",
@ -1698,7 +1698,7 @@ dependencies = [
[[package]] [[package]]
name = "lesavka_server" name = "lesavka_server"
version = "0.16.5" version = "0.16.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"base64", "base64",

View File

@ -4,7 +4,7 @@ path = "src/main.rs"
[package] [package]
name = "lesavka_client" name = "lesavka_client"
version = "0.16.5" version = "0.16.6"
edition = "2024" edition = "2024"
[dependencies] [dependencies]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lesavka_common" name = "lesavka_common"
version = "0.16.5" version = "0.16.6"
edition = "2024" edition = "2024"
build = "build.rs" build = "build.rs"

View File

@ -1039,32 +1039,51 @@ sudo systemctl enable lesavka-core lesavka-server
UDC_STATE=$(udc_state) UDC_STATE=$(udc_state)
FORCE_GADGET_REBUILD=0 FORCE_GADGET_REBUILD=0
GADGET_REBUILD_REASON=""
if [[ -z ${LESAVKA_DISABLE_UVC:-} ]] && ! uvc_gadget_present; then if [[ -z ${LESAVKA_DISABLE_UVC:-} ]] && ! uvc_gadget_present; then
FORCE_GADGET_REBUILD=1 FORCE_GADGET_REBUILD=1
GADGET_REBUILD_REASON="UVC function is missing from the live gadget"
echo "⚠️ UVC function is missing from the live gadget; forcing a rebuild before server start." echo "⚠️ UVC function is missing from the live gadget; forcing a rebuild before server start."
fi fi
if [[ "$UVC_ENV_CHANGED" == "1" ]] && is_attached_state "$UDC_STATE"; then if [[ "$UVC_ENV_CHANGED" == "1" ]] && is_attached_state "$UDC_STATE"; then
FORCE_GADGET_REBUILD=1 FORCE_GADGET_REBUILD=1
GADGET_REBUILD_REASON="UVC runtime settings changed while the host is attached"
echo "⚠️ UVC runtime settings changed while the host is attached; forcing a gadget rebuild so the new descriptors take effect." echo "⚠️ UVC runtime settings changed while the host is attached; forcing a gadget rebuild so the new descriptors take effect."
fi fi
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || [[ "$FORCE_GADGET_REBUILD" == "1" ]] || ! is_attached_state "$UDC_STATE"; then if [[ -n ${LESAVKA_FORCE_GADGET_REBUILD:-} ]]; then
FORCE_GADGET_REBUILD=1
GADGET_REBUILD_REASON="explicit LESAVKA_FORCE_GADGET_REBUILD request"
echo "⚠️ explicit LESAVKA_FORCE_GADGET_REBUILD request; forcing a gadget rebuild before server start."
fi
if [[ "$FORCE_GADGET_REBUILD" == "1" && -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]] && is_attached_state "$UDC_STATE"; then
echo "⚠️ ${GADGET_REBUILD_REASON:-Gadget state} requires a rebuild, but UDC state is '$UDC_STATE' and hard reset was not allowed." >&2
echo " Preserving the attached gadget to avoid wedging the Pi USB controller." >&2
echo " Run during a maintenance window with LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 if a hard rebuild is required." >&2
FORCE_GADGET_REBUILD=0
fi
if [[ "$FORCE_GADGET_REBUILD" == "1" ]] || ! is_attached_state "$UDC_STATE"; then
echo "⚠️ UDC state is '$UDC_STATE' - forcing a Lesavka gadget rebuild before server start." echo "⚠️ UDC state is '$UDC_STATE' - forcing a Lesavka gadget rebuild before server start."
sudo systemctl stop lesavka-server >/dev/null 2>&1 || true sudo systemctl stop lesavka-server >/dev/null 2>&1 || true
sudo systemctl stop lesavka-uvc >/dev/null 2>&1 || true sudo systemctl stop lesavka-uvc >/dev/null 2>&1 || true
sudo systemctl reset-failed lesavka-uvc >/dev/null 2>&1 || true sudo systemctl reset-failed lesavka-uvc >/dev/null 2>&1 || true
sudo env \ CORE_REBUILD_ENV=(
LESAVKA_ALLOW_GADGET_RESET=1 \ "LESAVKA_ALLOW_GADGET_RESET=1"
LESAVKA_ATTACH_WRITE_UDC=1 \ "LESAVKA_ATTACH_WRITE_UDC=1"
LESAVKA_DETACH_CLEAR_UDC=1 \ "LESAVKA_DETACH_CLEAR_UDC=1"
LESAVKA_RELOAD_UVCVIDEO=1 \ "LESAVKA_UVC_FALLBACK=0"
LESAVKA_UVC_FALLBACK=0 \ "LESAVKA_UVC_CODEC=${INSTALL_UVC_CODEC}"
LESAVKA_UVC_CODEC="${INSTALL_UVC_CODEC}" \ )
/usr/local/bin/lesavka-core.sh if [[ -n ${LESAVKA_RELOAD_UVCVIDEO:-} ]]; then
CORE_REBUILD_ENV+=("LESAVKA_RELOAD_UVCVIDEO=${LESAVKA_RELOAD_UVCVIDEO}")
fi
sudo env "${CORE_REBUILD_ENV[@]}" /usr/local/bin/lesavka-core.sh
sudo systemctl reset-failed lesavka-core >/dev/null 2>&1 || true sudo systemctl reset-failed lesavka-core >/dev/null 2>&1 || true
echo "✅ lesavka-core gadget rebuilt directly." echo "✅ lesavka-core gadget rebuilt directly."
elif [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]]; then
echo "✅ UDC state is '$UDC_STATE'; LESAVKA_ALLOW_GADGET_RESET permits recovery, but no hard gadget rebuild is needed."
else else
echo "⚠️ UDC state is '$UDC_STATE' - skipping lesavka-core restart." echo "⚠️ UDC state is '$UDC_STATE' - skipping lesavka-core restart."
echo " Set LESAVKA_ALLOW_GADGET_RESET=1 to force." echo " Set LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 to force during a maintenance window."
fi fi
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-uvc.service >/dev/null cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-uvc.service >/dev/null

View File

@ -10,7 +10,7 @@ bench = false
[package] [package]
name = "lesavka_server" name = "lesavka_server"
version = "0.16.5" version = "0.16.6"
edition = "2024" edition = "2024"
autobins = false autobins = false

View File

@ -98,6 +98,22 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
SERVER_INSTALL.contains("lesavka-core gadget rebuilt directly."), SERVER_INSTALL.contains("lesavka-core gadget rebuilt directly."),
"install script should trust the direct forced rebuild instead of immediately rerunning the oneshot core unit" "install script should trust the direct forced rebuild instead of immediately rerunning the oneshot core unit"
); );
assert!(
SERVER_INSTALL.contains("LESAVKA_FORCE_GADGET_REBUILD"),
"install script should require an explicit force knob for attached-host gadget rebuilds"
);
assert!(
SERVER_INSTALL.contains("no hard gadget rebuild is needed"),
"LESAVKA_ALLOW_GADGET_RESET should permit recovery without causing unconditional hard rebuilds"
);
assert!(
!SERVER_INSTALL.contains("[[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || [[ \"$FORCE_GADGET_REBUILD\" == \"1\" ]]"),
"LESAVKA_ALLOW_GADGET_RESET must not itself trigger a hard UDC detach/rebind"
);
assert!(
!SERVER_INSTALL.contains("LESAVKA_RELOAD_UVCVIDEO=1"),
"install script should not unload/reload uvcvideo during ordinary server installs"
);
assert!( assert!(
SERVER_INSTALL.contains("sudo systemctl stop lesavka-uvc"), SERVER_INSTALL.contains("sudo systemctl stop lesavka-uvc"),
"install script should stop the UVC helper before directly rebuilding the gadget underneath it" "install script should stop the UVC helper before directly rebuilding the gadget underneath it"