proc: better handling of process groups + shell interrupts

This commit is contained in:
Mark Poliakov 2024-03-13 17:55:14 +02:00
parent ae09849fda
commit 2dc162d1a0
20 changed files with 220 additions and 93 deletions

View File

@ -17,7 +17,10 @@ use libk_mm_interface::{
process::ProcessAddressSpaceManager, process::ProcessAddressSpaceManager,
table::{MapAttributes, TableAllocator}, table::{MapAttributes, TableAllocator},
}; };
use yggdrasil_abi::{error::Error, process::Signal}; use yggdrasil_abi::{
error::Error,
process::{ProcessGroupId, Signal},
};
pub struct ArchitectureImpl; pub struct ArchitectureImpl;
@ -171,6 +174,6 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator> TaskContext<K, PA>
} }
#[no_mangle] #[no_mangle]
extern "Rust" fn __signal_process_group(_group_id: u32, _signal: Signal) { extern "Rust" fn __signal_process_group(_group_id: ProcessGroupId, _signal: Signal) {
unimplemented!() unimplemented!()
} }

View File

@ -19,7 +19,7 @@ use yggdrasil_abi::{
DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions, DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
TerminalOutputOptions, TerminalSize, TerminalOutputOptions, TerminalSize,
}, },
process::{ProcessId, Signal}, process::{ProcessGroupId, Signal},
}; };
const CAPACITY: usize = 8192; const CAPACITY: usize = 8192;
@ -38,7 +38,7 @@ struct PtyMasterToSlaveHalf {
// Actual data to be read by the slave // Actual data to be read by the slave
buffer: IrqSafeSpinlock<MasterToSlaveBuffer>, buffer: IrqSafeSpinlock<MasterToSlaveBuffer>,
ready_ring: LossyRingQueue<u8>, ready_ring: LossyRingQueue<u8>,
signal_pgroup: IrqSafeRwLock<Option<ProcessId>>, signal_pgroup: IrqSafeRwLock<Option<ProcessGroupId>>,
} }
/// Pseudo-terminal shared device /// Pseudo-terminal shared device

View File

@ -18,7 +18,7 @@ use yggdrasil_abi::{
io::SeekFrom, io::SeekFrom,
pass::{Place, Placer}, pass::{Place, Placer},
path::Path, path::Path,
process::ProgramArgumentInner, process::{ProcessGroupId, ProgramArgumentInner},
}; };
use crate::{ use crate::{
@ -170,6 +170,7 @@ fn setup_context(
fn setup_binary<S, PM, IO>( fn setup_binary<S, PM, IO>(
name: S, name: S,
group_id: ProcessGroupId,
parent: Option<Weak<ProcessImpl<PM, IO>>>, parent: Option<Weak<ProcessImpl<PM, IO>>>,
space: ProcessAddressSpace, space: ProcessAddressSpace,
image: ProcessImage, image: ProcessImage,
@ -182,8 +183,14 @@ where
IO: ProcessIo, IO: ProcessIo,
{ {
let context = setup_context(&space, &image, args, envs)?; let context = setup_context(&space, &image, args, envs)?;
let (process, main) = let (process, main) = ProcessImpl::new_with_main(
ProcessImpl::new_with_main(name, parent, Arc::new(space), context, Some(image)); name,
group_id,
parent,
Arc::new(space),
context,
Some(image),
);
Ok((process, main)) Ok((process, main))
} }
@ -237,6 +244,7 @@ fn xxx_load_program<PS: ProgramLoadSource, P: AsRef<Path>>(
/// Loads a program from given `path` /// Loads a program from given `path`
pub fn load<PS, P, PM, IO>( pub fn load<PS, P, PM, IO>(
source: &mut PS, source: &mut PS,
group_id: ProcessGroupId,
parent: Option<Weak<ProcessImpl<PM, IO>>>, parent: Option<Weak<ProcessImpl<PM, IO>>>,
path: P, path: P,
args: &[&str], args: &[&str],
@ -254,7 +262,7 @@ where
let space = ProcessAddressSpace::new()?; let space = ProcessAddressSpace::new()?;
let (image, args, envs) = xxx_load_program(&space, source, path, args, envs)?; let (image, args, envs) = xxx_load_program(&space, source, path, args, envs)?;
setup_binary(path.display(), parent, space, image, &args, &envs) setup_binary(path.display(), group_id, parent, space, image, &args, &envs)
} }
pub fn load_into<P, PM, IO>( pub fn load_into<P, PM, IO>(

View File

@ -16,10 +16,10 @@ use kernel_arch::{Architecture, ArchitectureImpl, KernelTableManagerImpl};
use libk_mm::phys::GlobalPhysicalAllocator; use libk_mm::phys::GlobalPhysicalAllocator;
pub(crate) mod api { pub(crate) mod api {
use yggdrasil_abi::process::{ProcessId, Signal}; use yggdrasil_abi::process::{ProcessGroupId, Signal};
extern "Rust" { extern "Rust" {
pub fn __signal_process_group(group_id: ProcessId, signal: Signal); pub fn __signal_process_group(group_id: ProcessGroupId, signal: Signal);
} }
} }
@ -41,7 +41,7 @@ pub type TaskContextImpl =
use sched::CpuQueue; use sched::CpuQueue;
pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState}; pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState};
use yggdrasil_abi::process::{ProcessId, Signal}; use yggdrasil_abi::process::{ProcessGroupId, Signal};
/// Returns local CPU index /// Returns local CPU index
#[inline] #[inline]
@ -53,6 +53,6 @@ pub fn cpu_count() -> usize {
ArchitectureImpl::cpu_count() ArchitectureImpl::cpu_count()
} }
pub fn signal_process_group(group_id: ProcessId, signal: Signal) { pub fn signal_process_group(group_id: ProcessGroupId, signal: Signal) {
unsafe { __signal_process_group(group_id, signal) } unsafe { __signal_process_group(group_id, signal) }
} }

View File

@ -1,4 +1,7 @@
use core::marker::PhantomData; use core::{
marker::PhantomData,
sync::atomic::{AtomicU32, Ordering},
};
use abi_lib::SyscallRegister; use abi_lib::SyscallRegister;
use alloc::{ use alloc::{
@ -19,7 +22,7 @@ use libk_util::{
}; };
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
process::{ExitCode, ProcessId, Signal, ThreadSpawnOptions}, process::{ExitCode, ProcessGroupId, ProcessId, Signal, ThreadSpawnOptions},
}; };
use crate::{ use crate::{
@ -69,7 +72,7 @@ pub struct ProcessImage {
pub struct ProcessInner<IO: ProcessIo> { pub struct ProcessInner<IO: ProcessIo> {
session_id: ProcessId, session_id: ProcessId,
group_id: ProcessId, group_id: ProcessGroupId,
session_terminal: Option<Arc<IO::Node>>, session_terminal: Option<Arc<IO::Node>>,
threads: Vec<Arc<Thread>>, threads: Vec<Arc<Thread>>,
@ -140,6 +143,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
/// Creates a new process with given main thread /// Creates a new process with given main thread
pub fn new_with_main<S: Into<String>>( pub fn new_with_main<S: Into<String>>(
name: S, name: S,
group_id: ProcessGroupId,
parent: Option<Weak<Self>>, parent: Option<Weak<Self>>,
space: Arc<ProcessAddressSpace>, space: Arc<ProcessAddressSpace>,
context: TaskContextImpl, context: TaskContextImpl,
@ -153,7 +157,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
id, id,
parent, parent,
inner: IrqSafeRwLock::new(ProcessInner::new(id, Some(space.clone()), image)), inner: IrqSafeRwLock::new(ProcessInner::new(id, group_id, Some(space.clone()), image)),
exit: OneTimeEvent::new(), exit: OneTimeEvent::new(),
io: IrqSafeSpinlock::new(IO::new()), io: IrqSafeSpinlock::new(IO::new()),
@ -170,6 +174,13 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
(process, thread) (process, thread)
} }
pub fn create_group() -> ProcessGroupId {
static ID: AtomicU32 = AtomicU32::new(1);
let id = ID.fetch_add(1, Ordering::AcqRel);
unsafe { ProcessGroupId::from_raw(id) }
}
/// Spawns a new child thread within the process /// Spawns a new child thread within the process
pub fn spawn_thread(self: &Arc<Self>, options: &ThreadSpawnOptions) -> Result<ThreadId, Error> { pub fn spawn_thread(self: &Arc<Self>, options: &ThreadSpawnOptions) -> Result<ThreadId, Error> {
log::debug!( log::debug!(
@ -216,6 +227,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
let (new_process, new_main) = Self::new_with_main( let (new_process, new_main) = Self::new_with_main(
self.name(), self.name(),
self.group_id(),
Some(Arc::downgrade(self)), Some(Arc::downgrade(self)),
Arc::new(new_space), Arc::new(new_space),
new_context, new_context,
@ -282,7 +294,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
} }
/// Returns the process group ID of the process /// Returns the process group ID of the process
pub fn group_id(&self) -> ProcessId { pub fn group_id(&self) -> ProcessGroupId {
self.inner.read().group_id self.inner.read().group_id
} }
@ -292,7 +304,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
} }
/// Changes the process's group ID /// Changes the process's group ID
pub fn set_group_id(&self, id: ProcessId) { pub fn set_group_id(&self, id: ProcessGroupId) {
self.inner.write().group_id = id; self.inner.write().group_id = id;
} }
@ -359,7 +371,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
} }
/// Raises a signal for the specified process group /// Raises a signal for the specified process group
pub fn signal_group(group_id: ProcessId, signal: Signal) { pub fn signal_group(group_id: ProcessGroupId, signal: Signal) {
PM::for_each(|_, proc| { PM::for_each(|_, proc| {
let inner = proc.inner.read(); let inner = proc.inner.read();
if !proc.has_exited() && inner.group_id == group_id { if !proc.has_exited() && inner.group_id == group_id {
@ -397,12 +409,13 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
impl<IO: ProcessIo> ProcessInner<IO> { impl<IO: ProcessIo> ProcessInner<IO> {
pub fn new( pub fn new(
id: ProcessId, id: ProcessId,
group_id: ProcessGroupId,
space: Option<Arc<ProcessAddressSpace>>, space: Option<Arc<ProcessAddressSpace>>,
image: Option<ProcessImage>, image: Option<ProcessImage>,
) -> Self { ) -> Self {
Self { Self {
session_id: id, session_id: id,
group_id: id, group_id,
session_terminal: None, session_terminal: None,
threads: Vec::new(), threads: Vec::new(),

View File

@ -8,7 +8,7 @@ use core::{
use abi::{ use abi::{
error::Error, error::Error,
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions}, io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
process::{ProcessId, Signal}, process::{ProcessGroupId, Signal},
}; };
use device_api::serial::SerialDevice; use device_api::serial::SerialDevice;
use futures_util::Future; use futures_util::Future;
@ -83,7 +83,7 @@ impl TerminalRing {
struct TtyContextInner { struct TtyContextInner {
config: TerminalOptions, config: TerminalOptions,
process_group: Option<ProcessId>, process_group: Option<ProcessGroupId>,
} }
/// Represents the context of a terminal device /// Represents the context of a terminal device
@ -99,7 +99,7 @@ pub trait TtyDevice: SerialDevice {
fn context(&self) -> &TtyContext; fn context(&self) -> &TtyContext;
/// Sets the process group to which signals from this terminal should be delivered /// Sets the process group to which signals from this terminal should be delivered
fn set_signal_group(&self, id: ProcessId) { fn set_signal_group(&self, id: ProcessGroupId) {
self.context().inner.lock().process_group.replace(id); self.context().inner.lock().process_group.replace(id);
} }

View File

@ -11,7 +11,7 @@ use vfs::{impls::FnSymlink, IoContext, NodeRef};
use crate::{ use crate::{
fs::{FileBlockAllocator, INITRD_DATA}, fs::{FileBlockAllocator, INITRD_DATA},
proc::{self, random}, proc::{self, random},
task::process::ProcessManagerImpl, task::process::{ProcessImpl, ProcessManagerImpl},
}; };
fn setup_root() -> Result<NodeRef, Error> { fn setup_root() -> Result<NodeRef, Error> {
@ -67,8 +67,9 @@ pub fn kinit() -> Result<(), Error> {
let mut ioctx = IoContext::new(root); let mut ioctx = IoContext::new(root);
{ {
let group_id = ProcessImpl::create_group();
let (user_init, user_init_main) = let (user_init, user_init_main) =
proc::load_binary(&mut ioctx, None, "/init", &["/init", "xxx"], &[])?; proc::load_binary(&mut ioctx, group_id, None, "/init", &["/init", "xxx"], &[])?;
let mut io = user_init.io.lock(); let mut io = user_init.io.lock();
io.set_ioctx(ioctx); io.set_ioctx(ioctx);

View File

@ -1,6 +1,6 @@
//! Internal management for processes //! Internal management for processes
use abi::{error::Error, path::Path}; use abi::{error::Error, path::Path, process::ProcessGroupId};
use alloc::sync::{Arc, Weak}; use alloc::sync::{Arc, Weak};
use libk_thread::thread::Thread; use libk_thread::thread::Thread;
use vfs::IoContext; use vfs::IoContext;
@ -14,10 +14,11 @@ pub mod random;
#[inline] #[inline]
pub fn load_binary<P: AsRef<Path>>( pub fn load_binary<P: AsRef<Path>>(
ioctx: &mut IoContext, ioctx: &mut IoContext,
group_id: ProcessGroupId,
parent: Option<Weak<ProcessImpl>>, parent: Option<Weak<ProcessImpl>>,
path: P, path: P,
args: &[&str], args: &[&str],
envs: &[&str], envs: &[&str],
) -> Result<(Arc<ProcessImpl>, Arc<Thread>), Error> { ) -> Result<(Arc<ProcessImpl>, Arc<Thread>), Error> {
libk_thread::binary::load(ioctx, parent, path, args, envs) libk_thread::binary::load(ioctx, group_id, parent, path, args, envs)
} }

View File

@ -54,7 +54,7 @@ mod impls {
}; };
use abi::{ use abi::{
io::ChannelPublisherId, io::ChannelPublisherId,
process::{ExecveOptions, ProcessId, SpawnOption}, process::{ExecveOptions, ProcessGroupId, ProcessId, SpawnOption},
}; };
use alloc::{boxed::Box, sync::Arc}; use alloc::{boxed::Box, sync::Arc};
use libk::{block, runtime}; use libk::{block, runtime};
@ -81,7 +81,7 @@ mod impls {
debug::LogLevel, debug::LogLevel,
fs, fs,
proc::{self, random}, proc::{self, random},
task::process::ProcessManagerImpl, task::process::{ProcessImpl, ProcessManagerImpl},
}; };
use super::{run_with_io, run_with_io_at}; use super::{run_with_io, run_with_io_at};
@ -175,6 +175,18 @@ mod impls {
} }
// Process/thread management // Process/thread management
pub(crate) fn create_process_group() -> ProcessGroupId {
let id = ProcessImpl::create_group();
log::info!("CREATED PROCESS GROUP {}", id);
id
}
pub(crate) fn get_process_group_id() -> ProcessGroupId {
let thread = Thread::current();
let process = thread.process::<ProcessManagerImpl>();
process.group_id()
}
pub(crate) fn exit_process(code: ExitCode) -> ! { pub(crate) fn exit_process(code: ExitCode) -> ! {
let thread = Thread::current(); let thread = Thread::current();
thread.exit_process::<ProcessManagerImpl>(code) thread.exit_process::<ProcessManagerImpl>(code)
@ -188,6 +200,7 @@ mod impls {
// Setup a new process from the file // Setup a new process from the file
let (child_process, child_main) = proc::load_binary( let (child_process, child_main) = proc::load_binary(
io.ioctx_mut(), io.ioctx_mut(),
process.group_id(),
Some(Arc::downgrade(&process)), Some(Arc::downgrade(&process)),
options.program, options.program,
options.arguments, options.arguments,
@ -213,7 +226,7 @@ mod impls {
} }
&SpawnOption::SetProcessGroup(pgroup) => { &SpawnOption::SetProcessGroup(pgroup) => {
let pgroup = if pgroup.into_raw() == 0 { let pgroup = if pgroup.into_raw() == 0 {
child_process.id() todo!()
} else { } else {
pgroup pgroup
}; };
@ -261,11 +274,10 @@ mod impls {
process.id() process.id()
} }
pub(crate) fn nanosleep(duration: &Duration) { pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
block! { block! {
runtime::sleep(*duration).await runtime::sleep(*duration).await
} }
.unwrap();
} }
pub(crate) fn set_signal_entry(ip: usize, sp: usize) { pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
@ -311,8 +323,10 @@ mod impls {
}); });
} }
let group_id = ProcessImpl::create_group();
process.set_session_id(process.id()); process.set_session_id(process.id());
process.set_group_id(process.id()); process.set_group_id(group_id);
Ok(()) Ok(())
} }

View File

@ -1,6 +1,6 @@
//! Process data structures //! Process data structures
use abi::process::{ProcessId, Signal}; use abi::process::{ProcessGroupId, ProcessId, Signal};
use alloc::{collections::BTreeMap, sync::Arc}; use alloc::{collections::BTreeMap, sync::Arc};
use libk_thread::process::{Process, ProcessManager}; use libk_thread::process::{Process, ProcessManager};
use libk_util::sync::spin_rwlock::IrqSafeRwLock; use libk_util::sync::spin_rwlock::IrqSafeRwLock;
@ -40,6 +40,6 @@ impl ProcessManager for ProcessManagerImpl {
} }
#[no_mangle] #[no_mangle]
fn __signal_process_group(group_id: ProcessId, signal: Signal) { fn __signal_process_group(group_id: ProcessGroupId, signal: Signal) {
ProcessImpl::signal_group(group_id, signal) ProcessImpl::signal_group(group_id, signal)
} }

View File

@ -52,12 +52,15 @@ syscall map_memory(hint: Option<NonZeroUsize>, len: usize, source: &MappingSourc
syscall unmap_memory(address: usize, len: usize) -> Result<()>; syscall unmap_memory(address: usize, len: usize) -> Result<()>;
// Process/thread management // Process/thread management
syscall create_process_group() -> ProcessGroupId;
syscall get_process_group_id() -> ProcessGroupId;
syscall exit_process(code: ExitCode) -> !; syscall exit_process(code: ExitCode) -> !;
syscall spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId>; syscall spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId>;
syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>; syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>;
syscall get_pid() -> ProcessId; syscall get_pid() -> ProcessId;
syscall nanosleep(dur: &Duration); syscall nanosleep(dur: &Duration) -> Result<()>;
syscall exit_signal(frame: &SignalEntryData) -> !; syscall exit_signal(frame: &SignalEntryData) -> !;
syscall set_signal_entry(ip: usize, sp: usize); syscall set_signal_entry(ip: usize, sp: usize);

View File

@ -1,6 +1,6 @@
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use crate::generated::ProcessId; use crate::generated::ProcessGroupId;
use super::terminal; use super::terminal;
@ -19,7 +19,7 @@ pub enum DeviceRequest {
/// Return the terminal's size /// Return the terminal's size
GetTerminalSize(MaybeUninit<terminal::TerminalSize>), GetTerminalSize(MaybeUninit<terminal::TerminalSize>),
/// Sets a foreground process group ID for the terminal /// Sets a foreground process group ID for the terminal
SetTerminalGroup(ProcessId), SetTerminalGroup(ProcessGroupId),
/// "Acquires" ownership of the device, preventing others from accessing it /// "Acquires" ownership of the device, preventing others from accessing it
AcquireDevice, AcquireDevice,
/// "Releases" ownership of the device /// "Releases" ownership of the device

View File

@ -10,7 +10,8 @@ use crate::{
mod exit; mod exit;
pub use crate::generated::{ pub use crate::generated::{
ExecveOptions, ProcessId, Signal, SignalEntryData, SpawnOptions, ThreadId, ThreadSpawnOptions, ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnOptions, ThreadId,
ThreadSpawnOptions,
}; };
pub use exit::ExitCode; pub use exit::ExitCode;
@ -25,7 +26,7 @@ pub enum SpawnOption {
child: RawFd, child: RawFd,
}, },
/// The new process should be placed in the specified group /// The new process should be placed in the specified group
SetProcessGroup(ProcessId), SetProcessGroup(ProcessGroupId),
/// Gain terminal control for the given FD /// Gain terminal control for the given FD
GainTerminal(RawFd), GainTerminal(RawFd),
} }
@ -70,3 +71,9 @@ impl fmt::Display for ProcessId {
write!(f, "<Process {}>", self.0) write!(f, "<Process {}>", self.0)
} }
} }
impl fmt::Display for ProcessGroupId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<Process group {}>", self.0)
}
}

View File

@ -1,6 +1,7 @@
//! Process management data types //! Process management data types
pub use abi::process::{ pub use abi::process::{
ExecveOptions, ExitCode, MutexOperation, ProcessId, ProcessInfoElement, ProgramArgumentInner, ExecveOptions, ExitCode, MutexOperation, ProcessGroupId, ProcessId, ProcessInfoElement,
Signal, SignalEntryData, SpawnOption, SpawnOptions, ThreadId, ThreadSpawnOptions, ProgramArgumentInner, Signal, SignalEntryData, SpawnOption, SpawnOptions, ThreadId,
ThreadSpawnOptions,
}; };

View File

@ -18,7 +18,9 @@ mod generated {
}, },
mem::MappingSource, mem::MappingSource,
net::SocketType, net::SocketType,
process::{ExecveOptions, ProcessId, Signal, SignalEntryData, SpawnOptions}, process::{
ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnOptions,
},
SyscallFunction, SyscallFunction,
}; };

View File

@ -1,4 +1,4 @@
#![cfg_attr(not(unix), feature(yggdrasil_os))] #![cfg_attr(not(unix), feature(yggdrasil_os, rustc_private))]
use std::{ use std::{
collections::HashMap, collections::HashMap,
@ -7,7 +7,7 @@ use std::{
io::{self, stdin, stdout, BufRead, BufReader, Stdin, Write}, io::{self, stdin, stdout, BufRead, BufReader, Stdin, Write},
os::fd::{FromRawFd, IntoRawFd, OwnedFd}, os::fd::{FromRawFd, IntoRawFd, OwnedFd},
path::Path, path::Path,
process::{ExitCode, Stdio}, process::{Child, ExitCode, Stdio},
}; };
use clap::Parser; use clap::Parser;
@ -36,6 +36,22 @@ pub enum Outcome {
ExitShell(ExitCode), ExitShell(ExitCode),
} }
pub struct PipelineElement<'a> {
pub command: &'a str,
pub args: &'a [String],
pub input: Stdio,
pub output: Stdio,
}
pub struct Pipeline<'a> {
pub elements: Vec<PipelineElement<'a>>,
pub env: &'a HashMap<String, String>,
}
pub struct SpawnedPipeline {
pub children: Vec<Child>,
}
pub enum Input { pub enum Input {
Interactive(Stdin), Interactive(Stdin),
File(BufReader<File>), File(BufReader<File>),
@ -95,7 +111,6 @@ pub fn exec(
let mut inputs = vec![]; let mut inputs = vec![];
let mut outputs = vec![]; let mut outputs = vec![];
let mut children = vec![];
let mut pipe_fds = vec![]; let mut pipe_fds = vec![];
inputs.push(Stdio::inherit()); inputs.push(Stdio::inherit());
@ -118,31 +133,39 @@ pub fn exec(
assert_eq!(inputs.len(), outputs.len()); assert_eq!(inputs.len(), outputs.len());
assert_eq!(inputs.len(), pipeline.len()); assert_eq!(inputs.len(), pipeline.len());
let mut elements = vec![];
let ios = inputs.drain(..).zip(outputs.drain(..)); let ios = inputs.drain(..).zip(outputs.drain(..));
for (command, (input, output)) in pipeline.iter().zip(ios) { for (command, (input, output)) in pipeline.iter().zip(ios) {
let (cmd, args) = command.words.split_first().unwrap(); let (cmd, args) = command.words.split_first().unwrap();
let child = sys::exec_binary(interactive, cmd, args, env, input, output)?; let element = PipelineElement {
command: cmd,
args,
input,
output,
};
children.push(child); elements.push(element);
} }
let pipeline = Pipeline { elements, env };
let pipeline = sys::spawn_pipeline(interactive, pipeline)?;
drop(pipe_fds); drop(pipe_fds);
for mut child in children.drain(..) { let status = sys::wait_for_pipeline(interactive, pipeline)?;
let status = child.wait()?;
if !status.success() { Ok(status)
return Ok(Outcome::from(status));
}
}
Ok(Outcome::ok())
} }
fn run(mut input: Input, vars: &mut HashMap<String, String>) -> io::Result<ExitCode> { fn run(mut input: Input, vars: &mut HashMap<String, String>) -> io::Result<ExitCode> {
let mut line = String::new(); let mut line = String::new();
if input.is_interactive() {
sys::init_signal_handler();
}
let code = loop { let code = loop {
line.clear(); line.clear();
@ -171,8 +194,7 @@ fn run(mut input: Input, vars: &mut HashMap<String, String>) -> io::Result<ExitC
} }
Ok(Outcome::Exited(code)) => code % 256, Ok(Outcome::Exited(code)) => code % 256,
Err(e) => { Err(e) => {
// TODO stringify the command back eprintln!("{}: {}", line, e);
eprintln!("<command>: {}", e);
127 127
} }
}; };

View File

@ -1,41 +1,76 @@
use std::os::yggdrasil::io::pipe; use std::os::yggdrasil::{
io::{
device::{DeviceRequest, FdDeviceRequest},
pipe,
},
process::{self, CommandExt, ExitStatusExt, ProcessGroupId},
signal::{set_signal_handler, Signal, SignalHandler},
};
use std::{ use std::{
collections::HashMap, io::{self, stdin},
io,
os::fd::OwnedFd, os::fd::OwnedFd,
process::{Child, Command, ExitStatus, Stdio}, process::{Command, ExitStatus},
}; };
use crate::Outcome; use crate::{Outcome, Pipeline, SpawnedPipeline};
pub struct Pipe { pub struct Pipe {
pub read: OwnedFd, pub read: OwnedFd,
pub write: OwnedFd, pub write: OwnedFd,
} }
pub fn exec_binary( fn set_terminal_group(group_id: ProcessGroupId) -> Result<(), io::Error> {
unsafe { stdin().device_request(&mut DeviceRequest::SetTerminalGroup(group_id)) }
}
pub fn spawn_pipeline(
interactive: bool, interactive: bool,
binary: &str, pipeline: Pipeline<'_>,
args: &[String], ) -> Result<SpawnedPipeline, io::Error> {
env: &HashMap<String, String>, let group_id = process::create_process_group();
input: Stdio,
output: Stdio,
) -> Result<Child, io::Error> {
use std::os::yggdrasil::process::CommandExt;
let mut command = Command::new(binary);
let mut command = command
.args(args)
.envs(env.iter())
.stdin(input)
.stdout(output);
if interactive { if interactive {
unsafe { set_terminal_group(group_id)?;
command = command.process_group_raw(0); }
let mut children = vec![];
for element in pipeline.elements {
let mut command = Command::new(element.command);
command
.args(element.args)
.envs(pipeline.env.iter())
.stdin(element.input)
.stdout(element.output)
.process_group(group_id);
children.push(command.spawn()?);
}
Ok(SpawnedPipeline { children })
}
pub fn wait_for_pipeline(
interactive: bool,
mut pipeline: SpawnedPipeline,
) -> Result<Outcome, io::Error> {
let self_group_id = process::group_id();
for mut child in pipeline.children.drain(..) {
let status = child.wait()?;
if !status.success() {
if interactive {
set_terminal_group(self_group_id).ok();
}
return Ok(Outcome::from(status));
} }
} }
command.spawn() if interactive {
set_terminal_group(self_group_id).ok();
}
Ok(Outcome::ok())
} }
pub fn create_pipe() -> Result<Pipe, io::Error> { pub fn create_pipe() -> Result<Pipe, io::Error> {
@ -43,10 +78,16 @@ pub fn create_pipe() -> Result<Pipe, io::Error> {
Ok(Pipe { read, write }) Ok(Pipe { read, write })
} }
fn signal_handler(_sig: Signal) {
println!();
}
pub fn init_signal_handler() {
set_signal_handler(Signal::Interrupted, SignalHandler::Function(signal_handler));
}
impl From<ExitStatus> for Outcome { impl From<ExitStatus> for Outcome {
fn from(value: ExitStatus) -> Self { fn from(value: ExitStatus) -> Self {
use std::os::yggdrasil::process::ExitStatusExt;
if let Some(code) = value.code() { if let Some(code) = value.code() {
Self::Exited(code) Self::Exited(code)
} else if let Some(sig) = value.signal() { } else if let Some(sig) = value.signal() {

View File

@ -1,14 +1,14 @@
use std::{ use std::{
env, env,
fs::File, io::{self, stdout, Read, Stdout, Write},
io::{self, stdout, BufReader, Read, Stdout, Write},
path::Path,
process::ExitCode, process::ExitCode,
}; };
fn cat_file<P: AsRef<Path>>(stdout: &mut Stdout, path: P) -> io::Result<()> { use sysutils::Input;
fn cat_file(stdout: &mut Stdout, path: &str) -> io::Result<()> {
let mut buf = [0; 4096]; let mut buf = [0; 4096];
let mut reader = BufReader::new(File::open(path)?); let mut reader = Input::open_str(path)?;
loop { loop {
let count = reader.read(&mut buf)?; let count = reader.read(&mut buf)?;
@ -25,11 +25,20 @@ fn main() -> ExitCode {
let mut stdout = stdout(); let mut stdout = stdout();
let mut exit = ExitCode::SUCCESS; let mut exit = ExitCode::SUCCESS;
for arg in env::args().skip(1) { let args = env::args().skip(1);
if let Err(error) = cat_file(&mut stdout, &arg) {
eprintln!("{}: {}", arg, error); if args.len() == 0 {
if let Err(error) = cat_file(&mut stdout, "-") {
eprintln!("{}: {}", "<stdin>", error);
exit = ExitCode::FAILURE; exit = ExitCode::FAILURE;
} }
} else {
for arg in env::args().skip(1) {
if let Err(error) = cat_file(&mut stdout, &arg) {
eprintln!("{}: {}", arg, error);
exit = ExitCode::FAILURE;
}
}
} }
exit exit

View File

@ -70,9 +70,9 @@ fn main() -> ExitCode {
// "Attach" the terminal // "Attach" the terminal
unsafe { unsafe {
let pid = std::os::yggdrasil::process::id_ext(); let group_id = std::os::yggdrasil::process::group_id();
stdin() stdin()
.device_request(&mut DeviceRequest::SetTerminalGroup(pid)) .device_request(&mut DeviceRequest::SetTerminalGroup(group_id))
.expect("Could not attach the terminal"); .expect("Could not attach the terminal");
} }

View File

@ -6,6 +6,7 @@ use std::{
os::{ os::{
fd::{AsRawFd, FromRawFd, RawFd}, fd::{AsRawFd, FromRawFd, RawFd},
yggdrasil::{ yggdrasil::{
self,
io::{ io::{
device::{DeviceRequest, FdDeviceRequest}, device::{DeviceRequest, FdDeviceRequest},
poll::PollChannel, poll::PollChannel,
@ -262,13 +263,14 @@ impl<'a> Terminal<'a> {
poll.add(pty_master_fd)?; poll.add(pty_master_fd)?;
let pty_slave_fd = pty_slave.as_raw_fd(); let pty_slave_fd = pty_slave.as_raw_fd();
let group_id = yggdrasil::process::create_process_group();
let shell = unsafe { let shell = unsafe {
Command::new("/bin/sh") Command::new("/bin/sh")
.arg("-l") .arg("-l")
.stdin(Stdio::from_raw_fd(pty_slave_fd)) .stdin(Stdio::from_raw_fd(pty_slave_fd))
.stdout(Stdio::from_raw_fd(pty_slave_fd)) .stdout(Stdio::from_raw_fd(pty_slave_fd))
.stderr(Stdio::from_raw_fd(pty_slave_fd)) .stderr(Stdio::from_raw_fd(pty_slave_fd))
.process_group_raw(0) .process_group(group_id)
.gain_terminal(0) .gain_terminal(0)
.spawn()? .spawn()?
}; };