diff --git a/src/config/handler.rs b/src/config/handler.rs index 22f102e4..86fa9a4a 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1004,7 +1004,7 @@ impl ConfigProxyHandler { Some(f) => f, _ => return Err(CphError::NoForker), }; - forker.spawn(prog.to_string(), args, env, None); + forker.spawn(prog.to_string(), args, env); Ok(()) } diff --git a/src/forker.rs b/src/forker.rs index 2d6f5ffd..3973b89c 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -17,6 +17,7 @@ use { }, xwayland, }, + ahash::AHashMap, bincode::Options, jay_config::_private::bincode_ops, log::Level, @@ -155,30 +156,42 @@ impl ForkerProxy { wmfd: Rc, waylandfd: Rc, ) -> Result<(Rc, c::pid_t), ForkerError> { - self.fds - .borrow_mut() - .extend([stderr, dfd, listenfd, wmfd, waylandfd]); - let id = self.next_id.fetch_add(1); - self.outgoing.push(ServerMessage::Xwayland { id }); - self.pidfd(id).await + let (prog, args) = xwayland::build_args(); + let env = vec![("WAYLAND_SOCKET".to_string(), "6".to_string())]; + let fds = vec![ + (2, stderr), + (3, dfd), + (4, listenfd), + (5, wmfd), + (6, waylandfd), + ]; + let pidfd_id = self.next_id.fetch_add(1); + self.spawn_(prog, args, env, fds, Some(pidfd_id)); + self.pidfd(pidfd_id).await } - pub fn spawn( + pub fn spawn(&self, prog: String, args: Vec, env: Vec<(String, String)>) { + self.spawn_(prog, args, env, vec![], None) + } + + fn spawn_( &self, prog: String, args: Vec, env: Vec<(String, String)>, - stderr: Option>, + fds: Vec<(i32, Rc)>, + pidfd_id: Option, ) { - let have_stderr = stderr.is_some(); - if let Some(stderr) = stderr { - self.fds.borrow_mut().push(stderr); + for (_, fd) in &fds { + self.fds.borrow_mut().push(fd.clone()); } + let fds = fds.into_iter().map(|(a, _)| a).collect(); self.outgoing.push(ServerMessage::Spawn { prog, args, env, - stderr: have_stderr, + fds, + pidfd_id, }) } @@ -272,10 +285,8 @@ enum ServerMessage { prog: String, args: Vec, env: Vec<(String, String)>, - stderr: bool, - }, - Xwayland { - id: u32, + fds: Vec, + pidfd_id: Option, }, } @@ -372,9 +383,9 @@ impl Forker { prog, args, env, - stderr, - } => self.handle_spawn(prog, args, env, stderr, io), - ServerMessage::Xwayland { id } => self.handle_xwayland(io, id), + fds, + pidfd_id, + } => self.handle_spawn(prog, args, env, fds, io, pidfd_id), } } @@ -386,32 +397,20 @@ impl Forker { } } - fn handle_xwayland(self: &Rc, io: &mut IoIn, id: u32) { - let stderr = io.pop_fd(); - let fds = vec![ - Rc::try_unwrap(io.pop_fd().unwrap()).unwrap(), - Rc::try_unwrap(io.pop_fd().unwrap()).unwrap(), - Rc::try_unwrap(io.pop_fd().unwrap()).unwrap(), - Rc::try_unwrap(io.pop_fd().unwrap()).unwrap(), - ]; - let (prog, args) = xwayland::build_args(&fds); - let env = vec![("WAYLAND_SOCKET".to_string(), fds[3].raw().to_string())]; - self.spawn(prog, args, env, stderr, fds, Some(id)); - } - fn handle_spawn( self: &Rc, prog: String, args: Vec, env: Vec<(String, String)>, - stderr: bool, + fds: Vec, io: &mut IoIn, + pidfd_id: Option, ) { - let stderr = match stderr { - true => io.pop_fd(), - _ => None, - }; - self.spawn(prog, args, env, stderr, vec![], None) + let fds = fds + .into_iter() + .map(|a| (a, Rc::try_unwrap(io.pop_fd().unwrap()).unwrap())) + .collect(); + self.spawn(prog, args, env, fds, pidfd_id) } fn spawn( @@ -419,8 +418,7 @@ impl Forker { prog: String, args: Vec, env: Vec<(String, String)>, - stderr: Option>, - fds: Vec, + fds: Vec<(i32, OwnedFd)>, pidfd_id: Option, ) { let (read, mut write) = pipe2(c::O_CLOEXEC).unwrap(); @@ -476,9 +474,13 @@ impl Forker { } Forked::Child { .. } => { let err = (|| { - if let Some(stderr) = stderr { - uapi::dup2(stderr.raw(), 2).unwrap(); + if let Some(max_desired) = fds.iter().map(|v| v.0).max() { + match uapi::fcntl_dupfd_cloexec(write.raw(), max_desired.wrapping_add(1)) { + Ok(new) => write = new, + Err(e) => return Err(SpawnError::Dupfd(e.into())), + } } + let fds = map_fds(fds)?; for fd in fds { let fd = fd.unwrap(); let res: Result<_, Errno> = (|| { @@ -521,6 +523,8 @@ enum SpawnError { Exec(#[source] crate::utils::oserror::OsError), #[error("Could not unset cloexec flag")] Cloexec(#[source] crate::utils::oserror::OsError), + #[error("dupfd faild")] + Dupfd(#[source] crate::utils::oserror::OsError), } fn setup_fds(mut socket: OwnedFd) -> OwnedFd { @@ -564,3 +568,35 @@ fn setup_name(name: &str) { c::prctl(c::PR_SET_NAME, name.as_ptr()); } } + +fn map_fds(fds: Vec<(i32, OwnedFd)>) -> Result, SpawnError> { + let mut desired: Vec<_> = fds.iter().map(|v| v.0).collect(); + desired.sort_by(|a, b| b.cmp(a)); + let mut existing_to_desired: AHashMap<_, _> = fds.iter().map(|v| (v.1.raw(), v.0)).collect(); + let mut desired_to_existing: AHashMap<_, _> = fds.into_iter().map(|v| (v.0, v.1)).collect(); + for desired in desired { + let existing = desired_to_existing.get(&desired).unwrap().raw(); + if existing == desired { + continue; + } + if let Some(conflict_desired) = existing_to_desired.get(&desired).copied() { + match uapi::fcntl_dupfd_cloexec(desired, 0) { + Ok(new) => { + existing_to_desired.remove(&desired); + existing_to_desired.insert(new.raw(), conflict_desired); + desired_to_existing.insert(conflict_desired, new); + } + Err(e) => return Err(SpawnError::Dupfd(e.into())), + } + } + match uapi::dup3(existing, desired, c::O_CLOEXEC) { + Ok(_) => { + existing_to_desired.remove(&existing); + existing_to_desired.insert(desired, desired); + desired_to_existing.insert(desired, OwnedFd::new(desired)); + } + Err(e) => return Err(SpawnError::Dupfd(e.into())), + } + } + Ok(desired_to_existing.into_values().collect()) +} diff --git a/src/xwayland.rs b/src/xwayland.rs index 5ecb4ea8..af43e885 100644 --- a/src/xwayland.rs +++ b/src/xwayland.rs @@ -198,7 +198,7 @@ async fn run( Ok(()) } -pub fn build_args(fds: &[OwnedFd]) -> (String, Vec) { +pub fn build_args() -> (String, Vec) { let prog = "Xwayland".to_string(); let args = vec![ "-terminate".to_string(), @@ -206,11 +206,11 @@ pub fn build_args(fds: &[OwnedFd]) -> (String, Vec) { "-verbose".to_string(), 10.to_string(), "-displayfd".to_string(), - fds[0].raw().to_string(), + "3".to_string(), "-listenfd".to_string(), - fds[1].raw().to_string(), + "4".to_string(), "-wm".to_string(), - fds[2].raw().to_string(), + "5".to_string(), ]; (prog, args) }