summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml36
-rw-r--r--CHANGELOG.md26
-rw-r--r--Cargo.toml4
-rw-r--r--README.md32
-rw-r--r--src/macros.rs178
-rw-r--r--src/pty.rs86
-rw-r--r--src/sys/mod.rs1
-rw-r--r--src/sys/socket/mod.rs18
-rw-r--r--src/sys/termios.rs1416
-rw-r--r--src/unistd.rs654
-rw-r--r--test/sys/test_socket.rs39
-rw-r--r--test/sys/test_termios.rs139
-rw-r--r--test/test_pty.rs19
-rw-r--r--test/test_ptymaster_drop.rs21
-rw-r--r--test/test_unistd.rs32
15 files changed, 2006 insertions, 695 deletions
diff --git a/.travis.yml b/.travis.yml
index f0e94c67..92234237 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,6 @@
dist: trusty
language: rust
services: docker
-sudo: required
env:
global:
@@ -96,35 +95,8 @@ matrix:
# Testing beta on main targets
- env: TARGET=x86_64-unknown-linux-gnu
rust: beta
- - env: TARGET=x86_64-apple-darwin
- os: osx
- rust: beta
-
- # Testing nightly on main targets (allowed to fail)
- - env: TARGET=x86_64-unknown-linux-gnu
- rust: nightly
- - env: TARGET=x86_64-apple-darwin
- os: osx
- rust: nightly
allow_failures:
- # iOS is still being worked on, so for now don't block on compilation failures
- - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1
- rust: 1.13.0
- os: osx
- - env: TARGET=armv7-apple-ios DISABLE_TESTS=1
- rust: 1.13.0
- os: osx
- - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1
- rust: 1.13.0
- os: osx
- - env: TARGET=i386-apple-ios DISABLE_TESTS=1
- rust: 1.13.0
- os: osx
- - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1
- rust: 1.13.0
- os: osx
-
# Planning to add these targets, but they can fail for now
- env: TARGET=mips64-unknown-linux-gnuabi64
rust: 1.13.0
@@ -135,14 +107,6 @@ matrix:
- env: TARGET=s390x-unknown-linux-gnu
rust: 1.13.0
- # Failures for nightlies may be because of compiler bugs, so don't fail the
- # build if these fail.
- - env: TARGET=x86_64-unknown-linux-gnu
- rust: nightly
- - env: TARGET=x86_64-apple-darwin
- os: osx
- rust: nightly
-
before_install: set -e
install:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15f42a45..8179de26 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
+- Added `sysconf`, `pathconf`, and `fpathconf`
+ ([#630](https://github.com/nix-rust/nix/pull/630)
- Added `sys::signal::SigAction::{ flags, mask, handler}`
([#611](https://github.com/nix-rust/nix/pull/609)
- Added `nix::sys::pthread::pthread_self`
@@ -21,30 +23,32 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `nix::ptrace::{ptrace_get_data, ptrace_getsiginfo, ptrace_setsiginfo
and nix::Error::UnsupportedOperation}`
([#614](https://github.com/nix-rust/nix/pull/614))
+- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527))
### Changed
-- Changed ioctl! write to take argument by value instead as pointer.
- If you need a pointer as argument, use ioctl! write buf.
+- Changed `ioctl!(write ...)` to take argument by value instead as pointer.
+ If you need a pointer as argument, use `ioctl!(write buf ...)`.
([#626](https://github.com/nix-rust/nix/pull/626))
- Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe.
([#559](https://github.com/nix-rust/nix/pull/559))
-- Minimum supported Rust version is now 1.13
+- Minimum supported Rust version is now 1.13.
- Removed `revents` argument from `PollFd::new()` as it's an output argument and
will be overwritten regardless of value.
([#542](https://github.com/nix-rust/nix/pull/542))
- Changed type signature of `sys::select::FdSet::contains` to make `self`
immutable ([#564](https://github.com/nix-rust/nix/pull/564))
-- Changed type of `sched::sched_setaffinity`'s `pid` argument to `pid_t`
-- Introduced wrapper types for gid_t, pid_t, and uid_t as Gid, Pid, and Uid
+- Introduced wrapper types for `gid_t`, `pid_t`, and `uid_t` as `Gid`, `Pid`, and `Uid`
respectively. Various functions have been changed to use these new types as
arguments. ([#629](https://github.com/nix-rust/nix/pull/629))
-- Promoted all Android targets to Tier 2 support
+- Fixed compilation on all Android and iOS targets ([#527](https://github.com/nix-rust/nix/pull/527))
+ and promoted them to Tier 2 support.
- `nix::sys::statfs::{statfs,fstatfs}` uses statfs definition from `libc::statfs` instead of own linux specific type `nix::sys::Statfs`.
Also file system type constants like `nix::sys::statfs::ADFS_SUPER_MAGIC` were removed in favor of the libc equivalent.
([#561](https://github.com/nix-rust/nix/pull/561))
+- Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527))
### Removed
-- Removed io::Error from nix::Error and conversion from nix::Error to Errno
+- Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno`
([#614](https://github.com/nix-rust/nix/pull/614))
### Fixed
@@ -60,6 +64,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `nix::ptrace` on all Linux-kernel-based platforms
[#624](https://github.com/nix-rust/nix/pull/624). Previously it was
only available on x86, x86-64, and ARM, and also not on Android.
+- Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter.
+ ([#623](https://github.com/nix-rust/nix/pull/623))
+- Multiple constants related to the termios API have now been properly defined for
+ all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527))
## [0.8.1] 2017-04-16
@@ -73,7 +81,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `::nix::sys::termios::BaudRate` enum to provide portable baudrate
values. ([#518](https://github.com/nix-rust/nix/pull/518))
- Added a new `WaitStatus::PtraceEvent` to support ptrace events on Linux
- and Android ([([#438](https://github.com/nix-rust/nix/pull/438))
+ and Android ([#438](https://github.com/nix-rust/nix/pull/438))
- Added support for POSIX AIO
([#483](https://github.com/nix-rust/nix/pull/483))
([#506](https://github.com/nix-rust/nix/pull/506))
@@ -109,7 +117,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Added `ppoll` in `::nix::poll`
([#520](https://github.com/nix-rust/nix/pull/520))
- Added support for getting and setting pipe size with fcntl(2) on Linux
- ([#540](https://github.com/nix-rust/nix/pull/540)
+ ([#540](https://github.com/nix-rust/nix/pull/540))
### Changed
- `::nix::sys::termios::{cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed}`
diff --git a/Cargo.toml b/Cargo.toml
index 64a212f6..e768f4a1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -48,3 +48,7 @@ test = true
name = "test-mount"
path = "test/test_mount.rs"
harness = false
+
+[[test]]
+name = "test-ptymaster-drop"
+path = "test/test_ptymaster_drop.rs"
diff --git a/README.md b/README.md
index 8cda561a..4ccc4785 100644
--- a/README.md
+++ b/README.md
@@ -50,41 +50,41 @@ The following targets are all supported by nix on Rust 1.13.0 or newer (unless
otherwise noted):
Tier 1:
- * i686-unknown-linux-gnu
- * x86_64-unknown-linux-gnu
- * i686-apple-darwin
- * x86_64-apple-darwin
* aarch64-unknown-linux-gnu
- * armv7-unknown-linux-gnueabihf
* arm-unknown-linux-gnueabi
- * x86_64-unknown-freebsd
+ * armv7-unknown-linux-gnueabihf
+ * i686-apple-darwin
+ * i686-unknown-linux-gnu
+ * i686-unknown-linux-musl
+ * mips-unknown-linux-gnu
+ * mipsel-unknown-linux-gnu
* powerpc-unknown-linux-gnu
* powerpc64-unknown-linux-gnu
* powerpc64le-unknown-linux-gnu
- * mips-unknown-linux-gnu
- * mipsel-unknown-linux-gnu
- * i686-unknown-linux-musl
+ * x86_64-apple-darwin
+ * x86_64-unknown-freebsd
+ * x86_64-unknown-linux-gnu
* x86_64-unknown-linux-musl
Tier 2:
- * i686-unknown-freebsd
- * x86_64-unknown-netbsd
+ * aarch64-apple-ios
* aarch64-linux-android
* arm-linux-androideabi
+ * armv7-apple-ios
* armv7-linux-androideabi
+ * armv7s-apple-ios
+ * i386-apple-ios
* i686-linux-android (requires Rust >= 1.18)
+ * i686-unknown-freebsd
+ * x86_64-apple-ios
* x86_64-linux-android (requires Rust >= 1.18)
+ * x86_64-unknown-netbsd
Tier 3:
- * aarch64-apple-ios
* arm-unknown-linux-musleabi (requires Rust >= 1.14)
- * armv7-apple-ios
- * armv7s-apple-ios
- * i386-apple-ios
* mips64-unknown-linux-gnuabi64
* mips64el-unknown-linux-gnuabi64
* s390x-unknown-linux-gnu
- * x86_64-apple-ios
## Usage
diff --git a/src/macros.rs b/src/macros.rs
index b8707d0b..d8284f71 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -239,6 +239,184 @@ macro_rules! libc_bitflags {
};
}
+/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
+/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```
+/// libc_enum!{
+/// pub enum ProtFlags {
+/// PROT_NONE,
+/// PROT_READ,
+/// PROT_WRITE,
+/// PROT_EXEC,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSDOWN,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSUP,
+/// }
+/// }
+/// ```
+macro_rules! libc_enum {
+ // (non-pub) Exit rule.
+ (@make_enum
+ {
+ name: $BitFlags:ident,
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+ enum $BitFlags {
+ $($entries)*
+ }
+ };
+
+ // (pub) Exit rule.
+ (@make_enum
+ {
+ pub,
+ name: $BitFlags:ident,
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+ pub enum $BitFlags {
+ $($entries)*
+ }
+ };
+
+ // (non-pub) Done accumulating.
+ (@accumulate_entries
+ {
+ name: $BitFlags:ident,
+ attrs: $attrs:tt,
+ },
+ $entries:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ {
+ name: $BitFlags,
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // (pub) Done accumulating.
+ (@accumulate_entries
+ {
+ pub,
+ name: $BitFlags:ident,
+ attrs: $attrs:tt,
+ },
+ $entries:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ {
+ pub,
+ name: $BitFlags,
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // Munch an attr.
+ (@accumulate_entries
+ $prefix:tt,
+ [$($entries:tt)*];
+ #[$attr:meta] $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ $prefix,
+ [
+ $($entries)*
+ #[$attr]
+ ];
+ $($tail)*
+ }
+ };
+
+ // Munch last ident if not followed by a comma.
+ (@accumulate_entries
+ $prefix:tt,
+ [$($entries:tt)*];
+ $entry:ident
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ];
+ }
+ };
+
+ // Munch an ident; covers terminating comma case.
+ (@accumulate_entries
+ $prefix:tt,
+ [$($entries:tt)*];
+ $entry:ident, $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ];
+ $($tail)*
+ }
+ };
+
+ // (non-pub) Entry rule.
+ (
+ $(#[$attr:meta])*
+ enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ {
+ name: $BitFlags,
+ attrs: [$(#[$attr])*],
+ },
+ [];
+ $($vals)*
+ }
+ };
+
+ // (pub) Entry rule.
+ (
+ $(#[$attr:meta])*
+ pub enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ {
+ pub,
+ name: $BitFlags,
+ attrs: [$(#[$attr])*],
+ },
+ [];
+ $($vals)*
+ }
+ };
+}
+
/// A Rust version of the familiar C `offset_of` macro. It returns the byte
/// offset of `field` within struct `ty`
macro_rules! offset_of {
diff --git a/src/pty.rs b/src/pty.rs
index c450e058..26c8b500 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -14,7 +14,8 @@ use {Errno, Result, Error, fcntl};
/// Representation of a master/slave pty pair
///
-/// This is returned by `openpty`
+/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
+/// must manually close the file descriptors.
pub struct OpenptyResult {
pub master: RawFd,
pub slave: RawFd,
@@ -45,10 +46,17 @@ impl IntoRawFd for PtyMaster {
impl Drop for PtyMaster {
fn drop(&mut self) {
- // Errors when closing are ignored because we don't actually know if the file descriptor
- // was closed. If we retried, it's possible that descriptor was reallocated in the mean
- // time and the wrong file descriptor could be closed.
- let _ = ::unistd::close(self.0);
+ // On drop, we ignore errors like EINTR and EIO because there's no clear
+ // way to handle them, we can't return anything, and (on FreeBSD at
+ // least) the file descriptor is deallocated in these cases. However,
+ // we must panic on EBADF, because it is always an error to close an
+ // invalid file descriptor. That frequently indicates a double-close
+ // condition, which can cause confusing errors for future I/O
+ // operations.
+ let e = ::unistd::close(self.0);
+ if e == Err(Error::Sys(Errno::EBADF)) {
+ panic!("Closing an invalid file descriptor!");
+ };
}
}
@@ -188,23 +196,57 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
use std::ptr;
- let mut slave: libc::c_int = -1;
- let mut master: libc::c_int = -1;
- let c_termios = match termios.into() {
- Some(termios) => termios as *const Termios,
- None => ptr::null() as *const Termios,
- };
- let c_winsize = match winsize.into() {
- Some(ws) => ws as *const Winsize,
- None => ptr::null() as *const Winsize,
- };
- let ret = unsafe {
- libc::openpty(
- &mut master as *mut libc::c_int,
- &mut slave as *mut libc::c_int,
- ptr::null_mut(),
- c_termios as *mut libc::termios,
- c_winsize as *mut Winsize)
+ let mut slave: libc::c_int = unsafe { mem::uninitialized() };
+ let mut master: libc::c_int = unsafe { mem::uninitialized() };
+ let ret = {
+ match (termios.into(), winsize.into()) {
+ (Some(termios), Some(winsize)) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (None, Some(winsize)) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (Some(termios), None) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ ptr::null_mut(),
+ )
+ }
+ }
+ (None, None) => {
+ unsafe {
+ libc::openpty(
+ &mut master,
+ &mut slave,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ }
+ }
+ }
};
Errno::result(ret)?;
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 783b8364..627f7697 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -38,7 +38,6 @@ pub mod syscall;
#[cfg(any(target_os = "linux"))]
pub mod reboot;
-#[cfg(not(target_os = "ios"))]
pub mod termios;
#[cfg(any(target_os = "linux", target_os = "android"))]
diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs
index a126f8b4..c11b5367 100644
--- a/src/sys/socket/mod.rs
+++ b/src/sys/socket/mod.rs
@@ -258,14 +258,12 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
len += cmsg.len();
capacity += cmsg.space();
}
- // Alignment hackery. Note that capacity is guaranteed to be a
- // multiple of size_t. Note also that the resulting vector claims
- // to have length == capacity, so it's presently uninitialized.
+ // Note that the resulting vector claims to have length == capacity,
+ // so it's presently uninitialized.
let mut cmsg_buffer = unsafe {
let mut vec = Vec::<u8>::with_capacity(len);
- let ptr = vec.as_mut_ptr();
- mem::forget(vec);
- Vec::<u8>::from_raw_parts(ptr as *mut _, len, len)
+ vec.set_len(len);
+ vec
};
{
let mut ptr = &mut cmsg_buffer[..];
@@ -279,12 +277,18 @@ pub fn sendmsg<'a>(fd: RawFd, iov: &[IoVec<&'a [u8]>], cmsgs: &[ControlMessage<'
None => (0 as *const _, 0),
};
+ let cmsg_ptr = if capacity > 0 {
+ cmsg_buffer.as_ptr() as *const c_void
+ } else {
+ ptr::null()
+ };
+
let mhdr = msghdr {
msg_name: name as *const c_void,
msg_namelen: namelen,
msg_iov: iov.as_ptr(),
msg_iovlen: iov.len() as size_t,
- msg_control: cmsg_buffer.as_ptr() as *const c_void,
+ msg_control: cmsg_ptr,
msg_controllen: capacity as size_t,
msg_flags: 0,
};
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 0f8bd6c9..8651a84d 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -1,647 +1,895 @@
+//! An interface for controlling asynchronous communication ports
+//!
+//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
+//! underlying types are all implemented in libc for most platforms and either wrapped in safer
+//! types here or exported directly.
+//!
+//! If you are unfamiliar with the `termios` API, you should first read the
+//! [API documentation](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! then come back to understand how `nix` safely wraps it.
+//!
+//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
+//! As this interface is not used with high-bandwidth information, this should be fine in most
+//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
+//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
+//! This means that when crossing the FFI interface to the underlying C library, data is first
+//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
+//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
+//! relatively small across all platforms (on the order of 32-64 bytes).
+//!
+//! The following examples highlight some of the API use cases such that users coming from using C
+//! or reading the standard documentation will understand how to use the safe API exposed here.
+//!
+//! Example disabling processing of the end-of-file control character:
+//!
+//! ```
+//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
+//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
+//! ```
+//!
+//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
+//! an interface for working with bitfields that is similar to working with the raw unsigned
+//! integer types but offers type safety because of the internal checking that values will always
+//! be a valid combination of the defined flags.
+//!
+//! An example showing some of the basic operations for interacting with the control flags:
+//!
+//! ```
+//! # use self::nix::sys::termios::{CS5, CSIZE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_flags & CSIZE == CS5;
+//! termios.control_flags |= CS5;
+//! ```
+
use {Errno, Result};
-use libc::c_int;
+use libc::{self, c_int, tcflag_t};
+use std::cell::{Ref, RefCell};
+use std::convert::From;
use std::mem;
use std::os::unix::io::RawFd;
-pub use self::ffi::consts::*;
-pub use self::ffi::consts::SetArg::*;
-pub use self::ffi::consts::FlushArg::*;
-pub use self::ffi::consts::FlowArg::*;
-
-mod ffi {
- pub use self::consts::*;
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
- mod non_android {
- use super::consts::*;
- use libc::c_int;
-
- // `Termios` contains bitflags which are not considered
- // `foreign-function-safe` by the compiler.
- #[allow(improper_ctypes)]
- extern {
- pub fn cfgetispeed(termios: *const Termios) -> speed_t;
- pub fn cfgetospeed(termios: *const Termios) -> speed_t;
- pub fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int;
- pub fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int;
- pub fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
- pub fn tcsetattr(fd: c_int,
- optional_actions: c_int,
- termios: *const Termios) -> c_int;
- pub fn tcdrain(fd: c_int) -> c_int;
- pub fn tcflow(fd: c_int, action: c_int) -> c_int;
- pub fn tcflush(fd: c_int, action: c_int) -> c_int;
- pub fn tcsendbreak(fd: c_int, duration: c_int) -> c_int;
- }
- }
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
- pub use self::non_android::*;
-
- // On Android before 5.0, Bionic directly inline these to ioctl() calls.
- #[cfg(all(target_os = "android", not(target_arch = "mips")))]
- mod android {
- use libc;
- use libc::c_int;
- use super::consts::*;
-
- const TCGETS: c_int = 0x5401;
- const TCSBRK: c_int = 0x5409;
- const TCXONC: c_int = 0x540a;
- const TCFLSH: c_int = 0x540b;
- const TCSBRKP: c_int = 0x5425;
+use ::unistd::Pid;
+
+/// Stores settings for the termios API
+///
+/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
+/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
+/// an open port using `tcgetattr()`.
+#[derive(Clone)]
+pub struct Termios {
+ inner: RefCell<libc::termios>,
+ /// Input mode flags (see `termios.c_iflag` documentation)
+ pub input_flags: InputFlags,
+ /// Output mode flags (see `termios.c_oflag` documentation)
+ pub output_flags: OutputFlags,
+ /// Control mode flags (see `termios.c_cflag` documentation)
+ pub control_flags: ControlFlags,
+ /// Local mode flags (see `termios.c_lflag` documentation)
+ pub local_flags: LocalFlags,
+ /// Control characters (see `termios.c_cc` documentation)
+ pub control_chars: [libc::cc_t; libc::NCCS],
+}
- pub unsafe fn cfgetispeed(termios: *const Termios) -> speed_t {
- ((*termios).c_cflag & CBAUD).bits() as speed_t
- }
- pub unsafe fn cfgetospeed(termios: *const Termios) -> speed_t {
- ((*termios).c_cflag & CBAUD).bits() as speed_t
- }
- pub unsafe fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int {
- (*termios).c_cflag.remove(CBAUD);
- (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
- 0
- }
- pub unsafe fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int {
- (*termios).c_cflag.remove(CBAUD);
- (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
- 0
- }
- pub unsafe fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int {
- libc::ioctl(fd, TCGETS, termios)
- }
- pub unsafe fn tcsetattr(fd: c_int,
- optional_actions: c_int,
- termios: *const Termios) -> c_int {
- libc::ioctl(fd, optional_actions, termios)
- }
- pub unsafe fn tcdrain(fd: c_int) -> c_int {
- libc::ioctl(fd, TCSBRK, 1)
- }
- pub unsafe fn tcflow(fd: c_int, action: c_int) -> c_int {
- libc::ioctl(fd, TCXONC, action)
- }
- pub unsafe fn tcflush(fd: c_int, action: c_int) -> c_int {
- libc::ioctl(fd, TCFLSH, action)
- }
- pub unsafe fn tcsendbreak(fd: c_int, duration: c_int) -> c_int {
- libc::ioctl(fd, TCSBRKP, duration)
+impl Termios {
+ /// Exposes an immutable reference to the underlying `libc::termios` data structure.
+ ///
+ /// This can be used for interfacing with other FFI functions like:
+ ///
+ /// ```rust
+ /// # extern crate libc;
+ /// # extern crate nix;
+ /// # fn main() {
+ /// # use nix::sys::termios::Termios;
+ /// # let mut termios = unsafe { Termios::default_uninit() };
+ /// let inner_termios = termios.get_libc_termios();
+ /// unsafe { libc::cfgetispeed(&*inner_termios) };
+ /// # }
+ /// ```
+ ///
+ /// There is no public API exposed for functions that modify the underlying `libc::termios`
+ /// data because it requires additional work to maintain type safety.
+ // FIXME: Switch this over to use pub(crate)
+ #[doc(hidden)]
+ pub fn get_libc_termios(&self) -> Ref<libc::termios> {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
}
+ self.inner.borrow()
}
- #[cfg(target_os = "android")]
- pub use self::android::*;
-
-
- #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
- pub mod consts {
-
- use libc;
-
- use libc::c_int;
-
- pub type tcflag_t = libc::tcflag_t;
- pub type cc_t = libc::cc_t;
- pub type speed_t = libc::speed_t;
-
- #[repr(C)]
- #[derive(Clone, Copy)]
- pub struct Termios {
- pub c_iflag: InputFlags,
- pub c_oflag: OutputFlags,
- pub c_cflag: ControlFlags,
- pub c_lflag: LocalFlags,
- pub c_cc: [cc_t; NCCS],
- pub c_ispeed: speed_t,
- pub c_ospeed: speed_t
- }
-
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum BaudRate {
- B0,
- B50,
- B75,
- B110,
- B134,
- B150,
- B200,
- B300,
- B600,
- B1200,
- B1800,
- B2400,
- B4800,
- B9600,
- B19200,
- B38400,
- B7200,
- B14400,
- B28800,
- B57600,
- B76800,
- B115200,
- B230400,
- #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
- B460800,
- #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
- B921600,
- }
-
- impl From<speed_t> for BaudRate {
- fn from(s: speed_t) -> BaudRate {
-
- use libc::{
- B0, B50, B75, B110, B134, B150,
- B200, B300, B600, B1200, B1800, B2400,
- B4800, B9600, B19200, B38400,
- B7200, B14400, B28800, B57600,
- B76800, B115200, B230400};
-
- #[cfg(target_os = "freebsd")]
- use libc::{B460800, B921600};
-
- match s {
- B0 => BaudRate::B0,
- B50 => BaudRate::B50,
- B75 => BaudRate::B75,
- B110 => BaudRate::B110,
- B134 => BaudRate::B134,
- B150 => BaudRate::B150,
- B200 => BaudRate::B200,
- B300 => BaudRate::B300,
- B600 => BaudRate::B600,
- B1200 => BaudRate::B1200,
- B1800 => BaudRate::B1800,
- B2400 => BaudRate::B2400,
- B4800 => BaudRate::B4800,
- B9600 => BaudRate::B9600,
- B19200 => BaudRate::B19200,
- B38400 => BaudRate::B38400,
- B7200 => BaudRate::B7200,
- B14400 => BaudRate::B14400,
- B28800 => BaudRate::B28800,
- B57600 => BaudRate::B57600,
- B76800 => BaudRate::B76800,
- B115200 => BaudRate::B115200,
- B230400 => BaudRate::B230400,
- #[cfg(target_os = "freebsd")]
- B460800 => BaudRate::B460800,
- #[cfg(target_os = "freebsd")]
- B921600 => BaudRate::B921600,
- b @ _ => unreachable!("Invalid baud constant: {}", b),
- }
- }
- }
-
- pub const VEOF: usize = 0;
- pub const VEOL: usize = 1;
- pub const VEOL2: usize = 2;
- pub const VERASE: usize = 3;
- pub const VWERASE: usize = 4;
- pub const VKILL: usize = 5;
- pub const VREPRINT: usize = 6;
- pub const VINTR: usize = 8;
- pub const VQUIT: usize = 9;
- pub const VSUSP: usize = 10;
- pub const VDSUSP: usize = 11;
- pub const VSTART: usize = 12;
- pub const VSTOP: usize = 13;
- pub const VLNEXT: usize = 14;
- pub const VDISCARD: usize = 15;
- pub const VMIN: usize = 16;
- pub const VTIME: usize = 17;
- pub const VSTATUS: usize = 18;
- pub const NCCS: usize = 20;
-
- bitflags! {
- pub struct InputFlags: tcflag_t {
- const IGNBRK = 0x00000001;
- const BRKINT = 0x00000002;
- const IGNPAR = 0x00000004;
- const PARMRK = 0x00000008;
- const INPCK = 0x00000010;
- const ISTRIP = 0x00000020;
- const INLCR = 0x00000040;
- const IGNCR = 0x00000080;
- const ICRNL = 0x00000100;
- const IXON = 0x00000200;
- const IXOFF = 0x00000400;
- const IXANY = 0x00000800;
- const IMAXBEL = 0x00002000;
- #[cfg(not(target_os = "dragonfly"))]
- const IUTF8 = 0x00004000;
- }
- }
-
- bitflags! {
- pub struct OutputFlags: tcflag_t {
- const OPOST = 0x00000001;
- const ONLCR = 0x00000002;
- const OXTABS = 0x00000004;
- const ONOEOT = 0x00000008;
- }
- }
-
- bitflags! {
- pub struct ControlFlags: tcflag_t {
- const CIGNORE = 0x00000001;
- const CSIZE = 0x00000300;
- const CS5 = 0x00000000;
- const CS6 = 0x00000100;
- const CS7 = 0x00000200;
- const CS8 = 0x00000300;
- const CSTOPB = 0x00000400;
- const CREAD = 0x00000800;
- const PARENB = 0x00001000;
- const PARODD = 0x00002000;
- const HUPCL = 0x00004000;
- const CLOCAL = 0x00008000;
- const CCTS_OFLOW = 0x00010000;
- const CRTSCTS = 0x00030000;
- const CRTS_IFLOW = 0x00020000;
- const CDTR_IFLOW = 0x00040000;
- const CDSR_OFLOW = 0x00080000;
- const CCAR_OFLOW = 0x00100000;
- const MDMBUF = 0x00100000;
- }
+ /// Exposes the inner `libc::termios` datastore within `Termios`.
+ ///
+ /// This is unsafe because if this is used to modify the inner libc::termios struct, it will not
+ /// automatically update the safe wrapper type around it. Therefore we disable docs to
+ /// effectively limit its use to nix internals. In this case it should also be paired with a
+ /// call to `update_wrapper()` so that the wrapper-type and internal representation stay
+ /// consistent.
+ unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
}
+ self.inner.as_ptr()
+ }
- bitflags! {
- pub struct LocalFlags: tcflag_t {
- const ECHOKE = 0x00000001;
- const ECHOE = 0x00000002;
- const ECHOK = 0x00000004;
- const ECHO = 0x00000008;
- const ECHONL = 0x00000010;
- const ECHOPRT = 0x00000020;
- const ECHOCTL = 0x00000040;
- const ISIG = 0x00000080;
- const ICANON = 0x00000100;
- const ALTWERASE = 0x00000200;
- const IEXTEN = 0x00000400;
- const EXTPROC = 0x00000800;
- const TOSTOP = 0x00400000;
- const FLUSHO = 0x00800000;
- const NOKERNINFO = 0x02000000;
- const PENDIN = 0x20000000;
- const NOFLSH = 0x80000000;
- }
+ /// Allows for easily creating new Termios structs that will be overwritten with real data.
+ ///
+ /// This should only be used when the inner libc::termios struct will be overwritten before it's
+ /// read.
+ // FIXME: Switch this over to use pub(crate)
+ #[doc(hidden)]
+ pub unsafe fn default_uninit() -> Self {
+ Termios {
+ inner: RefCell::new(mem::uninitialized()),
+ input_flags: InputFlags::empty(),
+ output_flags: OutputFlags::empty(),
+ control_flags: ControlFlags::empty(),
+ local_flags: LocalFlags::empty(),
+ control_chars: [0 as libc::cc_t; NCCS],
}
+ }
- pub const NL0: c_int = 0x00000000;
- pub const NL1: c_int = 0x00000100;
- pub const NL2: c_int = 0x00000200;
- pub const NL3: c_int = 0x00000300;
- pub const TAB0: c_int = 0x00000000;
- pub const TAB1: c_int = 0x00000400;
- pub const TAB2: c_int = 0x00000800;
- pub const TAB3: c_int = 0x00000004;
- pub const CR0: c_int = 0x00000000;
- pub const CR1: c_int = 0x00001000;
- pub const CR2: c_int = 0x00002000;
- pub const CR3: c_int = 0x00003000;
- pub const FF0: c_int = 0x00000000;
- pub const FF1: c_int = 0x00004000;
- pub const BS0: c_int = 0x00000000;
- pub const BS1: c_int = 0x00008000;
- pub const VT0: c_int = 0x00000000;
- pub const VT1: c_int = 0x00010000;
-
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum SetArg {
- TCSANOW = 0,
- TCSADRAIN = 1,
- TCSAFLUSH = 2,
- TCSASOFT = 16,
- }
+ /// Updates the wrapper values from the internal `libc::termios` data structure.
+ #[doc(hidden)]
+ pub fn update_wrapper(&mut self) {
+ let termios = *self.inner.borrow_mut();
+ self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
+ self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
+ self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
+ self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
+ self.control_chars = termios.c_cc;
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlushArg {
- TCIFLUSH = 1,
- TCOFLUSH = 2,
- TCIOFLUSH = 3,
+impl From<libc::termios> for Termios {
+ fn from(termios: libc::termios) -> Self {
+ Termios {
+ inner: RefCell::new(termios),
+ input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
+ output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
+ control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
+ local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
+ control_chars: termios.c_cc,
}
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlowArg {
- TCOOFF = 1,
- TCOON = 2,
- TCIOFF = 3,
- TCION = 4,
- }
+impl From<Termios> for libc::termios {
+ fn from(termios: Termios) -> Self {
+ termios.inner.into_inner()
}
+}
- #[cfg(any(target_os = "linux", target_os = "android"))]
- pub mod consts {
- use libc::{c_int, c_uint, c_uchar};
-
- pub type tcflag_t = c_uint;
- pub type cc_t = c_uchar;
- pub type speed_t = c_uint;
-
- #[repr(C)]
- #[derive(Clone, Copy)]
- pub struct Termios {
- pub c_iflag: InputFlags,
- pub c_oflag: OutputFlags,
- pub c_cflag: ControlFlags,
- pub c_lflag: LocalFlags,
- pub c_line: cc_t,
- pub c_cc: [cc_t; NCCS],
- pub c_ispeed: speed_t,
- pub c_ospeed: speed_t
- }
+libc_enum!{
+ /// Baud rates supported by the system
+ ///
+ /// B0 is special and will disable the port.
+ #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
+ #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
+ pub enum BaudRate {
+ B0,
+ B50,
+ B75,
+ B110,
+ B134,
+ B150,
+ B200,
+ B300,
+ B600,
+ B1200,
+ B1800,
+ B2400,
+ B4800,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B7200,
+ B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B14400,
+ B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B28800,
+ B38400,
+ B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B76800,
+ B115200,
+ B230400,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B4000000,
+ }
+}
- #[derive(Clone, Copy, Debug, PartialEq)]
- pub enum BaudRate {
- B0,
- B50,
- B75,
- B110,
- B134,
- B150,
- B200,
- B300,
- B600,
- B1200,
- B1800,
- B2400,
- B4800,
- B9600,
- B19200,
- B38400,
- B57600,
- B115200,
- B230400,
- B460800,
- B500000,
- B576000,
- B921600,
- B1000000,
- B1152000,
- B1500000,
- B2000000,
- B2500000,
- B3000000,
- B3500000,
- B4000000,
+impl From<libc::speed_t> for BaudRate {
+ fn from(s: libc::speed_t) -> BaudRate {
+
+ use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800,
+ B9600, B19200, B38400, B57600, B115200, B230400};
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000,
+ B3500000, B4000000};
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ use libc::{B7200, B14400, B28800, B76800};
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd"))]
+ use libc::{B460800, B921600};
+
+ match s {
+ B0 => BaudRate::B0,
+ B50 => BaudRate::B50,
+ B75 => BaudRate::B75,
+ B110 => BaudRate::B110,
+ B134 => BaudRate::B134,
+ B150 => BaudRate::B150,
+ B200 => BaudRate::B200,
+ B300 => BaudRate::B300,
+ B600 => BaudRate::B600,
+ B1200 => BaudRate::B1200,
+ B1800 => BaudRate::B1800,
+ B2400 => BaudRate::B2400,
+ B4800 => BaudRate::B4800,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B7200 => BaudRate::B7200,
+ B9600 => BaudRate::B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B14400 => BaudRate::B14400,
+ B19200 => BaudRate::B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B28800 => BaudRate::B28800,
+ B38400 => BaudRate::B38400,
+ B57600 => BaudRate::B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ B76800 => BaudRate::B76800,
+ B115200 => BaudRate::B115200,
+ B230400 => BaudRate::B230400,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B460800 => BaudRate::B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B500000 => BaudRate::B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B576000 => BaudRate::B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "linux",
+ taget_os = "netbsd"))]
+ B921600 => BaudRate::B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1000000 => BaudRate::B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1152000 => BaudRate::B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B1500000 => BaudRate::B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2000000 => BaudRate::B2000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B2500000 => BaudRate::B2500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3000000 => BaudRate::B3000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B3500000 => BaudRate::B3500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ B4000000 => BaudRate::B4000000,
+ b @ _ => unreachable!("Invalid baud constant: {}", b),
}
+ }
+}
- impl From<speed_t> for BaudRate {
- fn from(s: speed_t) -> BaudRate {
-
- use libc::{
- B0, B50, B75, B110, B134, B150,
- B200, B300, B600, B1200, B1800, B2400,
- B4800, B9600, B19200, B38400, B57600,
- B115200, B230400, B460800, B500000,
- B576000, B921600, B1000000, B1152000,
- B1500000, B2000000, B2500000, B3000000,
- B3500000, B4000000};
-
- match s {
- B0 => BaudRate::B0,
- B50 => BaudRate::B50,
- B75 => BaudRate::B75,
- B110 => BaudRate::B110,
- B134 => BaudRate::B134,
- B150 => BaudRate::B150,
- B200 => BaudRate::B200,
- B300 => BaudRate::B300,
- B600 => BaudRate::B600,
- B1200 => BaudRate::B1200,
- B1800 => BaudRate::B1800,
- B2400 => BaudRate::B2400,
- B4800 => BaudRate::B4800,
- B9600 => BaudRate::B9600,
- B19200 => BaudRate::B19200,
- B38400 => BaudRate::B38400,
- B57600 => BaudRate::B57600,
- B115200 => BaudRate::B115200,
- B230400 => BaudRate::B230400,
- B460800 => BaudRate::B460800,
- B500000 => BaudRate::B500000,
- B576000 => BaudRate::B576000,
- B921600 => BaudRate::B921600,
- B1000000 => BaudRate::B1000000,
- B1152000 => BaudRate::B1152000,
- B1500000 => BaudRate::B1500000,
- B2000000 => BaudRate::B2000000,
- B2500000 => BaudRate::B2500000,
- B3000000 => BaudRate::B3000000,
- B3500000 => BaudRate::B3500000,
- B4000000 => BaudRate::B4000000,
- b @ _ => unreachable!("Invalid baud constant: {}", b),
- }
- }
- }
+// TODO: Add TCSASOFT, which will require treating this as a bitfield.
+libc_enum! {
+ /// Specify when a port configuration change should occur.
+ ///
+ /// Used as an argument to `tcsetattr()`
+ #[repr(i32)]
+ pub enum SetArg {
+ /// The change will occur immediately
+ TCSANOW,
+ /// The change occurs after all output has been written
+ TCSADRAIN,
+ /// Same as `TCSADRAIN`, but will also flush the input buffer
+ TCSAFLUSH,
+ }
+}
- pub const VEOF: usize = 4;
- pub const VEOL: usize = 11;
- pub const VEOL2: usize = 16;
- pub const VERASE: usize = 2;
- pub const VWERASE: usize = 14;
- pub const VKILL: usize = 3;
- pub const VREPRINT: usize = 12;
- pub const VINTR: usize = 0;
- pub const VQUIT: usize = 1;
- pub const VSUSP: usize = 10;
- pub const VSTART: usize = 8;
- pub const VSTOP: usize = 9;
- pub const VLNEXT: usize = 15;
- pub const VDISCARD: usize = 13;
- pub const VMIN: usize = 6;
- pub const VTIME: usize = 5;
- pub const NCCS: usize = 32;
-
- bitflags! {
- pub struct InputFlags: tcflag_t {
- const IGNBRK = 0x00000001;
- const BRKINT = 0x00000002;
- const IGNPAR = 0x00000004;
- const PARMRK = 0x00000008;
- const INPCK = 0x00000010;
- const ISTRIP = 0x00000020;
- const INLCR = 0x00000040;
- const IGNCR = 0x00000080;
- const ICRNL = 0x00000100;
- const IXON = 0x00000400;
- const IXOFF = 0x00001000;
- const IXANY = 0x00000800;
- const IMAXBEL = 0x00002000;
- const IUTF8 = 0x00004000;
- }
- }
+libc_enum! {
+ /// Specify a combination of the input and output buffers to flush
+ ///
+ /// Used as an argument to `tcflush()`.
+ #[repr(i32)]
+ pub enum FlushArg {
+ /// Flush data that was received but not read
+ TCIFLUSH,
+ /// Flush data written but not transmitted
+ TCOFLUSH,
+ /// Flush both received data not read and written data not transmitted
+ TCIOFLUSH,
+ }
+}
- bitflags! {
- pub struct OutputFlags: tcflag_t {
- const OPOST = 0x00000001;
- const ONLCR = 0x00000004;
- }
- }
+libc_enum! {
+ /// Specify how transmission flow should be altered
+ ///
+ /// Used as an argument to `tcflow()`.
+ #[repr(i32)]
+ pub enum FlowArg {
+ /// Suspend transmission
+ TCOOFF,
+ /// Resume transmission
+ TCOON,
+ /// Transmit a STOP character, which should disable a connected terminal device
+ TCIOFF,
+ /// Transmit a START character, which should re-enable a connected terminal device
+ TCION,
+ }
+}
- bitflags! {
- pub struct ControlFlags: tcflag_t {
- const CSIZE = 0x00000030;
- const CS5 = 0x00000000;
- const CS6 = 0x00000010;
- const CS7 = 0x00000020;
- const CS8 = 0x00000030;
- const CSTOPB = 0x00000040;
- const CREAD = 0x00000080;
- const PARENB = 0x00000100;
- const PARODD = 0x00000200;
- const HUPCL = 0x00000400;
- const CLOCAL = 0x00000800;
- const CRTSCTS = 0x80000000;
- #[cfg(target_os = "android")]
- const CBAUD = 0o0010017;
- }
- }
+// TODO: Make this usable directly as a slice index.
+libc_enum! {
+ /// Indices into the `termios.c_cc` array for special characters.
+ #[repr(usize)]
+ pub enum SpecialCharacterIndices {
+ VDISCARD,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ VDSUSP,
+ VEOF,
+ VEOL,
+ VEOL2,
+ VERASE,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ VERASE2,
+ VINTR,
+ VKILL,
+ VLNEXT,
+ VMIN,
+ VQUIT,
+ VREPRINT,
+ VSTART,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ VSTATUS,
+ VSTOP,
+ VSUSP,
+ #[cfg(target_os = "linux")]
+ VSWTC,
+ #[cfg(target_os = "haiku")]
+ VSWTCH,
+ VTIME,
+ VWERASE,
+ #[cfg(target_os = "dragonfly")]
+ VCHECKPT,
+ }
+}
- bitflags! {
- pub struct LocalFlags: tcflag_t {
- const ECHOKE = 0x00000800;
- const ECHOE = 0x00000010;
- const ECHOK = 0x00000020;
- const ECHO = 0x00000008;
- const ECHONL = 0x00000040;
- const ECHOPRT = 0x00000400;
- const ECHOCTL = 0x00000200;
- const ISIG = 0x00000001;
- const ICANON = 0x00000002;
- const IEXTEN = 0x00008000;
- const EXTPROC = 0x00010000;
- const TOSTOP = 0x00000100;
- const FLUSHO = 0x00001000;
- const PENDIN = 0x00004000;
- const NOFLSH = 0x00000080;
- }
- }
+pub use libc::NCCS;
+#[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+pub use libc::_POSIX_VDISABLE;
+
+libc_bitflags! {
+ /// Flags for configuring the input mode of a terminal
+ pub flags InputFlags: tcflag_t {
+ IGNBRK,
+ BRKINT,
+ IGNPAR,
+ PARMRK,
+ INPCK,
+ ISTRIP,
+ INLCR,
+ IGNCR,
+ ICRNL,
+ IXON,
+ IXOFF,
+ IXANY,
+ IMAXBEL,
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ IUTF8,
+ }
+}
- pub const NL0: c_int = 0x00000000;
- pub const NL1: c_int = 0x00000100;
- pub const TAB0: c_int = 0x00000000;
- pub const TAB1: c_int = 0x00000800;
- pub const TAB2: c_int = 0x00001000;
- pub const TAB3: c_int = 0x00001800;
- pub const CR0: c_int = 0x00000000;
- pub const CR1: c_int = 0x00000200;
- pub const CR2: c_int = 0x00000400;
- pub const CR3: c_int = 0x00000600;
- pub const FF0: c_int = 0x00000000;
- pub const FF1: c_int = 0x00008000;
- pub const BS0: c_int = 0x00000000;
- pub const BS1: c_int = 0x00002000;
- pub const VT0: c_int = 0x00000000;
- pub const VT1: c_int = 0x00004000;
-
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum SetArg {
- TCSANOW = 0,
- TCSADRAIN = 1,
- TCSAFLUSH = 2,
- }
+libc_bitflags! {
+ /// Flags for configuring the output mode of a terminal
+ pub flags OutputFlags: tcflag_t {
+ OPOST,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "linux",
+ target_os = "openbsd"))]
+ OLCUC,
+ ONLCR,
+ OCRNL as tcflag_t,
+ ONOCR as tcflag_t,
+ ONLRET as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ OFILL as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ OFDEL as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NL0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NL1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR2 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CR3 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB2 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TAB3 as tcflag_t,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ XTABS,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BS0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BS1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VT0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VT1 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FF0 as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FF1 as tcflag_t,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ OXTABS,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ ONOEOT as tcflag_t,
+
+ // Bitmasks for use with OutputFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ NLDLY as tcflag_t, // FIXME: Datatype needs to be corrected in libc for mac
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ CRDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ TABDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ BSDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ VTDLY as tcflag_t,
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ FFDLY as tcflag_t,
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlushArg {
- TCIFLUSH = 0,
- TCOFLUSH = 1,
- TCIOFLUSH = 2,
- }
+libc_bitflags! {
+ /// Flags for setting the control mode of a terminal
+ pub flags ControlFlags: tcflag_t {
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CIGNORE,
+ CS5,
+ CS6,
+ CS7,
+ CS8,
+ CSTOPB,
+ CREAD,
+ PARENB,
+ PARODD,
+ HUPCL,
+ CLOCAL,
+ CRTSCTS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ CBAUD,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+ CMSPAR,
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux",
+ not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
+ CIBAUD,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ CBAUDEX,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ MDMBUF,
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ CHWFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CCTS_OFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ CRTS_IFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CDTR_IFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CDSR_OFLOW,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ CCAR_OFLOW,
+
+ // Bitmasks for use with ControlFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ CSIZE,
+ }
+}
- // XXX: We're using `repr(C)` because `c_int` doesn't work here.
- // See https://github.com/rust-lang/rust/issues/10374.
- #[derive(Clone, Copy)]
- #[repr(C)]
- pub enum FlowArg {
- TCOOFF = 0,
- TCOON = 1,
- TCIOFF = 2,
- TCION = 3,
- }
+libc_bitflags! {
+ /// Flags for setting any local modes
+ pub flags LocalFlags: tcflag_t {
+ ECHOKE,
+ ECHOE,
+ ECHOK,
+ ECHO,
+ ECHONL,
+ ECHOPRT,
+ ECHOCTL,
+ ISIG,
+ ICANON,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ ALTWERASE,
+ IEXTEN,
+ EXTPROC,
+ TOSTOP,
+ FLUSHO,
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ NOKERNINFO,
+ PENDIN,
+ NOFLSH,
}
}
+/// Get input baud rate (see
+/// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+///
+/// `cfgetispeed()` extracts the input baud rate from the given Termios structure.
pub fn cfgetispeed(termios: &Termios) -> BaudRate {
- unsafe {
- ffi::cfgetispeed(termios).into()
- }
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) }.into()
}
+/// Get output baud rate (see
+/// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+///
+/// `cfgetospeed()` extracts the output baud rate from the given Termios structure.
pub fn cfgetospeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) }.into()
+}
+
+/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
+/// [termios(3)](http://man7.org/linux/man-pages/man3/termios.3.html)).
+///
+/// `cfmakeraw()` configures the termios structure such that input is available character-by-
+/// character, echoing is disabled, and all special input and output processing is disabled. Note
+/// that this is a non-standard function, but is available on Linux and BSDs.
+pub fn cfmakeraw(termios: &mut Termios) {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
unsafe {
- ffi::cfgetospeed(termios).into()
+ libc::cfmakeraw(inner_termios);
}
+ termios.update_wrapper();
}
+/// Set input baud rate (see
+/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+///
+/// `cfsetispeed()` sets the intput baud rate in the given Termios structure.
pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- Errno::result(unsafe {
- ffi::cfsetispeed(termios, baud as speed_t)
- }).map(drop)
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
}
+/// Set output baud rate (see
+/// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+///
+/// `cfsetospeed()` sets the output baud rate in the given termios structure.
pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- Errno::result(unsafe {
- ffi::cfsetospeed(termios, baud as speed_t)
- }).map(drop)
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
}
+/// Set both the input and output baud rates (see
+/// [termios(3)](http://man7.org/linux/man-pages/man3/termios.3.html)).
+///
+/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
+/// this is part of the 4.4BSD standard and not part of POSIX.
+pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+}
+
+/// Return the configuration of a port
+/// [tcgetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
+///
+/// `tcgetattr()` returns a Termios structure with the current configuration for a port. Modifying
+/// this structure *will not* reconfigure the port, instead the modifications should be done to
+/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
- let mut termios = unsafe { mem::uninitialized() };
+ let mut termios: libc::termios = unsafe { mem::uninitialized() };
- let res = unsafe {
- ffi::tcgetattr(fd, &mut termios)
- };
+ let res = unsafe { libc::tcgetattr(fd, &mut termios) };
try!(Errno::result(res));
- Ok(termios)
+ Ok(termios.into())
}
-pub fn tcsetattr(fd: RawFd,
- actions: SetArg,
- termios: &Termios) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcsetattr(fd, actions as c_int, termios)
- }).map(drop)
+/// Set the configuration for a terminal (see
+/// [tcsetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+///
+/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
+/// takes affect at a time specified by `actions`. Note that this function may return success if
+/// *any* of the parameters were successfully set, not only if all were set successfully.
+pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
+ let inner_termios = termios.get_libc_termios();
+ Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
}
+/// Block until all output data is written (see
+/// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
pub fn tcdrain(fd: RawFd) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcdrain(fd)
- }).map(drop)
+ Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
}
+/// Suspend or resume the transmission or reception of data (see
+/// [tcflow(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+///
+/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
+/// depending on the value of `action`.
pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcflow(fd, action as c_int)
- }).map(drop)
+ Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
}
+/// Discard data in the output or input queue (see
+/// [tcflush(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+///
+/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
+/// depending on the value of `action`.
pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcflush(fd, action as c_int)
- }).map(drop)
+ Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
+}
+
+/// Send a break for a specific duration (see
+/// [tcsendbreak(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+///
+/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
+/// of zero-valued bits for an implementation-defined duration.
+pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
+ Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
}
-pub fn tcsendbreak(fd: RawFd, action: c_int) -> Result<()> {
- Errno::result(unsafe {
- ffi::tcsendbreak(fd, action)
- }).map(drop)
+/// Get the session controlled by the given terminal (see
+/// [tcgetsid(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
+pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
+ let res = unsafe { libc::tcgetsid(fd) };
+
+ Errno::result(res).map(Pid::from_raw)
}
diff --git a/src/unistd.rs b/src/unistd.rs
index 2925f41c..196232b6 100644
--- a/src/unistd.rs
+++ b/src/unistd.rs
@@ -1,9 +1,11 @@
//! Safe wrappers around functions found in libc "unistd.h" header
+use errno;
use {Errno, Error, Result, NixPath};
use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC};
use fcntl::FcntlArg::F_SETFD;
-use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t};
+use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t,
+ uid_t, gid_t, mode_t};
use std::mem;
use std::ffi::{CString, CStr, OsString, OsStr};
use std::os::unix::ffi::{OsStringExt, OsStrExt};
@@ -668,6 +670,40 @@ pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> {
})
}
+/// Close a raw file descriptor
+///
+/// Be aware that many Rust types implicitly close-on-drop, including
+/// `std::fs::File`. Explicitly closing them with this method too can result in
+/// a double-close condition, which can cause confusing `EBADF` errors in
+/// seemingly unrelated code. Caveat programmer.
+///
+/// # Examples
+///
+/// ```no_run
+/// extern crate tempfile;
+/// extern crate nix;
+///
+/// use std::os::unix::io::AsRawFd;
+/// use nix::unistd::close;
+///
+/// fn main() {
+/// let mut f = tempfile::tempfile().unwrap();
+/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
+/// }
+/// ```
+///
+/// ```rust
+/// extern crate tempfile;
+/// extern crate nix;
+///
+/// use std::os::unix::io::IntoRawFd;
+/// use nix::unistd::close;
+///
+/// fn main() {
+/// let mut f = tempfile::tempfile().unwrap();
+/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
+/// }
+/// ```
pub fn close(fd: RawFd) -> Result<()> {
let res = unsafe { libc::close(fd) };
Errno::result(res).map(drop)
@@ -937,6 +973,622 @@ pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
Ok((fd, PathBuf::from(pathname)))
}
+/// Variable names for `pathconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `PathconfVar` variables have the same name as the abstract
+/// variables shown in the `pathconf(2)` man page. Usually, it's the same as
+/// the C variable name without the leading `_PC_`.
+///
+/// POSIX 1003.1-2008 standardizes all of these variables, but some OSes choose
+/// not to implement variables that cannot change at runtime.
+///
+/// # References
+///
+/// - [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
+/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+pub enum PathconfVar {
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux",
+ target_os = "netbsd", target_os = "openbsd"))]
+ /// Minimum number of bits needed to represent, as a signed integer value,
+ /// the maximum size of a regular file allowed in the specified directory.
+ FILESIZEBITS = libc::_PC_FILESIZEBITS,
+ /// Maximum number of links to a single file.
+ LINK_MAX = libc::_PC_LINK_MAX,
+ /// Maximum number of bytes in a terminal canonical input line.
+ MAX_CANON = libc::_PC_MAX_CANON,
+ /// Minimum number of bytes for which space is available in a terminal input
+ /// queue; therefore, the maximum number of bytes a conforming application
+ /// may require to be typed as input before reading them.
+ MAX_INPUT = libc::_PC_MAX_INPUT,
+ /// Maximum number of bytes in a filename (not including the terminating
+ /// null of a filename string).
+ NAME_MAX = libc::_PC_NAME_MAX,
+ /// Maximum number of bytes the implementation will store as a pathname in a
+ /// user-supplied buffer of unspecified size, including the terminating null
+ /// character. Minimum number the implementation will accept as the maximum
+ /// number of bytes in a pathname.
+ PATH_MAX = libc::_PC_PATH_MAX,
+ /// Maximum number of bytes that is guaranteed to be atomic when writing to
+ /// a pipe.
+ PIPE_BUF = libc::_PC_PIPE_BUF,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "linux",
+ target_os = "netbsd", target_os = "openbsd"))]
+ /// Symbolic links can be created.
+ POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Minimum number of bytes of storage actually allocated for any portion of
+ /// a file.
+ POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Recommended increment for file transfer sizes between the
+ /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values.
+ POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Maximum recommended file transfer size.
+ POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Minimum recommended file transfer size.
+ POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Recommended file transfer buffer alignment.
+ POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
+ /// Maximum number of bytes in a symbolic link.
+ SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
+ /// The use of `chown` and `fchown` is restricted to a process with
+ /// appropriate privileges, and to changing the group ID of a file only to
+ /// the effective group ID of the process or to one of its supplementary
+ /// group IDs.
+ _POSIX_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED,
+ /// Pathname components longer than {NAME_MAX} generate an error.
+ _POSIX_NO_TRUNC = libc::_PC_NO_TRUNC,
+ /// This symbol shall be defined to be the value of a character that shall
+ /// disable terminal special character handling.
+ _POSIX_VDISABLE = libc::_PC_VDISABLE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Asynchronous input or output operations may be performed for the
+ /// associated file.
+ _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ /// Prioritized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_PRIO_IO = libc::_PC_PRIO_IO,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
+ /// Synchronized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_SYNC_IO = libc::_PC_SYNC_IO,
+ #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
+ /// The resolution in nanoseconds for all file timestamps.
+ _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION
+}
+
+/// Like `pathconf`, but works with file descriptors instead of paths (see
+/// [fpathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// # Parameters
+///
+/// - `fd`: The file descriptor whose variable should be interrogated
+/// - `var`: The pathconf variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::fpathconf(fd, var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Error::Sys(Errno::last()))
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
+/// Get path-dependent configurable system variables (see
+/// [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// Returns the value of a path-dependent configurable system variable. Most
+/// supported variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// `pathconf` variables: options and limits. See [pathconf(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details.
+///
+/// # Parameters
+///
+/// - `path`: Lookup the value of `var` for this file or directory
+/// - `var`: The `pathconf` variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = try!(path.with_nix_path(|cstr| {
+ unsafe {
+ Errno::clear();
+ libc::pathconf(cstr.as_ptr(), var as c_int)
+ }
+ }));
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Error::Sys(Errno::last()))
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
+/// Variable names for `sysconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `SysconfVar` variables have the same name as the abstract variables
+/// shown in the `sysconf(3)` man page. Usually, it's the same as the C
+/// variable name without the leading `_SC_`.
+///
+/// All of these symbols are standardized by POSIX 1003.1-2008, but haven't been
+/// implemented by all platforms.
+///
+/// # References
+///
+/// - [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
+/// - [unistd.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [limits.h](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+pub enum SysconfVar {
+ /// Maximum number of I/O operations in a single list I/O call supported by
+ /// the implementation.
+ AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
+ /// Maximum number of outstanding asynchronous I/O operations supported by
+ /// the implementation.
+ AIO_MAX = libc::_SC_AIO_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The maximum amount by which a process can decrease its asynchronous I/O
+ /// priority level from its own scheduling priority.
+ AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX,
+ /// Maximum length of argument to the exec functions including environment data.
+ ARG_MAX = libc::_SC_ARG_MAX,
+ /// Maximum number of functions that may be registered with `atexit`.
+ ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
+ /// Maximum obase values allowed by the bc utility.
+ BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
+ /// Maximum number of elements permitted in an array by the bc utility.
+ BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
+ /// Maximum scale value allowed by the bc utility.
+ BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
+ /// Maximum length of a string constant accepted by the bc utility.
+ BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
+ /// Maximum number of simultaneous processes per real user ID.
+ CHILD_MAX = libc::_SC_CHILD_MAX,
+ // _SC_CLK_TCK is obsolete
+ /// Maximum number of weights that can be assigned to an entry of the
+ /// LC_COLLATE order keyword in the locale definition file
+ COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
+ /// Maximum number of timer expiration overruns.
+ DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
+ /// Maximum number of expressions that can be nested within parentheses by
+ /// the expr utility.
+ EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// Maximum length of a host name (not including the terminating null) as
+ /// returned from the `gethostname` function
+ HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX,
+ /// Maximum number of iovec structures that one process has available for
+ /// use with `readv` or `writev`.
+ IOV_MAX = libc::_SC_IOV_MAX,
+ /// Unless otherwise noted, the maximum length, in bytes, of a utility's
+ /// input line (either standard input or another file), when the utility is
+ /// described as processing text files. The length includes room for the
+ /// trailing <newline>.
+ LINE_MAX = libc::_SC_LINE_MAX,
+ /// Maximum length of a login name.
+ LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
+ /// Maximum number of simultaneous supplementary group IDs per process.
+ NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
+ /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers
+ GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
+ /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers
+ GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
+ /// The maximum number of open message queue descriptors a process may hold.
+ MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
+ /// The maximum number of message priorities supported by the implementation.
+ MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX,
+ /// A value one greater than the maximum value that the system may assign to
+ /// a newly-created file descriptor.
+ OPEN_MAX = libc::_SC_OPEN_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Advisory Information option.
+ _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports barriers.
+ _POSIX_BARRIERS = libc::_SC_BARRIERS,
+ /// The implementation supports asynchronous input and output.
+ _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports clock selection.
+ _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Process CPU-Time Clocks option.
+ _POSIX_CPUTIME = libc::_SC_CPUTIME,
+ /// The implementation supports the File Synchronization option.
+ _POSIX_FSYNC = libc::_SC_FSYNC,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the IPv6 option.
+ _POSIX_IPV6 = libc::_SC_IPV6,
+ /// The implementation supports job control.
+ _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL,
+ /// The implementation supports memory mapped Files.
+ _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES,
+ /// The implementation supports the Process Memory Locking option.
+ _POSIX_MEMLOCK = libc::_SC_MEMLOCK,
+ /// The implementation supports the Range Memory Locking option.
+ _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
+ /// The implementation supports memory protection.
+ _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
+ /// The implementation supports the Message Passing option.
+ _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
+ /// The implementation supports the Monotonic Clock option.
+ _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the Prioritized Input and Output option.
+ _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
+ /// The implementation supports the Process Scheduling option.
+ _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Raw Sockets option.
+ _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports read-write locks.
+ _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ /// The implementation supports realtime signals.
+ _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Regular Expression Handling option.
+ _POSIX_REGEXP = libc::_SC_REGEXP,
+ /// Each process has a saved set-user-ID and a saved set-group-ID.
+ _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS,
+ /// The implementation supports semaphores.
+ _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES,
+ /// The implementation supports the Shared Memory Objects option.
+ _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the POSIX shell.
+ _POSIX_SHELL = libc::_SC_SHELL,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Spawn option.
+ _POSIX_SPAWN = libc::_SC_SPAWN,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports spin locks.
+ _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Process Sporadic Server option.
+ _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX,
+ /// The implementation supports the Synchronized Input and Output option.
+ _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
+ /// The implementation supports the Thread Stack Address Attribute option.
+ _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
+ /// The implementation supports the Thread Stack Size Attribute option.
+ _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd"))]
+ /// The implementation supports the Thread CPU-Time Clocks option.
+ _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME,
+ /// The implementation supports the Non-Robust Mutex Priority Inheritance
+ /// option.
+ _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
+ /// The implementation supports the Non-Robust Mutex Priority Protection option.
+ _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
+ /// The implementation supports the Thread Execution Scheduling option.
+ _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Thread Process-Shared Synchronization
+ /// option.
+ _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ /// The implementation supports the Robust Mutex Priority Inheritance option.
+ _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ /// The implementation supports the Robust Mutex Priority Protection option.
+ _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT,
+ /// The implementation supports thread-safe functions.
+ _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Thread Sporadic Server option.
+ _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER,
+ /// The implementation supports threads.
+ _POSIX_THREADS = libc::_SC_THREADS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports timeouts.
+ _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS,
+ /// The implementation supports timers.
+ _POSIX_TIMERS = libc::_SC_TIMERS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Trace option.
+ _POSIX_TRACE = libc::_SC_TRACE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Trace Event Filter option.
+ _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Trace Inherit option.
+ _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Trace Log option.
+ _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the Typed Memory Objects option.
+ _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS,
+ /// Integer value indicating version of this standard (C-language binding)
+ /// to which the implementation conforms. For implementations conforming to
+ /// POSIX.1-2008, the value shall be 200809L.
+ _POSIX_VERSION = libc::_SC_VERSION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at
+ /// least 64 bits.
+ _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation provides a C-language compilation environment with an
+ /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types
+ /// using at least 64 bits.
+ _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG,
+ /// The implementation supports the C-Language Binding option.
+ _POSIX2_C_BIND = libc::_SC_2_C_BIND,
+ /// The implementation supports the C-Language Development Utilities option.
+ _POSIX2_C_DEV = libc::_SC_2_C_DEV,
+ /// The implementation supports the Terminal Characteristics option.
+ _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
+ /// The implementation supports the FORTRAN Development Utilities option.
+ _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV,
+ /// The implementation supports the FORTRAN Runtime Utilities option.
+ _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN,
+ /// The implementation supports the creation of locales by the localedef
+ /// utility.
+ _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Batch Environment Services and Utilities
+ /// option.
+ _POSIX2_PBS = libc::_SC_2_PBS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Batch Accounting option.
+ _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Batch Checkpoint/Restart option.
+ _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Locate Batch Job Request option.
+ _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Batch Job Message Request option.
+ _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ /// The implementation supports the Track Batch Job Request option.
+ _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK,
+ /// The implementation supports the Software Development Utilities option.
+ _POSIX2_SW_DEV = libc::_SC_2_SW_DEV,
+ /// The implementation supports the User Portability Utilities option.
+ _POSIX2_UPE = libc::_SC_2_UPE,
+ /// Integer value indicating version of the Shell and Utilities volume of
+ /// POSIX.1 to which the implementation conforms.
+ _POSIX2_VERSION = libc::_SC_2_VERSION,
+ /// The size of a system page in bytes.
+ ///
+ /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two
+ /// enum constants to have the same value, so nix omits `PAGESIZE`.
+ PAGE_SIZE = libc::_SC_PAGE_SIZE,
+ PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
+ PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
+ PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
+ PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
+ RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ RTSIG_MAX = libc::_SC_RTSIG_MAX,
+ SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX,
+ STREAM_MAX = libc::_SC_STREAM_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
+ TIMER_MAX = libc::_SC_TIMER_MAX,
+ TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
+ TZNAME_MAX = libc::_SC_TZNAME_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the X/Open Encryption Option Group.
+ _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the Issue 4, Version 2 Enhanced
+ /// Internationalization Option Group.
+ _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the X/Open Realtime Option Group.
+ _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the X/Open Realtime Threads Option Group.
+ _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS,
+ /// The implementation supports the Issue 4, Version 2 Shared Memory Option
+ /// Group.
+ _XOPEN_SHM = libc::_SC_XOPEN_SHM,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ /// The implementation supports the XSI STREAMS Option Group.
+ _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// The implementation supports the XSI option
+ _XOPEN_UNIX = libc::_SC_XOPEN_UNIX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ /// Integer value indicating version of the X/Open Portability Guide to
+ /// which the implementation conforms.
+ _XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
+}
+
+/// Get configurable system variables (see
+/// [sysconf(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html))
+///
+/// Returns the value of a configurable system variable. Most supported
+/// variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// sysconf variables: options and limits. See sysconf(3) for more details.
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::sysconf(var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Error::Sys(Errno::last()))
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux {
use libc;
diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs
index b5465aa0..9f9c9bf0 100644
--- a/test/sys/test_socket.rs
+++ b/test/sys/test_socket.rs
@@ -131,7 +131,7 @@ pub fn test_scm_rights() {
panic!("unexpected cmsg");
}
}
- assert_eq!(msg.flags & (MSG_TRUNC | MSG_CTRUNC), MsgFlags::empty());
+ assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
close(fd2).unwrap();
}
@@ -145,6 +145,43 @@ pub fn test_scm_rights() {
close(w).unwrap();
}
+// Verify `sendmsg` builds a valid `msghdr` when passing an empty
+// `cmsgs` argument. This should result in a msghdr with a nullptr
+// msg_control field and a msg_controllen of 0 when calling into the
+// raw `sendmsg`.
+#[test]
+pub fn test_sendmsg_empty_cmsgs() {
+ use nix::sys::uio::IoVec;
+ use nix::unistd::close;
+ use nix::sys::socket::{socketpair, sendmsg, recvmsg,
+ AddressFamily, SockType, SockFlag,
+ CmsgSpace, MsgFlags,
+ MSG_TRUNC, MSG_CTRUNC};
+
+ let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, 0,
+ SockFlag::empty())
+ .unwrap();
+
+ {
+ let iov = [IoVec::from_slice(b"hello")];
+ assert_eq!(sendmsg(fd1, &iov, &[], MsgFlags::empty(), None).unwrap(), 5);
+ close(fd1).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let iov = [IoVec::from_mut_slice(&mut buf[..])];
+ let mut cmsgspace: CmsgSpace<[RawFd; 1]> = CmsgSpace::new();
+ let msg = recvmsg(fd2, &iov, Some(&mut cmsgspace), MsgFlags::empty()).unwrap();
+
+ for _ in msg.cmsgs() {
+ panic!("unexpected cmsg");
+ }
+ assert!(!msg.flags.intersects(MSG_TRUNC | MSG_CTRUNC));
+ close(fd2).unwrap();
+ }
+}
+
// Test creating and using named unix domain sockets
#[test]
pub fn test_unixdomain() {
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index a41304d7..c6572052 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -1,21 +1,126 @@
+use std::os::unix::prelude::*;
+use tempfile::tempfile;
+
+use nix::{Error, fcntl};
use nix::errno::Errno;
-use nix::sys::termios;
-use nix::{Error, unistd};
+use nix::pty::openpty;
+use nix::sys::termios::{self, ECHO, OPOST, OCRNL, Termios, tcgetattr};
+use nix::unistd::{read, write, close};
-#[test]
-fn test_tcgetattr() {
- for fd in 0..5 {
- let termios = termios::tcgetattr(fd);
- match unistd::isatty(fd) {
- // If `fd` is a TTY, tcgetattr must succeed.
- Ok(true) => assert!(termios.is_ok()),
- // If it's an invalid file descriptor, tcgetattr should also return
- // the same error
- Err(Error::Sys(Errno::EBADF)) => {
- assert_eq!(termios.err(), Some(Error::Sys(Errno::EBADF)));
- },
- // Otherwise it should return any error
- _ => assert!(termios.is_err())
- }
+/// Helper function analogous to std::io::Write::write_all, but for `RawFd`s
+fn write_all(f: RawFd, buf: &[u8]) {
+ let mut len = 0;
+ while len < buf.len() {
+ len += write(f, &buf[len..]).unwrap();
}
}
+
+// Test tcgetattr on a terminal
+#[test]
+fn test_tcgetattr_pty() {
+ let pty = openpty(None, None).unwrap();
+ assert!(termios::tcgetattr(pty.master).is_ok());
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+}
+// Test tcgetattr on something that isn't a terminal
+#[test]
+fn test_tcgetattr_enotty() {
+ let file = tempfile().unwrap();
+ assert_eq!(termios::tcgetattr(file.as_raw_fd()).err(),
+ Some(Error::Sys(Errno::ENOTTY)));
+}
+
+// Test tcgetattr on an invalid file descriptor
+#[test]
+fn test_tcgetattr_ebadf() {
+ assert_eq!(termios::tcgetattr(-1).err(),
+ Some(Error::Sys(Errno::EBADF)));
+}
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+ let termios = tcgetattr(pty.master).unwrap();
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+ termios
+ };
+
+ // Make sure postprocessing '\r' isn't specified by default or this test is useless.
+ assert!(!termios.output_flags.contains(OPOST | OCRNL));
+
+ // Specify that '\r' characters should be transformed to '\n'
+ // OPOST is specified to enable post-processing
+ termios.output_flags.insert(OPOST | OCRNL);
+
+ // Open a pty
+ let pty = openpty(None, &termios).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(pty.master, string.as_bytes());
+
+ // Read from the slave verifying that the output has been properly transformed
+ let mut buf = [0u8; 10];
+ ::read_exact(pty.slave, &mut buf);
+ let transformed_string = "foofoofoo\n";
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+ assert_eq!(&buf, transformed_string.as_bytes());
+}
+
+// Test modifying local flags
+#[test]
+fn test_local_flags() {
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+ let termios = tcgetattr(pty.master).unwrap();
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+ termios
+ };
+
+ // Make sure echo is specified by default or this test is useless.
+ assert!(termios.local_flags.contains(ECHO));
+
+ // Disable local echo
+ termios.local_flags.remove(ECHO);
+
+ // Open a new pty with our modified termios settings
+ let pty = openpty(None, &termios).unwrap();
+ assert!(pty.master > 0);
+ assert!(pty.slave > 0);
+
+ // Set the master is in nonblocking mode or reading will never return.
+ let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
+ let new_flags = fcntl::OFlag::from_bits(flags).unwrap() | fcntl::O_NONBLOCK;
+ fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(pty.master, string.as_bytes());
+
+ // Try to read from the master, which should not have anything as echoing was disabled.
+ let mut buf = [0u8; 10];
+ let read = read(pty.master, &mut buf).unwrap_err();
+ close(pty.master).unwrap();
+ close(pty.slave).unwrap();
+ assert_eq!(read, Error::Sys(Errno::EAGAIN));
+}
+
+#[test]
+fn test_cfmakeraw() {
+ let mut termios = unsafe { Termios::default_uninit() };
+ termios::cfmakeraw(&mut termios);
+}
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 8528caf7..55316ab4 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -1,5 +1,7 @@
+use std::io::Write;
use std::path::Path;
use std::os::unix::prelude::*;
+use tempfile::tempfile;
use nix::fcntl::{O_RDWR, open};
use nix::pty::*;
@@ -7,6 +9,20 @@ use nix::sys::stat;
use nix::sys::termios::*;
use nix::unistd::{write, close};
+/// Regression test for Issue #659
+/// This is the correct way to explicitly close a PtyMaster
+#[test]
+fn test_explicit_close() {
+ let mut f = {
+ let m = posix_openpt(O_RDWR).unwrap();
+ close(m.into_raw_fd()).unwrap();
+ tempfile().unwrap()
+ };
+ // This should work. But if there's been a double close, then it will
+ // return EBADF
+ f.write(b"whatever").unwrap();
+}
+
/// Test equivalence of `ptsname` and `ptsname_r`
#[test]
#[cfg(any(target_os = "android", target_os = "linux"))]
@@ -139,7 +155,8 @@ fn test_openpty_with_termios() {
close(pty.slave).unwrap();
termios
};
- termios.c_oflag &= !ONLCR;
+ // Make sure newlines are not transformed so the data is preserved when sent.
+ termios.output_flags.remove(ONLCR);
let pty = openpty(None, &termios).unwrap();
// Must be valid file descriptors
diff --git a/test/test_ptymaster_drop.rs b/test/test_ptymaster_drop.rs
new file mode 100644
index 00000000..664a4dd6
--- /dev/null
+++ b/test/test_ptymaster_drop.rs
@@ -0,0 +1,21 @@
+extern crate nix;
+
+use nix::fcntl::O_RDWR;
+use nix::pty::*;
+use nix::unistd::close;
+use std::os::unix::io::AsRawFd;
+
+/// Regression test for Issue #659
+/// PtyMaster should panic rather than double close the file descriptor
+/// This must run in its own test process because it deliberately creates a race
+/// condition.
+#[test]
+#[should_panic(expected = "Closing an invalid file descriptor!")]
+// In Travis on i686-unknown-linux-musl, this test gets SIGABRT. I don't know
+// why. It doesn't happen on any other target, and it doesn't happen on my PC.
+#[cfg_attr(all(target_env = "musl", target_arch = "x86"), ignore)]
+fn test_double_close() {
+ let m = posix_openpt(O_RDWR).unwrap();
+ close(m.as_raw_fd()).unwrap();
+ drop(m); // should panic here
+}
diff --git a/test/test_unistd.rs b/test/test_unistd.rs
index d5e58fd5..e6e7b5f2 100644
--- a/test/test_unistd.rs
+++ b/test/test_unistd.rs
@@ -230,3 +230,35 @@ execve_test_factory!(test_execve, execve, b"/bin/sh", b"/system/bin/sh");
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "execvpe")]
execve_test_factory!(test_execvpe, execvpe, b"sh", b"sh");
+
+#[test]
+fn test_fpathconf_limited() {
+ let f = tempfile().unwrap();
+ // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
+ let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
+ assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
+}
+
+#[test]
+fn test_pathconf_limited() {
+ // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
+ let path_max = pathconf(".", PathconfVar::PATH_MAX);
+ assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
+}
+
+#[test]
+fn test_sysconf_limited() {
+ // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
+ let open_max = sysconf(SysconfVar::OPEN_MAX);
+ assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
+}
+
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_sysconf_unsupported() {
+ // I know of no sysconf variables that are unsupported everywhere, but
+ // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
+ // we test.
+ let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
+ assert!(open_max.expect("sysconf failed").is_none())
+}