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);
68//! cfsetospeed(&mut t, BaudRate::B9600);
69//! cfsetspeed(&mut t, BaudRate::B9600);
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);
80//! let speed = cfgetispeed(&t);
81//! assert_eq!(speed, cfgetospeed(&t));
82//! cfsetispeed(&mut t, speed);
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 = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
259    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
260    #[non_exhaustive]
261    pub enum BaudRate {
262        B0,
263        B50,
264        B75,
265        B110,
266        B134,
267        B150,
268        B200,
269        B300,
270        B600,
271        B1200,
272        B1800,
273        B2400,
274        B4800,
275        #[cfg(any(target_os = "dragonfly",
276                target_os = "freebsd",
277                target_os = "macos",
278                target_os = "netbsd",
279                target_os = "openbsd"))]
280        #[cfg_attr(docsrs, doc(cfg(all())))]
281        B7200,
282        B9600,
283        #[cfg(any(target_os = "dragonfly",
284                target_os = "freebsd",
285                target_os = "macos",
286                target_os = "netbsd",
287                target_os = "openbsd"))]
288        #[cfg_attr(docsrs, doc(cfg(all())))]
289        B14400,
290        B19200,
291        #[cfg(any(target_os = "dragonfly",
292                target_os = "freebsd",
293                target_os = "macos",
294                target_os = "netbsd",
295                target_os = "openbsd"))]
296        #[cfg_attr(docsrs, doc(cfg(all())))]
297        B28800,
298        B38400,
299        B57600,
300        #[cfg(any(target_os = "dragonfly",
301                target_os = "freebsd",
302                target_os = "macos",
303                target_os = "netbsd",
304                target_os = "openbsd"))]
305        #[cfg_attr(docsrs, doc(cfg(all())))]
306        B76800,
307        B115200,
308        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
309        #[cfg_attr(docsrs, doc(cfg(all())))]
310        B153600,
311        B230400,
312        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
313        #[cfg_attr(docsrs, doc(cfg(all())))]
314        B307200,
315        #[cfg(any(target_os = "android",
316                  target_os = "freebsd",
317                  target_os = "illumos",
318                  target_os = "linux",
319                  target_os = "netbsd",
320                  target_os = "solaris"))]
321        #[cfg_attr(docsrs, doc(cfg(all())))]
322        B460800,
323        #[cfg(any(target_os = "android", target_os = "linux"))]
324        #[cfg_attr(docsrs, doc(cfg(all())))]
325        B500000,
326        #[cfg(any(target_os = "android", target_os = "linux"))]
327        #[cfg_attr(docsrs, doc(cfg(all())))]
328        B576000,
329        #[cfg(any(target_os = "android",
330                  target_os = "freebsd",
331                  target_os = "illumos",
332                  target_os = "linux",
333                  target_os = "netbsd",
334                  target_os = "solaris"))]
335        #[cfg_attr(docsrs, doc(cfg(all())))]
336        B921600,
337        #[cfg(any(target_os = "android", target_os = "linux"))]
338        #[cfg_attr(docsrs, doc(cfg(all())))]
339        B1000000,
340        #[cfg(any(target_os = "android", target_os = "linux"))]
341        #[cfg_attr(docsrs, doc(cfg(all())))]
342        B1152000,
343        #[cfg(any(target_os = "android", target_os = "linux"))]
344        #[cfg_attr(docsrs, doc(cfg(all())))]
345        B1500000,
346        #[cfg(any(target_os = "android", target_os = "linux"))]
347        #[cfg_attr(docsrs, doc(cfg(all())))]
348        B2000000,
349        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
350        #[cfg_attr(docsrs, doc(cfg(all())))]
351        B2500000,
352        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
353        #[cfg_attr(docsrs, doc(cfg(all())))]
354        B3000000,
355        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
356        #[cfg_attr(docsrs, doc(cfg(all())))]
357        B3500000,
358        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
359        #[cfg_attr(docsrs, doc(cfg(all())))]
360        B4000000,
361    }
362    impl TryFrom<libc::speed_t>
363}
364
365#[cfg(any(target_os = "freebsd",
366          target_os = "dragonfly",
367          target_os = "ios",
368          target_os = "macos",
369          target_os = "netbsd",
370          target_os = "openbsd"))]
371impl From<BaudRate> for u32 {
372    fn from(b: BaudRate) -> u32 {
373        b as u32
374    }
375}
376
377// TODO: Add TCSASOFT, which will require treating this as a bitfield.
378libc_enum! {
379    /// Specify when a port configuration change should occur.
380    ///
381    /// Used as an argument to `tcsetattr()`
382    #[repr(i32)]
383    #[non_exhaustive]
384    pub enum SetArg {
385        /// The change will occur immediately
386        TCSANOW,
387        /// The change occurs after all output has been written
388        TCSADRAIN,
389        /// Same as `TCSADRAIN`, but will also flush the input buffer
390        TCSAFLUSH,
391    }
392}
393
394libc_enum! {
395    /// Specify a combination of the input and output buffers to flush
396    ///
397    /// Used as an argument to `tcflush()`.
398    #[repr(i32)]
399    #[non_exhaustive]
400    pub enum FlushArg {
401        /// Flush data that was received but not read
402        TCIFLUSH,
403        /// Flush data written but not transmitted
404        TCOFLUSH,
405        /// Flush both received data not read and written data not transmitted
406        TCIOFLUSH,
407    }
408}
409
410libc_enum! {
411    /// Specify how transmission flow should be altered
412    ///
413    /// Used as an argument to `tcflow()`.
414    #[repr(i32)]
415    #[non_exhaustive]
416    pub enum FlowArg {
417        /// Suspend transmission
418        TCOOFF,
419        /// Resume transmission
420        TCOON,
421        /// Transmit a STOP character, which should disable a connected terminal device
422        TCIOFF,
423        /// Transmit a START character, which should re-enable a connected terminal device
424        TCION,
425    }
426}
427
428// TODO: Make this usable directly as a slice index.
429libc_enum! {
430    /// Indices into the `termios.c_cc` array for special characters.
431    #[repr(usize)]
432    #[non_exhaustive]
433    pub enum SpecialCharacterIndices {
434        VDISCARD,
435        #[cfg(any(target_os = "dragonfly",
436                target_os = "freebsd",
437                target_os = "illumos",
438                target_os = "macos",
439                target_os = "netbsd",
440                target_os = "openbsd",
441                target_os = "solaris"))]
442        #[cfg_attr(docsrs, doc(cfg(all())))]
443        VDSUSP,
444        VEOF,
445        VEOL,
446        VEOL2,
447        VERASE,
448        #[cfg(any(target_os = "dragonfly",
449                  target_os = "freebsd",
450                  target_os = "illumos",
451                  target_os = "solaris"))]
452        #[cfg_attr(docsrs, doc(cfg(all())))]
453        VERASE2,
454        VINTR,
455        VKILL,
456        VLNEXT,
457        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
458                target_os = "illumos", target_os = "solaris")))]
459        #[cfg_attr(docsrs, doc(cfg(all())))]
460        VMIN,
461        VQUIT,
462        VREPRINT,
463        VSTART,
464        #[cfg(any(target_os = "dragonfly",
465                target_os = "freebsd",
466                target_os = "illumos",
467                target_os = "macos",
468                target_os = "netbsd",
469                target_os = "openbsd",
470                target_os = "solaris"))]
471        #[cfg_attr(docsrs, doc(cfg(all())))]
472        VSTATUS,
473        VSTOP,
474        VSUSP,
475        #[cfg(target_os = "linux")]
476        #[cfg_attr(docsrs, doc(cfg(all())))]
477        VSWTC,
478        #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
479        #[cfg_attr(docsrs, doc(cfg(all())))]
480        VSWTCH,
481        #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
482                target_os = "illumos", target_os = "solaris")))]
483        #[cfg_attr(docsrs, doc(cfg(all())))]
484        VTIME,
485        VWERASE,
486        #[cfg(target_os = "dragonfly")]
487        #[cfg_attr(docsrs, doc(cfg(all())))]
488        VCHECKPT,
489    }
490}
491
492#[cfg(any(all(target_os = "linux", target_arch = "sparc64"),
493        target_os = "illumos", target_os = "solaris"))]
494impl SpecialCharacterIndices {
495    pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
496    pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
497}
498
499pub use libc::NCCS;
500#[cfg(any(target_os = "android",
501          target_os = "dragonfly",
502          target_os = "freebsd",
503          target_os = "linux",
504          target_os = "macos",
505          target_os = "netbsd",
506          target_os = "openbsd"))]
507#[cfg_attr(docsrs, doc(cfg(all())))]
508pub use libc::_POSIX_VDISABLE;
509
510libc_bitflags! {
511    /// Flags for configuring the input mode of a terminal
512    pub struct InputFlags: tcflag_t {
513        IGNBRK;
514        BRKINT;
515        IGNPAR;
516        PARMRK;
517        INPCK;
518        ISTRIP;
519        INLCR;
520        IGNCR;
521        ICRNL;
522        IXON;
523        IXOFF;
524        #[cfg(not(target_os = "redox"))]
525        #[cfg_attr(docsrs, doc(cfg(all())))]
526        IXANY;
527        #[cfg(not(target_os = "redox"))]
528        #[cfg_attr(docsrs, doc(cfg(all())))]
529        IMAXBEL;
530        #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
531        #[cfg_attr(docsrs, doc(cfg(all())))]
532        IUTF8;
533    }
534}
535
536libc_bitflags! {
537    /// Flags for configuring the output mode of a terminal
538    pub struct OutputFlags: tcflag_t {
539        OPOST;
540        #[cfg(any(target_os = "android",
541                  target_os = "haiku",
542                  target_os = "linux",
543                  target_os = "openbsd"))]
544        #[cfg_attr(docsrs, doc(cfg(all())))]
545        OLCUC;
546        ONLCR;
547        OCRNL as tcflag_t;
548        ONOCR as tcflag_t;
549        ONLRET as tcflag_t;
550        #[cfg(any(target_os = "android",
551                  target_os = "haiku",
552                  target_os = "ios",
553                  target_os = "linux",
554                  target_os = "macos"))]
555        #[cfg_attr(docsrs, doc(cfg(all())))]
556        OFILL as tcflag_t;
557        #[cfg(any(target_os = "android",
558                  target_os = "haiku",
559                  target_os = "ios",
560                  target_os = "linux",
561                  target_os = "macos"))]
562        #[cfg_attr(docsrs, doc(cfg(all())))]
563        OFDEL as tcflag_t;
564        #[cfg(any(target_os = "android",
565                  target_os = "haiku",
566                  target_os = "ios",
567                  target_os = "linux",
568                  target_os = "macos"))]
569        #[cfg_attr(docsrs, doc(cfg(all())))]
570        NL0 as tcflag_t;
571        #[cfg(any(target_os = "android",
572                  target_os = "haiku",
573                  target_os = "ios",
574                  target_os = "linux",
575                  target_os = "macos"))]
576        #[cfg_attr(docsrs, doc(cfg(all())))]
577        NL1 as tcflag_t;
578        #[cfg(any(target_os = "android",
579                  target_os = "haiku",
580                  target_os = "ios",
581                  target_os = "linux",
582                  target_os = "macos"))]
583        #[cfg_attr(docsrs, doc(cfg(all())))]
584        CR0 as tcflag_t;
585        #[cfg(any(target_os = "android",
586                  target_os = "haiku",
587                  target_os = "ios",
588                  target_os = "linux",
589                  target_os = "macos"))]
590        #[cfg_attr(docsrs, doc(cfg(all())))]
591        CR1 as tcflag_t;
592        #[cfg(any(target_os = "android",
593                  target_os = "haiku",
594                  target_os = "ios",
595                  target_os = "linux",
596                  target_os = "macos"))]
597        #[cfg_attr(docsrs, doc(cfg(all())))]
598        CR2 as tcflag_t;
599        #[cfg(any(target_os = "android",
600                  target_os = "haiku",
601                  target_os = "ios",
602                  target_os = "linux",
603                  target_os = "macos"))]
604        #[cfg_attr(docsrs, doc(cfg(all())))]
605        CR3 as tcflag_t;
606        #[cfg(any(target_os = "android",
607                  target_os = "freebsd",
608                  target_os = "haiku",
609                  target_os = "ios",
610                  target_os = "linux",
611                  target_os = "macos"))]
612        #[cfg_attr(docsrs, doc(cfg(all())))]
613        TAB0 as tcflag_t;
614        #[cfg(any(target_os = "android",
615                  target_os = "haiku",
616                  target_os = "ios",
617                  target_os = "linux",
618                  target_os = "macos"))]
619        #[cfg_attr(docsrs, doc(cfg(all())))]
620        TAB1 as tcflag_t;
621        #[cfg(any(target_os = "android",
622                  target_os = "haiku",
623                  target_os = "ios",
624                  target_os = "linux",
625                  target_os = "macos"))]
626        #[cfg_attr(docsrs, doc(cfg(all())))]
627        TAB2 as tcflag_t;
628        #[cfg(any(target_os = "android",
629                  target_os = "freebsd",
630                  target_os = "haiku",
631                  target_os = "ios",
632                  target_os = "linux",
633                  target_os = "macos"))]
634        #[cfg_attr(docsrs, doc(cfg(all())))]
635        TAB3 as tcflag_t;
636        #[cfg(any(target_os = "android", target_os = "linux"))]
637        #[cfg_attr(docsrs, doc(cfg(all())))]
638        XTABS;
639        #[cfg(any(target_os = "android",
640                  target_os = "haiku",
641                  target_os = "ios",
642                  target_os = "linux",
643                  target_os = "macos"))]
644        #[cfg_attr(docsrs, doc(cfg(all())))]
645        BS0 as tcflag_t;
646        #[cfg(any(target_os = "android",
647                  target_os = "haiku",
648                  target_os = "ios",
649                  target_os = "linux",
650                  target_os = "macos"))]
651        #[cfg_attr(docsrs, doc(cfg(all())))]
652        BS1 as tcflag_t;
653        #[cfg(any(target_os = "android",
654                  target_os = "haiku",
655                  target_os = "ios",
656                  target_os = "linux",
657                  target_os = "macos"))]
658        #[cfg_attr(docsrs, doc(cfg(all())))]
659        VT0 as tcflag_t;
660        #[cfg(any(target_os = "android",
661                  target_os = "haiku",
662                  target_os = "ios",
663                  target_os = "linux",
664                  target_os = "macos"))]
665        #[cfg_attr(docsrs, doc(cfg(all())))]
666        VT1 as tcflag_t;
667        #[cfg(any(target_os = "android",
668                  target_os = "haiku",
669                  target_os = "ios",
670                  target_os = "linux",
671                  target_os = "macos"))]
672        #[cfg_attr(docsrs, doc(cfg(all())))]
673        FF0 as tcflag_t;
674        #[cfg(any(target_os = "android",
675                  target_os = "haiku",
676                  target_os = "ios",
677                  target_os = "linux",
678                  target_os = "macos"))]
679        #[cfg_attr(docsrs, doc(cfg(all())))]
680        FF1 as tcflag_t;
681        #[cfg(any(target_os = "freebsd",
682                  target_os = "dragonfly",
683                  target_os = "ios",
684                  target_os = "macos",
685                  target_os = "netbsd",
686                  target_os = "openbsd"))]
687        #[cfg_attr(docsrs, doc(cfg(all())))]
688        OXTABS;
689        #[cfg(any(target_os = "freebsd",
690                  target_os = "dragonfly",
691                  target_os = "macos",
692                  target_os = "netbsd",
693                  target_os = "openbsd"))]
694        #[cfg_attr(docsrs, doc(cfg(all())))]
695        ONOEOT as tcflag_t;
696
697        // Bitmasks for use with OutputFlags to select specific settings
698        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
699        // is resolved.
700
701        #[cfg(any(target_os = "android",
702                  target_os = "haiku",
703                  target_os = "ios",
704                  target_os = "linux",
705                  target_os = "macos"))]
706        #[cfg_attr(docsrs, doc(cfg(all())))]
707        NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
708        #[cfg(any(target_os = "android",
709                  target_os = "haiku",
710                  target_os = "ios",
711                  target_os = "linux",
712                  target_os = "macos"))]
713        #[cfg_attr(docsrs, doc(cfg(all())))]
714        CRDLY as tcflag_t;
715        #[cfg(any(target_os = "android",
716                  target_os = "freebsd",
717                  target_os = "haiku",
718                  target_os = "ios",
719                  target_os = "linux",
720                  target_os = "macos"))]
721        #[cfg_attr(docsrs, doc(cfg(all())))]
722        TABDLY as tcflag_t;
723        #[cfg(any(target_os = "android",
724                  target_os = "haiku",
725                  target_os = "ios",
726                  target_os = "linux",
727                  target_os = "macos"))]
728        #[cfg_attr(docsrs, doc(cfg(all())))]
729        BSDLY as tcflag_t;
730        #[cfg(any(target_os = "android",
731                  target_os = "haiku",
732                  target_os = "ios",
733                  target_os = "linux",
734                  target_os = "macos"))]
735        #[cfg_attr(docsrs, doc(cfg(all())))]
736        VTDLY as tcflag_t;
737        #[cfg(any(target_os = "android",
738                  target_os = "haiku",
739                  target_os = "ios",
740                  target_os = "linux",
741                  target_os = "macos"))]
742        #[cfg_attr(docsrs, doc(cfg(all())))]
743        FFDLY as tcflag_t;
744    }
745}
746
747libc_bitflags! {
748    /// Flags for setting the control mode of a terminal
749    pub struct ControlFlags: tcflag_t {
750        #[cfg(any(target_os = "dragonfly",
751                  target_os = "freebsd",
752                  target_os = "ios",
753                  target_os = "macos",
754                  target_os = "netbsd",
755                  target_os = "openbsd"))]
756        #[cfg_attr(docsrs, doc(cfg(all())))]
757        CIGNORE;
758        CS5;
759        CS6;
760        CS7;
761        CS8;
762        CSTOPB;
763        CREAD;
764        PARENB;
765        PARODD;
766        HUPCL;
767        CLOCAL;
768        #[cfg(not(target_os = "redox"))]
769        #[cfg_attr(docsrs, doc(cfg(all())))]
770        CRTSCTS;
771        #[cfg(any(target_os = "android", target_os = "linux"))]
772        #[cfg_attr(docsrs, doc(cfg(all())))]
773        CBAUD;
774        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
775        #[cfg_attr(docsrs, doc(cfg(all())))]
776        CMSPAR;
777        #[cfg(any(target_os = "android",
778                  all(target_os = "linux",
779                      not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
780        CIBAUD;
781        #[cfg(any(target_os = "android", target_os = "linux"))]
782        #[cfg_attr(docsrs, doc(cfg(all())))]
783        CBAUDEX;
784        #[cfg(any(target_os = "dragonfly",
785                  target_os = "freebsd",
786                  target_os = "macos",
787                  target_os = "netbsd",
788                  target_os = "openbsd"))]
789        #[cfg_attr(docsrs, doc(cfg(all())))]
790        MDMBUF;
791        #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
792        #[cfg_attr(docsrs, doc(cfg(all())))]
793        CHWFLOW;
794        #[cfg(any(target_os = "dragonfly",
795                  target_os = "freebsd",
796                  target_os = "netbsd",
797                  target_os = "openbsd"))]
798        #[cfg_attr(docsrs, doc(cfg(all())))]
799        CCTS_OFLOW;
800        #[cfg(any(target_os = "dragonfly",
801                  target_os = "freebsd",
802                  target_os = "netbsd",
803                  target_os = "openbsd"))]
804        #[cfg_attr(docsrs, doc(cfg(all())))]
805        CRTS_IFLOW;
806        #[cfg(any(target_os = "dragonfly",
807                  target_os = "freebsd"))]
808        #[cfg_attr(docsrs, doc(cfg(all())))]
809        CDTR_IFLOW;
810        #[cfg(any(target_os = "dragonfly",
811                  target_os = "freebsd"))]
812        #[cfg_attr(docsrs, doc(cfg(all())))]
813        CDSR_OFLOW;
814        #[cfg(any(target_os = "dragonfly",
815                  target_os = "freebsd"))]
816        #[cfg_attr(docsrs, doc(cfg(all())))]
817        CCAR_OFLOW;
818
819        // Bitmasks for use with ControlFlags to select specific settings
820        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
821        // is resolved.
822
823        CSIZE;
824    }
825}
826
827libc_bitflags! {
828    /// Flags for setting any local modes
829    pub struct LocalFlags: tcflag_t {
830        #[cfg(not(target_os = "redox"))]
831        #[cfg_attr(docsrs, doc(cfg(all())))]
832        ECHOKE;
833        ECHOE;
834        ECHOK;
835        ECHO;
836        ECHONL;
837        #[cfg(not(target_os = "redox"))]
838        #[cfg_attr(docsrs, doc(cfg(all())))]
839        ECHOPRT;
840        #[cfg(not(target_os = "redox"))]
841        #[cfg_attr(docsrs, doc(cfg(all())))]
842        ECHOCTL;
843        ISIG;
844        ICANON;
845        #[cfg(any(target_os = "freebsd",
846                  target_os = "dragonfly",
847                  target_os = "ios",
848                  target_os = "macos",
849                  target_os = "netbsd",
850                  target_os = "openbsd"))]
851        #[cfg_attr(docsrs, doc(cfg(all())))]
852        ALTWERASE;
853        IEXTEN;
854        #[cfg(not(target_os = "redox"))]
855        #[cfg_attr(docsrs, doc(cfg(all())))]
856        EXTPROC;
857        TOSTOP;
858        #[cfg(not(target_os = "redox"))]
859        #[cfg_attr(docsrs, doc(cfg(all())))]
860        FLUSHO;
861        #[cfg(any(target_os = "freebsd",
862                  target_os = "dragonfly",
863                  target_os = "ios",
864                  target_os = "macos",
865                  target_os = "netbsd",
866                  target_os = "openbsd"))]
867        #[cfg_attr(docsrs, doc(cfg(all())))]
868        NOKERNINFO;
869        #[cfg(not(target_os = "redox"))]
870        #[cfg_attr(docsrs, doc(cfg(all())))]
871        PENDIN;
872        NOFLSH;
873    }
874}
875
876cfg_if!{
877    if #[cfg(any(target_os = "freebsd",
878                 target_os = "dragonfly",
879                 target_os = "ios",
880                 target_os = "macos",
881                 target_os = "netbsd",
882                 target_os = "openbsd"))] {
883        /// Get input baud rate (see
884        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
885        ///
886        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
887        // The cast is not unnecessary on all platforms.
888        #[allow(clippy::unnecessary_cast)]
889        pub fn cfgetispeed(termios: &Termios) -> u32 {
890            let inner_termios = termios.get_libc_termios();
891            unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
892        }
893
894        /// Get output baud rate (see
895        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
896        ///
897        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
898        // The cast is not unnecessary on all platforms.
899        #[allow(clippy::unnecessary_cast)]
900        pub fn cfgetospeed(termios: &Termios) -> u32 {
901            let inner_termios = termios.get_libc_termios();
902            unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
903        }
904
905        /// Set input baud rate (see
906        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
907        ///
908        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
909        pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
910            let inner_termios = unsafe { termios.get_libc_termios_mut() };
911            let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
912            termios.update_wrapper();
913            Errno::result(res).map(drop)
914        }
915
916        /// Set output baud rate (see
917        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
918        ///
919        /// `cfsetospeed()` sets the output baud rate in the given termios structure.
920        pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
921            let inner_termios = unsafe { termios.get_libc_termios_mut() };
922            let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
923            termios.update_wrapper();
924            Errno::result(res).map(drop)
925        }
926
927        /// Set both the input and output baud rates (see
928        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
929        ///
930        /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
931        /// this is part of the 4.4BSD standard and not part of POSIX.
932        pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
933            let inner_termios = unsafe { termios.get_libc_termios_mut() };
934            let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
935            termios.update_wrapper();
936            Errno::result(res).map(drop)
937        }
938    } else {
939        use std::convert::TryInto;
940
941        /// Get input baud rate (see
942        /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
943        ///
944        /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
945        pub fn cfgetispeed(termios: &Termios) -> BaudRate {
946            let inner_termios = termios.get_libc_termios();
947            unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
948        }
949
950        /// Get output baud rate (see
951        /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
952        ///
953        /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
954        pub fn cfgetospeed(termios: &Termios) -> BaudRate {
955            let inner_termios = termios.get_libc_termios();
956            unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
957        }
958
959        /// Set input baud rate (see
960        /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
961        ///
962        /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
963        pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
964            let inner_termios = unsafe { termios.get_libc_termios_mut() };
965            let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
966            termios.update_wrapper();
967            Errno::result(res).map(drop)
968        }
969
970        /// Set output baud rate (see
971        /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
972        ///
973        /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
974        pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
975            let inner_termios = unsafe { termios.get_libc_termios_mut() };
976            let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
977            termios.update_wrapper();
978            Errno::result(res).map(drop)
979        }
980
981        /// Set both the input and output baud rates (see
982        /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
983        ///
984        /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
985        /// this is part of the 4.4BSD standard and not part of POSIX.
986        pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
987            let inner_termios = unsafe { termios.get_libc_termios_mut() };
988            let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
989            termios.update_wrapper();
990            Errno::result(res).map(drop)
991        }
992    }
993}
994
995/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
996/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
997///
998/// `cfmakeraw()` configures the termios structure such that input is available character-by-
999/// character, echoing is disabled, and all special input and output processing is disabled. Note
1000/// that this is a non-standard function, but is available on Linux and BSDs.
1001pub fn cfmakeraw(termios: &mut Termios) {
1002    let inner_termios = unsafe { termios.get_libc_termios_mut() };
1003    unsafe {
1004        libc::cfmakeraw(inner_termios);
1005    }
1006    termios.update_wrapper();
1007}
1008
1009/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
1010/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
1011///
1012/// Note that this is a non-standard function, available on FreeBSD.
1013#[cfg(target_os = "freebsd")]
1014#[cfg_attr(docsrs, doc(cfg(all())))]
1015pub fn cfmakesane(termios: &mut Termios) {
1016    let inner_termios = unsafe { termios.get_libc_termios_mut() };
1017    unsafe {
1018        libc::cfmakesane(inner_termios);
1019    }
1020    termios.update_wrapper();
1021}
1022
1023/// Return the configuration of a port
1024/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
1025///
1026/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
1027/// this structure *will not* reconfigure the port, instead the modifications should be done to
1028/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
1029pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
1030    let mut termios = mem::MaybeUninit::uninit();
1031
1032    let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
1033
1034    Errno::result(res)?;
1035
1036    unsafe { Ok(termios.assume_init().into()) }
1037}
1038
1039/// Set the configuration for a terminal (see
1040/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
1041///
1042/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
1043/// takes affect at a time specified by `actions`. Note that this function may return success if
1044/// *any* of the parameters were successfully set, not only if all were set successfully.
1045pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
1046    let inner_termios = termios.get_libc_termios();
1047    Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
1048}
1049
1050/// Block until all output data is written (see
1051/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
1052pub fn tcdrain(fd: RawFd) -> Result<()> {
1053    Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
1054}
1055
1056/// Suspend or resume the transmission or reception of data (see
1057/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
1058///
1059/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
1060/// depending on the value of `action`.
1061pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
1062    Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
1063}
1064
1065/// Discard data in the output or input queue (see
1066/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
1067///
1068/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
1069/// depending on the value of `action`.
1070pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
1071    Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
1072}
1073
1074/// Send a break for a specific duration (see
1075/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
1076///
1077/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
1078/// of zero-valued bits for an implementation-defined duration.
1079pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
1080    Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
1081}
1082
1083feature! {
1084#![feature = "process"]
1085/// Get the session controlled by the given terminal (see
1086/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
1087pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
1088    let res = unsafe { libc::tcgetsid(fd) };
1089
1090    Errno::result(res).map(Pid::from_raw)
1091}
1092}
1093
1094#[cfg(test)]
1095mod test {
1096    use super::*;
1097    use std::convert::TryFrom;
1098
1099    #[test]
1100    fn try_from() {
1101        assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
1102        assert!(BaudRate::try_from(999999999).is_err());
1103    }
1104}