mirror of
https://github.com/asciinema/asciinema.git
synced 2025-12-16 03:38:03 +01:00
Handle SIGWINCH
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -66,6 +66,8 @@ dependencies = [
|
|||||||
"nix",
|
"nix",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
"termion",
|
"termion",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -259,6 +261,36 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|||||||
@@ -18,3 +18,5 @@ termion = "2.0.1"
|
|||||||
serde = { version = "1.0.189", features = ["derive"] }
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
serde_json = "1.0.107"
|
serde_json = "1.0.107"
|
||||||
clap = { version = "4.4.7", features = ["derive"] }
|
clap = { version = "4.4.7", features = ["derive"] }
|
||||||
|
signal-hook-mio = { version = "0.2.3", features = ["support-v0_8"] }
|
||||||
|
signal-hook = "0.3.17"
|
||||||
|
|||||||
106
src/pty.rs
106
src/pty.rs
@@ -1,5 +1,8 @@
|
|||||||
|
use anyhow::bail;
|
||||||
use mio::unix::SourceFd;
|
use mio::unix::SourceFd;
|
||||||
use nix::{fcntl, libc, pty, sys::signal, sys::wait, unistd, unistd::ForkResult};
|
use nix::{fcntl, libc, pty, sys::signal, sys::wait, unistd, unistd::ForkResult};
|
||||||
|
use signal_hook::consts::signal::*;
|
||||||
|
use signal_hook_mio::v0_8::Signals;
|
||||||
use std::ffi::{CString, NulError};
|
use std::ffi::{CString, NulError};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
@@ -26,9 +29,13 @@ pub fn exec<S: AsRef<str>, R: Recorder>(
|
|||||||
let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
|
let result = unsafe { pty::forkpty(Some(&winsize), None) }?;
|
||||||
|
|
||||||
match result.fork_result {
|
match result.fork_result {
|
||||||
ForkResult::Parent { child } => {
|
ForkResult::Parent { child } => handle_parent(
|
||||||
handle_parent(result.master.as_raw_fd(), tty, child, recorder)
|
result.master.as_raw_fd(),
|
||||||
}
|
tty,
|
||||||
|
child,
|
||||||
|
winsize_override,
|
||||||
|
recorder,
|
||||||
|
),
|
||||||
|
|
||||||
ForkResult::Child => {
|
ForkResult::Child => {
|
||||||
handle_child(args, env)?;
|
handle_child(args, env)?;
|
||||||
@@ -37,41 +44,14 @@ pub fn exec<S: AsRef<str>, R: Recorder>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_tty() -> io::Result<fs::File> {
|
|
||||||
fs::OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.open("/dev/tty")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tty_size(tty_fd: i32, winsize_override: (Option<u16>, Option<u16>)) -> pty::Winsize {
|
|
||||||
let mut winsize = pty::Winsize {
|
|
||||||
ws_row: 24,
|
|
||||||
ws_col: 80,
|
|
||||||
ws_xpixel: 0,
|
|
||||||
ws_ypixel: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { libc::ioctl(tty_fd, libc::TIOCGWINSZ, &mut winsize) };
|
|
||||||
|
|
||||||
if let Some(cols) = winsize_override.0 {
|
|
||||||
winsize.ws_col = cols;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(rows) = winsize_override.1 {
|
|
||||||
winsize.ws_row = rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
winsize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_parent<R: Recorder>(
|
fn handle_parent<R: Recorder>(
|
||||||
master_fd: RawFd,
|
master_fd: RawFd,
|
||||||
tty: fs::File,
|
tty: fs::File,
|
||||||
child: unistd::Pid,
|
child: unistd::Pid,
|
||||||
|
winsize_override: (Option<u16>, Option<u16>),
|
||||||
recorder: &mut R,
|
recorder: &mut R,
|
||||||
) -> anyhow::Result<i32> {
|
) -> anyhow::Result<i32> {
|
||||||
let copy_result = copy(master_fd, tty, recorder);
|
let copy_result = copy(master_fd, tty, winsize_override, recorder);
|
||||||
let wait_result = wait::waitpid(child, None);
|
let wait_result = wait::waitpid(child, None);
|
||||||
copy_result?;
|
copy_result?;
|
||||||
|
|
||||||
@@ -85,9 +65,15 @@ fn handle_parent<R: Recorder>(
|
|||||||
|
|
||||||
const MASTER: mio::Token = mio::Token(0);
|
const MASTER: mio::Token = mio::Token(0);
|
||||||
const TTY: mio::Token = mio::Token(1);
|
const TTY: mio::Token = mio::Token(1);
|
||||||
|
const SIGNAL: mio::Token = mio::Token(2);
|
||||||
const BUF_SIZE: usize = 128 * 1024;
|
const BUF_SIZE: usize = 128 * 1024;
|
||||||
|
|
||||||
fn copy<R: Recorder>(master_fd: RawFd, tty: fs::File, recorder: &mut R) -> anyhow::Result<()> {
|
fn copy<R: Recorder>(
|
||||||
|
master_fd: RawFd,
|
||||||
|
tty: fs::File,
|
||||||
|
winsize_override: (Option<u16>, Option<u16>),
|
||||||
|
recorder: &mut R,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let mut master = unsafe { fs::File::from_raw_fd(master_fd) };
|
let mut master = unsafe { fs::File::from_raw_fd(master_fd) };
|
||||||
let mut poll = mio::Poll::new()?;
|
let mut poll = mio::Poll::new()?;
|
||||||
let mut events = mio::Events::with_capacity(128);
|
let mut events = mio::Events::with_capacity(128);
|
||||||
@@ -95,6 +81,7 @@ fn copy<R: Recorder>(master_fd: RawFd, tty: fs::File, recorder: &mut R) -> anyho
|
|||||||
let mut tty = tty.into_raw_mode()?;
|
let mut tty = tty.into_raw_mode()?;
|
||||||
let tty_fd = tty.as_raw_fd();
|
let tty_fd = tty.as_raw_fd();
|
||||||
let mut tty_source = SourceFd(&tty_fd);
|
let mut tty_source = SourceFd(&tty_fd);
|
||||||
|
let mut signals = Signals::new([SIGWINCH])?;
|
||||||
let mut buf = [0u8; BUF_SIZE];
|
let mut buf = [0u8; BUF_SIZE];
|
||||||
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
let mut input: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
||||||
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
let mut output: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
||||||
@@ -103,14 +90,24 @@ fn copy<R: Recorder>(master_fd: RawFd, tty: fs::File, recorder: &mut R) -> anyho
|
|||||||
set_non_blocking(&master_fd)?;
|
set_non_blocking(&master_fd)?;
|
||||||
set_non_blocking(&tty_fd)?;
|
set_non_blocking(&tty_fd)?;
|
||||||
|
|
||||||
|
|
||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut master_source, MASTER, mio::Interest::READABLE)?;
|
.register(&mut master_source, MASTER, mio::Interest::READABLE)?;
|
||||||
|
|
||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut tty_source, TTY, mio::Interest::READABLE)?;
|
.register(&mut tty_source, TTY, mio::Interest::READABLE)?;
|
||||||
|
|
||||||
|
poll.registry()
|
||||||
|
.register(&mut signals, SIGNAL, mio::Interest::READABLE)?;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
poll.poll(&mut events, None).unwrap();
|
if let Err(e) = poll.poll(&mut events, None) {
|
||||||
|
if e.kind() == io::ErrorKind::Interrupted {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
bail!(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
match event.token() {
|
match event.token() {
|
||||||
@@ -191,6 +188,15 @@ fn copy<R: Recorder>(master_fd: RawFd, tty: fs::File, recorder: &mut R) -> anyho
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SIGNAL => {
|
||||||
|
for signal in signals.pending() {
|
||||||
|
if signal == SIGWINCH {
|
||||||
|
let winsize = get_tty_size(tty_fd, winsize_override);
|
||||||
|
set_pty_size(master_fd, &winsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,6 +216,38 @@ fn handle_child<S: AsRef<str>>(args: &[S], env: &[CString]) -> anyhow::Result<()
|
|||||||
unsafe { libc::_exit(1) }
|
unsafe { libc::_exit(1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_tty() -> io::Result<fs::File> {
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open("/dev/tty")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tty_size(tty_fd: i32, winsize_override: (Option<u16>, Option<u16>)) -> pty::Winsize {
|
||||||
|
let mut winsize = pty::Winsize {
|
||||||
|
ws_row: 24,
|
||||||
|
ws_col: 80,
|
||||||
|
ws_xpixel: 0,
|
||||||
|
ws_ypixel: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { libc::ioctl(tty_fd, libc::TIOCGWINSZ, &mut winsize) };
|
||||||
|
|
||||||
|
if let Some(cols) = winsize_override.0 {
|
||||||
|
winsize.ws_col = cols;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(rows) = winsize_override.1 {
|
||||||
|
winsize.ws_row = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
winsize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pty_size(pty_fd: i32, winsize: &pty::Winsize) {
|
||||||
|
unsafe { libc::ioctl(pty_fd, libc::TIOCSWINSZ, winsize) };
|
||||||
|
}
|
||||||
|
|
||||||
fn set_non_blocking(fd: &RawFd) -> Result<(), io::Error> {
|
fn set_non_blocking(fd: &RawFd) -> Result<(), io::Error> {
|
||||||
use fcntl::{fcntl, FcntlArg::*, OFlag};
|
use fcntl::{fcntl, FcntlArg::*, OFlag};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user