Move proc code to userspace.
This commit is contained in:
+12
-11
@@ -18,7 +18,7 @@ use crate::{
|
||||
memory::{allocate_p2frame, deallocate_p2frame, Enomem, Frame, RaiiFrame},
|
||||
paging::{RmmA, RmmArch},
|
||||
percpu::PercpuBlock,
|
||||
scheme::FileHandle,
|
||||
scheme::{FileHandle, SchemeNamespace},
|
||||
};
|
||||
|
||||
use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
|
||||
@@ -26,11 +26,9 @@ use crate::syscall::error::{Error, Result, EAGAIN, ESRCH};
|
||||
use super::{
|
||||
empty_cr3,
|
||||
memory::{AddrSpaceWrapper, GrantFileRef},
|
||||
process::{Process, ProcessId},
|
||||
};
|
||||
|
||||
/// The status of a context - used for scheduling
|
||||
/// See `syscall::process::waitpid` and the `sync` module for examples of usage
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Status {
|
||||
Runnable,
|
||||
@@ -124,13 +122,9 @@ impl PartialEq for WaitpidKey {
|
||||
|
||||
impl Eq for WaitpidKey {}
|
||||
|
||||
/// A context, which identifies either a process or a thread
|
||||
/// A context, which is typically mapped to a userspace thread
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
/// The process ID of this context
|
||||
pub pid: ProcessId,
|
||||
/// Process state shared with other threads
|
||||
pub process: Arc<RwLock<Process>>,
|
||||
/// Signal handler
|
||||
pub sig: Option<SignalState>,
|
||||
/// Status of context
|
||||
@@ -183,6 +177,11 @@ pub struct Context {
|
||||
pub userspace: bool,
|
||||
pub being_sigkilled: bool,
|
||||
pub fmap_ret: Option<Frame>,
|
||||
|
||||
// TODO: Temporary replacement for existing kernel logic, replace with capabilities!
|
||||
pub is_privileged: bool,
|
||||
pub ens: SchemeNamespace,
|
||||
pub rns: SchemeNamespace,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -203,10 +202,8 @@ pub struct SignalState {
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(pid: ProcessId, process: Arc<RwLock<Process>>) -> Result<Context> {
|
||||
pub fn new() -> Result<Context> {
|
||||
let this = Context {
|
||||
pid,
|
||||
process,
|
||||
sig: None,
|
||||
status: Status::HardBlocked {
|
||||
reason: HardBlockedReason::NotYetStarted,
|
||||
@@ -231,6 +228,10 @@ impl Context {
|
||||
fmap_ret: None,
|
||||
being_sigkilled: false,
|
||||
|
||||
is_privileged: false,
|
||||
ens: 0.into(),
|
||||
rns: 0.into(),
|
||||
|
||||
#[cfg(feature = "syscall_debug")]
|
||||
syscall_debug_info: crate::syscall::debug::SyscallDebugInfo::default(),
|
||||
};
|
||||
|
||||
+1
-24
@@ -17,10 +17,7 @@ use crate::{
|
||||
syscall::error::{Error, Result},
|
||||
};
|
||||
|
||||
use self::{
|
||||
context::Kstack,
|
||||
process::{Process, ProcessId, ProcessInfo},
|
||||
};
|
||||
use self::context::Kstack;
|
||||
pub use self::{
|
||||
context::{BorrowedHtBuf, Context, Status, WaitpidKey},
|
||||
switch::switch,
|
||||
@@ -54,9 +51,6 @@ pub mod file;
|
||||
/// Memory struct - contains a set of pages for a context
|
||||
pub mod memory;
|
||||
|
||||
/// Process handling - TODO move to userspace
|
||||
pub mod process;
|
||||
|
||||
/// Signal handling
|
||||
pub mod signal;
|
||||
|
||||
@@ -70,23 +64,11 @@ pub const CONTEXT_MAX_FILES: usize = 65_536;
|
||||
|
||||
pub use self::arch::empty_cr3;
|
||||
|
||||
static KMAIN_PROCESS: Once<Arc<RwLock<Process>>> = Once::new();
|
||||
|
||||
// Set of weak references to all contexts available for scheduling. The only strong references are
|
||||
// the context file descriptors.
|
||||
static CONTEXTS: RwLock<BTreeSet<ContextRef>> = RwLock::new(BTreeSet::new());
|
||||
|
||||
pub fn init() {
|
||||
let pid = ProcessId::new(0);
|
||||
let process = KMAIN_PROCESS.call_once(|| {
|
||||
Arc::new(RwLock::new(Process {
|
||||
info: ProcessInfo::default(),
|
||||
waitpid: Arc::new(WaitMap::new()),
|
||||
threads: Vec::new(),
|
||||
status: process::ProcessStatus::PossiblyRunnable,
|
||||
}))
|
||||
});
|
||||
|
||||
let mut context =
|
||||
Context::new(pid, Arc::clone(process)).expect("failed to create kmain context");
|
||||
context.sched_affinity = LogicalCpuSet::empty();
|
||||
@@ -135,10 +117,6 @@ pub fn is_current(context: &Arc<RwSpinlock<Context>>) -> bool {
|
||||
.with_context(|current| Arc::ptr_eq(context, current))
|
||||
}
|
||||
|
||||
pub fn current_pid() -> Result<ProcessId> {
|
||||
Ok(current().read().pid)
|
||||
}
|
||||
|
||||
pub struct ContextRef(pub Arc<RwSpinlock<Context>>);
|
||||
impl ContextRef {
|
||||
pub fn upgrade(&self) -> Option<Arc<RwSpinlock<Context>>> {
|
||||
@@ -166,7 +144,6 @@ impl Eq for ContextRef {}
|
||||
/// Spawn a context from a function.
|
||||
pub fn spawn(
|
||||
userspace_allowed: bool,
|
||||
process: Arc<RwLock<Process>>,
|
||||
func: extern "C" fn(),
|
||||
) -> Result<Arc<RwSpinlock<Context>>> {
|
||||
let stack = Kstack::new()?;
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
use core::{
|
||||
ops::{Deref, DerefMut},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
// TODO: move all this code to userspace
|
||||
use alloc::{
|
||||
collections::BTreeMap,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use spin::RwLock;
|
||||
use spinning_top::RwSpinlock;
|
||||
|
||||
use syscall::{Error, Result, ENOMEM, ESRCH};
|
||||
|
||||
use crate::{
|
||||
scheme::{CallerCtx, SchemeNamespace},
|
||||
sync::WaitMap,
|
||||
};
|
||||
|
||||
use crate::context::{self, Context, WaitpidKey};
|
||||
|
||||
int_like!(ProcessId, AtomicProcessId, usize, AtomicUsize);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Process {
|
||||
pub info: ProcessInfo,
|
||||
/// Context is being waited on
|
||||
pub waitpid: Arc<WaitMap<WaitpidKey, (ProcessId, usize)>>,
|
||||
pub status: ProcessStatus,
|
||||
pub threads: Vec<Weak<RwSpinlock<Context>>>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct ProcessInfo {
|
||||
/// The process ID of this process
|
||||
pub pid: ProcessId,
|
||||
/// The group ID of this process
|
||||
pub pgid: ProcessId,
|
||||
/// The ID of the parent process
|
||||
pub ppid: ProcessId,
|
||||
/// The ID of the session
|
||||
pub session_id: ProcessId,
|
||||
/// The real user id
|
||||
pub ruid: u32,
|
||||
/// The real group id
|
||||
pub rgid: u32,
|
||||
/// The real namespace id
|
||||
pub rns: SchemeNamespace,
|
||||
/// The effective user id
|
||||
pub euid: u32,
|
||||
/// The effective group id
|
||||
pub egid: u32,
|
||||
/// The effective namespace id
|
||||
pub ens: SchemeNamespace,
|
||||
}
|
||||
impl Deref for Process {
|
||||
type Target = ProcessInfo;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.info
|
||||
}
|
||||
}
|
||||
impl DerefMut for Process {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.info
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ProcessStatus {
|
||||
PossiblyRunnable,
|
||||
Stopped(usize),
|
||||
Exiting,
|
||||
Exited(usize),
|
||||
}
|
||||
|
||||
pub const INIT: ProcessId = ProcessId::new(1);
|
||||
static NEXT_PID: AtomicProcessId = AtomicProcessId::new(INIT);
|
||||
pub static PROCESSES: RwLock<BTreeMap<ProcessId, Arc<RwLock<Process>>>> =
|
||||
RwLock::new(BTreeMap::new());
|
||||
|
||||
/// Get an iterator of all parents
|
||||
pub fn ancestors(
|
||||
list: &BTreeMap<ProcessId, Arc<RwLock<Process>>>,
|
||||
id: ProcessId,
|
||||
) -> impl Iterator<Item = (ProcessId, &Arc<RwLock<Process>>)> + '_ {
|
||||
core::iter::successors(
|
||||
list.get(&id).map(|process| (id, process)),
|
||||
move |(_id, process)| {
|
||||
let process = process.read();
|
||||
let id = process.ppid;
|
||||
list.get(&id).map(|process| (id, process))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn current() -> Result<Arc<RwLock<Process>>> {
|
||||
let pid = context::current().read().pid;
|
||||
Ok(Arc::clone(
|
||||
PROCESSES.read().get(&pid).ok_or(Error::new(ESRCH))?,
|
||||
))
|
||||
}
|
||||
impl Process {
|
||||
pub fn caller_ctx(&self) -> CallerCtx {
|
||||
CallerCtx {
|
||||
pid: self.pid.into(),
|
||||
uid: self.euid,
|
||||
gid: self.egid,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn new_process(info: impl FnOnce(ProcessId) -> ProcessInfo) -> Result<Arc<RwLock<Process>>> {
|
||||
let pid = NEXT_PID.fetch_add(ProcessId::new(1), Ordering::Relaxed);
|
||||
let proc = Arc::try_new(RwLock::new(Process {
|
||||
waitpid: Arc::try_new(WaitMap::new()).map_err(|_| Error::new(ENOMEM))?,
|
||||
threads: Vec::new(),
|
||||
status: ProcessStatus::PossiblyRunnable,
|
||||
info: info(pid),
|
||||
}))
|
||||
.map_err(|_| Error::new(ENOMEM))?;
|
||||
PROCESSES.write().insert(pid, Arc::clone(&proc));
|
||||
Ok(proc)
|
||||
}
|
||||
@@ -80,7 +80,8 @@ pub fn excp_handler(_signal: usize) {
|
||||
|
||||
let Some(_eh) = context.sig.as_ref().and_then(|s| s.excp_handler) else {
|
||||
drop(context);
|
||||
crate::syscall::process::exit(SIGKILL << 8);
|
||||
// TODO: Send exception event to process manager
|
||||
todo!()
|
||||
};
|
||||
|
||||
// TODO
|
||||
|
||||
@@ -255,16 +255,17 @@ pub fn switch() -> SwitchResult {
|
||||
_next_guard: next_context_guard,
|
||||
}));
|
||||
|
||||
let (ptrace_session, ptrace_flags) = if let Some((session, bp)) = ptrace::sessions()
|
||||
/*let (ptrace_session, ptrace_flags) = if let Some((session, bp)) = ptrace::sessions()
|
||||
.get(&next_context.pid)
|
||||
.map(|s| (Arc::downgrade(s), s.data.lock().breakpoint))
|
||||
{
|
||||
(Some(session), bp.map_or(PtraceFlags::empty(), |f| f.flags))
|
||||
} else {
|
||||
(None, PtraceFlags::empty())
|
||||
};
|
||||
};*/
|
||||
let ptrace_flags = PtraceFlags::empty();
|
||||
|
||||
*percpu.ptrace_session.borrow_mut() = ptrace_session;
|
||||
//*percpu.ptrace_session.borrow_mut() = ptrace_session;
|
||||
percpu.ptrace_flags.set(ptrace_flags);
|
||||
prev_context.inside_syscall = percpu.inside_syscall.replace(next_context.inside_syscall);
|
||||
|
||||
|
||||
+3
-25
@@ -64,10 +64,7 @@ extern crate bitflags;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use crate::{
|
||||
context::{
|
||||
process::{new_process, ProcessInfo, INIT},
|
||||
switch::SwitchResult,
|
||||
},
|
||||
context::switch::SwitchResult,
|
||||
scheme::SchemeNamespace,
|
||||
};
|
||||
|
||||
@@ -202,8 +199,7 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
|
||||
//Initialize global schemes, such as `acpi:`.
|
||||
scheme::init_globals();
|
||||
|
||||
let pid = syscall::getpid();
|
||||
info!("BSP: {:?} {}", pid, cpu_count);
|
||||
info!("BSP: {}", cpu_count);
|
||||
info!("Env: {:?}", ::core::str::from_utf8(bootstrap.env));
|
||||
|
||||
BOOTSTRAP.call_once(|| bootstrap);
|
||||
@@ -211,30 +207,12 @@ fn kmain(cpu_count: u32, bootstrap: Bootstrap) -> ! {
|
||||
#[cfg(feature = "profiling")]
|
||||
profiling::ready_for_profiling();
|
||||
|
||||
let process = new_process(|_| ProcessInfo {
|
||||
pid: INIT,
|
||||
ppid: INIT,
|
||||
pgid: INIT,
|
||||
session_id: INIT,
|
||||
ruid: 0,
|
||||
rgid: 0,
|
||||
euid: 0,
|
||||
egid: 0,
|
||||
rns: SchemeNamespace::new(0),
|
||||
ens: SchemeNamespace::new(0),
|
||||
})
|
||||
.expect("failed to create init process");
|
||||
|
||||
match context::spawn(true, process, userspace_init) {
|
||||
match context::spawn(true, userspace_init) {
|
||||
Ok(context_lock) => {
|
||||
{
|
||||
let mut context = context_lock.write();
|
||||
context.status = context::Status::Runnable;
|
||||
context.name = "bootstrap".into();
|
||||
|
||||
let mut process = context.process.write();
|
||||
process.rns = SchemeNamespace::from(1);
|
||||
process.ens = SchemeNamespace::from(1);
|
||||
}
|
||||
INIT_THREAD.call_once(move || context_lock);
|
||||
}
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
type SessionMap = HashMap<ProcessId, Arc<Session>>;
|
||||
type SessionMap = HashMap<ContextId, Arc<Session>>;
|
||||
|
||||
static SESSIONS: Once<RwLock<SessionMap>> = Once::new();
|
||||
|
||||
|
||||
+16
-494
@@ -5,7 +5,6 @@ use crate::{
|
||||
context::{HardBlockedReason, SignalState},
|
||||
file::{FileDescriptor, InternalFlags},
|
||||
memory::{handle_notify_files, AddrSpaceWrapper, Grant, PageSpan},
|
||||
process::{self, Process, ProcessId, ProcessInfo, ProcessStatus},
|
||||
Context, Status,
|
||||
},
|
||||
memory::PAGE_SIZE,
|
||||
@@ -17,7 +16,7 @@ use crate::{
|
||||
error::*,
|
||||
flag::*,
|
||||
usercopy::{UserSliceRo, UserSliceWo},
|
||||
EnvRegisters, FloatRegisters, IntRegisters, KillMode, KillTarget,
|
||||
EnvRegisters, FloatRegisters, IntRegisters, KillMode,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -97,11 +96,6 @@ enum RegsKind {
|
||||
}
|
||||
#[derive(Clone)]
|
||||
enum ProcHandle {
|
||||
Trace {
|
||||
pid: ProcessId,
|
||||
clones: Vec<ProcessId>,
|
||||
excl: bool,
|
||||
},
|
||||
Static {
|
||||
ty: &'static str,
|
||||
bytes: Box<[u8]>,
|
||||
@@ -158,10 +152,6 @@ enum Handle {
|
||||
context: Arc<RwSpinlock<Context>>,
|
||||
kind: ContextHandle,
|
||||
},
|
||||
Process {
|
||||
process: Arc<RwLock<Process>>,
|
||||
kind: ProcHandle,
|
||||
},
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Attr {
|
||||
@@ -169,65 +159,6 @@ enum Attr {
|
||||
Gid,
|
||||
// TODO: namespace, tid, etc.
|
||||
}
|
||||
impl Handle {
|
||||
fn needs_child_process(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Process {
|
||||
kind: ProcHandle::Trace { .. } | ProcHandle::SessionId,
|
||||
..
|
||||
} | Self::Context {
|
||||
kind: ContextHandle::Regs(_)
|
||||
| ContextHandle::Filetable { .. }
|
||||
| ContextHandle::NewFiletable { .. }
|
||||
| ContextHandle::AddrSpace { .. }
|
||||
| ContextHandle::CurrentAddrSpace
|
||||
| ContextHandle::CurrentFiletable
|
||||
| ContextHandle::Sighandler,
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
fn needs_root(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::Process {
|
||||
kind: ProcHandle::Attr { .. },
|
||||
..
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
impl Handle {
|
||||
fn continue_ignored_children(&mut self) -> Option<()> {
|
||||
let Handle::Process {
|
||||
kind: ProcHandle::Trace { clones, .. },
|
||||
..
|
||||
} = self
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
for pid in clones.drain(..) {
|
||||
if ptrace::is_traced(pid) {
|
||||
continue;
|
||||
}
|
||||
let Some(child_process) = process::PROCESSES.read().get(&pid).map(Arc::clone) else {
|
||||
continue;
|
||||
};
|
||||
for thread in child_process
|
||||
.read()
|
||||
.threads
|
||||
.iter()
|
||||
.filter_map(|t| t.upgrade())
|
||||
{
|
||||
thread.write().status = context::Status::Runnable;
|
||||
}
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcScheme<const FULL: bool>;
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
@@ -256,7 +187,6 @@ fn new_handle((handle, fl): (Handle, InternalFlags)) -> Result<(usize, InternalF
|
||||
}
|
||||
|
||||
enum OpenTy {
|
||||
Proc(ProcessId),
|
||||
Ctxt(Arc<RwSpinlock<Context>>),
|
||||
}
|
||||
|
||||
@@ -309,44 +239,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
|
||||
_ => return Ok(None),
|
||||
}))
|
||||
}
|
||||
fn openat_process(
|
||||
&self,
|
||||
target: &Arc<RwLock<Process>>,
|
||||
name: &str,
|
||||
flags: usize,
|
||||
) -> Result<Option<(ProcHandle, bool)>> {
|
||||
Ok(Some(match name {
|
||||
"trace" => (
|
||||
ProcHandle::Trace {
|
||||
pid: target.read().pid,
|
||||
clones: Vec::new(),
|
||||
excl: flags & O_EXCL == O_EXCL,
|
||||
},
|
||||
false,
|
||||
),
|
||||
"exe" => (
|
||||
ProcHandle::Static {
|
||||
ty: "exe",
|
||||
// FIXME: allow opening any thread
|
||||
bytes: target
|
||||
.read()
|
||||
.threads
|
||||
.first()
|
||||
.and_then(|f| f.upgrade())
|
||||
.ok_or(Error::new(ESRCH))?
|
||||
.read()
|
||||
.name
|
||||
.as_bytes()
|
||||
.into(),
|
||||
},
|
||||
true,
|
||||
),
|
||||
"uid" => (ProcHandle::Attr { attr: Attr::Uid }, true),
|
||||
"gid" => (ProcHandle::Attr { attr: Attr::Gid }, true),
|
||||
"session_id" => (ProcHandle::SessionId, true),
|
||||
_ => return Ok(None),
|
||||
}))
|
||||
}
|
||||
fn open_inner(
|
||||
&self,
|
||||
ty: OpenTy,
|
||||
@@ -355,42 +247,24 @@ impl<const FULL: bool> ProcScheme<FULL> {
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
) -> Result<(usize, InternalFlags)> {
|
||||
let target = match ty {
|
||||
OpenTy::Proc(pid) => {
|
||||
let processes = process::PROCESSES.read();
|
||||
Arc::clone(processes.get(&pid).ok_or(Error::new(ESRCH))?)
|
||||
}
|
||||
OpenTy::Ctxt(ref context) => Arc::clone(&context.read().process),
|
||||
};
|
||||
|
||||
let operation_name = operation_str.ok_or(Error::new(EINVAL))?;
|
||||
let (mut handle, positioned) = {
|
||||
if let Some((kind, positioned)) = self.openat_process(&target, operation_name, flags)? {
|
||||
(
|
||||
Handle::Process {
|
||||
process: Arc::clone(&target),
|
||||
kind,
|
||||
},
|
||||
positioned,
|
||||
)
|
||||
let context = match ty {
|
||||
OpenTy::Proc(_) => target
|
||||
.read()
|
||||
.threads
|
||||
.first()
|
||||
.ok_or(Error::new(ESRCH))?
|
||||
.upgrade()
|
||||
.ok_or(Error::new(ESRCH))?,
|
||||
OpenTy::Ctxt(ref ctxt) => Arc::clone(&ctxt),
|
||||
};
|
||||
if let Some((kind, positioned)) =
|
||||
self.openat_context(operation_name, Arc::clone(&context))?
|
||||
{
|
||||
(Handle::Context { context, kind }, positioned)
|
||||
} else {
|
||||
let context = match ty {
|
||||
OpenTy::Proc(_) => target
|
||||
.read()
|
||||
.threads
|
||||
.first()
|
||||
.ok_or(Error::new(ESRCH))?
|
||||
.upgrade()
|
||||
.ok_or(Error::new(ESRCH))?,
|
||||
OpenTy::Ctxt(ref ctxt) => Arc::clone(&ctxt),
|
||||
};
|
||||
if let Some((kind, positioned)) =
|
||||
self.openat_context(operation_name, Arc::clone(&context))?
|
||||
{
|
||||
(Handle::Context { context, kind }, positioned)
|
||||
} else {
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -401,36 +275,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
|
||||
return Err(Error::new(ESRCH));
|
||||
}
|
||||
|
||||
// Unless root, check security
|
||||
if handle.needs_child_process() && uid != 0 && gid != 0 {
|
||||
let current = process::current()?;
|
||||
let current = current.read();
|
||||
|
||||
// Are we the process?
|
||||
if target.pid != current.pid {
|
||||
// Do we own the process?
|
||||
if uid != target.euid && gid != target.egid {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
// Is it a subprocess of us? In the future, a capability could
|
||||
// bypass this check.
|
||||
match process::ancestors(&*process::PROCESSES.read(), target.ppid)
|
||||
.find(|&(pid, _context)| pid == current.pid)
|
||||
{
|
||||
Some((id, context)) => {
|
||||
// Paranoid sanity check, as ptrace security holes
|
||||
// wouldn't be fun
|
||||
assert_eq!(id, current.pid);
|
||||
assert_eq!(id, context.read().pid);
|
||||
}
|
||||
None => return Err(Error::new(EPERM)),
|
||||
}
|
||||
}
|
||||
} else if handle.needs_root() && (uid != 0 || gid != 0) {
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
let filetable_opt = match handle {
|
||||
Handle::Context {
|
||||
kind:
|
||||
@@ -477,27 +321,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
|
||||
},
|
||||
))?;
|
||||
|
||||
if let Handle::Process {
|
||||
kind: ProcHandle::Trace { pid, .. },
|
||||
..
|
||||
} = handle
|
||||
{
|
||||
if !ptrace::try_new_session(pid, id) {
|
||||
// There is no good way to handle id being occupied for nothing
|
||||
// here, is there?
|
||||
return Err(Error::new(EBUSY));
|
||||
}
|
||||
|
||||
if flags & O_TRUNC == O_TRUNC {
|
||||
let target = target.read();
|
||||
for thread in target.threads.iter().filter_map(|t| t.upgrade()) {
|
||||
thread.write().status = context::Status::HardBlocked {
|
||||
reason: HardBlockedReason::PtraceStop,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((id, int_fl))
|
||||
}
|
||||
}
|
||||
@@ -505,21 +328,6 @@ impl<const FULL: bool> ProcScheme<FULL> {
|
||||
impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
fn kopen(&self, path: &str, flags: usize, ctx: CallerCtx) -> Result<OpenResult> {
|
||||
let mut parts = path.splitn(2, '/');
|
||||
let pid_str = parts.next().ok_or(Error::new(ENOENT))?;
|
||||
|
||||
let pid = if pid_str == "current" {
|
||||
OpenTy::Ctxt(context::current())
|
||||
} else if pid_str == "new" || pid_str == "new-child" {
|
||||
OpenTy::Ctxt(new_child()?)
|
||||
} else if pid_str == "new-thread" {
|
||||
OpenTy::Ctxt(new_thread()?)
|
||||
} else if !FULL {
|
||||
return Err(Error::new(EACCES));
|
||||
} else {
|
||||
OpenTy::Proc(ProcessId::new(
|
||||
pid_str.parse().map_err(|_| Error::new(ENOENT))?,
|
||||
))
|
||||
};
|
||||
|
||||
self.open_inner(pid, parts.next(), flags, ctx.uid, ctx.gid)
|
||||
.map(|(r, fl)| OpenResult::SchemeLocal(r, fl))
|
||||
@@ -530,19 +338,12 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
match handle {
|
||||
Handle::Process {
|
||||
kind: ProcHandle::Trace { pid, .. },
|
||||
process: _,
|
||||
} => ptrace::Session::with_session(*pid, |session| {
|
||||
Ok(session.data.lock().session_fevent_flags())
|
||||
}),
|
||||
_ => Ok(EventFlags::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<()> {
|
||||
let mut handle = HANDLES.write().remove(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.continue_ignored_children();
|
||||
|
||||
match handle {
|
||||
Handle::Context {
|
||||
@@ -577,26 +378,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
} => {
|
||||
context.write().files = new_ft;
|
||||
}
|
||||
Handle::Process {
|
||||
kind: ProcHandle::Trace { pid, excl, .. },
|
||||
process,
|
||||
} => {
|
||||
ptrace::close_session(pid);
|
||||
|
||||
if excl {
|
||||
syscall::kill(pid, SIGKILL, KillMode::Idempotent)?;
|
||||
}
|
||||
|
||||
let threads = process.read().threads.clone();
|
||||
|
||||
for thread in threads {
|
||||
let Some(context) = thread.upgrade() else {
|
||||
continue;
|
||||
};
|
||||
let mut context = context.write();
|
||||
context.status = context::Status::Runnable;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
@@ -694,9 +475,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
|
||||
match handle {
|
||||
Handle::Context { context, kind } => kind.kreadoff(id, context, buf, offset),
|
||||
Handle::Process { process, kind } => {
|
||||
kind.kreadoff(id, process, buf, offset, read_flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn kwriteoff(
|
||||
@@ -713,12 +491,10 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
let handle = {
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
handle.continue_ignored_children();
|
||||
handle.clone()
|
||||
};
|
||||
|
||||
match handle {
|
||||
Handle::Process { process, kind } => kind.kwriteoff(process, buf),
|
||||
Handle::Context { context, kind } => kind.kwriteoff(id, context, buf),
|
||||
}
|
||||
}
|
||||
@@ -727,21 +503,6 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let path = match handle {
|
||||
Handle::Process { process, kind } => format!(
|
||||
"proc:{}/{}",
|
||||
process.read().pid.get(),
|
||||
match kind {
|
||||
ProcHandle::Attr {
|
||||
attr: Attr::Uid, ..
|
||||
} => "uid",
|
||||
ProcHandle::Attr {
|
||||
attr: Attr::Gid, ..
|
||||
} => "gid",
|
||||
ProcHandle::Trace { .. } => "trace",
|
||||
ProcHandle::Static { ty, .. } => ty,
|
||||
ProcHandle::SessionId => "session_id",
|
||||
},
|
||||
),
|
||||
Handle::Context { context, kind } => format!(
|
||||
"proc:{}/{}",
|
||||
context.read().pid.get(),
|
||||
@@ -818,17 +579,12 @@ impl<const FULL: bool> KernelScheme for ProcScheme<FULL> {
|
||||
kind: ContextHandle::OpenViaDup,
|
||||
context,
|
||||
} => {
|
||||
let (uid, gid) = match &*process::current()?.read() {
|
||||
process => (process.euid, process.egid),
|
||||
};
|
||||
return self
|
||||
.open_inner(
|
||||
OpenTy::Ctxt(context),
|
||||
Some(core::str::from_utf8(buf).map_err(|_| Error::new(EINVAL))?)
|
||||
.filter(|s| !s.is_empty()),
|
||||
O_RDWR | O_CLOEXEC,
|
||||
uid,
|
||||
gid,
|
||||
)
|
||||
.map(|(r, fl)| OpenResult::SchemeLocal(r, fl));
|
||||
}
|
||||
@@ -922,38 +678,6 @@ extern "C" fn clone_handler() {
|
||||
// usermode.
|
||||
}
|
||||
|
||||
fn new_thread() -> Result<Arc<RwSpinlock<Context>>> {
|
||||
let current_process = process::current()?;
|
||||
context::spawn(true, current_process, clone_handler)
|
||||
}
|
||||
|
||||
fn new_child() -> Result<Arc<RwSpinlock<Context>>> {
|
||||
let new_context = {
|
||||
let current_process_info = process::current()?.read().info;
|
||||
let new_process = process::new_process(|new_pid| ProcessInfo {
|
||||
pid: new_pid,
|
||||
ppid: current_process_info.pid,
|
||||
..current_process_info
|
||||
})?;
|
||||
context::spawn(true, new_process, clone_handler)?
|
||||
};
|
||||
|
||||
if ptrace::send_event(crate::syscall::ptrace_event!(
|
||||
PTRACE_EVENT_CLONE,
|
||||
new_context.read().pid.into()
|
||||
))
|
||||
.is_some()
|
||||
{
|
||||
// Freeze the clone, allow ptrace to put breakpoints
|
||||
// to it before it starts
|
||||
let mut context = new_context.write();
|
||||
context.status = context::Status::HardBlocked {
|
||||
reason: HardBlockedReason::PtraceStop,
|
||||
};
|
||||
}
|
||||
|
||||
Ok(new_context)
|
||||
}
|
||||
fn extract_scheme_number(fd: usize) -> Result<(KernelSchemes, usize)> {
|
||||
let (scheme_id, number) = match &*context::current()
|
||||
.read()
|
||||
@@ -983,10 +707,6 @@ fn verify_scheme(scheme: KernelSchemes) -> Result<()> {
|
||||
impl Handle {
|
||||
fn fsize(&self) -> Result<u64> {
|
||||
match self {
|
||||
Self::Process {
|
||||
kind: ProcHandle::Static { ref bytes, .. },
|
||||
..
|
||||
} => Ok(bytes.len() as u64),
|
||||
Self::Context {
|
||||
kind:
|
||||
ContextHandle::Filetable { ref data, .. }
|
||||
@@ -997,183 +717,6 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ProcHandle {
|
||||
fn kwriteoff(self, process: Arc<RwLock<Process>>, buf: UserSliceRo) -> Result<usize> {
|
||||
match self {
|
||||
Self::Static { .. } => Err(Error::new(EBADF)),
|
||||
Self::Trace { pid, .. } => {
|
||||
let op = buf.read_u64()?;
|
||||
let op = PtraceFlags::from_bits(op).ok_or(Error::new(EINVAL))?;
|
||||
|
||||
// Set next breakpoint
|
||||
ptrace::Session::with_session(pid, |session| {
|
||||
session.data.lock().set_breakpoint(
|
||||
Some(op).filter(|op| op.intersects(PTRACE_STOP_MASK | PTRACE_EVENT_MASK)),
|
||||
);
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let first = process
|
||||
.read()
|
||||
.threads
|
||||
.first()
|
||||
.and_then(|f| f.upgrade())
|
||||
.ok_or(Error::new(ESRCH))?;
|
||||
|
||||
if op.contains(PTRACE_STOP_SINGLESTEP) {
|
||||
try_stop_context(first, |context| match context.regs_mut() {
|
||||
None => {
|
||||
println!(
|
||||
"{}:{}: Couldn't read registers from stopped process",
|
||||
file!(),
|
||||
line!()
|
||||
);
|
||||
Err(Error::new(ENOTRECOVERABLE))
|
||||
}
|
||||
Some(stack) => {
|
||||
stack.set_singlestep(true);
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
// disable the ptrace_stop flag, which is used in some cases
|
||||
for thread in process.read().threads.iter().filter_map(|t| t.upgrade()) {
|
||||
thread.write().status = context::Status::HardBlocked {
|
||||
reason: HardBlockedReason::PtraceStop,
|
||||
};
|
||||
}
|
||||
|
||||
// and notify the tracee's WaitCondition, which is used in other cases
|
||||
ptrace::Session::with_session(pid, |session| {
|
||||
session.tracee.notify();
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(mem::size_of::<u64>())
|
||||
}
|
||||
Self::Attr { attr } => {
|
||||
// TODO: What limit?
|
||||
let mut str_buf = [0_u8; 32];
|
||||
let bytes_copied = buf.copy_common_bytes_to_slice(&mut str_buf)?;
|
||||
|
||||
let id = core::str::from_utf8(&str_buf[..bytes_copied])
|
||||
.map_err(|_| Error::new(EINVAL))?
|
||||
.parse::<u32>()
|
||||
.map_err(|_| Error::new(EINVAL))?;
|
||||
|
||||
match attr {
|
||||
Attr::Uid => process.write().euid = id,
|
||||
Attr::Gid => process.write().egid = id,
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
Self::SessionId => {
|
||||
let session_id = ProcessId::new(buf.read_usize()?);
|
||||
|
||||
if session_id != process.read().pid {
|
||||
// Session ID can only be set to this process's ID
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
|
||||
for (_pid, process_lock) in process::PROCESSES.read().iter() {
|
||||
if session_id == process_lock.read().pgid {
|
||||
// The session ID cannot match the PGID of any process
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let mut process = process.write();
|
||||
process.pgid = session_id;
|
||||
process.session_id = session_id;
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn kreadoff(
|
||||
self,
|
||||
id: usize,
|
||||
process: Arc<RwLock<Process>>,
|
||||
buf: UserSliceWo,
|
||||
offset: u64,
|
||||
read_flags: u32,
|
||||
) -> Result<usize> {
|
||||
match self {
|
||||
Self::Static { bytes, .. } => read_from(buf, &bytes, offset),
|
||||
Self::Trace { pid, .. } => {
|
||||
// Wait for event
|
||||
if (read_flags as usize) & O_NONBLOCK != O_NONBLOCK {
|
||||
ptrace::wait(pid)?;
|
||||
}
|
||||
|
||||
// Check if process exists
|
||||
let _ = process::PROCESSES
|
||||
.read()
|
||||
.get(&pid)
|
||||
.ok_or(Error::new(ESRCH))?;
|
||||
|
||||
let mut src_buf = [PtraceEvent::default(); 4];
|
||||
|
||||
// Read events
|
||||
let src_len = src_buf.len();
|
||||
let slice = &mut src_buf
|
||||
[..core::cmp::min(src_len, buf.len() / mem::size_of::<PtraceEvent>())];
|
||||
|
||||
let (read, reached) = ptrace::Session::with_session(pid, |session| {
|
||||
let mut data = session.data.lock();
|
||||
Ok((data.recv_events(slice), data.is_reached()))
|
||||
})?;
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let Handle::Process {
|
||||
kind: ProcHandle::Trace { ref mut clones, .. },
|
||||
..
|
||||
} = handle
|
||||
else {
|
||||
return Err(Error::new(EBADFD));
|
||||
};
|
||||
|
||||
// Save child processes in a list of processes to restart
|
||||
for event in &slice[..read] {
|
||||
if event.cause == PTRACE_EVENT_CLONE {
|
||||
clones.push(ProcessId::from(event.a));
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no events, and breakpoint isn't reached, we
|
||||
// must not have waited.
|
||||
if read == 0 && !reached {
|
||||
return Err(Error::new(EAGAIN));
|
||||
}
|
||||
|
||||
for (dst, src) in buf
|
||||
.in_exact_chunks(mem::size_of::<PtraceEvent>())
|
||||
.zip(slice.iter())
|
||||
{
|
||||
dst.copy_exactly(src)?;
|
||||
}
|
||||
|
||||
// Return read events
|
||||
Ok(read * mem::size_of::<PtraceEvent>())
|
||||
}
|
||||
Self::SessionId => {
|
||||
read_from(buf, &process.read().session_id.get().to_ne_bytes(), offset)
|
||||
}
|
||||
Self::Attr { attr } => {
|
||||
let src_buf = match (attr, process.read()) {
|
||||
(Attr::Uid, process) => process.euid.to_string(),
|
||||
(Attr::Gid, process) => process.egid.to_string(),
|
||||
}
|
||||
.into_bytes();
|
||||
|
||||
read_from(buf, &src_buf, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ContextHandle {
|
||||
fn kwriteoff(
|
||||
self,
|
||||
@@ -1357,7 +900,6 @@ impl ContextHandle {
|
||||
return Err(Error::new(EBADF));
|
||||
};
|
||||
let filetable = match *entry.get_mut() {
|
||||
Handle::Process { .. } => return Err(Error::new(EBADF)),
|
||||
Handle::Context {
|
||||
kind: ContextHandle::Filetable { ref filetable, .. },
|
||||
..
|
||||
@@ -1445,18 +987,6 @@ impl ContextHandle {
|
||||
}
|
||||
let is_current = context::is_current(&context);
|
||||
|
||||
{
|
||||
let process = Arc::clone(&context.read().process);
|
||||
let mut process = process.write();
|
||||
if let Some(pos) = process
|
||||
.threads
|
||||
.iter()
|
||||
.position(|p| Weak::as_ptr(p) == Arc::as_ptr(&context))
|
||||
{
|
||||
process.threads.remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
if is_current {
|
||||
crate::syscall::exit_this_context();
|
||||
} else {
|
||||
@@ -1487,14 +1017,6 @@ impl ContextHandle {
|
||||
}
|
||||
}
|
||||
ContextHandle::Signal => {
|
||||
let me = {
|
||||
let p = process::current()?;
|
||||
let p = p.read();
|
||||
SenderInfo {
|
||||
pid: p.pid.get().try_into().unwrap_or(0),
|
||||
ruid: p.ruid,
|
||||
}
|
||||
};
|
||||
let sig = buf.read_u32()?;
|
||||
let mut killed_self = false;
|
||||
crate::syscall::process::send_signal(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use crate::{context::process, scheme, syscall::error::Result};
|
||||
use crate::context;
|
||||
use crate::{scheme, syscall::error::Result};
|
||||
|
||||
pub fn resource() -> Result<Vec<u8>> {
|
||||
let scheme_ns = process::current()?.read().ens;
|
||||
let scheme_ns = context::current().read().ens;
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
|
||||
@@ -194,12 +194,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> us
|
||||
clock_gettime(b, UserSlice::wo(c, core::mem::size_of::<TimeSpec>())?).map(|()| 0)
|
||||
}
|
||||
SYS_FUTEX => futex(b, c, d, e, f),
|
||||
SYS_GETPID => getpid().map(ProcessId::into),
|
||||
SYS_GETPGID => getpgid(ProcessId::from(b)).map(ProcessId::into),
|
||||
SYS_GETPPID => getppid().map(ProcessId::into),
|
||||
|
||||
SYS_EXIT => exit(b),
|
||||
SYS_KILL => kill(ProcessId::from(b), c, KillMode::Idempotent),
|
||||
SYS_SIGENQUEUE => kill(
|
||||
ProcessId::from(b),
|
||||
c,
|
||||
@@ -210,33 +205,16 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize) -> us
|
||||
SYS_SIGDEQUEUE => {
|
||||
sigdequeue(UserSlice::wo(b, size_of::<RtSigInfo>())?, c as u32).map(|()| 0)
|
||||
}
|
||||
SYS_WAITPID => waitpid(
|
||||
ProcessId::from(b),
|
||||
if c == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(UserSlice::wo(c, core::mem::size_of::<usize>())?)
|
||||
},
|
||||
WaitFlags::from_bits_truncate(d),
|
||||
)
|
||||
.map(ProcessId::into),
|
||||
SYS_IOPL => iopl(b),
|
||||
SYS_GETEGID => getegid(),
|
||||
SYS_GETENS => getens(),
|
||||
SYS_GETEUID => geteuid(),
|
||||
SYS_GETGID => getgid(),
|
||||
SYS_GETNS => getns(),
|
||||
SYS_GETUID => getuid(),
|
||||
SYS_MPROTECT => mprotect(b, c, MapFlags::from_bits_truncate(d)).map(|()| 0),
|
||||
SYS_MKNS => mkns(UserSlice::ro(
|
||||
b,
|
||||
c.checked_mul(core::mem::size_of::<[usize; 2]>())
|
||||
.ok_or(Error::new(EOVERFLOW))?,
|
||||
)?),
|
||||
SYS_SETPGID => setpgid(ProcessId::from(b), ProcessId::from(c)).map(|()| 0),
|
||||
SYS_SETREUID => setreuid(b as u32, c as u32).map(|()| 0),
|
||||
SYS_SETRENS => setrens(SchemeNamespace::from(b), SchemeNamespace::from(c)).map(|()| 0),
|
||||
SYS_SETREGID => setregid(b as u32, c as u32).map(|()| 0),
|
||||
SYS_VIRTTOPHYS => virttophys(b),
|
||||
|
||||
SYS_MREMAP => mremap(b, c, d, e, f),
|
||||
|
||||
+2
-489
@@ -10,7 +10,6 @@ use spin::RwLock;
|
||||
|
||||
use crate::context::{
|
||||
memory::{AddrSpace, Grant, PageSpan},
|
||||
process::{self, Process, ProcessId, ProcessInfo, ProcessStatus},
|
||||
Context, ContextRef, WaitpidKey,
|
||||
};
|
||||
|
||||
@@ -20,10 +19,7 @@ use crate::{
|
||||
ptrace,
|
||||
syscall::{
|
||||
error::*,
|
||||
flag::{
|
||||
wifcontinued, wifstopped, MapFlags, WaitFlags, PTRACE_STOP_EXIT, SIGCONT, WCONTINUED,
|
||||
WNOHANG, WUNTRACED,
|
||||
},
|
||||
flag::MapFlags,
|
||||
ptrace_event,
|
||||
},
|
||||
Bootstrap, CurrentRmmArch,
|
||||
@@ -72,133 +68,8 @@ pub fn wait_for_exit(context_lock: Arc<RwSpinlock<Context>>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit(status: usize) -> ! {
|
||||
if matches!(
|
||||
mem::replace(
|
||||
&mut process::current().unwrap().write().status,
|
||||
ProcessStatus::Exiting
|
||||
),
|
||||
ProcessStatus::Exiting
|
||||
) {
|
||||
// already exiting the current process, so just set our status to Dead and context switch.
|
||||
exit_this_context();
|
||||
}
|
||||
|
||||
ptrace::breakpoint_callback(
|
||||
PTRACE_STOP_EXIT,
|
||||
Some(ptrace_event!(PTRACE_STOP_EXIT, status)),
|
||||
);
|
||||
|
||||
let current_pid;
|
||||
let current_ruid;
|
||||
{
|
||||
let current_context = context::current();
|
||||
let current_process = process::current().expect("no active process during exit syscall");
|
||||
current_pid = current_process.read().pid;
|
||||
|
||||
let threads = core::mem::take(&mut current_process.write().threads);
|
||||
|
||||
for context_lock in threads.into_iter().filter_map(|t| t.upgrade()) {
|
||||
// Current context must be closed last, as it would otherwise be impossible to context
|
||||
// switch back, if closing file descriptors require scheme calls.
|
||||
if Arc::ptr_eq(&context_lock, ¤t_context) {
|
||||
continue;
|
||||
}
|
||||
wait_for_exit(context_lock);
|
||||
}
|
||||
|
||||
{
|
||||
// PGID and PPID must be grabbed after close, as context switches could change PGID or PPID if parent exits
|
||||
let (pgid, ppid) = {
|
||||
let process = current_process.read();
|
||||
current_ruid = process.ruid;
|
||||
(process.pgid, process.ppid)
|
||||
};
|
||||
if let Some(parent) = process::PROCESSES.read().get(&ppid).map(Arc::clone) {
|
||||
let _ = send_signal(
|
||||
KillTarget::Process(parent),
|
||||
SIGCHLD,
|
||||
KillMode::Idempotent,
|
||||
true,
|
||||
&mut false,
|
||||
SenderInfo {
|
||||
pid: current_pid.get().try_into().unwrap_or(0),
|
||||
ruid: current_ruid,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Transfer child processes to parent (TODO: to init)
|
||||
{
|
||||
let processes = context::process::PROCESSES.read();
|
||||
for (_child_pid, child_process_lock) in processes.iter() {
|
||||
let mut process = child_process_lock.write();
|
||||
if process.ppid == current_pid {
|
||||
process.ppid = ppid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_process.write().status = ProcessStatus::Exited(status);
|
||||
|
||||
let children = current_process.write().waitpid.receive_all();
|
||||
|
||||
{
|
||||
let processes = process::PROCESSES.read();
|
||||
if let Some(parent_lock) = processes.get(&ppid) {
|
||||
let waitpid = Arc::clone(&parent_lock.write().waitpid);
|
||||
|
||||
for (c_pid, c_status) in children {
|
||||
waitpid.send(c_pid, c_status);
|
||||
}
|
||||
|
||||
waitpid.send(
|
||||
WaitpidKey {
|
||||
pid: Some(current_pid),
|
||||
pgid: Some(pgid),
|
||||
},
|
||||
(current_pid, status),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Alert any tracers waiting of this process
|
||||
ptrace::close_tracee(current_pid);
|
||||
}
|
||||
}
|
||||
exit_this_context();
|
||||
}
|
||||
|
||||
pub fn getpid() -> Result<ProcessId> {
|
||||
context::current_pid()
|
||||
}
|
||||
|
||||
pub fn getpgid(pid: ProcessId) -> Result<ProcessId> {
|
||||
let process_lock = if pid.get() == 0 {
|
||||
process::current()?
|
||||
} else {
|
||||
Arc::clone(
|
||||
process::PROCESSES
|
||||
.read()
|
||||
.get(&pid)
|
||||
.ok_or(Error::new(ESRCH))?,
|
||||
)
|
||||
};
|
||||
let process = process_lock.read();
|
||||
Ok(process.pgid)
|
||||
}
|
||||
|
||||
pub fn getppid() -> Result<ProcessId> {
|
||||
Ok(process::current()?.read().ppid)
|
||||
}
|
||||
|
||||
pub enum KillTarget {
|
||||
Process(Arc<RwLock<Process>>),
|
||||
Thread(Arc<RwSpinlock<Context>>),
|
||||
}
|
||||
|
||||
pub fn send_signal(
|
||||
target: KillTarget,
|
||||
context: Arc<RwLock<Context>>,
|
||||
sig: usize,
|
||||
mode: KillMode,
|
||||
is_sigchld_to_parent: bool,
|
||||
@@ -229,13 +100,9 @@ pub fn send_signal(
|
||||
enum SendResult {
|
||||
Succeeded,
|
||||
SucceededSigchld {
|
||||
ppid: ProcessId,
|
||||
pgid: ProcessId,
|
||||
orig_signal: usize,
|
||||
},
|
||||
SucceededSigcont {
|
||||
ppid: ProcessId,
|
||||
pgid: ProcessId,
|
||||
},
|
||||
FullQ,
|
||||
Invalid,
|
||||
@@ -290,8 +157,6 @@ pub fn send_signal(
|
||||
}
|
||||
// POSIX XSI allows but does not reqiure SIGCHLD to be sent when SIGCONT occurs.
|
||||
return SendResult::SucceededSigcont {
|
||||
ppid: proc_info.ppid,
|
||||
pgid: proc_info.pgid,
|
||||
};
|
||||
}
|
||||
drop(process_guard);
|
||||
@@ -325,8 +190,6 @@ pub fn send_signal(
|
||||
}
|
||||
|
||||
return SendResult::SucceededSigchld {
|
||||
ppid: proc_info.ppid,
|
||||
pgid: proc_info.pgid,
|
||||
orig_signal: sig,
|
||||
};
|
||||
}
|
||||
@@ -429,42 +292,8 @@ pub fn send_signal(
|
||||
pgid,
|
||||
orig_signal,
|
||||
} => {
|
||||
let parent = process::PROCESSES
|
||||
.read()
|
||||
.get(&ppid)
|
||||
.map(Arc::clone)
|
||||
.ok_or(Error::new(ESRCH))?;
|
||||
let waitpid = Arc::clone(&parent.read().waitpid);
|
||||
waitpid.send(
|
||||
WaitpidKey {
|
||||
pid: Some(proc_info.pid),
|
||||
pgid: Some(pgid),
|
||||
},
|
||||
(proc_info.pid, (orig_signal << 8) | 0x7f),
|
||||
);
|
||||
send_signal(
|
||||
KillTarget::Process(parent),
|
||||
SIGCHLD,
|
||||
mode,
|
||||
true,
|
||||
killed_self,
|
||||
sender,
|
||||
)?;
|
||||
}
|
||||
SendResult::SucceededSigcont { ppid, pgid } => {
|
||||
let parent = process::PROCESSES
|
||||
.read()
|
||||
.get(&ppid)
|
||||
.map(Arc::clone)
|
||||
.ok_or(Error::new(ESRCH))?;
|
||||
let waitpid = Arc::clone(&parent.read().waitpid);
|
||||
waitpid.send(
|
||||
WaitpidKey {
|
||||
pid: Some(proc_info.pid),
|
||||
pgid: Some(pgid),
|
||||
},
|
||||
(proc_info.pid, 0xffff),
|
||||
);
|
||||
// POSIX XSI allows but does not require SIGCONT to send signals to the parent.
|
||||
//send_signal(KillTarget::Process(parent), SIGCHLD, true, killed_self)?;
|
||||
}
|
||||
@@ -479,106 +308,6 @@ pub enum KillMode {
|
||||
Queued(RtSigInfo),
|
||||
}
|
||||
|
||||
pub fn kill(pid: ProcessId, sig: usize, mode: KillMode) -> Result<usize> {
|
||||
let (current_ruid, current_euid, current_pgid, current_pid) = {
|
||||
let process_lock = process::current()?;
|
||||
let process = process_lock.read();
|
||||
(process.ruid, process.euid, process.pgid, process.pid)
|
||||
};
|
||||
let sender = SenderInfo {
|
||||
pid: current_pid.get().try_into().unwrap_or(0),
|
||||
ruid: current_ruid,
|
||||
};
|
||||
|
||||
let mut found = 0;
|
||||
let mut sent = 0;
|
||||
let mut killed_self = false;
|
||||
|
||||
// Non-root users cannot kill arbitrarily.
|
||||
let can_send = |proc_info: &ProcessInfo| {
|
||||
current_euid == 0 || current_euid == proc_info.ruid || current_ruid == proc_info.ruid
|
||||
};
|
||||
|
||||
{
|
||||
let processes = process::PROCESSES.read();
|
||||
|
||||
if pid.get() as isize > 0 {
|
||||
// Send to a single process
|
||||
if let Some(process_lock) = processes.get(&pid).map(Arc::clone) {
|
||||
found += 1;
|
||||
if can_send(&process_lock.read().info) {
|
||||
sent += 1;
|
||||
send_signal(
|
||||
KillTarget::Process(process_lock),
|
||||
sig,
|
||||
mode,
|
||||
false,
|
||||
&mut killed_self,
|
||||
sender,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else if pid.get() == 1_usize.wrapping_neg() {
|
||||
// Send to every process with permission, except for init
|
||||
for (pid, process_lock) in processes.iter() {
|
||||
if pid.get() <= 1 {
|
||||
continue;
|
||||
}
|
||||
found += 1;
|
||||
if can_send(&process_lock.read().info) {
|
||||
sent += 1;
|
||||
send_signal(
|
||||
KillTarget::Process(Arc::clone(process_lock)),
|
||||
sig,
|
||||
mode,
|
||||
false,
|
||||
&mut killed_self,
|
||||
sender,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let pgid = if pid.get() == 0 {
|
||||
current_pgid
|
||||
} else {
|
||||
ProcessId::from(pid.get().wrapping_neg())
|
||||
};
|
||||
|
||||
// Send to every process in the process group whose ID
|
||||
for (_pid, process_lock) in processes.iter() {
|
||||
if process_lock.read().pgid != pgid {
|
||||
continue;
|
||||
}
|
||||
found += 1;
|
||||
|
||||
if can_send(&process_lock.read().info) {
|
||||
sent += 1;
|
||||
send_signal(
|
||||
KillTarget::Process(Arc::clone(process_lock)),
|
||||
sig,
|
||||
mode,
|
||||
false,
|
||||
&mut killed_self,
|
||||
sender,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found == 0 {
|
||||
Err(Error::new(ESRCH))
|
||||
} else if sent == 0 {
|
||||
Err(Error::new(EPERM))
|
||||
} else if killed_self {
|
||||
// Inform userspace it should check its own mask
|
||||
|
||||
Err(Error::new(EINTR))
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mprotect(address: usize, size: usize, flags: MapFlags) -> Result<()> {
|
||||
// println!("mprotect {:#X}, {}, {:#X}", address, size, flags);
|
||||
|
||||
@@ -588,222 +317,6 @@ pub fn mprotect(address: usize, size: usize, flags: MapFlags) -> Result<()> {
|
||||
AddrSpace::current()?.mprotect(span, flags)
|
||||
}
|
||||
|
||||
pub fn setpgid(pid: ProcessId, pgid: ProcessId) -> Result<()> {
|
||||
let current_pid = context::current_pid()?;
|
||||
|
||||
let processes = process::PROCESSES.read();
|
||||
|
||||
let process_lock = if pid.get() == 0 {
|
||||
process::current()?
|
||||
} else {
|
||||
Arc::clone(processes.get(&pid).ok_or(Error::new(ESRCH))?)
|
||||
};
|
||||
|
||||
let mut process = process_lock.write();
|
||||
if process.pid == current_pid || process.ppid == current_pid {
|
||||
if pgid.get() == 0 {
|
||||
process.pgid = process.pid;
|
||||
} else {
|
||||
process.pgid = pgid;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::new(ESRCH))
|
||||
}
|
||||
}
|
||||
|
||||
fn reap(pid: ProcessId) -> Result<ProcessId> {
|
||||
let process_lock = Arc::clone(
|
||||
process::PROCESSES
|
||||
.read()
|
||||
.get(&pid)
|
||||
.ok_or(Error::new(ESRCH))?,
|
||||
);
|
||||
|
||||
// Spin until not running
|
||||
loop {
|
||||
// TODO: exit WaitCondition?
|
||||
{
|
||||
let process = process_lock.read();
|
||||
if process
|
||||
.threads
|
||||
.iter()
|
||||
.all(|t| t.upgrade().map_or(true, |t| !t.read().running))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: context switch?
|
||||
interrupt::pause();
|
||||
}
|
||||
|
||||
let _ = process::PROCESSES
|
||||
.write()
|
||||
.remove(&pid)
|
||||
.ok_or(Error::new(ESRCH))?;
|
||||
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
pub fn waitpid(
|
||||
pid: ProcessId,
|
||||
status_ptr: Option<UserSliceWo>,
|
||||
flags: WaitFlags,
|
||||
) -> Result<ProcessId> {
|
||||
let (ppid, waitpid) = {
|
||||
let process_lock = process::current()?;
|
||||
let process = process_lock.read();
|
||||
(process.pid, Arc::clone(&process.waitpid))
|
||||
};
|
||||
|
||||
let write_status = |value| {
|
||||
status_ptr
|
||||
.map(|ptr| ptr.write_usize(value))
|
||||
.unwrap_or(Ok(()))
|
||||
};
|
||||
|
||||
let grim_reaper = |w_pid: ProcessId, status: usize| -> Option<Result<ProcessId>> {
|
||||
if wifcontinued(status) {
|
||||
if flags & WCONTINUED == WCONTINUED {
|
||||
Some(write_status(status).map(|()| w_pid))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else if wifstopped(status) {
|
||||
if flags & WUNTRACED == WUNTRACED {
|
||||
Some(write_status(status).map(|()| w_pid))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Some(write_status(status).and_then(|()| reap(w_pid)))
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let res_opt = if pid.get() == 0 {
|
||||
// Check for existence of child
|
||||
{
|
||||
let mut found = false;
|
||||
|
||||
let processes = process::PROCESSES.read();
|
||||
for (_id, process_lock) in processes.iter() {
|
||||
let process = process_lock.read();
|
||||
if process.ppid == ppid {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return Err(Error::new(ECHILD));
|
||||
}
|
||||
}
|
||||
if flags & WNOHANG == WNOHANG {
|
||||
if let Some((_wid, (w_pid, status))) = waitpid.receive_any_nonblock() {
|
||||
grim_reaper(w_pid, status)
|
||||
} else {
|
||||
Some(Ok(ProcessId::from(0)))
|
||||
}
|
||||
} else {
|
||||
let (_wid, (w_pid, status)) = waitpid.receive_any("waitpid any");
|
||||
grim_reaper(w_pid, status)
|
||||
}
|
||||
} else if (pid.get() as isize) < 0 {
|
||||
let pgid = ProcessId::from(-(pid.get() as isize) as usize);
|
||||
|
||||
// Check for existence of child in process group PGID
|
||||
{
|
||||
let mut found = false;
|
||||
|
||||
let processes = process::PROCESSES.read();
|
||||
for (_pid, process_lock) in processes.iter() {
|
||||
let process = process_lock.read();
|
||||
if process.pgid == pgid {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return Err(Error::new(ECHILD));
|
||||
}
|
||||
}
|
||||
|
||||
if flags & WNOHANG == WNOHANG {
|
||||
if let Some((w_pid, status)) = waitpid.receive_nonblock(&WaitpidKey {
|
||||
pid: None,
|
||||
pgid: Some(pgid),
|
||||
}) {
|
||||
grim_reaper(w_pid, status)
|
||||
} else {
|
||||
Some(Ok(ProcessId::from(0)))
|
||||
}
|
||||
} else {
|
||||
let (w_pid, status) = waitpid
|
||||
.receive(
|
||||
&WaitpidKey {
|
||||
pid: None,
|
||||
pgid: Some(pgid),
|
||||
},
|
||||
"waitpid pgid",
|
||||
)
|
||||
.ok_or(Error::new(EINTR))?;
|
||||
grim_reaper(w_pid, status)
|
||||
}
|
||||
} else {
|
||||
let status = {
|
||||
let process_lock = Arc::clone(
|
||||
process::PROCESSES
|
||||
.read()
|
||||
.get(&pid)
|
||||
.ok_or(Error::new(ESRCH))?,
|
||||
);
|
||||
let process_guard = process_lock.read();
|
||||
|
||||
if process_guard.ppid != ppid {
|
||||
return Err(Error::new(ECHILD));
|
||||
}
|
||||
process_guard.status
|
||||
};
|
||||
|
||||
if let ProcessStatus::Exited(status) = status {
|
||||
let _ = waitpid.receive_nonblock(&WaitpidKey {
|
||||
pid: Some(pid),
|
||||
pgid: None,
|
||||
});
|
||||
grim_reaper(pid, status)
|
||||
} else if flags & WNOHANG == WNOHANG {
|
||||
let res = waitpid.receive_nonblock(&WaitpidKey {
|
||||
pid: Some(pid),
|
||||
pgid: None,
|
||||
});
|
||||
if let Some((w_pid, status)) = res {
|
||||
grim_reaper(w_pid, status)
|
||||
} else {
|
||||
Some(Ok(ProcessId::from(0)))
|
||||
}
|
||||
} else {
|
||||
let (w_pid, status) = waitpid
|
||||
.receive(
|
||||
&WaitpidKey {
|
||||
pid: Some(pid),
|
||||
pgid: None,
|
||||
},
|
||||
"waitpid pid",
|
||||
)
|
||||
.ok_or(Error::new(EINTR))?;
|
||||
grim_reaper(w_pid, status)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(res) = res_opt {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn usermode_bootstrap(bootstrap: &Bootstrap) {
|
||||
assert_ne!(bootstrap.page_count, 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user