//! 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).unwrap(); //! cfsetospeed(&mut t, BaudRate::B9600).unwrap(); //! cfsetspeed(&mut t, BaudRate::B9600).unwrap(); //! # } //! ``` //! //! 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).unwrap(); //! let speed = cfgetispeed(&t); //! assert_eq!(speed, cfgetospeed(&t)); //! cfsetispeed(&mut t, speed).unwrap(); //! # } //! ``` //! //! 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 crate::errno::Errno; use crate::Result; use cfg_if::cfg_if; use libc::{self, c_int, tcflag_t}; use std::cell::{Ref, RefCell}; use std::convert::From; use std::mem; use std::os::unix::io::{AsFd, AsRawFd}; #[cfg(feature = "process")] 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, /// 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], /// Line discipline (see `termios.c_line` documentation) #[cfg(any(target_os = "linux", target_os = "android",))] pub line_discipline: libc::cc_t, /// Line discipline (see `termios.c_line` documentation) #[cfg(target_os = "haiku")] pub line_discipline: libc::c_char, } 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 { { 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; #[cfg(any( target_os = "linux", target_os = "android", target_os = "haiku", ))] { termios.c_line = self.line_discipline; } } 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; #[cfg(any( target_os = "linux", target_os = "android", target_os = "haiku", ))] { termios.c_line = self.line_discipline; } } 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_retain(termios.c_cflag); self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); self.control_chars = termios.c_cc; #[cfg(any( target_os = "linux", target_os = "android", target_os = "haiku", ))] { self.line_discipline = termios.c_line; } } } impl From 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, #[cfg(any( target_os = "linux", target_os = "android", target_os = "haiku", ))] line_discipline: termios.c_line, } } } impl From 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 = "haiku"), target_pointer_width = "64"), repr(u8))] #[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_os = "haiku"), target_pointer_width = "64")), repr(u32))] #[non_exhaustive] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] B7200, B9600, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] B14400, B19200, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] B28800, B38400, B57600, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] B76800, B115200, #[cfg(any(target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] B153600, B230400, #[cfg(any(target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] B307200, #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] B460800, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B500000, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B576000, #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] B921600, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B1000000, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B1152000, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B1500000, #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] B2000000, #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] #[cfg_attr(docsrs, doc(cfg(all())))] B2500000, #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] #[cfg_attr(docsrs, doc(cfg(all())))] B3000000, #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] #[cfg_attr(docsrs, doc(cfg(all())))] B3500000, #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] #[cfg_attr(docsrs, doc(cfg(all())))] B4000000, } impl TryFrom } #[cfg(any( target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd" ))] impl From for u32 { fn from(b: BaudRate) -> u32 { b as u32 } } #[cfg(target_os = "haiku")] impl From for u8 { fn from(b: BaudRate) -> u8 { b as u8 } } // 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)] #[non_exhaustive] 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)] #[non_exhaustive] 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)] #[non_exhaustive] 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. #[cfg(not(target_os = "haiku"))] libc_enum! { /// Indices into the `termios.c_cc` array for special characters. #[repr(usize)] #[non_exhaustive] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] VDSUSP, VEOF, VEOL, VEOL2, VERASE, #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] VERASE2, VINTR, VKILL, VLNEXT, #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), target_os = "illumos", target_os = "solaris")))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] VSTATUS, VSTOP, VSUSP, #[cfg(target_os = "linux")] #[cfg_attr(docsrs, doc(cfg(all())))] VSWTC, #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))] #[cfg_attr(docsrs, doc(cfg(all())))] VSWTCH, #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"), target_os = "illumos", target_os = "solaris")))] #[cfg_attr(docsrs, doc(cfg(all())))] VTIME, VWERASE, #[cfg(target_os = "dragonfly")] #[cfg_attr(docsrs, doc(cfg(all())))] 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 = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd" ))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] IXANY; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] IMAXBEL; #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] OFILL as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] OFDEL as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] NL0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] NL1 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] CR0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] CR1 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] CR2 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] CR3 as tcflag_t; #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] TAB0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] TAB1 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] TAB2 as tcflag_t; #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] TAB3 as tcflag_t; #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] XTABS; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] BS0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] BS1 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] VT0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] VT1 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] FF0 as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] FF1 as tcflag_t; #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] OXTABS; #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] CRDLY as tcflag_t; #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] TABDLY as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] BSDLY as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] VTDLY as tcflag_t; #[cfg(any(target_os = "android", target_os = "haiku", target_os = "ios", target_os = "linux", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] CIGNORE; CS5; CS6; CS7; CS8; CSTOPB; CREAD; PARENB; PARODD; HUPCL; CLOCAL; #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] CRTSCTS; #[cfg(any(target_os = "android", target_os = "linux"))] #[cfg_attr(docsrs, doc(cfg(all())))] CBAUD; #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] CBAUDEX; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] MDMBUF; #[cfg(any(target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] CHWFLOW; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] CCTS_OFLOW; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] CRTS_IFLOW; #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] CDTR_IFLOW; #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] CDSR_OFLOW; #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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"))] #[cfg_attr(docsrs, doc(cfg(all())))] ECHOKE; ECHOE; ECHOK; ECHO; ECHONL; #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] ECHOPRT; #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] ECHOCTL; ISIG; ICANON; #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] ALTWERASE; IEXTEN; #[cfg(not(any(target_os = "redox", target_os = "haiku")))] #[cfg_attr(docsrs, doc(cfg(all())))] EXTPROC; TOSTOP; #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] FLUSHO; #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios", target_os = "macos", target_os = "netbsd", target_os = "openbsd"))] #[cfg_attr(docsrs, doc(cfg(all())))] NOKERNINFO; #[cfg(not(target_os = "redox"))] #[cfg_attr(docsrs, doc(cfg(all())))] 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. // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] 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. // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] 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>(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>(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>(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. #[cfg(not(target_os = "haiku"))] 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")] #[cfg_attr(docsrs, doc(cfg(all())))] 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: Fd) -> Result { let mut termios = mem::MaybeUninit::uninit(); let res = unsafe { libc::tcgetattr(fd.as_fd().as_raw_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: Fd, actions: SetArg, termios: &Termios, ) -> Result<()> { let inner_termios = termios.get_libc_termios(); Errno::result(unsafe { libc::tcsetattr( fd.as_fd().as_raw_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: Fd) -> Result<()> { Errno::result(unsafe { libc::tcdrain(fd.as_fd().as_raw_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: Fd, action: FlowArg) -> Result<()> { Errno::result(unsafe { libc::tcflow(fd.as_fd().as_raw_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: Fd, action: FlushArg) -> Result<()> { Errno::result(unsafe { libc::tcflush(fd.as_fd().as_raw_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: Fd, duration: c_int) -> Result<()> { Errno::result(unsafe { libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration) }) .map(drop) } feature! { #![feature = "process"] /// Get the session controlled by the given terminal (see /// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)). pub fn tcgetsid(fd: Fd) -> Result { let res = unsafe { libc::tcgetsid(fd.as_fd().as_raw_fd()) }; Errno::result(res).map(Pid::from_raw) } } #[cfg(test)] mod test { use super::*; use std::convert::TryFrom; #[test] fn try_from() { assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0)); #[cfg(not(target_os = "haiku"))] BaudRate::try_from(999999999).expect_err("assertion failed"); #[cfg(target_os = "haiku")] BaudRate::try_from(99).expect_err("assertion failed"); } }