diff --git a/Cargo.lock b/Cargo.lock index 666065d..03b7efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1642,7 +1642,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.14.22" +version = "0.14.23" dependencies = [ "anyhow", "async-stream", @@ -1676,7 +1676,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.14.22" +version = "0.14.23" dependencies = [ "anyhow", "base64", @@ -1688,7 +1688,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.14.22" +version = "0.14.23" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index 8fa3ccb..1cee5dd 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.14.22" +version = "0.14.23" edition = "2024" [dependencies] diff --git a/common/Cargo.toml b/common/Cargo.toml index a5e44a1..fde0b39 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.14.22" +version = "0.14.23" edition = "2024" build = "build.rs" diff --git a/scripts/install/server.sh b/scripts/install/server.sh index 764eca6..bd9c3ef 100755 --- a/scripts/install/server.sh +++ b/scripts/install/server.sh @@ -38,6 +38,57 @@ LESAVKA_UVC_MAXBURST=${LESAVKA_UVC_MAXBURST:-0} EOF } +find_uvc_output_node() { + local by_path_root=/dev/v4l/by-path + local ctrl="" + ctrl=$(ls /sys/class/udc 2>/dev/null | head -n1 || true) + if [[ -n $ctrl && -e "$by_path_root/platform-$ctrl-video-index0" ]]; then + printf '%s\n' "$by_path_root/platform-$ctrl-video-index0" + return 0 + fi + + local candidate + shopt -s nullglob + for candidate in "$by_path_root"/platform-*-video-index0; do + printf '%s\n' "$candidate" + shopt -u nullglob + return 0 + done + shopt -u nullglob + return 1 +} + +wait_for_uvc_output_node() { + local node="" + for _ in {1..50}; do + if node=$(find_uvc_output_node); then + printf '%s\n' "$node" + return 0 + fi + sleep 0.1 + done + return 1 +} + +validate_uvc_gadget_ready() { + if [[ -n ${LESAVKA_DISABLE_UVC:-} ]]; then + return 0 + fi + + if [[ ! -d /sys/kernel/config/usb_gadget/lesavka/functions/uvc.usb0 ]]; then + echo "❌ UVC gadget function is missing after rebuild; refusing to continue with a half-applied install." >&2 + return 1 + fi + + local node="" + if ! node=$(wait_for_uvc_output_node); then + echo "❌ UVC gadget video-output node did not appear after rebuild; refusing to continue." >&2 + return 1 + fi + + echo "✅ UVC gadget output ready at ${node}" +} + udc_state() { local udc="" udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true) @@ -603,7 +654,7 @@ if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || [[ "$FORCE_GADGET_REBUILD" == "1" LESAVKA_ATTACH_WRITE_UDC=1 \ LESAVKA_DETACH_CLEAR_UDC=1 \ LESAVKA_RELOAD_UVCVIDEO=1 \ - LESAVKA_UVC_FALLBACK=1 \ + LESAVKA_UVC_FALLBACK=0 \ LESAVKA_UVC_CODEC="${LESAVKA_UVC_CODEC:-mjpeg}" \ /usr/local/bin/lesavka-core.sh sudo systemctl restart lesavka-core @@ -656,6 +707,8 @@ else echo "⚠️ lesavka-uvc is not active; start via lesavka-core dependency path." fi +validate_uvc_gadget_ready + sudo systemctl restart lesavka-server 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) diff --git a/server/Cargo.toml b/server/Cargo.toml index 26a9b02..1a4a61f 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.14.22" +version = "0.14.23" edition = "2024" autobins = false diff --git a/testing/tests/server_install_script_contract.rs b/testing/tests/server_install_script_contract.rs index 291dacc..f86cb89 100644 --- a/testing/tests/server_install_script_contract.rs +++ b/testing/tests/server_install_script_contract.rs @@ -57,6 +57,18 @@ fn server_install_pins_hdmi_camera_and_display_defaults() { !SERVER_INSTALL.contains("Environment=LESAVKA_UVC_CODEC=mjpeg"), "install script should not pin UVC codec to mjpeg in the systemd units" ); + assert!( + SERVER_INSTALL.contains("LESAVKA_UVC_FALLBACK=0"), + "forced gadget rebuilds should fail loud instead of silently dropping UVC" + ); + assert!( + SERVER_INSTALL.contains("validate_uvc_gadget_ready"), + "install script should verify that the UVC gadget comes back before declaring success" + ); + assert!( + SERVER_INSTALL.contains("video-output node did not appear after rebuild"), + "install script should explain why it refuses a half-applied UVC install" + ); } #[test]