From d18bf07f3e9f7ed1d79636776540aae56d9624b2 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Thu, 13 Oct 2016 21:00:51 -0600 Subject: [PATCH] Add sys scheme to allow inspection of processes. WIP: Signals. --- Makefile | 3 +- kernel/context/context.rs | 3 + kernel/scheme/mod.rs | 5 + kernel/scheme/sys/context.rs | 90 ++++++++++++++++ kernel/scheme/sys/mod.rs | 162 ++++++++++++++++++++++++++++ kernel/syscall/process.rs | 201 +++++++++++++++++++++++++---------- syscall/src/flag.rs | 40 ++++++- syscall/src/lib.rs | 4 + syscall/src/number.rs | 1 + 9 files changed, 445 insertions(+), 64 deletions(-) create mode 100644 kernel/scheme/sys/context.rs create mode 100644 kernel/scheme/sys/mod.rs diff --git a/Makefile b/Makefile index d4c9269..16567a6 100644 --- a/Makefile +++ b/Makefile @@ -267,6 +267,7 @@ coreutils: \ filesystem/bin/mkdir \ filesystem/bin/mv \ filesystem/bin/printenv \ + filesystem/bin/ps \ filesystem/bin/pwd \ filesystem/bin/realpath \ filesystem/bin/reset \ @@ -280,7 +281,7 @@ coreutils: \ filesystem/bin/true \ filesystem/bin/wc \ filesystem/bin/yes - #filesystem/bin/free filesystem/bin/ps filesystem/bin/shutdown filesystem/bin/test + #filesystem/bin/free filesystem/bin/shutdown filesystem/bin/test extrautils: \ filesystem/bin/calc \ diff --git a/kernel/context/context.rs b/kernel/context/context.rs index 1093f17..fd51e96 100644 --- a/kernel/context/context.rs +++ b/kernel/context/context.rs @@ -55,6 +55,8 @@ pub struct Context { pub stack: Option, /// User grants pub grants: Arc>>, + /// The name of the context + pub name: Arc>>, /// The current working directory pub cwd: Arc>>, /// Kernel events @@ -87,6 +89,7 @@ impl Context { heap: None, stack: None, grants: Arc::new(Mutex::new(Vec::new())), + name: Arc::new(Mutex::new(Vec::new())), cwd: Arc::new(Mutex::new(Vec::new())), events: Arc::new(WaitQueue::new()), env: Arc::new(Mutex::new(BTreeMap::new())), diff --git a/kernel/scheme/mod.rs b/kernel/scheme/mod.rs index 1d61491..b6b2c35 100644 --- a/kernel/scheme/mod.rs +++ b/kernel/scheme/mod.rs @@ -22,6 +22,7 @@ use self::initfs::InitFsScheme; use self::irq::{IRQ_SCHEME_ID, IrqScheme}; use self::pipe::{PIPE_SCHEME_ID, PipeScheme}; use self::root::{ROOT_SCHEME_ID, RootScheme}; +use self::sys::SysScheme; /// Debug scheme pub mod debug; @@ -44,6 +45,9 @@ pub mod pipe; /// Root scheme pub mod root; +/// System information +pub mod sys; + /// Userspace schemes pub mod user; @@ -121,6 +125,7 @@ fn init_schemes() -> RwLock { list.insert(Box::new(*b"initfs"), Arc::new(Box::new(InitFsScheme::new()))).expect("failed to insert initfs scheme"); IRQ_SCHEME_ID.store(list.insert(Box::new(*b"irq"), Arc::new(Box::new(IrqScheme))).expect("failed to insert irq scheme"), Ordering::SeqCst); PIPE_SCHEME_ID.store(list.insert(Box::new(*b"pipe"), Arc::new(Box::new(PipeScheme))).expect("failed to insert pipe scheme"), Ordering::SeqCst); + list.insert(Box::new(*b"sys"), Arc::new(Box::new(SysScheme::new()))).expect("failed to insert sys scheme"); RwLock::new(list) } diff --git a/kernel/scheme/sys/context.rs b/kernel/scheme/sys/context.rs new file mode 100644 index 0000000..8c99a16 --- /dev/null +++ b/kernel/scheme/sys/context.rs @@ -0,0 +1,90 @@ +use collections::{String, Vec}; +use core::str; + +use context; +use syscall::error::Result; + +pub fn resource() -> Result> { + let mut string = format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", + "PID", + "PPID", + "UID", + "GID", + "STAT", + "MEM", + "NAME"); + { + let contexts = context::contexts(); + for (_id, context_lock) in contexts.iter() { + let context = context_lock.read(); + + let mut memory = 0; + if let Some(ref kfx) = context.kstack { + memory += kfx.len(); + } + if let Some(ref kstack) = context.kstack { + memory += kstack.len(); + } + for shared_mem in context.image.iter() { + shared_mem.with(|mem| { + memory += mem.size(); + }); + } + if let Some(ref heap) = context.heap { + heap.with(|heap| { + memory += heap.size(); + }); + } + if let Some(ref stack) = context.stack { + memory += stack.size(); + } + + let memory_string = if memory >= 1024 * 1024 * 1024 { + format!("{} GB", memory / 1024 / 1024 / 1024) + } else if memory >= 1024 * 1024 { + format!("{} MB", memory / 1024 / 1024) + } else if memory >= 1024 { + format!("{} KB", memory / 1024) + } else { + format!("{} B", memory) + }; + + let mut stat_string = String::new(); + if context.stack.is_some() { + stat_string.push('U'); + } else { + stat_string.push('K'); + } + match context.status { + context::Status::Runnable => { + stat_string.push('R'); + }, + context::Status::Blocked => if context.wake.is_some() { + stat_string.push('S'); + } else { + stat_string.push('B'); + }, + context::Status::Exited(_status) => { + stat_string.push('Z'); + } + } + if context.running { + stat_string.push('+'); + } + + let name_bytes = context.name.lock(); + let name = str::from_utf8(&name_bytes).unwrap_or(""); + + string.push_str(&format!("{:<6}{:<6}{:<6}{:<6}{:<6}{:<8}{}\n", + context.id, + context.ppid, + context.euid, + context.egid, + stat_string, + memory_string, + name)); + } + } + + Ok(string.into_bytes()) +} diff --git a/kernel/scheme/sys/mod.rs b/kernel/scheme/sys/mod.rs new file mode 100644 index 0000000..28b60b5 --- /dev/null +++ b/kernel/scheme/sys/mod.rs @@ -0,0 +1,162 @@ +use alloc::boxed::Box; +use collections::{BTreeMap, Vec}; +use core::{cmp, str}; +use core::sync::atomic::{AtomicUsize, Ordering}; +use spin::RwLock; + +use syscall::data::Stat; +use syscall::error::{Error, EBADF, EINVAL, ENOENT, Result}; +use syscall::flag::{MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::scheme::Scheme; + +mod context; +//mod interrupt; +//mod log; +//mod memory; +//mod test; + +struct Handle { + path: &'static [u8], + data: Vec, + mode: u16, + seek: usize +} + +type SysFn = Fn() -> Result> + Send + Sync; + +/// System information scheme +pub struct SysScheme { + next_id: AtomicUsize, + files: BTreeMap<&'static [u8], Box>, + handles: RwLock> +} + +impl SysScheme { + pub fn new() -> SysScheme { + let mut files: BTreeMap<&'static [u8], Box> = BTreeMap::new(); + + files.insert(b"context", Box::new(move || context::resource())); + //files.insert(b"interrupt", Box::new(move || interrupt::resource())); + //files.insert(b"log", Box::new(move || log::resource())); + //files.insert(b"memory", Box::new(move || memory::resource())); + //files.insert(b"test", Box::new(move || test::resource())); + + SysScheme { + next_id: AtomicUsize::new(0), + files: files, + handles: RwLock::new(BTreeMap::new()) + } + } +} + +impl Scheme for SysScheme { + fn open(&self, path: &[u8], _flags: usize, _uid: u32, _gid: u32) -> Result { + let path_utf8 = str::from_utf8(path).map_err(|_err| Error::new(ENOENT))?; + let path_trimmed = path_utf8.trim_matches('/'); + + //Have to iterate to get the path without allocation + for entry in self.files.iter() { + if entry.0 == &path_trimmed.as_bytes() { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.write().insert(id, Handle { + path: entry.0, + data: entry.1()?, + mode: MODE_FILE | 0o444, + seek: 0 + }); + + return Ok(id) + } + } + + Err(Error::new(ENOENT)) + } + + fn dup(&self, id: usize) -> Result { + let (path, data, mode, seek) = { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + (handle.path, handle.data.clone(), handle.mode, handle.seek) + }; + + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.write().insert(id, Handle { + path: path, + data: data, + mode: mode, + seek: seek + }); + + Ok(id) + } + + fn read(&self, id: usize, buffer: &mut [u8]) -> Result { + let mut handles = self.handles.write(); + let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let mut i = 0; + while i < buffer.len() && handle.seek < handle.data.len() { + buffer[i] = handle.data[handle.seek]; + i += 1; + handle.seek += 1; + } + + Ok(i) + } + + fn seek(&self, id: usize, pos: usize, whence: usize) -> Result { + let mut handles = self.handles.write(); + let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + handle.seek = match whence { + SEEK_SET => cmp::min(handle.data.len(), pos), + SEEK_CUR => cmp::max(0, cmp::min(handle.data.len() as isize, handle.seek as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(handle.data.len() as isize, handle.data.len() as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(handle.seek) + } + + fn fpath(&self, id: usize, buf: &mut [u8]) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + //TODO: Copy scheme part in kernel + let mut i = 0; + let scheme_path = b"initfs:"; + while i < buf.len() && i < scheme_path.len() { + buf[i] = scheme_path[i]; + i += 1; + } + + let mut j = 0; + while i < buf.len() && j < handle.path.len() { + buf[i] = handle.path[j]; + i += 1; + j += 1; + } + + Ok(i) + } + + fn fstat(&self, id: usize, stat: &mut Stat) -> Result { + let handles = self.handles.read(); + let handle = handles.get(&id).ok_or(Error::new(EBADF))?; + + stat.st_mode = handle.mode; + stat.st_uid = 0; + stat.st_gid = 0; + stat.st_size = handle.data.len() as u64; + + Ok(0) + } + + fn fsync(&self, _id: usize) -> Result { + Ok(0) + } + + fn close(&self, id: usize) -> Result { + self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0)) + } +} diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index 765edb5..721211c 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -4,7 +4,7 @@ use alloc::boxed::Box; use collections::{BTreeMap, Vec}; use core::{mem, str}; use core::ops::DerefMut; -use spin::Mutex; +use spin::{Mutex, RwLock}; use arch; use arch::externs::memcpy; @@ -71,6 +71,7 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let mut heap_option = None; let mut stack_option = None; let grants; + let name; let cwd; let env; let files; @@ -186,6 +187,12 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { grants = Arc::new(Mutex::new(Vec::new())); } + if flags & CLONE_VM == CLONE_VM { + name = context.name.clone(); + } else { + name = Arc::new(Mutex::new(context.name.lock().clone())); + } + if flags & CLONE_FS == CLONE_FS { cwd = context.cwd.clone(); } else { @@ -382,6 +389,8 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { context.stack = Some(stack); } + context.name = name; + context.cwd = cwd; context.env = env; @@ -395,61 +404,6 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { Ok(pid) } -pub fn exit(status: usize) -> ! { - { - let mut close_files = Vec::new(); - { - let contexts = context::contexts(); - let (vfork, ppid) = { - let context_lock = contexts.current().expect("tried to exit without context"); - let mut context = context_lock.write(); - context.image.clear(); - drop(context.heap.take()); - drop(context.stack.take()); - context.grants = Arc::new(Mutex::new(Vec::new())); - if Arc::strong_count(&context.files) == 1 { - mem::swap(context.files.lock().deref_mut(), &mut close_files); - } - context.files = Arc::new(Mutex::new(Vec::new())); - context.status = context::Status::Exited(status); - - let vfork = context.vfork; - context.vfork = false; - context.waitpid.notify(); - (vfork, context.ppid) - }; - if vfork { - if let Some(context_lock) = contexts.get(ppid) { - let mut context = context_lock.write(); - if ! context.unblock() { - println!("{} not blocked for exit vfork unblock", ppid); - } - } else { - println!("{} not found for exit vfork unblock", ppid); - } - } - } - - for (fd, file_option) in close_files.drain(..).enumerate() { - if let Some(file) = file_option { - context::event::unregister(fd, file.scheme, file.number); - - let scheme_option = { - let schemes = scheme::schemes(); - schemes.get(file.scheme).map(|scheme| scheme.clone()) - }; - if let Some(scheme) = scheme_option { - let _ = scheme.close(file.number); - } - } - } - } - - unsafe { context::switch(); } - - unreachable!(); -} - pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { let entry; let mut sp = arch::USER_STACK_OFFSET + arch::USER_STACK_SIZE - 256; @@ -461,14 +415,14 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { args.push(arg.to_vec()); // Must be moved into kernel space before exec unmaps all memory } - let (uid, gid) = { + let (uid, gid, canonical) = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.euid, context.egid) + (context.euid, context.egid, context.canonicalize(path)) }; - let file = syscall::open(path, 0)?; + let file = syscall::open(&canonical, 0)?; let mut stat = Stat::default(); syscall::file_op_mut_slice(syscall::number::SYS_FSTAT, file, &mut stat)?; @@ -505,6 +459,9 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let mut context = context_lock.write(); + // Set name + context.name = Arc::new(Mutex::new(canonical)); + // Unmap previous image and stack context.image.clear(); drop(context.heap.take()); @@ -639,6 +596,69 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { unsafe { usermode(entry, sp); } } +fn terminate(context_lock: Arc>, status: usize) { + let mut close_files = Vec::new(); + { + let (vfork, ppid) = { + let mut context = context_lock.write(); + context.image.clear(); + drop(context.heap.take()); + drop(context.stack.take()); + context.grants = Arc::new(Mutex::new(Vec::new())); + if Arc::strong_count(&context.files) == 1 { + mem::swap(context.files.lock().deref_mut(), &mut close_files); + } + context.files = Arc::new(Mutex::new(Vec::new())); + context.status = context::Status::Exited(status); + + let vfork = context.vfork; + context.vfork = false; + context.waitpid.notify(); + (vfork, context.ppid) + }; + if vfork { + let contexts = context::contexts(); + if let Some(parent_lock) = contexts.get(ppid) { + let mut parent = parent_lock.write(); + if ! parent.unblock() { + println!("{} not blocked for exit vfork unblock", ppid); + } + } else { + println!("{} not found for exit vfork unblock", ppid); + } + } + } + + for (fd, file_option) in close_files.drain(..).enumerate() { + if let Some(file) = file_option { + context::event::unregister(fd, file.scheme, file.number); + + let scheme_option = { + let schemes = scheme::schemes(); + schemes.get(file.scheme).map(|scheme| scheme.clone()) + }; + if let Some(scheme) = scheme_option { + let _ = scheme.close(file.number); + } + } + } +} + +pub fn exit(status: usize) -> ! { + { + let context_lock = { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH)).expect("exit failed to find context"); + context_lock.clone() + }; + terminate(context_lock, status); + } + + unsafe { context::switch(); } + + unreachable!(); +} + pub fn getegid() -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -679,6 +699,69 @@ pub fn iopl(_level: usize) -> Result { Ok(0) } +pub fn kill(pid: usize, sig: usize) -> Result { + use syscall::flag::*; + + let context_lock = { + let contexts = context::contexts(); + let context_lock = contexts.get(pid).ok_or(Error::new(ESRCH))?; + context_lock.clone() + }; + + let term = |context_lock| { + terminate(context_lock, !sig); + }; + + let core = |context_lock| { + terminate(context_lock, !sig); + }; + + let stop = || { + + }; + + let cont = || { + + }; + + match sig { + 0 => (), + SIGHUP => term(context_lock), + SIGINT => term(context_lock), + SIGQUIT => core(context_lock), + SIGILL => core(context_lock), + SIGTRAP => core(context_lock), + SIGABRT => core(context_lock), + SIGBUS => core(context_lock), + SIGFPE => core(context_lock), + SIGKILL => term(context_lock), + SIGUSR1 => term(context_lock), + SIGSEGV => core(context_lock), + SIGPIPE => term(context_lock), + SIGALRM => term(context_lock), + SIGTERM => term(context_lock), + SIGSTKFLT => term(context_lock), + SIGCHLD => (), + SIGCONT => cont(), + SIGSTOP => stop(), + SIGTSTP => stop(), + SIGTTIN => stop(), + SIGTTOU => stop(), + SIGURG => (), + SIGXCPU => core(context_lock), + SIGXFSZ => core(context_lock), + SIGVTALRM => term(context_lock), + SIGPROF => term(context_lock), + SIGWINCH => (), + SIGIO => term(context_lock), + SIGPWR => term(context_lock), + SIGSYS => core(context_lock), + _ => return Err(Error::new(EINVAL)) + } + + Ok(0) +} + pub fn physalloc(size: usize) -> Result { allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get()) } diff --git a/syscall/src/flag.rs b/syscall/src/flag.rs index 679fe5e..47e3939 100644 --- a/syscall/src/flag.rs +++ b/syscall/src/flag.rs @@ -34,10 +34,6 @@ pub const MODE_PERM: u16 = 0x0FFF; pub const MODE_SETUID: u16 = 0o4000; pub const MODE_SETGID: u16 = 0o2000; -pub const SEEK_SET: usize = 0; -pub const SEEK_CUR: usize = 1; -pub const SEEK_END: usize = 2; - pub const O_RDONLY: usize = 0x0000_0000; pub const O_WRONLY: usize = 0x0001_0000; pub const O_RDWR: usize = 0x0002_0000; @@ -52,4 +48,40 @@ pub const O_CREAT: usize = 0x0200_0000; pub const O_TRUNC: usize = 0x0400_0000; pub const O_EXCL: usize = 0x0800_0000; +pub const SEEK_SET: usize = 0; +pub const SEEK_CUR: usize = 1; +pub const SEEK_END: usize = 2; + +pub const SIGHUP: usize = 1; +pub const SIGINT: usize = 2; +pub const SIGQUIT: usize = 3; +pub const SIGILL: usize = 4; +pub const SIGTRAP: usize = 5; +pub const SIGABRT: usize = 6; +pub const SIGBUS: usize = 7; +pub const SIGFPE: usize = 8; +pub const SIGKILL: usize = 9; +pub const SIGUSR1: usize = 10; +pub const SIGSEGV: usize = 11; +pub const SIGUSR2: usize = 12; +pub const SIGPIPE: usize = 13; +pub const SIGALRM: usize = 14; +pub const SIGTERM: usize = 15; +pub const SIGSTKFLT: usize= 16; +pub const SIGCHLD: usize = 17; +pub const SIGCONT: usize = 18; +pub const SIGSTOP: usize = 19; +pub const SIGTSTP: usize = 20; +pub const SIGTTIN: usize = 21; +pub const SIGTTOU: usize = 22; +pub const SIGURG: usize = 23; +pub const SIGXCPU: usize = 24; +pub const SIGXFSZ: usize = 25; +pub const SIGVTALRM: usize= 26; +pub const SIGPROF: usize = 27; +pub const SIGWINCH: usize = 28; +pub const SIGIO: usize = 29; +pub const SIGPWR: usize = 30; +pub const SIGSYS: usize = 31; + pub const WNOHANG: usize = 1; diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 8cc7916..7646c00 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -112,6 +112,10 @@ pub unsafe fn iopl(level: usize) -> Result { syscall1(SYS_IOPL, level) } +pub fn kill(pid: usize, sig: usize) -> Result { + unsafe { syscall2(SYS_KILL, pid, sig) } +} + pub unsafe fn link(old: *const u8, new: *const u8) -> Result { syscall2(SYS_LINK, old as usize, new as usize) } diff --git a/syscall/src/number.rs b/syscall/src/number.rs index d5c5856..408d185 100644 --- a/syscall/src/number.rs +++ b/syscall/src/number.rs @@ -41,6 +41,7 @@ pub const SYS_GETGID: usize = 200; pub const SYS_GETPID: usize = 20; pub const SYS_GETUID: usize = 199; pub const SYS_IOPL: usize = 110; +pub const SYS_KILL: usize = 37; pub const SYS_NANOSLEEP: usize =162; pub const SYS_PHYSALLOC: usize =945; pub const SYS_PHYSFREE: usize = 946;