nix/sys/
termios.rs

1//! An interface for controlling asynchronous communication ports
2//!
3//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
4//! underlying types are all implemented in libc for most platforms and either wrapped in safer
5//! types here or exported directly.
6//!
7//! If you are unfamiliar with the `termios` API, you should first read the
8//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
9//! then come back to understand how `nix` safely wraps it.
10//!
11//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
12//! As this interface is not used with high-bandwidth information, this should be fine in most
13//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
14//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
15//! This means that when crossing the FFI interface to the underlying C library, data is first
16//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
17//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
18//! relatively small across all platforms (on the order of 32-64 bytes).
19//!
20//! The following examples highlight some of the API use cases such that users coming from using C
21//! or reading the standard documentation will understand how to use the safe API exposed here.
22//!
23//! Example disabling processing of the end-of-file control character:
24//!
25//! ```
26//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
27//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
28//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
29//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
30//! ```
31//!
32//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
33//! an interface for working with bitfields that is similar to working with the raw unsigned
34//! integer types but offers type safety because of the internal checking that values will always
35//! be a valid combination of the defined flags.
36//!
37//! An example showing some of the basic operations for interacting with the control flags:
38//!
39//! ```
40//! # use self::nix::sys::termios::{ControlFlags, Termios};
41//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
42//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
43//! termios.control_flags |= ControlFlags::CS5;
44//! ```
45//!
46//! # Baud rates
47//!
48//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
49//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
50//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
51//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
52//! conventions:
53//!
54//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
55//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
56//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
57//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
58//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
59//!
60//! The most common use case of specifying a baud rate using the enum will work the same across
61//! platforms:
62//!
63//! ```rust
64//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
65//! # fn main() {
66//! # let mut t: Termios = unsafe { std::mem::zeroed() };
67//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
68//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
69//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
70//! # }
71//! ```
72//!
73//! Additionally round-tripping baud rates is consistent across platforms:
74//!
75//! ```rust
76//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
77//! # fn main() {
78//! # let mut t: Termios = unsafe { std::mem::zeroed() };
79//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
80//! let speed = cfgetispeed(&t);
81//! assert_eq!(speed, cfgetospeed(&t));
82//! cfsetispeed(&mut t, speed).unwrap();
83//! # }
84//! ```
85//!
86//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
87//!
88#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
89                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
90            doc = " ```rust,ignore")]
91#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
92                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
93            doc = " ```rust")]
94//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
95//! # fn main() {
96//! # let mut t: Termios = unsafe { std::mem::zeroed() };
97//! # cfsetspeed(&mut t, BaudRate::B9600);
98//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
99//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
100//! # }
101//! ```
102//!
103//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
104//!
105#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
106                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
107            doc = " ```rust")]
108#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
109                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
110            doc = " ```rust,ignore")]
111//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
112//! # fn main() {
113//! # let mut t: Termios = unsafe { std::mem::zeroed() };
114//! # cfsetspeed(&mut t, 9600u32);
115//! assert_eq!(cfgetispeed(&t), 9600u32);
116//! assert_eq!(cfgetospeed(&t), 9600u32);
117//! # }
118//! ```
119//!
120//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
121//!
122#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
123                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
124            doc = " ```rust")]
125#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
126                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
127            doc = " ```rust,ignore")]
128//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
129//! # fn main() {
130//! # let mut t: Termios = unsafe { std::mem::zeroed() };
131//! # cfsetspeed(&mut t, 9600u32);
132//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
133//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
134//! # }
135//! ```
136//!
137//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
138//! by specifying baud rates directly using `u32`s:
139//!
140#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
141                target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
142            doc = " ```rust")]
143#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
144                    target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
145            doc = " ```rust,ignore")]
146//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
147//! # fn main() {
148//! # let mut t: Termios = unsafe { std::mem::zeroed() };
149//! cfsetispeed(&mut t, 9600u32);
150//! cfsetospeed(&mut t, 9600u32);
151//! cfsetspeed(&mut t, 9600u32);
152//! # }
153//! ```
154use cfg_if::cfg_if;
155use crate::Result;
156use crate::errno::Errno;
157use libc::{self, c_int, tcflag_t};
158use std::cell::{Ref, RefCell};
159use std::convert::From;
160use std::mem;
161use std::os::unix::io::RawFd;
162
163#[cfg(feature = "process")]
164use crate::unistd::Pid;
165
166/// Stores settings for the termios API
167///
168/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
169/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
170/// an open port using `tcgetattr()`.
171#[derive(Clone, Debug, Eq, PartialEq)]
172pub struct Termios {
173    inner: RefCell<libc::termios>,
174    /// Input mode flags (see `termios.c_iflag` documentation)
175    pub input_flags: InputFlags,
176    /// Output mode flags (see `termios.c_oflag` documentation)
177    pub output_flags: OutputFlags,
178    /// Control mode flags (see `termios.c_cflag` documentation)
179    pub control_flags: ControlFlags,
180    /// Local mode flags (see `termios.c_lflag` documentation)
181    pub local_flags: LocalFlags,
182    /// Control characters (see `termios.c_cc` documentation)
183    pub control_chars: [libc::cc_t; NCCS],
184}
185
186impl Termios {
187    /// Exposes an immutable reference to the underlying `libc::termios` data structure.
188    ///
189    /// This is not part of `nix`'s public API because it requires additional work to maintain type
190    /// safety.
191    pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
192        {
193            let mut termios = self.inner.borrow_mut();
194            termios.c_iflag = self.input_flags.bits();
195            termios.c_oflag = self.output_flags.bits();
196            termios.c_cflag = self.control_flags.bits();
197            termios.c_lflag = self.local_flags.bits();
198            termios.c_cc = self.control_chars;
199        }
200        self.inner.borrow()
201    }
202
203    /// Exposes the inner `libc::termios` datastore within `Termios`.
204    ///
205    /// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
206    /// not automatically update the safe wrapper type around it. In this case it should also be
207    /// paired with a call to `update_wrapper()` so that the wrapper-type and internal
208    /// representation stay consistent.
209    pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
210        {
211            let mut termios = self.inner.borrow_mut();
212            termios.c_iflag = self.input_flags.bits();
213            termios.c_oflag = self.output_flags.bits();
214            termios.c_cflag = self.control_flags.bits();
215            termios.c_lflag = self.local_flags.bits();
216            termios.c_cc = self.control_chars;
217        }
218        self.inner.as_ptr()
219    }
220
221    /// Updates the wrapper values from the internal `libc::termios` data structure.
222    pub(crate) fn update_wrapper(&mut self) {
223        let termios = *self.inner.borrow_mut();
224        self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
225        self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
226        self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
227        self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
228        self.control_chars = termios.c_cc;
229    }
230}
231
232impl From<libc::termios> for Termios {
233    fn from(termios: libc::termios) -> Self {
234        Termios {
235            inner: RefCell::new(termios),
236            input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
237            output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
238            control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
239            local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
240            control_chars: termios.c_cc,
241        }
242    }
243}
244
245impl From<Termios> for libc::termios {
246    fn from(termios: Termios) -> Self {
247        termios.inner.into_inner()
248    }
249}
250
251libc_enum!{
252    /// Baud rates supported by the system.
253    ///
254    /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
255    /// enum.
256    ///
257    /// B0 is special and will disable the port.
258    #[cfg_attr(all(any(target_os = "haiku"), target_pointer_width = "64"), repr(u8))]
259    #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
260    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos", target_os = "haiku"), target_pointer_width = "64")), repr(u32))]
261    #[non_exhaustive]
262    pub enum BaudRate {
263        B0,
264        B50,
265        B75,
266        B110,
267        B134,
268        B150,
269        B200,
270        B300,
271        B600,
272        B1200,
273        B1800,
274        B2400,
275        B4800,
276        #[cfg(any(target_os = "dragonfly",
277                target_os = "freebsd",
278                target_os = "macos",
279                target_os = "netbsd",
280                target_os = "openbsd"))]
281        #[cfg_attr(docsrs, doc(cfg(all())))]
282        B7200,
283        B9600,
284        #[cfg(any(target_os = "dragonfly",
285                target_os = "freebsd",
286                target_os = "macos",
287                target_os = "netbsd",
288                target_os = "openbsd"))]
289        #[cfg_attr(docsrs, doc(cfg(all())))]
290        B14400,
291        B19200,
292        #[cfg(any(target_os = "dragonfly",
293                target_os = "freebsd",
294                target_os = "macos",
295                target_os = "netbsd",
296                target_os = "openbsd"))]
297        #[cfg_attr(docsrs, doc(cfg(all())))]
298        B28800,
299        B38400,
300        B57600,
301        #[cfg(any(target_os = "dragonfly",
302                target_os = "freebsd",
303                target_os = "macos",
304                target_os = "netbsd",
305                target_os = "openbsd"))]
306        #[cfg_attr(docsrs, doc(cfg(all())))]
307        B76800,
308        B115200,
309        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
310        #[cfg_attr(docsrs, doc(cfg(all())))]
311        B153600,
312        B230400,
313        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
314        #[cfg_attr(docsrs, doc(cfg(all())))]
315        B307200,
316        #[cfg(any(target_os = "android",
317                  target_os = "freebsd",
318                  target_os = "illumos",
319                  target_os = "linux",
320                  target_os = "netbsd",
321                  target_os = "solaris"))]
322        #[cfg_attr(docsrs, doc(cfg(all())))]
323        B460800,
324        #[cfg(any(target_os = "android", target_os = "linux"))]
325        #[cfg_attr(docsrs, doc(cfg(all())))]
326        B500000,
327        #[cfg(any(target_os = "android", target_os = "linux"))]
328        #[cfg_attr(docsrs, doc(cfg(all())))]
329        B576000,
330        #[cfg(any(target_os = "android",
331                  target_os = "freebsd",
332                  target_os = "illumos",
333                  target_os = "linux",
334                  target_os = "netbsd",
335                  target_os = "solaris"))]
336        #[cfg_attr(docsrs, doc(cfg(all())))]
337        B921600,
338        #[cfg(any(target_os = "android", target_os = "linux"))]
339        #[cfg_attr(docsrs, doc(cfg(all())))]
340        B1000000,
341        #[cfg(any(target_os = "android", target_os = "linux"))]
342        #[cfg_attr(docsrs, doc(cfg(all())))]
343        B1152000,
344        #[cfg(any(target_os = "android", target_os = "linux"))]
345        #[cfg_attr(docsrs, doc(cfg(all())))]
346        B1500000,
347        #[cfg(any(target_os = "android", target_os = "linux"))]
348        #[cfg_attr(docsrs, doc(cfg(all())))]
349        B2000000,
350        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
351        #[cfg_attr(docsrs, doc(cfg(all())))]
352        B2500000,
353        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
354        #[cfg_attr(docsrs, doc(cfg(all())))]
355        B3000000,
356        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
357        #[cfg_attr(docsrs, doc(cfg(all())))]
358        B3500000,
359        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
360        #[cfg_attr(docsrs, doc(cfg(all())))]
361        B4000000,
362    }
363    impl TryFrom<libc::speed_t>
364}
365
366#[cfg(any(target_os = "freebsd",
367          target_os = "dragonfly",
368          target_os = "ios",
369          target_os = "macos",
370          target_os = "netbsd",
371          target_os = "openbsd"))]
372impl From<BaudRate> for u32 {
373    fn from(b: BaudRate) -> u32 {
374        b as u32
375    }
376}
377
378#[cfg(target_os = "haiku")]
379impl From<BaudRate> for u8 {
380    fn from(b: BaudRate) -> u8 {
381        b as u8
382    }
383}
384
385
386// TODO: Add TCSASOFT, which will require treating this as a bitfield.
387libc_enum! {
388    /// Specify when a port configuration change should occur.
389    ///
390    /// Used as an argument to `tcsetattr()`
391    #[repr(i32)]
392    #[non_exhaustive]
393    pub enum SetArg {
394        /// The change will occur immediately
395        TCSANOW,
396        /// The change occurs after all output has been written
397        TCSADRAIN,
398        /// Same as `TCSADRAIN`, but will also flush the input buffer
399        TCSAFLUSH,
400    }
401}
402
403libc_enum! {
404    /// Specify a combination of the input and output buffers to flush
405    ///
406    /// Used as an argument to `tcflush()`.
407    #[repr(i32)]
408    #[non_exhaustive]
409    pub enum FlushArg {
410        /// Flush data that was received but not read
411        TCIFLUSH,
412        /// Flush data written but not transmitted
413        TCOFLUSH,
414        /// Flush both received data not read and written data not transmitted
415        TCIOFLUSH,
416    }
417}
418
419libc_enum! {
420    /// Specify how transmission flow should be altered
421    ///
422    /// Used as an argument to `tcflow()`.
423    #[repr(i32)]
424    #[non_exhaustive]
425    pub enum FlowArg {
426        /// Suspend transmission
427        TCOOFF,
428        /// Resume transmission
429        TCOON,
430        /// Transmit a STOP character, which should disable a connected terminal device
431        TCIOFF,
432        /// Transmit a START character, which should re-enable a connected terminal device
433        TCION,
434    }
435}
436
437// TODO: Make this usable directly as a slice index.
438#[cfg(not(target_os = "haiku"))]
439libc_enum! {
440    /// Indices into the `termios.c_cc` array for special characters.
441    #[repr(usize)]
442    #[non_exhaustive]
443    pub enum SpecialCharacterIndices {
444        VDISCARD,
445        #[cfg(any(target_os = "dragonfly",
446                target_os = "freebsd",
447                target_os = "illumos",
448                target_os = "macos",
449                target_os = "netbsd",
450                target_os = "openbsd",
451                target_os = "solaris"))]
452        #[cfg_attr(docsrs, doc(cfg(all())))]
453        VDSUSP,
454        VEOF,
455        VEOL,
456        VEOL2,
457        VERASE,
458        #[cfg(any(target_os = "dragonfly",
459                  target_os = "freebsd",
460                  target_os = "illumos",
461                  target_os = "solaris"))]
462        #[cfg_attr(docsrs, doc(cfg(all())))]
463        VERASE2,
464        VINTR,
465        VKILL,
466        VLNEXT,
467        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
468                target_os = "illumos", target_os = "solaris")))]
469        #[cfg_attr(docsrs, doc(cfg(all())))]
470        VMIN,
471        VQUIT,
472        VREPRINT,
473        VSTART,
474        #[cfg(any(target_os = "dragonfly",
475                target_os = "freebsd",
476                target_os = "illumos",
477                target_os = "macos",
478                target_os = "netbsd",
479                target_os = "openbsd",
480                target_os = "solaris"))]
481        #[cfg_attr(docsrs, doc(cfg(all())))]
482        VSTATUS,
483        VSTOP,
484        VSUSP,
485        #[cfg(target_os = "linux")]
486        #[cfg_attr(docsrs, doc(cfg(all())))]
487        VSWTC,
488        #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
489        #[cfg_attr(docsrs, doc(cfg(all())))]
490        VSWTCH,
491        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
492                target_os = "illumos", target_os = "solaris")))]
493        #[cfg_attr(docsrs, doc(cfg(all())))]
494        VTIME,
495        VWERASE,
496        #[cfg(target_os = "dragonfly")]
497        #[cfg_attr(docsrs, doc(cfg(all())))]
498        VCHECKPT,
499    }
500}
501
502#[cfg(any(all(target_os = "linux", target_arch = "sparc64"),
503        target_os = "illumos", target_os = "solaris"))]
504impl SpecialCharacterIndices {
505    pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
506    pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
507}
508
509pub use libc::NCCS;
510#[cfg(any(target_os = "android",
511          target_os = "dragonfly",
512          target_os = "freebsd",
513          target_os = "linux",
514          target_os = "macos",
515          target_os = "netbsd",
516          target_os = "openbsd"))]
517#[cfg_attr(docsrs, doc(cfg(all())))]
518pub use libc::_POSIX_VDISABLE;
519
520libc_bitflags! {
521    /// Flags for configuring the input mode of a terminal
522    pub struct InputFlags: tcflag_t {
523        IGNBRK;
524        BRKINT;
525        IGNPAR;
526        PARMRK;
527        INPCK;
528        ISTRIP;
529        INLCR;
530        IGNCR;
531        ICRNL;
532        IXON;
533        IXOFF;
534        #[cfg(not(target_os = "redox"))]
535        #[cfg_attr(docsrs, doc(cfg(all())))]
536        IXANY;
537        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
538        #[cfg_attr(docsrs, doc(cfg(all())))]
539        IMAXBEL;
540        #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
541        #[cfg_attr(docsrs, doc(cfg(all())))]
542        IUTF8;
543    }
544}
545
546libc_bitflags! {
547    /// Flags for configuring the output mode of a terminal
548    pub struct OutputFlags: tcflag_t {
549        OPOST;
550        #[cfg(any(target_os = "android",
551                  target_os = "haiku",
552                  target_os = "linux",
553                  target_os = "openbsd"))]
554        #[cfg_attr(docsrs, doc(cfg(all())))]
555        OLCUC;
556        ONLCR;
557        OCRNL as tcflag_t;
558        ONOCR as tcflag_t;
559        ONLRET as tcflag_t;
560        #[cfg(any(target_os = "android",
561                  target_os = "haiku",
562                  target_os = "ios",
563                  target_os = "linux",
564                  target_os = "macos"))]
565        #[cfg_attr(docsrs, doc(cfg(all())))]
566        OFILL as tcflag_t;
567        #[cfg(any(target_os = "android",
568                  target_os = "haiku",
569                  target_os = "ios",
570                  target_os = "linux",
571                  target_os = "macos"))]
572        #[cfg_attr(docsrs, doc(cfg(all())))]
573        OFDEL as tcflag_t;
574        #[cfg(any(target_os = "android",
575                  target_os = "haiku",
576                  target_os = "ios",
577                  target_os = "linux",
578                  target_os = "macos"))]
579        #[cfg_attr(docsrs, doc(cfg(all())))]
580        NL0 as tcflag_t;
581        #[cfg(any(target_os = "android",
582                  target_os = "haiku",
583                  target_os = "ios",
584                  target_os = "linux",
585                  target_os = "macos"))]
586        #[cfg_attr(docsrs, doc(cfg(all())))]
587        NL1 as tcflag_t;
588        #[cfg(any(target_os = "android",
589                  target_os = "haiku",
590                  target_os = "ios",
591                  target_os = "linux",
592                  target_os = "macos"))]
593        #[cfg_attr(docsrs, doc(cfg(all())))]
594        CR0 as tcflag_t;
595        #[cfg(any(target_os = "android",
596                  target_os = "haiku",
597                  target_os = "ios",
598                  target_os = "linux",
599                  target_os = "macos"))]
600        #[cfg_attr(docsrs, doc(cfg(all())))]
601        CR1 as tcflag_t;
602        #[cfg(any(target_os = "android",
603                  target_os = "haiku",
604                  target_os = "ios",
605                  target_os = "linux",
606                  target_os = "macos"))]
607        #[cfg_attr(docsrs, doc(cfg(all())))]
608        CR2 as tcflag_t;
609        #[cfg(any(target_os = "android",
610                  target_os = "haiku",
611                  target_os = "ios",
612                  target_os = "linux",
613                  target_os = "macos"))]
614        #[cfg_attr(docsrs, doc(cfg(all())))]
615        CR3 as tcflag_t;
616        #[cfg(any(target_os = "android",
617                  target_os = "freebsd",
618                  target_os = "haiku",
619                  target_os = "ios",
620                  target_os = "linux",
621                  target_os = "macos"))]
622        #[cfg_attr(docsrs, doc(cfg(all())))]
623        TAB0 as tcflag_t;
624        #[cfg(any(target_os = "android",
625                  target_os = "haiku",
626                  target_os = "ios",
627                  target_os = "linux",
628                  target_os = "macos"))]
629        #[cfg_attr(docsrs, doc(cfg(all())))]
630        TAB1 as tcflag_t;
631        #[cfg(any(target_os = "android",
632                  target_os = "haiku",
633                  target_os = "ios",
634                  target_os = "linux",
635                  target_os = "macos"))]
636        #[cfg_attr(docsrs, doc(cfg(all())))]
637        TAB2 as tcflag_t;
638        #[cfg(any(target_os = "android",
639                  target_os = "freebsd",
640                  target_os = "haiku",
641                  target_os = "ios",
642                  target_os = "linux",
643                  target_os = "macos"))]
644        #[cfg_attr(docsrs, doc(cfg(all())))]
645        TAB3 as tcflag_t;
646        #[cfg(any(target_os = "android", target_os = "linux"))]
647        #[cfg_attr(docsrs, doc(cfg(all())))]
648        XTABS;
649        #[cfg(any(target_os = "android",
650                  target_os = "haiku",
651                  target_os = "ios",
652                  target_os = "linux",
653                  target_os = "macos"))]
654        #[cfg_attr(docsrs, doc(cfg(all())))]
655        BS0 as tcflag_t;
656        #[cfg(any(target_os = "android",
657                  target_os = "haiku",
658                  target_os = "ios",
659                  target_os = "linux",
660                  target_os = "macos"))]
661        #[cfg_attr(docsrs, doc(cfg(all())))]
662        BS1 as tcflag_t;
663        #[cfg(any(target_os = "android",
664                  target_os = "haiku",
665                  target_os = "ios",
666                  target_os = "linux",
667                  target_os = "macos"))]
668        #[cfg_attr(docsrs, doc(cfg(all())))]
669        VT0 as tcflag_t;
670        #[cfg(any(target_os = "android",
671                  target_os = "haiku",
672                  target_os = "ios",
673                  target_os = "linux",
674                  target_os = "macos"))]
675        #[cfg_attr(docsrs, doc(cfg(all())))]
676        VT1 as tcflag_t;
677        #[cfg(any(target_os = "android",
678                  target_os = "haiku",
679                  target_os = "ios",
680                  target_os = "linux",
681                  target_os = "macos"))]
682        #[cfg_attr(docsrs, doc(cfg(all())))]
683        FF0 as tcflag_t;
684        #[cfg(any(target_os = "android",
685                  target_os = "haiku",
686                  target_os = "ios",
687                  target_os = "linux",
688                  target_os = "macos"))]
689        #[cfg_attr(docsrs, doc(cfg(all())))]
690        FF1 as tcflag_t;
691        #[cfg(any(target_os = "freebsd",
692                  target_os = "dragonfly",
693                  target_os = "ios",
694                  target_os = "macos",
695                  target_os = "netbsd",
696                  target_os = "openbsd"))]
697        #[cfg_attr(docsrs, doc(cfg(all())))]
698        OXTABS;
699        #[cfg(any(target_os = "freebsd",
700                  target_os = "dragonfly",
701                  target_os = "macos",
702                  target_os = "netbsd",
703                  target_os = "openbsd"))]
704        #[cfg_attr(docsrs, doc(cfg(all())))]
705        ONOEOT as tcflag_t;
706
707        // Bitmasks for use with OutputFlags to select specific settings
708        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
709        // is resolved.
710
711        #[cfg(any(target_os = "android",
712                  target_os = "haiku",
713                  target_os = "ios",
714                  target_os = "linux",
715                  target_os = "macos"))]
716        #[cfg_attr(docsrs, doc(cfg(all())))]
717        NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
718        #[cfg(any(target_os = "android",
719                  target_os = "haiku",
720                  target_os = "ios",
721                  target_os = "linux",
722                  target_os = "macos"))]
723        #[cfg_attr(docsrs, doc(cfg(all())))]
724        CRDLY as tcflag_t;
725        #[cfg(any(target_os = "android",
726                  target_os = "freebsd",
727                  target_os = "haiku",
728                  target_os = "ios",
729                  target_os = "linux",
730                  target_os = "macos"))]
731        #[cfg_attr(docsrs, doc(cfg(all())))]
732        TABDLY as tcflag_t;
733        #[cfg(any(target_os = "android",
734                  target_os = "haiku",
735                  target_os = "ios",
736                  target_os = "linux",
737                  target_os = "macos"))]
738        #[cfg_attr(docsrs, doc(cfg(all())))]
739        BSDLY as tcflag_t;
740        #[cfg(any(target_os = "android",
741                  target_os = "haiku",
742                  target_os = "ios",
743                  target_os = "linux",
744                  target_os = "macos"))]
745        #[cfg_attr(docsrs, doc(cfg(all())))]
746        VTDLY as tcflag_t;
747        #[cfg(any(target_os = "android",
748                  target_os = "haiku",
749                  target_os = "ios",
750                  target_os = "linux",
751                  target_os = "macos"))]
752        #[cfg_attr(docsrs, doc(cfg(all())))]
753        FFDLY as tcflag_t;
754    }
755}
756
757libc_bitflags! {
758    /// Flags for setting the control mode of a terminal
759    pub struct ControlFlags: tcflag_t {
760        #[cfg(any(target_os = "dragonfly",
761                  target_os = "freebsd",
762                  target_os = "ios",
763                  target_os = "macos",
764                  target_os = "netbsd",
765                  target_os = "openbsd"))]
766        #[cfg_attr(docsrs, doc(cfg(all())))]
767        CIGNORE;
768        CS5;
769        CS6;
770        CS7;
771        CS8;
772        CSTOPB;
773        CREAD;
774        PARENB;
775        PARODD;
776        HUPCL;
777        CLOCAL;
778        #[cfg(not(target_os = "redox"))]
779        #[cfg_attr(docsrs, doc(cfg(all())))]
780        CRTSCTS;
781        #[cfg(any(target_os = "android", target_os = "linux"))]
782        #[cfg_attr(docsrs, doc(cfg(all())))]
783        CBAUD;
784        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
785        #[cfg_attr(docsrs, doc(cfg(all())))]
786        CMSPAR;
787        #[cfg(any(target_os = "android",
788                  all(target_os = "linux",
789                      not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
790        CIBAUD;
791        #[cfg(any(target_os = "android", target_os = "linux"))]
792        #[cfg_attr(docsrs, doc(cfg(all())))]
793        CBAUDEX;
794        #[cfg(any(target_os = "dragonfly",
795                  target_os = "freebsd",
796                  target_os = "macos",
797                  target_os = "netbsd",
798                  target_os = "openbsd"))]
799        #[cfg_attr(docsrs, doc(cfg(all())))]
800        MDMBUF;
801        #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
802        #[cfg_attr(docsrs, doc(cfg(all())))]
803        CHWFLOW;
804        #[cfg(any(target_os = "dragonfly",
805                  target_os = "freebsd",
806                  target_os = "netbsd",
807                  target_os = "openbsd"))]
808        #[cfg_attr(docsrs, doc(cfg(all())))]
809        CCTS_OFLOW;
810        #[cfg(any(target_os = "dragonfly",
811                  target_os = "freebsd",
812                  target_os = "netbsd",
813                  target_os = "openbsd"))]
814        #[cfg_attr(docsrs, doc(cfg(all())))]
815        CRTS_IFLOW;
816        #[cfg(any(target_os = "dragonfly",
817                  target_os = "freebsd"))]
818        #[cfg_attr(docsrs, doc(cfg(all())))]
819        CDTR_IFLOW;
820        #[cfg(any(target_os = "dragonfly",
821                  target_os = "freebsd"))]
822        #[cfg_attr(docsrs, doc(cfg(all())))]
823        CDSR_OFLOW;
824        #[cfg(any(target_os = "dragonfly",
825                  target_os = "freebsd"))]
826        #[cfg_attr(docsrs, doc(cfg(all())))]
827        CCAR_OFLOW;
828
829        // Bitmasks for use with ControlFlags to select specific settings
830        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
831        // is resolved.
832
833        CSIZE;
834    }
835}
836
837libc_bitflags! {
838    /// Flags for setting any local modes
839    pub struct LocalFlags: tcflag_t {
840        #[cfg(not(target_os = "redox"))]
841        #[cfg_attr(docsrs, doc(cfg(all())))]
842        ECHOKE;
843        ECHOE;
844        ECHOK;
845        ECHO;
846        ECHONL;
847        #[cfg(not(target_os = "redox"))]
848        #[cfg_attr(docsrs, doc(cfg(all())))]
849        ECHOPRT;
850        #[cfg(not(target_os = "redox"))]
851        #[cfg_attr(docsrs, doc(cfg(all())))]
852        ECHOCTL;
853        ISIG;
854        ICANON;
855        #[cfg(any(target_os = "freebsd",
856                  target_os = "dragonfly",
857                  target_os = "ios",
858                  target_os = "macos",
859                  target_os = "netbsd",
860                  target_os = "openbsd"))]
861        #[cfg_attr(docsrs, doc(cfg(all())))]
862        ALTWERASE;
863        IEXTEN;
864        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
865        #[cfg_attr(docsrs, doc(cfg(all())))]
866        EXTPROC;
867        TOSTOP;
868        #[cfg(not(target_os = "redox"))]
869        #[cfg_attr(docsrs, doc(cfg(all())))]
870        FLUSHO;
871        #[cfg(any(target_os = "freebsd",
872                  target_os = "dragonfly",
873                  target_os = "ios",
874                  target_os = "macos",
875                  target_os = "netbsd",
876                  target_os = "openbsd"))]
877        #[cfg_attr(docsrs, doc(cfg(all())))]
878        NOKERNINFO;
879        #[cfg(not(target_os = "redox"))]
880        #[cfg_attr(docsrs, doc(cfg(all())))]
881        PENDIN;
882        NOFLSH;
883    }
884}
885
886cfg_if!{
887    if #[cfg(any(target_os = "freebsd",
888                 target_os = "dragonfly",
889                 target_os = "ios",
890                 target_os = "macos",
891                 target_os = "netbsd",
892                 target_os = "openbsd"))] {
893        /// Get input baud rate (see
894        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
895        ///
896        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
897        // The cast is not unnecessary on all platforms.
898        #[allow(clippy::unnecessary_cast)]
899        pub fn cfgetispeed(termios: &Termios) -> u32 {
900            let inner_termios = termios.get_libc_termios();
901            unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
902        }
903
904        /// Get output baud rate (see
905        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
906        ///
907        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
908        // The cast is not unnecessary on all platforms.
909        #[allow(clippy::unnecessary_cast)]
910        pub fn cfgetospeed(termios: &Termios) -> u32 {
911            let inner_termios = termios.get_libc_termios();
912            unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
913        }
914
915        /// Set input baud rate (see
916        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
917        ///
918        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
919        pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
920            let inner_termios = unsafe { termios.get_libc_termios_mut() };
921            let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
922            termios.update_wrapper();
923            Errno::result(res).map(drop)
924        }
925
926        /// Set output baud rate (see
927        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
928        ///
929        /// `cfsetospeed()` sets the output baud rate in the given termios structure.
930        pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
931            let inner_termios = unsafe { termios.get_libc_termios_mut() };
932            let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
933            termios.update_wrapper();
934            Errno::result(res).map(drop)
935        }
936
937        /// Set both the input and output baud rates (see
938        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
939        ///
940        /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
941        /// this is part of the 4.4BSD standard and not part of POSIX.
942        pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
943            let inner_termios = unsafe { termios.get_libc_termios_mut() };
944            let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
945            termios.update_wrapper();
946            Errno::result(res).map(drop)
947        }
948    } else {
949        use std::convert::TryInto;
950
951        /// Get input baud rate (see
952        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
953        ///
954        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
955        pub fn cfgetispeed(termios: &Termios) -> BaudRate {
956            let inner_termios = termios.get_libc_termios();
957            unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
958        }
959
960        /// Get output baud rate (see
961        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
962        ///
963        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
964        pub fn cfgetospeed(termios: &Termios) -> BaudRate {
965            let inner_termios = termios.get_libc_termios();
966            unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
967        }
968
969        /// Set input baud rate (see
970        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
971        ///
972        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
973        pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
974            let inner_termios = unsafe { termios.get_libc_termios_mut() };
975            let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
976            termios.update_wrapper();
977            Errno::result(res).map(drop)
978        }
979
980        /// Set output baud rate (see
981        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
982        ///
983        /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
984        pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
985            let inner_termios = unsafe { termios.get_libc_termios_mut() };
986            let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
987            termios.update_wrapper();
988            Errno::result(res).map(drop)
989        }
990
991        /// Set both the input and output baud rates (see
992        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
993        ///
994        /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
995        /// this is part of the 4.4BSD standard and not part of POSIX.
996        #[cfg(not(target_os = "haiku"))]
997        pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
998            let inner_termios = unsafe { termios.get_libc_termios_mut() };
999            let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
1000            termios.update_wrapper();
1001            Errno::result(res).map(drop)
1002        }
1003    }
1004}
1005
1006/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
1007/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
1008///
1009/// `cfmakeraw()` configures the termios structure such that input is available character-by-
1010/// character, echoing is disabled, and all special input and output processing is disabled. Note
1011/// that this is a non-standard function, but is available on Linux and BSDs.
1012pub fn cfmakeraw(termios: &mut Termios) {
1013    let inner_termios = unsafe { termios.get_libc_termios_mut() };
1014    unsafe {
1015        libc::cfmakeraw(inner_termios);
1016    }
1017    termios.update_wrapper();
1018}
1019
1020/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
1021/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
1022///
1023/// Note that this is a non-standard function, available on FreeBSD.
1024#[cfg(target_os = "freebsd")]
1025#[cfg_attr(docsrs, doc(cfg(all())))]
1026pub fn cfmakesane(termios: &mut Termios) {
1027    let inner_termios = unsafe { termios.get_libc_termios_mut() };
1028    unsafe {
1029        libc::cfmakesane(inner_termios);
1030    }
1031    termios.update_wrapper();
1032}
1033
1034/// Return the configuration of a port
1035/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
1036///
1037/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
1038/// this structure *will not* reconfigure the port, instead the modifications should be done to
1039/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
1040pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
1041    let mut termios = mem::MaybeUninit::uninit();
1042
1043    let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
1044
1045    Errno::result(res)?;
1046
1047    unsafe { Ok(termios.assume_init().into()) }
1048}
1049
1050/// Set the configuration for a terminal (see
1051/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
1052///
1053/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
1054/// takes affect at a time specified by `actions`. Note that this function may return success if
1055/// *any* of the parameters were successfully set, not only if all were set successfully.
1056pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
1057    let inner_termios = termios.get_libc_termios();
1058    Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
1059}
1060
1061/// Block until all output data is written (see
1062/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
1063pub fn tcdrain(fd: RawFd) -> Result<()> {
1064    Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
1065}
1066
1067/// Suspend or resume the transmission or reception of data (see
1068/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
1069///
1070/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
1071/// depending on the value of `action`.
1072pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
1073    Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
1074}
1075
1076/// Discard data in the output or input queue (see
1077/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
1078///
1079/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
1080/// depending on the value of `action`.
1081pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
1082    Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
1083}
1084
1085/// Send a break for a specific duration (see
1086/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
1087///
1088/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
1089/// of zero-valued bits for an implementation-defined duration.
1090pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
1091    Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
1092}
1093
1094feature! {
1095#![feature = "process"]
1096/// Get the session controlled by the given terminal (see
1097/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
1098pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
1099    let res = unsafe { libc::tcgetsid(fd) };
1100
1101    Errno::result(res).map(Pid::from_raw)
1102}
1103}
1104
1105#[cfg(test)]
1106mod test {
1107    use super::*;
1108    use std::convert::TryFrom;
1109
1110    #[test]
1111    fn try_from() {
1112        assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
1113        #[cfg(not(target_os = "haiku"))]
1114        BaudRate::try_from(999999999).expect_err("assertion failed");
1115        #[cfg(target_os = "haiku")]
1116        BaudRate::try_from(99).expect_err("assertion failed");
1117    }
1118}