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