Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

my take #2

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 38 additions & 34 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,54 @@ extern crate libc;
#[macro_use]
extern crate simple_error;

use std::os::unix::io::IntoRawFd;
use std::os::unix::io::FromRawFd;
use std::os::unix::io::AsRawFd;
use simple_error::SimpleError;
use std::io::{Stdin, Read, Write};

fn main() -> std::result::Result<(), Box<dyn std::error::Error> > {
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
// 1 -- First get the original terminal attributes
let mut stdin = std::io::stdin();
let mut stdout = std::io::stdout();

let orig_attr = nix::sys::termios::tcgetattr(stdin.as_raw_fd())?;

let stdin = std::io::stdin();
let window : libc::winsize = unsafe {
get_window(&stdin)?
};

println!("{:?}", window);
let stdin = std::io::stdin().as_raw_fd();
let stdout = std::io::stdout().as_raw_fd();

let orig_attr = nix::sys::termios::tcgetattr(stdin)?;

if let Err(err) = script(&window, stdin, &orig_attr, stdout) {
nix::sys::termios::tcsetattr(
stdin,
nix::sys::termios::SetArg::TCSANOW,
&orig_attr,
)?;
Err(err)
} else {
Ok(())
}
}

let fork_result = nix::pty::forkpty(Some(&window), Some(&orig_attr))?;
fn script(window: &libc::winsize, stdin: i32, stdin_attr: &nix::sys::termios::Termios, stdout: i32) -> Result<(), Box<dyn std::error::Error>> {
let fork_result = nix::pty::forkpty(Some(window), Some(stdin_attr))?;

match fork_result.fork_result {

// the child simply exec's into a shell
nix::unistd::ForkResult::Child => {
// TODO: Figure out why executing other shells other than bash or sh
// cause failure.

/*
let shell = std::env::var_os("SHELL")
.unwrap_or(std::ffi::OsString::from("/bin/sh"))
.into_string().expect("We expected to convert from OString to String");
*/

nix::sys::termios::tcsetattr(
stdin,
nix::sys::termios::SetArg::TCSANOW,
&stdin_attr,
)?;
let shell = "/bin/bash";

let c_str = std::ffi::CString::new(shell).expect("CString::new failed");
nix::unistd::execv(&c_str, &[]);
nix::unistd::execv(&c_str, &[&c_str])?;
}

// the parent will relay data between terminal and pty master
nix::unistd::ForkResult::Parent { child, .. } => {
nix::unistd::ForkResult::Parent { .. } => {
let mut master_file : std::fs::File = unsafe {
std::fs::File::from_raw_fd(fork_result.master)
};
Expand All @@ -55,34 +61,34 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error> > {
// https://linux.die.net/man/4/ptmx
// Each file descriptor obtained by opening /dev/ptmx
// is an independent PTM with its own associated pseudoterminal slaves (PTS)
println!("Executing parent.");
println!("Child Pid: {:?}", child);
println!("Master Fd: {:?}", master_file);

let mut output_file = std::fs::File::create("typescript")?;

let mut tty = nix::sys::termios::tcgetattr(stdin.as_raw_fd())?;
let mut tty = nix::sys::termios::tcgetattr(stdin)?;
nix::sys::termios::cfmakeraw(&mut tty);
nix::sys::termios::tcsetattr(
stdin,
nix::sys::termios::SetArg::TCSANOW,
&tty,
)?;

let mut in_fds = nix::sys::select::FdSet::new();

let mut buffer = [0; 256];
loop {

in_fds.clear();
in_fds.insert(stdin.as_raw_fd());
in_fds.insert(stdin);
in_fds.insert(fork_result.master);

let _ = nix::sys::select::select(None, Some(&mut in_fds), None, None, None)?;

// if the terminal has any input available, then the program reads some of that
// input and writes it to the pseudoterminal master
if in_fds.contains(stdin.as_raw_fd()) {
let bytes_read = stdin.read(&mut buffer)?;
if in_fds.contains(stdin) {
let bytes_read = nix::unistd::read(stdin, &mut buffer)?;
let bytes_written = master_file.write(&buffer[..bytes_read])?;

//flush it
master_file.flush()?;
if bytes_read != bytes_written {
bail!("partial failed read[{}]/write[{}] (masterFd)", bytes_read, bytes_written);
}
Expand All @@ -93,8 +99,8 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error> > {
if in_fds.contains(fork_result.master) {
let bytes_read = master_file.read(&mut buffer)?;

let bytes_written = stdout.write(&buffer[..bytes_read])?;
if bytes_written != bytes_read {
let bytes_written = nix::unistd::write(stdout, &buffer[..bytes_read])?;
if bytes_written != bytes_read {
bail!("partial failed read[{}]/write[{}] (stdout)", bytes_read, bytes_written);
}

Expand All @@ -108,7 +114,6 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error> > {
}
}


Ok(())
}

Expand All @@ -120,4 +125,3 @@ unsafe fn get_window(stdin: &Stdin) -> Result<libc::winsize, SimpleError> {
}
Ok(window.assume_init())
}