termios/
lib.rs

1//! The `termios` crate provides Rust bindings for the POSIX termios API that is implemented on
2//! Unix operating systems. The termios API is defined in the [IEEE Std 1003.1 ("POSIX.1")
3//! specification](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html).
4//!
5//! ## Getting Started
6//!
7//! The termios API operates on file descriptors that are associated with terminal devices, e.g.,
8//! `/dev/tty*`. When used with other file descriptors, termios functions return an error. All
9//! functions that are part of the POSIX standard are included in the `termios` crate. Where file
10//! descriptors are expected, the type `std::os::unix::io::RawFd` is used, and integer error codes
11//! are translated to `std::io::Result`.
12//!
13//! A major feature of the termios API is configuring a terminal device's parameters. The POSIX
14//! standard defines a `termios` structure that contains the parameters and several functions for
15//! manipulating the parameters. The `termios` crate defines a safe constructor that returns a
16//! [`Termios`](struct.Termios.html) struct populated with the parameters of an open terminal
17//! device:
18//!
19//! ```no_run
20//! use termios::*;
21//! # let fd = 1;
22//! let mut termios = Termios::from_fd(fd).unwrap();
23//! ```
24//!
25//! The [`Termios`](struct.Termios.html) struct provides access to the fields defined in the POSIX
26//! standard (`c_iflag`, `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`):
27//!
28//! ```no_run
29//! # use termios::*;
30//! # let fd = 1;
31//! # let mut termios = Termios::from_fd(fd).unwrap();
32//! termios.c_cflag |= CREAD | CLOCAL;
33//! termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN);
34//! termios.c_oflag &= !OPOST;
35//! termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK);
36//!
37//! termios.c_cc[VMIN] = 0;
38//! termios.c_cc[VTIME] = 0;
39//! ```
40//!
41//! The [`Termios`](struct.Termios.html) struct can also be manipulated using any of the standard
42//! termios API functions:
43//!
44//! ```no_run
45//! # use termios::*;
46//! # let fd = 1;
47//! # let mut termios = Termios::from_fd(fd).unwrap();
48//! cfgetispeed(&termios);
49//! cfgetospeed(&termios);
50//! cfsetispeed(&mut termios, B9600).unwrap();
51//! cfsetospeed(&mut termios, B9600).unwrap();
52//! tcsetattr(fd, TCSANOW, &termios).unwrap();
53//! ```
54//!
55//! ## Portability
56//!
57//! The `termios` crate is organized in a way to help write portable code, while also allowing
58//! access to OS-specific functionality when necessary.
59//!
60//! The crate root contains types, constants, and function definitions that are common across Unix
61//! operating systems. Most of the definitions in the crate root are from the POSIX standard;
62//! however, support for the standard may differ across operating systems. A couple functions in
63//! the crate root are not part of the POSIX standard, but are included in the crate root because
64//! they are widely available across Unix operating systems.
65//!
66//! To write portable code, import the `termios` crate and use only the definitions from the crate
67//! root.
68//!
69//! ### OS-Specific Extensions
70//!
71//! Each operating system may define extensions to the POSIX API. To make it clear when code
72//! depends on OS-specific definitions, any non-standard definitions are exported in the
73//! `termios::os` module. Programs that depend on OS-specific functionality must explicity opt-in.
74//! When writing portable code that depends on OS-specific definitions, it will often be necessary
75//! to use `#[cfg(...)]` attributes to support alternative implementations. The following is an
76//! example of a portable function that sets the maximum speed on a `Termios` struct.
77//!
78//! ```no_run
79//! use std::io;
80//! use termios::{Termios,cfsetspeed};
81//!
82//! #[cfg(target_os = "linux")]
83//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
84//!     cfsetspeed(termios, termios::os::linux::B4000000)
85//! }
86//!
87//! #[cfg(target_os = "macos")]
88//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
89//!     cfsetspeed(termios, termios::os::macos::B230400)
90//! }
91//!
92//! #[cfg(target_os = "freebsd")]
93//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
94//!     cfsetspeed(termios, termios::os::freebsd::B921600)
95//! }
96//!
97//! #[cfg(target_os = "openbsd")]
98//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
99//!     cfsetspeed(termios, termios::os::openbsd::B921600)
100//! }
101//!
102//! #[cfg(target_os = "netbsd")]
103//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
104//!     cfsetspeed(termios, termios::os::netbsd::B921600)
105//! }
106//!
107//! #[cfg(target_os = "dragonfly")]
108//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
109//!     cfsetspeed(termios, termios::os::dragonfly::B230400)
110//! }
111//!
112//! #[cfg(target_os = "solaris")]
113//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
114//!     cfsetspeed(termios, termios::os::solaris::B921600)
115//! }
116//!
117//! #[cfg(target_os = "illumos")]
118//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
119//!     cfsetspeed(termios, termios::os::illumos::B921600)
120//! }
121//!
122//! # let fd = 1;
123//! let mut termios = Termios::from_fd(fd).unwrap();
124//! set_fastest_speed(&mut termios).unwrap();
125//! ```
126
127extern crate libc;
128
129use std::io;
130use std::mem;
131use std::ops::{Deref,DerefMut};
132use std::os::unix::io::RawFd;
133
134use libc::{c_int,pid_t};
135
136pub use ::os::target::{cc_t,speed_t,tcflag_t}; // types
137pub use ::os::target::{VEOF,VEOL,VERASE,VINTR,VKILL,VMIN,VQUIT,VSTART,VSTOP,VSUSP,VTIME}; // c_cc subscripts
138pub use ::os::target::{BRKINT,ICRNL,IGNBRK,IGNCR,IGNPAR,INLCR,INPCK,ISTRIP,IXANY,IXOFF,IXON,PARMRK}; // input modes
139pub use ::os::target::{OPOST,ONLCR,OCRNL,ONOCR,ONLRET}; // output modes
140pub use ::os::target::{B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400}; // baud rate selection
141pub use ::os::target::{CSIZE,CS5,CS6,CS7,CS8,CSTOPB,CREAD,PARENB,PARODD,HUPCL,CLOCAL}; // control modes
142pub use ::os::target::{ECHO,ECHOE,ECHOK,ECHONL,ICANON,IEXTEN,ISIG,NOFLSH,TOSTOP}; // local modes
143pub use ::os::target::{TCSANOW,TCSADRAIN,TCSAFLUSH}; // attribute selection
144pub use ::os::target::{TCIFLUSH,TCIOFLUSH,TCOFLUSH,TCIOFF,TCION,TCOOFF,TCOON}; // line control
145
146pub mod ffi;
147pub mod os;
148
149
150/// Unix terminal I/O control structure.
151///
152/// The `Termios` structure is a thin wrapper for the OS-specific `termios` struct. The only safe
153/// way to obtain a `Termios` structure is to fill one from a file descriptor with
154/// [`Termios::from_fd()`](#method.from_fd), after which it can be treated just like the POSIX
155/// `termios` struct. It provides access to the standard fields of the `termios` struct (`c_iflag`,
156/// `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`) through the `Deref` and `DerefMut` traits.
157///
158/// ## Example
159///
160/// The following is an example of how one might setup a file descriptor for a serial port:
161///
162/// ```no_run
163/// use std::io;
164/// use std::os::unix::io::RawFd;
165///
166/// fn setup_serial(fd: RawFd) -> io::Result<()> {
167///     use termios::*;
168///
169///     let mut termios = try!(Termios::from_fd(fd));
170///
171///     termios.c_cflag |= CREAD | CLOCAL;
172///     termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN);
173///     termios.c_oflag &= !OPOST;
174///     termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK);
175///
176///     termios.c_cc[VMIN] = 0;
177///     termios.c_cc[VTIME] = 0;
178///
179///     try!(cfsetspeed(&mut termios, B9600));
180///     try!(tcsetattr(fd, TCSANOW, &mut termios));
181///
182///     Ok(())
183/// }
184/// ```
185#[derive(Debug,Copy,Clone,Eq,PartialEq)]
186pub struct Termios {
187    inner: ::os::target::termios
188}
189
190impl Termios {
191    /// Creates a `Termios` structure based on the current settings of a file descriptor.
192    ///
193    /// `fd` must be an open file descriptor for a terminal device.
194    pub fn from_fd(fd: RawFd) -> io::Result<Self> {
195        let mut termios = unsafe { mem::uninitialized() };
196
197        match tcgetattr(fd, &mut termios) {
198            Ok(_) => Ok(termios),
199            Err(err) => Err(err)
200        }
201    }
202
203    fn inner(&self) -> &::os::target::termios {
204        &self.inner
205    }
206
207    fn inner_mut(&mut self) -> &mut ::os::target::termios {
208        &mut self.inner
209    }
210}
211
212impl Deref for Termios {
213    type Target = ::os::target::termios;
214
215    fn deref(&self) -> &::os::target::termios {
216        self.inner()
217    }
218}
219
220impl DerefMut for Termios {
221    fn deref_mut(&mut self) -> &mut ::os::target::termios {
222        self.inner_mut()
223    }
224}
225
226
227/// Gets the input baud rate stored in a `Termios` structure.
228///
229/// # Examples
230///
231/// ```
232/// # use std::mem;
233/// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed};
234/// # let mut termios = unsafe { mem::uninitialized() };
235/// cfsetispeed(&mut termios, B9600).unwrap();
236/// assert_eq!(cfgetispeed(&termios), B9600);
237/// ```
238pub fn cfgetispeed(termios: &Termios) -> speed_t {
239    unsafe { ffi::cfgetispeed(termios.inner()) }
240}
241
242/// Gets the output baud rate stored in a `Termios` structure.
243///
244/// # Examples
245///
246/// ```
247/// # use std::mem;
248/// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed};
249/// # let mut termios = unsafe { mem::uninitialized() };
250/// cfsetospeed(&mut termios, B9600).unwrap();
251/// assert_eq!(cfgetospeed(&termios), B9600);
252/// ```
253pub fn cfgetospeed(termios: &Termios) -> speed_t {
254    unsafe { ffi::cfgetospeed(termios.inner()) }
255}
256
257/// Sets the input baud rate.
258///
259/// This function only sets the necessary values on the given `Termios` structure. The settings are
260/// applied by a subsequent call to [`tcsetattr()`](fn.tcsetattr.html).
261///
262/// # Parameters
263///
264/// * `termios` should be a mutable reference to a `Termios` structure.
265/// * `speed` should be one of the baud rate constants:
266///   - `B0`
267///   - `B50`
268///   - `B75`
269///   - `B110`
270///   - `B134`
271///   - `B150`
272///   - `B200`
273///   - `B300`
274///   - `B600`
275///   - `B1200`
276///   - `B1800`
277///   - `B2400`
278///   - `B4800`
279///   - `B9600`
280///   - `B19200`
281///   - `B38400`
282///   - any OS-specific baud rate defined in [`termios::os`](os/index.html).
283///
284/// A value of `B0` for `speed` sets the input baud rate to be the same as the output baud rate.
285///
286/// # Examples
287///
288/// ```
289/// # use std::mem;
290/// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed};
291/// # let mut termios = unsafe { mem::uninitialized() };
292/// cfsetispeed(&mut termios, B9600).unwrap();
293/// assert_eq!(cfgetispeed(&termios), B9600);
294/// ```
295pub fn cfsetispeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
296    io_result(unsafe { ffi::cfsetispeed(termios.inner_mut(), speed) })
297}
298
299/// Sets the output baud rate.
300///
301/// This function only sets the necessary values on the given `Termios` structure. The settings are
302/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
303///
304/// # Parameters
305///
306/// * `termios` should be a mutable reference to a `Termios` structure.
307/// * `speed` should be one of the baud rate constants:
308///   - `B0` (hang up)
309///   - `B50`
310///   - `B75`
311///   - `B110`
312///   - `B134`
313///   - `B150`
314///   - `B200`
315///   - `B300`
316///   - `B600`
317///   - `B1200`
318///   - `B1800`
319///   - `B2400`
320///   - `B4800`
321///   - `B9600`
322///   - `B19200`
323///   - `B38400`
324///   - any OS-specific baud rate defined in [`termios::os`](os/index.html).
325///
326/// A value of `B0` for `speed` deasserts the modem control lines when applied with
327/// [`tcsetattr()`](fn.tcsetattr.html).  This normally has the effect of disconnecting the line.
328///
329/// # Examples
330///
331/// ```
332/// # use std::mem;
333/// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed};
334/// # let mut termios = unsafe { mem::uninitialized() };
335/// cfsetospeed(&mut termios, B9600).unwrap();
336/// assert_eq!(cfgetospeed(&termios), B9600);
337/// ```
338pub fn cfsetospeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
339    io_result(unsafe { ffi::cfsetospeed(termios.inner_mut(), speed) })
340}
341
342/// Sets input and output baud rates.
343///
344/// This function only sets the necessary values on the given `Termios` structure. The settings are
345/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
346///
347/// # Parameters
348///
349/// * `termios` should be a mutable reference to a `Termios` structure.
350/// * `speed` should be one of the baud rate constants:
351///   - `B0`
352///   - `B50`
353///   - `B75`
354///   - `B110`
355///   - `B134`
356///   - `B150`
357///   - `B200`
358///   - `B300`
359///   - `B600`
360///   - `B1200`
361///   - `B1800`
362///   - `B2400`
363///   - `B4800`
364///   - `B9600`
365///   - `B19200`
366///   - `B38400`
367///   - any OS-specific baud rate defined in [`termios::os`](os/index.html).
368///
369/// # Examples
370///
371/// ```
372/// # use std::mem;
373/// # use termios::{Termios,B9600,cfsetspeed,cfgetispeed,cfgetospeed};
374/// # let mut termios = unsafe { mem::uninitialized() };
375/// cfsetspeed(&mut termios, B9600).unwrap();
376/// assert_eq!(cfgetispeed(&termios), B9600);
377/// assert_eq!(cfgetospeed(&termios), B9600);
378/// ```
379///
380/// # Portability
381///
382/// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available
383/// on Linux, BSD, and OS X.
384pub fn cfsetspeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
385    io_result(unsafe { ffi::cfsetspeed(termios.inner_mut(), speed) })
386}
387
388/// Sets flags to disable all input and output processing.
389///
390/// This function only sets the necessary values on the given `Termios` structure. The settings are
391/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
392///
393/// # Portability
394///
395/// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available
396/// on Linux, BSD, and OS X.
397pub fn cfmakeraw(termios: &mut Termios) {
398    unsafe { ffi::cfmakeraw(termios.inner_mut()) };
399}
400
401/// Blocks until all output written to the file descriptor is transmitted.
402///
403/// # Parameters
404///
405/// * `fd` should be an open file descriptor associated with a terminal.
406pub fn tcdrain(fd: RawFd) -> io::Result<()> {
407    io_result(unsafe { ffi::tcdrain(fd) })
408}
409
410/// Suspends or restarts transmission or reception of data.
411///
412/// # Parameters
413///
414/// * `fd` should be an open file descriptor associated with a terminal.
415/// * `action` should be one of the following constants:
416///   - `TCOOFF` suspends output.
417///   - `TCOON` restarts output.
418///   - `TCIOFF` transmits a STOP character, intended to cause the remote device to stop
419///     transmitting.
420///   - `TCION` transmits a START character, intended to cause the remote device to resume
421///     transmitting.
422pub fn tcflow(fd: RawFd, action: c_int) -> io::Result<()> {
423    io_result(unsafe { ffi::tcflow(fd, action) })
424}
425
426/// Discards data waiting in the terminal device's buffers.
427///
428/// `tcflush()` discards data that has been written to the device by an application but has not yet
429/// been transmitted by the hardware or data that has been received by the hardware but has not yet
430/// been read by an application.
431///
432/// # Parameters
433///
434/// * `fd` should be an open file descriptor associated with a terminal.
435/// * `queue_selector` should be one of:
436///   - `TCIFLUSH` to discard data received but not read.
437///   - `TCOFLUSH` to discard data written but not transmitted.
438///   - `TCIOFLUSH` to discard both data received but not read and data written but not
439///     transmitted.
440pub fn tcflush(fd: RawFd, queue_selector: c_int) -> io::Result<()> {
441    io_result(unsafe { ffi::tcflush(fd, queue_selector) })
442}
443
444/// Populates a `Termios` structure with parameters associated with a terminal.
445///
446/// Upon successful completion, the `Termios` structure referred to by the `termios` parameter will
447/// contain the parameters associated with the terminal device referred to by `fd`.
448///
449/// # Parameters
450///
451/// * `fd` should be an open file descriptor associated with a terminal.
452/// * `termios` should be a mutable reference to the `Termios` structure that will hold the
453///   terminal device's parameters.
454pub fn tcgetattr(fd: RawFd, termios: &mut Termios) -> io::Result<()> {
455    io_result(unsafe { ffi::tcgetattr(fd, termios.inner_mut()) })
456}
457
458/// Sets a terminal device's parameters.
459///
460/// `tcsetattr()` returns successfully if it was able to perform any of the requested actions, even
461/// if other requested actions could not be performed. It will set all attributes that the
462/// implementation supports and leave others unchanged. The `Termios` structure will not be updated
463/// to reflect the changes that were applied.
464///
465/// In order to determine which parameters were applied to the terminal device, an application
466/// should use [`tcgetattr()`](fn.tcgetattr.html) to obtain the latest state of the terminal
467/// device. In particular, when attempting to change baud rates, [`tcgetattr()`](fn.tcgetattr.html)
468/// can be used to determine which baud rates were actually selected.
469///
470/// If none of the requested actions could be performed, then `tcsetattr()` returns an error.
471///
472/// # Parameters
473///
474/// * `fd` should be an open file descriptor associated with a terminal.
475/// * `action` should be one of the constants:
476///   - `TCSANOW` applies the change immediately.
477///   - `TCSADRAIN` applies the change after all output previously written to `fd` is transmitted.
478///     This mode should be used when changing parameters that affect output.
479///   - `TCSAFLUSH` applies the change after all output previously written to `fd` is transmitted.
480///     All data received but not read is discarded before applying the change.
481/// * `termios` should be a mutable reference to a `Termios` structure containing the parameters to
482///   apply to the terminal device.
483pub fn tcsetattr(fd: RawFd, action: c_int, termios: &Termios) -> io::Result<()> {
484    io_result(unsafe { ffi::tcsetattr(fd, action, termios.inner()) })
485}
486
487/// Returns the process group ID of the controlling terminal's session.
488///
489/// # Parameters
490///
491/// * `fd` should be an open file descriptor associated with a controlling terminal.
492pub fn tcgetsid(fd: RawFd) -> pid_t {
493    unsafe { ffi::tcgetsid(fd) }
494}
495
496/// Transmits data to generate a break condition.
497///
498/// If the terminal device is using asynchronous data transmission, `tcsendbreak()` transmits a
499/// continuous stream of zero bits for a specific duration.
500///
501/// # Parameters
502///
503/// * `fd` should be an open file descriptor associated with a terminal.
504/// * `duration` controls the duration of the transmitted zero bits. A value of 0 causes a
505///   transmission between 0.25 and 0.5 seconds. A value other than 0 causes a transmission for an
506///   implementation-defined period of time.
507pub fn tcsendbreak(fd: RawFd, duration: c_int) -> io::Result<()> {
508    io_result(unsafe { ffi::tcsendbreak(fd, duration) })
509}
510
511
512#[inline]
513fn io_result(result: c_int) -> io::Result<()> {
514    match result {
515        0 => Ok(()),
516        _ => Err(io::Error::last_os_error())
517    }
518}