From 4ea0ce53b0073b688c403e52f5c86591d0f9665d Mon Sep 17 00:00:00 2001 From: GabMus Date: Wed, 4 Dec 2024 19:14:14 +0000 Subject: [PATCH] feat: prefer symlinks over generating files for openxr active runtime json file --- src/file_builders/active_runtime_json.rs | 142 ++++++++++------------- src/main.rs | 11 +- src/profile.rs | 13 +++ src/ui/app.rs | 14 ++- src/util/file_utils.rs | 16 ++- 5 files changed, 102 insertions(+), 94 deletions(-) diff --git a/src/file_builders/active_runtime_json.rs b/src/file_builders/active_runtime_json.rs index f3461ae..3cc7138 100644 --- a/src/file_builders/active_runtime_json.rs +++ b/src/file_builders/active_runtime_json.rs @@ -1,18 +1,17 @@ use crate::{ - paths::{get_backup_dir, SYSTEM_PREFIX}, + paths::SYSTEM_PREFIX, profile::Profile, - util::{ - file_utils::{copy_file, deserialize_file, get_writer, set_file_readonly}, - steam_library_folder::SteamLibraryFolder, - }, + util::file_utils::{deserialize_file, get_writer, set_file_readonly}, xdg::XDG, }; +use anyhow::bail; use serde::{Deserialize, Serialize}; use std::{ - fs::remove_file, + fs::{remove_file, rename}, + os::unix::fs::symlink, path::{Path, PathBuf}, }; -use tracing::error; +use tracing::{info, warn}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ActiveRuntimeInnerRuntime { @@ -38,46 +37,6 @@ fn get_active_runtime_json_path() -> PathBuf { get_openxr_conf_dir().join("1/active_runtime.json") } -pub fn is_steam(active_runtime: &ActiveRuntime) -> bool { - matches!(active_runtime.runtime.valve_runtime_is_steamvr, Some(true)) -} - -pub const STEAMVR_STEAM_APPID: u32 = 250820; - -pub fn find_steam_openxr_json() -> Option { - match SteamLibraryFolder::get_folders() { - Ok(libraryfolders) => libraryfolders - .iter() - .find(|(_, folder)| folder.apps.contains_key(&STEAMVR_STEAM_APPID)) - .map(|(_, folder)| { - PathBuf::from(&folder.path).join("steamapps/common/SteamVR/steamxr_linux64.json") - }), - Err(e) => { - error!("unable to find steam openxr json: unable to load steam libraryfolders: {e}"); - None - } - } -} - -fn get_backup_steam_active_runtime_path() -> PathBuf { - get_backup_dir().join("active_runtime.json.steam.bak") -} - -fn get_backed_up_steam_active_runtime() -> Option { - get_active_runtime_from_path(&get_backup_steam_active_runtime_path()) -} - -fn backup_steam_active_runtime() { - if let Some(ar) = get_current_active_runtime() { - if is_steam(&ar) { - copy_file( - &get_active_runtime_json_path(), - &get_backup_steam_active_runtime_path(), - ); - } - } -} - fn get_active_runtime_from_path(path: &Path) -> Option { deserialize_file(path) } @@ -99,29 +58,6 @@ pub fn dump_current_active_runtime(active_runtime: &ActiveRuntime) -> anyhow::Re dump_active_runtime_to_path(active_runtime, &get_active_runtime_json_path()) } -fn build_steam_active_runtime() -> ActiveRuntime { - if let Some(backup) = get_backed_up_steam_active_runtime() { - return backup; - } - ActiveRuntime { - file_format_version: "1.0.0".into(), - runtime: ActiveRuntimeInnerRuntime { - valve_runtime_is_steamvr: Some(true), - libmonado_path: None, - library_path: XDG - .get_data_home() - .join("Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so"), - name: Some("SteamVR".into()), - }, - } -} - -pub fn set_current_active_runtime_to_steam() -> anyhow::Result<()> { - set_file_readonly(&get_active_runtime_json_path(), false)?; - dump_current_active_runtime(&build_steam_active_runtime())?; - Ok(()) -} - pub fn build_profile_active_runtime(profile: &Profile) -> anyhow::Result { let Some(libopenxr_path) = profile.libopenxr_so() else { anyhow::bail!( @@ -158,18 +94,66 @@ fn relativize_active_runtime_lib_path(ar: &ActiveRuntime, path: &Path) -> Active res } +const ACTIVE_RUNTIME_BAK: &str = "active_runtime.json.envision.bak"; + pub fn set_current_active_runtime_to_profile(profile: &Profile) -> anyhow::Result<()> { let dest = get_active_runtime_json_path(); - set_file_readonly(&dest, false)?; - backup_steam_active_runtime(); - let pfx = profile.clone().prefix; - let mut ar = build_profile_active_runtime(profile)?; - // hack: relativize libopenxr_monado.so path for system installs - if pfx == PathBuf::from(SYSTEM_PREFIX) { - ar = relativize_active_runtime_lib_path(&ar, &dest); + if dest.is_dir() { + bail!("{} is a directory", dest.to_string_lossy()); } - dump_current_active_runtime(&ar)?; - set_file_readonly(&dest, true)?; + if !dest.is_symlink() { + set_file_readonly(&dest, false)?; + } + if dest.is_file() || dest.is_symlink() { + rename(&dest, dest.parent().unwrap().join(ACTIVE_RUNTIME_BAK))?; + } else { + info!("no active_runtime.json file to backup") + } + + let profile_openxr_json = profile.openxr_json_path(); + if profile_openxr_json.is_file() { + symlink(profile_openxr_json, &dest)?; + } else { + warn!("profile openxr json file doesn't exist"); + // fallback: build the file from scratch + let pfx = profile.clone().prefix; + let mut ar = build_profile_active_runtime(profile)?; + // hack: relativize libopenxr_monado.so path for system installs + if pfx == PathBuf::from(SYSTEM_PREFIX) { + ar = relativize_active_runtime_lib_path(&ar, &dest); + } + dump_current_active_runtime(&ar)?; + set_file_readonly(&dest, true)?; + } + Ok(()) +} + +pub fn remove_current_active_runtime() -> anyhow::Result<()> { + let dest = get_active_runtime_json_path(); + if dest.is_dir() { + bail!("{} is a directory", dest.to_string_lossy()); + } + if !dest.exists() { + info!("no current active_runtime.json to remove") + } + Ok(remove_file(dest)?) +} + +pub fn restore_active_runtime_backup() -> anyhow::Result<()> { + let dest = get_active_runtime_json_path(); + let bak = dest.parent().unwrap().join(ACTIVE_RUNTIME_BAK); + if bak.is_file() || bak.is_symlink() { + if dest.is_dir() { + bail!("{} is a directory", dest.to_string_lossy()); + } + if !dest.is_symlink() { + set_file_readonly(&dest, false)?; + } + rename(&bak, &dest)?; + } else { + info!("{ACTIVE_RUNTIME_BAK} does not exist, nothing to restore"); + } + Ok(()) } diff --git a/src/main.rs b/src/main.rs index a13a2ce..4453911 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use anyhow::Result; use constants::{resources, APP_ID, APP_NAME, GETTEXT_PACKAGE, LOCALE_DIR, RESOURCES_BASE_PATH}; use file_builders::{ - active_runtime_json::{get_current_active_runtime, set_current_active_runtime_to_steam}, + active_runtime_json::restore_active_runtime_backup, openvrpaths_vrpath::{get_current_openvrpaths, set_current_openvrpaths_to_steam}, }; use gettextrs::LocaleCategory; @@ -48,14 +48,9 @@ pub mod xdg; pub mod xr_devices; fn restore_steam_xr_files() { - let active_runtime = get_current_active_runtime(); let openvrpaths = get_current_openvrpaths(); - if let Some(ar) = active_runtime { - if !file_builders::active_runtime_json::is_steam(&ar) { - if let Err(e) = set_current_active_runtime_to_steam() { - warn!("failed to restore active runtime to steam: {e}"); - } - } + if let Err(e) = restore_active_runtime_backup() { + warn!("failed to restore active runtime to steam: {e}"); } if let Some(ovrp) = openvrpaths { if !file_builders::openvrpaths_vrpath::is_steam(&ovrp) { diff --git a/src/profile.rs b/src/profile.rs index a0a3903..e199980 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -47,6 +47,13 @@ impl XRServiceType { } } + pub fn openxr_json_rel_path(&self) -> &'static str { + match self { + Self::Monado => "share/openxr/1/openxr_monado.json", + Self::Wivrn => "share/openxr/1/openxr_wivrn.json", + } + } + pub fn ipc_file_path(&self) -> PathBuf { XDG.get_runtime_directory() .expect("XDG runtime directory is not available") @@ -586,6 +593,12 @@ impl Profile { missing_deps.dedup(); // dedup only works if sorted, hence the above missing_deps } + + /// the file that will become active_runtime.json, as installed in the + /// prefix + pub fn openxr_json_path(&self) -> PathBuf { + self.prefix.join(self.xrservice_type.openxr_json_rel_path()) + } } pub fn prepare_ld_library_path(prefix: &Path) -> String { diff --git a/src/ui/app.rs b/src/ui/app.rs index 34c5549..54f3d31 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -26,7 +26,8 @@ use crate::{ depcheck::common::dep_pkexec, file_builders::{ active_runtime_json::{ - set_current_active_runtime_to_profile, set_current_active_runtime_to_steam, + remove_current_active_runtime, restore_active_runtime_backup, + set_current_active_runtime_to_profile, }, openvrpaths_vrpath::{ set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam, @@ -250,9 +251,16 @@ impl App { pub fn restore_openxr_openvr_files(&self) { restore_runtime_entrypoint(); - if let Err(e) = set_current_active_runtime_to_steam() { + if let Err(e) = remove_current_active_runtime() { alert( - "Could not restore Steam active runtime", + "Could not remove profile active runtime", + Some(&format!("{e}")), + Some(&self.app_win.clone().upcast::()), + ); + } + if let Err(e) = restore_active_runtime_backup() { + alert( + "Could not restore previous active runtime", Some(&format!("{e}")), Some(&self.app_win.clone().upcast::()), ); diff --git a/src/util/file_utils.rs b/src/util/file_utils.rs index 04ef1bc..59f8dad 100644 --- a/src/util/file_utils.rs +++ b/src/util/file_utils.rs @@ -57,7 +57,13 @@ pub fn deserialize_file(path: &Path) -> Option Result<(), std::io::Error> { +pub fn set_file_readonly(path: &Path, readonly: bool) -> anyhow::Result<()> { + if path.is_symlink() { + bail!( + "path {} is a symlink, trying to change its write permission will only change the original file", + path.to_string_lossy() + ); + } if !path.is_file() { debug!( "trying to set readonly on a file that does not exist: {}", @@ -69,7 +75,7 @@ pub fn set_file_readonly(path: &Path, readonly: bool) -> Result<(), std::io::Err .expect("Could not get metadata for file") .permissions(); perms.set_readonly(readonly); - fs::set_permissions(path, perms) + Ok(fs::set_permissions(path, perms)?) } pub fn setcap_cap_sys_nice_eip_cmd(profile: &Profile) -> Vec { @@ -104,8 +110,10 @@ pub fn copy_file(source: &Path, dest: &Path) { .unwrap_or_else(|_| panic!("Failed to create dir {}", parent.to_str().unwrap())); } } - set_file_readonly(dest, false) - .unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest.to_string_lossy())); + if !dest.is_symlink() { + set_file_readonly(dest, false) + .unwrap_or_else(|_| panic!("Failed to set file {} as rw", dest.to_string_lossy())); + } copy(source, dest).unwrap_or_else(|e| { panic!( "Failed to copy {} to {}: {e}",