diff --git a/src/cli.rs b/src/cli.rs index f809a36a..d00b7039 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,6 +8,7 @@ mod input; mod log; mod quit; mod randr; +mod reexec; mod run_privileged; pub mod screenshot; mod seat_test; @@ -19,7 +20,8 @@ use { crate::{ cli::{ color_management::ColorManagementArgs, damage_tracking::DamageTrackingArgs, - idle::IdleCmd, input::InputArgs, randr::RandrArgs, xwayland::XwaylandArgs, + idle::IdleCmd, input::InputArgs, randr::RandrArgs, reexec::ReexecArgs, + xwayland::XwaylandArgs, }, compositor::start_compositor, format::{Format, ref_formats}, @@ -81,6 +83,9 @@ pub enum Cmd { Xwayland(XwaylandArgs), /// Inspect/modify the color-management settings. ColorManagement(ColorManagementArgs), + /// Replace the compositor by another process. (Only for development.) + #[clap(hide = true)] + Reexec(ReexecArgs), #[cfg(feature = "it")] RunTests, } @@ -241,5 +246,6 @@ pub fn main() { Cmd::ColorManagement(a) => color_management::main(cli.global, a), #[cfg(feature = "it")] Cmd::RunTests => crate::it::run_tests(), + Cmd::Reexec(a) => reexec::main(cli.global, a), } } diff --git a/src/cli/reexec.rs b/src/cli/reexec.rs new file mode 100644 index 00000000..c4049e83 --- /dev/null +++ b/src/cli/reexec.rs @@ -0,0 +1,80 @@ +use { + crate::{ + cli::GlobalArgs, + tools::tool_client::{Handle, ToolClient, with_tool_client}, + utils::errorfmt::ErrorFmt, + wire::{jay_compositor, jay_reexec}, + }, + clap::Args, + std::rc::Rc, +}; + +#[derive(Args, Debug)] +pub struct ReexecArgs { + /// The path to the new executable. + /// + /// If this is not given, uses the path of **this** executable instead. + path: Option, + /// The arguments to pass to the new executable. + args: Vec, +} + +pub fn main(global: GlobalArgs, reexec_args: ReexecArgs) { + with_tool_client(global.log_level.into(), |tc| async move { + let rexec = Rc::new(Reexec { tc: tc.clone() }); + rexec.run(reexec_args).await; + }); +} + +struct Reexec { + tc: Rc, +} + +impl Reexec { + async fn run(&self, args: ReexecArgs) { + let tc = &self.tc; + let comp = tc.jay_compositor().await; + let reexec = tc.id(); + tc.send(jay_compositor::Reexec { + self_id: comp, + id: reexec, + }); + if let Some(path) = &args.path { + for arg in &args.args { + tc.send(jay_reexec::Arg { + self_id: reexec, + arg, + }); + } + tc.send(jay_reexec::Exec { + self_id: reexec, + path, + }); + } else { + let exe = match std::env::current_exe() { + Ok(e) => e, + Err(e) => { + log::error!("Could not determine the executable path: {}", ErrorFmt(e)); + std::process::exit(1); + } + }; + let Some(exe) = exe.to_str() else { + log::error!("Executable path is not a string: {:?}", exe); + std::process::exit(1); + }; + tc.send(jay_reexec::Arg { + self_id: reexec, + arg: "run", + }); + tc.send(jay_reexec::Exec { + self_id: reexec, + path: exe, + }); + } + jay_reexec::Failed::handle(tc, reexec, (), |_, msg| { + log::error!("Exec failed: {}", msg.msg); + std::process::exit(1); + }); + tc.round_trip().await; + } +} diff --git a/src/ifs.rs b/src/ifs.rs index 5a398248..9e202ba3 100644 --- a/src/ifs.rs +++ b/src/ifs.rs @@ -21,6 +21,7 @@ pub mod jay_log_file; pub mod jay_output; pub mod jay_pointer; pub mod jay_randr; +pub mod jay_reexec; pub mod jay_render_ctx; pub mod jay_screencast; pub mod jay_screenshot; diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 41452698..703aea33 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -12,6 +12,7 @@ use { jay_output::JayOutput, jay_pointer::JayPointer, jay_randr::JayRandr, + jay_reexec::JayReexec, jay_render_ctx::JayRenderCtx, jay_screencast::JayScreencast, jay_screenshot::JayScreenshot, @@ -73,7 +74,7 @@ impl Global for JayCompositorGlobal { } fn version(&self) -> u32 { - 16 + 17 } fn required_caps(&self) -> ClientCaps { @@ -456,6 +457,19 @@ impl JayCompositorRequestHandler for JayCompositor { self.client.add_client_obj(&obj)?; Ok(()) } + + fn reexec(&self, req: Reexec, _slf: &Rc) -> Result<(), Self::Error> { + let obj = Rc::new(JayReexec { + id: req.id, + client: self.client.clone(), + tracker: Default::default(), + version: self.version, + args: Default::default(), + }); + track!(self.client, obj); + self.client.add_client_obj(&obj)?; + Ok(()) + } } object_base! { diff --git a/src/ifs/jay_reexec.rs b/src/ifs/jay_reexec.rs new file mode 100644 index 00000000..8d7cc465 --- /dev/null +++ b/src/ifs/jay_reexec.rs @@ -0,0 +1,67 @@ +use { + crate::{ + client::{Client, ClientError}, + leaks::Tracker, + object::{Object, Version}, + utils::oserror::OsError, + wire::{JayReexecId, jay_reexec::*}, + }, + std::{cell::RefCell, rc::Rc}, + thiserror::Error, + uapi::UstrPtr, +}; + +pub struct JayReexec { + pub id: JayReexecId, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, + pub args: RefCell>, +} + +impl JayReexec { + fn send_failed(&self, msg: &str) { + self.client.event(Failed { + self_id: self.id, + msg, + }); + } +} + +impl JayReexecRequestHandler for JayReexec { + type Error = JayReexecError; + + fn arg(&self, req: Arg<'_>, _slf: &Rc) -> Result<(), Self::Error> { + self.args.borrow_mut().push(req.arg.to_owned()); + Ok(()) + } + + fn exec(&self, req: Exec<'_>, _slf: &Rc) -> Result<(), Self::Error> { + let args = self.args.borrow(); + let mut args2 = UstrPtr::new(); + args2.push(req.path); + for arg in &*args { + args2.push(&**arg); + } + if let Err(e) = uapi::execvp(req.path, &args2) { + self.send_failed(&OsError(e.0).to_string()); + } + Ok(()) + } +} + +object_base! { + self = JayReexec; + version = self.version; +} + +impl Object for JayReexec {} + +simple_add_obj!(JayReexec); + +#[derive(Debug, Error)] +pub enum JayReexecError { + #[error(transparent)] + ClientError(Box), +} +efrom!(JayReexecError, ClientError); diff --git a/src/tools/tool_client.rs b/src/tools/tool_client.rs index b33b0de3..7c75a8b7 100644 --- a/src/tools/tool_client.rs +++ b/src/tools/tool_client.rs @@ -332,7 +332,7 @@ impl ToolClient { self_id: s.registry, name: s.jay_compositor.0, interface: JayCompositor.name(), - version: s.jay_compositor.1.min(16), + version: s.jay_compositor.1.min(17), id: id.into(), }); self.jay_compositor.set(Some(id)); diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 79866072..24ec05b4 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -105,6 +105,10 @@ request get_color_management (since = 14) { id: id(jay_color_management), } +request reexec (since = 17) { + id: id(jay_reexec), +} + # events event client_id { diff --git a/wire/jay_reexec.txt b/wire/jay_reexec.txt new file mode 100644 index 00000000..54d5ae30 --- /dev/null +++ b/wire/jay_reexec.txt @@ -0,0 +1,11 @@ +request arg { + arg: str, +} + +request exec { + path: str, +} + +event failed { + msg: str, +}