From 2037a37c1e3975362d99dc3f92c6d65ecd1c711b Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 6 Mar 2024 19:44:43 +0100 Subject: [PATCH] config: allow attaching file descriptors to commands --- Cargo.lock | 26 +++++++++------------- Cargo.toml | 2 +- jay-config/Cargo.toml | 2 +- jay-config/src/_private/client.rs | 26 +++++++++++++++++----- jay-config/src/_private/ipc.rs | 6 ++++++ jay-config/src/exec.rs | 36 ++++++++++++++++++++++++++++++- src/config/handler.rs | 17 ++++++++++++--- src/forker.rs | 10 +++++++-- 8 files changed, 96 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2bfb0b5f..06ca0cbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "getrandom", "once_cell", "version_check", @@ -144,7 +144,7 @@ checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -200,12 +200,6 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -395,7 +389,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] @@ -575,7 +569,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "winapi", ] @@ -585,7 +579,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "windows-sys 0.48.0", ] @@ -694,7 +688,7 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "redox_syscall", "smallvec", @@ -1016,12 +1010,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "uapi" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "019450240401d342e2a5bc47f7fbaeb002a38fe18197b83788750d7ffb143274" +checksum = "651f13cef1d1988a4c73d215c39fec385fc1be4c7eb6daf89822d5021f7029f8" dependencies = [ "cc", - "cfg-if 0.1.10", + "cfg-if", "libc", "uapi-proc", ] @@ -1070,7 +1064,7 @@ version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] diff --git a/Cargo.toml b/Cargo.toml index 0f21b60f..98706ef5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ panic = "abort" panic = "abort" [dependencies] -uapi = "0.2.10" +uapi = "0.2.12" thiserror = "1.0.56" ahash = "0.8.7" log = { version = "0.4.20", features = ["std"] } diff --git a/jay-config/Cargo.toml b/jay-config/Cargo.toml index 15246115..bead5f1c 100644 --- a/jay-config/Cargo.toml +++ b/jay-config/Cargo.toml @@ -10,6 +10,6 @@ bincode = "1.3.3" serde = { version = "1.0.196", features = ["derive"] } log = "0.4.14" futures-util = { version = "0.3.30", features = ["io"] } -uapi = "0.2.10" +uapi = "0.2.12" thiserror = "1.0.57" backtrace = "0.3.69" diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index 67d6b7f2..b23a6c44 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -28,6 +28,7 @@ use { future::Future, mem, ops::Deref, + os::fd::IntoRawFd, panic::{catch_unwind, AssertUnwindSafe}, pin::Pin, ptr, @@ -264,11 +265,26 @@ impl Client { .iter() .map(|(a, b)| (a.to_string(), b.to_string())) .collect(); - self.send(&ClientMessage::Run { - prog: &command.prog, - args: command.args.clone(), - env, - }); + let fds: Vec<_> = command + .fds + .borrow_mut() + .drain() + .map(|(a, b)| (a, b.into_raw_fd())) + .collect(); + if fds.is_empty() { + self.send(&ClientMessage::Run { + prog: &command.prog, + args: command.args.clone(), + env, + }); + } else { + self.send(&ClientMessage::Run2 { + prog: &command.prog, + args: command.args.clone(), + env, + fds, + }); + } } pub fn grab(&self, kb: InputDevice, grab: bool) { diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 15c2be29..f23896cd 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -375,6 +375,12 @@ pub enum ClientMessage<'a> { pollable: PollableId, writable: bool, }, + Run2 { + prog: &'a str, + args: Vec, + env: Vec<(String, String)>, + fds: Vec<(i32, i32)>, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/exec.rs b/jay-config/src/exec.rs index 6f828b18..f1c2e2ee 100644 --- a/jay-config/src/exec.rs +++ b/jay-config/src/exec.rs @@ -1,6 +1,6 @@ //! Tools for spawning programs. -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap, os::fd::OwnedFd}; /// Sets an environment variable. /// @@ -14,6 +14,7 @@ pub struct Command { pub(crate) prog: String, pub(crate) args: Vec, pub(crate) env: HashMap, + pub(crate) fds: RefCell>, } impl Command { @@ -28,6 +29,7 @@ impl Command { prog: prog.to_string(), args: vec![], env: Default::default(), + fds: Default::default(), } } @@ -43,7 +45,39 @@ impl Command { self } + /// Sets a file descriptor of the process. + /// + /// By default, the process starts with exactly stdin, stdout, and stderr open and all + /// pointing to `/dev/null`. + pub fn fd>(&mut self, idx: i32, fd: F) -> &mut Self { + self.fds.borrow_mut().insert(idx, fd.into()); + self + } + + /// Sets the stdin of the process. + /// + /// This is equivalent to `fd(0, fd)`. + pub fn stdin>(&mut self, fd: F) -> &mut Self { + self.fd(0, fd) + } + + /// Sets the stdout of the process. + /// + /// This is equivalent to `fd(1, fd)`. + pub fn stdout>(&mut self, fd: F) -> &mut Self { + self.fd(1, fd) + } + + /// Sets the stderr of the process. + /// + /// This is equivalent to `fd(2, fd)`. + pub fn stderr>(&mut self, fd: F) -> &mut Self { + self.fd(2, fd) + } + /// Executes the command. + /// + /// This consumes all attached file descriptors. pub fn spawn(&self) { get!().spawn(self); } diff --git a/src/config/handler.rs b/src/config/handler.rs index 86fa9a4a..030fc498 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -51,7 +51,7 @@ use { log::Level, std::{cell::Cell, ops::Deref, rc::Rc, time::Duration}, thiserror::Error, - uapi::{c, fcntl_dupfd_cloexec}, + uapi::{c, fcntl_dupfd_cloexec, OwnedFd}, }; pub(super) struct ConfigProxyHandler { @@ -999,12 +999,17 @@ impl ConfigProxyHandler { prog: &str, args: Vec, env: Vec<(String, String)>, + fds: Vec<(i32, i32)>, ) -> Result<(), CphError> { + let fds: Vec<_> = fds + .into_iter() + .map(|(a, b)| (a, Rc::new(OwnedFd::new(b)))) + .collect(); let forker = match self.state.forker.get() { Some(f) => f, _ => return Err(CphError::NoForker), }; - forker.spawn(prog.to_string(), args, env); + forker.spawn(prog.to_string(), args, env, fds); Ok(()) } @@ -1305,7 +1310,7 @@ impl ConfigProxyHandler { ClientMessage::GetSeats => self.handle_get_seats(), ClientMessage::RemoveSeat { .. } => {} ClientMessage::Run { prog, args, env } => { - self.handle_run(prog, args, env).wrn("run")? + self.handle_run(prog, args, env, vec![]).wrn("run")? } ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab).wrn("grab")?, ClientMessage::SetColor { colorable, color } => { @@ -1505,6 +1510,12 @@ impl ConfigProxyHandler { ClientMessage::AddInterest { pollable, writable } => self .handle_add_interest(pollable, writable) .wrn("add_interest")?, + ClientMessage::Run2 { + prog, + args, + env, + fds, + } => self.handle_run(prog, args, env, fds).wrn("run")?, } Ok(()) } diff --git a/src/forker.rs b/src/forker.rs index 3973b89c..b5796923 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -170,8 +170,14 @@ impl ForkerProxy { self.pidfd(pidfd_id).await } - pub fn spawn(&self, prog: String, args: Vec, env: Vec<(String, String)>) { - self.spawn_(prog, args, env, vec![], None) + pub fn spawn( + &self, + prog: String, + args: Vec, + env: Vec<(String, String)>, + fds: Vec<(i32, Rc)>, + ) { + self.spawn_(prog, args, env, fds, None) } fn spawn_(