Use strongly typed Option<Pid> for event handler

This caught an incorrect description for process/job exit handlers for ANY_PID
(now removed) which has been replaced with a message stating the handler is for
any process exit event.
This commit is contained in:
Mahmoud Al-Qudsi 2024-11-11 14:27:37 -06:00
parent 95ac51101e
commit 2cf4b12d41
6 changed files with 50 additions and 40 deletions

View file

@ -50,7 +50,7 @@ use fish::{
printf, printf,
proc::{ proc::{
get_login, is_interactive_session, mark_login, mark_no_exec, proc_init, get_login, is_interactive_session, mark_login, mark_no_exec, proc_init,
set_interactive_session, set_interactive_session, Pid,
}, },
reader::{reader_init, reader_read, term_copy_modes}, reader::{reader_init, reader_read, term_copy_modes},
signal::{signal_clear_cancel, signal_unblock_all}, signal::{signal_clear_cancel, signal_unblock_all},
@ -695,7 +695,10 @@ fn throwing_main() -> i32 {
parser.get_last_status() parser.get_last_status()
}; };
event::fire(parser, Event::process_exit(getpid(), exit_status)); event::fire(
parser,
Event::process_exit(Pid::new(getpid()).unwrap(), exit_status),
);
// Trigger any exit handlers. // Trigger any exit handlers.
event::fire_generic( event::fire_generic(

View file

@ -10,6 +10,7 @@ use crate::global_safety::RelaxedAtomicBool;
use crate::nix::getpid; use crate::nix::getpid;
use crate::parse_tree::NodeRef; use crate::parse_tree::NodeRef;
use crate::parser_keywords::parser_keywords_is_reserved; use crate::parser_keywords::parser_keywords_is_reserved;
use crate::proc::Pid;
use crate::signal::Signal; use crate::signal::Signal;
use std::sync::Arc; use std::sync::Arc;
@ -148,7 +149,7 @@ fn parse_cmd_opts(
} }
e = EventDescription::CallerExit { caller_id }; e = EventDescription::CallerExit { caller_id };
} else if opt == 'p' && woptarg == "%self" { } else if opt == 'p' && woptarg == "%self" {
let pid: i32 = getpid(); let pid = Pid::new(getpid());
e = EventDescription::ProcessExit { pid }; e = EventDescription::ProcessExit { pid };
} else { } else {
let Ok(pid @ 0..) = fish_wcstoi(woptarg) else { let Ok(pid @ 0..) = fish_wcstoi(woptarg) else {
@ -160,12 +161,12 @@ fn parse_cmd_opts(
return STATUS_INVALID_ARGS; return STATUS_INVALID_ARGS;
}; };
if opt == 'p' { if opt == 'p' {
e = EventDescription::ProcessExit { pid }; e = EventDescription::ProcessExit { pid: Pid::new(pid) };
} else { } else {
// TODO: rationalize why a default of 0 is sensible. // TODO: rationalize why a default of 0 is sensible.
let internal_job_id = job_id_for_pid(pid, parser).unwrap_or(0); let internal_job_id = job_id_for_pid(pid, parser).unwrap_or(0);
e = EventDescription::JobExit { e = EventDescription::JobExit {
pid, pid: Pid::new(pid),
internal_job_id, internal_job_id,
}; };
} }
@ -370,14 +371,14 @@ pub fn function(
// process has already exited, run it immediately (#7210). // process has already exited, run it immediately (#7210).
for ed in &opts.events { for ed in &opts.events {
match *ed { match *ed {
EventDescription::ProcessExit { pid } if pid != event::ANY_PID => { EventDescription::ProcessExit { pid: Some(pid) } => {
let wh = parser.get_wait_handles().get_by_pid(pid); let wh = parser.get_wait_handles().get_by_pid(pid.as_pid_t());
if let Some(status) = wh.and_then(|wh| wh.status()) { if let Some(status) = wh.and_then(|wh| wh.status()) {
event::fire(parser, event::Event::process_exit(pid, status)); event::fire(parser, event::Event::process_exit(pid, status));
} }
} }
EventDescription::JobExit { pid, .. } if pid != event::ANY_PID => { EventDescription::JobExit { pid: Some(pid), .. } => {
let wh = parser.get_wait_handles().get_by_pid(pid); let wh = parser.get_wait_handles().get_by_pid(pid.as_pid_t());
if let Some(wh) = wh { if let Some(wh) = wh {
if wh.is_completed() { if wh.is_completed() {
event::fire(parser, event::Event::job_exit(pid, wh.internal_job_id)); event::fire(parser, event::Event::job_exit(pid, wh.internal_job_id));

View file

@ -37,7 +37,7 @@ fn cpu_use(j: &Job) -> f64 {
let mut u = 0.0; let mut u = 0.0;
for p in j.external_procs() { for p in j.external_procs() {
let now = timef(); let now = timef();
let jiffies = proc_get_jiffies(p.pid.load().unwrap().as_pid_t()); let jiffies = proc_get_jiffies(p.pid.load().unwrap());
let last_jiffies = p.last_times.get().jiffies; let last_jiffies = p.last_times.get().jiffies;
let since = now - last_jiffies as f64; let since = now - last_jiffies as f64;
if since > 0.0 && jiffies > last_jiffies { if since > 0.0 && jiffies > last_jiffies {

View file

@ -4,7 +4,6 @@
//! defined when these functions produce output or perform memory allocations, since such functions //! defined when these functions produce output or perform memory allocations, since such functions
//! may not be safely called by signal handlers. //! may not be safely called by signal handlers.
use libc::pid_t;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -13,6 +12,7 @@ use crate::flog::FLOG;
use crate::io::{IoChain, IoStreams}; use crate::io::{IoChain, IoStreams};
use crate::job_group::MaybeJobId; use crate::job_group::MaybeJobId;
use crate::parser::{Block, Parser}; use crate::parser::{Block, Parser};
use crate::proc::Pid;
use crate::signal::{signal_check_cancel, signal_handle, Signal}; use crate::signal::{signal_check_cancel, signal_handle, Signal};
use crate::termsize; use crate::termsize;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
@ -27,8 +27,6 @@ pub enum event_type_t {
generic, generic,
} }
pub const ANY_PID: pid_t = 0;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum EventDescription { pub enum EventDescription {
/// Matches any event type (not always any event, as the function name may limit the choice as /// Matches any event type (not always any event, as the function name may limit the choice as
@ -40,13 +38,13 @@ pub enum EventDescription {
Variable { name: WString }, Variable { name: WString },
/// An event triggered by a process exit. /// An event triggered by a process exit.
ProcessExit { ProcessExit {
/// Process ID. Use [`ANY_PID`] to match any pid. /// Process ID. Use [`None`] to match any pid.
pid: pid_t, pid: Option<Pid>,
}, },
/// An event triggered by a job exit. /// An event triggered by a job exit.
JobExit { JobExit {
/// pid requested by the event, or [`ANY_PID`] for all. /// pid requested by the event, or [`None`] for all.
pid: pid_t, pid: Option<Pid>,
/// `internal_job_id` of the job to match. /// `internal_job_id` of the job to match.
/// If this is 0, we match either all jobs (`pid == ANY_PID`) or no jobs (otherwise). /// If this is 0, we match either all jobs (`pid == ANY_PID`) or no jobs (otherwise).
internal_job_id: u64, internal_job_id: u64,
@ -147,8 +145,8 @@ impl EventHandler {
/// Return true if a handler is "one shot": it fires at most once. /// Return true if a handler is "one shot": it fires at most once.
fn is_one_shot(&self) -> bool { fn is_one_shot(&self) -> bool {
match self.desc { match self.desc {
EventDescription::ProcessExit { pid } => pid != ANY_PID, EventDescription::ProcessExit { pid } => pid.is_some(),
EventDescription::JobExit { pid, .. } => pid != ANY_PID, EventDescription::JobExit { pid, .. } => pid.is_some(),
EventDescription::CallerExit { .. } => true, EventDescription::CallerExit { .. } => true,
EventDescription::Signal { .. } EventDescription::Signal { .. }
| EventDescription::Variable { .. } | EventDescription::Variable { .. }
@ -171,7 +169,7 @@ impl EventHandler {
( (
EventDescription::ProcessExit { pid }, EventDescription::ProcessExit { pid },
EventDescription::ProcessExit { pid: ev_pid }, EventDescription::ProcessExit { pid: ev_pid },
) => *pid == ANY_PID || pid == ev_pid, ) => pid.is_none() || pid == ev_pid,
( (
EventDescription::JobExit { EventDescription::JobExit {
pid, pid,
@ -181,7 +179,7 @@ impl EventHandler {
internal_job_id: ev_internal_job_id, internal_job_id: ev_internal_job_id,
.. ..
}, },
) => *pid == ANY_PID || internal_job_id == ev_internal_job_id, ) => pid.is_none() || internal_job_id == ev_internal_job_id,
( (
EventDescription::CallerExit { caller_id }, EventDescription::CallerExit { caller_id },
EventDescription::CallerExit { EventDescription::CallerExit {
@ -226,9 +224,9 @@ impl Event {
} }
} }
pub fn process_exit(pid: pid_t, status: i32) -> Self { pub fn process_exit(pid: Pid, status: i32) -> Self {
Self { Self {
desc: EventDescription::ProcessExit { pid }, desc: EventDescription::ProcessExit { pid: Some(pid) },
arguments: vec![ arguments: vec![
"PROCESS_EXIT".into(), "PROCESS_EXIT".into(),
pid.to_string().into(), pid.to_string().into(),
@ -237,10 +235,10 @@ impl Event {
} }
} }
pub fn job_exit(pgid: pid_t, jid: u64) -> Self { pub fn job_exit(pgid: Pid, jid: u64) -> Self {
Self { Self {
desc: EventDescription::JobExit { desc: EventDescription::JobExit {
pid: pgid, pid: Some(pgid),
internal_job_id: jid, internal_job_id: jid,
}, },
arguments: vec![ arguments: vec![
@ -382,12 +380,19 @@ pub fn get_desc(parser: &Parser, evt: &Event) -> WString {
format!("signal handler for {} ({})", signal.name(), signal.desc(),) format!("signal handler for {} ({})", signal.name(), signal.desc(),)
} }
EventDescription::Variable { name } => format!("handler for variable '{name}'"), EventDescription::Variable { name } => format!("handler for variable '{name}'"),
EventDescription::ProcessExit { pid } => format!("exit handler for process {pid}"), EventDescription::ProcessExit { pid: None } => format!("exit handler for any process"),
EventDescription::ProcessExit { pid: Some(pid) } => {
format!("exit handler for process {pid}")
}
EventDescription::JobExit { pid, .. } => { EventDescription::JobExit { pid, .. } => {
if let Some(job) = parser.job_get_from_pid(*pid) { if let Some(pid) = pid {
format!("exit handler for job {}, '{}'", job.job_id(), job.command()) if let Some(job) = parser.job_get_from_pid(pid.as_pid_t()) {
format!("exit handler for job {}, '{}'", job.job_id(), job.command())
} else {
format!("exit handler for job with pid {pid}")
}
} else { } else {
format!("exit handler for job with pid {pid}") format!("exit handler for any job")
} }
} }
EventDescription::CallerExit { .. } => { EventDescription::CallerExit { .. } => {

View file

@ -442,9 +442,11 @@ impl FunctionProperties {
sprintf!(=> &mut out, " --on-variable %ls", name); sprintf!(=> &mut out, " --on-variable %ls", name);
} }
EventDescription::ProcessExit { pid } => { EventDescription::ProcessExit { pid } => {
sprintf!(=> &mut out, " --on-process-exit %d", pid); let pid = pid.map(|p| p.get()).unwrap_or(0);
sprintf!(=> &mut out, " --on-process-exit %d", pid)
} }
EventDescription::JobExit { pid, .. } => { EventDescription::JobExit { pid, .. } => {
let pid = pid.map(|p| p.get()).unwrap_or(0);
sprintf!(=> &mut out, " --on-job-exit %d", pid); sprintf!(=> &mut out, " --on-job-exit %d", pid);
} }
EventDescription::CallerExit { .. } => { EventDescription::CallerExit { .. } => {

View file

@ -512,7 +512,7 @@ impl Drop for TtyTransfer {
/// A type-safe equivalent to [`libc::pid_t`]. /// A type-safe equivalent to [`libc::pid_t`].
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Pid(NonZeroU32); pub struct Pid(NonZeroU32);
impl Pid { impl Pid {
@ -1276,8 +1276,8 @@ pub fn print_exit_warning_for_jobs(jobs: &JobList) {
/// Use the procfs filesystem to look up how many jiffies of cpu time was used by a given pid. This /// Use the procfs filesystem to look up how many jiffies of cpu time was used by a given pid. This
/// function is only available on systems with the procfs file entry 'stat', i.e. Linux. /// function is only available on systems with the procfs file entry 'stat', i.e. Linux.
pub fn proc_get_jiffies(inpid: libc::pid_t) -> ClockTicks { pub fn proc_get_jiffies(inpid: Pid) -> ClockTicks {
if inpid <= 0 || !have_proc_stat() { if !have_proc_stat() {
return 0; return 0;
} }
@ -1315,7 +1315,7 @@ pub fn proc_update_jiffies(parser: &Parser) {
for p in job.external_procs() { for p in job.external_procs() {
p.last_times.replace(ProcTimes { p.last_times.replace(ProcTimes {
time: timef(), time: timef(),
jiffies: proc_get_jiffies(p.pid.load().map(|p| p.as_pid_t()).unwrap()), jiffies: proc_get_jiffies(p.pid.load().unwrap()),
}); });
} }
} }
@ -1414,7 +1414,7 @@ pub fn hup_jobs(jobs: &JobList) {
pub fn add_disowned_job(j: &Job) { pub fn add_disowned_job(j: &Job) {
let mut disowned_pids = DISOWNED_PIDS.get().borrow_mut(); let mut disowned_pids = DISOWNED_PIDS.get().borrow_mut();
for process in j.external_procs() { for process in j.external_procs() {
disowned_pids.push(process.pid().unwrap().as_pid_t()); disowned_pids.push(process.pid().unwrap());
} }
} }
@ -1425,7 +1425,7 @@ fn reap_disowned_pids() {
// if it has changed or an error occurs (presumably ECHILD because the child does not exist) // if it has changed or an error occurs (presumably ECHILD because the child does not exist)
disowned_pids.retain(|pid| { disowned_pids.retain(|pid| {
let mut status: libc::c_int = 0; let mut status: libc::c_int = 0;
let ret = unsafe { libc::waitpid(*pid, &mut status, WNOHANG) }; let ret = unsafe { libc::waitpid(pid.as_pid_t(), &mut status, WNOHANG) };
if ret > 0 { if ret > 0 {
FLOGF!(proc_reap_external, "Reaped disowned PID or PGID %d", pid); FLOGF!(proc_reap_external, "Reaped disowned PID or PGID %d", pid);
} }
@ -1435,8 +1435,7 @@ fn reap_disowned_pids() {
/// A list of pids that have been disowned. They are kept around until either they exit or /// A list of pids that have been disowned. They are kept around until either they exit or
/// we exit. Poll these from time-to-time to prevent zombie processes from happening (#5342). /// we exit. Poll these from time-to-time to prevent zombie processes from happening (#5342).
static DISOWNED_PIDS: MainThread<RefCell<Vec<libc::pid_t>>> = static DISOWNED_PIDS: MainThread<RefCell<Vec<Pid>>> = MainThread::new(RefCell::new(Vec::new()));
MainThread::new(RefCell::new(Vec::new()));
/// See if any reapable processes have exited, and mark them accordingly. /// See if any reapable processes have exited, and mark them accordingly.
/// \param block_ok if no reapable processes have exited, block until one is (or until we receive a /// \param block_ok if no reapable processes have exited, block until one is (or until we receive a
@ -1598,7 +1597,7 @@ fn generate_process_exit_events(j: &Job, out_evts: &mut Vec<Event>) {
if p.is_completed() && !p.posted_proc_exit.load() { if p.is_completed() && !p.posted_proc_exit.load() {
p.posted_proc_exit.store(true); p.posted_proc_exit.store(true);
out_evts.push(Event::process_exit( out_evts.push(Event::process_exit(
p.pid().unwrap().as_pid_t(), p.pid().unwrap(),
p.status.status_value(), p.status.status_value(),
)); ));
} }
@ -1613,7 +1612,7 @@ fn generate_job_exit_events(j: &Job, out_evts: &mut Vec<Event>) {
// job_exit events. // job_exit events.
if j.posts_job_exit_events() { if j.posts_job_exit_events() {
if let Some(last_pid) = j.get_last_pid() { if let Some(last_pid) = j.get_last_pid() {
out_evts.push(Event::job_exit(last_pid.as_pid_t(), j.internal_job_id)); out_evts.push(Event::job_exit(last_pid, j.internal_job_id));
} }
} }
} }