From ef982314bf5b81fc192903a6cf1be402551f0291 Mon Sep 17 00:00:00 2001 From: zarik5 Date: Tue, 20 Aug 2024 18:01:09 +0200 Subject: [PATCH] feat: :sparkles: Allow overwriting fs layout; rename SERVER_DATA_MANAGER to SESSION_MANAGER (#2344) --- alvr/dashboard/src/data_sources.rs | 50 ++++++------ alvr/dashboard/src/main.rs | 12 +-- alvr/dashboard/src/steamvr_launcher/mod.rs | 2 +- alvr/filesystem/src/lib.rs | 2 +- alvr/server_core/src/c_api.rs | 38 +++++++-- alvr/server_core/src/connection.rs | 93 ++++++++++++---------- alvr/server_core/src/lib.rs | 84 ++++++++++--------- alvr/server_core/src/logging_backend.rs | 61 +++++++------- alvr/server_core/src/web_server.rs | 61 ++++---------- alvr/server_io/src/lib.rs | 75 ++++++++++------- alvr/server_openvr/src/lib.rs | 9 ++- 11 files changed, 265 insertions(+), 222 deletions(-) diff --git a/alvr/dashboard/src/data_sources.rs b/alvr/dashboard/src/data_sources.rs index 23b0ec6aab..68a87f0631 100644 --- a/alvr/dashboard/src/data_sources.rs +++ b/alvr/dashboard/src/data_sources.rs @@ -1,7 +1,7 @@ use alvr_common::{debug, error, info, parking_lot::Mutex, warn, RelaxedAtomic}; use alvr_events::{Event, EventType}; use alvr_packets::ServerRequest; -use alvr_server_io::ServerDataManager; +use alvr_server_io::ServerSessionManager; use eframe::egui; use std::{ env, @@ -16,17 +16,17 @@ use tungstenite::http::Uri; const REQUEST_TIMEOUT: Duration = Duration::from_millis(200); -enum DataSource { - Local(Box), +enum SessionSource { + Local(Box), Remote, // Note: the remote (server) is probably living as a separate process in the same PC } -pub fn get_local_data_source() -> ServerDataManager { +pub fn get_local_session_source() -> ServerSessionManager { let session_file_path = alvr_filesystem::filesystem_layout_from_dashboard_exe(&env::current_exe().unwrap()) .session(); - ServerDataManager::new(&session_file_path) + ServerSessionManager::new(Some(session_file_path)) } fn report_event_local( @@ -49,12 +49,12 @@ fn report_event_local( fn report_session_local( context: &egui::Context, sender: &mpsc::Sender, - data_manager: &mut ServerDataManager, + session_manager: &mut ServerSessionManager, ) { report_event_local( context, sender, - EventType::Session(Box::new(data_manager.session().clone())), + EventType::Session(Box::new(session_manager.session().clone())), ) } @@ -83,14 +83,14 @@ impl DataSources { let (requests_sender, requests_receiver) = mpsc::channel(); let server_connected = Arc::new(RelaxedAtomic::new(false)); - let server_data_manager = get_local_data_source(); - let port = server_data_manager.settings().connection.web_server_port; - let data_source = Arc::new(Mutex::new(DataSource::Local(Box::new(server_data_manager)))); + let session_manager = get_local_session_source(); + let port = session_manager.settings().connection.web_server_port; + let session_source = Arc::new(Mutex::new(SessionSource::Local(Box::new(session_manager)))); let requests_thread = thread::spawn({ let running = Arc::clone(&running); let context = context.clone(); - let data_source = Arc::clone(&data_source); + let session_source = Arc::clone(&session_source); let events_sender = events_sender.clone(); move || { let uri = format!("http://127.0.0.1:{port}/api/dashboard-request"); @@ -102,31 +102,31 @@ impl DataSources { while let Ok(request) = requests_receiver.try_recv() { debug!("Dashboard request: {request:?}"); - if let DataSource::Local(data_manager) = &mut *data_source.lock() { + if let SessionSource::Local(session_manager) = &mut *session_source.lock() { match request { ServerRequest::Log(_) => (), ServerRequest::GetSession => { - report_session_local(&context, &events_sender, data_manager); + report_session_local(&context, &events_sender, session_manager); } ServerRequest::UpdateSession(session) => { - *data_manager.session_mut() = *session; + *session_manager.session_mut() = *session; - report_session_local(&context, &events_sender, data_manager); + report_session_local(&context, &events_sender, session_manager); } ServerRequest::SetValues(descs) => { - if let Err(e) = data_manager.set_values(descs) { + if let Err(e) = session_manager.set_values(descs) { error!("Failed to set session value: {e}") } - report_session_local(&context, &events_sender, data_manager); + report_session_local(&context, &events_sender, session_manager); } ServerRequest::UpdateClientList { hostname, action } => { - data_manager.update_client_list(hostname, action); + session_manager.update_client_list(hostname, action); - report_session_local(&context, &events_sender, data_manager); + report_session_local(&context, &events_sender, session_manager); } ServerRequest::GetAudioDevices => { - if let Ok(list) = data_manager.get_audio_devices_list() { + if let Ok(list) = session_manager.get_audio_devices_list() { report_event_local( &context, &events_sender, @@ -261,7 +261,7 @@ impl DataSources { let ping_thread = thread::spawn({ let running = Arc::clone(&running); - let data_source = Arc::clone(&data_source); + let data_source = Arc::clone(&session_source); let server_connected = Arc::clone(&server_connected); move || { const PING_INTERVAL: Duration = Duration::from_secs(1); @@ -277,13 +277,13 @@ impl DataSources { { let mut data_source_lock = data_source.lock(); - if connected && matches!(*data_source_lock, DataSource::Local(_)) { + if connected && matches!(*data_source_lock, SessionSource::Local(_)) { info!("Server connected"); - *data_source_lock = DataSource::Remote; - } else if !connected && matches!(*data_source_lock, DataSource::Remote) { + *data_source_lock = SessionSource::Remote; + } else if !connected && matches!(*data_source_lock, SessionSource::Remote) { info!("Server disconnected"); *data_source_lock = - DataSource::Local(Box::new(get_local_data_source())); + SessionSource::Local(Box::new(get_local_session_source())); } } diff --git a/alvr/dashboard/src/main.rs b/alvr/dashboard/src/main.rs index 80ea7b71d7..bad2e4b8ac 100644 --- a/alvr/dashboard/src/main.rs +++ b/alvr/dashboard/src/main.rs @@ -34,9 +34,9 @@ fn main() { logging_backend::init_logging(server_events_sender.clone()); { - let mut data_manager = data_sources::get_local_data_source(); + let mut session_manager = data_sources::get_local_session_source(); - data_manager.clean_client_list(); + session_manager.clean_client_list(); #[cfg(target_os = "linux")] { @@ -51,7 +51,7 @@ fn main() { .any(|adapter| adapter.get_info().vendor == 0x10de); if has_nvidia { - data_manager + session_manager .session_mut() .session_settings .extra @@ -60,14 +60,14 @@ fn main() { } } - if data_manager.session().server_version != *ALVR_VERSION { - let mut session_ref = data_manager.session_mut(); + if session_manager.session().server_version != *ALVR_VERSION { + let mut session_ref = session_manager.session_mut(); session_ref.server_version = ALVR_VERSION.clone(); session_ref.client_connections.clear(); session_ref.session_settings.extra.open_setup_wizard = true; } - if data_manager + if session_manager .settings() .extra .steamvr_launcher diff --git a/alvr/dashboard/src/steamvr_launcher/mod.rs b/alvr/dashboard/src/steamvr_launcher/mod.rs index d4263de16d..77bb87ff7c 100644 --- a/alvr/dashboard/src/steamvr_launcher/mod.rs +++ b/alvr/dashboard/src/steamvr_launcher/mod.rs @@ -73,7 +73,7 @@ impl Launcher { #[cfg(target_os = "linux")] linux_steamvr::linux_hardware_checks(); - let mut data_source = data_sources::get_local_data_source(); + let mut data_source = data_sources::get_local_session_source(); let launch_action = &data_source .settings() diff --git a/alvr/filesystem/src/lib.rs b/alvr/filesystem/src/lib.rs index 0191fef304..2312e0d15d 100644 --- a/alvr/filesystem/src/lib.rs +++ b/alvr/filesystem/src/lib.rs @@ -78,7 +78,7 @@ pub fn dashboard_fname() -> &'static str { } // Layout of the ALVR installation. All paths are absolute -#[derive(Clone)] +#[derive(Clone, Default, Debug)] pub struct Layout { // directory containing the dashboard executable pub executables_dir: PathBuf, diff --git a/alvr/server_core/src/c_api.rs b/alvr/server_core/src/c_api.rs index 52960564fb..47441503c6 100644 --- a/alvr/server_core/src/c_api.rs +++ b/alvr/server_core/src/c_api.rs @@ -1,7 +1,7 @@ #![allow(dead_code, unused_variables)] #![allow(clippy::missing_safety_doc)] -use crate::{logging_backend, ServerCoreContext, ServerCoreEvent, SERVER_DATA_MANAGER}; +use crate::{logging_backend, ServerCoreContext, ServerCoreEvent, SESSION_MANAGER}; use alvr_common::{ log, once_cell::sync::Lazy, @@ -13,7 +13,9 @@ use alvr_session::CodecType; use std::{ collections::{HashMap, VecDeque}, ffi::{c_char, CStr, CString}, + path::PathBuf, ptr, + str::FromStr, sync::mpsc, time::{Duration, Instant}, }; @@ -247,9 +249,35 @@ pub extern "C" fn alvr_get_settings_json(buffer: *mut c_char) -> u64 { string_to_c_str(buffer, &serde_json::to_string(&crate::settings()).unwrap()) } +/// This must be called before alvr_initialize() #[no_mangle] -pub extern "C" fn alvr_initialize_logging() { - logging_backend::init_logging(); +pub unsafe extern "C" fn alvr_initialize_environment( + config_dir: *const c_char, + log_dir: *const c_char, +) { + let config_dir = PathBuf::from_str(CStr::from_ptr(config_dir).to_str().unwrap()).unwrap(); + let log_dir = PathBuf::from_str(CStr::from_ptr(log_dir).to_str().unwrap()).unwrap(); + + crate::initialize_environment(alvr_filesystem::Layout { + config_dir, + log_dir, + ..Default::default() + }); +} + +/// Either session_log_path or crash_log_path can be null, in which case log is outputted to +/// stdout/stderr on Windows. +#[no_mangle] +pub unsafe extern "C" fn alvr_initialize_logging( + session_log_path: *const c_char, + crash_log_path: *const c_char, +) { + let session_log_path = (!session_log_path.is_null()) + .then(|| PathBuf::from_str(CStr::from_ptr(session_log_path).to_str().unwrap()).unwrap()); + let crash_log_path = (!crash_log_path.is_null()) + .then(|| PathBuf::from_str(CStr::from_ptr(crash_log_path).to_str().unwrap()).unwrap()); + + logging_backend::init_logging(session_log_path, crash_log_path); } #[no_mangle] @@ -258,8 +286,8 @@ pub unsafe extern "C" fn alvr_initialize() -> AlvrTargetConfig { *SERVER_CORE_CONTEXT.write() = Some(context); *EVENTS_RECEIVER.lock() = Some(receiver); - let data_manager_lock = SERVER_DATA_MANAGER.read(); - let restart_settings = &data_manager_lock.session().openvr_config; + let session_manager_lock = SESSION_MANAGER.read(); + let restart_settings = &session_manager_lock.session().openvr_config; AlvrTargetConfig { game_render_width: restart_settings.target_eye_resolution_width, diff --git a/alvr/server_core/src/connection.rs b/alvr/server_core/src/connection.rs index b4c1cecb13..8ab80b0b85 100644 --- a/alvr/server_core/src/connection.rs +++ b/alvr/server_core/src/connection.rs @@ -7,7 +7,7 @@ use crate::{ sockets::WelcomeSocket, statistics::StatisticsManager, tracking::{self, TrackingManager}, - ConnectionContext, ServerCoreEvent, ViewsConfig, SERVER_DATA_MANAGER, + ConnectionContext, ServerCoreEvent, ViewsConfig, SESSION_MANAGER, }; use alvr_audio::AudioDevice; use alvr_common::{ @@ -58,7 +58,7 @@ fn align32(value: f32) -> u32 { } fn is_streaming(client_hostname: &str) -> bool { - SERVER_DATA_MANAGER + SESSION_MANAGER .read() .client_list() .get(client_hostname) @@ -241,7 +241,7 @@ pub fn handshake_loop(ctx: Arc, lifecycle_state: Arc, lifecycle_state: Arc, lifecycle_state: Arc, lifecycle_state: Arc, lifecycle_state: Arc ConResult { - // This session lock will make sure settings cannot be changed while connecting and no other - // client can connect (until handshake is finished) - let mut server_data_lock = SERVER_DATA_MANAGER.write(); + // This session lock will make sure settings and client list cannot be changed while connecting + // to thos client, no other client can connect until handshake is finished. It will then be + // temporarily relocked while shutting down the threads. + let mut session_manager_lock = SESSION_MANAGER.write(); - server_data_lock.update_client_list( + session_manager_lock.update_client_list( client_hostname.clone(), ClientListAction::SetConnectionState(ConnectionState::Connecting), ); - server_data_lock.update_client_list( + session_manager_lock.update_client_list( client_hostname.clone(), ClientListAction::UpdateCurrentIp(Some(client_ip)), ); @@ -427,7 +428,7 @@ fn connection_pipeline( .. } = connection_result { - server_data_lock.update_client_list( + session_manager_lock.update_client_list( client_hostname.clone(), ClientListAction::SetDisplayName(display_name), ); @@ -454,7 +455,7 @@ fn connection_pipeline( con_bail!("Only streaming clients are supported for now"); }; - let settings = server_data_lock.settings().clone(); + let settings = session_manager_lock.settings().clone(); fn get_view_res(config: FrameSize, default_res: UVec2) -> UVec2 { let res = match config { @@ -597,7 +598,7 @@ fn connection_pipeline( }; let stream_config_packet = alvr_packets::encode_stream_config( - server_data_lock.session(), + session_manager_lock.session(), &NegotiatedStreamingConfig { view_resolution: stream_view_resolution, refresh_rate_hint: fps, @@ -611,7 +612,7 @@ fn connection_pipeline( let (mut control_sender, mut control_receiver) = proto_socket.split(STREAMING_RECV_TIMEOUT).to_con()?; - let mut new_openvr_config = contruct_openvr_config(server_data_lock.session()); + let mut new_openvr_config = contruct_openvr_config(session_manager_lock.session()); new_openvr_config.eye_resolution_width = stream_view_resolution.x; new_openvr_config.eye_resolution_height = stream_view_resolution.y; new_openvr_config.target_eye_resolution_width = target_view_resolution.x; @@ -622,8 +623,8 @@ fn connection_pipeline( new_openvr_config.use_10bit_encoder = enable_10_bits_encoding; new_openvr_config.codec = codec as _; - if server_data_lock.session().openvr_config != new_openvr_config { - server_data_lock.session_mut().openvr_config = new_openvr_config; + if session_manager_lock.session().openvr_config != new_openvr_config { + session_manager_lock.session_mut().openvr_config = new_openvr_config; control_sender.send(&ServerControlPacket::Restarting).ok(); @@ -868,7 +869,7 @@ fn connection_pipeline( }; let controllers_config = { - let data_lock = SERVER_DATA_MANAGER.read(); + let data_lock = SESSION_MANAGER.read(); data_lock .settings() .headset @@ -881,8 +882,8 @@ fn connection_pipeline( let hand_skeletons; { let mut tracking_manager_lock = tracking_manager.lock(); - let data_manager_lock = SERVER_DATA_MANAGER.read(); - let headset_config = &data_manager_lock.settings().headset; + let session_manager_lock = SESSION_MANAGER.read(); + let headset_config = &session_manager_lock.settings().headset; motions = tracking_manager_lock.transform_motions( headset_config, @@ -910,8 +911,8 @@ fn connection_pipeline( .unwrap_or_default(); { - let data_manager_lock = SERVER_DATA_MANAGER.read(); - if data_manager_lock.settings().extra.logging.log_tracking { + let session_manager_lock = SESSION_MANAGER.read(); + if session_manager_lock.settings().extra.logging.log_tracking { alvr_events::send_event(EventType::Tracking(Box::new(TrackingEvent { device_motions: motions .iter() @@ -936,9 +937,9 @@ fn connection_pipeline( } let track_body = { - let data_manager_lock = SERVER_DATA_MANAGER.read(); + let session_manager_lock = SESSION_MANAGER.read(); matches!( - data_manager_lock.settings().headset.body_tracking, + session_manager_lock.settings().headset.body_tracking, Switch::Enabled(BodyTrackingConfig { tracked: true, .. }) ) }; @@ -1039,9 +1040,9 @@ fn connection_pipeline( .send(ServerCoreEvent::GameRenderLatencyFeedback(game_latency)) .ok(); - let server_data_lock = SERVER_DATA_MANAGER.read(); + let session_manager_lock = SESSION_MANAGER.read(); ctx.bitrate_manager.lock().report_frame_latencies( - &server_data_lock.settings().video.bitrate.mode, + &session_manager_lock.settings().video.bitrate.mode, timestamp, network_latency, decoder_latency, @@ -1075,7 +1076,11 @@ fn connection_pipeline( let control_receive_thread = thread::spawn({ let ctx = Arc::clone(&ctx); - let controllers_config = server_data_lock.settings().headset.controllers.as_option(); + let controllers_config = session_manager_lock + .settings() + .headset + .controllers + .as_option(); let mut controller_button_mapping_manager = controllers_config.map(|config| { if let Some(mappings) = &config.button_mappings { ButtonMappingManager::new_manual(mappings) @@ -1118,8 +1123,8 @@ fn connection_pipeline( match packet { ClientControlPacket::PlayspaceSync(packet) => { if !settings.headset.tracking_ref_only { - let data_manager_lock = SERVER_DATA_MANAGER.read(); - let config = &data_manager_lock.settings().headset; + let session_manager_lock = SESSION_MANAGER.read(); + let config = &session_manager_lock.settings().headset; tracking_manager.lock().recenter( config.position_recentering_mode, config.rotation_recentering_mode, @@ -1192,8 +1197,8 @@ fn connection_pipeline( } ClientControlPacket::Buttons(entries) => { { - let data_manager_lock = SERVER_DATA_MANAGER.read(); - if data_manager_lock + let session_manager_lock = SESSION_MANAGER.read(); + if session_manager_lock .settings() .extra .logging @@ -1231,7 +1236,7 @@ fn connection_pipeline( } ClientControlPacket::ActiveInteractionProfile { profile_id, .. } => { controller_button_mapping_manager = if let Switch::Enabled(config) = - &SERVER_DATA_MANAGER.read().settings().headset.controllers + &SESSION_MANAGER.read().settings().headset.controllers { if let Some(mappings) = &config.button_mappings { Some(ButtonMappingManager::new_manual(mappings)) @@ -1272,7 +1277,7 @@ fn connection_pipeline( .. } => { controller_button_mapping_manager = if let Switch::Enabled(config) = - &SERVER_DATA_MANAGER.read().settings().headset.controllers + &SESSION_MANAGER.read().settings().headset.controllers { if let Some(mappings) = &config.button_mappings { Some(ButtonMappingManager::new_manual(mappings)) @@ -1325,7 +1330,7 @@ fn connection_pipeline( let disconnect_notif = Arc::clone(&disconnect_notif); let client_hostname = client_hostname.clone(); move || { - while SERVER_DATA_MANAGER + while SESSION_MANAGER .read() .client_list() .get(&client_hostname) @@ -1355,10 +1360,10 @@ fn connection_pipeline( } if settings.extra.capture.startup_video_recording { - crate::create_recording_file(&ctx, server_data_lock.settings()); + crate::create_recording_file(&ctx, session_manager_lock.settings()); } - server_data_lock.update_client_list( + session_manager_lock.update_client_list( client_hostname.clone(), ClientListAction::SetConnectionState(ConnectionState::Streaming), ); @@ -1367,7 +1372,7 @@ fn connection_pipeline( .send(ServerCoreEvent::ClientConnected) .ok(); - alvr_common::wait_rwlock(&disconnect_notif, &mut server_data_lock); + alvr_common::wait_rwlock(&disconnect_notif, &mut session_manager_lock); // This requests shutdown from threads *ctx.video_channel_sender.lock() = None; @@ -1375,12 +1380,12 @@ fn connection_pipeline( *ctx.video_recording_file.lock() = None; - server_data_lock.update_client_list( + session_manager_lock.update_client_list( client_hostname.clone(), ClientListAction::SetConnectionState(ConnectionState::Disconnecting), ); - let on_disconnect_script = server_data_lock + let on_disconnect_script = session_manager_lock .settings() .connection .on_disconnect_script @@ -1396,7 +1401,7 @@ fn connection_pipeline( } // Allow threads to shutdown correctly - drop(server_data_lock); + drop(session_manager_lock); // Ensure shutdown of threads video_send_thread.join().ok(); diff --git a/alvr/server_core/src/lib.rs b/alvr/server_core/src/lib.rs index 10814021e5..7a5dcb4794 100644 --- a/alvr/server_core/src/lib.rs +++ b/alvr/server_core/src/lib.rs @@ -31,7 +31,7 @@ use alvr_packets::{ BatteryInfo, ButtonEntry, ClientListAction, DecoderInitializationConfig, Haptics, Tracking, VideoPacketHeader, }; -use alvr_server_io::ServerDataManager; +use alvr_server_io::ServerSessionManager; use alvr_session::{CodecType, OpenvrProperty, Settings}; use alvr_sockets::StreamSender; use bitrate::{BitrateManager, DynamicEncoderParams}; @@ -44,7 +44,7 @@ use std::{ sync::{ atomic::{AtomicBool, Ordering}, mpsc::{self, SyncSender, TrySendError}, - Arc, + Arc, OnceLock, }, thread::{self, JoinHandle}, time::{Duration, Instant}, @@ -52,14 +52,21 @@ use std::{ use sysinfo::{ProcessRefreshKind, RefreshKind}; use tokio::{runtime::Runtime, sync::broadcast}; -static FILESYSTEM_LAYOUT: Lazy = Lazy::new(|| { - afs::filesystem_layout_from_openvr_driver_root_dir( - &alvr_server_io::get_driver_dir_from_registered().unwrap(), - ) +static FILESYSTEM_LAYOUT: OnceLock = OnceLock::new(); + +pub fn initialize_environment(layout: afs::Layout) { + FILESYSTEM_LAYOUT.set(layout).unwrap(); +} + +// This is lazily initialized when initializing logging or ServerCoreContext. So FILESYSTEM_LAYOUT +// needs to be initialized first using initialize_environment(). +// NB: this must remain a global because only one instance should exist for the whole application +// execution time. +static SESSION_MANAGER: Lazy> = Lazy::new(|| { + RwLock::new(ServerSessionManager::new( + FILESYSTEM_LAYOUT.get().map(|l| l.session()), + )) }); -// NB: this must remain a global because only one instance should exist at a time -static SERVER_DATA_MANAGER: Lazy> = - Lazy::new(|| RwLock::new(ServerDataManager::new(&FILESYSTEM_LAYOUT.session()))); // todo: use this as the network packet pub struct ViewsConfig { @@ -111,7 +118,7 @@ pub fn create_recording_file(connection_context: &ConnectionContext, settings: & CodecType::AV1 => "av1", }; - let path = FILESYSTEM_LAYOUT.log_dir.join(format!( + let path = FILESYSTEM_LAYOUT.get().unwrap().log_dir.join(format!( "recording.{}.{ext}", chrono::Local::now().format("%F.%H-%M-%S") )); @@ -153,12 +160,12 @@ pub fn notify_restart_driver() { } pub fn settings() -> Settings { - SERVER_DATA_MANAGER.read().settings().clone() + SESSION_MANAGER.read().settings().clone() } pub fn registered_button_set() -> HashSet { - let data_manager = SERVER_DATA_MANAGER.read(); - if let Switch::Enabled(input_mapping) = &data_manager.settings().headset.controllers { + let session_manager = SESSION_MANAGER.read(); + if let Switch::Enabled(input_mapping) = &session_manager.settings().headset.controllers { input_mapping::registered_button_set(&input_mapping.emulation_mode) } else { HashSet::new() @@ -175,7 +182,7 @@ pub struct ServerCoreContext { impl ServerCoreContext { pub fn new() -> (Self, mpsc::Receiver) { - if SERVER_DATA_MANAGER + if SESSION_MANAGER .read() .settings() .extra @@ -185,7 +192,7 @@ impl ServerCoreContext { env::set_var("RUST_BACKTRACE", "1"); } - SERVER_DATA_MANAGER.write().clean_client_list(); + SESSION_MANAGER.write().clean_client_list(); let (events_sender, events_receiver) = mpsc::channel(); @@ -234,9 +241,9 @@ impl ServerCoreContext { pub fn send_haptics(&self, haptics: Haptics) { let haptics_config = { - let data_manager_lock = SERVER_DATA_MANAGER.read(); + let session_manager_lock = SESSION_MANAGER.read(); - if data_manager_lock.settings().extra.logging.log_haptics { + if session_manager_lock.settings().extra.logging.log_haptics { alvr_events::send_event(EventType::Haptics(HapticsEvent { path: DEVICE_ID_TO_PATH .get(&haptics.device_id) @@ -248,7 +255,7 @@ impl ServerCoreContext { })) } - data_manager_lock + session_manager_lock .settings() .headset .controllers @@ -293,7 +300,7 @@ impl ServerCoreContext { STREAM_CORRUPTED.store(false, Ordering::SeqCst); } - if let Switch::Enabled(config) = &SERVER_DATA_MANAGER + if let Switch::Enabled(config) = &SESSION_MANAGER .read() .settings() .extra @@ -311,7 +318,7 @@ impl ServerCoreContext { if is_idr { create_recording_file( &self.connection_context, - SERVER_DATA_MANAGER.read().settings(), + SESSION_MANAGER.read().settings(), ); *LAST_IDR_INSTANT.lock() = Instant::now(); } @@ -319,7 +326,7 @@ impl ServerCoreContext { } if !STREAM_CORRUPTED.load(Ordering::SeqCst) - || !SERVER_DATA_MANAGER + || !SESSION_MANAGER .read() .settings() .connection @@ -367,11 +374,11 @@ impl ServerCoreContext { pub fn get_dynamic_encoder_params(&self) -> Option { let pair = { - let server_data_lock = SERVER_DATA_MANAGER.read(); + let session_manager_lock = SESSION_MANAGER.read(); self.connection_context .bitrate_manager .lock() - .get_encoder_params(&server_data_lock.settings().video.bitrate) + .get_encoder_params(&session_manager_lock.settings().video.bitrate) }; if let Some((params, stats)) = pair { @@ -396,11 +403,17 @@ impl ServerCoreContext { stats.report_frame_present(target_timestamp, offset); } - let server_data_lock = SERVER_DATA_MANAGER.read(); + let session_manager_lock = SESSION_MANAGER.read(); self.connection_context .bitrate_manager .lock() - .report_frame_present(&server_data_lock.settings().video.bitrate.adapt_to_framerate); + .report_frame_present( + &session_manager_lock + .settings() + .video + .bitrate + .adapt_to_framerate, + ); } pub fn duration_until_next_vsync(&self) -> Option { @@ -424,9 +437,9 @@ impl Drop for ServerCoreContext { *self.lifecycle_state.write() = LifecycleState::ShuttingDown; { - let mut data_manager_lock = SERVER_DATA_MANAGER.write(); + let mut session_manager_lock = SESSION_MANAGER.write(); - let hostnames = data_manager_lock + let hostnames = session_manager_lock .client_list() .iter() .filter(|&(_, info)| { @@ -439,7 +452,7 @@ impl Drop for ServerCoreContext { .collect::>(); for hostname in hostnames { - data_manager_lock.update_client_list( + session_manager_lock.update_client_list( hostname, ClientListAction::SetConnectionState(ConnectionState::Disconnecting), ); @@ -452,22 +465,17 @@ impl Drop for ServerCoreContext { // apply openvr config for the next launch { - let mut server_data_lock = SERVER_DATA_MANAGER.write(); - server_data_lock.session_mut().openvr_config = - connection::contruct_openvr_config(server_data_lock.session()); + let mut session_manager_lock = SESSION_MANAGER.write(); + session_manager_lock.session_mut().openvr_config = + connection::contruct_openvr_config(session_manager_lock.session()); } - if let Some(backup) = SERVER_DATA_MANAGER - .write() - .session_mut() - .drivers_backup - .take() - { + if let Some(backup) = SESSION_MANAGER.write().session_mut().drivers_backup.take() { alvr_server_io::driver_registration(&backup.other_paths, true).ok(); alvr_server_io::driver_registration(&[backup.alvr_path], false).ok(); } - while SERVER_DATA_MANAGER + while SESSION_MANAGER .read() .client_list() .iter() diff --git a/alvr/server_core/src/logging_backend.rs b/alvr/server_core/src/logging_backend.rs index 4d60d07cd0..586dc914aa 100644 --- a/alvr/server_core/src/logging_backend.rs +++ b/alvr/server_core/src/logging_backend.rs @@ -1,16 +1,15 @@ -use crate::{FILESYSTEM_LAYOUT, SERVER_DATA_MANAGER}; use alvr_common::{log::LevelFilter, once_cell::sync::Lazy, LogEntry, LogSeverity}; use alvr_events::{Event, EventType}; use chrono::Local; use fern::Dispatch; -use std::fs; +use std::{fs, path::PathBuf}; use tokio::sync::broadcast; static CHANNEL_CAPACITY: usize = 256; pub static LOGGING_EVENTS_SENDER: Lazy> = Lazy::new(|| broadcast::channel(CHANNEL_CAPACITY).0); -pub fn init_logging() { +pub fn init_logging(session_log_path: Option, crash_log_path: Option) { let mut log_dispatch = Dispatch::new() // Note: meta::target() is in the format :: .filter(|meta| !meta.target().starts_with("mdns_sd")) @@ -39,43 +38,45 @@ pub fn init_logging() { log_dispatch = log_dispatch.level(LevelFilter::Info); } - if SERVER_DATA_MANAGER - .read() - .settings() - .extra - .logging - .log_to_disk - { - log_dispatch = log_dispatch.chain( + log_dispatch = if let Some(path) = session_log_path { + log_dispatch.chain( fs::OpenOptions::new() .write(true) .create(true) .truncate(true) - .open(FILESYSTEM_LAYOUT.session_log()) + .open(path) .unwrap(), - ); - } else { + ) + } else if cfg!(target_os = "linux") { // this sink is required to make sure all log gets processed and forwarded to the websocket - if cfg!(target_os = "linux") { - log_dispatch = log_dispatch.chain( - fs::OpenOptions::new() - .write(true) - .open("/dev/null") - .unwrap(), - ); - } else { - log_dispatch = log_dispatch.chain(std::io::stdout()); - } - } + log_dispatch.chain( + fs::OpenOptions::new() + .write(true) + .open("/dev/null") + .unwrap(), + ) + } else { + log_dispatch.chain(std::io::stdout()) + }; - log_dispatch - .chain( + log_dispatch = if let Some(path) = crash_log_path { + log_dispatch.chain( Dispatch::new() .level(LevelFilter::Error) - .chain(fern::log_file(FILESYSTEM_LAYOUT.crash_log()).unwrap()), + .chain(fern::log_file(path).unwrap()), ) - .apply() - .unwrap(); + } else if cfg!(target_os = "linux") { + log_dispatch.chain( + fs::OpenOptions::new() + .write(true) + .open("/dev/null") + .unwrap(), + ) + } else { + log_dispatch.chain(std::io::stderr()) + }; + + log_dispatch.apply().unwrap(); alvr_common::set_panic_hook(); } diff --git a/alvr/server_core/src/web_server.rs b/alvr/server_core/src/web_server.rs index 7b4b76c9ac..0b7efd01a8 100644 --- a/alvr/server_core/src/web_server.rs +++ b/alvr/server_core/src/web_server.rs @@ -1,6 +1,6 @@ use crate::{ logging_backend::LOGGING_EVENTS_SENDER, ConnectionContext, ServerCoreEvent, FILESYSTEM_LAYOUT, - SERVER_DATA_MANAGER, + SESSION_MANAGER, }; use alvr_common::{ anyhow::{self, Result}, @@ -12,7 +12,7 @@ use bytes::Buf; use futures::SinkExt; use headers::HeaderMapExt; use hyper::{ - header::{self, HeaderValue, ACCESS_CONTROL_ALLOW_ORIGIN, CACHE_CONTROL, CONTENT_TYPE}, + header::{self, HeaderValue, ACCESS_CONTROL_ALLOW_ORIGIN, CACHE_CONTROL}, service, Body, Request, Response, StatusCode, }; use serde::de::DeserializeOwned; @@ -20,7 +20,6 @@ use serde_json as json; use std::{net::SocketAddr, sync::Arc}; use tokio::sync::broadcast::{self, error::RecvError}; use tokio_tungstenite::{tungstenite::protocol, WebSocketStream}; -use tokio_util::codec::{BytesCodec, FramedRead}; pub const WS_BROADCAST_CAPACITY: usize = 256; @@ -102,22 +101,22 @@ async fn http_api( } ServerRequest::GetSession => { alvr_events::send_event(EventType::Session(Box::new( - SERVER_DATA_MANAGER.read().session().clone(), + crate::SESSION_MANAGER.read().session().clone(), ))); } ServerRequest::UpdateSession(session) => { - *SERVER_DATA_MANAGER.write().session_mut() = *session + *SESSION_MANAGER.write().session_mut() = *session } ServerRequest::SetValues(descs) => { - SERVER_DATA_MANAGER.write().set_values(descs).ok(); + SESSION_MANAGER.write().set_values(descs).ok(); } ServerRequest::UpdateClientList { hostname, mut action, } => { - let mut data_manager = SERVER_DATA_MANAGER.write(); + let mut session_manager = SESSION_MANAGER.write(); if matches!(action, ClientListAction::RemoveEntry) { - if let Some(entry) = data_manager.client_list().get(&hostname) { + if let Some(entry) = session_manager.client_list().get(&hostname) { if entry.connection_state != ConnectionState::Disconnected { connection_context .clients_to_be_removed @@ -131,10 +130,10 @@ async fn http_api( } } - data_manager.update_client_list(hostname, action); + session_manager.update_client_list(hostname, action); } ServerRequest::GetAudioDevices => { - if let Ok(list) = SERVER_DATA_MANAGER.read().get_audio_devices_list() { + if let Ok(list) = crate::SESSION_MANAGER.read().get_audio_devices_list() { alvr_events::send_event(EventType::AudioDevices(list)); } } @@ -152,7 +151,7 @@ async fn http_api( } ServerRequest::StartRecording => crate::create_recording_file( connection_context, - SERVER_DATA_MANAGER.read().settings(), + crate::SESSION_MANAGER.read().settings(), ), ServerRequest::StopRecording => { *connection_context.video_recording_file.lock() = None @@ -166,7 +165,11 @@ async fn http_api( } ServerRequest::RegisterAlvrDriver => { alvr_server_io::driver_registration( - &[FILESYSTEM_LAYOUT.openvr_driver_root_dir.clone()], + &[FILESYSTEM_LAYOUT + .get() + .unwrap() + .openvr_driver_root_dir + .clone()], true, ) .ok(); @@ -267,37 +270,7 @@ async fn http_api( .body(latency.to_string().into())? } "/api/ping" => reply(StatusCode::OK)?, - other_uri => { - if other_uri.contains("..") { - // Attempted tree traversal - reply(StatusCode::FORBIDDEN)? - } else { - let path_branch = match other_uri { - "/" => "/index.html", - other_path => other_path, - }; - - let maybe_file = tokio::fs::File::open(format!( - "{}{path_branch}", - FILESYSTEM_LAYOUT.dashboard_dir().to_string_lossy(), - )) - .await; - - if let Ok(file) = maybe_file { - let mut builder = Response::builder(); - if other_uri.ends_with(".js") { - builder = builder.header(CONTENT_TYPE, "text/javascript"); - } - if other_uri.ends_with(".wasm") { - builder = builder.header(CONTENT_TYPE, "application/wasm"); - } - - builder.body(Body::wrap_stream(FramedRead::new(file, BytesCodec::new())))? - } else { - reply(StatusCode::NOT_FOUND)? - } - } - } + _ => reply(StatusCode::NOT_FOUND)?, }; response.headers_mut().insert( @@ -312,7 +285,7 @@ async fn http_api( } pub async fn web_server(connection_context: Arc) -> Result<()> { - let web_server_port = SERVER_DATA_MANAGER + let web_server_port = crate::SESSION_MANAGER .read() .settings() .connection diff --git a/alvr/server_io/src/lib.rs b/alvr/server_io/src/lib.rs index 95a911e9bf..67986cf442 100644 --- a/alvr/server_io/src/lib.rs +++ b/alvr/server_io/src/lib.rs @@ -16,6 +16,7 @@ use alvr_session::{ClientConnectionConfig, SessionConfig, Settings}; use serde_json as json; use std::{ collections::{hash_map::Entry, HashMap}, + fmt::{self, Debug}, fs, ops::{Deref, DerefMut}, path::{Path, PathBuf}, @@ -30,7 +31,7 @@ fn save_session(session: &SessionConfig, path: &Path) -> Result<()> { // SessionConfig wrapper that saves session.json on destruction. pub struct SessionLock<'a> { session_desc: &'a mut SessionConfig, - session_path: &'a Path, + session_path: Option<&'a Path>, settings: &'a mut Settings, } @@ -49,7 +50,10 @@ impl DerefMut for SessionLock<'_> { impl Drop for SessionLock<'_> { fn drop(&mut self) { - save_session(self.session_desc, self.session_path).unwrap(); + if let Some(session_path) = self.session_path { + save_session(self.session_desc, session_path).ok(); + } + *self.settings = self.session_desc.to_settings(); alvr_events::send_event(EventType::Session(Box::new(self.session_desc.clone()))); } @@ -60,21 +64,25 @@ impl Drop for SessionLock<'_> { // read, within the same lock. // fixme: the dashboard is doing this wrong because it is holding its own session state. If read and // write need to happen on separate threads, a critical region should be implemented. -pub struct ServerDataManager { - session: SessionConfig, +pub struct ServerSessionManager { + session_config: SessionConfig, settings: Settings, - session_path: PathBuf, + session_path: Option, } -impl ServerDataManager { - pub fn new(session_path: &Path) -> Self { - let config_dir = session_path.parent().unwrap(); - fs::create_dir_all(config_dir).ok(); - let session_desc = Self::load_session(session_path, config_dir); +impl ServerSessionManager { + pub fn new(session_path: Option) -> Self { + let session_config = if let Some(session_path) = &session_path { + let config_dir = session_path.parent().unwrap(); + fs::create_dir_all(config_dir).ok(); + Self::load_session(session_path, config_dir) + } else { + SessionConfig::default() + }; Self { - session: session_desc.clone(), - settings: session_desc.to_settings(), + session_config: session_config.clone(), + settings: session_config.to_settings(), session_path: session_path.to_owned(), } } @@ -128,13 +136,13 @@ impl ServerDataManager { // prefer settings() pub fn session(&self) -> &SessionConfig { - &self.session + &self.session_config } pub fn session_mut(&mut self) -> SessionLock { SessionLock { - session_desc: &mut self.session, - session_path: &self.session_path, + session_desc: &mut self.session_config, + session_path: self.session_path.as_deref(), settings: &mut self.settings, } } @@ -145,7 +153,7 @@ impl ServerDataManager { // Note: "value" can be any session subtree, in json format. pub fn set_values(&mut self, descs: Vec) -> Result<()> { - let mut session_json = serde_json::to_value(self.session.clone()).unwrap(); + let mut session_json = serde_json::to_value(self.session_config.clone()).unwrap(); for desc in descs { let mut session_ref = &mut session_json; @@ -171,21 +179,24 @@ impl ServerDataManager { } // session_json has been updated - self.session = serde_json::from_value(session_json)?; - self.settings = self.session.to_settings(); + self.session_config = serde_json::from_value(session_json)?; + self.settings = self.session_config.to_settings(); + + if let Some(session_path) = &self.session_path { + save_session(&self.session_config, session_path)?; + } - save_session(&self.session, &self.session_path).unwrap(); - alvr_events::send_event(EventType::Session(Box::new(self.session.clone()))); + alvr_events::send_event(EventType::Session(Box::new(self.session_config.clone()))); Ok(()) } pub fn client_list(&self) -> &HashMap { - &self.session.client_connections + &self.session_config.client_connections } pub fn update_client_list(&mut self, hostname: String, action: ClientListAction) { - let mut client_connections = self.session.client_connections.clone(); + let mut client_connections = self.session_config.client_connections.clone(); let maybe_client_entry = client_connections.entry(hostname); @@ -258,15 +269,21 @@ impl ServerDataManager { } if updated { - self.session.client_connections = client_connections; + self.session_config.client_connections = client_connections; - save_session(&self.session, &self.session_path).unwrap(); - alvr_events::send_event(EventType::Session(Box::new(self.session.clone()))); + if let Some(session_path) = &self.session_path { + save_session(&self.session_config, session_path).ok(); + } + alvr_events::send_event(EventType::Session(Box::new(self.session_config.clone()))); } } pub fn client_hostnames(&self) -> Vec { - self.session.client_connections.keys().cloned().collect() + self.session_config + .client_connections + .keys() + .cloned() + .collect() } // Run at the start of dashboard or server @@ -316,4 +333,8 @@ impl ServerDataManager { } } -pub fn prepare_client_list() {} +impl Debug for ServerSessionManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.session_path) + } +} diff --git a/alvr/server_openvr/src/lib.rs b/alvr/server_openvr/src/lib.rs index 9ef4464b75..c178cd2a89 100644 --- a/alvr/server_openvr/src/lib.rs +++ b/alvr/server_openvr/src/lib.rs @@ -377,7 +377,14 @@ pub unsafe extern "C" fn HmdDriverFactory( ) -> *mut c_void { static ONCE: Once = Once::new(); ONCE.call_once(|| { - alvr_server_core::init_logging(); + alvr_server_core::initialize_environment(FILESYSTEM_LAYOUT.clone()); + + let log_to_disk = alvr_server_core::settings().extra.logging.log_to_disk; + + alvr_server_core::init_logging( + log_to_disk.then(|| FILESYSTEM_LAYOUT.session_log()), + Some(FILESYSTEM_LAYOUT.crash_log()), + ); unsafe { g_sessionPath = CString::new(FILESYSTEM_LAYOUT.session().to_string_lossy().to_string())