From 5e3956adf1e91feeebb77e16827e2c9044d53e08 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Sat, 22 Jul 2023 17:41:30 +1000 Subject: [PATCH] misc: getty open consumer channel to get events Signed-off-by: Anhad Singh --- Cargo.lock | 67 +++++++++++- Cargo.toml | 1 + src/bin/getty.rs | 280 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 290 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7645eafa7c..1b91550329 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,6 +140,12 @@ dependencies = [ "either", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.139" @@ -158,6 +164,18 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +[[package]] +name = "orbclient" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "221d488cd70617f1bd599ed8ceb659df2147d9393717954d82a0f5e8032a6ab1" +dependencies = [ + "libc", + "redox_syscall 0.3.5", + "sdl2", + "sdl2-sys", +] + [[package]] name = "redox-daemon" version = "0.1.0" @@ -165,7 +183,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e31c834277709c7ff3eb74959fe62be4b45b1189ba9d41fd3744cd3a9c554f" dependencies = [ "libc", - "redox_syscall", + "redox_syscall 0.2.16", ] [[package]] @@ -189,13 +207,22 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_termios" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ - "redox_syscall", + "redox_syscall 0.2.16", ] [[package]] @@ -205,7 +232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "rust-argon2", ] @@ -221,6 +248,29 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "sdl2" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + [[package]] name = "strsim" version = "0.8.0" @@ -235,7 +285,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" dependencies = [ "libc", "numtoa", - "redox_syscall", + "redox_syscall 0.2.16", "redox_termios", ] @@ -260,9 +310,10 @@ version = "0.1.0" dependencies = [ "clap", "extra", + "orbclient", "redox-daemon", "redox_liner", - "redox_syscall", + "redox_syscall 0.2.16", "redox_termios", "redox_users", "termion", @@ -274,6 +325,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 7e43a168a2..2bcc0beb22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ path = "src/bin/usermod.rs" [dependencies] clap = "2.33.0" extra = { git = "https://gitlab.redox-os.org/redox-os/libextra.git" } +orbclient = "0.3.45" redox-daemon = "0.1" redox_liner = "0.5.1" redox_syscall = "0.2.12" diff --git a/src/bin/getty.rs b/src/bin/getty.rs index c3550be83d..8c72079636 100644 --- a/src/bin/getty.rs +++ b/src/bin/getty.rs @@ -1,18 +1,24 @@ #[macro_use] extern crate clap; +extern crate extra; +extern crate orbclient; extern crate redox_termios; extern crate syscall; -extern crate extra; -use std::str; use std::fs::{File, OpenOptions}; -use std::io::{self, ErrorKind, Read, Write, Stderr}; -use std::os::unix::io::{FromRawFd, RawFd}; +use std::io::{self, ErrorKind, Read, Stderr, Write}; use std::process::{Child, Command, Stdio}; +use std::str; + +use std::os::fd::AsRawFd; +use std::os::unix::io::{FromRawFd, RawFd}; + +use orbclient::{Event, EventOption}; use extra::io::fail; -const _MAN_PAGE: &'static str = /* @MANSTART{getty} */ r#" +const _MAN_PAGE: &'static str = /* @MANSTART{getty} */ + r#" NAME getty - set terminal mode @@ -39,8 +45,92 @@ AUTHOR const DEFAULT_COLS: u32 = 80; const DEFAULT_LINES: u32 = 30; -pub fn handle(event_file: &mut File, tty_fd: RawFd, master_fd: RawFd, process: &mut Child) { - let handle_event = |event_id: usize| { +fn process_events(ctrl: &mut bool, events: &[Event]) -> Vec { + let mut buf = vec![]; + + for event in events.iter() { + if let EventOption::Key(key_event) = event.to_option() { + if key_event.scancode == 0x1D { + *ctrl = key_event.pressed; + } else if key_event.pressed { + match key_event.scancode { + 0x0E => { + // Backspace + buf.extend_from_slice(b"\x7F"); + } + 0x47 => { + // Home + buf.extend_from_slice(b"\x1B[H"); + } + 0x48 => { + // Up + buf.extend_from_slice(b"\x1B[A"); + } + 0x49 => { + // Page up + buf.extend_from_slice(b"\x1B[5~"); + } + 0x4B => { + // Left + buf.extend_from_slice(b"\x1B[D"); + } + 0x4D => { + // Right + buf.extend_from_slice(b"\x1B[C"); + } + 0x4F => { + // End + buf.extend_from_slice(b"\x1B[F"); + } + 0x50 => { + // Down + buf.extend_from_slice(b"\x1B[B"); + } + 0x51 => { + // Page down + buf.extend_from_slice(b"\x1B[6~"); + } + 0x52 => { + // Insert + buf.extend_from_slice(b"\x1B[2~"); + } + 0x53 => { + // Delete + buf.extend_from_slice(b"\x1B[3~"); + } + _ => { + let c = match key_event.character { + c @ 'A'..='Z' if *ctrl => ((c as u8 - b'A') + b'\x01') as char, + c @ 'a'..='z' if *ctrl => ((c as u8 - b'a') + b'\x01') as char, + c => c, + }; + + if c != '\0' { + let mut b = [0; 4]; + buf.extend_from_slice(c.encode_utf8(&mut b).as_bytes()); + } + } + } + } + } + } + + buf +} + +pub fn handle( + event_file: &mut File, + tty_fd: RawFd, + consumer_fd: RawFd, + master_fd: RawFd, + process: &mut Child, +) { + // tty_fd => Display + // master_fd => PTY + // consumer_fd => Either(`input:consumer/{#VT}`, $DEVICE) + + let mut ctrl = false; + let mut handle_event = |event_id: usize| { if event_id as RawFd == tty_fd { let mut packet = [0; 4096]; loop { @@ -48,9 +138,10 @@ pub fn handle(event_file: &mut File, tty_fd: RawFd, master_fd: RawFd, process: & Ok(0) => return, Ok(count) => count, Err(ref err) if err.errno == syscall::EAGAIN => break, - Err(_) => panic!("getty: failed to read from TTY") + Err(_) => panic!("getty: failed to read from TTY"), }; - syscall::write(master_fd as usize, &packet[..count]).expect("getty: failed to write master PTY"); + syscall::write(master_fd as usize, &packet[..count]) + .expect("getty: failed to write master PTY"); } } else if event_id as RawFd == master_fd { let mut packet = [0; 4096]; @@ -59,9 +150,35 @@ pub fn handle(event_file: &mut File, tty_fd: RawFd, master_fd: RawFd, process: & Ok(0) => return, Ok(count) => count, Err(ref err) if err.errno == syscall::EAGAIN => break, - Err(_) => panic!("getty: failed to read from master TTY") + Err(_) => panic!("getty: failed to read from master TTY"), }; - syscall::write(tty_fd as usize, &packet[1..count]).expect("getty: failed to write to TTY"); + syscall::write(tty_fd as usize, &packet[1..count]) + .expect("getty: failed to write to TTY"); + if packet[0] & 1 == 1 { + let _ = syscall::fsync(tty_fd as usize); + } + } + } else if event_id as RawFd == consumer_fd { + let mut packet = [0; 4096]; + loop { + let count = match syscall::read(consumer_fd as usize, &mut packet) { + Ok(0) => return, + Ok(count) => count, + Err(ref err) if err.errno == syscall::EAGAIN => break, + Err(_) => panic!("getty: failed to read from master TTY"), + }; + + let events = unsafe { + core::slice::from_raw_parts( + packet.as_ptr() as *const Event, + count / core::mem::size_of::(), + ) + }; + + let buf = process_events(&mut ctrl, events); + syscall::write(master_fd as usize, buf.as_slice()) + .expect("getty: failed to write to TTY"); + if packet[0] & 1 == 1 { let _ = syscall::fsync(tty_fd as usize); } @@ -73,21 +190,24 @@ pub fn handle(event_file: &mut File, tty_fd: RawFd, master_fd: RawFd, process: & handle_event(tty_fd as usize); handle_event(master_fd as usize); + handle_event(consumer_fd as usize); 'events: loop { let mut sys_event = syscall::Event::default(); - event_file.read(&mut sys_event).expect("getty: failed to read event file"); + event_file + .read(&mut sys_event) + .expect("getty: failed to read event file"); handle_event(sys_event.id); match process.try_wait() { Ok(status) => match status { Some(_code) => break 'events, - None => () + None => (), }, Err(err) => match err.kind() { ErrorKind::WouldBlock => (), - _ => panic!("getty: failed to wait on child: {:?}", err) - } + _ => panic!("getty: failed to wait on child: {:?}", err), + }, } } @@ -96,22 +216,31 @@ pub fn handle(event_file: &mut File, tty_fd: RawFd, master_fd: RawFd, process: & } pub fn getpty(columns: u32, lines: u32) -> (RawFd, String) { - let master = syscall::open("pty:", syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("getty: failed to create PTY"); + let master = syscall::open( + "pty:", + syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ) + .expect("getty: failed to create PTY"); if let Ok(winsize_fd) = syscall::dup(master, b"winsize") { - let _ = syscall::write(winsize_fd, &redox_termios::Winsize { - ws_row: lines as u16, - ws_col: columns as u16 - }); + let _ = syscall::write( + winsize_fd, + &redox_termios::Winsize { + ws_row: lines as u16, + ws_col: columns as u16, + }, + ); let _ = syscall::close(winsize_fd); } let mut buf: [u8; 4096] = [0; 4096]; let count = syscall::fpath(master, &mut buf).unwrap(); - (master as RawFd, unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) }) + (master as RawFd, unsafe { + String::from_utf8_unchecked(Vec::from(&buf[..count])) + }) } -fn daemon(tty_fd: RawFd, clear: bool, stderr: &mut Stderr) { +fn daemon(tty_fd: RawFd, consumer_fd: RawFd, clear: bool, stderr: &mut Stderr) { let (columns, lines) = { let mut path = [0; 4096]; if let Ok(count) = syscall::fpath(tty_fd as usize, &mut path) { @@ -134,17 +263,21 @@ fn daemon(tty_fd: RawFd, clear: bool, stderr: &mut Stderr) { .open("event:") .expect("getty: failed to open event file"); - event_file.write(&syscall::Event { - id: tty_fd as usize, - flags: syscall::flag::EVENT_READ, - data: 0 - }).expect("getty: failed to fevent TTY"); + event_file + .write(&syscall::Event { + id: consumer_fd as usize, + flags: syscall::flag::EVENT_READ, + data: 0, + }) + .expect("getty: failed to fevent TTY"); - event_file.write(&syscall::Event { - id: master_fd as usize, - flags: syscall::flag::EVENT_READ, - data: 0 - }).expect("getty: failed to fevent master PTY"); + event_file + .write(&syscall::Event { + id: master_fd as usize, + flags: syscall::flag::EVENT_READ, + data: 0, + }) + .expect("getty: failed to fevent master PTY"); loop { if clear { @@ -152,29 +285,36 @@ fn daemon(tty_fd: RawFd, clear: bool, stderr: &mut Stderr) { } let _ = syscall::fsync(tty_fd as usize); - let slave_stdin = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_RDONLY).expect("getty: failed to open slave stdin"); - let slave_stdout = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_WRONLY).expect("getty: failed to open slave stdout"); - let slave_stderr = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_WRONLY).expect("getty: failed to open slave stderr"); + let slave_stdin = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_RDONLY) + .expect("getty: failed to open slave stdin"); + let slave_stdout = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_WRONLY) + .expect("getty: failed to open slave stdout"); + let slave_stderr = syscall::open(&pty, syscall::O_CLOEXEC | syscall::O_WRONLY) + .expect("getty: failed to open slave stderr"); let mut command = Command::new("login"); unsafe { command - .stdin(Stdio::from_raw_fd(slave_stdin as RawFd)) - .stdout(Stdio::from_raw_fd(slave_stdout as RawFd)) - .stderr(Stdio::from_raw_fd(slave_stderr as RawFd)) - .env("COLUMNS", format!("{}", columns)) - .env("LINES", format!("{}", lines)) - .env("TERM", "xterm-256color") - .env("TTY", &pty); + .stdin(Stdio::from_raw_fd(slave_stdin as RawFd)) + .stdout(Stdio::from_raw_fd(slave_stdout as RawFd)) + .stderr(Stdio::from_raw_fd(slave_stderr as RawFd)) + .env("COLUMNS", format!("{}", columns)) + .env("LINES", format!("{}", lines)) + .env("TERM", "xterm-256color") + .env("TTY", &pty); } match command.spawn() { Ok(mut process) => { - handle(&mut event_file, tty_fd, master_fd, &mut process); - }, - Err(err) => { - fail(&format!("getty: failed to execute login: {}", err), stderr) + handle( + &mut event_file, + tty_fd, + consumer_fd, + master_fd, + &mut process, + ); } + Err(err) => fail(&format!("getty: failed to execute login: {}", err), stderr), } } } @@ -187,19 +327,53 @@ pub fn main() { (about: "Set terminal mode") (@arg TTY: +required "") (@arg NO_CLEAR: -J --("no-clear") "Do not clear the screen before forking") - ).get_matches(); + ) + .get_matches(); let clear = !args.is_present("NO_CLEAR"); - let tty = args.value_of("TTY").unwrap(); - let tty_fd = match syscall::open(tty, syscall::O_CLOEXEC | syscall::flag::O_RDWR | syscall::flag::O_NONBLOCK) { - Ok(fd) => fd, - Err(err) => fail(&format!("getty: failed to open TTY {}: {}", tty, err), &mut stderr), + let vt = args.value_of("TTY").unwrap(); + + let mut buf = [0; 1024]; + let (vt_path, consumer) = if vt.parse::().is_ok() { + let consumer = + File::open(format!("input:consumer/{vt}")).expect("getty: failed to open consumer"); + + let written = syscall::fpath(consumer.as_raw_fd() as usize, &mut buf) + .expect("getty: failed to get the display"); + assert!(written <= buf.len()); + + ( + core::str::from_utf8(&buf[..written]) + .expect("getty: UTF-8 validation failed for the display path"), + consumer, + ) + } else { + (vt, File::open(vt).expect("getty: failed to open display")) }; + let tty_fd = match syscall::open( + vt_path, + syscall::O_CLOEXEC | syscall::flag::O_RDWR | syscall::flag::O_NONBLOCK, + ) { + Ok(fd) => fd, + Err(err) => fail( + &format!("getty: failed to open TTY {}: {}", vt_path, err), + &mut stderr, + ), + }; + + let consumer_fd = consumer.as_raw_fd(); + redox_daemon::Daemon::new(|d| { d.ready().expect("getty: failed to notify "); - daemon(tty_fd as RawFd, clear, &mut stderr); + daemon(tty_fd as RawFd, consumer_fd, clear, &mut stderr); std::process::exit(0); - }).unwrap_or_else(|err| fail(&format!("getty: failed to fork login: {}", err), &mut stderr)); + }) + .unwrap_or_else(|err| { + fail( + &format!("getty: failed to fork login: {}", err), + &mut stderr, + ) + }); }