server: guard gadget cycling
This commit is contained in:
parent
0da8fbb5f7
commit
4e96c271ed
@ -22,6 +22,15 @@ udc_state() {
|
||||
cat "/sys/class/udc/$udc/state" 2>/dev/null || echo "unknown"
|
||||
}
|
||||
|
||||
is_attached_state() {
|
||||
case "$1" in
|
||||
configured|addressed|default|suspended)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
detach_gadget() {
|
||||
local udc=""
|
||||
udc="$(find_udc)"
|
||||
@ -177,6 +186,14 @@ fi
|
||||
[[ -n $UDC ]] || { log "❌ UDC not present after manual bind"; exit 1; }
|
||||
log "✅ UDC detected: $UDC"
|
||||
|
||||
# Guard against lockups: don't reset gadget while host is attached unless forced.
|
||||
UDC_STATE="$(udc_state "$UDC")"
|
||||
if [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]] && is_attached_state "$UDC_STATE"; then
|
||||
log "🔒 UDC state is '$UDC_STATE' - refusing gadget reset while host attached."
|
||||
log " Set LESAVKA_ALLOW_GADGET_RESET=1 to force."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
#──────────────────────────────────────────────────
|
||||
# 3. (Re‑)create gadget
|
||||
#──────────────────────────────────────────────────
|
||||
|
||||
@ -5,6 +5,25 @@ ORIG_USER=${SUDO_USER:-$(id -un)}
|
||||
|
||||
REF=${LESAVKA_REF:-master} # fallback
|
||||
|
||||
udc_state() {
|
||||
local udc=""
|
||||
udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true)
|
||||
if [[ -z $udc ]]; then
|
||||
echo "unknown"
|
||||
return 0
|
||||
fi
|
||||
cat "/sys/class/udc/$udc/state" 2>/dev/null || echo "unknown"
|
||||
}
|
||||
|
||||
is_attached_state() {
|
||||
case "$1" in
|
||||
configured|addressed|default|suspended|unknown)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-r|--ref) REF="$2"; shift 2 ;;
|
||||
@ -192,9 +211,16 @@ UNIT
|
||||
echo "==> 6c. Systemd units - initialization"
|
||||
sudo truncate -s 0 /tmp/lesavka-server.log
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now lesavka-core
|
||||
sudo systemctl restart lesavka-core
|
||||
echo "✅ lesavka-core installed and restarted..."
|
||||
sudo systemctl enable lesavka-core lesavka-uvc lesavka-server
|
||||
|
||||
UDC_STATE=$(udc_state)
|
||||
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || ! is_attached_state "$UDC_STATE"; then
|
||||
sudo systemctl restart lesavka-core
|
||||
echo "✅ lesavka-core installed and restarted..."
|
||||
else
|
||||
echo "⚠️ UDC state is '$UDC_STATE' - skipping lesavka-core restart."
|
||||
echo " Set LESAVKA_ALLOW_GADGET_RESET=1 to force."
|
||||
fi
|
||||
|
||||
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-uvc.service >/dev/null
|
||||
[Unit]
|
||||
@ -217,10 +243,12 @@ User=root
|
||||
WantedBy=multi-user.target
|
||||
UNIT
|
||||
|
||||
sudo systemctl enable --now lesavka-uvc
|
||||
sudo systemctl restart lesavka-uvc
|
||||
echo "✅ lesavka-uvc installed and restarted..."
|
||||
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || ! is_attached_state "$UDC_STATE"; then
|
||||
sudo systemctl restart lesavka-uvc
|
||||
echo "✅ lesavka-uvc installed and restarted..."
|
||||
else
|
||||
echo "⚠️ UDC state is '$UDC_STATE' - skipping lesavka-uvc restart."
|
||||
fi
|
||||
|
||||
sudo systemctl enable --now lesavka-server
|
||||
sudo systemctl restart lesavka-server
|
||||
echo "✅ lesavka-server installed and restarted..."
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// server/src/gadget.rs
|
||||
use anyhow::{Context, Result};
|
||||
use std::{
|
||||
env,
|
||||
fs::{self, OpenOptions},
|
||||
io::Write,
|
||||
path::Path,
|
||||
@ -115,6 +116,31 @@ impl UsbGadget {
|
||||
let ctrl = Self::find_controller().or_else(|_| {
|
||||
Self::probe_platform_udc()?.ok_or_else(|| anyhow::anyhow!("no UDC present"))
|
||||
})?;
|
||||
let force_cycle = env::var("LESAVKA_GADGET_FORCE_CYCLE").is_ok();
|
||||
match Self::state(&ctrl) {
|
||||
Ok(state)
|
||||
if !force_cycle
|
||||
&& matches!(state.as_str(), "configured" | "addressed" | "default" | "suspended") =>
|
||||
{
|
||||
warn!(
|
||||
"🔒 refusing gadget cycle while host attached (state={state}); set LESAVKA_GADGET_FORCE_CYCLE=1 to override"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
Ok(state) if !force_cycle && state == "unknown" => {
|
||||
warn!(
|
||||
"🔒 refusing gadget cycle with unknown UDC state; set LESAVKA_GADGET_FORCE_CYCLE=1 to override"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
Err(_) if !force_cycle => {
|
||||
warn!(
|
||||
"🔒 refusing gadget cycle without UDC state; set LESAVKA_GADGET_FORCE_CYCLE=1 to override"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
/* 1 - detach gadget */
|
||||
info!("🔌 detaching gadget from {ctrl}");
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
//! lesavka-server - **auto-cycle disabled**
|
||||
//! lesavka-server - gadget cycle guarded by env
|
||||
// server/src/main.rs
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
@ -26,8 +26,6 @@ use lesavka_common::lesavka::{
|
||||
use lesavka_server::{audio, gadget::UsbGadget, handshake::HandshakeSvc, video};
|
||||
|
||||
/*──────────────── constants ────────────────*/
|
||||
/// **false** = never reset automatically.
|
||||
const AUTO_CYCLE: bool = false;
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
const PKG_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
|
||||
@ -90,6 +88,10 @@ fn next_minute() -> SystemTime {
|
||||
UNIX_EPOCH + Duration::from_secs(next)
|
||||
}
|
||||
|
||||
fn allow_gadget_cycle() -> bool {
|
||||
std::env::var("LESAVKA_ALLOW_GADGET_CYCLE").is_ok()
|
||||
}
|
||||
|
||||
async fn recover_hid_if_needed(
|
||||
err: &std::io::Error,
|
||||
gadget: UsbGadget,
|
||||
@ -109,12 +111,19 @@ async fn recover_hid_if_needed(
|
||||
return;
|
||||
}
|
||||
|
||||
let allow_cycle = allow_gadget_cycle();
|
||||
tokio::spawn(async move {
|
||||
warn!("🔁 HID transport down (errno={code:?}) - cycling gadget");
|
||||
match tokio::task::spawn_blocking(move || gadget.cycle()).await {
|
||||
Ok(Ok(())) => info!("✅ USB gadget cycle complete (auto-recover)"),
|
||||
Ok(Err(e)) => error!("💥 USB gadget cycle failed: {e:#}"),
|
||||
Err(e) => error!("💥 USB gadget cycle task panicked: {e:#}"),
|
||||
if allow_cycle {
|
||||
warn!("🔁 HID transport down (errno={code:?}) - cycling gadget");
|
||||
match tokio::task::spawn_blocking(move || gadget.cycle()).await {
|
||||
Ok(Ok(())) => info!("✅ USB gadget cycle complete (auto-recover)"),
|
||||
Ok(Err(e)) => error!("💥 USB gadget cycle failed: {e:#}"),
|
||||
Err(e) => error!("💥 USB gadget cycle task panicked: {e:#}"),
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"🔒 HID transport down (errno={code:?}) - gadget cycle disabled; set LESAVKA_ALLOW_GADGET_CYCLE=1 to enable"
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(e) = async {
|
||||
@ -258,11 +267,11 @@ struct Handler {
|
||||
|
||||
impl Handler {
|
||||
async fn new(gadget: UsbGadget) -> anyhow::Result<Self> {
|
||||
if AUTO_CYCLE {
|
||||
if allow_gadget_cycle() {
|
||||
info!("🛠️ Initial USB reset…");
|
||||
let _ = gadget.cycle(); // ignore failure - may boot without host
|
||||
} else {
|
||||
info!("🛠️ AUTO_CYCLE disabled - no initial reset");
|
||||
info!("🔒 gadget cycle disabled at startup (set LESAVKA_ALLOW_GADGET_CYCLE=1 to enable)");
|
||||
}
|
||||
|
||||
info!("🛠️ opening HID endpoints …");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user