Skip to content

Commit

Permalink
Merge branch 'feat/detached-process'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jun 21, 2023
2 parents 8e47876 + 7ad9cb3 commit d21c48e
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ pathdiff = "0.2.0"

[target.'cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd", target_os = "illumos", target_os = "solaris" ))'.dependencies]
is-wsl = "0.4.0"

[target."cfg(unix)".dependencies]
libc = "0.2"
67 changes: 66 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ use std::{
/// # Beware
///
/// Sometimes, depending on the platform and system configuration, launchers *can* block.
/// If you want to be sure they don't, use [`that_in_background()`] instead.
/// If you want to be sure they don't, use [`that_in_background()`] or [`that_detached`] instead.
pub fn that(path: impl AsRef<OsStr>) -> io::Result<()> {
let mut last_err = None;
for mut cmd in commands(path) {
Expand Down Expand Up @@ -232,6 +232,33 @@ pub fn with_in_background<T: AsRef<OsStr>>(
thread::spawn(|| with(path, app))
}

/// Open path with the default application using a detached process. which is useful if
/// the program ends up to be blocking or want to out-live your app
///
/// See documentation of [`that()`] for more details.
pub fn that_detached(path: impl AsRef<OsStr>) -> io::Result<()> {
let mut last_err = None;
for mut cmd in commands(path) {
match cmd.spawn_detached() {
Ok(_) => {
return Ok(());
}
Err(err) => last_err = Some(err),
}
}
Err(last_err.expect("no launcher worked, at least one error"))
}

/// Open path with the given application using a detached process, which is useful if
/// the program ends up to be blocking or want to out-live your app. Otherwise, prefer [`with()`] for
/// straightforward error handling.
///
/// See documentation of [`with()`] for more details.
pub fn with_detached<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
let mut cmd = with_command(path, app);
cmd.spawn_detached()
}

trait IntoResult<T> {
fn into_result(self, cmd: &Command) -> T;
}
Expand All @@ -251,6 +278,7 @@ impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {

trait CommandExt {
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
fn spawn_detached(&mut self) -> io::Result<()>;
}

impl CommandExt for Command {
Expand All @@ -260,6 +288,43 @@ impl CommandExt for Command {
.stderr(Stdio::null())
.status()
}

fn spawn_detached(&mut self) -> io::Result<()> {
// This is pretty much lifted from the implementation in Alacritty:
// https://github.com/alacritty/alacritty/blob/b9c886872d1202fc9302f68a0bedbb17daa35335/alacritty/src/daemon.rs

self.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());

#[cfg(unix)]
unsafe {
use std::os::unix::process::CommandExt as _;

self.pre_exec(move || {
match libc::fork() {
-1 => return Err(io::Error::last_os_error()),
0 => (),
_ => libc::_exit(0),
}

if libc::setsid() == -1 {
return Err(io::Error::last_os_error());
}

Ok(())
});
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
const CREATE_NO_WINDOW: u32 = 0x08000000;
self.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW);
}

self.spawn().map(|_| ())
}
}

#[cfg(windows)]
Expand Down

0 comments on commit d21c48e

Please sign in to comment.