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