Implement PTY-native session terminal control
This commit is contained in:
parent
058e58e816
commit
8cef037fa3
6 changed files with 1488 additions and 382 deletions
|
|
@ -230,6 +230,7 @@ pub enum ControlRequest {
|
|||
Register(SessionRegistrationRequest),
|
||||
Execute(NonInteractiveSessionRequest),
|
||||
ExecuteInteractive(super::InteractiveSessionRequest),
|
||||
ExecuteInteractiveV2(super::InteractiveSessionRequestV2),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
|
|
|
|||
|
|
@ -4,25 +4,28 @@ use super::{
|
|||
DefaultAction, DerivedNetworkPolicy, DeveloperDecision, DeveloperDecisionKind, Domain,
|
||||
DomainStore, DomainStoreMetadata, EnvironmentReference, EscalationOutcome, Generation,
|
||||
GenerationIdentity, GitRoutingOwnership, GuestProcessIdentity, HelperOwnership,
|
||||
HostProcessIdentity, HostProcessOwnershipSlot, InteractiveSessionBatch,
|
||||
InteractiveSessionEvent, InteractiveSessionMessage, InteractiveSessionRequest, Mailbox,
|
||||
MailboxCancellation, MailboxRequest, MailboxResult, MailboxResultAck, MailboxResultChunk,
|
||||
MountIdentity, NamespaceIdentity, NetworkCanaryObservation, NetworkObservation,
|
||||
NetworkOwnership, NetworkReadinessAttestation, NetworkReadinessEvidence,
|
||||
NetworkReadinessIdentity, NonInteractiveOutput, NonInteractiveSessionMessage,
|
||||
NonInteractiveSessionRequest, Participant, ParticipantOwnership, ParticipantState,
|
||||
PersistedHostProcessOwnership, PidfsProcessAuthorityV1, PidfsProcessIdentityV1, PolicyAction,
|
||||
PolicyDelivery, PrefixRule, PrepareOperation, PrepareStageState, PreparedArtifact,
|
||||
PreparedLowerOwnership, ProcessOwnershipCommitAck, ProcessOwnershipMessageVersion,
|
||||
ProtocolCredential, ProtocolCredentialRole, ProtocolPublicationIdentity, PtyIdentity,
|
||||
PublicationOwnership, ResolvedAutoApprovalRule, ResolvedVolume, Resolver, ResultStream,
|
||||
RunscProcessOwnership, SandboxCredentialBundle, SandboxOwnership, Session, SessionBinding,
|
||||
SessionClose, SessionExit, SessionExitOutcome, SessionRegistrationRequest,
|
||||
SessionRegistrationResponse, SessionRequestCapability, SessionTerminal, SessionTerminalEvent,
|
||||
SessionTerminalMessage, SessionTerminalState, SignedProtocolRecord, StagedProcessOwnership,
|
||||
StateAnchorOwnership, StateAnchorReleaseEvidence, StorageCatalog, StorageCatalogMetadata,
|
||||
StoreMetadata, TerminalInput, TerminalOutput, TerminalPrompt, TerminalResize, TerminalSignal,
|
||||
TerminalSignalKind, TlsCredential, TlsRole, TrustManifest, TrustedHostConfigurationV1,
|
||||
HostProcessIdentity, HostProcessOwnershipSlot, InteractiveProtocolVersion,
|
||||
InteractiveSessionBatch, InteractiveSessionBatchV2, InteractiveSessionEvent,
|
||||
InteractiveSessionEventV2, InteractiveSessionMessage, InteractiveSessionRequest,
|
||||
InteractiveSessionRequestV2, Mailbox, MailboxCancellation, MailboxRequest, MailboxResult,
|
||||
MailboxResultAck, MailboxResultChunk, MountIdentity, NamespaceIdentity,
|
||||
NetworkCanaryObservation, NetworkObservation, NetworkOwnership, NetworkReadinessAttestation,
|
||||
NetworkReadinessEvidence, NetworkReadinessIdentity, NonInteractiveOutput,
|
||||
NonInteractiveSessionMessage, NonInteractiveSessionRequest, Participant, ParticipantOwnership,
|
||||
ParticipantState, PersistedHostProcessOwnership, PidfsProcessAuthorityV1,
|
||||
PidfsProcessIdentityV1, PolicyAction, PolicyDelivery, PrefixRule, PrepareOperation,
|
||||
PrepareStageState, PreparedArtifact, PreparedLowerOwnership, ProcessOwnershipCommitAck,
|
||||
ProcessOwnershipMessageVersion, ProtocolCredential, ProtocolCredentialRole,
|
||||
ProtocolPublicationIdentity, PtyIdentity, PublicationOwnership, ResolvedAutoApprovalRule,
|
||||
ResolvedVolume, Resolver, ResultStream, RunscProcessOwnership, SandboxCredentialBundle,
|
||||
SandboxOwnership, Session, SessionBinding, SessionClose, SessionExit, SessionExitOutcome,
|
||||
SessionRegistrationRequest, SessionRegistrationResponse, SessionRequestCapability,
|
||||
SessionTerminal, SessionTerminalEvent, SessionTerminalEventV2, SessionTerminalMessage,
|
||||
SessionTerminalState, SignedProtocolRecord, StagedProcessOwnership, StateAnchorOwnership,
|
||||
StateAnchorReleaseEvidence, StorageCatalog, StorageCatalogMetadata, StoreMetadata,
|
||||
TerminalBreak, TerminalInput, TerminalModesV2, TerminalOutput, TerminalPrompt, TerminalResize,
|
||||
TerminalSignal, TerminalSignalKind, TlsCredential, TlsRole, TrustManifest,
|
||||
TrustedHostConfigurationV1,
|
||||
};
|
||||
use crate::protocol::{BootstrapWaitOutcome, BootstrapWaitRequest};
|
||||
|
||||
|
|
@ -67,16 +70,23 @@ schema_types!(
|
|||
"ProcessOwnershipCommitAck" => ProcessOwnershipCommitAck,
|
||||
"GuestProcessIdentity" => GuestProcessIdentity,
|
||||
"SessionTerminalEvent" => SessionTerminalEvent,
|
||||
"SessionTerminalEventV2" => SessionTerminalEventV2,
|
||||
"TerminalInput" => TerminalInput,
|
||||
"TerminalResize" => TerminalResize,
|
||||
"TerminalModesV2" => TerminalModesV2,
|
||||
"TerminalBreak" => TerminalBreak,
|
||||
"TerminalSignal" => TerminalSignal,
|
||||
"TerminalSignalKind" => TerminalSignalKind,
|
||||
"SessionClose" => SessionClose,
|
||||
"SessionTerminalMessage" => SessionTerminalMessage,
|
||||
"InteractiveSessionRequest" => InteractiveSessionRequest,
|
||||
"InteractiveProtocolVersion" => InteractiveProtocolVersion,
|
||||
"InteractiveSessionRequestV2" => InteractiveSessionRequestV2,
|
||||
"InteractiveSessionEvent" => InteractiveSessionEvent,
|
||||
"InteractiveSessionEventV2" => InteractiveSessionEventV2,
|
||||
"InteractiveSessionMessage" => InteractiveSessionMessage,
|
||||
"InteractiveSessionBatch" => InteractiveSessionBatch,
|
||||
"InteractiveSessionBatchV2" => InteractiveSessionBatchV2,
|
||||
"TerminalPrompt" => TerminalPrompt,
|
||||
"NonInteractiveSessionRequest" => NonInteractiveSessionRequest,
|
||||
"NonInteractiveOutput" => NonInteractiveOutput,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@ use crate::identity::{RequestDigest, RequestId};
|
|||
|
||||
use super::{Deserialize, NonZeroU16, ProtocolVersion, Serialize, SessionId};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum InteractiveProtocolVersion {
|
||||
V2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TerminalInput {
|
||||
pub session_id: SessionId,
|
||||
|
|
@ -17,6 +22,34 @@ pub struct TerminalResize {
|
|||
pub pixel_height: u16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TerminalModesV2 {
|
||||
pub input_modes: u32,
|
||||
pub output_modes: u32,
|
||||
pub control_modes: u32,
|
||||
pub local_modes: u32,
|
||||
pub line_discipline: u8,
|
||||
pub input_speed: u32,
|
||||
pub output_speed: u32,
|
||||
pub interrupt: u8,
|
||||
pub quit: u8,
|
||||
pub erase: u8,
|
||||
pub kill: u8,
|
||||
pub eof: u8,
|
||||
pub time: u8,
|
||||
pub minimum: u8,
|
||||
pub switch: u8,
|
||||
pub start: u8,
|
||||
pub stop: u8,
|
||||
pub suspend: u8,
|
||||
pub end_of_line: u8,
|
||||
pub reprint: u8,
|
||||
pub discard: u8,
|
||||
pub word_erase: u8,
|
||||
pub literal_next: u8,
|
||||
pub end_of_line_2: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum TerminalSignalKind {
|
||||
Interrupt,
|
||||
|
|
@ -35,6 +68,11 @@ pub struct SessionClose {
|
|||
pub session_id: SessionId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TerminalBreak {
|
||||
pub session_id: SessionId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum SessionTerminalEvent {
|
||||
Input(TerminalInput),
|
||||
|
|
@ -43,6 +81,14 @@ pub enum SessionTerminalEvent {
|
|||
Close(SessionClose),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum SessionTerminalEventV2 {
|
||||
Input(TerminalInput),
|
||||
Resize(TerminalResize),
|
||||
Break(TerminalBreak),
|
||||
Close(SessionClose),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TerminalOutput {
|
||||
pub session_id: SessionId,
|
||||
|
|
@ -147,6 +193,15 @@ pub struct InteractiveSessionRequest {
|
|||
pub initial_size: TerminalResize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct InteractiveSessionRequestV2 {
|
||||
pub protocol_version: InteractiveProtocolVersion,
|
||||
pub session_id: SessionId,
|
||||
pub argv: Vec<Vec<u8>>,
|
||||
pub initial_size: TerminalResize,
|
||||
pub initial_modes: TerminalModesV2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct TerminalPrompt {
|
||||
pub session_id: SessionId,
|
||||
|
|
@ -169,6 +224,18 @@ pub enum InteractiveSessionEvent {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum InteractiveSessionEventV2 {
|
||||
Terminal(SessionTerminalEventV2),
|
||||
PromptDecision {
|
||||
session_id: SessionId,
|
||||
signed_decision: Vec<u8>,
|
||||
},
|
||||
Poll {
|
||||
session_id: SessionId,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub enum InteractiveSessionMessage {
|
||||
Terminal(SessionTerminalMessage),
|
||||
|
|
@ -181,6 +248,12 @@ pub struct InteractiveSessionBatch {
|
|||
pub messages: Vec<InteractiveSessionMessage>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct InteractiveSessionBatchV2 {
|
||||
pub session_id: SessionId,
|
||||
pub messages: Vec<InteractiveSessionMessage>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct NonInteractiveSessionRequest {
|
||||
pub protocol_version: super::ProtocolVersion,
|
||||
|
|
|
|||
|
|
@ -288,6 +288,35 @@ fn session_id() -> SessionId {
|
|||
SessionId::new(opaque(5))
|
||||
}
|
||||
|
||||
fn terminal_modes_v2() -> TerminalModesV2 {
|
||||
TerminalModesV2 {
|
||||
input_modes: 1,
|
||||
output_modes: 2,
|
||||
control_modes: 3,
|
||||
local_modes: 4,
|
||||
line_discipline: 0,
|
||||
input_speed: 38_400,
|
||||
output_speed: 38_400,
|
||||
interrupt: 3,
|
||||
quit: 28,
|
||||
erase: 127,
|
||||
kill: 21,
|
||||
eof: 4,
|
||||
time: 0,
|
||||
minimum: 1,
|
||||
switch: 0,
|
||||
start: 17,
|
||||
stop: 19,
|
||||
suspend: 26,
|
||||
end_of_line: 0,
|
||||
reprint: 18,
|
||||
discard: 15,
|
||||
word_erase: 23,
|
||||
literal_next: 22,
|
||||
end_of_line_2: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn process(seed: u32) -> PidfsProcessIdentityV1 {
|
||||
PidfsProcessIdentityV1::new(
|
||||
PidfsProcessAuthorityV1 {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,12 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
|||
}),
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
SessionTerminalEventV2::Break(TerminalBreak {
|
||||
session_id: session_id(),
|
||||
}),
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
TerminalInput {
|
||||
session_id: session_id(),
|
||||
|
|
@ -162,6 +168,13 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
|||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(terminal_modes_v2(), &mut seen);
|
||||
verify_shape(
|
||||
TerminalBreak {
|
||||
session_id: session_id(),
|
||||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
TerminalSignal {
|
||||
session_id: session_id(),
|
||||
|
|
@ -227,12 +240,29 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
|||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(InteractiveProtocolVersion::V2, &mut seen);
|
||||
verify_shape(
|
||||
InteractiveSessionRequestV2 {
|
||||
protocol_version: InteractiveProtocolVersion::V2,
|
||||
session_id: session_id(),
|
||||
argv: vec![b"tool".to_vec()],
|
||||
initial_size: interactive_resize,
|
||||
initial_modes: terminal_modes_v2(),
|
||||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
InteractiveSessionEvent::Poll {
|
||||
session_id: session_id(),
|
||||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
InteractiveSessionEventV2::Poll {
|
||||
session_id: session_id(),
|
||||
},
|
||||
&mut seen,
|
||||
);
|
||||
let prompt = TerminalPrompt {
|
||||
session_id: session_id(),
|
||||
request_id: RequestId::new(opaque(54)),
|
||||
|
|
@ -251,6 +281,13 @@ fn every_schema_shape_round_trips_and_rejects_malformed_or_unknown_versions() {
|
|||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(
|
||||
InteractiveSessionBatchV2 {
|
||||
session_id: session_id(),
|
||||
messages: Vec::new(),
|
||||
},
|
||||
&mut seen,
|
||||
);
|
||||
verify_shape(generation_identity(), &mut seen);
|
||||
verify_shape(trust_manifest(), &mut seen);
|
||||
verify_shape(tls_credential.clone(), &mut seen);
|
||||
|
|
@ -649,8 +686,8 @@ persistent_dirs = []
|
|||
assert_eq!(
|
||||
seen.digest(),
|
||||
[
|
||||
54, 187, 13, 149, 227, 157, 1, 54, 234, 66, 127, 9, 34, 65, 173, 20, 214, 200, 126, 67,
|
||||
1, 216, 152, 76, 167, 32, 177, 218, 0, 192, 175, 28,
|
||||
201, 9, 82, 185, 208, 17, 32, 181, 237, 9, 161, 239, 26, 109, 45, 148, 192, 138, 104,
|
||||
114, 203, 147, 61, 180, 162, 184, 139, 77, 227, 8, 27, 128,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ fn control_corpus(digest: &mut Sha256) {
|
|||
ControlRequest::Register(_) => ControlRequest::Register(registration_request()),
|
||||
ControlRequest::Execute(_) => ControlRequest::Execute(NonInteractiveSessionRequest { protocol_version: ProtocolVersion::V1, session_id: session_id(), argv: vec![b"tool".to_vec()] }),
|
||||
ControlRequest::ExecuteInteractive(_) => ControlRequest::ExecuteInteractive(InteractiveSessionRequest { protocol_version: ProtocolVersion::V1, session_id: session_id(), argv: vec![b"tool".to_vec()], initial_size: resize }),
|
||||
ControlRequest::ExecuteInteractiveV2(_) => ControlRequest::ExecuteInteractiveV2(InteractiveSessionRequestV2 { protocol_version: InteractiveProtocolVersion::V2, session_id: session_id(), argv: vec![b"tool".to_vec()], initial_size: resize, initial_modes: terminal_modes_v2() }),
|
||||
});
|
||||
enum_corpus!(digest, ControlResponse, {
|
||||
ControlResponse::Enrolled(_) => ControlResponse::Enrolled(enrollment_response()),
|
||||
|
|
@ -231,6 +232,9 @@ fn credential_corpus(digest: &mut Sha256) {
|
|||
enum_corpus!(digest, ProtocolVersion, {
|
||||
ProtocolVersion::V1 => ProtocolVersion::V1,
|
||||
});
|
||||
enum_corpus!(digest, InteractiveProtocolVersion, {
|
||||
InteractiveProtocolVersion::V2 => InteractiveProtocolVersion::V2,
|
||||
});
|
||||
enum_corpus!(digest, TlsRole, {
|
||||
TlsRole::SessionCoordinator => TlsRole::SessionCoordinator,
|
||||
TlsRole::ClientCli => TlsRole::ClientCli,
|
||||
|
|
@ -325,8 +329,8 @@ fn every_serialized_enum_variant_has_fixed_shape_evidence() {
|
|||
assert_eq!(
|
||||
digest.finalize().as_slice(),
|
||||
[
|
||||
63, 126, 242, 211, 164, 165, 11, 185, 180, 135, 109, 135, 92, 142, 111, 169, 142, 215,
|
||||
49, 4, 170, 169, 119, 237, 235, 161, 192, 89, 92, 93, 88, 35,
|
||||
16, 136, 82, 10, 228, 231, 132, 63, 189, 59, 106, 16, 164, 103, 232, 11, 245, 31, 144,
|
||||
53, 91, 201, 91, 74, 66, 14, 174, 78, 110, 17, 181, 21,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue