2024-01-03 16:59:42 +01:00
|
|
|
use crate::io::set_non_blocking;
|
2024-01-18 12:02:08 +01:00
|
|
|
use crate::tty::{Tty, TtySize};
|
2024-01-06 21:54:57 +01:00
|
|
|
use anyhow::{bail, Result};
|
2024-01-06 16:27:48 +01:00
|
|
|
use nix::errno::Errno;
|
2024-02-12 11:25:14 +01:00
|
|
|
use nix::libc::{SIGALRM, SIGCHLD};
|
2024-01-06 21:54:57 +01:00
|
|
|
use nix::sys::select::{select, FdSet};
|
2024-01-03 16:59:42 +01:00
|
|
|
use nix::{libc, pty, sys::signal, sys::wait, unistd, unistd::ForkResult};
|
2024-01-06 21:54:57 +01:00
|
|
|
use signal_hook::consts::{SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGWINCH};
|
|
|
|
|
use signal_hook::SigId;
|
2023-12-23 16:42:40 +01:00
|
|
|
use std::collections::HashMap;
|
2023-10-28 15:20:19 +02:00
|
|
|
use std::ffi::{CString, NulError};
|
2024-01-09 11:18:44 +01:00
|
|
|
use std::io::{self, ErrorKind, Read, Write};
|
2024-01-06 16:27:48 +01:00
|
|
|
use std::os::fd::{AsFd, RawFd};
|
2024-01-09 11:56:12 +01:00
|
|
|
use std::os::fd::{BorrowedFd, OwnedFd};
|
2023-10-21 14:40:39 +02:00
|
|
|
use std::os::unix::io::{AsRawFd, FromRawFd};
|
2024-02-01 11:43:01 +01:00
|
|
|
use std::str::FromStr;
|
2023-12-23 16:42:40 +01:00
|
|
|
use std::{env, fs};
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2023-12-23 16:42:40 +01:00
|
|
|
type ExtraEnv = HashMap<String, String>;
|
|
|
|
|
|
2023-10-24 10:29:09 +02:00
|
|
|
pub trait Recorder {
|
2024-01-18 12:02:08 +01:00
|
|
|
fn start(&mut self, size: TtySize) -> io::Result<()>;
|
2023-10-24 10:29:09 +02:00
|
|
|
fn output(&mut self, data: &[u8]);
|
2024-01-12 12:18:04 +01:00
|
|
|
fn input(&mut self, data: &[u8]) -> bool;
|
2024-01-18 12:02:08 +01:00
|
|
|
fn resize(&mut self, size: TtySize);
|
2023-10-24 10:29:09 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
pub fn exec<S: AsRef<str>, T: Tty + ?Sized, R: Recorder>(
|
2024-01-10 15:31:45 +01:00
|
|
|
command: &[S],
|
2023-12-23 16:42:40 +01:00
|
|
|
extra_env: &ExtraEnv,
|
2024-01-10 10:42:57 +01:00
|
|
|
tty: &mut T,
|
2024-02-01 11:43:01 +01:00
|
|
|
winsize_override: Option<WinsizeOverride>,
|
2023-10-28 15:20:19 +02:00
|
|
|
recorder: &mut R,
|
2024-01-06 21:56:09 +01:00
|
|
|
) -> Result<i32> {
|
2024-02-01 11:43:01 +01:00
|
|
|
let winsize = get_winsize(&*tty, winsize_override.as_ref());
|
2024-01-18 12:02:08 +01:00
|
|
|
recorder.start(winsize.into())?;
|
2023-10-21 14:40:39 +02:00
|
|
|
let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
|
|
|
|
|
|
|
|
|
|
match result.fork_result {
|
2023-10-30 13:32:26 +01:00
|
|
|
ForkResult::Parent { child } => handle_parent(
|
|
|
|
|
result.master.as_raw_fd(),
|
|
|
|
|
child,
|
2024-01-05 14:53:01 +01:00
|
|
|
tty,
|
2023-10-30 13:32:26 +01:00
|
|
|
winsize_override,
|
|
|
|
|
recorder,
|
|
|
|
|
),
|
2023-10-21 14:40:39 +02:00
|
|
|
|
|
|
|
|
ForkResult::Child => {
|
2024-01-10 15:31:45 +01:00
|
|
|
handle_child(command, extra_env)?;
|
2023-10-21 15:21:53 +02:00
|
|
|
unreachable!();
|
2023-10-21 14:40:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
fn handle_parent<T: Tty + ?Sized, R: Recorder>(
|
2023-10-24 10:29:09 +02:00
|
|
|
master_fd: RawFd,
|
|
|
|
|
child: unistd::Pid,
|
2024-01-10 10:42:57 +01:00
|
|
|
tty: &mut T,
|
2024-02-01 11:43:01 +01:00
|
|
|
winsize_override: Option<WinsizeOverride>,
|
2023-10-24 10:29:09 +02:00
|
|
|
recorder: &mut R,
|
2024-01-06 21:56:09 +01:00
|
|
|
) -> Result<i32> {
|
2024-01-05 14:53:01 +01:00
|
|
|
let copy_result = copy(master_fd, child, tty, winsize_override, recorder);
|
2023-10-21 15:21:53 +02:00
|
|
|
let wait_result = wait::waitpid(child, None);
|
|
|
|
|
copy_result?;
|
|
|
|
|
|
|
|
|
|
match wait_result {
|
|
|
|
|
Ok(wait::WaitStatus::Exited(_pid, status)) => Ok(status),
|
|
|
|
|
Ok(wait::WaitStatus::Signaled(_pid, signal, ..)) => Ok(128 + signal as i32),
|
|
|
|
|
Ok(_) => Ok(1),
|
|
|
|
|
Err(e) => Err(anyhow::anyhow!(e)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-21 14:40:39 +02:00
|
|
|
const BUF_SIZE: usize = 128 * 1024;
|
|
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
fn copy<T: Tty + ?Sized, R: Recorder>(
|
2024-01-06 16:27:48 +01:00
|
|
|
master_raw_fd: RawFd,
|
2023-10-31 10:18:43 +01:00
|
|
|
child: unistd::Pid,
|
2024-01-10 10:42:57 +01:00
|
|
|
tty: &mut T,
|
2024-02-01 11:43:01 +01:00
|
|
|
winsize_override: Option<WinsizeOverride>,
|
2023-10-30 13:32:26 +01:00
|
|
|
recorder: &mut R,
|
2024-01-06 21:56:09 +01:00
|
|
|
) -> Result<()> {
|
2024-01-06 16:27:48 +01:00
|
|
|
let mut master = unsafe { fs::File::from_raw_fd(master_raw_fd) };
|
2023-10-21 14:40:39 +02:00
|
|
|
let mut buf = [0u8; BUF_SIZE];
|
|
|
|
|
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
|
|
|
|
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
2024-01-09 11:18:44 +01:00
|
|
|
let mut master_closed = false;
|
2024-02-12 11:25:14 +01:00
|
|
|
|
2024-01-06 21:54:57 +01:00
|
|
|
let sigwinch_fd = SignalFd::open(SIGWINCH)?;
|
|
|
|
|
let sigint_fd = SignalFd::open(SIGINT)?;
|
|
|
|
|
let sigterm_fd = SignalFd::open(SIGTERM)?;
|
|
|
|
|
let sigquit_fd = SignalFd::open(SIGQUIT)?;
|
|
|
|
|
let sighup_fd = SignalFd::open(SIGHUP)?;
|
2024-02-12 11:25:14 +01:00
|
|
|
let sigalrm_fd = SignalFd::open(SIGALRM)?;
|
|
|
|
|
let sigchld_fd = SignalFd::open(SIGCHLD)?;
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-06 16:27:48 +01:00
|
|
|
set_non_blocking(&master_raw_fd)?;
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let master_fd = master.as_fd();
|
|
|
|
|
let tty_fd = tty.as_fd();
|
|
|
|
|
let mut rfds = FdSet::new();
|
|
|
|
|
let mut wfds = FdSet::new();
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-06 16:27:48 +01:00
|
|
|
rfds.insert(&tty_fd);
|
2024-01-06 21:54:57 +01:00
|
|
|
rfds.insert(&sigwinch_fd);
|
|
|
|
|
rfds.insert(&sigint_fd);
|
|
|
|
|
rfds.insert(&sigterm_fd);
|
|
|
|
|
rfds.insert(&sigquit_fd);
|
|
|
|
|
rfds.insert(&sighup_fd);
|
2024-02-12 11:25:14 +01:00
|
|
|
rfds.insert(&sigalrm_fd);
|
|
|
|
|
rfds.insert(&sigchld_fd);
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-09 11:18:44 +01:00
|
|
|
if !master_closed {
|
2024-01-06 16:27:48 +01:00
|
|
|
rfds.insert(&master_fd);
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-09 11:18:44 +01:00
|
|
|
if !input.is_empty() {
|
|
|
|
|
wfds.insert(&master_fd);
|
|
|
|
|
}
|
2024-01-06 16:27:48 +01:00
|
|
|
}
|
2023-10-30 13:32:26 +01:00
|
|
|
|
2024-01-06 16:27:48 +01:00
|
|
|
if !output.is_empty() {
|
|
|
|
|
wfds.insert(&tty_fd);
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-06 21:54:57 +01:00
|
|
|
if let Err(e) = select(None, &mut rfds, &mut wfds, None, None) {
|
2024-01-06 16:27:48 +01:00
|
|
|
if e == Errno::EINTR {
|
2023-10-30 13:32:26 +01:00
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
bail!(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-06 16:27:48 +01:00
|
|
|
let master_read = rfds.contains(&master_fd);
|
|
|
|
|
let master_write = wfds.contains(&master_fd);
|
|
|
|
|
let tty_read = rfds.contains(&tty_fd);
|
|
|
|
|
let tty_write = wfds.contains(&tty_fd);
|
2024-01-06 21:54:57 +01:00
|
|
|
let sigwinch_read = rfds.contains(&sigwinch_fd);
|
|
|
|
|
let sigint_read = rfds.contains(&sigint_fd);
|
|
|
|
|
let sigterm_read = rfds.contains(&sigterm_fd);
|
|
|
|
|
let sigquit_read = rfds.contains(&sigquit_fd);
|
|
|
|
|
let sighup_read = rfds.contains(&sighup_fd);
|
2024-02-12 11:25:14 +01:00
|
|
|
let sigalrm_read = rfds.contains(&sigalrm_fd);
|
|
|
|
|
let sigchld_read = rfds.contains(&sigchld_fd);
|
2024-01-06 16:27:48 +01:00
|
|
|
|
|
|
|
|
if master_read {
|
2024-01-10 10:42:57 +01:00
|
|
|
while let Some(n) = read_non_blocking(&mut master, &mut buf)? {
|
2024-01-09 11:18:44 +01:00
|
|
|
if n > 0 {
|
|
|
|
|
recorder.output(&buf[0..n]);
|
|
|
|
|
output.extend_from_slice(&buf[0..n]);
|
|
|
|
|
} else if output.is_empty() {
|
|
|
|
|
return Ok(());
|
|
|
|
|
} else {
|
|
|
|
|
master_closed = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2024-01-06 16:27:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if master_write {
|
2024-01-09 11:18:44 +01:00
|
|
|
let mut buf: &[u8] = input.as_ref();
|
|
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
while let Some(n) = write_non_blocking(&mut master, buf)? {
|
2024-01-09 11:18:44 +01:00
|
|
|
buf = &buf[n..];
|
|
|
|
|
|
|
|
|
|
if buf.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let left = buf.len();
|
|
|
|
|
|
|
|
|
|
if left == 0 {
|
|
|
|
|
input.clear();
|
|
|
|
|
} else {
|
|
|
|
|
let rot = input.len() - left;
|
|
|
|
|
input.rotate_left(rot);
|
|
|
|
|
input.truncate(left);
|
|
|
|
|
}
|
2024-01-06 16:27:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tty_write {
|
2024-01-09 11:18:44 +01:00
|
|
|
let mut buf: &[u8] = output.as_ref();
|
2024-01-06 16:27:48 +01:00
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
while let Some(n) = write_non_blocking(tty, buf)? {
|
2024-01-09 11:18:44 +01:00
|
|
|
buf = &buf[n..];
|
|
|
|
|
|
|
|
|
|
if buf.is_empty() {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let left = buf.len();
|
|
|
|
|
|
|
|
|
|
if left == 0 {
|
|
|
|
|
if master_closed {
|
|
|
|
|
return Ok(());
|
|
|
|
|
} else {
|
|
|
|
|
output.clear();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
let rot = output.len() - left;
|
|
|
|
|
output.rotate_left(rot);
|
|
|
|
|
output.truncate(left);
|
2024-01-06 16:27:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if tty_read {
|
2024-01-10 10:42:57 +01:00
|
|
|
while let Some(n) = read_non_blocking(tty, &mut buf)? {
|
2024-01-09 11:18:44 +01:00
|
|
|
if n > 0 {
|
2024-01-12 12:18:04 +01:00
|
|
|
if recorder.input(&buf[0..n]) {
|
|
|
|
|
input.extend_from_slice(&buf[0..n]);
|
|
|
|
|
}
|
2024-01-09 11:18:44 +01:00
|
|
|
} else {
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2023-10-21 14:40:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-06 21:54:57 +01:00
|
|
|
|
|
|
|
|
if sigwinch_read {
|
|
|
|
|
sigwinch_fd.flush();
|
2024-02-01 11:43:01 +01:00
|
|
|
let winsize = get_winsize(&*tty, winsize_override.as_ref());
|
2024-01-06 21:54:57 +01:00
|
|
|
set_pty_size(master_raw_fd, &winsize);
|
2024-01-18 12:02:08 +01:00
|
|
|
recorder.resize(winsize.into());
|
2024-01-06 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sigint_read {
|
|
|
|
|
sigint_fd.flush();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 11:36:13 +01:00
|
|
|
if sigterm_read || sigquit_read || sighup_read || sigalrm_read || sigchld_read {
|
2024-01-06 21:54:57 +01:00
|
|
|
if sigterm_read {
|
|
|
|
|
sigterm_fd.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sigquit_read {
|
|
|
|
|
sigquit_fd.flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sighup_read {
|
|
|
|
|
sighup_fd.flush();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 11:25:14 +01:00
|
|
|
if sigalrm_read {
|
|
|
|
|
sigalrm_fd.flush();
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 11:36:13 +01:00
|
|
|
if sigchld_read {
|
|
|
|
|
sigchld_fd.flush();
|
|
|
|
|
}
|
2024-01-06 21:54:57 +01:00
|
|
|
unsafe { libc::kill(child.as_raw(), SIGTERM) };
|
|
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
|
}
|
2023-10-21 14:40:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-10 15:31:45 +01:00
|
|
|
fn handle_child<S: AsRef<str>>(command: &[S], extra_env: &ExtraEnv) -> Result<()> {
|
2023-10-22 11:52:30 +02:00
|
|
|
use signal::{SigHandler, Signal};
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-10 15:31:45 +01:00
|
|
|
let command = command
|
2023-10-21 14:40:39 +02:00
|
|
|
.iter()
|
|
|
|
|
.map(|s| CString::new(s.as_ref()))
|
|
|
|
|
.collect::<Result<Vec<CString>, NulError>>()?;
|
|
|
|
|
|
2023-12-23 16:42:40 +01:00
|
|
|
for (k, v) in extra_env {
|
|
|
|
|
env::set_var(k, v);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-22 11:52:30 +02:00
|
|
|
unsafe { signal::signal(Signal::SIGPIPE, SigHandler::SigDfl) }?;
|
2024-01-10 15:31:45 +01:00
|
|
|
unistd::execvp(&command[0], &command)?;
|
2023-10-21 14:40:39 +02:00
|
|
|
unsafe { libc::_exit(1) }
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 11:43:01 +01:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
pub enum WinsizeOverride {
|
|
|
|
|
Full(u16, u16),
|
|
|
|
|
Cols(u16),
|
|
|
|
|
Rows(u16),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for WinsizeOverride {
|
|
|
|
|
type Err = anyhow::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s.split_once('x') {
|
|
|
|
|
Some((cols, "")) => {
|
|
|
|
|
let cols: u16 = cols.parse()?;
|
|
|
|
|
|
|
|
|
|
Ok(WinsizeOverride::Cols(cols))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(("", rows)) => {
|
|
|
|
|
let rows: u16 = rows.parse()?;
|
|
|
|
|
|
|
|
|
|
Ok(WinsizeOverride::Rows(rows))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some((cols, rows)) => {
|
|
|
|
|
let cols: u16 = cols.parse()?;
|
|
|
|
|
let rows: u16 = rows.parse()?;
|
|
|
|
|
|
|
|
|
|
Ok(WinsizeOverride::Full(cols, rows))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None => {
|
|
|
|
|
bail!("{s}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_winsize<T: Tty + ?Sized>(
|
2024-01-05 14:53:01 +01:00
|
|
|
tty: &T,
|
2024-02-01 11:43:01 +01:00
|
|
|
winsize_override: Option<&WinsizeOverride>,
|
2024-01-05 14:53:01 +01:00
|
|
|
) -> pty::Winsize {
|
|
|
|
|
let mut winsize = tty.get_size();
|
2023-10-30 13:32:26 +01:00
|
|
|
|
2024-02-01 11:43:01 +01:00
|
|
|
match winsize_override {
|
|
|
|
|
Some(WinsizeOverride::Full(cols, rows)) => {
|
|
|
|
|
winsize.ws_col = *cols;
|
|
|
|
|
winsize.ws_row = *rows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(WinsizeOverride::Cols(cols)) => {
|
|
|
|
|
winsize.ws_col = *cols;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(WinsizeOverride::Rows(rows)) => {
|
|
|
|
|
winsize.ws_row = *rows;
|
|
|
|
|
}
|
2023-10-30 13:32:26 +01:00
|
|
|
|
2024-02-01 11:43:01 +01:00
|
|
|
None => (),
|
2023-10-30 13:32:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
winsize
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_pty_size(pty_fd: i32, winsize: &pty::Winsize) {
|
|
|
|
|
unsafe { libc::ioctl(pty_fd, libc::TIOCSWINSZ, winsize) };
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
fn read_non_blocking<R: Read + ?Sized>(
|
|
|
|
|
source: &mut R,
|
|
|
|
|
buf: &mut [u8],
|
|
|
|
|
) -> io::Result<Option<usize>> {
|
2024-01-09 11:18:44 +01:00
|
|
|
match source.read(buf) {
|
|
|
|
|
Ok(n) => Ok(Some(n)),
|
2023-10-21 14:40:39 +02:00
|
|
|
|
2024-01-09 11:18:44 +01:00
|
|
|
Err(e) => {
|
|
|
|
|
if e.kind() == ErrorKind::WouldBlock {
|
|
|
|
|
Ok(None)
|
|
|
|
|
} else if e.raw_os_error().is_some_and(|code| code == 5) {
|
|
|
|
|
Ok(Some(0))
|
|
|
|
|
} else {
|
|
|
|
|
return Err(e);
|
2023-10-21 14:40:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-21 22:01:37 +02:00
|
|
|
|
2024-01-10 10:42:57 +01:00
|
|
|
fn write_non_blocking<W: Write + ?Sized>(sink: &mut W, buf: &[u8]) -> io::Result<Option<usize>> {
|
2024-01-09 11:18:44 +01:00
|
|
|
match sink.write(buf) {
|
|
|
|
|
Ok(n) => Ok(Some(n)),
|
2023-10-21 22:01:37 +02:00
|
|
|
|
2024-01-09 11:18:44 +01:00
|
|
|
Err(e) => {
|
|
|
|
|
if e.kind() == ErrorKind::WouldBlock {
|
|
|
|
|
Ok(None)
|
|
|
|
|
} else if e.raw_os_error().is_some_and(|code| code == 5) {
|
|
|
|
|
Ok(Some(0))
|
|
|
|
|
} else {
|
|
|
|
|
return Err(e);
|
2023-10-21 22:01:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-24 11:28:41 +02:00
|
|
|
|
2024-01-06 21:54:57 +01:00
|
|
|
struct SignalFd {
|
|
|
|
|
sigid: SigId,
|
2024-01-09 11:56:12 +01:00
|
|
|
rx: OwnedFd,
|
2024-01-06 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SignalFd {
|
|
|
|
|
fn open(signal: libc::c_int) -> Result<Self> {
|
2024-01-09 16:47:02 +01:00
|
|
|
let (rx, tx) = unistd::pipe()?;
|
2024-01-06 21:54:57 +01:00
|
|
|
set_non_blocking(&rx)?;
|
|
|
|
|
set_non_blocking(&tx)?;
|
2024-01-09 11:56:12 +01:00
|
|
|
let rx = unsafe { OwnedFd::from_raw_fd(rx) };
|
|
|
|
|
let tx = unsafe { OwnedFd::from_raw_fd(tx) };
|
2024-01-06 21:54:57 +01:00
|
|
|
|
|
|
|
|
let sigid = unsafe {
|
|
|
|
|
signal_hook::low_level::register(signal, move || {
|
2024-01-09 11:56:12 +01:00
|
|
|
let _ = unistd::write(tx.as_raw_fd(), &[0]);
|
2024-01-06 21:54:57 +01:00
|
|
|
})
|
|
|
|
|
}?;
|
|
|
|
|
|
|
|
|
|
Ok(Self { sigid, rx })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn flush(&self) {
|
|
|
|
|
let mut buf = [0; 256];
|
|
|
|
|
|
2024-01-09 11:56:12 +01:00
|
|
|
while let Ok(n) = unistd::read(self.rx.as_raw_fd(), &mut buf) {
|
2024-01-06 21:54:57 +01:00
|
|
|
if n == 0 {
|
|
|
|
|
break;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AsFd for SignalFd {
|
|
|
|
|
fn as_fd(&self) -> BorrowedFd<'_> {
|
2024-01-09 11:56:12 +01:00
|
|
|
self.rx.as_fd()
|
2024-01-06 21:54:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Drop for SignalFd {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
signal_hook::low_level::unregister(self.sigid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-24 11:28:41 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2024-01-18 12:02:08 +01:00
|
|
|
use super::Recorder;
|
2024-02-01 11:43:01 +01:00
|
|
|
use crate::pty::{ExtraEnv, WinsizeOverride};
|
2024-01-18 12:02:08 +01:00
|
|
|
use crate::tty::{NullTty, TtySize};
|
2023-12-23 16:42:40 +01:00
|
|
|
|
2023-10-24 11:28:41 +02:00
|
|
|
#[derive(Default)]
|
|
|
|
|
struct TestRecorder {
|
2024-01-18 12:02:08 +01:00
|
|
|
tty_size: Option<TtySize>,
|
2023-10-24 11:28:41 +02:00
|
|
|
output: Vec<Vec<u8>>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 12:02:08 +01:00
|
|
|
impl Recorder for TestRecorder {
|
|
|
|
|
fn start(&mut self, tty_size: TtySize) -> std::io::Result<()> {
|
|
|
|
|
self.tty_size = Some(tty_size);
|
2023-10-24 11:28:41 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn output(&mut self, data: &[u8]) {
|
|
|
|
|
self.output.push(data.into());
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-12 12:18:04 +01:00
|
|
|
fn input(&mut self, _data: &[u8]) -> bool {
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 12:02:08 +01:00
|
|
|
fn resize(&mut self, _size: TtySize) {}
|
2023-10-24 11:28:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TestRecorder {
|
|
|
|
|
fn output(&self) -> Vec<String> {
|
|
|
|
|
self.output
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|x| String::from_utf8_lossy(x).to_string())
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
2024-01-09 11:18:44 +01:00
|
|
|
fn exec_basic() {
|
2023-10-24 11:28:41 +02:00
|
|
|
let mut recorder = TestRecorder::default();
|
|
|
|
|
|
|
|
|
|
let code = r#"
|
|
|
|
|
import sys;
|
|
|
|
|
import time;
|
|
|
|
|
sys.stdout.write('foo');
|
|
|
|
|
sys.stdout.flush();
|
2024-01-09 11:18:44 +01:00
|
|
|
time.sleep(0.1);
|
2023-10-24 11:28:41 +02:00
|
|
|
sys.stdout.write('bar');
|
|
|
|
|
"#;
|
|
|
|
|
|
2024-01-09 11:18:44 +01:00
|
|
|
super::exec(
|
2023-12-23 16:42:40 +01:00
|
|
|
&["python3", "-c", code],
|
|
|
|
|
&ExtraEnv::new(),
|
2024-01-10 10:42:57 +01:00
|
|
|
&mut NullTty::open().unwrap(),
|
2024-02-01 11:43:01 +01:00
|
|
|
None,
|
2023-12-23 16:42:40 +01:00
|
|
|
&mut recorder,
|
2024-01-09 11:18:44 +01:00
|
|
|
)
|
|
|
|
|
.unwrap();
|
2023-10-24 11:28:41 +02:00
|
|
|
|
2024-01-05 14:53:01 +01:00
|
|
|
assert_eq!(recorder.output(), vec!["foo", "bar"]);
|
2024-01-18 12:02:08 +01:00
|
|
|
assert_eq!(recorder.tty_size, Some(TtySize(80, 24)));
|
2024-01-09 11:18:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exec_no_output() {
|
|
|
|
|
let mut recorder = TestRecorder::default();
|
|
|
|
|
|
|
|
|
|
super::exec(
|
|
|
|
|
&["true"],
|
|
|
|
|
&ExtraEnv::new(),
|
2024-01-10 10:42:57 +01:00
|
|
|
&mut NullTty::open().unwrap(),
|
2024-02-01 11:43:01 +01:00
|
|
|
None,
|
2024-01-09 11:18:44 +01:00
|
|
|
&mut recorder,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(recorder.output().is_empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exec_quick() {
|
|
|
|
|
let mut recorder = TestRecorder::default();
|
|
|
|
|
|
|
|
|
|
super::exec(
|
|
|
|
|
&["w"],
|
|
|
|
|
&ExtraEnv::new(),
|
2024-01-10 10:42:57 +01:00
|
|
|
&mut NullTty::open().unwrap(),
|
2024-02-01 11:43:01 +01:00
|
|
|
None,
|
2024-01-09 11:18:44 +01:00
|
|
|
&mut recorder,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(!recorder.output().is_empty());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exec_extra_env() {
|
|
|
|
|
let mut recorder = TestRecorder::default();
|
|
|
|
|
|
|
|
|
|
let mut env = ExtraEnv::new();
|
|
|
|
|
env.insert("ASCIINEMA_TEST_FOO".to_owned(), "bar".to_owned());
|
|
|
|
|
|
|
|
|
|
super::exec(
|
|
|
|
|
&["sh", "-c", "echo -n $ASCIINEMA_TEST_FOO"],
|
|
|
|
|
&env,
|
2024-01-10 10:42:57 +01:00
|
|
|
&mut NullTty::open().unwrap(),
|
2024-02-01 11:43:01 +01:00
|
|
|
None,
|
2024-01-09 11:18:44 +01:00
|
|
|
&mut recorder,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert_eq!(recorder.output(), vec!["bar"]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn exec_winsize_override() {
|
|
|
|
|
let mut recorder = TestRecorder::default();
|
|
|
|
|
|
|
|
|
|
super::exec(
|
|
|
|
|
&["true"],
|
|
|
|
|
&ExtraEnv::new(),
|
2024-01-10 10:42:57 +01:00
|
|
|
&mut NullTty::open().unwrap(),
|
2024-02-01 11:43:01 +01:00
|
|
|
Some(WinsizeOverride::Full(100, 50)),
|
2024-01-09 11:18:44 +01:00
|
|
|
&mut recorder,
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
2024-01-18 12:02:08 +01:00
|
|
|
assert_eq!(recorder.tty_size, Some(TtySize(100, 50)));
|
2023-10-24 11:28:41 +02:00
|
|
|
}
|
|
|
|
|
}
|