tiny_std/linux/
get_pass.rs1use crate::error::Error;
2use core::ops::{BitAndAssign, BitOrAssign};
3use rusl::platform::{SetAction, STDIN};
4use rusl::termios::{tcgetattr, tcsetattr};
5
6pub fn get_pass(buf: &mut [u8]) -> crate::error::Result<&str> {
15 #[cold]
20 unsafe fn drain_newline(buf: &mut [u8]) -> Result<(), Error> {
21 loop {
22 let res = rusl::unistd::read(STDIN, buf)?;
23 if res == 0 || res < buf.len() {
25 return Ok(());
26 }
27 let last = *buf.last().unwrap_unchecked();
28 if last == b'\n' {
29 return Ok(());
30 }
31 }
32 }
33 if buf.is_empty() {
34 return Err(Error::Uncategorized(
35 "Supplied a zero-length buffer to getpass, need an initialized buffer to populate",
36 ));
37 }
38 let mut stdin_term = tcgetattr(STDIN)?;
39 let orig_flags = stdin_term;
40 let iflag = &mut stdin_term.0.c_lflag;
41 iflag.bitand_assign(!(rusl::platform::ECHO as u32));
42 iflag.bitor_assign(rusl::platform::ECHONL as u32);
43 tcsetattr(STDIN, SetAction::NOW, &stdin_term)?;
44 let read = rusl::unistd::read(STDIN, buf)?;
45 unsafe {
46 if read == buf.len() && *buf.last().unwrap_unchecked() != b'\n' {
48 drain_newline(buf)?;
49 tcsetattr(STDIN, SetAction::NOW, &orig_flags)?;
50 return Err(Error::Uncategorized(
51 "Supplied a buffer that was too small to getpass, buffer overrun.",
52 ));
53 }
54 }
55 tcsetattr(STDIN, SetAction::NOW, &orig_flags)?;
56 core::str::from_utf8(&buf[..read])
57 .map_err(|_| Error::no_code("Failed to convert read password to utf8. "))
58}