lesavka/server/src/main.rs

207 lines
7.4 KiB
Rust
Raw Normal View History

2025-06-27 06:56:08 -05:00
//! lesavkaserver **autocycle disabled**
// server/src/main.rs
2025-06-01 16:04:00 -05:00
#![forbid(unsafe_code)]
2025-06-01 13:31:22 -05:00
2025-06-25 20:00:34 -05:00
use std::{panic, backtrace::Backtrace, pin::Pin, sync::Arc, time::Duration};
2025-06-27 06:56:08 -05:00
use std::sync::atomic::AtomicBool;
2025-06-25 18:23:38 -05:00
use anyhow::Context as _;
2025-06-27 06:56:08 -05:00
use futures_util::{Stream, StreamExt};
use tokio::{
fs::{OpenOptions},
io::AsyncWriteExt,
sync::Mutex,
};
use tokio_stream::wrappers::ReceiverStream;
use tonic::{Request, Response, Status};
use tonic::transport::Server;
2025-06-27 19:31:46 -05:00
use tonic_reflection::server::{Builder as ReflBuilder};
2025-06-27 06:56:08 -05:00
use tracing::{info, warn, error, trace};
2025-06-25 16:23:50 -05:00
use tracing_subscriber::{filter::EnvFilter, fmt, prelude::*};
2025-06-25 16:52:26 -05:00
use tracing_appender::non_blocking::WorkerGuard;
2025-06-21 05:21:57 -05:00
2025-06-23 07:18:26 -05:00
use lesavka_common::lesavka::{
2025-06-27 06:56:08 -05:00
ResetUsbRequest, ResetUsbReply,
2025-06-01 21:26:57 -05:00
relay_server::{Relay, RelayServer},
2025-06-27 06:56:08 -05:00
KeyboardReport, MouseReport, MonitorRequest, VideoPacket,
2025-06-01 16:04:00 -05:00
};
2025-06-02 20:24:00 -05:00
2025-06-27 06:56:08 -05:00
use lesavka_server::{usb_gadget::UsbGadget, video};
/*──────────────── constants ────────────────*/
/// **false** = never reset automatically.
const AUTO_CYCLE: bool = false;
/*──────────────── logging ───────────────────*/
2025-06-25 16:52:26 -05:00
fn init_tracing() -> anyhow::Result<WorkerGuard> {
2025-06-25 16:23:50 -05:00
let file = std::fs::OpenOptions::new()
2025-06-27 06:56:08 -05:00
.create(true).append(true).write(true)
2025-06-25 16:23:50 -05:00
.open("/tmp/lesavka-server.log")?;
2025-06-27 06:56:08 -05:00
let (file_writer, guard) = tracing_appender::non_blocking(file);
2025-06-25 16:23:50 -05:00
tracing_subscriber::registry()
2025-06-27 06:56:08 -05:00
.with(EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("lesavka_server=info")))
.with(fmt::layer().with_target(true).with_thread_ids(true))
.with(fmt::layer().with_writer(file_writer).with_ansi(false))
2025-06-25 16:23:50 -05:00
.init();
2025-06-25 16:52:26 -05:00
Ok(guard)
2025-06-25 16:23:50 -05:00
}
2025-06-27 06:56:08 -05:00
/*──────────────── helpers ───────────────────*/
2025-06-25 21:15:24 -05:00
async fn open_with_retry(path: &str) -> anyhow::Result<tokio::fs::File> {
2025-06-27 06:56:08 -05:00
for attempt in 1..=200 { // ≈10s
2025-06-25 19:04:11 -05:00
match OpenOptions::new()
2025-06-27 06:56:08 -05:00
.write(true).custom_flags(libc::O_NONBLOCK).open(path).await
2025-06-25 19:04:11 -05:00
{
Ok(f) => {
2025-06-25 21:15:24 -05:00
info!("✅ {path} opened on attempt #{attempt}");
2025-06-25 19:04:11 -05:00
return Ok(f);
}
2025-06-25 22:24:58 -05:00
Err(e) if e.raw_os_error() == Some(libc::EBUSY) => {
2025-06-27 06:56:08 -05:00
trace!("⏳ {path} busy… retry #{attempt}");
2025-06-25 21:15:24 -05:00
tokio::time::sleep(Duration::from_millis(50)).await;
2025-06-25 20:00:34 -05:00
}
2025-06-27 06:56:08 -05:00
Err(e) => return Err(e).with_context(|| format!("opening {path}")),
2025-06-25 19:04:11 -05:00
}
}
2025-06-27 06:56:08 -05:00
Err(anyhow::anyhow!("timeout waiting for {path}"))
2025-06-25 19:04:11 -05:00
}
2025-06-27 06:56:08 -05:00
/*──────────────── Handler ───────────────────*/
2025-06-02 20:41:36 -05:00
struct Handler {
2025-06-17 20:54:31 -05:00
kb: Arc<Mutex<tokio::fs::File>>,
ms: Arc<Mutex<tokio::fs::File>>,
2025-06-25 07:46:50 -05:00
gadget: UsbGadget,
2025-06-25 15:13:49 -05:00
did_cycle: AtomicBool,
2025-06-24 23:48:06 -05:00
}
impl Handler {
2025-06-27 06:56:08 -05:00
async fn new(gadget: UsbGadget) -> anyhow::Result<Self> {
if AUTO_CYCLE {
info!("🛠️ Initial USB reset…");
let _ = gadget.cycle(); // ignore failure may boot without host
2025-06-26 21:49:29 -05:00
} else {
2025-06-27 06:56:08 -05:00
info!("🛠️ AUTO_CYCLE disabled  no initial reset");
2025-06-26 21:49:29 -05:00
}
2025-06-25 16:23:50 -05:00
2025-06-27 06:56:08 -05:00
info!("🛠️ opening HID endpoints …");
2025-06-25 21:15:24 -05:00
let kb = open_with_retry("/dev/hidg0").await?;
let ms = open_with_retry("/dev/hidg1").await?;
2025-06-25 20:00:34 -05:00
info!("✅ HID endpoints ready");
2025-06-26 21:49:29 -05:00
Ok(Self {
kb: Arc::new(Mutex::new(kb)),
ms: Arc::new(Mutex::new(ms)),
gadget,
did_cycle: AtomicBool::new(false),
})
}
2025-06-01 16:04:00 -05:00
}
2025-06-01 13:31:22 -05:00
2025-06-27 06:56:08 -05:00
/*──────────────── gRPC service ─────────────*/
2025-06-02 20:41:36 -05:00
#[tonic::async_trait]
2025-06-16 22:14:52 -05:00
impl Relay for Handler {
2025-06-27 06:56:08 -05:00
/* existing streams ─ unchanged, except: no more autoreset */
2025-06-17 08:17:23 -05:00
type StreamKeyboardStream = ReceiverStream<Result<KeyboardReport, Status>>;
type StreamMouseStream = ReceiverStream<Result<MouseReport, Status>>;
2025-06-27 06:56:08 -05:00
type CaptureVideoStream = Pin<Box<dyn Stream<Item=Result<VideoPacket,Status>> + Send + Sync>>;
2025-06-01 13:31:22 -05:00
2025-06-17 08:17:23 -05:00
async fn stream_keyboard(
2025-06-01 13:31:22 -05:00
&self,
2025-06-17 08:17:23 -05:00
req: Request<tonic::Streaming<KeyboardReport>>,
) -> Result<Response<Self::StreamKeyboardStream>, Status> {
2025-06-27 06:56:08 -05:00
let (tx, rx) = tokio::sync::mpsc::channel(32);
2025-06-12 01:57:08 -05:00
let kb = self.kb.clone();
2025-06-02 20:41:36 -05:00
tokio::spawn(async move {
2025-06-17 08:17:23 -05:00
let mut s = req.into_inner();
2025-06-17 20:54:31 -05:00
while let Some(pkt) = s.next().await.transpose()? {
2025-06-27 06:56:08 -05:00
if let Err(e) = kb.lock().await.write_all(&pkt.data).await {
warn!("⌨️ write failed: {e} (dropped)");
2025-06-25 15:13:49 -05:00
}
2025-06-27 06:56:08 -05:00
tx.send(Ok(pkt)).await.ok();
2025-06-17 08:17:23 -05:00
}
Ok::<(), Status>(())
});
2025-06-27 06:56:08 -05:00
2025-06-26 15:12:23 -05:00
Ok(Response::new(ReceiverStream::new(rx)))
2025-06-17 08:17:23 -05:00
}
2025-06-16 19:19:14 -05:00
2025-06-17 08:17:23 -05:00
async fn stream_mouse(
&self,
req: Request<tonic::Streaming<MouseReport>>,
) -> Result<Response<Self::StreamMouseStream>, Status> {
2025-06-27 06:56:08 -05:00
let (tx, rx) = tokio::sync::mpsc::channel(4096);
2025-06-17 08:17:23 -05:00
let ms = self.ms.clone();
2025-06-16 19:19:14 -05:00
2025-06-17 08:17:23 -05:00
tokio::spawn(async move {
let mut s = req.into_inner();
2025-06-17 20:54:31 -05:00
while let Some(pkt) = s.next().await.transpose()? {
2025-06-27 06:56:08 -05:00
if let Err(e) = ms.lock().await.write_all(&pkt.data).await {
warn!("🖱️ write failed: {e} (dropped)");
2025-06-26 20:38:55 -05:00
}
2025-06-27 06:56:08 -05:00
tx.send(Ok(pkt)).await.ok();
2025-06-01 13:31:22 -05:00
}
2025-06-17 08:17:23 -05:00
Ok::<(), Status>(())
2025-06-01 13:31:22 -05:00
});
2025-06-27 06:56:08 -05:00
2025-06-26 15:12:23 -05:00
Ok(Response::new(ReceiverStream::new(rx)))
2025-06-01 13:31:22 -05:00
}
2025-06-21 05:21:57 -05:00
async fn capture_video(
&self,
req: Request<MonitorRequest>,
) -> Result<Response<Self::CaptureVideoStream>, Status> {
2025-06-27 06:56:08 -05:00
let id = req.into_inner().id;
let dev = match id {
2025-06-25 12:31:48 -05:00
0 => "/dev/lesavka_l_eye",
1 => "/dev/lesavka_r_eye",
_ => return Err(Status::invalid_argument("monitor id must be 0 or 1")),
2025-06-27 06:56:08 -05:00
};
info!("🎥 streaming {dev}");
let s = video::spawn_camera(dev, id, 6_000)
2025-06-21 05:21:57 -05:00
.await
2025-06-27 06:56:08 -05:00
.map_err(|e| Status::internal(format!("{e:#}")))?;
Ok(Response::new(Box::pin(s)))
}
2025-06-21 05:21:57 -05:00
2025-06-27 06:56:08 -05:00
/*────────────── USBreset RPC ───────────*/
async fn reset_usb(
&self,
_req: Request<ResetUsbRequest>,
) -> Result<Response<ResetUsbReply>, Status> {
info!("🔴 explicit ResetUsb() called");
match self.gadget.cycle() {
Ok(_) => Ok(Response::new(ResetUsbReply { ok: true })),
Err(e) => {
error!("💥 cycle failed: {e:#}");
Err(Status::internal(e.to_string()))
}
}
2025-06-21 05:21:57 -05:00
}
2025-06-01 13:31:22 -05:00
}
2025-06-27 06:56:08 -05:00
/*──────────────── main ───────────────────────*/
#[tokio::main(worker_threads = 4)]
2025-06-02 20:41:36 -05:00
async fn main() -> anyhow::Result<()> {
2025-06-27 06:56:08 -05:00
let _guard = init_tracing()?;
2025-06-05 22:44:27 -05:00
2025-06-25 20:00:34 -05:00
panic::set_hook(Box::new(|p| {
let bt = Backtrace::force_capture();
error!("💥 panic: {p}\n{bt}");
}));
2025-06-27 06:56:08 -05:00
let gadget = UsbGadget::new("lesavka");
let handler = Handler::new(gadget.clone()).await?;
2025-06-12 01:57:08 -05:00
2025-06-25 19:04:11 -05:00
info!("🌐 lesavkaserver listening on 0.0.0.0:50051");
2025-06-27 06:56:08 -05:00
Server::builder()
.tcp_nodelay(true)
.max_frame_size(Some(256*1024))
.add_service(RelayServer::new(handler))
2025-06-27 19:31:46 -05:00
.add_service(ReflBuilder::configure().build_v1().unwrap())
2025-06-27 06:56:08 -05:00
.serve(([0,0,0,0], 50051).into())
.await?;
2025-06-01 13:31:22 -05:00
Ok(())
}