| //! 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](https://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: Termios = unsafe { std::mem::zeroed() }; |
| //! 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::{ControlFlags, Termios}; |
| //! # let mut termios: Termios = unsafe { std::mem::zeroed() }; |
| //! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5; |
| //! termios.control_flags |= ControlFlags::CS5; |
| //! ``` |
| //! |
| //! # Baud rates |
| //! |
| //! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both |
| //! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs |
| //! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer |
| //! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following |
| //! conventions: |
| //! |
| //! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux |
| //! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux |
| //! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux |
| //! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux |
| //! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux |
| //! |
| //! The most common use case of specifying a baud rate using the enum will work the same across |
| //! platforms: |
| //! |
| //! ```rust |
| //! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! cfsetispeed(&mut t, BaudRate::B9600); |
| //! cfsetospeed(&mut t, BaudRate::B9600); |
| //! cfsetspeed(&mut t, BaudRate::B9600); |
| //! # } |
| //! ``` |
| //! |
| //! Additionally round-tripping baud rates is consistent across platforms: |
| //! |
| //! ```rust |
| //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! # cfsetspeed(&mut t, BaudRate::B9600); |
| //! let speed = cfgetispeed(&t); |
| //! assert_eq!(speed, cfgetospeed(&t)); |
| //! cfsetispeed(&mut t, speed); |
| //! # } |
| //! ``` |
| //! |
| //! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`: |
| //! |
| #![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd"), |
| doc = " ```rust,ignore")] |
| #![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd")), |
| doc = " ```rust")] |
| //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! # cfsetspeed(&mut t, BaudRate::B9600); |
| //! assert_eq!(cfgetispeed(&t), BaudRate::B9600); |
| //! assert_eq!(cfgetospeed(&t), BaudRate::B9600); |
| //! # } |
| //! ``` |
| //! |
| //! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s: |
| //! |
| #![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd"), |
| doc = " ```rust")] |
| #![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd")), |
| doc = " ```rust,ignore")] |
| //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! # cfsetspeed(&mut t, 9600u32); |
| //! assert_eq!(cfgetispeed(&t), 9600u32); |
| //! assert_eq!(cfgetospeed(&t), 9600u32); |
| //! # } |
| //! ``` |
| //! |
| //! It's trivial to convert from a `BaudRate` to a `u32` on BSDs: |
| //! |
| #![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd"), |
| doc = " ```rust")] |
| #![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd")), |
| doc = " ```rust,ignore")] |
| //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! # cfsetspeed(&mut t, 9600u32); |
| //! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into()); |
| //! assert_eq!(u32::from(BaudRate::B9600), 9600u32); |
| //! # } |
| //! ``` |
| //! |
| //! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support) |
| //! by specifying baud rates directly using `u32`s: |
| //! |
| #![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd"), |
| doc = " ```rust")] |
| #![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", |
| target_os = "macos", target_os = "netbsd", target_os = "openbsd")), |
| doc = " ```rust,ignore")] |
| //! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios}; |
| //! # fn main() { |
| //! # let mut t: Termios = unsafe { std::mem::zeroed() }; |
| //! cfsetispeed(&mut t, 9600u32); |
| //! cfsetospeed(&mut t, 9600u32); |
| //! cfsetspeed(&mut t, 9600u32); |
| //! # } |
| //! ``` |
| use cfg_if::cfg_if; |
| use crate::{Error, Result}; |
| use crate::errno::Errno; |
| use libc::{self, c_int, tcflag_t}; |
| use std::cell::{Ref, RefCell}; |
| use std::convert::{From, TryFrom}; |
| use std::mem; |
| use std::os::unix::io::RawFd; |
| |
| use crate::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, Debug, Eq, PartialEq)] |
| 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; NCCS], |
| } |
| |
| impl Termios { |
| /// Exposes an immutable reference to the underlying `libc::termios` data structure. |
| /// |
| /// This is not part of `nix`'s public API because it requires additional work to maintain type |
| /// safety. |
| pub(crate) 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() |
| } |
| |
| /// 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. In this case it should also be |
| /// paired with a call to `update_wrapper()` so that the wrapper-type and internal |
| /// representation stay consistent. |
| pub(crate) 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() |
| } |
| |
| /// Updates the wrapper values from the internal `libc::termios` data structure. |
| pub(crate) 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; |
| } |
| } |
| |
| 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, |
| } |
| } |
| } |
| |
| impl From<Termios> for libc::termios { |
| fn from(termios: Termios) -> Self { |
| termios.inner.into_inner() |
| } |
| } |
| |
| libc_enum!{ |
| /// Baud rates supported by the system. |
| /// |
| /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this |
| /// enum. |
| /// |
| /// 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, |
| #[cfg(any(target_os = "illumos", target_os = "solaris"))] |
| B153600, |
| B230400, |
| #[cfg(any(target_os = "illumos", target_os = "solaris"))] |
| B307200, |
| #[cfg(any(target_os = "android", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "linux", |
| target_os = "netbsd", |
| target_os = "solaris"))] |
| 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 = "illumos", |
| target_os = "linux", |
| target_os = "netbsd", |
| target_os = "solaris"))] |
| 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", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B2500000, |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B3000000, |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B3500000, |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B4000000, |
| } |
| } |
| |
| impl TryFrom<libc::speed_t> for BaudRate { |
| type Error = Error; |
| |
| fn try_from(s: libc::speed_t) -> Result<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}; |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| use libc::{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}; |
| #[cfg(any(target_os = "illumos", target_os = "solaris"))] |
| use libc::{B153600, B307200, B460800, B921600}; |
| |
| match s { |
| B0 => Ok(BaudRate::B0), |
| B50 => Ok(BaudRate::B50), |
| B75 => Ok(BaudRate::B75), |
| B110 => Ok(BaudRate::B110), |
| B134 => Ok(BaudRate::B134), |
| B150 => Ok(BaudRate::B150), |
| B200 => Ok(BaudRate::B200), |
| B300 => Ok(BaudRate::B300), |
| B600 => Ok(BaudRate::B600), |
| B1200 => Ok(BaudRate::B1200), |
| B1800 => Ok(BaudRate::B1800), |
| B2400 => Ok(BaudRate::B2400), |
| B4800 => Ok(BaudRate::B4800), |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| B7200 => Ok(BaudRate::B7200), |
| B9600 => Ok(BaudRate::B9600), |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| B14400 => Ok(BaudRate::B14400), |
| B19200 => Ok(BaudRate::B19200), |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| B28800 => Ok(BaudRate::B28800), |
| B38400 => Ok(BaudRate::B38400), |
| B57600 => Ok(BaudRate::B57600), |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| B76800 => Ok(BaudRate::B76800), |
| B115200 => Ok(BaudRate::B115200), |
| #[cfg(any(target_os = "illumos", |
| target_os = "solaris"))] |
| B153600 => Ok(BaudRate::B153600), |
| B230400 => Ok(BaudRate::B230400), |
| #[cfg(any(target_os = "illumos", |
| target_os = "solaris"))] |
| B307200 => Ok(BaudRate::B307200), |
| #[cfg(any(target_os = "android", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "linux", |
| target_os = "netbsd", |
| target_os = "solaris"))] |
| B460800 => Ok(BaudRate::B460800), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B500000 => Ok(BaudRate::B500000), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B576000 => Ok(BaudRate::B576000), |
| #[cfg(any(target_os = "android", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "linux", |
| target_os = "netbsd", |
| target_os = "solaris"))] |
| B921600 => Ok(BaudRate::B921600), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B1000000 => Ok(BaudRate::B1000000), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B1152000 => Ok(BaudRate::B1152000), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B1500000 => Ok(BaudRate::B1500000), |
| #[cfg(any(target_os = "android", target_os = "linux"))] |
| B2000000 => Ok(BaudRate::B2000000), |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B2500000 => Ok(BaudRate::B2500000), |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B3000000 => Ok(BaudRate::B3000000), |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B3500000 => Ok(BaudRate::B3500000), |
| #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] |
| B4000000 => Ok(BaudRate::B4000000), |
| _ => Err(Error::from(Errno::EINVAL)) |
| } |
| } |
| } |
| |
| #[cfg(any(target_os = "freebsd", |
| target_os = "dragonfly", |
| target_os = "ios", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| impl From<BaudRate> for u32 { |
| fn from(b: BaudRate) -> u32 { |
| b as u32 |
| } |
| } |
| |
| // 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, |
| } |
| } |
| |
| 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, |
| } |
| } |
| |
| 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, |
| } |
| } |
| |
| // 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 = "illumos", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd", |
| target_os = "solaris"))] |
| VDSUSP, |
| VEOF, |
| VEOL, |
| VEOL2, |
| VERASE, |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "solaris"))] |
| VERASE2, |
| VINTR, |
| VKILL, |
| VLNEXT, |
| #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), |
| target_os = "illumos", target_os = "solaris")))] |
| VMIN, |
| VQUIT, |
| VREPRINT, |
| VSTART, |
| #[cfg(any(target_os = "dragonfly", |
| target_os = "freebsd", |
| target_os = "illumos", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd", |
| target_os = "solaris"))] |
| VSTATUS, |
| VSTOP, |
| VSUSP, |
| #[cfg(target_os = "linux")] |
| VSWTC, |
| #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))] |
| VSWTCH, |
| #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), |
| target_os = "illumos", target_os = "solaris")))] |
| VTIME, |
| VWERASE, |
| #[cfg(target_os = "dragonfly")] |
| VCHECKPT, |
| } |
| } |
| |
| #[cfg(any(all(target_os = "linux", target_arch = "sparc64"), |
| target_os = "illumos", target_os = "solaris"))] |
| impl SpecialCharacterIndices { |
| pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF; |
| pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL; |
| } |
| |
| 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 struct InputFlags: tcflag_t { |
| IGNBRK; |
| BRKINT; |
| IGNPAR; |
| PARMRK; |
| INPCK; |
| ISTRIP; |
| INLCR; |
| IGNCR; |
| ICRNL; |
| IXON; |
| IXOFF; |
| #[cfg(not(target_os = "redox"))] |
| IXANY; |
| #[cfg(not(target_os = "redox"))] |
| IMAXBEL; |
| #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] |
| IUTF8; |
| } |
| } |
| |
| libc_bitflags! { |
| /// Flags for configuring the output mode of a terminal |
| pub struct 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; |
| } |
| } |
| |
| libc_bitflags! { |
| /// Flags for setting the control mode of a terminal |
| pub struct 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; |
| #[cfg(not(target_os = "redox"))] |
| 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; |
| } |
| } |
| |
| libc_bitflags! { |
| /// Flags for setting any local modes |
| pub struct LocalFlags: tcflag_t { |
| #[cfg(not(target_os = "redox"))] |
| ECHOKE; |
| ECHOE; |
| ECHOK; |
| ECHO; |
| ECHONL; |
| #[cfg(not(target_os = "redox"))] |
| ECHOPRT; |
| #[cfg(not(target_os = "redox"))] |
| 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; |
| #[cfg(not(target_os = "redox"))] |
| EXTPROC; |
| TOSTOP; |
| #[cfg(not(target_os = "redox"))] |
| FLUSHO; |
| #[cfg(any(target_os = "freebsd", |
| target_os = "dragonfly", |
| target_os = "ios", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] |
| NOKERNINFO; |
| #[cfg(not(target_os = "redox"))] |
| PENDIN; |
| NOFLSH; |
| } |
| } |
| |
| cfg_if!{ |
| if #[cfg(any(target_os = "freebsd", |
| target_os = "dragonfly", |
| target_os = "ios", |
| target_os = "macos", |
| target_os = "netbsd", |
| target_os = "openbsd"))] { |
| /// Get input baud rate (see |
| /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). |
| /// |
| /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. |
| pub fn cfgetispeed(termios: &Termios) -> u32 { |
| let inner_termios = termios.get_libc_termios(); |
| unsafe { libc::cfgetispeed(&*inner_termios) as u32 } |
| } |
| |
| /// Get output baud rate (see |
| /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). |
| /// |
| /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. |
| pub fn cfgetospeed(termios: &Termios) -> u32 { |
| let inner_termios = termios.get_libc_termios(); |
| unsafe { libc::cfgetospeed(&*inner_termios) as u32 } |
| } |
| |
| /// Set input baud rate (see |
| /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)). |
| /// |
| /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure. |
| pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { |
| let inner_termios = unsafe { termios.get_libc_termios_mut() }; |
| let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) }; |
| termios.update_wrapper(); |
| Errno::result(res).map(drop) |
| } |
| |
| /// Set output baud rate (see |
| /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)). |
| /// |
| /// `cfsetospeed()` sets the output baud rate in the given termios structure. |
| pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { |
| let inner_termios = unsafe { termios.get_libc_termios_mut() }; |
| let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) }; |
| termios.update_wrapper(); |
| Errno::result(res).map(drop) |
| } |
| |
| /// Set both the input and output baud rates (see |
| /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). |
| /// |
| /// `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<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { |
| let inner_termios = unsafe { termios.get_libc_termios_mut() }; |
| let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) }; |
| termios.update_wrapper(); |
| Errno::result(res).map(drop) |
| } |
| } else { |
| use std::convert::TryInto; |
| |
| /// Get input baud rate (see |
| /// [cfgetispeed(3p)](https://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 { |
| let inner_termios = termios.get_libc_termios(); |
| unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap() |
| } |
| |
| /// Get output baud rate (see |
| /// [cfgetospeed(3p)](https://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) }.try_into().unwrap() |
| } |
| |
| /// Set input baud rate (see |
| /// [cfsetispeed(3p)](https://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<()> { |
| 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)](https://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<()> { |
| 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)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). |
| /// |
| /// `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) |
| } |
| } |
| } |
| |
| /// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see |
| /// [termios(3)](https://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 { |
| libc::cfmakeraw(inner_termios); |
| } |
| termios.update_wrapper(); |
| } |
| |
| /// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see |
| /// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)). |
| /// |
| /// Note that this is a non-standard function, available on FreeBSD. |
| #[cfg(target_os = "freebsd")] |
| pub fn cfmakesane(termios: &mut Termios) { |
| let inner_termios = unsafe { termios.get_libc_termios_mut() }; |
| unsafe { |
| libc::cfmakesane(inner_termios); |
| } |
| termios.update_wrapper(); |
| } |
| |
| /// Return the configuration of a port |
| /// [tcgetattr(3p)](https://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 = mem::MaybeUninit::uninit(); |
| |
| let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) }; |
| |
| Errno::result(res)?; |
| |
| unsafe { Ok(termios.assume_init().into()) } |
| } |
| |
| /// Set the configuration for a terminal (see |
| /// [tcsetattr(3p)](https://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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)). |
| pub fn tcdrain(fd: RawFd) -> Result<()> { |
| Errno::result(unsafe { libc::tcdrain(fd) }).map(drop) |
| } |
| |
| /// Suspend or resume the transmission or reception of data (see |
| /// [tcflow(3p)](https://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 { libc::tcflow(fd, action as c_int) }).map(drop) |
| } |
| |
| /// Discard data in the output or input queue (see |
| /// [tcflush(3p)](https://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 { libc::tcflush(fd, action as c_int) }).map(drop) |
| } |
| |
| /// Send a break for a specific duration (see |
| /// [tcsendbreak(3p)](https://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) |
| } |
| |
| /// Get the session controlled by the given terminal (see |
| /// [tcgetsid(3)](https://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) |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn try_from() { |
| assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0)); |
| assert!(BaudRate::try_from(999999999).is_err()); |
| } |
| } |