Isolate interactive terminal session ownership
This commit is contained in:
parent
b240bc1d12
commit
690738d846
3 changed files with 280 additions and 24 deletions
|
|
@ -147,7 +147,6 @@ impl SessionTerminalEngine {
|
|||
session_id: SessionId,
|
||||
generation_id: GenerationId,
|
||||
) -> Result<Vec<SessionTerminalMessage>, TerminalError> {
|
||||
let authoritative = self.refresh_route(session_id, generation_id)?;
|
||||
let terminal = self.active_mut(session_id, generation_id)?;
|
||||
if terminal.process.exec_exited()? {
|
||||
return self.close(
|
||||
|
|
@ -158,6 +157,8 @@ impl SessionTerminalEngine {
|
|||
GuestReleaseAuthority::Validate,
|
||||
);
|
||||
}
|
||||
let authoritative = self.refresh_route(session_id, generation_id)?;
|
||||
let terminal = self.active_mut(session_id, generation_id)?;
|
||||
if authoritative != TerminalRoute::Sandbox || terminal.releasing {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
|
@ -446,6 +447,13 @@ pub(super) fn release_physical(
|
|||
} else {
|
||||
guest_authorized = false;
|
||||
}
|
||||
if matches!(
|
||||
failure.as_ref(),
|
||||
Some(TerminalError::GuestIdentityMismatch | TerminalError::GuestGroupStillPresent)
|
||||
) {
|
||||
failure = None;
|
||||
guest_authorized = false;
|
||||
}
|
||||
if let Some(error) = failure {
|
||||
return Err(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ impl SessionTerminalFactory for RunscSessionTerminalFactory {
|
|||
target = match slave
|
||||
.try_clone()
|
||||
.and_then(|stdin| target.with_target_stdin(stdin))
|
||||
.map(lazaret_linux::process::TargetCommand::with_controlling_terminal)
|
||||
{
|
||||
Ok(target) => target,
|
||||
Err(error) => {
|
||||
|
|
|
|||
|
|
@ -321,15 +321,20 @@ fn changed_guest_identity_refuses_numeric_group_action() {
|
|||
Err(TerminalError::GuestIdentityMismatch)
|
||||
));
|
||||
assert!(factory.log.lock().unwrap().is_empty());
|
||||
assert!(matches!(
|
||||
engine.handle(
|
||||
let messages = engine
|
||||
.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
session_id: session,
|
||||
}),
|
||||
),
|
||||
Err(TerminalError::GuestIdentityMismatch)
|
||||
));
|
||||
)
|
||||
.unwrap();
|
||||
assert!(
|
||||
messages
|
||||
.iter()
|
||||
.any(|message| matches!(message, SessionTerminalMessage::Exit(_)))
|
||||
);
|
||||
assert!(!engine.contains(session));
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
|
|
@ -337,7 +342,7 @@ fn changed_guest_identity_refuses_numeric_group_action() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn absent_leader_with_live_group_retains_release_until_group_is_empty() {
|
||||
fn absent_leader_with_live_group_closes_with_release_intent() {
|
||||
let session = id(6);
|
||||
let (fixture, factory, mut engine) = setup(&[session]);
|
||||
engine.acquire(&acquisition(session)).unwrap();
|
||||
|
|
@ -347,25 +352,7 @@ fn absent_leader_with_live_group_retains_release_until_group_is_empty() {
|
|||
state.guest.leader_present = false;
|
||||
state.guest.group_present = true;
|
||||
}
|
||||
assert!(matches!(
|
||||
engine.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
session_id: session,
|
||||
}),
|
||||
),
|
||||
Err(TerminalError::GuestIdentityMismatch)
|
||||
));
|
||||
let state = factory.state(0);
|
||||
assert!(state.lock().unwrap().resources.exec_reaped);
|
||||
assert!(!state.lock().unwrap().resources.pty_open);
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
|
||||
state.lock().unwrap().guest.group_present = false;
|
||||
engine
|
||||
let messages = engine
|
||||
.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
|
|
@ -373,9 +360,73 @@ fn absent_leader_with_live_group_retains_release_until_group_is_empty() {
|
|||
}),
|
||||
)
|
||||
.unwrap();
|
||||
let state = factory.state(0);
|
||||
assert!(state.lock().unwrap().resources.exec_reaped);
|
||||
assert!(!state.lock().unwrap().resources.pty_open);
|
||||
assert!(
|
||||
messages
|
||||
.iter()
|
||||
.any(|message| matches!(message, SessionTerminalMessage::Exit(_)))
|
||||
);
|
||||
assert!(!engine.contains(session));
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::Clean
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exited_exec_with_absent_leader_emits_exit_and_retains_group_release() {
|
||||
let session = id(6);
|
||||
let (fixture, factory, mut engine) = setup(&[session]);
|
||||
engine.acquire(&acquisition(session)).unwrap();
|
||||
{
|
||||
let state = factory.state(0);
|
||||
let mut state = state.lock().unwrap();
|
||||
state.guest.leader_present = false;
|
||||
state.guest.group_present = true;
|
||||
state.resources.exec_exited = true;
|
||||
}
|
||||
|
||||
let messages = engine.poll(session, GenerationId::new(7)).unwrap();
|
||||
|
||||
assert!(
|
||||
messages
|
||||
.iter()
|
||||
.any(|message| matches!(message, SessionTerminalMessage::Exit(_)))
|
||||
);
|
||||
assert!(!engine.contains(session));
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persistent_guest_group_closes_session_with_release_intent() {
|
||||
let session = id(6);
|
||||
let (fixture, factory, mut engine) = setup(&[session]);
|
||||
engine.acquire(&acquisition(session)).unwrap();
|
||||
factory.state(0).lock().unwrap().guest.clears_after_signal = "never";
|
||||
|
||||
let messages = engine
|
||||
.handle(
|
||||
GenerationId::new(7),
|
||||
SessionTerminalEvent::Close(lazaret_core::records::SessionClose {
|
||||
session_id: session,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
messages
|
||||
.iter()
|
||||
.any(|message| matches!(message, SessionTerminalMessage::Exit(_)))
|
||||
);
|
||||
assert!(!engine.contains(session));
|
||||
assert_eq!(
|
||||
fixture.store.snapshot().unwrap().session_terminals[0].state(),
|
||||
SessionTerminalState::ReleaseIntent
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue