]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/tty.rs
docs: fix typos
[proxmox-backup.git] / src / tools / tty.rs
CommitLineData
c9b296f1
WB
1//! Helpers for terminal interaction
2
3use std::io::{Read, Write};
48b85e8e 4use std::mem::MaybeUninit;
c9b296f1
WB
5use std::os::unix::io::AsRawFd;
6
7use failure::*;
8
9ea4bce4 9use proxmox::try_block;
c9b296f1
WB
10
11/// Returns whether the current stdin is a tty .
12pub fn stdin_isatty() -> bool {
13 unsafe { libc::isatty(std::io::stdin().as_raw_fd()) == 1 }
14}
15
16/// Read a password from stdin, masking the echoed output with asterisks and writing a query first.
17pub fn read_password(query: &str) -> Result<Vec<u8>, Error> {
18 let input = std::io::stdin();
19 if unsafe { libc::isatty(input.as_raw_fd()) } != 1 {
20 let mut out = String::new();
21 input.read_line(&mut out)?;
22 return Ok(out.into_bytes());
23 }
24
25 let mut out = std::io::stdout();
26 let _ignore_error = out.write_all(query.as_bytes());
27 let _ignore_error = out.flush();
28
29 let infd = input.as_raw_fd();
48b85e8e
WB
30 let mut termios = MaybeUninit::<libc::termios>::uninit();
31 if unsafe { libc::tcgetattr(infd, &mut *termios.as_mut_ptr()) } != 0 {
c9b296f1
WB
32 bail!("tcgetattr() failed");
33 }
48b85e8e 34 let mut termios = unsafe { termios.assume_init() };
62ee2eb4 35 let old_termios = termios; // termios is a 'Copy' type
c9b296f1
WB
36 unsafe {
37 libc::cfmakeraw(&mut termios);
38 }
39 if unsafe { libc::tcsetattr(infd, libc::TCSANOW, &termios) } != 0 {
40 bail!("tcsetattr() failed");
41 }
42
43 let mut password = Vec::<u8>::new();
44 let mut asterisks = true;
45
46 let ok: Result<(), Error> = try_block!({
47 for byte in input.bytes() {
48 let byte = byte?;
49 match byte {
50 3 => bail!("cancelled"), // ^C
48b85e8e
WB
51 4 => break, // ^D / EOF
52 9 => asterisks = false, // tab disables echo
53 0xA | 0xD => {
54 // newline, we're done
c9b296f1
WB
55 let _ignore_error = out.write_all("\r\n".as_bytes());
56 let _ignore_error = out.flush();
57 break;
58 }
48b85e8e
WB
59 0x7F => {
60 // backspace
62ee2eb4 61 if !password.is_empty() {
c9b296f1
WB
62 password.pop();
63 if asterisks {
64 let _ignore_error = out.write_all("\x08 \x08".as_bytes());
65 let _ignore_error = out.flush();
66 }
67 }
68 }
69 other => {
70 password.push(other);
71 if asterisks {
62ee2eb4 72 let _ignore_error = out.write_all(b"*");
c9b296f1
WB
73 let _ignore_error = out.flush();
74 }
75 }
76 }
77 }
78 Ok(())
79 });
80 if unsafe { libc::tcsetattr(infd, libc::TCSANOW, &old_termios) } != 0 {
81 // not fatal...
82 eprintln!("failed to reset terminal attributes!");
83 }
84 match ok {
85 Ok(_) => Ok(password),
86 Err(e) => Err(e),
87 }
88}
cbe01dc5
OB
89
90pub fn read_and_verify_password(prompt: &str) -> Result<Vec<u8>, Error> {
91
92 let password = String::from_utf8(crate::tools::tty::read_password(prompt)?)?;
93 let verify_password = String::from_utf8(crate::tools::tty::read_password("Verify Password: ")?)?;
94
95 if password != verify_password {
96 bail!("Passwords do not match!");
97 }
98
99 if password.len() < 5 {
100 bail!("Password too short!");
101 }
102
103 Ok(password.into_bytes())
104}