1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::mem::zeroed;
use std::ops::{Range, RangeTo, RangeFrom, RangeFull};
use std::os::unix::io::RawFd;
use nix::errno::errno;
use libc::getrlimit;
use libc::RLIMIT_NOFILE;
use stdio::{Fd};
use Command;
/// This is just a temporary enum to coerce `std::ops::Range*` variants
/// into single value for convenience. Used in `close_fds` method.
pub enum AnyRange {
RangeFrom(RawFd),
Range(RawFd, RawFd),
}
impl Command {
/// Configuration for any other file descriptor (panics for fds < 3) use
/// stdin/stdout/stderr for them
///
/// Rust creates file descriptors with CLOEXEC flag by default, so no
/// descriptors are inherited except ones specifically configured here
/// (and stdio which is inherited by default)
pub fn file_descriptor(&mut self, target_fd: RawFd, cfg: Fd)
-> &mut Command
{
if target_fd <= 2 {
panic!("Stdio file descriptors must be configured with respective \
methods instead of passing fd {} to `file_descritor()`",
target_fd)
}
self.fds.insert(target_fd, cfg);
self
}
/// Close a range of file descriptors as soon as process forks
///
/// Subsequent calls to this method add additional range. Use `reset_fds`
/// to remove all the ranges.
///
/// File descriptors that never closed are:
///
/// * the stdio file descriptors
/// * descriptors configured using `file_descriptor`/`file_descriptor_raw`
/// methods
/// * internal file descriptors used for parent child notification by
/// unshare crate itself (they are guaranteed to have CLOEXEC)
///
/// You should avoid this method if possilble and rely on CLOEXEC to
/// do the work. But sometimes it's inevitable:
///
/// 1. If you need to ensure closing descriptors for security reasons
/// 2. If you have some bad library out of your control which doesn't
/// set CLOEXEC on owned the file descriptors
///
/// Ranges obey the following rules:
///
/// * Range like `..12` is transformed into `3..12`
/// * Range with undefined upper bound `3..` is capped at current ulimit
/// for file descriptors **at the moment of calling the method**
/// * The full range `..` is an alias to `3..`
/// * Multiple overlapping ranges are closed multiple times which is
/// both harmless and useless
///
/// # Panics
///
/// Panics when can't get rlimit for range without upper bound. Should
/// never happen in practice.
///
/// Panics when lower range of fd is < 3 (stdio file descriptors)
///
pub fn close_fds<A: Into<AnyRange>>(&mut self, range: A)
-> &mut Command
{
self.close_fds.push(match range.into() {
AnyRange::Range(x, y) => {
assert!(x >= 3);
(x, y)
}
AnyRange::RangeFrom(x) => unsafe {
assert!(x >= 3);
let mut rlim = zeroed();
let rc = getrlimit(RLIMIT_NOFILE, &mut rlim);
if rc < 0 {
panic!("Can't get rlimit: errno {}", errno());
}
(x, rlim.rlim_cur as RawFd)
}
});
self
}
/// Reset file descriptor including stdio to the initial state
///
/// Initial state is inherit all the stdio and do nothing to other fds.
pub fn reset_fds(&mut self) -> &mut Command {
self.fds = vec![
(0, Fd::inherit()),
(1, Fd::inherit()),
(2, Fd::inherit()),
].into_iter().collect();
self.close_fds.clear();
self
}
}
impl Into<AnyRange> for Range<RawFd> {
fn into(self) -> AnyRange {
return AnyRange::Range(self.start, self.end);
}
}
impl Into<AnyRange> for RangeTo<RawFd> {
fn into(self) -> AnyRange {
return AnyRange::Range(3, self.end);
}
}
impl Into<AnyRange> for RangeFrom<RawFd> {
fn into(self) -> AnyRange {
return AnyRange::RangeFrom(self.start);
}
}
impl Into<AnyRange> for RangeFull {
fn into(self) -> AnyRange {
return AnyRange::RangeFrom(3);
}
}