install: defer attached uvc runtime changes
This commit is contained in:
parent
7c797e6248
commit
4bc5264513
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-stream",
|
"async-stream",
|
||||||
@ -1686,7 +1686,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
@ -1698,7 +1698,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_client"
|
name = "lesavka_client"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lesavka_common"
|
name = "lesavka_common"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
|||||||
@ -439,6 +439,49 @@ uvc_gadget_present() {
|
|||||||
[[ -d /sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0 ]]
|
[[ -d /sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
live_uvc_descriptor_codec() {
|
||||||
|
local function_root=/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0
|
||||||
|
if [[ -e "$function_root/streaming/header/h/mjpeg" ]]; then
|
||||||
|
echo "mjpeg"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -e "$function_root/streaming/header/h/yuyv" ]]; then
|
||||||
|
echo "hevc"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -d "$function_root/streaming/mjpeg/m" ]]; then
|
||||||
|
echo "mjpeg"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -d "$function_root/streaming/uncompressed/yuyv" ]]; then
|
||||||
|
echo "hevc"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
live_uvc_descriptor_matches_request() {
|
||||||
|
local function_root=/sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0
|
||||||
|
local frame_root=""
|
||||||
|
case "$(live_uvc_descriptor_codec)" in
|
||||||
|
mjpeg)
|
||||||
|
[[ "$INSTALL_UVC_CODEC" == "mjpeg" ]] || return 1
|
||||||
|
frame_root="$function_root/streaming/mjpeg/m/720p"
|
||||||
|
;;
|
||||||
|
hevc)
|
||||||
|
[[ "$INSTALL_UVC_CODEC" != "mjpeg" ]] || return 1
|
||||||
|
frame_root="$function_root/streaming/uncompressed/yuyv/480p"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
[[ -r "$frame_root/wWidth" && -r "$frame_root/wHeight" && -r "$frame_root/dwDefaultFrameInterval" ]] || return 1
|
||||||
|
[[ "$(cat "$frame_root/wWidth" 2>/dev/null || true)" == "${LESAVKA_UVC_WIDTH:-1280}" ]] || return 1
|
||||||
|
[[ "$(cat "$frame_root/wHeight" 2>/dev/null || true)" == "${LESAVKA_UVC_HEIGHT:-720}" ]] || return 1
|
||||||
|
[[ "$(cat "$frame_root/dwDefaultFrameInterval" 2>/dev/null || true)" == "${LESAVKA_UVC_INTERVAL:-333333}" ]]
|
||||||
|
}
|
||||||
|
|
||||||
udc_state() {
|
udc_state() {
|
||||||
local udc=""
|
local udc=""
|
||||||
udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true)
|
udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true)
|
||||||
@ -1204,6 +1247,7 @@ if [[ -n $HDMI_CONNECTOR ]]; then
|
|||||||
else
|
else
|
||||||
echo "⚠️ no connected HDMI connector detected; leaving LESAVKA_HDMI_CONNECTOR unset." >&2
|
echo "⚠️ no connected HDMI connector detected; leaving LESAVKA_HDMI_CONNECTOR unset." >&2
|
||||||
fi
|
fi
|
||||||
|
SERVER_ENV_TMP=$(mktemp)
|
||||||
{
|
{
|
||||||
echo "# generated by lesavka/scripts/install/server.sh"
|
echo "# generated by lesavka/scripts/install/server.sh"
|
||||||
echo "# Edit only for local hardware overrides; rerunning the installer refreshes defaults."
|
echo "# Edit only for local hardware overrides; rerunning the installer refreshes defaults."
|
||||||
@ -1271,7 +1315,7 @@ fi
|
|||||||
printf 'LESAVKA_TLS_CERT=%s\n' "${LESAVKA_TLS_CERT:-$LESAVKA_TLS_DIR/server.crt}"
|
printf 'LESAVKA_TLS_CERT=%s\n' "${LESAVKA_TLS_CERT:-$LESAVKA_TLS_DIR/server.crt}"
|
||||||
printf 'LESAVKA_TLS_KEY=%s\n' "${LESAVKA_TLS_KEY:-$LESAVKA_TLS_DIR/server.key}"
|
printf 'LESAVKA_TLS_KEY=%s\n' "${LESAVKA_TLS_KEY:-$LESAVKA_TLS_DIR/server.key}"
|
||||||
printf 'LESAVKA_TLS_CLIENT_CA=%s\n' "${LESAVKA_TLS_CLIENT_CA:-$LESAVKA_TLS_DIR/ca.crt}"
|
printf 'LESAVKA_TLS_CLIENT_CA=%s\n' "${LESAVKA_TLS_CLIENT_CA:-$LESAVKA_TLS_DIR/ca.crt}"
|
||||||
} | sudo tee /etc/lesavka/server.env >/dev/null
|
} >"$SERVER_ENV_TMP"
|
||||||
|
|
||||||
UVC_ENV_TMP=$(mktemp)
|
UVC_ENV_TMP=$(mktemp)
|
||||||
render_uvc_env_file >"$UVC_ENV_TMP"
|
render_uvc_env_file >"$UVC_ENV_TMP"
|
||||||
@ -1279,8 +1323,60 @@ UVC_ENV_CHANGED=1
|
|||||||
if sudo test -f /etc/lesavka/uvc.env && sudo cmp -s "$UVC_ENV_TMP" /etc/lesavka/uvc.env; then
|
if sudo test -f /etc/lesavka/uvc.env && sudo cmp -s "$UVC_ENV_TMP" /etc/lesavka/uvc.env; then
|
||||||
UVC_ENV_CHANGED=0
|
UVC_ENV_CHANGED=0
|
||||||
fi
|
fi
|
||||||
|
UDC_STATE=$(udc_state)
|
||||||
|
EXPLICIT_GADGET_REBUILD=0
|
||||||
|
if [[ -n ${LESAVKA_FORCE_GADGET_REBUILD:-} ]]; then
|
||||||
|
EXPLICIT_GADGET_REBUILD=1
|
||||||
|
fi
|
||||||
|
LIVE_UVC_DESCRIPTOR_MISMATCH=0
|
||||||
|
if is_attached_state "$UDC_STATE" && uvc_gadget_present && ! live_uvc_descriptor_matches_request; then
|
||||||
|
LIVE_UVC_DESCRIPTOR_MISMATCH=1
|
||||||
|
fi
|
||||||
|
LIVE_UVC_FUNCTION_MISSING=0
|
||||||
|
if [[ -z ${LESAVKA_DISABLE_UVC:-} ]] && is_attached_state "$UDC_STATE" && ! uvc_gadget_present; then
|
||||||
|
LIVE_UVC_FUNCTION_MISSING=1
|
||||||
|
fi
|
||||||
|
ATTACHED_UVC_CHANGE_DEFERRED=0
|
||||||
|
if is_attached_state "$UDC_STATE" \
|
||||||
|
&& { [[ "$LIVE_UVC_DESCRIPTOR_MISMATCH" == "1" ]] || [[ "$LIVE_UVC_FUNCTION_MISSING" == "1" ]]; } \
|
||||||
|
&& { [[ "$EXPLICIT_GADGET_REBUILD" != "1" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]; }; then
|
||||||
|
ATTACHED_UVC_CHANGE_DEFERRED=1
|
||||||
|
fi
|
||||||
|
ATTACHED_UVC_RESTART_DEFERRED=0
|
||||||
|
if is_attached_state "$UDC_STATE" \
|
||||||
|
&& [[ "$UVC_ENV_CHANGED" == "1" ]] \
|
||||||
|
&& [[ "$LIVE_UVC_DESCRIPTOR_MISMATCH" != "1" ]] \
|
||||||
|
&& [[ "$LIVE_UVC_FUNCTION_MISSING" != "1" ]] \
|
||||||
|
&& { [[ "$EXPLICIT_GADGET_REBUILD" != "1" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]; }; then
|
||||||
|
ATTACHED_UVC_RESTART_DEFERRED=1
|
||||||
|
fi
|
||||||
|
if [[ "$ATTACHED_UVC_CHANGE_DEFERRED" == "1" ]]; then
|
||||||
|
echo "⚠️ UVC runtime settings or live descriptors differ while the host is attached." >&2
|
||||||
|
echo " Refusing to rewrite UVC/server runtime env or restart Lesavka services because that can wedge the Pi USB controller." >&2
|
||||||
|
echo " Leaving /etc/lesavka/server.env and /etc/lesavka/uvc.env unchanged; binaries were installed but running services were left untouched." >&2
|
||||||
|
echo " To apply descriptor-changing UVC settings, use a maintenance window with LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1." >&2
|
||||||
|
rm -f "$SERVER_ENV_TMP" "$UVC_ENV_TMP"
|
||||||
|
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
||||||
|
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
||||||
|
echo "✅ lesavka-server binaries installed; live service restart deferred for attached-gadget safety."
|
||||||
|
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
||||||
|
echo "➡️ Deferred UVC codec request: ${INSTALL_UVC_CODEC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
sudo install -m 0644 "$SERVER_ENV_TMP" /etc/lesavka/server.env
|
||||||
sudo install -m 0644 "$UVC_ENV_TMP" /etc/lesavka/uvc.env
|
sudo install -m 0644 "$UVC_ENV_TMP" /etc/lesavka/uvc.env
|
||||||
rm -f "$UVC_ENV_TMP"
|
rm -f "$SERVER_ENV_TMP" "$UVC_ENV_TMP"
|
||||||
|
if [[ "$ATTACHED_UVC_RESTART_DEFERRED" == "1" ]]; then
|
||||||
|
echo "⚠️ UVC runtime env changed while the host is attached, but the requested descriptor matches the live gadget." >&2
|
||||||
|
echo " Updated /etc/lesavka/server.env and /etc/lesavka/uvc.env without restarting live services." >&2
|
||||||
|
echo " Rerun the installer with the same UVC settings to restart services after the env is stable, or use LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 during a maintenance window." >&2
|
||||||
|
INSTALLED_VERSION=$(manifest_package_version "$SRC_DIR/server/Cargo.toml" 2>/dev/null || true)
|
||||||
|
INSTALLED_SHA=$(git -C "$SCRIPT_REPO_ROOT" rev-parse --short HEAD 2>/dev/null || true)
|
||||||
|
echo "✅ lesavka-server binaries and runtime env installed; live service restart deferred for attached-gadget safety."
|
||||||
|
echo "➡️ Installed binaries: lesavka-server ${INSTALLED_VERSION:-unknown}${INSTALLED_SHA:+ ($INSTALLED_SHA)}"
|
||||||
|
echo "➡️ Deferred restart UVC codec: ${INSTALL_UVC_CODEC}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
echo "==> 6a. Systemd units - lesavka-core"
|
echo "==> 6a. Systemd units - lesavka-core"
|
||||||
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-core.service >/dev/null
|
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-core.service >/dev/null
|
||||||
@ -1347,10 +1443,8 @@ sudo truncate -s 0 /var/log/lesavka/server.log
|
|||||||
sudo systemctl daemon-reload
|
sudo systemctl daemon-reload
|
||||||
sudo systemctl enable lesavka-core lesavka-server
|
sudo systemctl enable lesavka-core lesavka-server
|
||||||
|
|
||||||
UDC_STATE=$(udc_state)
|
|
||||||
FORCE_GADGET_REBUILD=0
|
FORCE_GADGET_REBUILD=0
|
||||||
GADGET_REBUILD_REASON=""
|
GADGET_REBUILD_REASON=""
|
||||||
EXPLICIT_GADGET_REBUILD=0
|
|
||||||
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"
|
GADGET_REBUILD_REASON="UVC function is missing from the live gadget"
|
||||||
@ -1361,6 +1455,11 @@ if [[ "$UVC_ENV_CHANGED" == "1" ]] && is_attached_state "$UDC_STATE"; then
|
|||||||
GADGET_REBUILD_REASON="UVC runtime settings changed while the host is attached"
|
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 [[ "$LIVE_UVC_DESCRIPTOR_MISMATCH" == "1" ]] && is_attached_state "$UDC_STATE"; then
|
||||||
|
FORCE_GADGET_REBUILD=1
|
||||||
|
GADGET_REBUILD_REASON="live UVC descriptor does not match requested codec ${INSTALL_UVC_CODEC}"
|
||||||
|
echo "⚠️ live UVC descriptor does not match requested codec ${INSTALL_UVC_CODEC}; forcing a gadget rebuild so descriptors and runtime agree."
|
||||||
|
fi
|
||||||
if [[ -n ${LESAVKA_FORCE_GADGET_REBUILD:-} ]]; then
|
if [[ -n ${LESAVKA_FORCE_GADGET_REBUILD:-} ]]; then
|
||||||
FORCE_GADGET_REBUILD=1
|
FORCE_GADGET_REBUILD=1
|
||||||
EXPLICIT_GADGET_REBUILD=1
|
EXPLICIT_GADGET_REBUILD=1
|
||||||
|
|||||||
@ -10,7 +10,7 @@ bench = false
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "lesavka_server"
|
name = "lesavka_server"
|
||||||
version = "0.22.2"
|
version = "0.22.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
autobins = false
|
autobins = false
|
||||||
|
|
||||||
|
|||||||
@ -209,6 +209,80 @@ fn server_install_pins_hdmi_camera_and_display_defaults() {
|
|||||||
SERVER_INSTALL.contains("EXPLICIT_GADGET_REBUILD=1"),
|
SERVER_INSTALL.contains("EXPLICIT_GADGET_REBUILD=1"),
|
||||||
"installer should distinguish organic rebuild needs from an explicit operator hard-reset request"
|
"installer should distinguish organic rebuild needs from an explicit operator hard-reset request"
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("ATTACHED_UVC_CHANGE_DEFERRED=1"),
|
||||||
|
"attached UVC descriptor/runtime changes should be deferred instead of half-applied"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.contains("Leaving /etc/lesavka/server.env and /etc/lesavka/uvc.env unchanged"),
|
||||||
|
"deferred attached-gadget installs should preserve the last known live runtime env"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("binaries were installed but running services were left untouched"),
|
||||||
|
"deferred attached-gadget installs must not restart live services"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("LIVE_UVC_DESCRIPTOR_MISMATCH"),
|
||||||
|
"installer should catch partial previous runs where env already changed but live descriptors did not"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("LIVE_UVC_FUNCTION_MISSING"),
|
||||||
|
"installer should not rebuild a missing attached UVC function without explicit maintenance-window force"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("ATTACHED_UVC_RESTART_DEFERRED=1"),
|
||||||
|
"attached UVC env changes that match the live descriptor should still defer service restarts"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("Updated /etc/lesavka/server.env and /etc/lesavka/uvc.env without restarting live services"),
|
||||||
|
"safe env repairs should not immediately restart the attached gadget path"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("dwDefaultFrameInterval"),
|
||||||
|
"live descriptor matching should include frame interval, not only codec labels"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL.contains("streaming/header/h/mjpeg")
|
||||||
|
&& SERVER_INSTALL.contains("streaming/header/h/yuyv"),
|
||||||
|
"live descriptor codec detection should prefer the active UVC header links"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.find("sudo install -m 0644 \"$UVC_ENV_TMP\" /etc/lesavka/uvc.env")
|
||||||
|
.unwrap()
|
||||||
|
< SERVER_INSTALL
|
||||||
|
.find("if [[ \"$ATTACHED_UVC_RESTART_DEFERRED\" == \"1\" ]]")
|
||||||
|
.unwrap(),
|
||||||
|
"safe env repair should install env files before exiting without service restarts"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.find("if [[ \"$ATTACHED_UVC_RESTART_DEFERRED\" == \"1\" ]]")
|
||||||
|
.unwrap()
|
||||||
|
< SERVER_INSTALL
|
||||||
|
.find("sudo systemctl restart lesavka-uvc")
|
||||||
|
.unwrap(),
|
||||||
|
"safe env repair must exit before live UVC helper restarts"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.find("ATTACHED_UVC_CHANGE_DEFERRED=1")
|
||||||
|
.unwrap()
|
||||||
|
< SERVER_INSTALL
|
||||||
|
.find("sudo install -m 0644 \"$SERVER_ENV_TMP\" /etc/lesavka/server.env")
|
||||||
|
.unwrap(),
|
||||||
|
"attached UVC deferral must run before runtime env files are rewritten"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
SERVER_INSTALL
|
||||||
|
.find("ATTACHED_UVC_CHANGE_DEFERRED=1")
|
||||||
|
.unwrap()
|
||||||
|
< SERVER_INSTALL
|
||||||
|
.find("sudo systemctl restart lesavka-uvc")
|
||||||
|
.unwrap(),
|
||||||
|
"attached UVC deferral must run before any live UVC helper restart"
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
SERVER_INSTALL.contains("[[ \"$EXPLICIT_GADGET_REBUILD\" != \"1\" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]"),
|
SERVER_INSTALL.contains("[[ \"$EXPLICIT_GADGET_REBUILD\" != \"1\" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]"),
|
||||||
"attached-host hard rebuilds should require both allow and explicit force flags"
|
"attached-host hard rebuilds should require both allow and explicit force flags"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user