nix/sys/termios.rs
1//! An interface for controlling asynchronous communication ports
2//!
3//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
4//! underlying types are all implemented in libc for most platforms and either wrapped in safer
5//! types here or exported directly.
6//!
7//! If you are unfamiliar with the `termios` API, you should first read the
8//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
9//! then come back to understand how `nix` safely wraps it.
10//!
11//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
12//! As this interface is not used with high-bandwidth information, this should be fine in most
13//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
14//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
15//! This means that when crossing the FFI interface to the underlying C library, data is first
16//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
17//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
18//! relatively small across all platforms (on the order of 32-64 bytes).
19//!
20//! The following examples highlight some of the API use cases such that users coming from using C
21//! or reading the standard documentation will understand how to use the safe API exposed here.
22//!
23//! Example disabling processing of the end-of-file control character:
24//!
25//! ```
26//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
27//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
28//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
29//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
30//! ```
31//!
32//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
33//! an interface for working with bitfields that is similar to working with the raw unsigned
34//! integer types but offers type safety because of the internal checking that values will always
35//! be a valid combination of the defined flags.
36//!
37//! An example showing some of the basic operations for interacting with the control flags:
38//!
39//! ```
40//! # use self::nix::sys::termios::{ControlFlags, Termios};
41//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
42//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
43//! termios.control_flags |= ControlFlags::CS5;
44//! ```
45//!
46//! # Baud rates
47//!
48//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
49//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
50//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
51//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
52//! conventions:
53//!
54//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
55//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
56//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
57//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
58//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
59//!
60//! The most common use case of specifying a baud rate using the enum will work the same across
61//! platforms:
62//!
63//! ```rust
64//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
65//! # fn main() {
66//! # let mut t: Termios = unsafe { std::mem::zeroed() };
67//! cfsetispeed(&mut t, BaudRate::B9600);
68//! cfsetospeed(&mut t, BaudRate::B9600);
69//! cfsetspeed(&mut t, BaudRate::B9600);
70//! # }
71//! ```
72//!
73//! Additionally round-tripping baud rates is consistent across platforms:
74//!
75//! ```rust
76//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
77//! # fn main() {
78//! # let mut t: Termios = unsafe { std::mem::zeroed() };
79//! # cfsetspeed(&mut t, BaudRate::B9600);
80//! let speed = cfgetispeed(&t);
81//! assert_eq!(speed, cfgetospeed(&t));
82//! cfsetispeed(&mut t, speed);
83//! # }
84//! ```
85//!
86//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
87//!
88#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
89 target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
90 doc = " ```rust,ignore")]
91#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
92 target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
93 doc = " ```rust")]
94//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
95//! # fn main() {
96//! # let mut t: Termios = unsafe { std::mem::zeroed() };
97//! # cfsetspeed(&mut t, BaudRate::B9600);
98//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
99//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
100//! # }
101//! ```
102//!
103//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
104//!
105#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
106 target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
107 doc = " ```rust")]
108#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
109 target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
110 doc = " ```rust,ignore")]
111//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
112//! # fn main() {
113//! # let mut t: Termios = unsafe { std::mem::zeroed() };
114//! # cfsetspeed(&mut t, 9600u32);
115//! assert_eq!(cfgetispeed(&t), 9600u32);
116//! assert_eq!(cfgetospeed(&t), 9600u32);
117//! # }
118//! ```
119//!
120//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
121//!
122#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
123 target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
124 doc = " ```rust")]
125#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
126 target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
127 doc = " ```rust,ignore")]
128//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
129//! # fn main() {
130//! # let mut t: Termios = unsafe { std::mem::zeroed() };
131//! # cfsetspeed(&mut t, 9600u32);
132//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
133//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
134//! # }
135//! ```
136//!
137//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
138//! by specifying baud rates directly using `u32`s:
139//!
140#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
141 target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
142 doc = " ```rust")]
143#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
144 target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
145 doc = " ```rust,ignore")]
146//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
147//! # fn main() {
148//! # let mut t: Termios = unsafe { std::mem::zeroed() };
149//! cfsetispeed(&mut t, 9600u32);
150//! cfsetospeed(&mut t, 9600u32);
151//! cfsetspeed(&mut t, 9600u32);
152//! # }
153//! ```
154use cfg_if::cfg_if;
155use crate::Result;
156use crate::errno::Errno;
157use libc::{self, c_int, tcflag_t};
158use std::cell::{Ref, RefCell};
159use std::convert::From;
160use std::mem;
161use std::os::unix::io::RawFd;
162
163use 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)]
171pub 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
185impl 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.
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.
208 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.
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
231impl From<libc::termios> for Termios {
232 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
244impl From<Termios> for libc::termios {
245 fn from(termios: Termios) -> Self {
246 termios.inner.into_inner()
247 }
248}
249
250libc_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"))]
352impl From<BaudRate> for u32 {
353 fn from(b: BaudRate) -> u32 {
354 b as u32
355 }
356}
357
358// TODO: Add TCSASOFT, which will require treating this as a bitfield.
359libc_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
375libc_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
391libc_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.
410libc_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"))]
467impl SpecialCharacterIndices {
468 pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
469 pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
470}
471
472pub 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"))]
479pub use libc::_POSIX_VDISABLE;
480
481libc_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
504libc_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
687libc_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
755libc_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
796cfg_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 // The cast is not unnecessary on all platforms.
808 #[allow(clippy::unnecessary_cast)]
809 pub fn cfgetispeed(termios: &Termios) -> u32 {
810 let inner_termios = termios.get_libc_termios();
811 unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
812 }
813
814 /// Get output baud rate (see
815 /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
816 ///
817 /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
818 // The cast is not unnecessary on all platforms.
819 #[allow(clippy::unnecessary_cast)]
820 pub fn cfgetospeed(termios: &Termios) -> u32 {
821 let inner_termios = termios.get_libc_termios();
822 unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
823 }
824
825 /// Set input baud rate (see
826 /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
827 ///
828 /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
829 pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
830 let inner_termios = unsafe { termios.get_libc_termios_mut() };
831 let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
832 termios.update_wrapper();
833 Errno::result(res).map(drop)
834 }
835
836 /// Set output baud rate (see
837 /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
838 ///
839 /// `cfsetospeed()` sets the output baud rate in the given termios structure.
840 pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
841 let inner_termios = unsafe { termios.get_libc_termios_mut() };
842 let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
843 termios.update_wrapper();
844 Errno::result(res).map(drop)
845 }
846
847 /// Set both the input and output baud rates (see
848 /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
849 ///
850 /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
851 /// this is part of the 4.4BSD standard and not part of POSIX.
852 pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
853 let inner_termios = unsafe { termios.get_libc_termios_mut() };
854 let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
855 termios.update_wrapper();
856 Errno::result(res).map(drop)
857 }
858 } else {
859 use std::convert::TryInto;
860
861 /// Get input baud rate (see
862 /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
863 ///
864 /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
865 pub fn cfgetispeed(termios: &Termios) -> BaudRate {
866 let inner_termios = termios.get_libc_termios();
867 unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
868 }
869
870 /// Get output baud rate (see
871 /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
872 ///
873 /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
874 pub fn cfgetospeed(termios: &Termios) -> BaudRate {
875 let inner_termios = termios.get_libc_termios();
876 unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
877 }
878
879 /// Set input baud rate (see
880 /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
881 ///
882 /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
883 pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
884 let inner_termios = unsafe { termios.get_libc_termios_mut() };
885 let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
886 termios.update_wrapper();
887 Errno::result(res).map(drop)
888 }
889
890 /// Set output baud rate (see
891 /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
892 ///
893 /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
894 pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
895 let inner_termios = unsafe { termios.get_libc_termios_mut() };
896 let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
897 termios.update_wrapper();
898 Errno::result(res).map(drop)
899 }
900
901 /// Set both the input and output baud rates (see
902 /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
903 ///
904 /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
905 /// this is part of the 4.4BSD standard and not part of POSIX.
906 pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
907 let inner_termios = unsafe { termios.get_libc_termios_mut() };
908 let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
909 termios.update_wrapper();
910 Errno::result(res).map(drop)
911 }
912 }
913}
914
915/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
916/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
917///
918/// `cfmakeraw()` configures the termios structure such that input is available character-by-
919/// character, echoing is disabled, and all special input and output processing is disabled. Note
920/// that this is a non-standard function, but is available on Linux and BSDs.
921pub fn cfmakeraw(termios: &mut Termios) {
922 let inner_termios = unsafe { termios.get_libc_termios_mut() };
923 unsafe {
924 libc::cfmakeraw(inner_termios);
925 }
926 termios.update_wrapper();
927}
928
929/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
930/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
931///
932/// Note that this is a non-standard function, available on FreeBSD.
933#[cfg(target_os = "freebsd")]
934pub fn cfmakesane(termios: &mut Termios) {
935 let inner_termios = unsafe { termios.get_libc_termios_mut() };
936 unsafe {
937 libc::cfmakesane(inner_termios);
938 }
939 termios.update_wrapper();
940}
941
942/// Return the configuration of a port
943/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
944///
945/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
946/// this structure *will not* reconfigure the port, instead the modifications should be done to
947/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
948pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
949 let mut termios = mem::MaybeUninit::uninit();
950
951 let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
952
953 Errno::result(res)?;
954
955 unsafe { Ok(termios.assume_init().into()) }
956}
957
958/// Set the configuration for a terminal (see
959/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
960///
961/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
962/// takes affect at a time specified by `actions`. Note that this function may return success if
963/// *any* of the parameters were successfully set, not only if all were set successfully.
964pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
965 let inner_termios = termios.get_libc_termios();
966 Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
967}
968
969/// Block until all output data is written (see
970/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
971pub fn tcdrain(fd: RawFd) -> Result<()> {
972 Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
973}
974
975/// Suspend or resume the transmission or reception of data (see
976/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
977///
978/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
979/// depending on the value of `action`.
980pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
981 Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
982}
983
984/// Discard data in the output or input queue (see
985/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
986///
987/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
988/// depending on the value of `action`.
989pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
990 Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
991}
992
993/// Send a break for a specific duration (see
994/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
995///
996/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
997/// of zero-valued bits for an implementation-defined duration.
998pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
999 Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
1000}
1001
1002/// Get the session controlled by the given terminal (see
1003/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
1004pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
1005 let res = unsafe { libc::tcgetsid(fd) };
1006
1007 Errno::result(res).map(Pid::from_raw)
1008}
1009
1010#[cfg(test)]
1011mod test {
1012 use super::*;
1013 use std::convert::TryFrom;
1014
1015 #[test]
1016 fn try_from() {
1017 assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
1018 assert!(BaudRate::try_from(999999999).is_err());
1019 }
1020}