use libc::{pid_t, c_void, c_int};
use libc;
+use std::c_str::CString;
+use std::io;
use std::mem;
use std::os;
use std::ptr;
-use std::rt::rtio;
use std::rt::rtio::{ProcessConfig, IoResult, IoError};
-use std::c_str::CString;
+use std::rt::rtio;
use super::file;
use super::util;
/// A handle to the process - on unix this will always be NULL, but on
/// windows it will be a HANDLE to the process, which will prevent the
/// pid being re-used until the handle is closed.
- handle: *(),
+ handle: *mut (),
/// None until finish() is called.
exit_code: Option<rtio::ProcessExit>,
fn get_io(io: rtio::StdioContainer,
ret: &mut Vec<Option<file::FileDesc>>)
- -> (Option<os::Pipe>, c_int)
+ -> IoResult<Option<file::FileDesc>>
{
match io {
- rtio::Ignored => { ret.push(None); (None, -1) }
- rtio::InheritFd(fd) => { ret.push(None); (None, fd) }
+ rtio::Ignored => { ret.push(None); Ok(None) }
+ rtio::InheritFd(fd) => {
+ ret.push(None);
+ Ok(Some(file::FileDesc::new(fd, true)))
+ }
rtio::CreatePipe(readable, _writable) => {
- let pipe = os::pipe();
+ let (reader, writer) = try!(pipe());
let (theirs, ours) = if readable {
- (pipe.input, pipe.out)
+ (reader, writer)
} else {
- (pipe.out, pipe.input)
+ (writer, reader)
};
- ret.push(Some(file::FileDesc::new(ours, true)));
- (Some(pipe), theirs)
+ ret.push(Some(ours));
+ Ok(Some(theirs))
}
}
}
let mut ret_io = Vec::new();
- let (in_pipe, in_fd) = get_io(cfg.stdin, &mut ret_io);
- let (out_pipe, out_fd) = get_io(cfg.stdout, &mut ret_io);
- let (err_pipe, err_fd) = get_io(cfg.stderr, &mut ret_io);
-
- let res = spawn_process_os(cfg, in_fd, out_fd, err_fd);
-
- unsafe {
- for pipe in in_pipe.iter() { let _ = libc::close(pipe.input); }
- for pipe in out_pipe.iter() { let _ = libc::close(pipe.out); }
- for pipe in err_pipe.iter() { let _ = libc::close(pipe.out); }
- }
+ let res = spawn_process_os(cfg,
+ try!(get_io(cfg.stdin, &mut ret_io)),
+ try!(get_io(cfg.stdout, &mut ret_io)),
+ try!(get_io(cfg.stderr, &mut ret_io)));
match res {
Ok(res) => {
- Ok((Process {
- pid: res.pid,
- handle: res.handle,
- exit_code: None,
- exit_signal: None,
- deadline: 0,
- },
- ret_io))
+ let p = Process {
+ pid: res.pid,
+ handle: res.handle,
+ exit_code: None,
+ exit_signal: None,
+ deadline: 0,
+ };
+ Ok((p, ret_io))
}
Err(e) => Err(e)
}
Some(..) => return Err(IoError {
code: ERROR as uint,
extra: 0,
- detail: Some("can't kill an exited process".to_str()),
+ detail: Some("can't kill an exited process".to_string()),
}),
None => {}
}
}
}
+fn pipe() -> IoResult<(file::FileDesc, file::FileDesc)> {
+ #[cfg(unix)] use ERROR = libc::EMFILE;
+ #[cfg(windows)] use ERROR = libc::WSAEMFILE;
+ struct Closer { fd: libc::c_int }
+
+ let os::Pipe { reader, writer } = match unsafe { os::pipe() } {
+ Ok(p) => p,
+ Err(io::IoError { detail, .. }) => return Err(IoError {
+ code: ERROR as uint,
+ extra: 0,
+ detail: detail,
+ })
+ };
+ let mut reader = Closer { fd: reader };
+ let mut writer = Closer { fd: writer };
+
+ let native_reader = file::FileDesc::new(reader.fd, true);
+ reader.fd = -1;
+ let native_writer = file::FileDesc::new(writer.fd, true);
+ writer.fd = -1;
+ return Ok((native_reader, native_writer));
+
+ impl Drop for Closer {
+ fn drop(&mut self) {
+ if self.fd != -1 {
+ let _ = unsafe { libc::close(self.fd) };
+ }
+ }
+ }
+}
+
#[cfg(windows)]
unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
struct SpawnProcessResult {
pid: pid_t,
- handle: *(),
+ handle: *mut (),
}
#[cfg(windows)]
fn spawn_process_os(cfg: ProcessConfig,
- in_fd: c_int, out_fd: c_int, err_fd: c_int)
+ in_fd: Option<file::FileDesc>,
+ out_fd: Option<file::FileDesc>,
+ err_fd: Option<file::FileDesc>)
-> IoResult<SpawnProcessResult> {
use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
use libc::consts::os::extra::{
use libc::funcs::extra::msvcrt::get_osfhandle;
use std::mem;
+ use std::iter::Iterator;
+ use std::str::StrSlice;
if cfg.gid.is_some() || cfg.uid.is_some() {
return Err(IoError {
code: libc::ERROR_CALL_NOT_IMPLEMENTED as uint,
extra: 0,
- detail: Some("unsupported gid/uid requested on windows".to_str()),
+ detail: Some("unsupported gid/uid requested on windows".to_string()),
})
}
+ // To have the spawning semantics of unix/windows stay the same, we need to
+ // read the *child's* PATH if one is provided. See #15149 for more details.
+ let program = cfg.env.and_then(|env| {
+ for &(ref key, ref v) in env.iter() {
+ if b"PATH" != key.as_bytes_no_nul() { continue }
+
+ // Split the value and test each path to see if the program exists.
+ for path in os::split_paths(v.as_bytes_no_nul()).move_iter() {
+ let path = path.join(cfg.program.as_bytes_no_nul())
+ .with_extension(os::consts::EXE_EXTENSION);
+ if path.exists() {
+ return Some(path.to_c_str())
+ }
+ }
+ break
+ }
+ None
+ });
+
unsafe {
let mut si = zeroed_startupinfo();
si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
// Similarly to unix, we don't actually leave holes for the stdio file
// descriptors, but rather open up /dev/null equivalents. These
// equivalents are drawn from libuv's windows process spawning.
- let set_fd = |fd: c_int, slot: &mut HANDLE, is_stdin: bool| {
- if fd == -1 {
- let access = if is_stdin {
- libc::FILE_GENERIC_READ
- } else {
- libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
- };
- let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
- let mut sa = libc::SECURITY_ATTRIBUTES {
- nLength: size as libc::DWORD,
- lpSecurityDescriptor: ptr::mut_null(),
- bInheritHandle: 1,
- };
- let filename = "NUL".to_utf16().append_one(0);
- *slot = libc::CreateFileW(filename.as_ptr(),
- access,
- libc::FILE_SHARE_READ |
- libc::FILE_SHARE_WRITE,
- &mut sa,
- libc::OPEN_EXISTING,
- 0,
- ptr::mut_null());
- if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
- return Err(super::last_error())
- }
- } else {
- let orig = get_osfhandle(fd) as HANDLE;
- if orig == INVALID_HANDLE_VALUE as HANDLE {
- return Err(super::last_error())
+ let set_fd = |fd: &Option<file::FileDesc>, slot: &mut HANDLE,
+ is_stdin: bool| {
+ match *fd {
+ None => {
+ let access = if is_stdin {
+ libc::FILE_GENERIC_READ
+ } else {
+ libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
+ };
+ let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
+ let mut sa = libc::SECURITY_ATTRIBUTES {
+ nLength: size as libc::DWORD,
+ lpSecurityDescriptor: ptr::mut_null(),
+ bInheritHandle: 1,
+ };
+ let filename: Vec<u16> = "NUL".utf16_units().collect();
+ let filename = filename.append_one(0);
+ *slot = libc::CreateFileW(filename.as_ptr(),
+ access,
+ libc::FILE_SHARE_READ |
+ libc::FILE_SHARE_WRITE,
+ &mut sa,
+ libc::OPEN_EXISTING,
+ 0,
+ ptr::mut_null());
+ if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
+ return Err(super::last_error())
+ }
}
- if DuplicateHandle(cur_proc, orig, cur_proc, slot,
- 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
- return Err(super::last_error())
+ Some(ref fd) => {
+ let orig = get_osfhandle(fd.fd()) as HANDLE;
+ if orig == INVALID_HANDLE_VALUE as HANDLE {
+ return Err(super::last_error())
+ }
+ if DuplicateHandle(cur_proc, orig, cur_proc, slot,
+ 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+ return Err(super::last_error())
+ }
}
}
Ok(())
};
- try!(set_fd(in_fd, &mut si.hStdInput, true));
- try!(set_fd(out_fd, &mut si.hStdOutput, false));
- try!(set_fd(err_fd, &mut si.hStdError, false));
+ try!(set_fd(&in_fd, &mut si.hStdInput, true));
+ try!(set_fd(&out_fd, &mut si.hStdOutput, false));
+ try!(set_fd(&err_fd, &mut si.hStdError, false));
- let cmd_str = make_command_line(cfg.program, cfg.args);
+ let cmd_str = make_command_line(program.as_ref().unwrap_or(cfg.program),
+ cfg.args);
let mut pi = zeroed_process_information();
let mut create_err = None;
with_envp(cfg.env, |envp| {
with_dirp(cfg.cwd, |dirp| {
- let mut cmd_str = cmd_str.to_utf16().append_one(0);
+ let mut cmd_str: Vec<u16> = cmd_str.as_slice().utf16_units().collect();
+ cmd_str = cmd_str.append_one(0);
let created = CreateProcessW(ptr::null(),
cmd_str.as_mut_ptr(),
ptr::mut_null(),
Ok(SpawnProcessResult {
pid: pi.dwProcessId as pid_t,
- handle: pi.hProcess as *()
+ handle: pi.hProcess as *mut ()
})
}
}
}
#[cfg(unix)]
-fn spawn_process_os(cfg: ProcessConfig, in_fd: c_int, out_fd: c_int, err_fd: c_int)
+fn spawn_process_os(cfg: ProcessConfig,
+ in_fd: Option<file::FileDesc>,
+ out_fd: Option<file::FileDesc>,
+ err_fd: Option<file::FileDesc>)
-> IoResult<SpawnProcessResult>
{
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
}
#[cfg(target_os = "macos")]
- unsafe fn set_environ(envp: *c_void) {
- extern { fn _NSGetEnviron() -> *mut *c_void; }
+ unsafe fn set_environ(envp: *const c_void) {
+ extern { fn _NSGetEnviron() -> *mut *const c_void; }
*_NSGetEnviron() = envp;
}
#[cfg(not(target_os = "macos"))]
- unsafe fn set_environ(envp: *c_void) {
- extern { static mut environ: *c_void; }
+ unsafe fn set_environ(envp: *const c_void) {
+ extern { static mut environ: *const c_void; }
environ = envp;
}
assert_eq!(ret, 0);
}
- let dirp = cfg.cwd.map(|c| c.with_ref(|p| p)).unwrap_or(ptr::null());
+ let dirp = cfg.cwd.map(|c| c.as_ptr()).unwrap_or(ptr::null());
+
+ let cfg = unsafe {
+ mem::transmute::<ProcessConfig,ProcessConfig<'static>>(cfg)
+ };
with_envp(cfg.env, proc(envp) {
with_argv(cfg.program, cfg.args, proc(argv) unsafe {
- let pipe = os::pipe();
- let mut input = file::FileDesc::new(pipe.input, true);
- let mut output = file::FileDesc::new(pipe.out, true);
+ let (mut input, mut output) = try!(pipe());
// We may use this in the child, so perform allocations before the
// fork
let pid = fork();
if pid < 0 {
- fail!("failure in fork: {}", os::last_os_error());
+ return Err(super::last_error())
} else if pid > 0 {
drop(output);
let mut bytes = [0, ..4];
Err(..) => {
Ok(SpawnProcessResult {
pid: pid,
- handle: ptr::null()
+ handle: ptr::mut_null()
})
}
Ok(..) => fail!("short read on the cloexec pipe"),
// up /dev/null into that file descriptor. Otherwise, the first file
// descriptor opened up in the child would be numbered as one of the
// stdio file descriptors, which is likely to wreak havoc.
- let setup = |src: c_int, dst: c_int| {
- let src = if src == -1 {
- let flags = if dst == libc::STDIN_FILENO {
- libc::O_RDONLY
- } else {
- libc::O_RDWR
- };
- devnull.with_ref(|p| libc::open(p, flags, 0))
- } else {
- src
+ let setup = |src: Option<file::FileDesc>, dst: c_int| {
+ let src = match src {
+ None => {
+ let flags = if dst == libc::STDIN_FILENO {
+ libc::O_RDONLY
+ } else {
+ libc::O_RDWR
+ };
+ libc::open(devnull.as_ptr(), flags, 0)
+ }
+ Some(obj) => {
+ let fd = obj.fd();
+ // Leak the memory and the file descriptor. We're in the
+ // child now an all our resources are going to be
+ // cleaned up very soon
+ mem::forget(obj);
+ fd
+ }
};
src != -1 && retry(|| dup2(src, dst)) != -1
};
}
match cfg.uid {
Some(u) => {
- // When dropping privileges from root, the `setgroups` call will
- // remove any extraneous groups. If we don't call this, then
- // even though our uid has dropped, we may still have groups
- // that enable us to do super-user things. This will fail if we
- // aren't root, so don't bother checking the return value, this
- // is just done as an optimistic privilege dropping function.
+ // When dropping privileges from root, the `setgroups` call
+ // will remove any extraneous groups. If we don't call this,
+ // then even though our uid has dropped, we may still have
+ // groups that enable us to do super-user things. This will
+ // fail if we aren't root, so don't bother checking the
+ // return value, this is just done as an optimistic
+ // privilege dropping function.
extern {
fn setgroups(ngroups: libc::c_int,
- ptr: *libc::c_void) -> libc::c_int;
+ ptr: *const libc::c_void) -> libc::c_int;
}
- let _ = setgroups(0, 0 as *libc::c_void);
+ let _ = setgroups(0, 0 as *const libc::c_void);
if libc::setuid(u as libc::uid_t) != 0 {
fail(&mut output);
if !envp.is_null() {
set_environ(envp);
}
- let _ = execvp(*argv, argv);
+ let _ = execvp(*argv, argv as *mut _);
fail(&mut output);
})
})
}
#[cfg(unix)]
-fn with_argv<T>(prog: &CString, args: &[CString], cb: proc(**libc::c_char) -> T) -> T {
- let mut ptrs: Vec<*libc::c_char> = Vec::with_capacity(args.len()+1);
+fn with_argv<T>(prog: &CString, args: &[CString],
+ cb: proc(*const *const libc::c_char) -> T) -> T {
+ let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
// Convert the CStrings into an array of pointers. Note: the
// lifetime of the various CStrings involved is guaranteed to be
// larger than the lifetime of our invocation of cb, but this is
// technically unsafe as the callback could leak these pointers
// out of our scope.
- ptrs.push(prog.with_ref(|buf| buf));
- ptrs.extend(args.iter().map(|tmp| tmp.with_ref(|buf| buf)));
+ ptrs.push(prog.as_ptr());
+ ptrs.extend(args.iter().map(|tmp| tmp.as_ptr()));
// Add a terminating null pointer (required by libc).
ptrs.push(ptr::null());
}
#[cfg(unix)]
-fn with_envp<T>(env: Option<&[(CString, CString)]>, cb: proc(*c_void) -> T) -> T {
+fn with_envp<T>(env: Option<&[(&CString, &CString)]>,
+ cb: proc(*const c_void) -> T) -> T {
// On posixy systems we can pass a char** for envp, which is a
// null-terminated array of "k=v\0" strings. Since we must create
// these strings locally, yet expose a raw pointer to them, we
}
// As with `with_argv`, this is unsafe, since cb could leak the pointers.
- let mut ptrs: Vec<*libc::c_char> =
+ let mut ptrs: Vec<*const libc::c_char> =
tmps.iter()
- .map(|tmp| tmp.as_ptr() as *libc::c_char)
+ .map(|tmp| tmp.as_ptr() as *const libc::c_char)
.collect();
ptrs.push(ptr::null());
- cb(ptrs.as_ptr() as *c_void)
+ cb(ptrs.as_ptr() as *const c_void)
}
_ => cb(ptr::null())
}
}
#[cfg(windows)]
-fn with_envp<T>(env: Option<&[(CString, CString)]>, cb: |*mut c_void| -> T) -> T {
+fn with_envp<T>(env: Option<&[(&CString, &CString)]>, cb: |*mut c_void| -> T) -> T {
// On win32 we pass an "environment block" which is not a char**, but
// rather a concatenation of null-terminated k=v\0 sequences, with a final
// \0 to terminate.
let kv = format!("{}={}",
pair.ref0().as_str().unwrap(),
pair.ref1().as_str().unwrap());
- blk.push_all(kv.to_utf16().as_slice());
+ blk.extend(kv.as_slice().utf16_units());
blk.push(0);
}
}
#[cfg(windows)]
-fn with_dirp<T>(d: Option<&CString>, cb: |*u16| -> T) -> T {
+fn with_dirp<T>(d: Option<&CString>, cb: |*const u16| -> T) -> T {
match d {
Some(dir) => {
let dir_str = dir.as_str()
.expect("expected workingdirectory to be utf-8 encoded");
- let dir_str = dir_str.to_utf16().append_one(0);
+ let dir_str: Vec<u16> = dir_str.utf16_units().collect();
+ let dir_str = dir_str.append_one(0);
+
cb(dir_str.as_ptr())
},
None => cb(ptr::null())
}
#[cfg(windows)]
-fn free_handle(handle: *()) {
+fn free_handle(handle: *mut ()) {
assert!(unsafe {
libc::CloseHandle(mem::transmute(handle)) != 0
})
}
#[cfg(unix)]
-fn free_handle(_handle: *()) {
+fn free_handle(_handle: *mut ()) {
// unix has no process handle object, just a pid
}
#[cfg(target_os = "macos")]
#[cfg(target_os = "ios")]
#[cfg(target_os = "freebsd")]
+ #[cfg(target_os = "dragonfly")]
mod imp {
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
let now = ::io::timer::now();
let ms = if now < deadline {deadline - now} else {0};
tv = util::ms_to_timeval(ms);
- (&tv as *_, idx)
+ (&mut tv as *mut _, idx)
}
- None => (ptr::null(), -1),
+ None => (ptr::mut_null(), -1),
};
// Wait for something to happen
c::fd_set(&mut set, input);
c::fd_set(&mut set, read_fd);
- match unsafe { c::select(max, &set, ptr::null(), ptr::null(), p) } {
+ match unsafe { c::select(max, &mut set, ptr::mut_null(),
+ ptr::mut_null(), p) } {
// interrupted, retry
-1 if os::errno() == libc::EINTR as int => continue,
// which will wake up the other end at some point, so we just allow this
// signal to be coalesced with the pending signals on the pipe.
extern fn sigchld_handler(_signum: libc::c_int) {
- let mut msg = 1;
+ let msg = 1i;
match unsafe {
- libc::write(WRITE_FD, &mut msg as *mut _ as *libc::c_void, 1)
+ libc::write(WRITE_FD, &msg as *const _ as *const libc::c_void, 1)
} {
1 => {}
-1 if util::wouldblock() => {} // see above comments