proc: better handling of process groups + shell interrupts
This commit is contained in:
parent
ae09849fda
commit
2dc162d1a0
@ -17,7 +17,10 @@ use libk_mm_interface::{
|
||||
process::ProcessAddressSpaceManager,
|
||||
table::{MapAttributes, TableAllocator},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, process::Signal};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
process::{ProcessGroupId, Signal},
|
||||
};
|
||||
|
||||
pub struct ArchitectureImpl;
|
||||
|
||||
@ -171,6 +174,6 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator> TaskContext<K, PA>
|
||||
}
|
||||
|
||||
#[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!()
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use yggdrasil_abi::{
|
||||
DeviceRequest, TerminalInputOptions, TerminalLineOptions, TerminalOptions,
|
||||
TerminalOutputOptions, TerminalSize,
|
||||
},
|
||||
process::{ProcessId, Signal},
|
||||
process::{ProcessGroupId, Signal},
|
||||
};
|
||||
|
||||
const CAPACITY: usize = 8192;
|
||||
@ -38,7 +38,7 @@ struct PtyMasterToSlaveHalf {
|
||||
// Actual data to be read by the slave
|
||||
buffer: IrqSafeSpinlock<MasterToSlaveBuffer>,
|
||||
ready_ring: LossyRingQueue<u8>,
|
||||
signal_pgroup: IrqSafeRwLock<Option<ProcessId>>,
|
||||
signal_pgroup: IrqSafeRwLock<Option<ProcessGroupId>>,
|
||||
}
|
||||
|
||||
/// Pseudo-terminal shared device
|
||||
|
@ -18,7 +18,7 @@ use yggdrasil_abi::{
|
||||
io::SeekFrom,
|
||||
pass::{Place, Placer},
|
||||
path::Path,
|
||||
process::ProgramArgumentInner,
|
||||
process::{ProcessGroupId, ProgramArgumentInner},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -170,6 +170,7 @@ fn setup_context(
|
||||
|
||||
fn setup_binary<S, PM, IO>(
|
||||
name: S,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<ProcessImpl<PM, IO>>>,
|
||||
space: ProcessAddressSpace,
|
||||
image: ProcessImage,
|
||||
@ -182,8 +183,14 @@ where
|
||||
IO: ProcessIo,
|
||||
{
|
||||
let context = setup_context(&space, &image, args, envs)?;
|
||||
let (process, main) =
|
||||
ProcessImpl::new_with_main(name, parent, Arc::new(space), context, Some(image));
|
||||
let (process, main) = ProcessImpl::new_with_main(
|
||||
name,
|
||||
group_id,
|
||||
parent,
|
||||
Arc::new(space),
|
||||
context,
|
||||
Some(image),
|
||||
);
|
||||
Ok((process, main))
|
||||
}
|
||||
|
||||
@ -237,6 +244,7 @@ fn xxx_load_program<PS: ProgramLoadSource, P: AsRef<Path>>(
|
||||
/// Loads a program from given `path`
|
||||
pub fn load<PS, P, PM, IO>(
|
||||
source: &mut PS,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<ProcessImpl<PM, IO>>>,
|
||||
path: P,
|
||||
args: &[&str],
|
||||
@ -254,7 +262,7 @@ where
|
||||
|
||||
let space = ProcessAddressSpace::new()?;
|
||||
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>(
|
||||
|
@ -16,10 +16,10 @@ use kernel_arch::{Architecture, ArchitectureImpl, KernelTableManagerImpl};
|
||||
use libk_mm::phys::GlobalPhysicalAllocator;
|
||||
|
||||
pub(crate) mod api {
|
||||
use yggdrasil_abi::process::{ProcessId, Signal};
|
||||
use yggdrasil_abi::process::{ProcessGroupId, Signal};
|
||||
|
||||
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;
|
||||
pub use types::{AtomicThreadState, ThreadAffinity, ThreadId, ThreadState};
|
||||
|
||||
use yggdrasil_abi::process::{ProcessId, Signal};
|
||||
use yggdrasil_abi::process::{ProcessGroupId, Signal};
|
||||
|
||||
/// Returns local CPU index
|
||||
#[inline]
|
||||
@ -53,6 +53,6 @@ pub fn cpu_count() -> usize {
|
||||
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) }
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use abi_lib::SyscallRegister;
|
||||
use alloc::{
|
||||
@ -19,7 +22,7 @@ use libk_util::{
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
process::{ExitCode, ProcessId, Signal, ThreadSpawnOptions},
|
||||
process::{ExitCode, ProcessGroupId, ProcessId, Signal, ThreadSpawnOptions},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -69,7 +72,7 @@ pub struct ProcessImage {
|
||||
|
||||
pub struct ProcessInner<IO: ProcessIo> {
|
||||
session_id: ProcessId,
|
||||
group_id: ProcessId,
|
||||
group_id: ProcessGroupId,
|
||||
|
||||
session_terminal: Option<Arc<IO::Node>>,
|
||||
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
|
||||
pub fn new_with_main<S: Into<String>>(
|
||||
name: S,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<Self>>,
|
||||
space: Arc<ProcessAddressSpace>,
|
||||
context: TaskContextImpl,
|
||||
@ -153,7 +157,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
|
||||
id,
|
||||
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(),
|
||||
io: IrqSafeSpinlock::new(IO::new()),
|
||||
@ -170,6 +174,13 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
|
||||
(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
|
||||
pub fn spawn_thread(self: &Arc<Self>, options: &ThreadSpawnOptions) -> Result<ThreadId, Error> {
|
||||
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(
|
||||
self.name(),
|
||||
self.group_id(),
|
||||
Some(Arc::downgrade(self)),
|
||||
Arc::new(new_space),
|
||||
new_context,
|
||||
@ -282,7 +294,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
@ -292,7 +304,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
@ -359,7 +371,7 @@ impl<PM: ProcessManager<Process = Self>, IO: ProcessIo> ProcessImpl<PM, IO> {
|
||||
}
|
||||
|
||||
/// 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| {
|
||||
let inner = proc.inner.read();
|
||||
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> {
|
||||
pub fn new(
|
||||
id: ProcessId,
|
||||
group_id: ProcessGroupId,
|
||||
space: Option<Arc<ProcessAddressSpace>>,
|
||||
image: Option<ProcessImage>,
|
||||
) -> Self {
|
||||
Self {
|
||||
session_id: id,
|
||||
group_id: id,
|
||||
group_id,
|
||||
session_terminal: None,
|
||||
threads: Vec::new(),
|
||||
|
||||
|
@ -8,7 +8,7 @@ use core::{
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{TerminalInputOptions, TerminalLineOptions, TerminalOptions, TerminalOutputOptions},
|
||||
process::{ProcessId, Signal},
|
||||
process::{ProcessGroupId, Signal},
|
||||
};
|
||||
use device_api::serial::SerialDevice;
|
||||
use futures_util::Future;
|
||||
@ -83,7 +83,7 @@ impl TerminalRing {
|
||||
|
||||
struct TtyContextInner {
|
||||
config: TerminalOptions,
|
||||
process_group: Option<ProcessId>,
|
||||
process_group: Option<ProcessGroupId>,
|
||||
}
|
||||
|
||||
/// Represents the context of a terminal device
|
||||
@ -99,7 +99,7 @@ pub trait TtyDevice: SerialDevice {
|
||||
fn context(&self) -> &TtyContext;
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ use vfs::{impls::FnSymlink, IoContext, NodeRef};
|
||||
use crate::{
|
||||
fs::{FileBlockAllocator, INITRD_DATA},
|
||||
proc::{self, random},
|
||||
task::process::ProcessManagerImpl,
|
||||
task::process::{ProcessImpl, ProcessManagerImpl},
|
||||
};
|
||||
|
||||
fn setup_root() -> Result<NodeRef, Error> {
|
||||
@ -67,8 +67,9 @@ pub fn kinit() -> Result<(), Error> {
|
||||
let mut ioctx = IoContext::new(root);
|
||||
|
||||
{
|
||||
let group_id = ProcessImpl::create_group();
|
||||
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();
|
||||
io.set_ioctx(ioctx);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Internal management for processes
|
||||
|
||||
use abi::{error::Error, path::Path};
|
||||
use abi::{error::Error, path::Path, process::ProcessGroupId};
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use libk_thread::thread::Thread;
|
||||
use vfs::IoContext;
|
||||
@ -14,10 +14,11 @@ pub mod random;
|
||||
#[inline]
|
||||
pub fn load_binary<P: AsRef<Path>>(
|
||||
ioctx: &mut IoContext,
|
||||
group_id: ProcessGroupId,
|
||||
parent: Option<Weak<ProcessImpl>>,
|
||||
path: P,
|
||||
args: &[&str],
|
||||
envs: &[&str],
|
||||
) -> 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)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ mod impls {
|
||||
};
|
||||
use abi::{
|
||||
io::ChannelPublisherId,
|
||||
process::{ExecveOptions, ProcessId, SpawnOption},
|
||||
process::{ExecveOptions, ProcessGroupId, ProcessId, SpawnOption},
|
||||
};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use libk::{block, runtime};
|
||||
@ -81,7 +81,7 @@ mod impls {
|
||||
debug::LogLevel,
|
||||
fs,
|
||||
proc::{self, random},
|
||||
task::process::ProcessManagerImpl,
|
||||
task::process::{ProcessImpl, ProcessManagerImpl},
|
||||
};
|
||||
|
||||
use super::{run_with_io, run_with_io_at};
|
||||
@ -175,6 +175,18 @@ mod impls {
|
||||
}
|
||||
|
||||
// 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) -> ! {
|
||||
let thread = Thread::current();
|
||||
thread.exit_process::<ProcessManagerImpl>(code)
|
||||
@ -188,6 +200,7 @@ mod impls {
|
||||
// Setup a new process from the file
|
||||
let (child_process, child_main) = proc::load_binary(
|
||||
io.ioctx_mut(),
|
||||
process.group_id(),
|
||||
Some(Arc::downgrade(&process)),
|
||||
options.program,
|
||||
options.arguments,
|
||||
@ -213,7 +226,7 @@ mod impls {
|
||||
}
|
||||
&SpawnOption::SetProcessGroup(pgroup) => {
|
||||
let pgroup = if pgroup.into_raw() == 0 {
|
||||
child_process.id()
|
||||
todo!()
|
||||
} else {
|
||||
pgroup
|
||||
};
|
||||
@ -261,11 +274,10 @@ mod impls {
|
||||
process.id()
|
||||
}
|
||||
|
||||
pub(crate) fn nanosleep(duration: &Duration) {
|
||||
pub(crate) fn nanosleep(duration: &Duration) -> Result<(), Error> {
|
||||
block! {
|
||||
runtime::sleep(*duration).await
|
||||
}
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
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_group_id(process.id());
|
||||
process.set_group_id(group_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Process data structures
|
||||
|
||||
use abi::process::{ProcessId, Signal};
|
||||
use abi::process::{ProcessGroupId, ProcessId, Signal};
|
||||
use alloc::{collections::BTreeMap, sync::Arc};
|
||||
use libk_thread::process::{Process, ProcessManager};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
@ -40,6 +40,6 @@ impl ProcessManager for ProcessManagerImpl {
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
@ -52,12 +52,15 @@ syscall map_memory(hint: Option<NonZeroUsize>, len: usize, source: &MappingSourc
|
||||
syscall unmap_memory(address: usize, len: usize) -> Result<()>;
|
||||
|
||||
// Process/thread management
|
||||
syscall create_process_group() -> ProcessGroupId;
|
||||
syscall get_process_group_id() -> ProcessGroupId;
|
||||
|
||||
syscall exit_process(code: ExitCode) -> !;
|
||||
syscall spawn_process(options: &SpawnOptions<'_>) -> Result<ProcessId>;
|
||||
syscall wait_process(pid: ProcessId, status: &mut ExitCode) -> Result<()>;
|
||||
syscall get_pid() -> ProcessId;
|
||||
|
||||
syscall nanosleep(dur: &Duration);
|
||||
syscall nanosleep(dur: &Duration) -> Result<()>;
|
||||
|
||||
syscall exit_signal(frame: &SignalEntryData) -> !;
|
||||
syscall set_signal_entry(ip: usize, sp: usize);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::generated::ProcessId;
|
||||
use crate::generated::ProcessGroupId;
|
||||
|
||||
use super::terminal;
|
||||
|
||||
@ -19,7 +19,7 @@ pub enum DeviceRequest {
|
||||
/// Return the terminal's size
|
||||
GetTerminalSize(MaybeUninit<terminal::TerminalSize>),
|
||||
/// Sets a foreground process group ID for the terminal
|
||||
SetTerminalGroup(ProcessId),
|
||||
SetTerminalGroup(ProcessGroupId),
|
||||
/// "Acquires" ownership of the device, preventing others from accessing it
|
||||
AcquireDevice,
|
||||
/// "Releases" ownership of the device
|
||||
|
@ -10,7 +10,8 @@ use crate::{
|
||||
mod exit;
|
||||
|
||||
pub use crate::generated::{
|
||||
ExecveOptions, ProcessId, Signal, SignalEntryData, SpawnOptions, ThreadId, ThreadSpawnOptions,
|
||||
ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnOptions, ThreadId,
|
||||
ThreadSpawnOptions,
|
||||
};
|
||||
pub use exit::ExitCode;
|
||||
|
||||
@ -25,7 +26,7 @@ pub enum SpawnOption {
|
||||
child: RawFd,
|
||||
},
|
||||
/// The new process should be placed in the specified group
|
||||
SetProcessGroup(ProcessId),
|
||||
SetProcessGroup(ProcessGroupId),
|
||||
/// Gain terminal control for the given FD
|
||||
GainTerminal(RawFd),
|
||||
}
|
||||
@ -70,3 +71,9 @@ impl fmt::Display for ProcessId {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Process management data types
|
||||
|
||||
pub use abi::process::{
|
||||
ExecveOptions, ExitCode, MutexOperation, ProcessId, ProcessInfoElement, ProgramArgumentInner,
|
||||
Signal, SignalEntryData, SpawnOption, SpawnOptions, ThreadId, ThreadSpawnOptions,
|
||||
ExecveOptions, ExitCode, MutexOperation, ProcessGroupId, ProcessId, ProcessInfoElement,
|
||||
ProgramArgumentInner, Signal, SignalEntryData, SpawnOption, SpawnOptions, ThreadId,
|
||||
ThreadSpawnOptions,
|
||||
};
|
||||
|
@ -18,7 +18,9 @@ mod generated {
|
||||
},
|
||||
mem::MappingSource,
|
||||
net::SocketType,
|
||||
process::{ExecveOptions, ProcessId, Signal, SignalEntryData, SpawnOptions},
|
||||
process::{
|
||||
ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnOptions,
|
||||
},
|
||||
SyscallFunction,
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![cfg_attr(not(unix), feature(yggdrasil_os))]
|
||||
#![cfg_attr(not(unix), feature(yggdrasil_os, rustc_private))]
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@ -7,7 +7,7 @@ use std::{
|
||||
io::{self, stdin, stdout, BufRead, BufReader, Stdin, Write},
|
||||
os::fd::{FromRawFd, IntoRawFd, OwnedFd},
|
||||
path::Path,
|
||||
process::{ExitCode, Stdio},
|
||||
process::{Child, ExitCode, Stdio},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
@ -36,6 +36,22 @@ pub enum Outcome {
|
||||
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 {
|
||||
Interactive(Stdin),
|
||||
File(BufReader<File>),
|
||||
@ -95,7 +111,6 @@ pub fn exec(
|
||||
|
||||
let mut inputs = vec![];
|
||||
let mut outputs = vec![];
|
||||
let mut children = vec![];
|
||||
let mut pipe_fds = vec![];
|
||||
|
||||
inputs.push(Stdio::inherit());
|
||||
@ -118,31 +133,39 @@ pub fn exec(
|
||||
assert_eq!(inputs.len(), outputs.len());
|
||||
assert_eq!(inputs.len(), pipeline.len());
|
||||
|
||||
let mut elements = vec![];
|
||||
let ios = inputs.drain(..).zip(outputs.drain(..));
|
||||
|
||||
for (command, (input, output)) in pipeline.iter().zip(ios) {
|
||||
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);
|
||||
|
||||
for mut child in children.drain(..) {
|
||||
let status = child.wait()?;
|
||||
let status = sys::wait_for_pipeline(interactive, pipeline)?;
|
||||
|
||||
if !status.success() {
|
||||
return Ok(Outcome::from(status));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Outcome::ok())
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
fn run(mut input: Input, vars: &mut HashMap<String, String>) -> io::Result<ExitCode> {
|
||||
let mut line = String::new();
|
||||
|
||||
if input.is_interactive() {
|
||||
sys::init_signal_handler();
|
||||
}
|
||||
|
||||
let code = loop {
|
||||
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,
|
||||
Err(e) => {
|
||||
// TODO stringify the command back
|
||||
eprintln!("<command>: {}", e);
|
||||
eprintln!("{}: {}", line, e);
|
||||
127
|
||||
}
|
||||
};
|
||||
|
@ -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::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
io::{self, stdin},
|
||||
os::fd::OwnedFd,
|
||||
process::{Child, Command, ExitStatus, Stdio},
|
||||
process::{Command, ExitStatus},
|
||||
};
|
||||
|
||||
use crate::Outcome;
|
||||
use crate::{Outcome, Pipeline, SpawnedPipeline};
|
||||
|
||||
pub struct Pipe {
|
||||
pub read: 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,
|
||||
binary: &str,
|
||||
args: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
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);
|
||||
pipeline: Pipeline<'_>,
|
||||
) -> Result<SpawnedPipeline, io::Error> {
|
||||
let group_id = process::create_process_group();
|
||||
|
||||
if interactive {
|
||||
unsafe {
|
||||
command = command.process_group_raw(0);
|
||||
set_terminal_group(group_id)?;
|
||||
}
|
||||
|
||||
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> {
|
||||
@ -43,10 +78,16 @@ pub fn create_pipe() -> Result<Pipe, io::Error> {
|
||||
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 {
|
||||
fn from(value: ExitStatus) -> Self {
|
||||
use std::os::yggdrasil::process::ExitStatusExt;
|
||||
|
||||
if let Some(code) = value.code() {
|
||||
Self::Exited(code)
|
||||
} else if let Some(sig) = value.signal() {
|
||||
|
@ -1,14 +1,14 @@
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{self, stdout, BufReader, Read, Stdout, Write},
|
||||
path::Path,
|
||||
io::{self, stdout, Read, Stdout, Write},
|
||||
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 reader = BufReader::new(File::open(path)?);
|
||||
let mut reader = Input::open_str(path)?;
|
||||
|
||||
loop {
|
||||
let count = reader.read(&mut buf)?;
|
||||
@ -25,11 +25,20 @@ fn main() -> ExitCode {
|
||||
let mut stdout = stdout();
|
||||
let mut exit = ExitCode::SUCCESS;
|
||||
|
||||
for arg in env::args().skip(1) {
|
||||
if let Err(error) = cat_file(&mut stdout, &arg) {
|
||||
eprintln!("{}: {}", arg, error);
|
||||
let args = env::args().skip(1);
|
||||
|
||||
if args.len() == 0 {
|
||||
if let Err(error) = cat_file(&mut stdout, "-") {
|
||||
eprintln!("{}: {}", "<stdin>", error);
|
||||
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
|
||||
|
@ -70,9 +70,9 @@ fn main() -> ExitCode {
|
||||
|
||||
// "Attach" the terminal
|
||||
unsafe {
|
||||
let pid = std::os::yggdrasil::process::id_ext();
|
||||
let group_id = std::os::yggdrasil::process::group_id();
|
||||
stdin()
|
||||
.device_request(&mut DeviceRequest::SetTerminalGroup(pid))
|
||||
.device_request(&mut DeviceRequest::SetTerminalGroup(group_id))
|
||||
.expect("Could not attach the terminal");
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ use std::{
|
||||
os::{
|
||||
fd::{AsRawFd, FromRawFd, RawFd},
|
||||
yggdrasil::{
|
||||
self,
|
||||
io::{
|
||||
device::{DeviceRequest, FdDeviceRequest},
|
||||
poll::PollChannel,
|
||||
@ -262,13 +263,14 @@ impl<'a> Terminal<'a> {
|
||||
poll.add(pty_master_fd)?;
|
||||
|
||||
let pty_slave_fd = pty_slave.as_raw_fd();
|
||||
let group_id = yggdrasil::process::create_process_group();
|
||||
let shell = unsafe {
|
||||
Command::new("/bin/sh")
|
||||
.arg("-l")
|
||||
.stdin(Stdio::from_raw_fd(pty_slave_fd))
|
||||
.stdout(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)
|
||||
.spawn()?
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user