Stop terminal grace polling after authority loss
This commit is contained in:
parent
c5ed009fb8
commit
0f4845d4f8
1 changed files with 189 additions and 63 deletions
|
|
@ -63,6 +63,12 @@ enum GuestReleaseAuthority {
|
|||
HostOnly,
|
||||
}
|
||||
|
||||
enum GuestWaitOutcome {
|
||||
Absent,
|
||||
TimedOut,
|
||||
Revoked,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TerminalError {
|
||||
Io(io::Error),
|
||||
|
|
@ -788,9 +794,18 @@ fn release_physical(
|
|||
failure.get_or_insert(error);
|
||||
break;
|
||||
}
|
||||
if let Err(error) = wait_until(SIGNAL_GRACE, || process.group_absent()) {
|
||||
failure.get_or_insert(error);
|
||||
break;
|
||||
match wait_for_group_absence(process, authorize_guest) {
|
||||
Ok(GuestWaitOutcome::Absent) => break,
|
||||
Ok(GuestWaitOutcome::TimedOut) => {}
|
||||
Ok(GuestWaitOutcome::Revoked) => {
|
||||
guest_authorized = false;
|
||||
break;
|
||||
}
|
||||
Err(error) => {
|
||||
failure.get_or_insert(error);
|
||||
guest_authorized = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -812,29 +827,7 @@ fn release_physical(
|
|||
None
|
||||
}
|
||||
};
|
||||
let mut output = Vec::new();
|
||||
if connected {
|
||||
let deadline = Instant::now() + DRAIN_DEADLINE;
|
||||
let mut bytes = [0_u8; MAX_OUTPUT_BYTES];
|
||||
let mut drained = 0;
|
||||
while Instant::now() < deadline && drained < MAX_DRAIN_BYTES {
|
||||
match process.read_output(&mut bytes) {
|
||||
Ok(0) => break,
|
||||
Ok(length) => {
|
||||
let length = length.min(MAX_DRAIN_BYTES - drained);
|
||||
drained += length;
|
||||
output.push(SessionTerminalMessage::Output(TerminalOutput {
|
||||
session_id,
|
||||
bytes: bytes[..length].to_vec(),
|
||||
}));
|
||||
}
|
||||
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
|
||||
std::thread::sleep(POLL_INTERVAL);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
let output = drain_output(process, session_id, connected);
|
||||
process.close_pty();
|
||||
if guest_authorized && authorize_guest() {
|
||||
match process.group_absent() {
|
||||
|
|
@ -860,6 +853,38 @@ fn release_physical(
|
|||
))
|
||||
}
|
||||
|
||||
fn drain_output(
|
||||
process: &mut dyn SessionTerminalProcess,
|
||||
session_id: SessionId,
|
||||
connected: bool,
|
||||
) -> Vec<SessionTerminalMessage> {
|
||||
let mut output = Vec::new();
|
||||
if !connected {
|
||||
return output;
|
||||
}
|
||||
let deadline = Instant::now() + DRAIN_DEADLINE;
|
||||
let mut bytes = [0_u8; MAX_OUTPUT_BYTES];
|
||||
let mut drained = 0;
|
||||
while Instant::now() < deadline && drained < MAX_DRAIN_BYTES {
|
||||
match process.read_output(&mut bytes) {
|
||||
Ok(0) => break,
|
||||
Ok(length) => {
|
||||
let length = length.min(MAX_DRAIN_BYTES - drained);
|
||||
drained += length;
|
||||
output.push(SessionTerminalMessage::Output(TerminalOutput {
|
||||
session_id,
|
||||
bytes: bytes[..length].to_vec(),
|
||||
}));
|
||||
}
|
||||
Err(error) if error.kind() == io::ErrorKind::WouldBlock => {
|
||||
std::thread::sleep(POLL_INTERVAL);
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn with_cleanup<T>(original: TerminalError, cleanup: Result<T, TerminalError>) -> TerminalError {
|
||||
match cleanup {
|
||||
Ok(_) => original,
|
||||
|
|
@ -870,6 +895,23 @@ fn with_cleanup<T>(original: TerminalError, cleanup: Result<T, TerminalError>) -
|
|||
}
|
||||
}
|
||||
|
||||
fn wait_for_group_absence(
|
||||
process: &dyn SessionTerminalProcess,
|
||||
authorize_guest: &mut dyn FnMut() -> bool,
|
||||
) -> Result<GuestWaitOutcome, TerminalError> {
|
||||
let deadline = Instant::now() + SIGNAL_GRACE;
|
||||
while Instant::now() < deadline {
|
||||
if !authorize_guest() {
|
||||
return Ok(GuestWaitOutcome::Revoked);
|
||||
}
|
||||
if process.group_absent()? {
|
||||
return Ok(GuestWaitOutcome::Absent);
|
||||
}
|
||||
std::thread::sleep(POLL_INTERVAL);
|
||||
}
|
||||
Ok(GuestWaitOutcome::TimedOut)
|
||||
}
|
||||
|
||||
fn flush_input(terminal: &mut ActiveTerminal) -> Result<(), TerminalError> {
|
||||
while !terminal.pending_input.is_empty() {
|
||||
let (first, second) = terminal.pending_input.as_slices();
|
||||
|
|
@ -888,20 +930,6 @@ fn flush_input(terminal: &mut ActiveTerminal) -> Result<(), TerminalError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn wait_until(
|
||||
duration: Duration,
|
||||
mut predicate: impl FnMut() -> Result<bool, TerminalError>,
|
||||
) -> Result<(), TerminalError> {
|
||||
let deadline = Instant::now() + duration;
|
||||
while Instant::now() < deadline {
|
||||
if predicate()? {
|
||||
return Ok(());
|
||||
}
|
||||
std::thread::sleep(POLL_INTERVAL);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exit_message(
|
||||
session_id: SessionId,
|
||||
loss: Option<TerminalLoss>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue