server: supervise uvc helper

This commit is contained in:
Brad Stein 2026-01-06 11:48:36 -03:00
parent f1aaa1743d
commit 3c1f647b04

View File

@ -7,8 +7,8 @@ use futures_util::{Stream, StreamExt};
use gstreamer as gst; use gstreamer as gst;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::{backtrace::Backtrace, panic, pin::Pin, process::Command, sync::Arc}; use std::{backtrace::Backtrace, panic, pin::Pin, sync::Arc};
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync::Mutex}; use tokio::{fs::OpenOptions, io::AsyncWriteExt, process::Command, sync::Mutex};
use tokio_stream::wrappers::ReceiverStream; use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Server; use tonic::transport::Server;
use tonic::{Request, Response, Status}; use tonic::{Request, Response, Status};
@ -164,9 +164,12 @@ fn pick_uvc_device() -> anyhow::Result<String> {
)) ))
} }
fn spawn_uvc_control(uvc_dev: &str) -> anyhow::Result<std::process::Child> { fn uvc_ctrl_bin() -> String {
let bin = std::env::var("LESAVKA_UVC_CTRL_BIN") std::env::var("LESAVKA_UVC_CTRL_BIN")
.unwrap_or_else(|_| "/usr/local/bin/lesavka-uvc".to_string()); .unwrap_or_else(|_| "/usr/local/bin/lesavka-uvc".to_string())
}
fn spawn_uvc_control(bin: &str, uvc_dev: &str) -> anyhow::Result<tokio::process::Child> {
Command::new(bin) Command::new(bin)
.arg("--device") .arg("--device")
.arg(uvc_dev) .arg(uvc_dev)
@ -174,6 +177,48 @@ fn spawn_uvc_control(uvc_dev: &str) -> anyhow::Result<std::process::Child> {
.context("spawning lesavka-uvc") .context("spawning lesavka-uvc")
} }
async fn supervise_uvc_control(bin: String) {
let mut waiting_logged = false;
loop {
let uvc_dev = match pick_uvc_device() {
Ok(dev) => {
if waiting_logged {
info!(%dev, "📷 UVC device discovered");
waiting_logged = false;
}
dev
}
Err(e) => {
if !waiting_logged {
warn!("⚠️ UVC device not ready: {e:#}");
waiting_logged = true;
}
tokio::time::sleep(Duration::from_secs(2)).await;
continue;
}
};
match spawn_uvc_control(&bin, &uvc_dev) {
Ok(mut child) => {
info!(%uvc_dev, "📷 UVC control helper started");
match child.wait().await {
Ok(status) => {
warn!(%uvc_dev, "⚠️ lesavka-uvc exited: {status}");
}
Err(e) => {
warn!(%uvc_dev, "⚠️ lesavka-uvc wait failed: {e:#}");
}
}
}
Err(e) => {
warn!(%uvc_dev, "⚠️ failed to start lesavka-uvc: {e:#}");
}
}
tokio::time::sleep(Duration::from_secs(2)).await;
}
}
/*──────────────── Handler ───────────────────*/ /*──────────────── Handler ───────────────────*/
struct Handler { struct Handler {
kb: Arc<Mutex<tokio::fs::File>>, kb: Arc<Mutex<tokio::fs::File>>,
@ -401,27 +446,12 @@ async fn main() -> anyhow::Result<()> {
})); }));
let gadget = UsbGadget::new("lesavka"); let gadget = UsbGadget::new("lesavka");
let _uvc_ctrl = if std::env::var("LESAVKA_DISABLE_UVC").is_err() { if std::env::var("LESAVKA_DISABLE_UVC").is_err() {
match pick_uvc_device() { let bin = uvc_ctrl_bin();
Ok(uvc_dev) => match spawn_uvc_control(&uvc_dev) { tokio::spawn(supervise_uvc_control(bin));
Ok(child) => {
info!(%uvc_dev, "📷 UVC control helper started");
Some(child)
}
Err(e) => {
warn!("⚠️ failed to start lesavka-uvc: {e:#}");
None
}
},
Err(e) => {
warn!("⚠️ UVC device not ready: {e:#}");
None
}
}
} else { } else {
info!("📷 UVC disabled (LESAVKA_DISABLE_UVC set)"); info!("📷 UVC disabled (LESAVKA_DISABLE_UVC set)");
None }
};
let handler = Handler::new(gadget.clone()).await?; let handler = Handler::new(gadget.clone()).await?;
info!("🌐 lesavka-server listening on 0.0.0.0:50051"); info!("🌐 lesavka-server listening on 0.0.0.0:50051");