diff --git a/src/ui/app.rs b/src/ui/app.rs index afe2869..ec33efd 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -43,7 +43,8 @@ use crate::{ restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile, }, util::file_utils::{ - setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd, verify_cap_sys_nice_eip, + mark_as_executable, setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd, + verify_cap_sys_nice_eip, }, vulkaninfo::VulkanInfo, wivrn_dbus, @@ -80,6 +81,7 @@ pub struct App { config: Config, xrservice_worker: Option, autostart_worker: Option, + plugins_worker: Option, restart_xrservice: bool, build_worker: Option, profiles: Vec, @@ -102,6 +104,7 @@ pub enum Msg { OnServiceLog(Vec), OnServiceExit(i32), OnAutostartExit(i32), + OnPluginsExit(i32), OnBuildLog(Vec), OnBuildExit(i32), ClockTicking, @@ -257,6 +260,39 @@ impl App { autostart_worker.start(); self.autostart_worker = Some(autostart_worker); } + + let plugins_cmd = self + .config + .plugins + .values() + .filter_map(|cp| { + if cp.enabled { + if let Err(e) = mark_as_executable(&cp.exec_path) { + eprintln!("Failed to mark plugin {} as executable: {e}", cp.appid); + None + } else { + Some(format!("'{}'", cp.exec_path.to_string_lossy())) + } + } else { + None + } + }) + .collect::>() + .join(" & "); + if !plugins_cmd.is_empty() { + let mut jobs = VecDeque::new(); + jobs.push_back(WorkerJob::new_cmd( + Some(prof.environment.clone()), + "sh".into(), + Some(vec!["-c".into(), plugins_cmd]), + )); + let plugins_worker = JobWorker::new(jobs, sender.input_sender(), |msg| match msg { + JobWorkerOut::Log(rows) => Msg::OnServiceLog(rows), + JobWorkerOut::Exit(code) => Msg::OnPluginsExit(code), + }); + plugins_worker.start(); + self.plugins_worker = Some(plugins_worker); + } } pub fn restore_openxr_openvr_files(&self) { @@ -285,27 +321,20 @@ impl App { } pub fn shutdown_xrservice(&mut self) { - if let Some(worker) = self.autostart_worker.as_ref() { - worker.stop(); + if let Some(w) = self.autostart_worker.as_ref() { + w.stop(); + } + if let Some(w) = self.plugins_worker.as_ref() { + w.stop(); } - self.xrservice_ready = false; if let Some(w) = self.openxr_prober_worker.as_ref() { w.stop(); // this can cause threads to remain hanging... self.openxr_prober_worker = None; } - self.set_inhibit_session(false); - if let Some(worker) = self.xrservice_worker.as_ref() { - worker.stop(); + if let Some(w) = self.xrservice_worker.as_ref() { + w.stop(); } - self.libmonado = None; - self.main_view - .sender() - .emit(MainViewMsg::XRServiceActiveChanged(false, None, false)); - self.debug_view - .sender() - .emit(DebugViewMsg::XRServiceActiveChanged(false)); - self.xr_devices = vec![]; } } @@ -371,6 +400,8 @@ impl AsyncComponent for App { } } Msg::OnServiceExit(code) => { + self.set_inhibit_session(false); + self.xrservice_ready = false; self.restore_openxr_openvr_files(); self.main_view .sender() @@ -378,6 +409,8 @@ impl AsyncComponent for App { self.debug_view .sender() .emit(DebugViewMsg::XRServiceActiveChanged(false)); + self.libmonado = None; + self.xr_devices = vec![]; if code != 0 && code != 15 { // 15 is SIGTERM sender.input(Msg::OnServiceLog(vec![format!( @@ -393,6 +426,7 @@ impl AsyncComponent for App { } } Msg::OnAutostartExit(_) => self.autostart_worker = None, + Msg::OnPluginsExit(_) => self.plugins_worker = None, Msg::ClockTicking => { self.main_view.sender().emit(MainViewMsg::ClockTicking); let xrservice_worker_is_alive = self @@ -998,6 +1032,7 @@ impl AsyncComponent for App { profiles, xrservice_worker: None, autostart_worker: None, + plugins_worker: None, build_worker: None, xr_devices: vec![], restart_xrservice: false, diff --git a/src/ui/plugins/mod.rs b/src/ui/plugins/mod.rs index d402ece..ea5d935 100644 --- a/src/ui/plugins/mod.rs +++ b/src/ui/plugins/mod.rs @@ -2,7 +2,7 @@ pub mod store; mod store_detail; mod store_row_factory; -use crate::paths::get_plugins_dir; +use crate::{paths::get_plugins_dir, util::file_utils::mark_as_executable}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; @@ -41,4 +41,8 @@ impl Plugin { pub fn is_installed(&self) -> bool { self.exec_path().exists() } + + pub fn mark_as_executable(&self) -> anyhow::Result<()> { + mark_as_executable(&self.exec_path()) + } } diff --git a/src/util/file_utils.rs b/src/util/file_utils.rs index 936aacf..ff56926 100644 --- a/src/util/file_utils.rs +++ b/src/util/file_utils.rs @@ -7,6 +7,7 @@ use nix::{ use std::{ fs::{self, copy, create_dir_all, remove_dir_all, File, OpenOptions}, io::{BufReader, BufWriter}, + os::unix::fs::PermissionsExt, path::Path, }; use tracing::{debug, error}; @@ -151,6 +152,17 @@ pub fn mount_has_nosuid(path: &Path) -> Result { } } +pub fn mark_as_executable(path: &Path) -> anyhow::Result<()> { + if !path.is_file() { + bail!("Path '{}' is not a file", path.to_string_lossy()) + } else { + let mut perms = fs::metadata(path)?.permissions(); + perms.set_mode(perms.mode() | 0o111); + fs::set_permissions(path, perms)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use super::mount_has_nosuid;