142 lines
4.1 KiB
Rust
142 lines
4.1 KiB
Rust
use {
|
|
crate::{
|
|
client::{Client, ClientError},
|
|
leaks::Tracker,
|
|
object::{Object, Version},
|
|
utils::{
|
|
clone3::{Forked, fork_with_pidfd},
|
|
errorfmt::ErrorFmt,
|
|
oserror::OsErrorExt,
|
|
pipe::{Pipe, pipe},
|
|
},
|
|
wire::{JayReexecId, jay_reexec::*},
|
|
},
|
|
std::{array::from_mut, cell::RefCell, rc::Rc},
|
|
thiserror::Error,
|
|
uapi::{OwnedFd, UstrPtr, c, close_range, dup2, waitpid},
|
|
};
|
|
|
|
pub struct JayReexec {
|
|
pub id: JayReexecId,
|
|
pub client: Rc<Client>,
|
|
pub tracker: Tracker<Self>,
|
|
pub version: Version,
|
|
pub args: RefCell<Vec<String>>,
|
|
}
|
|
|
|
impl JayReexec {
|
|
fn send_failed(&self, msg: &str) {
|
|
self.client.event(Failed {
|
|
self_id: self.id,
|
|
msg,
|
|
});
|
|
}
|
|
|
|
fn delay_close_input_fd(&self) -> Option<OwnedFd> {
|
|
// It's 2025 and closing evdev fds is still abysmally slow.
|
|
let mut fds = self.client.state.backend.get().get_input_fds();
|
|
if fds.is_empty() {
|
|
return None;
|
|
}
|
|
macro_rules! pipe {
|
|
() => {
|
|
match pipe() {
|
|
Ok(p) => p,
|
|
Err(e) => {
|
|
log::error!("Could not create pipe: {}", ErrorFmt(e));
|
|
return None;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
let Pipe {
|
|
read: p1,
|
|
write: c1,
|
|
} = pipe!();
|
|
let Pipe {
|
|
read: c2,
|
|
write: p2,
|
|
} = pipe!();
|
|
if let Ok(f) = fork_with_pidfd(false) {
|
|
match f {
|
|
Forked::Parent { pid, .. } => {
|
|
let _ = waitpid(pid, 0);
|
|
}
|
|
Forked::Child { .. } => {
|
|
if let Ok(f) = fork_with_pidfd(false)
|
|
&& let Forked::Child { .. } = f
|
|
{
|
|
drop(p2);
|
|
fds.sort_by_key(|fd| fd.raw());
|
|
let c2_dup = c1.raw().max(c2.raw()).max(fds.last().unwrap().raw()) + 1;
|
|
let c1_dup = c2_dup + 1;
|
|
let _ = dup2(c1.raw(), c1_dup);
|
|
let _ = dup2(c2.raw(), c2_dup);
|
|
for (idx, fd) in fds.iter().enumerate() {
|
|
let _ = dup2(fd.raw(), idx as _);
|
|
}
|
|
let c2_dup_dup = fds.len() as _;
|
|
let _ = dup2(c2_dup, c2_dup_dup);
|
|
let _ = close_range(c2_dup_dup as c::c_uint + 1, !0, 0);
|
|
let mut pollfd = c::pollfd {
|
|
fd: c2_dup_dup,
|
|
events: 0,
|
|
revents: 0,
|
|
};
|
|
let _ = uapi::poll(from_mut(&mut pollfd), -1);
|
|
}
|
|
unsafe {
|
|
c::_exit(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
drop(c1);
|
|
let mut pollfd = c::pollfd {
|
|
fd: p1.raw(),
|
|
events: 0,
|
|
revents: 0,
|
|
};
|
|
let _ = uapi::poll(from_mut(&mut pollfd), -1);
|
|
Some(p2)
|
|
}
|
|
}
|
|
|
|
impl JayReexecRequestHandler for JayReexec {
|
|
type Error = JayReexecError;
|
|
|
|
fn arg(&self, req: Arg<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
|
self.args.borrow_mut().push(req.arg.to_owned());
|
|
Ok(())
|
|
}
|
|
|
|
fn exec(&self, req: Exec<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
|
let args = self.args.borrow();
|
|
let mut args2 = UstrPtr::new();
|
|
args2.push(req.path);
|
|
for arg in &*args {
|
|
args2.push(&**arg);
|
|
}
|
|
let _drop_after_exec = self.delay_close_input_fd();
|
|
if let Err(e) = uapi::execvp(req.path, &args2).to_os_error() {
|
|
self.send_failed(&e.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<ClientError>),
|
|
}
|
|
efrom!(JayReexecError, ClientError);
|