spawn/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod handle;
4mod spawn;
5
6use caps::{CapSet, CapsHashSet};
7use log::warn;
8use nix::unistd::{dup, pipe};
9use std::os::fd::AsFd;
10use std::{os::fd::OwnedFd, sync::LazyLock};
11
12pub use handle::Error as HandleError;
13pub use handle::Handle;
14pub use handle::Stream;
15pub use spawn::Error as SpawnError;
16pub use spawn::Spawner;
17pub use spawn::StreamMode;
18
19#[cfg(feature = "fork")]
20pub use fork::Error as ForkError;
21
22#[cfg(feature = "fork")]
23pub use fork::Fork;
24
25#[cfg(feature = "fork")]
26mod fork;
27
28/// An OwnedFd pointing to /dev/null, duplicated for
29/// StreamMode::Discard.
30static NULL: LazyLock<OwnedFd> = LazyLock::new(|| {
31    std::fs::File::open("/dev/null")
32        .expect("Failed to open /dev/null")
33        .into()
34});
35
36/// Format an iterator into a string.
37fn format_iter<T, V>(iter: T) -> String
38where
39    T: Iterator<Item = V>,
40    V: std::fmt::Display,
41{
42    let mut ret = String::new();
43    iter.for_each(|f| ret.push_str(&format!("{f} ")));
44    ret
45}
46
47/// Clears the capabilities of the current thread.
48fn clear_capabilities(diff: CapsHashSet) {
49    for set in [
50        CapSet::Ambient,
51        CapSet::Ambient,
52        CapSet::Effective,
53        CapSet::Inheritable,
54        CapSet::Permitted,
55    ] {
56        for cap in &diff {
57            if let Err(e) = caps::drop(None, set, *cap) {
58                warn!("Could not drop {cap}: {e}");
59            }
60        }
61    }
62}
63
64/// Create a duplicate FD pointing to /dev/null
65fn dup_null() -> Result<OwnedFd, SpawnError> {
66    dup(NULL.as_fd()).map_err(|e| SpawnError::Errno(None, "dup", e))
67}
68
69/// Conditionally create a pipe.
70/// Returns either a set of `None`, or the result of `pipe()`
71fn cond_pipe(cond: &StreamMode) -> Result<Option<(OwnedFd, OwnedFd)>, SpawnError> {
72    match cond {
73        StreamMode::Pipe | StreamMode::Log(_) => match pipe() {
74            Ok((r, w)) => Ok(Some((r, w))),
75            Err(e) => Err(SpawnError::Errno(None, "pipe", e)),
76        },
77        _ => Ok(None),
78    }
79}
80
81/// Log all activity from the child at the desired level.
82fn logger(level: log::Level, fd: OwnedFd, name: String) {
83    let stream = Stream::new(fd);
84    while let Some(line) = stream.read_line() {
85        log::log!(level, "{name}: {line}");
86    }
87}