feature: Ctrl+C signal to foreground pgid
This commit is contained in:
parent
349418ed36
commit
a7d89158cb
@ -149,7 +149,11 @@ extern "C" fn __aa64_exc_sync_handler(exc: &mut ExceptionFrame) {
|
||||
|
||||
if sched::is_ready() {
|
||||
let thread = Thread::current();
|
||||
errorln!("Unhandled exception in thread {}, {:?}", thread.id(), thread.owner().map(|e| e.id()));
|
||||
errorln!(
|
||||
"Unhandled exception in thread {}, {:?}",
|
||||
thread.id(),
|
||||
thread.owner().map(|e| e.id())
|
||||
);
|
||||
}
|
||||
|
||||
errorln!(
|
||||
|
@ -86,9 +86,7 @@ impl IntController for Gic {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.scheduler_irq.0 == irq_number {
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
}
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
|
||||
{
|
||||
let table = self.table.lock();
|
||||
@ -100,8 +98,6 @@ impl IntController for Gic {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gicc.clear_irq(irq_number as u32, ic);
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
|
@ -1,10 +1,12 @@
|
||||
//! Teletype (TTY) device facilities
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use crate::proc::wait::{Wait, WAIT_SELECT};
|
||||
use crate::proc::{Process, wait::{Wait, WAIT_SELECT}};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use libsys::error::Errno;
|
||||
use libsys::{
|
||||
termios::{Termios, TermiosIflag, TermiosLflag, TermiosOflag},
|
||||
proc::Pid,
|
||||
signal::Signal,
|
||||
ioctl::IoctlCmd
|
||||
};
|
||||
use core::mem::size_of;
|
||||
@ -16,6 +18,7 @@ struct CharRingInner<const N: usize> {
|
||||
wr: usize,
|
||||
data: [u8; N],
|
||||
flags: u8,
|
||||
fg_pgid: Option<Pid>,
|
||||
}
|
||||
|
||||
/// Ring buffer for TTYs
|
||||
@ -54,6 +57,11 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
*self.ring().config.lock() = src.clone();
|
||||
Ok(size_of::<Termios>())
|
||||
},
|
||||
IoctlCmd::TtySetPgrp => {
|
||||
let src = arg::struct_ref::<u32>(ptr)?;
|
||||
self.ring().inner.lock().fg_pgid = Some(unsafe { Pid::from_raw(*src) });
|
||||
Ok(0)
|
||||
},
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
@ -110,6 +118,19 @@ pub trait TtyDevice<const N: usize>: SerialDevice {
|
||||
}
|
||||
}
|
||||
|
||||
if byte == 0x3 && config.lflag.contains(TermiosLflag::ISIG) {
|
||||
drop(config);
|
||||
let pgid = ring.inner.lock().fg_pgid;
|
||||
if let Some(pgid) = pgid {
|
||||
// TODO send to pgid
|
||||
let proc = Process::get(pgid);
|
||||
if let Some(proc) = proc {
|
||||
proc.set_signal(Signal::Interrupt);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self.ring().putc(byte, false).ok();
|
||||
}
|
||||
|
||||
@ -232,14 +253,15 @@ impl<const N: usize> CharRing<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: IrqSafeSpinLock::new(CharRingInner {
|
||||
fg_pgid: None,
|
||||
rd: 0,
|
||||
wr: 0,
|
||||
data: [0; N],
|
||||
flags: 0,
|
||||
}),
|
||||
config: IrqSafeSpinLock::new(Termios::new()),
|
||||
wait_read: Wait::new(),
|
||||
wait_write: Wait::new(),
|
||||
wait_read: Wait::new("tty_read"),
|
||||
wait_write: Wait::new("tty_write"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! {
|
||||
io.set_file(FileDescriptor::STDIN, stdin).unwrap();
|
||||
io.set_file(FileDescriptor::STDOUT, stdout).unwrap();
|
||||
io.set_file(FileDescriptor::STDERR, stderr).unwrap();
|
||||
io.set_ctty(tty_node);
|
||||
}
|
||||
|
||||
drop(cfg);
|
||||
|
@ -1,12 +1,13 @@
|
||||
//! Process file descriptors and I/O context
|
||||
use alloc::collections::BTreeMap;
|
||||
use libsys::{error::Errno, stat::FileDescriptor};
|
||||
use vfs::{FileRef, Ioctx};
|
||||
use vfs::{FileRef, Ioctx, VnodeRef, VnodeKind};
|
||||
|
||||
/// Process I/O context. Contains file tables, root/cwd info etc.
|
||||
pub struct ProcessIo {
|
||||
ioctx: Option<Ioctx>,
|
||||
files: BTreeMap<u32, FileRef>,
|
||||
ctty: Option<VnodeRef>,
|
||||
}
|
||||
|
||||
impl ProcessIo {
|
||||
@ -21,6 +22,15 @@ impl ProcessIo {
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
pub fn set_ctty(&mut self, node: VnodeRef) {
|
||||
assert_eq!(node.kind(), VnodeKind::Char);
|
||||
self.ctty = Some(node);
|
||||
}
|
||||
|
||||
pub fn ctty(&mut self) -> Option<VnodeRef> {
|
||||
self.ctty.clone()
|
||||
}
|
||||
|
||||
/// Returns [File] struct referred to by file descriptor `idx`
|
||||
pub fn file(&mut self, fd: FileDescriptor) -> Result<FileRef, Errno> {
|
||||
self.files.get(&u32::from(fd)).cloned().ok_or(Errno::InvalidFile)
|
||||
@ -54,6 +64,7 @@ impl ProcessIo {
|
||||
Self {
|
||||
files: BTreeMap::new(),
|
||||
ioctx: None,
|
||||
ctty: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ use libsys::{
|
||||
proc::{ExitCode, Pid},
|
||||
signal::Signal,
|
||||
};
|
||||
use vfs::{VnodeRef, VnodeKind};
|
||||
|
||||
/// Wrapper type for a process struct reference
|
||||
pub type ProcessRef = Rc<Process>;
|
||||
@ -33,6 +34,9 @@ struct ProcessInner {
|
||||
space: Option<&'static mut Space>,
|
||||
state: ProcessState,
|
||||
id: Pid,
|
||||
pgid: Pid,
|
||||
ppid: Option<Pid>,
|
||||
sid: Pid,
|
||||
exit: Option<ExitCode>,
|
||||
threads: Vec<u32>,
|
||||
}
|
||||
@ -56,6 +60,25 @@ impl Process {
|
||||
self.inner.lock().id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sid(&self) -> Pid {
|
||||
self.inner.lock().sid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pgid(&self) -> Pid {
|
||||
self.inner.lock().pgid
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ppid(&self) -> Option<Pid> {
|
||||
self.inner.lock().ppid
|
||||
}
|
||||
|
||||
pub fn set_pgid(&self, pgid: Pid) {
|
||||
self.inner.lock().pgid = pgid;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current() -> ProcessRef {
|
||||
Thread::current().owner().unwrap()
|
||||
@ -75,6 +98,9 @@ impl Process {
|
||||
let mut inner = ProcessInner {
|
||||
threads: Vec::new(),
|
||||
id,
|
||||
pgid: id,
|
||||
ppid: None,
|
||||
sid: id,
|
||||
exit: None,
|
||||
space: None,
|
||||
state: ProcessState::Active,
|
||||
@ -82,7 +108,7 @@ impl Process {
|
||||
inner.threads.push(thread.id());
|
||||
|
||||
let res = Rc::new(Self {
|
||||
exit_wait: Wait::new(),
|
||||
exit_wait: Wait::new("process_exit"),
|
||||
io: IrqSafeSpinLock::new(ProcessIo::new()),
|
||||
signal_state: AtomicU32::new(0),
|
||||
inner: IrqSafeSpinLock::new(inner),
|
||||
@ -107,8 +133,11 @@ impl Process {
|
||||
|
||||
/// Sets a pending signal for a process
|
||||
pub fn set_signal(&self, signal: Signal) {
|
||||
let lock = self.inner.lock();
|
||||
let mut lock = self.inner.lock();
|
||||
let ttbr0 =
|
||||
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
|
||||
let main_thread = Thread::get(lock.threads[0]).unwrap();
|
||||
drop(lock);
|
||||
|
||||
// TODO check that `signal` is not a fault signal
|
||||
// it is illegal to call this function with
|
||||
@ -116,14 +145,15 @@ impl Process {
|
||||
|
||||
match main_thread.state() {
|
||||
ThreadState::Running => {
|
||||
Process::enter_signal_on(lock, main_thread, signal);
|
||||
main_thread.enter_signal(signal, ttbr0);
|
||||
}
|
||||
ThreadState::Waiting => {
|
||||
// TODO abort whatever the process is waiting for
|
||||
todo!()
|
||||
main_thread.clone().setup_signal(signal, ttbr0);
|
||||
main_thread.interrupt_wait(true);
|
||||
}
|
||||
ThreadState::Ready => {
|
||||
todo!()
|
||||
main_thread.clone().setup_signal(signal, ttbr0);
|
||||
main_thread.interrupt_wait(false);
|
||||
}
|
||||
ThreadState::Finished => {
|
||||
// TODO report error back
|
||||
@ -132,20 +162,11 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_signal_on(
|
||||
mut inner: IrqSafeSpinLockGuard<ProcessInner>,
|
||||
thread: ThreadRef,
|
||||
signal: Signal,
|
||||
) {
|
||||
let ttbr0 =
|
||||
inner.space.as_mut().unwrap().address_phys() | ((inner.id.asid() as usize) << 48);
|
||||
drop(inner);
|
||||
thread.enter_signal(signal, ttbr0);
|
||||
}
|
||||
|
||||
pub fn enter_fault_signal(&self, thread: ThreadRef, signal: Signal) {
|
||||
let lock = self.inner.lock();
|
||||
Process::enter_signal_on(lock, thread, signal);
|
||||
let mut lock = self.inner.lock();
|
||||
let ttbr0 =
|
||||
lock.space.as_mut().unwrap().address_phys() | ((lock.id.asid() as usize) << 48);
|
||||
thread.enter_signal(signal, ttbr0);
|
||||
}
|
||||
|
||||
pub fn new_user_thread(&self, entry: usize, stack: usize, arg: usize) -> Result<u32, Errno> {
|
||||
@ -178,7 +199,7 @@ impl Process {
|
||||
threads.push(tid);
|
||||
|
||||
let dst = Rc::new(Self {
|
||||
exit_wait: Wait::new(),
|
||||
exit_wait: Wait::new("process_exit"),
|
||||
io: IrqSafeSpinLock::new(src_io.fork()?),
|
||||
signal_state: AtomicU32::new(0),
|
||||
inner: IrqSafeSpinLock::new(ProcessInner {
|
||||
@ -187,6 +208,9 @@ impl Process {
|
||||
space: Some(dst_space),
|
||||
state: ProcessState::Active,
|
||||
id: dst_id,
|
||||
pgid: src_inner.pgid,
|
||||
ppid: Some(src_inner.id),
|
||||
sid: src_inner.sid
|
||||
}),
|
||||
});
|
||||
|
||||
@ -198,15 +222,11 @@ impl Process {
|
||||
Ok(dst_id)
|
||||
}
|
||||
|
||||
// TODO a way to terminate a single thread?
|
||||
/// Terminates a process.
|
||||
pub fn exit(status: ExitCode) {
|
||||
unsafe {
|
||||
asm!("msr daifclr, #0xF");
|
||||
}
|
||||
pub fn exit(self: ProcessRef, status: ExitCode) {
|
||||
let thread = Thread::current();
|
||||
let process = thread.owner().unwrap();
|
||||
let mut lock = process.inner.lock();
|
||||
let mut lock = self.inner.lock();
|
||||
let is_running = thread.owner_id().map(|e| e == lock.id).unwrap_or(false);
|
||||
|
||||
infoln!("Process {:?} is exiting: {:?}", lock.id, status);
|
||||
assert!(lock.exit.is_none());
|
||||
@ -214,11 +234,9 @@ impl Process {
|
||||
lock.state = ProcessState::Finished;
|
||||
|
||||
for &tid in lock.threads.iter() {
|
||||
debugln!("Dequeue {:?}", tid);
|
||||
Thread::get(tid).unwrap().terminate(status);
|
||||
SCHED.dequeue(tid);
|
||||
}
|
||||
SCHED.debug();
|
||||
|
||||
if let Some(space) = lock.space.take() {
|
||||
unsafe {
|
||||
@ -227,13 +245,16 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
process.io.lock().handle_exit();
|
||||
self.io.lock().handle_exit();
|
||||
|
||||
drop(lock);
|
||||
|
||||
process.exit_wait.wakeup_all();
|
||||
SCHED.switch(true);
|
||||
panic!("This code should never run");
|
||||
self.exit_wait.wakeup_all();
|
||||
|
||||
if is_running {
|
||||
SCHED.switch(true);
|
||||
panic!("This code should never run");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_thread(thread: ThreadRef, status: ExitCode) {
|
||||
@ -246,8 +267,8 @@ impl Process {
|
||||
if lock.threads.len() == 1 {
|
||||
// TODO call Process::exit instead?
|
||||
drop(lock);
|
||||
Process::exit(status);
|
||||
panic!();
|
||||
process.exit(status);
|
||||
return;
|
||||
}
|
||||
|
||||
lock.threads.retain(|&e| e != tid);
|
||||
@ -291,7 +312,6 @@ impl Process {
|
||||
if let Some(r) = proc.collect() {
|
||||
// TODO drop the process struct itself
|
||||
PROCESSES.lock().remove(&proc.id());
|
||||
debugln!("pid {:?} has {} refs", proc.id(), Rc::strong_count(&proc));
|
||||
return Ok(r);
|
||||
}
|
||||
|
||||
@ -327,6 +347,8 @@ impl Process {
|
||||
let r = processes.remove(&old_pid);
|
||||
assert!(r.is_some());
|
||||
process_lock.id = new_pid;
|
||||
process_lock.pgid = new_pid;
|
||||
process_lock.sid = new_pid;
|
||||
let r = processes.insert(new_pid, proc.clone());
|
||||
assert!(r.is_none());
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::arch::aarch64::exception::ExceptionFrame;
|
||||
use crate::proc::{wait::Wait, Process, ProcessRef, SCHED, THREADS};
|
||||
use crate::proc::{wait::{Wait, WaitStatus}, Process, ProcessRef, SCHED, THREADS};
|
||||
use crate::sync::IrqSafeSpinLock;
|
||||
use crate::util::InitOnce;
|
||||
use alloc::rc::Rc;
|
||||
@ -33,7 +33,7 @@ struct ThreadInner {
|
||||
state: State,
|
||||
owner: Option<Pid>,
|
||||
pending_wait: Option<&'static Wait>,
|
||||
wait_flag: bool,
|
||||
wait_status: WaitStatus,
|
||||
signal_entry: usize,
|
||||
signal_stack: usize,
|
||||
}
|
||||
@ -63,6 +63,10 @@ impl Thread {
|
||||
self.inner.lock().owner.and_then(Process::get)
|
||||
}
|
||||
|
||||
pub fn owner_id(&self) -> Option<Pid> {
|
||||
self.inner.lock().owner
|
||||
}
|
||||
|
||||
/// Creates a new kernel process
|
||||
pub fn new_kernel(
|
||||
owner: Option<Pid>,
|
||||
@ -75,7 +79,7 @@ impl Thread {
|
||||
ctx: UnsafeCell::new(Context::kernel(entry as usize, arg)),
|
||||
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||
signal_pending: AtomicU32::new(0),
|
||||
exit_wait: Wait::new(),
|
||||
exit_wait: Wait::new("thread_exit"),
|
||||
exit_status: InitOnce::new(),
|
||||
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||
signal_entry: 0,
|
||||
@ -83,7 +87,7 @@ impl Thread {
|
||||
id,
|
||||
owner,
|
||||
pending_wait: None,
|
||||
wait_flag: false,
|
||||
wait_status: WaitStatus::Done,
|
||||
state: State::Ready,
|
||||
}),
|
||||
});
|
||||
@ -106,7 +110,7 @@ impl Thread {
|
||||
ctx: UnsafeCell::new(Context::user(entry, arg, ttbr0, stack)),
|
||||
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||
signal_pending: AtomicU32::new(0),
|
||||
exit_wait: Wait::new(),
|
||||
exit_wait: Wait::new("thread_exit"),
|
||||
exit_status: InitOnce::new(),
|
||||
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||
signal_entry: 0,
|
||||
@ -114,7 +118,7 @@ impl Thread {
|
||||
id,
|
||||
owner: Some(owner),
|
||||
pending_wait: None,
|
||||
wait_flag: false,
|
||||
wait_status: WaitStatus::Done,
|
||||
state: State::Ready,
|
||||
}),
|
||||
});
|
||||
@ -134,7 +138,7 @@ impl Thread {
|
||||
ctx: UnsafeCell::new(Context::fork(frame, ttbr0)),
|
||||
signal_ctx: UnsafeCell::new(Context::empty()),
|
||||
signal_pending: AtomicU32::new(0),
|
||||
exit_wait: Wait::new(),
|
||||
exit_wait: Wait::new("thread_exit"),
|
||||
exit_status: InitOnce::new(),
|
||||
inner: IrqSafeSpinLock::new(ThreadInner {
|
||||
signal_entry: 0,
|
||||
@ -142,7 +146,7 @@ impl Thread {
|
||||
id,
|
||||
owner,
|
||||
pending_wait: None,
|
||||
wait_flag: false,
|
||||
wait_status: WaitStatus::Done,
|
||||
state: State::Ready,
|
||||
}),
|
||||
});
|
||||
@ -185,7 +189,7 @@ impl Thread {
|
||||
assert_eq!(src_lock.state, State::Running);
|
||||
src_lock.state = State::Ready;
|
||||
}
|
||||
assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
|
||||
// assert!(dst_lock.state == State::Ready || dst_lock.state == State::Waiting);
|
||||
dst_lock.state = State::Running;
|
||||
}
|
||||
|
||||
@ -223,7 +227,7 @@ impl Thread {
|
||||
let mut lock = self.inner.lock();
|
||||
// FIXME this is not cool
|
||||
lock.pending_wait = Some(unsafe { &*wait });
|
||||
lock.wait_flag = true;
|
||||
lock.wait_status = WaitStatus::Pending;
|
||||
}
|
||||
|
||||
pub fn waittid(tid: u32) -> Result<(), Errno> {
|
||||
@ -243,19 +247,19 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_wait_reached(&self) {
|
||||
pub fn set_wait_status(&self, status: WaitStatus) {
|
||||
let mut lock = self.inner.lock();
|
||||
lock.wait_flag = false;
|
||||
lock.wait_status = status;
|
||||
}
|
||||
|
||||
pub fn reset_wait(&self) {
|
||||
let mut lock = self.inner.lock();
|
||||
lock.pending_wait = None;
|
||||
lock.wait_status = WaitStatus::Done;
|
||||
}
|
||||
|
||||
/// Returns `true` if process wait condition has not been reached
|
||||
pub fn wait_flag(&self) -> bool {
|
||||
self.inner.lock().wait_flag
|
||||
pub fn wait_status(&self) -> WaitStatus {
|
||||
self.inner.lock().wait_status
|
||||
}
|
||||
|
||||
/// Switches current thread back from signal handler
|
||||
@ -291,8 +295,7 @@ impl Thread {
|
||||
lock.signal_stack = stack;
|
||||
}
|
||||
|
||||
/// Switches process main thread to a signal handler
|
||||
pub fn enter_signal(self: ThreadRef, signal: Signal, ttbr0: usize) {
|
||||
pub fn setup_signal(self: ThreadRef, signal: Signal, ttbr0: usize) {
|
||||
if self
|
||||
.signal_pending
|
||||
.compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed)
|
||||
@ -305,7 +308,7 @@ impl Thread {
|
||||
if lock.signal_entry == 0 || lock.signal_stack == 0 {
|
||||
drop(lock);
|
||||
Process::exit_thread(self, ExitCode::from(-1));
|
||||
panic!();
|
||||
return;
|
||||
}
|
||||
|
||||
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
|
||||
@ -318,7 +321,6 @@ impl Thread {
|
||||
lock.signal_stack,
|
||||
ttbr0
|
||||
);
|
||||
assert_eq!(lock.state, State::Running);
|
||||
|
||||
unsafe {
|
||||
signal_ctx.setup_signal_entry(
|
||||
@ -329,13 +331,31 @@ impl Thread {
|
||||
);
|
||||
}
|
||||
|
||||
drop(lock);
|
||||
}
|
||||
|
||||
/// Switches process main thread to a signal handler
|
||||
pub fn enter_signal(self: ThreadRef, signal: Signal, ttbr0: usize) {
|
||||
let src_ctx = self.ctx.get();
|
||||
let signal_ctx = unsafe { &mut *self.signal_ctx.get() };
|
||||
|
||||
assert_eq!(self.state(), State::Running);
|
||||
self.setup_signal(signal, ttbr0);
|
||||
|
||||
unsafe {
|
||||
(&mut *src_ctx).switch(signal_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_wait(&self, enqueue: bool) {
|
||||
let mut lock = self.inner.lock();
|
||||
let tid = lock.id;
|
||||
let wait = lock.pending_wait.take();
|
||||
drop(lock);
|
||||
if let Some(wait) = wait {
|
||||
wait.abort(tid, enqueue);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn terminate(&self, status: ExitCode) {
|
||||
let mut lock = self.inner.lock();
|
||||
lock.state = State::Finished;
|
||||
@ -343,7 +363,7 @@ impl Thread {
|
||||
let wait = lock.pending_wait.take();
|
||||
drop(lock);
|
||||
if let Some(wait) = wait {
|
||||
wait.abort(tid);
|
||||
wait.abort(tid, false);
|
||||
}
|
||||
self.exit_status.init(status);
|
||||
self.exit_wait.wakeup_all();
|
||||
|
@ -12,6 +12,14 @@ use libsys::{error::Errno, stat::FdSet};
|
||||
/// waiting for some event to happen.
|
||||
pub struct Wait {
|
||||
queue: IrqSafeSpinLock<LinkedList<u32>>,
|
||||
name: &'static str
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum WaitStatus {
|
||||
Pending,
|
||||
Interrupted,
|
||||
Done,
|
||||
}
|
||||
|
||||
struct Timeout {
|
||||
@ -20,7 +28,7 @@ struct Timeout {
|
||||
}
|
||||
|
||||
static TICK_LIST: IrqSafeSpinLock<LinkedList<Timeout>> = IrqSafeSpinLock::new(LinkedList::new());
|
||||
pub static WAIT_SELECT: Wait = Wait::new();
|
||||
pub static WAIT_SELECT: Wait = Wait::new("select");
|
||||
|
||||
/// Checks for any timed out wait channels and interrupts them
|
||||
pub fn tick() {
|
||||
@ -42,7 +50,7 @@ pub fn tick() {
|
||||
/// Suspends current process for given duration
|
||||
pub fn sleep(timeout: Duration, remaining: &mut Duration) -> Result<(), Errno> {
|
||||
// Dummy wait descriptor which will never receive notifications
|
||||
static SLEEP_NOTIFY: Wait = Wait::new();
|
||||
static SLEEP_NOTIFY: Wait = Wait::new("sleep");
|
||||
let deadline = machine::local_timer().timestamp()? + timeout;
|
||||
match SLEEP_NOTIFY.wait(Some(deadline)) {
|
||||
Err(Errno::Interrupt) => {
|
||||
@ -104,9 +112,39 @@ pub fn select(
|
||||
|
||||
impl Wait {
|
||||
/// Constructs a new wait channel
|
||||
pub const fn new() -> Self {
|
||||
pub const fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
queue: IrqSafeSpinLock::new(LinkedList::new()),
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abort(&self, tid: u32, enqueue: bool) {
|
||||
let mut queue = self.queue.lock();
|
||||
let mut tick_lock = TICK_LIST.lock();
|
||||
let mut cursor = tick_lock.cursor_front_mut();
|
||||
while let Some(item) = cursor.current() {
|
||||
if tid == item.tid {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
|
||||
let mut cursor = queue.cursor_front_mut();
|
||||
while let Some(item) = cursor.current() {
|
||||
if tid == *item {
|
||||
cursor.remove_current();
|
||||
let thread = Thread::get(tid).unwrap();
|
||||
thread.set_wait_status(WaitStatus::Interrupted);
|
||||
if enqueue {
|
||||
SCHED.enqueue(tid);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +167,7 @@ impl Wait {
|
||||
}
|
||||
drop(tick_lock);
|
||||
|
||||
Thread::get(tid).unwrap().set_wait_reached();
|
||||
Thread::get(tid).unwrap().set_wait_status(WaitStatus::Done);
|
||||
SCHED.enqueue(tid);
|
||||
}
|
||||
|
||||
@ -149,30 +187,6 @@ impl Wait {
|
||||
self.wakeup_some(1);
|
||||
}
|
||||
|
||||
pub fn abort(&self, tid: u32) {
|
||||
let mut queue = self.queue.lock();
|
||||
let mut tick_lock = TICK_LIST.lock();
|
||||
let mut cursor = tick_lock.cursor_front_mut();
|
||||
while let Some(item) = cursor.current() {
|
||||
if tid == item.tid {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
|
||||
let mut cursor = queue.cursor_front_mut();
|
||||
while let Some(item) = cursor.current() {
|
||||
if tid == *item {
|
||||
cursor.remove_current();
|
||||
break;
|
||||
} else {
|
||||
cursor.move_next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suspends current process until event is signalled or
|
||||
/// (optional) deadline is reached
|
||||
pub fn wait(&self, deadline: Option<Duration>) -> Result<(), Errno> {
|
||||
@ -191,9 +205,15 @@ impl Wait {
|
||||
}
|
||||
|
||||
loop {
|
||||
if !thread.wait_flag() {
|
||||
return Ok(());
|
||||
}
|
||||
match thread.wait_status() {
|
||||
WaitStatus::Pending => {}
|
||||
WaitStatus::Done => {
|
||||
return Ok(());
|
||||
}
|
||||
WaitStatus::Interrupted => {
|
||||
return Err(Errno::Interrupt);
|
||||
}
|
||||
};
|
||||
|
||||
drop(queue_lock);
|
||||
thread.enter_wait();
|
||||
|
@ -52,6 +52,7 @@ fn find_at_node<T: DerefMut<Target = ProcessIo>>(
|
||||
|
||||
/// Main system call dispatcher function
|
||||
pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
// debugln!("syscall {:?}", num);
|
||||
match num {
|
||||
// I/O
|
||||
SystemCall::Read => {
|
||||
@ -172,7 +173,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
if flags & (1 << 0) != 0 {
|
||||
Process::exit_thread(Thread::current(), status);
|
||||
} else {
|
||||
Process::exit(status);
|
||||
Process::current().exit(status);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
@ -187,7 +188,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
*status = i32::from(exit);
|
||||
Ok(0)
|
||||
}
|
||||
_ => todo!(),
|
||||
e => e.map(|e| i32::from(e) as usize),
|
||||
}
|
||||
},
|
||||
SystemCall::WaitTid => {
|
||||
@ -200,7 +201,7 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
_ => todo!(),
|
||||
}
|
||||
},
|
||||
SystemCall::GetPid => todo!(),
|
||||
SystemCall::GetPid => Ok(Process::current().id().value() as usize),
|
||||
SystemCall::GetTid => Ok(Thread::current().id() as usize),
|
||||
SystemCall::Sleep => {
|
||||
let rem_buf = arg::option_buf_ref(args[1], size_of::<u64>() * 2)?;
|
||||
@ -239,6 +240,68 @@ pub fn syscall(num: SystemCall, args: &[usize]) -> Result<usize, Errno> {
|
||||
proc::switch();
|
||||
Ok(0)
|
||||
},
|
||||
SystemCall::GetSid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
let pid = unsafe { Pid::from_raw(pid) };
|
||||
let proc = Process::get(pid).ok_or(Errno::DoesNotExist)?;
|
||||
if proc.sid() != current.sid() {
|
||||
return Err(Errno::PermissionDenied)
|
||||
}
|
||||
proc
|
||||
};
|
||||
|
||||
Ok(proc.sid().value() as usize)
|
||||
},
|
||||
SystemCall::GetPgid => {
|
||||
// TODO handle kernel processes here?
|
||||
let pid = args[0] as u32;
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
let pid = unsafe { Pid::from_raw(pid) };
|
||||
Process::get(pid).ok_or(Errno::DoesNotExist)?
|
||||
};
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
},
|
||||
SystemCall::GetPpid => {
|
||||
Ok(Process::current().ppid().unwrap().value() as usize)
|
||||
},
|
||||
SystemCall::SetSid => {
|
||||
let proc = Process::current();
|
||||
let mut io = proc.io.lock();
|
||||
|
||||
if let Some(ctty) = io.ctty() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
todo!();
|
||||
},
|
||||
SystemCall::SetPgid => {
|
||||
let pid = args[0] as u32;
|
||||
let pgid = args[1] as u32;
|
||||
|
||||
let current = Process::current();
|
||||
let proc = if pid == 0 {
|
||||
current
|
||||
} else {
|
||||
todo!()
|
||||
};
|
||||
|
||||
if pgid == 0 {
|
||||
proc.set_pgid(proc.id());
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
|
||||
Ok(proc.pgid().value() as usize)
|
||||
},
|
||||
|
||||
// System
|
||||
SystemCall::GetCpuTime => {
|
||||
|
@ -26,6 +26,11 @@ pub enum SystemCall {
|
||||
SignalReturn = 42,
|
||||
SendSignal = 43,
|
||||
Yield = 44,
|
||||
GetSid = 45,
|
||||
GetPgid = 46,
|
||||
GetPpid = 47,
|
||||
SetSid = 48,
|
||||
SetPgid = 49,
|
||||
// System
|
||||
GetCpuTime = 64,
|
||||
// Debugging
|
||||
|
@ -251,14 +251,15 @@ pub fn sys_ioctl(
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_getcputime() -> Result<Duration, Errno> {
|
||||
Errno::from_syscall(unsafe {
|
||||
syscall!(SystemCall::GetCpuTime)
|
||||
}).map(|e| Duration::from_nanos(e as u64))
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::GetCpuTime) })
|
||||
.map(|e| Duration::from_nanos(e as u64))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_signal(entry: usize, stack: usize) -> Result<(), Errno> {
|
||||
Errno::from_syscall_unit(unsafe { syscall!(SystemCall::SetSignalEntry, argn!(entry), argn!(stack)) })
|
||||
Errno::from_syscall_unit(unsafe {
|
||||
syscall!(SystemCall::SetSignalEntry, argn!(entry), argn!(stack))
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@ -349,7 +350,22 @@ pub fn sys_faccessat(
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_ex_gettid() -> u32 {
|
||||
unsafe {
|
||||
syscall!(SystemCall::GetTid) as u32
|
||||
}
|
||||
unsafe { syscall!(SystemCall::GetTid) as u32 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_getpid() -> Pid {
|
||||
unsafe { Pid::from_raw(syscall!(SystemCall::GetPid) as u32) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_getpgid(pid: Pid) -> Result<Pid, Errno> {
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::GetPgid, argn!(pid.value())) })
|
||||
.map(|e| unsafe { Pid::from_raw(e as u32) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn sys_setpgid(pid: Pid, pgid: Pid) -> Result<Pid, Errno> {
|
||||
Errno::from_syscall(unsafe { syscall!(SystemCall::SetPgid, argn!(pid.value()), argn!(pgid.value())) }).map(|e| unsafe { Pid::from_raw(e as u32) })
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use crate::error::Errno;
|
||||
pub enum IoctlCmd {
|
||||
TtySetAttributes = 1,
|
||||
TtyGetAttributes = 2,
|
||||
TtySetPgrp = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for IoctlCmd {
|
||||
@ -17,6 +18,7 @@ impl TryFrom<u32> for IoctlCmd {
|
||||
match u {
|
||||
1 => Ok(Self::TtySetAttributes),
|
||||
2 => Ok(Self::TtyGetAttributes),
|
||||
3 => Ok(Self::TtySetPgrp),
|
||||
_ => Err(Errno::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
use libsys::{
|
||||
calls::sys_fstatat,
|
||||
calls::{sys_fstatat, sys_ioctl},
|
||||
stat::{FileDescriptor, Stat},
|
||||
ioctl::IoctlCmd,
|
||||
error::Errno,
|
||||
proc::Pid
|
||||
};
|
||||
use core::mem::size_of;
|
||||
use core::fmt;
|
||||
|
||||
mod error;
|
||||
@ -24,6 +28,14 @@ pub trait AsRawFd {
|
||||
fn as_raw_fd(&self) -> FileDescriptor;
|
||||
}
|
||||
|
||||
pub fn tcgetpgrp(fd: FileDescriptor) -> Result<Pid, Errno> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn tcsetpgrp(fd: FileDescriptor, pgid: Pid) -> Result<(), Errno> {
|
||||
sys_ioctl(fd, IoctlCmd::TtySetPgrp, &pgid as *const _ as usize, size_of::<Pid>()).map(|_| ())
|
||||
}
|
||||
|
||||
pub fn stat(pathname: &str) -> Result<Stat, Error> {
|
||||
let mut buf = Stat::default();
|
||||
// TODO error handling
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub use libsys::signal::{Signal, SignalDestination};
|
||||
pub use libsys::proc::ExitCode;
|
||||
pub use libsys::proc::{self, ExitCode};
|
||||
pub use libsys::termios;
|
||||
pub use libsys::abi;
|
||||
pub use libsys::calls::*;
|
||||
|
@ -125,7 +125,7 @@ fn main() -> i32 {
|
||||
|
||||
// Test non-utf8 input fed into syscalls expecting strings
|
||||
// let old_signal = signal::set_handler(Signal::InvalidSystemCall, SignalHandler::Ignore);
|
||||
for _ in 0..100 {
|
||||
for _ in 0..10000 {
|
||||
random_bytes(&mut buf);
|
||||
let mut stat = Stat::default();
|
||||
|
||||
|
@ -6,19 +6,29 @@ extern crate libusr;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::borrow::ToOwned;
|
||||
use libusr::sys::{sys_exit, sys_execve, sys_waitpid, sys_fork, ExitCode, Errno};
|
||||
use libusr::io::{self, Read};
|
||||
use libusr::signal::{self, SignalHandler};
|
||||
use libusr::sys::{
|
||||
proc::Pid, sys_execve, sys_setpgid, sys_exit, sys_fork, sys_getpgid, sys_waitpid, Errno, ExitCode,
|
||||
FileDescriptor, Signal,
|
||||
};
|
||||
|
||||
fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result<Option<&'a str>, io::Error> {
|
||||
let size = f.read(bytes)?;
|
||||
Ok(if size == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(core::str::from_utf8(&bytes[..size]).unwrap().trim_end_matches('\n'))
|
||||
Some(
|
||||
core::str::from_utf8(&bytes[..size])
|
||||
.unwrap()
|
||||
.trim_end_matches('\n'),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn execvp(cmd: &str) -> ! {
|
||||
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
|
||||
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
|
||||
sys_execve(&("/bin/".to_owned() + cmd)).unwrap();
|
||||
sys_exit(ExitCode::from(-1));
|
||||
}
|
||||
@ -30,6 +40,8 @@ fn execute(line: &str) -> Result<ExitCode, Errno> {
|
||||
if let Some(pid) = unsafe { sys_fork()? } {
|
||||
let mut status = 0;
|
||||
sys_waitpid(pid, &mut status)?;
|
||||
let pgid = sys_getpgid(unsafe { Pid::from_raw(0) }).unwrap();
|
||||
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
|
||||
Ok(ExitCode::from(status))
|
||||
} else {
|
||||
execvp(cmd);
|
||||
@ -41,18 +53,30 @@ fn main() -> i32 {
|
||||
let mut buf = [0; 256];
|
||||
let mut stdin = io::stdin();
|
||||
|
||||
signal::set_handler(Signal::Interrupt, SignalHandler::Ignore);
|
||||
let pgid = sys_setpgid(unsafe { Pid::from_raw(0) }, unsafe { Pid::from_raw(0) }).unwrap();
|
||||
io::tcsetpgrp(FileDescriptor::STDIN, pgid).unwrap();
|
||||
|
||||
loop {
|
||||
print!("> ");
|
||||
let line = readline(&mut stdin, &mut buf).unwrap();
|
||||
if line.is_none() {
|
||||
break;
|
||||
}
|
||||
let line = line.unwrap().trim_start_matches(' ');
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
match readline(&mut stdin, &mut buf) {
|
||||
Ok(line) => {
|
||||
if line.is_none() {
|
||||
break;
|
||||
}
|
||||
let line = line.unwrap().trim_start_matches(' ');
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
execute(line).ok();
|
||||
execute(line).ok();
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Interrupt!");
|
||||
continue;
|
||||
},
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user