//! getty [-1] DEVICE -- run a shell on another terminal, e.g. /dev/hvc0. //! //! Real Unix systems run one "getty" per terminal line: it opens the line //! and starts a login shell on it. Ours skips the login. Since stage 15 it //! does it the classic Unix way, with fork + exec: //! //! 1. fork: the child will become the shell. //! 2. setsid (in the child): a new *session* with no terminal yet. //! 3. open DEVICE: a session leader without a terminal that opens one //! gets it as its *controlling terminal*. That's what lets ^C on //! that terminal send SIGINT to the right programs. //! 4. dup2 it onto fds 0, 1 and 2 (stdin, stdout, stderr). //! 5. execve the shell: it keeps the fds and the session. //! //! getty waits for the shell; when it exits (`exit`) a new one starts, //! unless `-1` was given. //! //! Try it: `getty /dev/hvc0 &` here, then on the host //! `socat - UNIX-CONNECT:target/hvc0.sock` and type commands. #![no_std] #![no_main] #[macro_use] #[path = "ulib.rs"] mod ulib; use ulib::*; const SHELL: &str = "/mnt/disk0/bin/sh"; /// The child's side (steps 2-5). Only returns on failure. fn become_shell(dev: &str) -> i64 { setsid(); let fd = open(dev, O_RDWR); // becomes our controlling terminal if fd < 0 { eprintln!("getty: {}: {}", dev, strerror(fd)); return 126; } for target in 0..3 { dup2(fd, target); } if fd > 2 { close(fd); } let greeting = "\nRustbelt getty: shell on this terminal (type `exit` to end it)\n"; write(1, greeting.as_bytes()); let err = execve(SHELL, &["sh"], &environ()); eprintln!("getty: {}: {}", SHELL, strerror(err)); 127 } fn main() -> i64 { let (once, dev) = match (arg(1), arg(2)) { (Some("-1"), Some(dev)) => (true, dev), (Some(dev), None) if dev != "-1" => (false, dev), _ => { eprintln!("usage: getty [-1] DEVICE (e.g. getty /dev/hvc0 &)"); return 2; } }; loop { let pid = fork(); if pid == 0 { exit(become_shell(dev)); } if pid < 0 { eprintln!("getty: fork: {}", strerror(pid)); return 1; } let status = wait(pid); if once || status == 126 || status == 127 { return status; // done, or the terminal/shell can't be opened } sleep_ms(500); // don't spin if the shell keeps dying at once } }