Skip to content

Commit ffe8d74

Browse files
committed
Null terminated slices for exec
1 parent 5fa5fd4 commit ffe8d74

File tree

3 files changed

+111
-37
lines changed

3 files changed

+111
-37
lines changed

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ pub mod libc {
3333
pub use libc::{c_int, c_void};
3434
pub use errno::Errno;
3535

36+
pub mod null_terminated;
37+
3638
pub mod errno;
3739
pub mod features;
3840
pub mod fcntl;

src/null_terminated.rs

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use std::ops::{Deref, DerefMut};
2+
use std::ffi::CStr;
3+
use std::mem::transmute;
4+
use std::iter;
5+
use libc::c_char;
6+
7+
pub struct NullTerminatedSlice<T> {
8+
inner: [Option<T>],
9+
}
10+
11+
impl<T> NullTerminatedSlice<T> {
12+
pub fn from_slice(slice: &[Option<T>]) -> Option<&Self> {
13+
if slice.last().map(Option::is_none).unwrap_or(false) {
14+
Some(unsafe { Self::from_slice_unchecked(slice) })
15+
} else {
16+
None
17+
}
18+
}
19+
20+
pub fn from_slice_mut(slice: &mut [Option<T>]) -> Option<&mut Self> {
21+
if slice.last().map(Option::is_none).unwrap_or(false) {
22+
Some(unsafe { Self::from_slice_mut_unchecked(slice) })
23+
} else {
24+
None
25+
}
26+
}
27+
28+
pub unsafe fn from_slice_unchecked(slice: &[Option<T>]) -> &Self {
29+
transmute(slice)
30+
}
31+
32+
pub unsafe fn from_slice_mut_unchecked(slice: &mut [Option<T>]) -> &mut Self {
33+
transmute(slice)
34+
}
35+
}
36+
37+
impl<'a, U: Sized> NullTerminatedSlice<&'a U> {
38+
pub fn as_ptr(&self) -> *const *const U {
39+
self.inner.as_ptr() as *const _
40+
}
41+
}
42+
43+
impl<T> Deref for NullTerminatedSlice<T> {
44+
type Target = [Option<T>];
45+
46+
fn deref(&self) -> &Self::Target {
47+
&self.inner[..self.inner.len() - 1]
48+
}
49+
}
50+
51+
impl<T> DerefMut for NullTerminatedSlice<T> {
52+
fn deref_mut(&mut self) -> &mut Self::Target {
53+
let len = self.inner.len();
54+
&mut self.inner[..len - 1]
55+
}
56+
}
57+
58+
pub trait BorrowNullTerminatedSlice<T> {
59+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R;
60+
}
61+
62+
impl<T: AsRef<CStr>, I: IntoIterator<Item=T>> BorrowNullTerminatedSlice<c_char> for I {
63+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&c_char>) -> R>(self, f: F) -> R {
64+
fn cstr_char<'a, S: AsRef<CStr> + 'a>(s: S) -> &'a c_char {
65+
unsafe {
66+
&*s.as_ref().as_ptr()
67+
}
68+
}
69+
70+
let values: Vec<_> = self.into_iter()
71+
.map(cstr_char)
72+
.map(Some).chain(iter::once(None)).collect();
73+
let terminated = unsafe { NullTerminatedSlice::from_slice_unchecked(&values[..]) };
74+
75+
f(terminated)
76+
}
77+
}
78+
79+
impl<'a, T: 'a> BorrowNullTerminatedSlice<T> for &'a NullTerminatedSlice<&'a T> {
80+
fn borrow_null_terminated_slice<R, F: FnOnce(&NullTerminatedSlice<&T>) -> R>(self, f: F) -> R {
81+
f(self)
82+
}
83+
}

src/unistd.rs

+26-37
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Standard symbolic constants and types
22
//!
33
use {Errno, Error, Result, NixPath};
4+
use null_terminated::BorrowNullTerminatedSlice;
45
use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC};
56
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
67
use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t};
78
use std::mem;
8-
use std::ffi::CString;
9+
use std::ffi::CStr;
910
use std::os::unix::io::RawFd;
1011
use void::Void;
1112

@@ -121,47 +122,39 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<uid_t>, group: Option<
121122
Errno::result(res).map(drop)
122123
}
123124

124-
fn to_exec_array(args: &[CString]) -> Vec<*const c_char> {
125-
use std::ptr;
126-
use libc::c_char;
127-
128-
let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
129-
args_p.push(ptr::null());
130-
args_p
131-
}
132-
133125
#[inline]
134-
pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> {
135-
let args_p = to_exec_array(argv);
136-
137-
unsafe {
138-
libc::execv(path.as_ptr(), args_p.as_ptr())
139-
};
126+
pub fn execv<A: BorrowNullTerminatedSlice<c_char>>(path: &CStr, argv: A) -> Result<Void> {
127+
argv.borrow_null_terminated_slice(|args_p| {
128+
unsafe {
129+
libc::execv(path.as_ptr(), args_p.as_ptr())
130+
};
140131

141-
Err(Error::Sys(Errno::last()))
132+
Err(Error::Sys(Errno::last()))
133+
})
142134
}
143135

144136
#[inline]
145-
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void> {
146-
let args_p = to_exec_array(args);
147-
let env_p = to_exec_array(env);
148-
149-
unsafe {
150-
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
151-
};
152-
153-
Err(Error::Sys(Errno::last()))
137+
pub fn execve<A: BorrowNullTerminatedSlice<c_char>, E: BorrowNullTerminatedSlice<c_char>>(path: &CStr, args: A, env: E) -> Result<Void> {
138+
args.borrow_null_terminated_slice(|args_p| {
139+
env.borrow_null_terminated_slice(|env_p| {
140+
unsafe {
141+
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
142+
};
143+
144+
Err(Error::Sys(Errno::last()))
145+
})
146+
})
154147
}
155148

156149
#[inline]
157-
pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> {
158-
let args_p = to_exec_array(args);
159-
160-
unsafe {
161-
libc::execvp(filename.as_ptr(), args_p.as_ptr())
162-
};
150+
pub fn execvp<A: BorrowNullTerminatedSlice<c_char>>(filename: &CStr, args: A) -> Result<Void> {
151+
args.borrow_null_terminated_slice(|args_p| {
152+
unsafe {
153+
libc::execvp(filename.as_ptr(), args_p.as_ptr())
154+
};
163155

164-
Err(Error::Sys(Errno::last()))
156+
Err(Error::Sys(Errno::last()))
157+
})
165158
}
166159

167160
pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
@@ -379,9 +372,6 @@ mod linux {
379372
use sys::syscall::{syscall, SYSPIVOTROOT};
380373
use {Errno, Result, NixPath};
381374

382-
#[cfg(feature = "execvpe")]
383-
use std::ffi::CString;
384-
385375
pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
386376
new_root: &P1, put_old: &P2) -> Result<()> {
387377
let res = try!(try!(new_root.with_nix_path(|new_root| {
@@ -395,7 +385,6 @@ mod linux {
395385
Errno::result(res).map(drop)
396386
}
397387

398-
#[inline]
399388
#[cfg(feature = "execvpe")]
400389
pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
401390
use std::ptr;

0 commit comments

Comments
 (0)