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,
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!()
}

View File

@ -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

View File

@ -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>(

View File

@ -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) }
}

View File

@ -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(),

View File

@ -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);
}

View File

@ -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);

View File

@ -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)
}

View File

@ -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(())
}

View File

@ -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)
}

View File

@ -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);

View File

@ -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

View File

@ -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)
}
}

View File

@ -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,
};

View File

@ -18,7 +18,9 @@ mod generated {
},
mem::MappingSource,
net::SocketType,
process::{ExecveOptions, ProcessId, Signal, SignalEntryData, SpawnOptions},
process::{
ExecveOptions, ProcessGroupId, ProcessId, Signal, SignalEntryData, SpawnOptions,
},
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::{
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
}
};

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::{
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() {

View File

@ -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,12 +25,21 @@ fn main() -> ExitCode {
let mut stdout = stdout();
let mut exit = ExitCode::SUCCESS;
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
}

View File

@ -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");
}

View File

@ -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()?
};