Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/base/src/sys/linux/terminal.rs
5394 views
1
// Copyright 2017 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::io::Stdin;
6
use std::mem::zeroed;
7
use std::os::unix::io::RawFd;
8
9
use libc::isatty;
10
use libc::read;
11
use libc::tcgetattr;
12
use libc::tcsetattr;
13
use libc::termios;
14
use libc::ECHO;
15
use libc::ICANON;
16
use libc::ISIG;
17
use libc::O_NONBLOCK;
18
use libc::STDIN_FILENO;
19
use libc::TCSANOW;
20
21
use crate::errno::Result;
22
use crate::errno_result;
23
use crate::unix::add_fd_flags;
24
use crate::unix::clear_fd_flags;
25
26
fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
27
// Safety:
28
// Safe because we check the return value of isatty.
29
if unsafe { isatty(fd) } != 1 {
30
return Ok(());
31
}
32
33
// Safety:
34
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
35
// check the return result.
36
let mut termios: termios = unsafe { zeroed() };
37
// Safety:
38
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
39
// check the return result.
40
let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };
41
if ret < 0 {
42
return errno_result();
43
}
44
let mut new_termios = termios;
45
f(&mut new_termios);
46
// SAFETY:
47
// Safe because the syscall will only read the extent of termios and we check the return result.
48
let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };
49
if ret < 0 {
50
return errno_result();
51
}
52
53
Ok(())
54
}
55
56
/// # Safety
57
///
58
/// Safe only when the FD given is valid and reading the fd will have no Rust safety implications.
59
unsafe fn read_raw(fd: RawFd, out: &mut [u8]) -> Result<usize> {
60
let ret = read(fd, out.as_mut_ptr() as *mut _, out.len());
61
if ret < 0 {
62
return errno_result();
63
}
64
65
Ok(ret as usize)
66
}
67
68
/// Read raw bytes from stdin.
69
///
70
/// This will block depending on the underlying mode of stdin. This will ignore the usual lock
71
/// around stdin that the stdlib usually uses. If other code is using stdin, it is undefined who
72
/// will get the underlying bytes.
73
pub fn read_raw_stdin(out: &mut [u8]) -> Result<usize> {
74
// SAFETY:
75
// Safe because reading from stdin shouldn't have any safety implications.
76
unsafe { read_raw(STDIN_FILENO, out) }
77
}
78
79
/// Trait for file descriptors that are TTYs, according to `isatty(3)`.
80
///
81
/// # Safety
82
/// This is marked unsafe because the implementation must promise that the returned RawFd is a valid
83
/// fd and that the lifetime of the returned fd is at least that of the trait object.
84
pub unsafe trait Terminal {
85
/// Gets the file descriptor of the TTY.
86
fn tty_fd(&self) -> RawFd;
87
88
/// Set this terminal's mode to canonical mode (`ICANON | ECHO | ISIG`).
89
fn set_canon_mode(&self) -> Result<()> {
90
modify_mode(self.tty_fd(), |t| t.c_lflag |= ICANON | ECHO | ISIG)
91
}
92
93
/// Set this terminal's mode to raw mode (`!(ICANON | ECHO | ISIG)`).
94
fn set_raw_mode(&self) -> Result<()> {
95
modify_mode(self.tty_fd(), |t| t.c_lflag &= !(ICANON | ECHO | ISIG))
96
}
97
98
/// Sets the non-blocking mode of this terminal's file descriptor.
99
///
100
/// If `non_block` is `true`, then `read_raw` will not block. If `non_block` is `false`, then
101
/// `read_raw` may block if there is nothing to read.
102
fn set_non_block(&self, non_block: bool) -> Result<()> {
103
if non_block {
104
add_fd_flags(self.tty_fd(), O_NONBLOCK)
105
} else {
106
clear_fd_flags(self.tty_fd(), O_NONBLOCK)
107
}
108
}
109
}
110
111
// # SAFETY:
112
// Safe because we return a genuine terminal fd that never changes and shares our lifetime.
113
unsafe impl Terminal for Stdin {
114
fn tty_fd(&self) -> RawFd {
115
STDIN_FILENO
116
}
117
}
118
119