Add sys scheme to allow inspection of processes. WIP: Signals.

This commit is contained in:
Jeremy Soller 2016-10-13 21:00:51 -06:00
parent 224c43f761
commit d18bf07f3e
9 changed files with 445 additions and 64 deletions

View file

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

View file

@ -55,6 +55,8 @@ pub struct Context {
pub stack: Option<Memory>,
/// User grants
pub grants: Arc<Mutex<Vec<Grant>>>,
/// The name of the context
pub name: Arc<Mutex<Vec<u8>>>,
/// The current working directory
pub cwd: Arc<Mutex<Vec<u8>>>,
/// 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())),

View file

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

View file

@ -0,0 +1,90 @@
use collections::{String, Vec};
use core::str;
use context;
use syscall::error::Result;
pub fn resource() -> Result<Vec<u8>> {
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())
}

162
kernel/scheme/sys/mod.rs Normal file
View file

@ -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<u8>,
mode: u16,
seek: usize
}
type SysFn = Fn() -> Result<Vec<u8>> + Send + Sync;
/// System information scheme
pub struct SysScheme {
next_id: AtomicUsize,
files: BTreeMap<&'static [u8], Box<SysFn>>,
handles: RwLock<BTreeMap<usize, Handle>>
}
impl SysScheme {
pub fn new() -> SysScheme {
let mut files: BTreeMap<&'static [u8], Box<SysFn>> = 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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
Ok(0)
}
fn close(&self, id: usize) -> Result<usize> {
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
}
}

View file

@ -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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
unsafe { usermode(entry, sp); }
}
fn terminate(context_lock: Arc<RwLock<context::Context>>, 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<usize> {
let contexts = context::contexts();
let context_lock = contexts.current().ok_or(Error::new(ESRCH))?;
@ -679,6 +699,69 @@ pub fn iopl(_level: usize) -> Result<usize> {
Ok(0)
}
pub fn kill(pid: usize, sig: usize) -> Result<usize> {
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<usize> {
allocate_frames((size + 4095)/4096).ok_or(Error::new(ENOMEM)).map(|frame| frame.start_address().get())
}

View file

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

View file

@ -112,6 +112,10 @@ pub unsafe fn iopl(level: usize) -> Result<usize> {
syscall1(SYS_IOPL, level)
}
pub fn kill(pid: usize, sig: usize) -> Result<usize> {
unsafe { syscall2(SYS_KILL, pid, sig) }
}
pub unsafe fn link(old: *const u8, new: *const u8) -> Result<usize> {
syscall2(SYS_LINK, old as usize, new as usize)
}

View file

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