Add coordinator-owned interactive session terminals
This commit is contained in:
parent
3b76b59bdf
commit
51ff63a3aa
1 changed files with 2078 additions and 15 deletions
|
|
@ -17,8 +17,11 @@ use lazaret_linux::process::{
|
|||
append_parent_identity, child_broker, current_process_identity, dispatch_private_process_role,
|
||||
verify_direct_child_pidfd,
|
||||
};
|
||||
use rustix::fs::{OFlags, fcntl_getfl, fcntl_setfl};
|
||||
use rustix::pipe::{PipeFlags, pipe_with};
|
||||
use rustix::process::{PidfdFlags, pidfd_open};
|
||||
use rustix::pty::{OpenptFlags, grantpt, ioctl_tiocgptpeer, openpt, unlockpt};
|
||||
use rustix::termios::{Winsize, tcgetwinsize, tcsetwinsize};
|
||||
|
||||
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ fn exit(result: std::io::Result<()>) -> ExitCode {
|
|||
fn run_all() -> std::io::Result<()> {
|
||||
successful_acquisition()?;
|
||||
transient_captured_child_preserves_streams_and_exit()?;
|
||||
terminal_child_restores_slave_stdin_and_merges_output()?;
|
||||
acknowledged_transition_precedes_exact_wrapper_release()?;
|
||||
failed_acknowledged_transition_refuses_exact_wrapper_release()?;
|
||||
lifetime_pipe_descriptor_contract()?;
|
||||
|
|
@ -57,6 +61,75 @@ fn run_all() -> std::io::Result<()> {
|
|||
concurrent_acquisitions_serialize()
|
||||
}
|
||||
|
||||
fn terminal_child_restores_slave_stdin_and_merges_output() -> std::io::Result<()> {
|
||||
let master = openpt(OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC)?;
|
||||
grantpt(&master)?;
|
||||
unlockpt(&master)?;
|
||||
let slave = ioctl_tiocgptpeer(
|
||||
&master,
|
||||
OpenptFlags::RDWR | OpenptFlags::NOCTTY | OpenptFlags::CLOEXEC,
|
||||
)?;
|
||||
tcsetwinsize(
|
||||
&master,
|
||||
Winsize {
|
||||
ws_row: 44,
|
||||
ws_col: 132,
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
},
|
||||
)?;
|
||||
if tcgetwinsize(&master)?.ws_row != 44 || tcgetwinsize(&master)?.ws_col != 132 {
|
||||
return Err(std::io::Error::other(
|
||||
"PTY resize did not update the master",
|
||||
));
|
||||
}
|
||||
let flags = fcntl_getfl(&master)?;
|
||||
fcntl_setfl(&master, flags | OFlags::NONBLOCK)?;
|
||||
let slave = File::from(slave);
|
||||
let target = TargetCommand::new("/bin/sh")
|
||||
.argument("-c")
|
||||
.argument("read value; printf 'out:%s\\n' \"$value\"; printf 'err\\n' >&2")
|
||||
.with_target_stdin(slave.try_clone()?)?;
|
||||
let expected_parent = current_process_identity()?;
|
||||
let ownership = ProcessOwnershipContext {
|
||||
domain_id: DomainId::new([41; 32]),
|
||||
generation_id: GenerationId::new(42),
|
||||
coordinator_id: CoordinatorId::new([43; 32]),
|
||||
slot: HostProcessOwnershipSlot::SessionTerminalExecClient(
|
||||
lazaret_core::identity::SessionId::new([44; 32]),
|
||||
),
|
||||
};
|
||||
let process = child_broker().acquire_with_stdio(
|
||||
expected_parent,
|
||||
ownership,
|
||||
&target,
|
||||
ChildStandardIo::terminal(slave)?,
|
||||
|_| Ok(()),
|
||||
|staged| Ok(ProcessOwnershipCommitAck::v1(staged)),
|
||||
)?;
|
||||
let mut master = File::from(master);
|
||||
master.write_all(b"hello\n")?;
|
||||
let mut merged = Vec::new();
|
||||
while !process.has_exited()? {
|
||||
let _ = read_available(&mut master, &mut merged);
|
||||
std::thread::yield_now();
|
||||
}
|
||||
let _ = read_available(&mut master, &mut merged);
|
||||
let exit = child_broker().reap_status(process)?;
|
||||
let text = String::from_utf8_lossy(&merged);
|
||||
let out = text.find("out:hello");
|
||||
let err = text.find("err");
|
||||
if !matches!((out, err), (Some(out), Some(err)) if out < err)
|
||||
|| exit.exit_code != Some(0)
|
||||
|| exit.terminating_signal.is_some()
|
||||
{
|
||||
return Err(std::io::Error::other(format!(
|
||||
"terminal stdio or merged ordering failed: {text:?}, {exit:?}"
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transient_captured_child_preserves_streams_and_exit() -> std::io::Result<()> {
|
||||
let target = TargetCommand::new("/bin/sh")
|
||||
.argument("-c")
|
||||
|
|
|
|||
Loading…
Reference in a new issue