// Server binary for relay RPCs, HID/audio/video gadget IO, and capture leasing. #[allow(clippy::useless_attribute)] #[forbid(unsafe_code)] use anyhow::Context; use futures_util::{Stream, StreamExt}; use std::collections::HashMap; use std::collections::HashSet; use std::net::SocketAddr; use std::os::unix::fs::FileTypeExt; use std::process::Command; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::{backtrace::Backtrace, panic, pin::Pin, sync::Arc, time::Duration}; use tokio::sync::{Mutex, broadcast}; use tokio_stream::wrappers::ReceiverStream; use tonic::transport::Server; use tonic::{Request, Response, Status}; use tonic_reflection::server::Builder as ReflBuilder; use tracing::{debug, error, info, warn}; use lesavka_common::lesavka::{ AudioPacket, CapturePowerCommand, CapturePowerState, Empty, KeyboardReport, MonitorRequest, MouseReport, PasteReply, PasteRequest, ResetUsbReply, SetCapturePowerRequest, VideoPacket, relay_server::{Relay, RelayServer}, }; use lesavka_server::{ camera, camera_runtime::CameraRuntime, capture_power::CapturePowerManager, gadget::UsbGadget, handshake::HandshakeSvc, paste, runtime_support, runtime_support::init_tracing, uvc_runtime, video, }; /*──────────────── constants ────────────────*/ const PKG_NAME: &str = env!("CARGO_PKG_NAME"); type VideoStream = Pin> + Send>>; type AudioStream = Pin> + Send>>; fn hid_endpoint(index: u8) -> String { std::env::var("LESAVKA_HID_DIR") .map(|dir| format!("{dir}/hidg{index}")) .unwrap_or_else(|_| format!("/dev/hidg{index}")) } fn live_keyboard_report_delay() -> Duration { std::env::var("LESAVKA_LIVE_KEYBOARD_REPORT_DELAY_MS") .ok() .and_then(|value| value.parse::().ok()) .map(Duration::from_millis) .unwrap_or_else(|| Duration::from_millis(8)) } fn server_bind_addr() -> anyhow::Result { // LESAVKA_SERVER_BIND_ADDR lets tests and constrained lab benches avoid // colliding with another live relay while keeping the field default stable. let raw = std::env::var("LESAVKA_SERVER_BIND_ADDR").unwrap_or_else(|_| "0.0.0.0:50051".to_string()); raw.parse::() .with_context(|| format!("parsing LESAVKA_SERVER_BIND_ADDR={raw:?}")) } /*──────────────── Handler ───────────────────*/ struct Handler { kb: Arc>>, ms: Arc>>, gadget: UsbGadget, did_cycle: Arc, camera_rt: Arc, capture_power: CapturePowerManager, eye_hubs: Arc>>>, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] struct EyeHubKey { source_id: u32, requested_width: u32, requested_height: u32, requested_fps: u32, } struct EyeHub { tx: broadcast::Sender, running: Arc, subscribers: Arc, abort: tokio::task::AbortHandle, } include!("main/handler_startup.rs"); include!("main/eye_video.rs"); include!("main/rpc_helpers.rs"); include!("main/usb_recovery_helpers.rs"); include!("main/eye_hub.rs"); include!("main/relay_service.rs"); include!("main/relay_service_coverage.rs"); include!("main/entrypoint.rs");