forked from proxmox-mirrors/proxmox
src/sys/linux/tty.rs: add tty helpers
This commit is contained in:
parent
98708e346d
commit
4d21db36fe
@ -5,6 +5,7 @@ use failure::*;
|
|||||||
pub mod magic;
|
pub mod magic;
|
||||||
pub mod pid;
|
pub mod pid;
|
||||||
pub mod procfs;
|
pub mod procfs;
|
||||||
|
pub mod tty;
|
||||||
|
|
||||||
/// Get pseudo random data (/dev/urandom)
|
/// Get pseudo random data (/dev/urandom)
|
||||||
pub fn random_data(size: usize) -> Result<Vec<u8>, Error> {
|
pub fn random_data(size: usize) -> Result<Vec<u8>, Error> {
|
||||||
|
132
proxmox/src/sys/linux/tty.rs
Normal file
132
proxmox/src/sys/linux/tty.rs
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
|
||||||
|
use failure::*;
|
||||||
|
|
||||||
|
use crate::try_block;
|
||||||
|
|
||||||
|
/// Get the current size of the terminal (for stdout).
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// uses unsafe call to tty_ioctl, see man tty_ioctl(2).
|
||||||
|
pub fn stdout_terminal_size() -> (usize, usize) {
|
||||||
|
let mut winsize = libc::winsize {
|
||||||
|
ws_row: 0,
|
||||||
|
ws_col: 0,
|
||||||
|
ws_xpixel: 0,
|
||||||
|
ws_ypixel: 0,
|
||||||
|
};
|
||||||
|
unsafe { libc::ioctl(libc::STDOUT_FILENO, libc::TIOCGWINSZ, &mut winsize) };
|
||||||
|
(winsize.ws_row as usize, winsize.ws_col as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the current stdout is a tty .
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// uses unsafe call to libc::isatty
|
||||||
|
pub fn stdout_isatty() -> bool {
|
||||||
|
unsafe { libc::isatty(std::io::stdin().as_raw_fd()) == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the current stdin is a tty .
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// uses unsafe call to libc::isatty
|
||||||
|
pub fn stdin_isatty() -> bool {
|
||||||
|
unsafe { libc::isatty(std::io::stdin().as_raw_fd()) == 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a password from stdin.
|
||||||
|
///
|
||||||
|
/// Masking the echoed output with asterisks and writing a query
|
||||||
|
/// first.
|
||||||
|
pub fn read_password(query: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
let input = std::io::stdin();
|
||||||
|
if unsafe { libc::isatty(input.as_raw_fd()) } != 1 {
|
||||||
|
let mut out = String::new();
|
||||||
|
input.read_line(&mut out)?;
|
||||||
|
return Ok(out.into_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out = std::io::stdout();
|
||||||
|
let _ignore_error = out.write_all(query.as_bytes());
|
||||||
|
let _ignore_error = out.flush();
|
||||||
|
|
||||||
|
let infd = input.as_raw_fd();
|
||||||
|
let mut termios = MaybeUninit::<libc::termios>::uninit();
|
||||||
|
if unsafe { libc::tcgetattr(infd, &mut *termios.as_mut_ptr()) } != 0 {
|
||||||
|
bail!("tcgetattr() failed");
|
||||||
|
}
|
||||||
|
let mut termios = unsafe { termios.assume_init() };
|
||||||
|
let old_termios = termios; // termios is a 'Copy' type
|
||||||
|
unsafe {
|
||||||
|
libc::cfmakeraw(&mut termios);
|
||||||
|
}
|
||||||
|
if unsafe { libc::tcsetattr(infd, libc::TCSANOW, &termios) } != 0 {
|
||||||
|
bail!("tcsetattr() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut password = Vec::<u8>::new();
|
||||||
|
let mut asterisks = true;
|
||||||
|
|
||||||
|
let ok: Result<(), Error> = try_block!({
|
||||||
|
for byte in input.bytes() {
|
||||||
|
let byte = byte?;
|
||||||
|
match byte {
|
||||||
|
3 => bail!("cancelled"), // ^C
|
||||||
|
4 => break, // ^D / EOF
|
||||||
|
9 => asterisks = false, // tab disables echo
|
||||||
|
0xA | 0xD => {
|
||||||
|
// newline, we're done
|
||||||
|
let _ignore_error = out.write_all("\r\n".as_bytes());
|
||||||
|
let _ignore_error = out.flush();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
0x7F => {
|
||||||
|
// backspace
|
||||||
|
if !password.is_empty() {
|
||||||
|
password.pop();
|
||||||
|
if asterisks {
|
||||||
|
let _ignore_error = out.write_all("\x08 \x08".as_bytes());
|
||||||
|
let _ignore_error = out.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
password.push(other);
|
||||||
|
if asterisks {
|
||||||
|
let _ignore_error = out.write_all(b"*");
|
||||||
|
let _ignore_error = out.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
if unsafe { libc::tcsetattr(infd, libc::TCSANOW, &old_termios) } != 0 {
|
||||||
|
// not fatal...
|
||||||
|
eprintln!("failed to reset terminal attributes!");
|
||||||
|
}
|
||||||
|
match ok {
|
||||||
|
Ok(_) => Ok(password),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a password from stdin, then read again to verify it.
|
||||||
|
pub fn read_and_verify_password(prompt: &str) -> Result<Vec<u8>, Error> {
|
||||||
|
|
||||||
|
let password = String::from_utf8(read_password(prompt)?)?;
|
||||||
|
let verify_password = String::from_utf8(read_password("Verify Password: ")?)?;
|
||||||
|
|
||||||
|
if password != verify_password {
|
||||||
|
bail!("Passwords do not match!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if password.len() < 5 {
|
||||||
|
bail!("Password too short!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(password.into_bytes())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user