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