Implement SIGCONT+SIGHUP on exit when new pgrp becomes orphaned.

This commit is contained in:
4lDO2
2025-04-20 23:18:55 +02:00
parent 7f08436bb0
commit fd80526dc6
+94 -17
View File
@@ -22,7 +22,7 @@ use hashbrown::{DefaultHashBuilder, HashMap, HashSet};
use redox_rt::proc::FdGuard;
use redox_rt::protocol::{
ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT,
ProcCall, ProcKillTarget, ProcMeta, RtSigInfo, ThreadCall, WaitFlags, SIGCHLD, SIGCONT, SIGHUP,
SIGKILL, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU,
};
use redox_scheme::scheme::{IntoTag, Op, OpCall};
@@ -321,6 +321,7 @@ const NAME_CAPAC: usize = 32;
#[derive(Debug)]
struct Process {
threads: Vec<Rc<RefCell<Thread>>>,
pid: ProcessId,
ppid: ProcessId,
pgid: ProcessId,
sid: ProcessId,
@@ -568,6 +569,7 @@ impl<'a> ProcScheme<'a> {
let thread_weak = Rc::downgrade(&thread);
let process = Rc::new(RefCell::new(Process {
threads: vec![thread],
pid: INIT_PID,
ppid: INIT_PID,
sid: INIT_PID,
pgid: INIT_PID,
@@ -648,6 +650,7 @@ impl<'a> ProcScheme<'a> {
let new_process = Rc::new(RefCell::new(Process {
threads: vec![thread],
ppid: parent_pid,
pid: child_pid,
pgid,
sid,
ruid,
@@ -1032,8 +1035,8 @@ impl<'a> ProcScheme<'a> {
// POSIX: any other process's pgid matches the caller pid
if self
.processes
.values()
.any(|p| p.borrow().pgid == caller_pid)
.iter()
.any(|(pid, rc)| *pid != caller_pid && rc.borrow().pgid == caller_pid)
{
return Err(Error::new(EPERM));
}
@@ -1653,19 +1656,85 @@ impl<'a> ProcScheme<'a> {
if let Some(parent_rc) = self.processes.get(&ppid) {
if let Some(init_rc) = self.processes.get(&INIT_PID) {
let mut init = init_rc.borrow_mut();
awoken.extend(init_rc.borrow_mut().waitpid_waiting.drain(..));
// TODO(opt): Store list of children in each process?
let children_iter = || {
self.processes
.values()
.filter(|p| !Rc::ptr_eq(p, init_rc))
.filter(|p| p.borrow().ppid == current_pid)
};
// TODO(opt): Avoid allocation?
let affected_pgids = children_iter()
.map(|child_rc| {
let child_pgid = child_rc.borrow().pgid;
(child_pgid, self.pgrp_is_orphaned(child_pgid))
})
.chain(Some((pgid, self.pgrp_is_orphaned(pgid))))
.collect::<HashMap<ProcessId, Option<bool>>>();
// Transfer children to init
for child_rc in self
.processes
.values()
.filter(|p| !Rc::ptr_eq(p, init_rc))
.filter(|p| p.borrow().ppid == current_pid)
{
for child_rc in children_iter() {
let mut child = child_rc.borrow_mut();
log::trace!(
"Reparenting {:?} (ppid {:?}) => {:?}",
child.pid,
child.ppid,
INIT_PID
);
child.ppid = INIT_PID;
init.waitpid.append(&mut child.waitpid);
init_rc.borrow_mut().waitpid.append(&mut child.waitpid);
drop(child);
}
// Check if any process group ID would become orphaned as a result of
// this exit.
for (affected_pgid, was_orphaned) in affected_pgids {
let is_orphaned = self.pgrp_is_orphaned(affected_pgid);
if !was_orphaned.unwrap_or(false)
&& is_orphaned.unwrap_or(false)
&& let Some(group) =
self.groups.get(&affected_pgid).map(|r| r.borrow())
{
for process_rc in
group.processes.iter().filter_map(|w| Weak::upgrade(&w))
{
if !matches!(
process_rc.borrow().status,
ProcessStatus::Stopped(_)
) {
continue;
}
let sighup_pid = process_rc.borrow().pid;
log::trace!("SENDING SIGCONT TO {sighup_pid:?}");
if let Err(err) = self.on_send_sig(
INIT_PID,
KillTarget::Proc(sighup_pid),
SIGCONT as u8,
&mut false,
KillMode::Idempotent,
false,
awoken,
) {
log::warn!("Failed to send newly-orphaned-pgid SIGHUP to PID {sighup_pid:?}: {err}");
}
log::trace!("SENDING SIGHUP TO {sighup_pid:?}");
if let Err(err) = self.on_send_sig(
INIT_PID,
KillTarget::Proc(sighup_pid),
SIGHUP as u8,
&mut false,
KillMode::Idempotent,
false,
awoken,
) {
log::warn!("Failed to send newly-orphaned-pgid SIGHUP to PID {sighup_pid:?}: {err}");
}
}
}
}
awoken.extend(init.waitpid_waiting.drain(..));
}
let mut parent = parent_rc.borrow_mut();
@@ -2306,11 +2375,19 @@ impl<'a> ProcScheme<'a> {
// POSIX defines orphaned process groups as those where
//
// forall process in group,
// process's parent pgid == process's pgid
// OR
// process's session id != process's session id
still_true &= parent.pgid == process.pgid || parent.sid != process.sid;
// forall process in group, parent = process.parent,
// parent's pgid == process's pgid
// OR
// parent's session id != process's session id
let cond = parent.pgid == process.pgid || parent.sid != process.sid;
if !cond {
log::trace!(
"COUNTEREXAMPLE: process {:#?} parent {:#?}",
process,
parent
);
}
still_true &= cond;
}
Some(still_true)
}