Skip to content

Commit 9506b74

Browse files
committed
Null terminated slices for exec
1 parent 2170b51 commit 9506b74

File tree

2 files changed

+211
-30
lines changed

2 files changed

+211
-30
lines changed

src/lib.rs

+185
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,188 @@ impl<'a, NP: ?Sized + NixPath> NixPath for Option<&'a NP> {
268268
}
269269
}
270270
}
271+
272+
/*
273+
*
274+
* ===== Null terminated slices for exec =====
275+
*
276+
*/
277+
278+
use std::ops::{Deref, DerefMut};
279+
use std::mem::transmute;
280+
use std::iter;
281+
282+
/// A conversion trait that may borrow or allocate memory depending on the input.
283+
/// Used to convert between terminated slices and `Vec`s.
284+
pub trait IntoRef<'a, T: ?Sized> {
285+
type Target: 'a + AsRef<T> + Deref<Target=T>;
286+
287+
fn into_ref(self) -> Self::Target;
288+
}
289+
290+
/// A slice of references terminated by `None`. Used by API calls that accept
291+
/// null-terminated arrays such as the `exec` family of functions.
292+
pub struct TerminatedSlice<T> {
293+
inner: [Option<T>],
294+
}
295+
296+
impl<T> TerminatedSlice<T> {
297+
/// Instantiate a `TerminatedSlice` from a slice ending in `None`. Returns
298+
/// `None` if the provided slice is not properly terminated.
299+
pub fn from_slice(slice: &[Option<T>]) -> Option<&Self> {
300+
if slice.last().map(Option::is_none).unwrap_or(false) {
301+
Some(unsafe { Self::from_slice_unchecked(slice) })
302+
} else {
303+
None
304+
}
305+
}
306+
307+
/// Instantiate a `TerminatedSlice` from a mutable slice ending in `None`.
308+
/// Returns `None` if the provided slice is not properly terminated.
309+
pub fn from_slice_mut(slice: &mut [Option<T>]) -> Option<&mut Self> {
310+
if slice.last().map(Option::is_none).unwrap_or(false) {
311+
Some(unsafe { Self::from_slice_mut_unchecked(slice) })
312+
} else {
313+
None
314+
}
315+
}
316+
317+
/// Instantiate a `TerminatedSlice` from a slice ending in `None`.
318+
///
319+
/// ## Unsafety
320+
///
321+
/// This assumes that the slice is properly terminated, and can cause
322+
/// undefined behaviour if that invariant is not upheld.
323+
pub unsafe fn from_slice_unchecked(slice: &[Option<T>]) -> &Self {
324+
transmute(slice)
325+
}
326+
327+
/// Instantiate a `TerminatedSlice` from a mutable slice ending in `None`.
328+
///
329+
/// ## Unsafety
330+
///
331+
/// This assumes that the slice is properly terminated, and can cause
332+
/// undefined behaviour if that invariant is not upheld.
333+
pub unsafe fn from_slice_mut_unchecked(slice: &mut [Option<T>]) -> &mut Self {
334+
transmute(slice)
335+
}
336+
}
337+
338+
impl<'a, U: Sized> TerminatedSlice<&'a U> {
339+
pub fn as_ptr(&self) -> *const *const U {
340+
self.inner.as_ptr() as *const _
341+
}
342+
}
343+
344+
impl<T> Deref for TerminatedSlice<T> {
345+
type Target = [Option<T>];
346+
347+
fn deref(&self) -> &Self::Target {
348+
&self.inner[..self.inner.len() - 1]
349+
}
350+
}
351+
352+
impl<T> DerefMut for TerminatedSlice<T> {
353+
fn deref_mut(&mut self) -> &mut Self::Target {
354+
let len = self.inner.len();
355+
&mut self.inner[..len - 1]
356+
}
357+
}
358+
359+
impl<T> AsRef<TerminatedSlice<T>> for TerminatedSlice<T> {
360+
fn as_ref(&self) -> &Self {
361+
self
362+
}
363+
}
364+
365+
/// Owned variant of `TerminatedSlice`.
366+
pub struct TerminatedVec<T> {
367+
inner: Vec<Option<T>>,
368+
}
369+
370+
impl<T> TerminatedVec<T> {
371+
/// Instantiates a `TerminatedVec` from a `None` terminated `Vec`. Returns
372+
/// `None` if the provided `Vec` is not properly terminated.
373+
pub fn from_vec(vec: Vec<Option<T>>) -> Option<Self> {
374+
if vec.last().map(Option::is_none).unwrap_or(false) {
375+
Some(unsafe { Self::from_vec_unchecked(vec) })
376+
} else {
377+
None
378+
}
379+
}
380+
381+
/// Instantiates a `TerminatedVec` from a `None` terminated `Vec`.
382+
///
383+
/// ## Unsafety
384+
///
385+
/// This assumes that the `Vec` is properly terminated, and can cause
386+
/// undefined behaviour if that invariant is not upheld.
387+
pub unsafe fn from_vec_unchecked(vec: Vec<Option<T>>) -> Self {
388+
TerminatedVec {
389+
inner: vec,
390+
}
391+
}
392+
393+
/// Consume `self` to return the inner wrapped `Vec`.
394+
pub fn into_inner(self) -> Vec<Option<T>> {
395+
self.inner
396+
}
397+
}
398+
399+
impl<'a> TerminatedVec<&'a c_char> {
400+
fn terminate<T: AsRef<CStr> + 'a, I: IntoIterator<Item=T>>(iter: I) -> Self {
401+
fn cstr_char<'a, S: AsRef<CStr> + 'a>(s: S) -> &'a c_char {
402+
unsafe {
403+
&*s.as_ref().as_ptr()
404+
}
405+
}
406+
407+
let terminated = iter.into_iter()
408+
.map(cstr_char)
409+
.map(Some).chain(iter::once(None)).collect();
410+
411+
unsafe {
412+
TerminatedVec::from_vec_unchecked(terminated)
413+
}
414+
}
415+
}
416+
417+
impl<T> Deref for TerminatedVec<T> {
418+
type Target = TerminatedSlice<T>;
419+
420+
fn deref(&self) -> &Self::Target {
421+
unsafe {
422+
TerminatedSlice::from_slice_unchecked(&self.inner)
423+
}
424+
}
425+
}
426+
427+
impl<T> DerefMut for TerminatedVec<T> {
428+
fn deref_mut(&mut self) -> &mut Self::Target {
429+
unsafe {
430+
TerminatedSlice::from_slice_mut_unchecked(&mut self.inner)
431+
}
432+
}
433+
}
434+
435+
impl<T> AsRef<TerminatedSlice<T>> for TerminatedVec<T> {
436+
fn as_ref(&self) -> &TerminatedSlice<T> {
437+
self
438+
}
439+
}
440+
441+
impl<'a, T: 'a> IntoRef<'a, TerminatedSlice<&'a T>> for &'a TerminatedSlice<&'a T> {
442+
type Target = &'a TerminatedSlice<&'a T>;
443+
444+
fn into_ref(self) -> Self::Target {
445+
self
446+
}
447+
}
448+
449+
impl<'a, T: AsRef<CStr> + 'a, I: IntoIterator<Item=T>> IntoRef<'a, TerminatedSlice<&'a c_char>> for I {
450+
type Target = TerminatedVec<&'a c_char>;
451+
452+
fn into_ref(self) -> Self::Target {
453+
TerminatedVec::terminate(self)
454+
}
455+
}

src/unistd.rs

+26-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Safe wrappers around functions found in libc "unistd.h" header
22
33
use errno::{self, Errno};
4-
use {Error, Result, NixPath};
4+
use {Error, Result, NixPath, IntoRef, TerminatedSlice};
55
use fcntl::{fcntl, FdFlag, OFlag};
66
use fcntl::FcntlArg::F_SETFD;
77
use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
@@ -552,25 +552,19 @@ pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gi
552552
Errno::result(res).map(drop)
553553
}
554554

555-
fn to_exec_array(args: &[CString]) -> Vec<*const c_char> {
556-
let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
557-
args_p.push(ptr::null());
558-
args_p
559-
}
560-
561555
/// Replace the current process image with a new one (see
562556
/// [exec(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
563557
///
564558
/// See the `::nix::unistd::execve` system call for additional details. `execv`
565559
/// performs the same action but does not allow for customization of the
566560
/// environment for the new process.
567561
#[inline]
568-
pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> {
569-
let args_p = to_exec_array(argv);
562+
pub fn execv<'a, P: ?Sized + NixPath, A: IntoRef<'a, TerminatedSlice<&'a c_char>>>(path: &P, argv: A) -> Result<Void> {
563+
let args_p = argv.into_ref();
570564

571-
unsafe {
572-
libc::execv(path.as_ptr(), args_p.as_ptr())
573-
};
565+
try!(path.with_nix_path(|cstr| {
566+
unsafe { libc::execv(cstr.as_ptr(), args_p.as_ptr()) }
567+
}));
574568

575569
Err(Error::Sys(Errno::last()))
576570
}
@@ -589,13 +583,13 @@ pub fn execv(path: &CString, argv: &[CString]) -> Result<Void> {
589583
/// in the `args` list is an argument to the new process. Each element in the
590584
/// `env` list should be a string in the form "key=value".
591585
#[inline]
592-
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void> {
593-
let args_p = to_exec_array(args);
594-
let env_p = to_exec_array(env);
586+
pub fn execve<'a, 'e, P: ?Sized + NixPath, A: IntoRef<'a, TerminatedSlice<&'a c_char>>, E: IntoRef<'e, TerminatedSlice<&'e c_char>>>(path: &P, args: A, env: E) -> Result<Void> {
587+
let args_p = args.into_ref();
588+
let env_p = env.into_ref();
595589

596-
unsafe {
597-
libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
598-
};
590+
try!(path.with_nix_path(|cstr| {
591+
unsafe { libc::execve(cstr.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) }
592+
}));
599593

600594
Err(Error::Sys(Errno::last()))
601595
}
@@ -610,12 +604,14 @@ pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<Void>
610604
/// would not work if "bash" was specified for the path argument, but `execvp`
611605
/// would assuming that a bash executable was on the system `PATH`.
612606
#[inline]
613-
pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> {
614-
let args_p = to_exec_array(args);
607+
pub fn execvp<'a, P: ?Sized + NixPath, A: IntoRef<'a, TerminatedSlice<&'a c_char>>>(filename: &P, args: A) -> Result<Void> {
608+
let args_p = args.into_ref();
615609

616-
unsafe {
617-
libc::execvp(filename.as_ptr(), args_p.as_ptr())
618-
};
610+
try!(filename.with_nix_path(|cstr| {
611+
unsafe {
612+
libc::execvp(cstr.as_ptr(), args_p.as_ptr())
613+
}
614+
}));
619615

620616
Err(Error::Sys(Errno::last()))
621617
}
@@ -633,9 +629,9 @@ pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> {
633629
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
634630
target_os = "netbsd", target_os = "openbsd", target_os = "linux"))]
635631
#[inline]
636-
pub fn fexecve(fd: RawFd, args: &[CString], env: &[CString]) -> Result<Void> {
637-
let args_p = to_exec_array(args);
638-
let env_p = to_exec_array(env);
632+
pub fn fexecve<'a, 'e, A: IntoRef<'a, TerminatedSlice<&'a c_char>>, E: IntoRef<'e, TerminatedSlice<&'e c_char>>>(fd: RawFd, args: A, env: E) -> Result<Void> {
633+
let args_p = args.into_ref();
634+
let env_p = env.into_ref();
639635

640636
unsafe {
641637
libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
@@ -656,10 +652,10 @@ pub fn fexecve(fd: RawFd, args: &[CString], env: &[CString]) -> Result<Void> {
656652
/// is referenced as a file descriptor to the base directory plus a path.
657653
#[cfg(any(target_os = "android", target_os = "linux"))]
658654
#[inline]
659-
pub fn execveat(dirfd: RawFd, pathname: &CString, args: &[CString],
660-
env: &[CString], flags: super::fcntl::AtFlags) -> Result<Void> {
661-
let args_p = to_exec_array(args);
662-
let env_p = to_exec_array(env);
655+
pub fn execveat<'a, 'e, A: IntoRef<'a, TerminatedSlice<&'a c_char>>, E: IntoRef<'e, TerminatedSlice<&'e c_char>>>(dirfd: RawFd, pathname: &CString, args: A,
656+
env: E, flags: super::fcntl::AtFlags) -> Result<Void> {
657+
let args_p = args.into_ref();
658+
let env_p = env.into_ref();
663659

664660
unsafe {
665661
libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(),

0 commit comments

Comments
 (0)