T04: close the pty master before reaping, so a program ignoring SIGHUP ends
This commit is contained in:
2 files changed
+22
-13
No files matched your search
+14
-7
@@ -269,14 +269,11 @@ mod tests {
|
|||||||
assert_eq!(ran.text, format!("8 {}", u16::MAX / 8));
|
assert_eq!(ran.text, format!("8 {}", u16::MAX / 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
/// Starts `script`, waits for it to print READY, closes it, and waits for the reap's log.
|
||||||
fn closing_a_session_hangs_up_its_program() {
|
fn close_after_ready(script: &str, reaped: &str) {
|
||||||
crate::testlog::init();
|
crate::testlog::init();
|
||||||
let launch = Launch {
|
let launch = Launch {
|
||||||
program: Some((
|
program: Some(("/bin/sh".into(), vec!["-c".into(), script.into()])),
|
||||||
"/bin/sh".into(),
|
|
||||||
vec!["-c".into(), "printf READY; exec cat".into()],
|
|
||||||
)),
|
|
||||||
cwd: std::env::temp_dir(),
|
cwd: std::env::temp_dir(),
|
||||||
env: Vec::new(),
|
env: Vec::new(),
|
||||||
};
|
};
|
||||||
@@ -295,7 +292,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
let pid = pty.pid;
|
let pid = pty.pid;
|
||||||
drop(pty);
|
drop(pty);
|
||||||
let hung_up = format!("pty {pid}: hung up, signal: 1 (SIGHUP)");
|
let hung_up = format!("pty {pid}: hung up, {reaped}");
|
||||||
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
|
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
|
||||||
while !crate::testlog::logged(&hung_up) {
|
while !crate::testlog::logged(&hung_up) {
|
||||||
assert!(std::time::Instant::now() < deadline, "no `{hung_up}`");
|
assert!(std::time::Instant::now() < deadline, "no `{hung_up}`");
|
||||||
@@ -306,6 +303,16 @@ mod tests {
|
|||||||
assert!(!alive, "the program was reaped");
|
assert!(!alive, "the program was reaped");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn closing_a_session_hangs_up_its_program() {
|
||||||
|
close_after_ready("printf READY; exec cat", "signal: 1 (SIGHUP)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_program_that_ignores_sighup_ends_when_the_pty_closes() {
|
||||||
|
close_after_ready("trap '' HUP; printf READY; exec cat", "exit status: 0");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_missing_folder_stops_the_start() {
|
fn a_missing_folder_stops_the_start() {
|
||||||
let launch = Launch {
|
let launch = Launch {
|
||||||
|
|||||||
+8
-6
@@ -3,7 +3,7 @@ use std::{
|
|||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, ErrorKind, Read},
|
io::{self, ErrorKind, Read},
|
||||||
mem::MaybeUninit,
|
mem::{ManuallyDrop, MaybeUninit},
|
||||||
os::{
|
os::{
|
||||||
fd::AsRawFd,
|
fd::AsRawFd,
|
||||||
unix::{ffi::OsStrExt, net::UnixStream, process::CommandExt},
|
unix::{ffi::OsStrExt, net::UnixStream, process::CommandExt},
|
||||||
@@ -35,7 +35,7 @@ const CHILD: usize = 1;
|
|||||||
/// A program on a pseudo-terminal, started here so a failed `chdir` fails the start.
|
/// A program on a pseudo-terminal, started here so a failed `chdir` fails the start.
|
||||||
pub(crate) struct Pty {
|
pub(crate) struct Pty {
|
||||||
child: Child,
|
child: Child,
|
||||||
file: File,
|
file: ManuallyDrop<File>,
|
||||||
signals: UnixStream,
|
signals: UnixStream,
|
||||||
sig: SigId,
|
sig: SigId,
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ pub(crate) fn spawn(
|
|||||||
}
|
}
|
||||||
Ok(Pty {
|
Ok(Pty {
|
||||||
child,
|
child,
|
||||||
file: File::from(master),
|
file: ManuallyDrop::new(File::from(master)),
|
||||||
signals,
|
signals,
|
||||||
sig,
|
sig,
|
||||||
})
|
})
|
||||||
@@ -214,7 +214,7 @@ impl EventedReadWrite for Pty {
|
|||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
interest.key = READ_WRITE;
|
interest.key = READ_WRITE;
|
||||||
unsafe {
|
unsafe {
|
||||||
poll.add_with_mode(&self.file, interest, mode)?;
|
poll.add_with_mode(&*self.file, interest, mode)?;
|
||||||
poll.add_with_mode(&self.signals, Event::readable(CHILD), PollMode::Level)
|
poll.add_with_mode(&self.signals, Event::readable(CHILD), PollMode::Level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,12 +226,12 @@ impl EventedReadWrite for Pty {
|
|||||||
mode: PollMode,
|
mode: PollMode,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
interest.key = READ_WRITE;
|
interest.key = READ_WRITE;
|
||||||
poll.modify_with_mode(&self.file, interest, mode)?;
|
poll.modify_with_mode(&*self.file, interest, mode)?;
|
||||||
poll.modify_with_mode(&self.signals, Event::readable(CHILD), PollMode::Level)
|
poll.modify_with_mode(&self.signals, Event::readable(CHILD), PollMode::Level)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deregister(&mut self, poll: &Arc<Poller>) -> io::Result<()> {
|
fn deregister(&mut self, poll: &Arc<Poller>) -> io::Result<()> {
|
||||||
poll.delete(&self.file)?;
|
poll.delete(&*self.file)?;
|
||||||
poll.delete(&self.signals)
|
poll.delete(&self.signals)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,6 +284,8 @@ impl Drop for Pty {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe { libc::kill(self.child.id() as libc::pid_t, libc::SIGHUP) };
|
unsafe { libc::kill(self.child.id() as libc::pid_t, libc::SIGHUP) };
|
||||||
unregister(self.sig);
|
unregister(self.sig);
|
||||||
|
// Closed before the wait, so a program that ignores SIGHUP reads EOF.
|
||||||
|
unsafe { ManuallyDrop::drop(&mut self.file) };
|
||||||
match self.child.wait() {
|
match self.child.wait() {
|
||||||
Ok(status) => log::info!("pty {}: hung up, {status}", self.id()),
|
Ok(status) => log::info!("pty {}: hung up, {status}", self.id()),
|
||||||
Err(error) => log::error!("pty {}: cannot reap the program: {error}", self.id()),
|
Err(error) => log::error!("pty {}: cannot reap the program: {error}", self.id()),
|
||||||
|
|||||||
Reference in new issue
Block a user