Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions litebox/src/broker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use litebox_broker_protocol::process::{
ChildExit, ChildSelector, MAX_CHILD_MEMORY_WRITE_SIZE, MAX_CHILD_OBJECT_DUPLICATES,
MAX_PROCESS_BOOTSTRAP_SIZE, ProcessExitStatus, ProcessIdentity, ProcessInfo, ThreadInfo,
};
use litebox_broker_protocol::process_group::{ProcessGroupError, ProcessGroupMembership};
use litebox_broker_protocol::random::MAX_RANDOM_TRANSFER_SIZE;
use litebox_broker_protocol::readiness::ReadinessFlags;
use litebox_broker_protocol::readiness_queue::{
Expand Down Expand Up @@ -161,6 +162,20 @@ pub(crate) trait BrokerControl: Send + Sync {
process_id: litebox_broker_protocol::ProcessId,
) -> core::result::Result<(), BrokerControlError>;

fn get_foreground_group(
&self,
terminal: ObjectHandle,
) -> core::result::Result<
core::result::Result<ProcessGroupMembership, ProcessGroupError>,
BrokerControlError,
>;

fn set_foreground_group(
&self,
terminal: ObjectHandle,
process_group: litebox_broker_protocol::ProcessGroupId,
) -> core::result::Result<core::result::Result<(), ProcessGroupError>, BrokerControlError>;

fn create_thread(&self) -> core::result::Result<ThreadId, BrokerControlError>;

fn exit_thread(&self, thread_id: ThreadId) -> core::result::Result<(), BrokerControlError>;
Expand Down Expand Up @@ -930,6 +945,24 @@ where
self.request(|local| local.create_session(process_id))
}

fn get_foreground_group(
&self,
terminal: ObjectHandle,
) -> core::result::Result<
core::result::Result<ProcessGroupMembership, ProcessGroupError>,
BrokerControlError,
> {
self.request(|local| local.get_foreground_group(terminal))
}

fn set_foreground_group(
&self,
terminal: ObjectHandle,
process_group: litebox_broker_protocol::ProcessGroupId,
) -> core::result::Result<core::result::Result<(), ProcessGroupError>, BrokerControlError> {
self.request(|local| local.set_foreground_group(terminal, process_group))
}

fn create_thread(&self) -> core::result::Result<ThreadId, BrokerControlError> {
self.request(BrokerLocal::create_thread)
}
Expand Down
52 changes: 52 additions & 0 deletions litebox/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use litebox_broker_protocol::process::{
ChildExit, ChildSelector, MAX_CHILD_MEMORY_WRITE_SIZE, ProcessExitStatus, ProcessIdentity,
ProcessInfo, ThreadInfo,
};
use litebox_broker_protocol::process_group::{ProcessGroupError, ProcessGroupMembership};
use litebox_broker_protocol::signal::{SignalEvent, SignalTarget};
use litebox_broker_protocol::{ObjectHandle, ProcessGroupId, ProcessId, ThreadId};
use litebox_platform::time::TimeProvider;
Expand Down Expand Up @@ -58,6 +59,15 @@ pub enum ProcessError {
/// No process has the target process ID.
#[error("no such process")]
NoSuchProcess,
/// The file is not this process's controlling terminal.
#[error("not the controlling terminal")]
NotControllingTerminal,
/// No process group has the target process group ID.
#[error("no such process group")]
NoSuchProcessGroup,
/// The target process group is in another session than this process.
#[error("process group is in another session")]
NotInSession,
}

impl<Platform: RawSyncPrimitivesProvider + TimeProvider> LiteBox<Platform> {
Expand Down Expand Up @@ -175,6 +185,37 @@ impl<Platform: RawSyncPrimitivesProvider + TimeProvider> LiteBox<Platform> {
broker.create_session(process_id).map_err(no_such_process)
}

/// Returns the foreground process group of this process's session and the
/// session, given its controlling terminal `terminal`.
///
/// Returns [`ProcessError::NotControllingTerminal`] if `terminal` is not
/// this process's controlling terminal.
pub fn get_foreground_group(
&self,
terminal: &File<Platform>,
) -> Result<ProcessGroupMembership, ProcessError> {
let broker = self.broker_control().ok_or(ProcessError::Unavailable)?;
Ok(broker.get_foreground_group(terminal.handle())??)
}

/// Moves `process_group` into the foreground of this process's controlling
/// terminal `terminal`, so the terminal's events go to its members.
///
/// Returns, in order of precedence,
/// [`ProcessError::NotControllingTerminal`] if `terminal` is not this
/// process's controlling terminal, [`ProcessError::NoSuchProcessGroup`] if
/// no process group has the ID `process_group`, and
/// [`ProcessError::NotInSession`] if the process group is in another
/// session.
pub fn set_foreground_group(
&self,
terminal: &File<Platform>,
process_group: ProcessGroupId,
) -> Result<(), ProcessError> {
let broker = self.broker_control().ok_or(ProcessError::Unavailable)?;
Ok(broker.set_foreground_group(terminal.handle(), process_group)??)
}

/// Opens the signals other processes send to this process and the exits
/// of its children, including those before it is opened.
///
Expand Down Expand Up @@ -405,6 +446,17 @@ fn no_such_process(error: BrokerControlError) -> ProcessError {
}
}

impl From<ProcessGroupError> for ProcessError {
fn from(error: ProcessGroupError) -> Self {
match error {
ProcessGroupError::NotControllingTerminal => Self::NotControllingTerminal,
ProcessGroupError::NoSuchProcessGroup => Self::NoSuchProcessGroup,
ProcessGroupError::NotInSession => Self::NotInSession,
_ => Self::ServiceFailed,
}
}
}

impl From<BrokerControlError> for ProcessError {
fn from(error: BrokerControlError) -> Self {
match error {
Expand Down
4 changes: 3 additions & 1 deletion litebox_broker_core/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ pub use litebox_broker_protocol::fs::{
FileDirectoryEntry as DirEntry, FileMode as Mode, FileNodeInfo as NodeInfo,
FileSeekWhence as SeekWhence, FileStatus, FileType, FileUser as UserInfo,
};
pub(crate) use service::{File, get_status_flags, open_without_reference, set_status_flags};
pub(crate) use service::{
File, get_status_flags, handle_terminal, open_without_reference, set_status_flags,
};
pub use service::{
FileResult, FileService, UnsupportedFileService, chmod, chown, handle_path, handle_status,
is_terminal, mkdir, open, path_status, read, read_directory, read_link, resolve_path, rmdir,
Expand Down
9 changes: 9 additions & 0 deletions litebox_broker_core/src/fs/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,15 @@ pub fn is_terminal(process: &BrokerProcess, handle: ObjectHandle) -> Result<File
Ok(Ok(file.terminal.is_some()))
}

/// Returns the terminal that a broker-owned open object refers to, if any.
pub(crate) fn handle_terminal(
process: &BrokerProcess,
handle: ObjectHandle,
) -> Result<Option<Arc<dyn crate::terminal::TerminalEndpoint>>> {
let file = file_with_any_rights(process, handle, ObjectRights::WAIT | ObjectRights::WRITE)?;
Ok(file.terminal)
}

/// Controls a shared terminal through any open file handle that refers to it.
///
/// Like Linux, every operation only requires an open handle, not a particular access mode;
Expand Down
15 changes: 8 additions & 7 deletions litebox_broker_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@ pub struct BrokerCore {
pub(crate) socket_provider: Arc<dyn SocketProvider>,
pub(crate) timer_provider: Arc<dyn TimerProvider>,
pub(crate) fs: Arc<dyn FileService>,
/// Guest terminal whose events reach the first root process's group.
pub(crate) terminal: Option<Arc<dyn terminal::TerminalEndpoint>>,
/// Guest terminal, controlling the first root process's session.
pub(crate) terminal: Option<Arc<process_group::ControllingTerminal>>,
pub(crate) socket_ports: BrokerSocketPorts,
pub(crate) process_lifecycle_sink: Arc<dyn ProcessLifecycleSink>,
}
Expand Down Expand Up @@ -421,10 +421,11 @@ impl BrokerCore {

/// Makes `terminal`, which the file service serves as `/dev/tty`, the
/// controlling terminal of the first root process's session: the terminal
/// then signals that process's group. Set it before sharing the core.
/// then signals that session's foreground process group, initially that
/// process's group. Set it before sharing the core.
#[must_use]
pub fn with_terminal(mut self, terminal: Arc<dyn terminal::TerminalEndpoint>) -> Self {
self.terminal = Some(terminal);
self.terminal = Some(Arc::new(process_group::ControllingTerminal::new(terminal)));
self
}

Expand Down Expand Up @@ -548,8 +549,8 @@ impl BrokerCore {
/// Creates one process and its initial thread.
///
/// If initial-thread creation fails, the process is discarded before the
/// error is returned. A root process's group receives the events of the
/// [terminal](Self::with_terminal) unless an earlier root's does.
/// error is returned. The first root process's session gets the
/// [terminal](Self::with_terminal).
///
/// # Panics
///
Expand All @@ -564,7 +565,7 @@ impl BrokerCore {
match process.create_initial_thread() {
Ok(()) => {
if let (None, Some(terminal)) = (parent_id, &self.terminal) {
terminal.attach(process_group::terminal_sink(&process));
terminal.attach(&process);
}
Ok(process)
}
Expand Down
Loading
Loading