diff options
Diffstat (limited to 'src/libstd/io/native')
| -rw-r--r-- | src/libstd/io/native/file.rs | 965 | ||||
| -rw-r--r-- | src/libstd/io/native/mod.rs | 228 | ||||
| -rw-r--r-- | src/libstd/io/native/process.rs | 654 |
3 files changed, 0 insertions, 1847 deletions
diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs deleted file mode 100644 index de2655303d6..00000000000 --- a/src/libstd/io/native/file.rs +++ /dev/null @@ -1,965 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Blocking posix-based file I/O - -#[allow(non_camel_case_types)]; - -use c_str::CString; -use io::IoError; -use io; -use libc::c_int; -use libc; -use ops::Drop; -use option::{Some, None, Option}; -use os; -use path::{Path, GenericPath}; -use ptr::RawPtr; -use result::{Result, Ok, Err}; -use rt::rtio; -use super::IoResult; -use unstable::intrinsics; -use vec::ImmutableVector; -use vec; - -#[cfg(windows)] use os::win32::{as_utf16_p, fill_utf16_buf_and_decode}; -#[cfg(windows)] use ptr; -#[cfg(windows)] use str; - -fn keep_going(data: &[u8], f: |*u8, uint| -> i64) -> i64 { - #[cfg(windows)] static eintr: int = 0; // doesn't matter - #[cfg(not(windows))] static eintr: int = libc::EINTR as int; - - let origamt = data.len(); - let mut data = data.as_ptr(); - let mut amt = origamt; - while amt > 0 { - let mut ret; - loop { - ret = f(data, amt); - if cfg!(not(windows)) { break } // windows has no eintr - // if we get an eintr, then try again - if ret != -1 || os::errno() as int != eintr { break } - } - if ret == 0 { - break - } else if ret != -1 { - amt -= ret as uint; - data = unsafe { data.offset(ret as int) }; - } else { - return ret; - } - } - return (origamt - amt) as i64; -} - -pub type fd_t = libc::c_int; - -pub struct FileDesc { - priv fd: fd_t, - priv close_on_drop: bool, -} - -impl FileDesc { - /// Create a `FileDesc` from an open C file descriptor. - /// - /// The `FileDesc` will take ownership of the specified file descriptor and - /// close it upon destruction if the `close_on_drop` flag is true, otherwise - /// it will not close the file descriptor when this `FileDesc` is dropped. - /// - /// Note that all I/O operations done on this object will be *blocking*, but - /// they do not require the runtime to be active. - pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc { - FileDesc { fd: fd, close_on_drop: close_on_drop } - } - - fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { - #[cfg(windows)] type rlen = libc::c_uint; - #[cfg(not(windows))] type rlen = libc::size_t; - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::read(self.fd, buf as *mut libc::c_void, len as rlen) as i64 - } - }); - if ret == 0 { - Err(io::standard_error(io::EndOfFile)) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as uint) - } - } - fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> { - #[cfg(windows)] type wlen = libc::c_uint; - #[cfg(not(windows))] type wlen = libc::size_t; - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::write(self.fd, buf as *libc::c_void, len as wlen) as i64 - } - }); - if ret < 0 { - Err(super::last_error()) - } else { - Ok(()) - } - } -} - -impl io::Reader for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Option<uint> { - match self.inner_read(buf) { Ok(n) => Some(n), Err(..) => None } - } - fn eof(&mut self) -> bool { false } -} - -impl io::Writer for FileDesc { - fn write(&mut self, buf: &[u8]) { - self.inner_write(buf); - } -} - -impl rtio::RtioFileStream for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> { - self.inner_read(buf).map(|i| i as int) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } - fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> { - return os_pread(self.fd, buf.as_ptr(), buf.len(), offset); - - #[cfg(windows)] - fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> { - unsafe { - let mut overlap: libc::OVERLAPPED = intrinsics::init(); - let handle = libc::get_osfhandle(fd) as libc::HANDLE; - let mut bytes_read = 0; - overlap.Offset = offset as libc::DWORD; - overlap.OffsetHigh = (offset >> 32) as libc::DWORD; - - match libc::ReadFile(handle, buf as libc::LPVOID, - amt as libc::DWORD, - &mut bytes_read, &mut overlap) { - 0 => Err(super::last_error()), - _ => Ok(bytes_read as int) - } - } - } - - #[cfg(unix)] - fn os_pread(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<int> { - match unsafe { - libc::pread(fd, buf as *libc::c_void, amt as libc::size_t, - offset as libc::off_t) - } { - -1 => Err(super::last_error()), - n => Ok(n as int) - } - } - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { - return os_pwrite(self.fd, buf.as_ptr(), buf.len(), offset); - - #[cfg(windows)] - fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { - unsafe { - let mut overlap: libc::OVERLAPPED = intrinsics::init(); - let handle = libc::get_osfhandle(fd) as libc::HANDLE; - overlap.Offset = offset as libc::DWORD; - overlap.OffsetHigh = (offset >> 32) as libc::DWORD; - - match libc::WriteFile(handle, buf as libc::LPVOID, - amt as libc::DWORD, - ptr::mut_null(), &mut overlap) { - 0 => Err(super::last_error()), - _ => Ok(()), - } - } - } - - #[cfg(unix)] - fn os_pwrite(fd: c_int, buf: *u8, amt: uint, offset: u64) -> IoResult<()> { - super::mkerr_libc(unsafe { - libc::pwrite(fd, buf as *libc::c_void, amt as libc::size_t, - offset as libc::off_t) - } as c_int) - } - } - #[cfg(windows)] - fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> { - let whence = match style { - io::SeekSet => libc::FILE_BEGIN, - io::SeekEnd => libc::FILE_END, - io::SeekCur => libc::FILE_CURRENT, - }; - unsafe { - let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; - let mut newpos = 0; - match libc::SetFilePointerEx(handle, pos, &mut newpos, whence) { - 0 => Err(super::last_error()), - _ => Ok(newpos as u64), - } - } - } - #[cfg(unix)] - fn seek(&mut self, pos: i64, whence: io::SeekStyle) -> Result<u64, IoError> { - let whence = match whence { - io::SeekSet => libc::SEEK_SET, - io::SeekEnd => libc::SEEK_END, - io::SeekCur => libc::SEEK_CUR, - }; - let n = unsafe { libc::lseek(self.fd, pos as libc::off_t, whence) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn tell(&self) -> Result<u64, IoError> { - let n = unsafe { libc::lseek(self.fd, 0, libc::SEEK_CUR) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn fsync(&mut self) -> Result<(), IoError> { - return os_fsync(self.fd); - - #[cfg(windows)] - fn os_fsync(fd: c_int) -> IoResult<()> { - super::mkerr_winbool(unsafe { - let handle = libc::get_osfhandle(fd); - libc::FlushFileBuffers(handle as libc::HANDLE) - }) - } - #[cfg(unix)] - fn os_fsync(fd: c_int) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::fsync(fd) }) - } - } - #[cfg(windows)] - fn datasync(&mut self) -> Result<(), IoError> { return self.fsync(); } - - #[cfg(not(windows))] - fn datasync(&mut self) -> Result<(), IoError> { - return super::mkerr_libc(os_datasync(self.fd)); - - #[cfg(target_os = "macos")] - fn os_datasync(fd: c_int) -> c_int { - unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) } - } - #[cfg(target_os = "linux")] - fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fdatasync(fd) } } - #[cfg(not(target_os = "macos"), not(target_os = "linux"))] - fn os_datasync(fd: c_int) -> c_int { unsafe { libc::fsync(fd) } } - } - - #[cfg(windows)] - fn truncate(&mut self, offset: i64) -> Result<(), IoError> { - let orig_pos = match self.tell() { Ok(i) => i, Err(e) => return Err(e) }; - match self.seek(offset, io::SeekSet) { - Ok(_) => {}, Err(e) => return Err(e), - }; - let ret = unsafe { - let handle = libc::get_osfhandle(self.fd) as libc::HANDLE; - match libc::SetEndOfFile(handle) { - 0 => Err(super::last_error()), - _ => Ok(()) - } - }; - self.seek(orig_pos as i64, io::SeekSet); - return ret; - } - #[cfg(unix)] - fn truncate(&mut self, offset: i64) -> Result<(), IoError> { - super::mkerr_libc(unsafe { - libc::ftruncate(self.fd, offset as libc::off_t) - }) - } -} - -impl rtio::RtioPipe for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } -} - -impl rtio::RtioTTY for FileDesc { - fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> { - self.inner_read(buf) - } - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - self.inner_write(buf) - } - fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> { - Err(super::unimpl()) - } - fn get_winsize(&mut self) -> Result<(int, int), IoError> { - Err(super::unimpl()) - } - fn isatty(&self) -> bool { false } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - // closing stdio file handles makes no sense, so never do it - if self.close_on_drop && self.fd > libc::STDERR_FILENO { - unsafe { libc::close(self.fd); } - } - } -} - -pub struct CFile { - priv file: *libc::FILE, - priv fd: FileDesc, -} - -impl CFile { - /// Create a `CFile` from an open `FILE` pointer. - /// - /// The `CFile` takes ownership of the `FILE` pointer and will close it upon - /// destruction. - pub fn new(file: *libc::FILE) -> CFile { - CFile { - file: file, - fd: FileDesc::new(unsafe { libc::fileno(file) }, false) - } - } - - pub fn flush(&mut self) -> Result<(), IoError> { - super::mkerr_libc(unsafe { libc::fflush(self.file) }) - } -} - -impl rtio::RtioFileStream for CFile { - fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }); - if ret == 0 { - Err(io::standard_error(io::EndOfFile)) - } else if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as int) - } - } - - fn write(&mut self, buf: &[u8]) -> Result<(), IoError> { - let ret = keep_going(buf, |buf, len| { - unsafe { - libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t, - self.file) as i64 - } - }); - if ret < 0 { - Err(super::last_error()) - } else { - Ok(()) - } - } - - fn pread(&mut self, buf: &mut [u8], offset: u64) -> Result<int, IoError> { - self.flush(); - self.fd.pread(buf, offset) - } - fn pwrite(&mut self, buf: &[u8], offset: u64) -> Result<(), IoError> { - self.flush(); - self.fd.pwrite(buf, offset) - } - fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> { - let whence = match style { - io::SeekSet => libc::SEEK_SET, - io::SeekEnd => libc::SEEK_END, - io::SeekCur => libc::SEEK_CUR, - }; - let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) }; - if n < 0 { - Err(super::last_error()) - } else { - Ok(n as u64) - } - } - fn tell(&self) -> Result<u64, IoError> { - let ret = unsafe { libc::ftell(self.file) }; - if ret < 0 { - Err(super::last_error()) - } else { - Ok(ret as u64) - } - } - fn fsync(&mut self) -> Result<(), IoError> { - self.flush(); - self.fd.fsync() - } - fn datasync(&mut self) -> Result<(), IoError> { - self.flush(); - self.fd.fsync() - } - fn truncate(&mut self, offset: i64) -> Result<(), IoError> { - self.flush(); - self.fd.truncate(offset) - } -} - -impl Drop for CFile { - fn drop(&mut self) { - unsafe { libc::fclose(self.file); } - } -} - -pub fn open(path: &CString, fm: io::FileMode, fa: io::FileAccess) - -> IoResult<FileDesc> { - let flags = match fm { - io::Open => 0, - io::Append => libc::O_APPEND, - io::Truncate => libc::O_TRUNC, - }; - // Opening with a write permission must silently create the file. - let (flags, mode) = match fa { - io::Read => (flags | libc::O_RDONLY, 0), - io::Write => (flags | libc::O_WRONLY | libc::O_CREAT, - libc::S_IRUSR | libc::S_IWUSR), - io::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT, - libc::S_IRUSR | libc::S_IWUSR), - }; - - return match os_open(path, flags, mode) { - -1 => Err(super::last_error()), - fd => Ok(FileDesc::new(fd, true)), - }; - - #[cfg(windows)] - fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { - as_utf16_p(path.as_str().unwrap(), |path| { - unsafe { libc::wopen(path, flags, mode) } - }) - } - - #[cfg(unix)] - fn os_open(path: &CString, flags: c_int, mode: c_int) -> c_int { - unsafe { libc::open(path.with_ref(|p| p), flags, mode) } - } -} - -pub fn mkdir(p: &CString, mode: io::FilePermission) -> IoResult<()> { - return os_mkdir(p, mode as c_int); - - #[cfg(windows)] - fn os_mkdir(p: &CString, _mode: c_int) -> IoResult<()> { - super::mkerr_winbool(unsafe { - // FIXME: turn mode into something useful? #2623 - as_utf16_p(p.as_str().unwrap(), |buf| { - libc::CreateDirectoryW(buf, ptr::mut_null()) - }) - }) - } - - #[cfg(unix)] - fn os_mkdir(p: &CString, mode: c_int) -> IoResult<()> { - super::mkerr_libc(unsafe { - libc::mkdir(p.with_ref(|p| p), mode as libc::mode_t) - }) - } -} - -pub fn readdir(p: &CString) -> IoResult<~[Path]> { - fn prune(root: &CString, dirs: ~[Path]) -> ~[Path] { - let root = unsafe { CString::new(root.with_ref(|p| p), false) }; - let root = Path::new(root); - - dirs.move_iter().filter(|path| { - path.as_vec() != bytes!(".") && path.as_vec() != bytes!("..") - }).map(|path| root.join(path)).collect() - } - - unsafe { - #[cfg(not(windows))] - unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { - use libc::{dirent_t}; - use libc::{opendir, readdir, closedir}; - extern { - fn rust_list_dir_val(ptr: *dirent_t) -> *libc::c_char; - } - debug!("os::list_dir -- BEFORE OPENDIR"); - - let dir_ptr = p.with_ref(|buf| opendir(buf)); - - if (dir_ptr as uint != 0) { - let mut paths = ~[]; - debug!("os::list_dir -- opendir() SUCCESS"); - let mut entry_ptr = readdir(dir_ptr); - while (entry_ptr as uint != 0) { - let cstr = CString::new(rust_list_dir_val(entry_ptr), false); - paths.push(Path::new(cstr)); - entry_ptr = readdir(dir_ptr); - } - closedir(dir_ptr); - Ok(paths) - } else { - Err(super::last_error()) - } - } - - #[cfg(windows)] - unsafe fn get_list(p: &CString) -> IoResult<~[Path]> { - use libc::consts::os::extra::INVALID_HANDLE_VALUE; - use libc::{wcslen, free}; - use libc::funcs::extra::kernel32::{ - FindFirstFileW, - FindNextFileW, - FindClose, - }; - use libc::types::os::arch::extra::HANDLE; - use os::win32::{ - as_utf16_p - }; - use rt::global_heap::malloc_raw; - - #[nolink] - extern { - fn rust_list_dir_wfd_size() -> libc::size_t; - fn rust_list_dir_wfd_fp_buf(wfd: *libc::c_void) -> *u16; - } - let p = CString::new(p.with_ref(|p| p), false); - let p = Path::new(p); - let star = p.join("*"); - as_utf16_p(star.as_str().unwrap(), |path_ptr| { - let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint); - let find_handle = FindFirstFileW(path_ptr, wfd_ptr as HANDLE); - if find_handle as libc::c_int != INVALID_HANDLE_VALUE { - let mut paths = ~[]; - let mut more_files = 1 as libc::c_int; - while more_files != 0 { - let fp_buf = rust_list_dir_wfd_fp_buf(wfd_ptr); - if fp_buf as uint == 0 { - fail!("os::list_dir() failure: got null ptr from wfd"); - } - else { - let fp_vec = vec::from_buf( - fp_buf, wcslen(fp_buf) as uint); - let fp_str = str::from_utf16(fp_vec); - paths.push(Path::new(fp_str)); - } - more_files = FindNextFileW(find_handle, wfd_ptr as HANDLE); - } - FindClose(find_handle); - free(wfd_ptr); - Ok(paths) - } else { - Err(super::last_error()) - } - }) - } - - get_list(p).map(|paths| prune(p, paths)) - } -} - -pub fn unlink(p: &CString) -> IoResult<()> { - return os_unlink(p); - - #[cfg(windows)] - fn os_unlink(p: &CString) -> IoResult<()> { - super::mkerr_winbool(unsafe { - as_utf16_p(p.as_str().unwrap(), |buf| { - libc::DeleteFileW(buf) - }) - }) - } - - #[cfg(unix)] - fn os_unlink(p: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { libc::unlink(p.with_ref(|p| p)) }) - } -} - -pub fn rename(old: &CString, new: &CString) -> IoResult<()> { - return os_rename(old, new); - - #[cfg(windows)] - fn os_rename(old: &CString, new: &CString) -> IoResult<()> { - super::mkerr_winbool(unsafe { - as_utf16_p(old.as_str().unwrap(), |old| { - as_utf16_p(new.as_str().unwrap(), |new| { - libc::MoveFileExW(old, new, libc::MOVEFILE_REPLACE_EXISTING) - }) - }) - }) - } - - #[cfg(unix)] - fn os_rename(old: &CString, new: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { - libc::rename(old.with_ref(|p| p), new.with_ref(|p| p)) - }) - } -} - -pub fn chmod(p: &CString, mode: io::FilePermission) -> IoResult<()> { - return super::mkerr_libc(os_chmod(p, mode as c_int)); - - #[cfg(windows)] - fn os_chmod(p: &CString, mode: c_int) -> c_int { - unsafe { - as_utf16_p(p.as_str().unwrap(), |p| libc::wchmod(p, mode)) - } - } - - #[cfg(unix)] - fn os_chmod(p: &CString, mode: c_int) -> c_int { - unsafe { libc::chmod(p.with_ref(|p| p), mode as libc::mode_t) } - } -} - -pub fn rmdir(p: &CString) -> IoResult<()> { - return super::mkerr_libc(os_rmdir(p)); - - #[cfg(windows)] - fn os_rmdir(p: &CString) -> c_int { - unsafe { - as_utf16_p(p.as_str().unwrap(), |p| libc::wrmdir(p)) - } - } - - #[cfg(unix)] - fn os_rmdir(p: &CString) -> c_int { - unsafe { libc::rmdir(p.with_ref(|p| p)) } - } -} - -pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> { - return super::mkerr_libc(os_chown(p, uid, gid)); - - // libuv has this as a no-op, so seems like this should as well? - #[cfg(windows)] - fn os_chown(_p: &CString, _uid: int, _gid: int) -> c_int { 0 } - - #[cfg(unix)] - fn os_chown(p: &CString, uid: int, gid: int) -> c_int { - unsafe { - libc::chown(p.with_ref(|p| p), uid as libc::uid_t, - gid as libc::gid_t) - } - } -} - -pub fn readlink(p: &CString) -> IoResult<Path> { - return os_readlink(p); - - // XXX: I have a feeling that this reads intermediate symlinks as well. - #[cfg(windows)] - fn os_readlink(p: &CString) -> IoResult<Path> { - let handle = unsafe { - as_utf16_p(p.as_str().unwrap(), |p| { - libc::CreateFileW(p, - libc::GENERIC_READ, - libc::FILE_SHARE_READ, - ptr::mut_null(), - libc::OPEN_EXISTING, - libc::FILE_ATTRIBUTE_NORMAL, - ptr::mut_null()) - }) - }; - if handle == ptr::mut_null() { return Err(super::last_error()) } - let ret = fill_utf16_buf_and_decode(|buf, sz| { - unsafe { - libc::GetFinalPathNameByHandleW(handle, buf as *u16, sz, - libc::VOLUME_NAME_NT) - } - }); - let ret = match ret { - Some(s) => Ok(Path::new(s)), - None => Err(super::last_error()), - }; - unsafe { libc::CloseHandle(handle) }; - return ret; - - } - - #[cfg(unix)] - fn os_readlink(p: &CString) -> IoResult<Path> { - let p = p.with_ref(|p| p); - let mut len = unsafe { libc::pathconf(p, libc::_PC_NAME_MAX) }; - if len == -1 { - len = 1024; // XXX: read PATH_MAX from C ffi? - } - let mut buf = vec::with_capacity::<u8>(len as uint); - match unsafe { - libc::readlink(p, buf.as_ptr() as *mut libc::c_char, - len as libc::size_t) - } { - -1 => Err(super::last_error()), - n => { - assert!(n > 0); - unsafe { buf.set_len(n as uint); } - Ok(Path::new(buf)) - } - } - } -} - -pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> { - return os_symlink(src, dst); - - #[cfg(windows)] - fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { - as_utf16_p(dst.as_str().unwrap(), |dst| { - unsafe { libc::CreateSymbolicLinkW(dst, src, 0) } - }) - })) - } - - #[cfg(unix)] - fn os_symlink(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { - libc::symlink(src.with_ref(|p| p), dst.with_ref(|p| p)) - }) - } -} - -pub fn link(src: &CString, dst: &CString) -> IoResult<()> { - return os_link(src, dst); - - #[cfg(windows)] - fn os_link(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_winbool(as_utf16_p(src.as_str().unwrap(), |src| { - as_utf16_p(dst.as_str().unwrap(), |dst| { - unsafe { libc::CreateHardLinkW(dst, src, ptr::mut_null()) } - }) - })) - } - - #[cfg(unix)] - fn os_link(src: &CString, dst: &CString) -> IoResult<()> { - super::mkerr_libc(unsafe { - libc::link(src.with_ref(|p| p), dst.with_ref(|p| p)) - }) - } -} - -#[cfg(windows)] -fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { - let path = unsafe { CString::new(path.with_ref(|p| p), false) }; - let kind = match (stat.st_mode as c_int) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, - }; - - io::FileStat { - path: Path::new(path), - size: stat.st_size as u64, - kind: kind, - perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, - created: stat.st_ctime as u64, - modified: stat.st_mtime as u64, - accessed: stat.st_atime as u64, - unstable: io::UnstableFileStat { - device: stat.st_dev as u64, - inode: stat.st_ino as u64, - rdev: stat.st_rdev as u64, - nlink: stat.st_nlink as u64, - uid: stat.st_uid as u64, - gid: stat.st_gid as u64, - blksize: 0, - blocks: 0, - flags: 0, - gen: 0, - } - } -} - -#[cfg(unix)] -fn mkstat(stat: &libc::stat, path: &CString) -> io::FileStat { - let path = unsafe { CString::new(path.with_ref(|p| p), false) }; - - // FileStat times are in milliseconds - fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 } - - let kind = match (stat.st_mode as c_int) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, - }; - - #[cfg(not(target_os = "linux"), not(target_os = "android"))] - fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 } - #[cfg(target_os = "linux")] #[cfg(target_os = "android")] - fn flags(_stat: &libc::stat) -> u64 { 0 } - - #[cfg(not(target_os = "linux"), not(target_os = "android"))] - fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 } - #[cfg(target_os = "linux")] #[cfg(target_os = "android")] - fn gen(_stat: &libc::stat) -> u64 { 0 } - - io::FileStat { - path: Path::new(path), - size: stat.st_size as u64, - kind: kind, - perm: (stat.st_mode) as io::FilePermission & io::AllPermissions, - created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), - modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64), - accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64), - unstable: io::UnstableFileStat { - device: stat.st_dev as u64, - inode: stat.st_ino as u64, - rdev: stat.st_rdev as u64, - nlink: stat.st_nlink as u64, - uid: stat.st_uid as u64, - gid: stat.st_gid as u64, - blksize: stat.st_blksize as u64, - blocks: stat.st_blocks as u64, - flags: flags(stat), - gen: gen(stat), - } - } -} - -pub fn stat(p: &CString) -> IoResult<io::FileStat> { - return os_stat(p); - - #[cfg(windows)] - fn os_stat(p: &CString) -> IoResult<io::FileStat> { - let mut stat: libc::stat = unsafe { intrinsics::uninit() }; - as_utf16_p(p.as_str().unwrap(), |up| { - match unsafe { libc::wstat(up, &mut stat) } { - 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), - } - }) - } - - #[cfg(unix)] - fn os_stat(p: &CString) -> IoResult<io::FileStat> { - let mut stat: libc::stat = unsafe { intrinsics::uninit() }; - match unsafe { libc::stat(p.with_ref(|p| p), &mut stat) } { - 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), - } - } -} - -pub fn lstat(p: &CString) -> IoResult<io::FileStat> { - return os_lstat(p); - - // XXX: windows implementation is missing - #[cfg(windows)] - fn os_lstat(_p: &CString) -> IoResult<io::FileStat> { - Err(super::unimpl()) - } - - #[cfg(unix)] - fn os_lstat(p: &CString) -> IoResult<io::FileStat> { - let mut stat: libc::stat = unsafe { intrinsics::uninit() }; - match unsafe { libc::lstat(p.with_ref(|p| p), &mut stat) } { - 0 => Ok(mkstat(&stat, p)), - _ => Err(super::last_error()), - } - } -} - -pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> { - return super::mkerr_libc(os_utime(p, atime, mtime)); - - #[cfg(windows)] - fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { - let buf = libc::utimbuf { - actime: (atime / 1000) as libc::time64_t, - modtime: (mtime / 1000) as libc::time64_t, - }; - unsafe { - as_utf16_p(p.as_str().unwrap(), |p| libc::wutime(p, &buf)) - } - } - - #[cfg(unix)] - fn os_utime(p: &CString, atime: u64, mtime: u64) -> c_int { - let buf = libc::utimbuf { - actime: (atime / 1000) as libc::time_t, - modtime: (mtime / 1000) as libc::time_t, - }; - unsafe { libc::utime(p.with_ref(|p| p), &buf) } - } -} - -#[cfg(test)] -mod tests { - use io::native::file::{CFile, FileDesc}; - use io; - use libc; - use os; - use result::Ok; - use rt::rtio::RtioFileStream; - - #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer - #[test] - fn test_file_desc() { - // Run this test with some pipes so we don't have to mess around with - // opening or closing files. - unsafe { - let os::Pipe { input, out } = os::pipe(); - let mut reader = FileDesc::new(input, true); - let mut writer = FileDesc::new(out, true); - - writer.inner_write(bytes!("test")); - let mut buf = [0u8, ..4]; - match reader.inner_read(buf) { - Ok(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => fail!("invalid read: {:?}", r) - } - - assert!(writer.inner_read(buf).is_err()); - assert!(reader.inner_write(buf).is_err()); - } - } - - #[ignore(cfg(windows))] // apparently windows doesn't like tmpfile - #[test] - fn test_cfile() { - unsafe { - let f = libc::tmpfile(); - assert!(!f.is_null()); - let mut file = CFile::new(f); - - file.write(bytes!("test")); - let mut buf = [0u8, ..4]; - file.seek(0, io::SeekSet); - match file.read(buf) { - Ok(4) => { - assert_eq!(buf[0], 't' as u8); - assert_eq!(buf[1], 'e' as u8); - assert_eq!(buf[2], 's' as u8); - assert_eq!(buf[3], 't' as u8); - } - r => fail!("invalid read: {:?}", r) - } - } - } -} diff --git a/src/libstd/io/native/mod.rs b/src/libstd/io/native/mod.rs deleted file mode 100644 index d9dccc84f1c..00000000000 --- a/src/libstd/io/native/mod.rs +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Native thread-blocking I/O implementation -//! -//! This module contains the implementation of native thread-blocking -//! implementations of I/O on all platforms. This module is not intended to be -//! used directly, but rather the rust runtime will fall back to using it if -//! necessary. -//! -//! Rust code normally runs inside of green tasks with a local scheduler using -//! asynchronous I/O to cooperate among tasks. This model is not always -//! available, however, and that's where these native implementations come into -//! play. The only dependencies of these modules are the normal system libraries -//! that you would find on the respective platform. - -use c_str::CString; -use comm::SharedChan; -use libc::c_int; -use libc; -use option::{Option, None, Some}; -use os; -use path::Path; -use result::{Result, Ok, Err}; -use rt::rtio; -use rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket, RtioUnixListener, - RtioPipe, RtioFileStream, RtioProcess, RtioSignal, RtioTTY, - CloseBehavior, RtioTimer}; -use io; -use io::IoError; -use io::net::ip::SocketAddr; -use io::process::ProcessConfig; -use io::signal::Signum; -use ai = io::net::addrinfo; - -// Local re-exports -pub use self::file::FileDesc; -pub use self::process::Process; - -// Native I/O implementations -pub mod file; -pub mod process; - -type IoResult<T> = Result<T, IoError>; - -fn unimpl() -> IoError { - IoError { - kind: io::IoUnavailable, - desc: "unimplemented I/O interface", - detail: None, - } -} - -fn last_error() -> IoError { - #[cfg(windows)] - fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { - match errno { - libc::EOF => (io::EndOfFile, "end of file"), - _ => (io::OtherIoError, "unknown error"), - } - } - - #[cfg(not(windows))] - fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) { - // XXX: this should probably be a bit more descriptive... - match errno { - libc::EOF => (io::EndOfFile, "end of file"), - - // These two constants can have the same value on some systems, but - // different values on others, so we can't use a match clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => - (io::ResourceUnavailable, "resource temporarily unavailable"), - - _ => (io::OtherIoError, "unknown error"), - } - } - - let (kind, desc) = get_err(os::errno() as i32); - IoError { - kind: kind, - desc: desc, - detail: Some(os::last_os_error()) - } -} - -// unix has nonzero values as errors -fn mkerr_libc(ret: libc::c_int) -> IoResult<()> { - if ret != 0 { - Err(last_error()) - } else { - Ok(()) - } -} - -// windows has zero values as errors -#[cfg(windows)] -fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> { - if ret == 0 { - Err(last_error()) - } else { - Ok(()) - } -} - -/// Implementation of rt::rtio's IoFactory trait to generate handles to the -/// native I/O functionality. -pub struct IoFactory; - -impl rtio::IoFactory for IoFactory { - // networking - fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> { - Err(unimpl()) - } - fn tcp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpListener> { - Err(unimpl()) - } - fn udp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioUdpSocket> { - Err(unimpl()) - } - fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> { - Err(unimpl()) - } - fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> { - Err(unimpl()) - } - fn get_host_addresses(&mut self, _host: Option<&str>, _servname: Option<&str>, - _hint: Option<ai::Hint>) -> IoResult<~[ai::Info]> { - Err(unimpl()) - } - - // filesystem operations - fn fs_from_raw_fd(&mut self, fd: c_int, - close: CloseBehavior) -> ~RtioFileStream { - let close = match close { - rtio::CloseSynchronously | rtio::CloseAsynchronously => true, - rtio::DontClose => false - }; - ~file::FileDesc::new(fd, close) as ~RtioFileStream - } - fn fs_open(&mut self, path: &CString, fm: io::FileMode, fa: io::FileAccess) - -> IoResult<~RtioFileStream> { - file::open(path, fm, fa).map(|fd| ~fd as ~RtioFileStream) - } - fn fs_unlink(&mut self, path: &CString) -> IoResult<()> { - file::unlink(path) - } - fn fs_stat(&mut self, path: &CString) -> IoResult<io::FileStat> { - file::stat(path) - } - fn fs_mkdir(&mut self, path: &CString, - mode: io::FilePermission) -> IoResult<()> { - file::mkdir(path, mode) - } - fn fs_chmod(&mut self, path: &CString, - mode: io::FilePermission) -> IoResult<()> { - file::chmod(path, mode) - } - fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> { - file::rmdir(path) - } - fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> { - file::rename(path, to) - } - fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<~[Path]> { - file::readdir(path) - } - fn fs_lstat(&mut self, path: &CString) -> IoResult<io::FileStat> { - file::lstat(path) - } - fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> { - file::chown(path, uid, gid) - } - fn fs_readlink(&mut self, path: &CString) -> IoResult<Path> { - file::readlink(path) - } - fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> { - file::symlink(src, dst) - } - fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> { - file::link(src, dst) - } - fn fs_utime(&mut self, src: &CString, atime: u64, - mtime: u64) -> IoResult<()> { - file::utime(src, atime, mtime) - } - - // misc - fn timer_init(&mut self) -> IoResult<~RtioTimer> { - Err(unimpl()) - } - fn spawn(&mut self, config: ProcessConfig) - -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> { - process::Process::spawn(config).map(|(p, io)| { - (~p as ~RtioProcess, - io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect()) - }) - } - fn pipe_open(&mut self, fd: c_int) -> IoResult<~RtioPipe> { - Ok(~file::FileDesc::new(fd, true) as ~RtioPipe) - } - fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> { - if unsafe { libc::isatty(fd) } != 0 { - // Don't ever close the stdio file descriptors, nothing good really - // comes of that. - Ok(~file::FileDesc::new(fd, fd > libc::STDERR_FILENO) as ~RtioTTY) - } else { - Err(IoError { - kind: io::MismatchedFileTypeForOperation, - desc: "file descriptor is not a TTY", - detail: None, - }) - } - } - fn signal(&mut self, _signal: Signum, _channel: SharedChan<Signum>) - -> IoResult<~RtioSignal> { - Err(unimpl()) - } -} - -pub static mut NATIVE_IO_FACTORY: IoFactory = IoFactory; - diff --git a/src/libstd/io/native/process.rs b/src/libstd/io/native/process.rs deleted file mode 100644 index ef972dc4d0a..00000000000 --- a/src/libstd/io/native/process.rs +++ /dev/null @@ -1,654 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use io; -use libc::{pid_t, c_void, c_int}; -use libc; -use os; -use prelude::*; -use ptr; -use rt::rtio; -use super::file; -#[cfg(windows)] -use cast; - -use p = io::process; - -/** - * A value representing a child process. - * - * The lifetime of this value is linked to the lifetime of the actual - * process - the Process destructor calls self.finish() which waits - * for the process to terminate. - */ -pub struct Process { - /// The unique id of the process (this should never be negative). - priv pid: pid_t, - - /// 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. - priv handle: *(), - - /// None until finish() is called. - priv exit_code: Option<int>, -} - -impl Process { - /// Creates a new process using native process-spawning abilities provided - /// by the OS. Operations on this process will be blocking instead of using - /// the runtime for sleeping just this current task. - /// - /// # Arguments - /// - /// * prog - the program to run - /// * args - the arguments to pass to the program, not including the program - /// itself - /// * env - an optional environment to specify for the child process. If - /// this value is `None`, then the child will inherit the parent's - /// environment - /// * cwd - an optionally specified current working directory of the child, - /// defaulting to the parent's current working directory - /// * stdin, stdout, stderr - These optionally specified file descriptors - /// dictate where the stdin/out/err of the child process will go. If - /// these are `None`, then this module will bind the input/output to an - /// os pipe instead. This process takes ownership of these file - /// descriptors, closing them upon destruction of the process. - pub fn spawn(config: p::ProcessConfig) - -> Result<(Process, ~[Option<file::FileDesc>]), io::IoError> - { - // right now we only handle stdin/stdout/stderr. - if config.io.len() > 3 { - return Err(super::unimpl()); - } - - fn get_io(io: &[p::StdioContainer], - ret: &mut ~[Option<file::FileDesc>], - idx: uint) -> (Option<os::Pipe>, c_int) { - if idx >= io.len() { return (None, -1); } - ret.push(None); - match io[idx] { - p::Ignored => (None, -1), - p::InheritFd(fd) => (None, fd), - p::CreatePipe(readable, _writable) => { - let pipe = os::pipe(); - let (theirs, ours) = if readable { - (pipe.input, pipe.out) - } else { - (pipe.out, pipe.input) - }; - ret[idx] = Some(file::FileDesc::new(ours, true)); - (Some(pipe), theirs) - } - } - } - - let mut ret_io = ~[]; - let (in_pipe, in_fd) = get_io(config.io, &mut ret_io, 0); - let (out_pipe, out_fd) = get_io(config.io, &mut ret_io, 1); - let (err_pipe, err_fd) = get_io(config.io, &mut ret_io, 2); - - let env = config.env.map(|a| a.to_owned()); - let cwd = config.cwd.map(|a| Path::new(a)); - let res = spawn_process_os(config.program, config.args, env, - cwd.as_ref(), in_fd, out_fd, err_fd); - - unsafe { - for pipe in in_pipe.iter() { libc::close(pipe.input); } - for pipe in out_pipe.iter() { libc::close(pipe.out); } - for pipe in err_pipe.iter() { libc::close(pipe.out); } - } - - Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io)) - } -} - -impl rtio::RtioProcess for Process { - fn id(&self) -> pid_t { self.pid } - - fn wait(&mut self) -> p::ProcessExit { - let code = match self.exit_code { - Some(code) => code, - None => { - let code = waitpid(self.pid); - self.exit_code = Some(code); - code - } - }; - return p::ExitStatus(code); // XXX: this is wrong - } - - fn kill(&mut self, signum: int) -> Result<(), io::IoError> { - // if the process has finished, and therefore had waitpid called, - // and we kill it, then on unix we might ending up killing a - // newer process that happens to have the same (re-used) id - match self.exit_code { - Some(..) => return Err(io::IoError { - kind: io::OtherIoError, - desc: "can't kill an exited process", - detail: None, - }), - None => {} - } - return unsafe { killpid(self.pid, signum) }; - - #[cfg(windows)] - unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { - match signal { - io::process::PleaseExitSignal | io::process::MustDieSignal => { - libc::funcs::extra::kernel32::TerminateProcess( - cast::transmute(pid), 1); - Ok(()) - } - _ => Err(io::IoError { - kind: io::OtherIoError, - desc: "unsupported signal on windows", - detail: None, - }) - } - } - - #[cfg(not(windows))] - unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> { - libc::funcs::posix88::signal::kill(pid, signal as c_int); - Ok(()) - } - } -} - -impl Drop for Process { - fn drop(&mut self) { - free_handle(self.handle); - } -} - -struct SpawnProcessResult { - pid: pid_t, - handle: *(), -} - -#[cfg(windows)] -fn spawn_process_os(prog: &str, args: &[~str], - env: Option<~[(~str, ~str)]>, - dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { - use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; - use libc::consts::os::extra::{ - TRUE, FALSE, - STARTF_USESTDHANDLES, - INVALID_HANDLE_VALUE, - DUPLICATE_SAME_ACCESS - }; - use libc::funcs::extra::kernel32::{ - GetCurrentProcess, - DuplicateHandle, - CloseHandle, - CreateProcessA - }; - use libc::funcs::extra::msvcrt::get_osfhandle; - - use mem; - - unsafe { - - let mut si = zeroed_startupinfo(); - si.cb = mem::size_of::<STARTUPINFO>() as DWORD; - si.dwFlags = STARTF_USESTDHANDLES; - - let cur_proc = GetCurrentProcess(); - - let orig_std_in = get_osfhandle(in_fd) as HANDLE; - if orig_std_in == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_in, cur_proc, &mut si.hStdInput, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let orig_std_out = get_osfhandle(out_fd) as HANDLE; - if orig_std_out == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_out, cur_proc, &mut si.hStdOutput, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let orig_std_err = get_osfhandle(err_fd) as HANDLE; - if orig_std_err == INVALID_HANDLE_VALUE as HANDLE { - fail!("failure in get_osfhandle: {}", os::last_os_error()); - } - if DuplicateHandle(cur_proc, orig_std_err, cur_proc, &mut si.hStdError, - 0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE { - fail!("failure in DuplicateHandle: {}", os::last_os_error()); - } - - let cmd = make_command_line(prog, args); - let mut pi = zeroed_process_information(); - let mut create_err = None; - - with_envp(env, |envp| { - with_dirp(dir, |dirp| { - cmd.with_c_str(|cmdp| { - let created = CreateProcessA(ptr::null(), cast::transmute(cmdp), - ptr::mut_null(), ptr::mut_null(), TRUE, - 0, envp, dirp, &mut si, &mut pi); - if created == FALSE { - create_err = Some(os::last_os_error()); - } - }) - }) - }); - - CloseHandle(si.hStdInput); - CloseHandle(si.hStdOutput); - CloseHandle(si.hStdError); - - for msg in create_err.iter() { - fail!("failure in CreateProcess: {}", *msg); - } - - // We close the thread handle because we don't care about keeping the - // thread id valid, and we aren't keeping the thread handle around to be - // able to close it later. We don't close the process handle however - // because we want the process id to stay valid at least until the - // calling code closes the process handle. - CloseHandle(pi.hThread); - - SpawnProcessResult { - pid: pi.dwProcessId as pid_t, - handle: pi.hProcess as *() - } - } -} - -#[cfg(windows)] -fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO { - libc::types::os::arch::extra::STARTUPINFO { - cb: 0, - lpReserved: ptr::mut_null(), - lpDesktop: ptr::mut_null(), - lpTitle: ptr::mut_null(), - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountCharts: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: ptr::mut_null(), - hStdInput: ptr::mut_null(), - hStdOutput: ptr::mut_null(), - hStdError: ptr::mut_null() - } -} - -#[cfg(windows)] -fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION { - libc::types::os::arch::extra::PROCESS_INFORMATION { - hProcess: ptr::mut_null(), - hThread: ptr::mut_null(), - dwProcessId: 0, - dwThreadId: 0 - } -} - -// FIXME: this is only pub so it can be tested (see issue #4536) -#[cfg(windows)] -pub fn make_command_line(prog: &str, args: &[~str]) -> ~str { - let mut cmd = ~""; - append_arg(&mut cmd, prog); - for arg in args.iter() { - cmd.push_char(' '); - append_arg(&mut cmd, *arg); - } - return cmd; - - fn append_arg(cmd: &mut ~str, arg: &str) { - let quote = arg.chars().any(|c| c == ' ' || c == '\t'); - if quote { - cmd.push_char('"'); - } - for i in range(0u, arg.len()) { - append_char_at(cmd, arg, i); - } - if quote { - cmd.push_char('"'); - } - } - - fn append_char_at(cmd: &mut ~str, arg: &str, i: uint) { - match arg[i] as char { - '"' => { - // Escape quotes. - cmd.push_str("\\\""); - } - '\\' => { - if backslash_run_ends_in_quote(arg, i) { - // Double all backslashes that are in runs before quotes. - cmd.push_str("\\\\"); - } else { - // Pass other backslashes through unescaped. - cmd.push_char('\\'); - } - } - c => { - cmd.push_char(c); - } - } - } - - fn backslash_run_ends_in_quote(s: &str, mut i: uint) -> bool { - while i < s.len() && s[i] as char == '\\' { - i += 1; - } - return i < s.len() && s[i] as char == '"'; - } -} - -#[cfg(unix)] -fn spawn_process_os(prog: &str, args: &[~str], - env: Option<~[(~str, ~str)]>, - dir: Option<&Path>, - in_fd: c_int, out_fd: c_int, err_fd: c_int) -> SpawnProcessResult { - use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; - use libc::funcs::bsd44::getdtablesize; - - mod rustrt { - extern { - pub fn rust_unset_sigprocmask(); - } - } - - #[cfg(windows)] - unsafe fn set_environ(_envp: *c_void) {} - #[cfg(target_os = "macos")] - unsafe fn set_environ(envp: *c_void) { - extern { fn _NSGetEnviron() -> *mut *c_void; } - - *_NSGetEnviron() = envp; - } - #[cfg(not(target_os = "macos"), not(windows))] - unsafe fn set_environ(envp: *c_void) { - extern { - static mut environ: *c_void; - } - environ = envp; - } - - unsafe { - - let pid = fork(); - if pid < 0 { - fail!("failure in fork: {}", os::last_os_error()); - } else if pid > 0 { - return SpawnProcessResult {pid: pid, handle: ptr::null()}; - } - - rustrt::rust_unset_sigprocmask(); - - if dup2(in_fd, 0) == -1 { - fail!("failure in dup2(in_fd, 0): {}", os::last_os_error()); - } - if dup2(out_fd, 1) == -1 { - fail!("failure in dup2(out_fd, 1): {}", os::last_os_error()); - } - if dup2(err_fd, 2) == -1 { - fail!("failure in dup3(err_fd, 2): {}", os::last_os_error()); - } - // close all other fds - for fd in range(3, getdtablesize()).invert() { - close(fd as c_int); - } - - with_dirp(dir, |dirp| { - if !dirp.is_null() && chdir(dirp) == -1 { - fail!("failure in chdir: {}", os::last_os_error()); - } - }); - - with_envp(env, |envp| { - if !envp.is_null() { - set_environ(envp); - } - with_argv(prog, args, |argv| { - execvp(*argv, argv); - // execvp only returns if an error occurred - fail!("failure in execvp: {}", os::last_os_error()); - }) - }) - } -} - -#[cfg(unix)] -fn with_argv<T>(prog: &str, args: &[~str], cb: |**libc::c_char| -> T) -> T { - use vec; - - // We can't directly convert `str`s into `*char`s, as someone needs to hold - // a reference to the intermediary byte buffers. So first build an array to - // hold all the ~[u8] byte strings. - let mut tmps = vec::with_capacity(args.len() + 1); - - tmps.push(prog.to_c_str()); - - for arg in args.iter() { - tmps.push(arg.to_c_str()); - } - - // Next, convert each of the byte strings into a pointer. This is - // technically unsafe as the caller could leak these pointers out of our - // scope. - let mut ptrs = tmps.map(|tmp| tmp.with_ref(|buf| buf)); - - // Finally, make sure we add a null pointer. - ptrs.push(ptr::null()); - - cb(ptrs.as_ptr()) -} - -#[cfg(unix)] -fn with_envp<T>(env: Option<~[(~str, ~str)]>, cb: |*c_void| -> T) -> T { - use vec; - - // On posixy systems we can pass a char** for envp, which is a - // null-terminated array of "k=v\n" strings. Like `with_argv`, we have to - // have a temporary buffer to hold the intermediary `~[u8]` byte strings. - match env { - Some(env) => { - let mut tmps = vec::with_capacity(env.len()); - - for pair in env.iter() { - let kv = format!("{}={}", pair.first(), pair.second()); - tmps.push(kv.to_c_str()); - } - - // Once again, this is unsafe. - let mut ptrs = tmps.map(|tmp| tmp.with_ref(|buf| buf)); - ptrs.push(ptr::null()); - - cb(ptrs.as_ptr() as *c_void) - } - _ => cb(ptr::null()) - } -} - -#[cfg(windows)] -fn with_envp<T>(env: Option<~[(~str, ~str)]>, 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. - match env { - Some(env) => { - let mut blk = ~[]; - - for pair in env.iter() { - let kv = format!("{}={}", pair.first(), pair.second()); - blk.push_all(kv.as_bytes()); - blk.push(0); - } - - blk.push(0); - - cb(blk.as_mut_ptr() as *mut c_void) - } - _ => cb(ptr::mut_null()) - } -} - -fn with_dirp<T>(d: Option<&Path>, cb: |*libc::c_char| -> T) -> T { - match d { - Some(dir) => dir.with_c_str(|buf| cb(buf)), - None => cb(ptr::null()) - } -} - -#[cfg(windows)] -fn free_handle(handle: *()) { - unsafe { - libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle)); - } -} - -#[cfg(unix)] -fn free_handle(_handle: *()) { - // unix has no process handle object, just a pid -} - -/** - * Waits for a process to exit and returns the exit code, failing - * if there is no process with the specified id. - * - * Note that this is private to avoid race conditions on unix where if - * a user calls waitpid(some_process.get_id()) then some_process.finish() - * and some_process.destroy() and some_process.finalize() will then either - * operate on a none-existent process or, even worse, on a newer process - * with the same id. - */ -fn waitpid(pid: pid_t) -> int { - return waitpid_os(pid); - - #[cfg(windows)] - fn waitpid_os(pid: pid_t) -> int { - use libc::types::os::arch::extra::DWORD; - use libc::consts::os::extra::{ - SYNCHRONIZE, - PROCESS_QUERY_INFORMATION, - FALSE, - STILL_ACTIVE, - INFINITE, - WAIT_FAILED - }; - use libc::funcs::extra::kernel32::{ - OpenProcess, - GetExitCodeProcess, - CloseHandle, - WaitForSingleObject - }; - - unsafe { - - let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, - FALSE, - pid as DWORD); - if process.is_null() { - fail!("failure in OpenProcess: {}", os::last_os_error()); - } - - loop { - let mut status = 0; - if GetExitCodeProcess(process, &mut status) == FALSE { - CloseHandle(process); - fail!("failure in GetExitCodeProcess: {}", os::last_os_error()); - } - if status != STILL_ACTIVE { - CloseHandle(process); - return status as int; - } - if WaitForSingleObject(process, INFINITE) == WAIT_FAILED { - CloseHandle(process); - fail!("failure in WaitForSingleObject: {}", os::last_os_error()); - } - } - } - } - - #[cfg(unix)] - fn waitpid_os(pid: pid_t) -> int { - use libc::funcs::posix01::wait::*; - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - fn WIFEXITED(status: i32) -> bool { - (status & 0xffi32) == 0i32 - } - - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - fn WIFEXITED(status: i32) -> bool { - (status & 0x7fi32) == 0i32 - } - - #[cfg(target_os = "linux")] - #[cfg(target_os = "android")] - fn WEXITSTATUS(status: i32) -> i32 { - (status >> 8i32) & 0xffi32 - } - - #[cfg(target_os = "macos")] - #[cfg(target_os = "freebsd")] - fn WEXITSTATUS(status: i32) -> i32 { - status >> 8i32 - } - - let mut status = 0 as c_int; - if unsafe { waitpid(pid, &mut status, 0) } == -1 { - fail!("failure in waitpid: {}", os::last_os_error()); - } - - return if WIFEXITED(status) { - WEXITSTATUS(status) as int - } else { - 1 - }; - } -} - -#[cfg(test)] -mod tests { - - #[test] #[cfg(windows)] - fn test_make_command_line() { - use super::make_command_line; - assert_eq!( - make_command_line("prog", [~"aaa", ~"bbb", ~"ccc"]), - ~"prog aaa bbb ccc" - ); - assert_eq!( - make_command_line("C:\\Program Files\\blah\\blah.exe", [~"aaa"]), - ~"\"C:\\Program Files\\blah\\blah.exe\" aaa" - ); - assert_eq!( - make_command_line("C:\\Program Files\\test", [~"aa\"bb"]), - ~"\"C:\\Program Files\\test\" aa\\\"bb" - ); - assert_eq!( - make_command_line("echo", [~"a b c"]), - ~"echo \"a b c\"" - ); - } - - // Currently most of the tests of this functionality live inside std::run, - // but they may move here eventually as a non-blocking backend is added to - // std::run -} |
