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