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