process_wrap/tokio/
process_session.rs

1use std::io::{Error, Result};
2
3use nix::unistd::{setsid, Pid};
4use tokio::process::Command;
5#[cfg(feature = "tracing")]
6use tracing::instrument;
7
8use super::{CommandWrap, CommandWrapper};
9
10/// Wrapper which creates a new session and group for the `Command`.
11///
12/// This wrapper is only available on Unix.
13///
14/// It creates a new session and new process group and sets the [`Command`] as its leader.
15/// See [setsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html).
16///
17/// You may find that some programs behave differently or better when running in a session rather
18/// than a process group, or vice versa.
19///
20/// This wrapper uses [the same child wrapper as `ProcessGroup`](super::ProcessGroupChild) and does
21/// the same setup (plus the session setup); using both together is unnecessary and may misbehave.
22#[derive(Clone, Copy, Debug)]
23pub struct ProcessSession;
24
25impl CommandWrapper for ProcessSession {
26	#[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
27	fn pre_spawn(&mut self, command: &mut Command, _core: &CommandWrap) -> Result<()> {
28		unsafe {
29			command.pre_exec(move || setsid().map_err(Error::from).map(|_| ()));
30		}
31
32		Ok(())
33	}
34
35	#[cfg_attr(feature = "tracing", instrument(level = "debug", skip(self)))]
36	fn wrap_child(
37		&mut self,
38		inner: Box<dyn super::core::ChildWrapper>,
39		_core: &CommandWrap,
40	) -> Result<Box<dyn super::core::ChildWrapper>> {
41		let pgid = Pid::from_raw(
42			i32::try_from(
43				inner
44					.id()
45					.expect("Command was reaped before we could read its PID"),
46			)
47			.expect("Command PID > i32::MAX"),
48		);
49
50		Ok(Box::new(super::ProcessGroupChild::new(inner, pgid)))
51	}
52}