-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathlib.rs
294 lines (267 loc) · 8.56 KB
/
lib.rs
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//! A simple wrapper around the C library's `execvp` function.
//!
//! For examples, see [the repository](https://github.com/faradayio/exec-rs).
//!
//! We'd love to fully integrate this with `std::process::Command`, but
//! that module doesn't export sufficient hooks to allow us to add a new
//! way to execute a program.
extern crate errno;
extern crate libc;
use errno::{errno, Errno};
use std::error;
use std::ffi::{CString, NulError, OsStr, OsString};
use std::fmt;
use std::iter::{IntoIterator, Iterator};
use std::os::unix::ffi::OsStrExt;
use std::ptr;
/// Represents an error calling `exec`.
///
/// This is marked `#[must_use]`, which is unusual for error types.
/// Normally, the fact that `Result` is marked in this fashion is
/// sufficient, but in this case, this error is returned bare from
/// functions that only return a result if they fail.
#[derive(Debug)]
#[must_use]
pub enum Error {
/// One of the strings passed to `execv` contained an internal null byte
/// and can't be passed correctly to C.
BadArgument(NulError),
/// An error was returned by the system.
Errno(Errno),
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
&Error::BadArgument(_) => "bad argument to exec",
&Error::Errno(_) => "couldn't exec process",
}
}
fn cause(&self) -> Option<&dyn error::Error> {
match self {
&Error::BadArgument(ref err) => Some(err),
&Error::Errno(_) => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Error::BadArgument(ref err) => write!(f, "{}: {}", self.to_string(), err),
&Error::Errno(err) => write!(f, "{}: {}", self.to_string(), err),
}
}
}
impl From<NulError> for Error {
/// Convert a `NulError` into an `ExecError`.
fn from(err: NulError) -> Error {
Error::BadArgument(err)
}
}
/// Like `try!`, but it just returns the error directly without wrapping it
/// in `Err`. For functions that only return if something goes wrong.
macro_rules! exec_try {
( $ expr : expr ) => {
match $expr {
Ok(val) => val,
Err(err) => return From::from(err),
}
};
}
/// Run `program` with `args`, completely replacing the currently running
/// program. If it returns at all, it always returns an error.
///
/// Note that `program` and the first element of `args` will normally be
/// identical. The former is the program we ask the operating system to
/// run, and the latter is the value that will show up in `argv[0]` when
/// the program executes. On POSIX systems, these can technically be
/// completely different, and we've perserved that much of the low-level
/// API here.
///
/// # Examples
///
/// ```no_run
/// let err = exec::execvp("echo", &["echo", "foo"]);
/// println!("Error: {}", err);
/// ```
pub fn execvp<S, I>(program: S, args: I) -> Error
where
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
// Add null terminations to our strings and our argument array,
// converting them into a C-compatible format.
let program_cstring = exec_try!(to_program_cstring(program));
let argv = exec_try!(to_argv(args));
// Use an `unsafe` block so that we can call directly into C.
let res = unsafe { libc::execvp(program_cstring.as_ptr(), argv.char_ptrs.as_ptr()) };
// Handle our error result.
if res < 0 {
Error::Errno(errno())
} else {
// Should never happen.
panic!("execvp returned unexpectedly")
}
}
/// Run `program` with `args` and environment `envs`, completely replacing
/// the currently running program. If it returns at all, it always return
/// an error.
///
/// Note that `program` and the first element of `args` will normally be
/// identical. The former is the program we ask the operating system to
/// run, and the latter is the value that will show up in `argv[0]` when
/// the program executes. On POSIX systems, these can technically be
/// completely different, and we've preserved that much of the low-level
/// API here.
///
/// # Examples
///
/// ```no_run
/// use std::env::vars_os;
/// use std::ffi::OsString;
/// let err = execvpe(
/// "bash",
/// ["bash"],
/// vars_os().chain([(OsString::from("NAME"), OsString::from("VALUE"))]),
/// println!("Error: {}", err);
/// ```
#[cfg(not(target_os = "macos"))]
pub fn execvpe<S, I, J, N, V>(program: S, args: I, envs: J) -> Error
where
S: AsRef<OsStr>,
I: IntoIterator,
I::Item: AsRef<OsStr>,
J: IntoIterator<Item = (N, V)>,
N: AsRef<OsStr> + std::fmt::Debug,
V: AsRef<OsStr> + std::fmt::Debug,
{
// Add null terminations to our strings and our argument array,
// converting them into a C-compatible format.
let program_cstring = exec_try!(to_program_cstring(program));
let argv = exec_try!(to_argv(args));
let envp = exec_try!(to_envp(envs));
// Use an `unsafe` block so that we can call directly into C.
let res = unsafe {
libc::execvpe(
program_cstring.as_ptr(),
argv.char_ptrs.as_ptr(),
envp.char_ptrs.as_ptr(),
)
};
// Handle our error result.
if res < 0 {
Error::Errno(errno())
} else {
// Should never happen.
panic!("execvp returned unexpectedly")
}
}
fn to_program_cstring<S>(program: S) -> std::result::Result<CString, NulError>
where
S: AsRef<OsStr>,
{
CString::new(program.as_ref().as_bytes())
}
// Struct ensures that cstrings have same lifetime as char_ptrs that points into them
struct Argv {
#[allow(dead_code)]
cstrings: Vec<CString>,
char_ptrs: Vec<*const i8>,
}
fn to_argv<I>(args: I) -> std::result::Result<Argv, NulError>
where
I: IntoIterator,
I::Item: AsRef<OsStr>,
{
let cstrings = args
.into_iter()
.map(|arg| CString::new(arg.as_ref().as_bytes()))
.collect::<Result<Vec<_>, _>>()?;
let mut char_ptrs = cstrings.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
char_ptrs.push(ptr::null());
Ok(Argv {
cstrings: cstrings,
char_ptrs: char_ptrs,
})
}
// Struct ensures that cstrings have same lifetime as char_ptrs that points into them
#[cfg(not(target_os = "macos"))]
struct Envp {
#[allow(dead_code)]
cstrings: Vec<CString>,
char_ptrs: Vec<*const i8>,
}
#[cfg(not(target_os = "macos"))]
fn to_envp<J, N, V>(envs: J) -> std::result::Result<Envp, NulError>
where
J: IntoIterator<Item = (N, V)>,
N: AsRef<OsStr> + std::fmt::Debug,
V: AsRef<OsStr> + std::fmt::Debug,
{
let cstrings = envs
.into_iter()
.map(|(n, v)| {
let mut temp: OsString = OsString::new();
temp.push(n);
temp.push("=");
temp.push(v);
CString::new(temp.as_bytes())
})
.collect::<std::result::Result<Vec<_>, _>>()?;
let mut char_ptrs = cstrings.iter().map(|x| x.as_ptr()).collect::<Vec<_>>();
char_ptrs.push(ptr::null());
Ok(Envp {
cstrings: cstrings,
char_ptrs: char_ptrs,
})
}
/// Build a command to execute. This has an API which is deliberately
/// similar to `std::process::Command`.
///
/// ```no_run
/// let err = exec::Command::new("echo")
/// .arg("hello")
/// .arg("world")
/// .exec();
/// println!("Error: {}", err);
/// ```
///
/// If the `exec` function succeeds, it will never return.
pub struct Command {
/// The program name and arguments, in typical C `argv` style.
argv: Vec<OsString>,
}
impl Command {
/// Create a new command builder, specifying the program to run. The
/// program will be searched for using the usual rules for `PATH`.
pub fn new<S: AsRef<OsStr>>(program: S) -> Command {
Command {
argv: vec![program.as_ref().to_owned()],
}
}
/// Add an argument to the command builder. This can be chained.
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
self.argv.push(arg.as_ref().to_owned());
self
}
/// Add multiple arguments to the command builder. This can be
/// chained.
///
/// ```no_run
/// let err = exec::Command::new("echo")
/// .args(&["hello", "world"])
/// .exec();
/// println!("Error: {}", err);
/// ```
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command {
for arg in args {
self.arg(arg.as_ref());
}
self
}
/// Execute the command we built. If this function succeeds, it will
/// never return.
pub fn exec(&mut self) -> Error {
execvp(&self.argv[0], &self.argv)
}
}