// Copyright 2017 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::io::Stdin;5use std::mem::zeroed;6use std::os::unix::io::RawFd;78use libc::isatty;9use libc::read;10use libc::tcgetattr;11use libc::tcsetattr;12use libc::termios;13use libc::ECHO;14use libc::ICANON;15use libc::ISIG;16use libc::O_NONBLOCK;17use libc::STDIN_FILENO;18use libc::TCSANOW;1920use crate::errno::Result;21use crate::errno_result;22use crate::unix::add_fd_flags;23use crate::unix::clear_fd_flags;2425fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {26// Safety:27// Safe because we check the return value of isatty.28if unsafe { isatty(fd) } != 1 {29return Ok(());30}3132// Safety:33// The following pair are safe because termios gets totally overwritten by tcgetattr and we34// check the return result.35let mut termios: termios = unsafe { zeroed() };36// Safety:37// The following pair are safe because termios gets totally overwritten by tcgetattr and we38// check the return result.39let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };40if ret < 0 {41return errno_result();42}43let mut new_termios = termios;44f(&mut new_termios);45// SAFETY:46// Safe because the syscall will only read the extent of termios and we check the return result.47let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };48if ret < 0 {49return errno_result();50}5152Ok(())53}5455/// # Safety56///57/// Safe only when the FD given is valid and reading the fd will have no Rust safety implications.58unsafe fn read_raw(fd: RawFd, out: &mut [u8]) -> Result<usize> {59let ret = read(fd, out.as_mut_ptr() as *mut _, out.len());60if ret < 0 {61return errno_result();62}6364Ok(ret as usize)65}6667/// Read raw bytes from stdin.68///69/// This will block depending on the underlying mode of stdin. This will ignore the usual lock70/// around stdin that the stdlib usually uses. If other code is using stdin, it is undefined who71/// will get the underlying bytes.72pub fn read_raw_stdin(out: &mut [u8]) -> Result<usize> {73// SAFETY:74// Safe because reading from stdin shouldn't have any safety implications.75unsafe { read_raw(STDIN_FILENO, out) }76}7778/// Trait for file descriptors that are TTYs, according to `isatty(3)`.79///80/// # Safety81/// This is marked unsafe because the implementation must promise that the returned RawFd is a valid82/// fd and that the lifetime of the returned fd is at least that of the trait object.83pub unsafe trait Terminal {84/// Gets the file descriptor of the TTY.85fn tty_fd(&self) -> RawFd;8687/// Set this terminal's mode to canonical mode (`ICANON | ECHO | ISIG`).88fn set_canon_mode(&self) -> Result<()> {89modify_mode(self.tty_fd(), |t| t.c_lflag |= ICANON | ECHO | ISIG)90}9192/// Set this terminal's mode to raw mode (`!(ICANON | ECHO | ISIG)`).93fn set_raw_mode(&self) -> Result<()> {94modify_mode(self.tty_fd(), |t| t.c_lflag &= !(ICANON | ECHO | ISIG))95}9697/// Sets the non-blocking mode of this terminal's file descriptor.98///99/// If `non_block` is `true`, then `read_raw` will not block. If `non_block` is `false`, then100/// `read_raw` may block if there is nothing to read.101fn set_non_block(&self, non_block: bool) -> Result<()> {102if non_block {103add_fd_flags(self.tty_fd(), O_NONBLOCK)104} else {105clear_fd_flags(self.tty_fd(), O_NONBLOCK)106}107}108}109110// # SAFETY:111// Safe because we return a genuine terminal fd that never changes and shares our lifetime.112unsafe impl Terminal for Stdin {113fn tty_fd(&self) -> RawFd {114STDIN_FILENO115}116}117118119