Use rustix/libc instead of nix; use pipe to capture double-fork pid

`rustix` and `libc` are already in the dependency tree, and `rustix`
follows IO safety rules, so use those.

We can use a pipe to get the PID of the double-forked process.
This commit is contained in:
Ian Douglas Scott 2024-07-24 18:06:11 -07:00 committed by Ian Douglas Scott
parent a5996b4e90
commit fe035e37b0
2 changed files with 38 additions and 18 deletions

View file

@ -24,7 +24,7 @@ debug = ["iced/debug"]
# Enables pipewire support in ashpd, if ashpd is enabled # Enables pipewire support in ashpd, if ashpd is enabled
pipewire = ["ashpd?/pipewire"] pipewire = ["ashpd?/pipewire"]
# Enables process spawning helper # Enables process spawning helper
process = ["dep:nix"] process = ["dep:libc", "dep:rustix"]
# Use rfd for file dialogs # Use rfd for file dialogs
rfd = ["dep:rfd"] rfd = ["dep:rfd"]
# Enables desktop files helpers # Enables desktop files helpers
@ -83,10 +83,11 @@ derive_setters = "0.1.5"
fraction = "0.14.0" fraction = "0.14.0"
image = { version = "0.25.1", optional = true } image = { version = "0.25.1", optional = true }
lazy_static = "1.4.0" lazy_static = "1.4.0"
libc = { version = "0.2.155", optional = true }
mime = { version = "0.3.17", optional = true } mime = { version = "0.3.17", optional = true }
nix = { version = "0.27", features = ["process"], optional = true }
palette = "0.7.3" palette = "0.7.3"
rfd = { version = "0.14.0", optional = true } rfd = { version = "0.14.0", optional = true }
rustix = { version = "0.38.34", features = ["pipe", "process"], optional = true }
serde = { version = "1.0.180", features = ["derive"] } serde = { version = "1.0.180", features = ["derive"] }
slotmap = "1.0.6" slotmap = "1.0.6"
textdistance = { version = "1.0.2", optional = true } textdistance = { version = "1.0.2", optional = true }

View file

@ -1,31 +1,50 @@
use std::fs::File;
use std::io;
use std::process::{exit, Command, Stdio}; use std::process::{exit, Command, Stdio};
use nix::sys::wait::waitpid;
use nix::unistd::{fork, ForkResult};
/// Performs a double fork with setsid to spawn and detach a command. /// Performs a double fork with setsid to spawn and detach a command.
pub fn spawn(mut command: Command) { pub fn spawn(mut command: Command) -> Option<u32> {
command command
.stdin(Stdio::null()) .stdin(Stdio::null())
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()); .stderr(Stdio::null());
unsafe { let Ok((read, write)) = rustix::pipe::pipe_with(rustix::pipe::PipeFlags::CLOEXEC) else {
match fork() { return None;
Ok(ForkResult::Parent { child }) => { };
let _res = waitpid(Some(child), None);
match unsafe { libc::fork() } {
// Parent process
child @ 1.. => {
let child = rustix::process::Pid::from_raw(child).unwrap();
let _res = rustix::process::waitpid(Some(child), rustix::process::WaitOptions::empty());
// Read PID from pipe
let mut bytes = [0; 4];
if rustix::io::read(read, &mut bytes) == Ok(4) {
Some(u32::from_ne_bytes(bytes))
} else {
None
}
}
// Child process
0 => {
let _res = rustix::process::setsid();
if let Ok(child) = command.spawn() {
// Write PID to pipe
let _ = rustix::io::write(write, &child.id().to_ne_bytes());
} }
Ok(ForkResult::Child) => { exit(0)
let _res = nix::unistd::setsid(); }
let _res = command.spawn();
exit(0); ..=-1 => {
} println!(
"failed to fork and spawn command: {}",
io::Error::last_os_error()
);
Err(why) => { None
println!("failed to fork and spawn command: {}", why.desc());
}
} }
} }
} }