diff --git a/src/jobs.rs b/src/jobs.rs index 958ed5a..962a60e 100644 --- a/src/jobs.rs +++ b/src/jobs.rs @@ -1,3 +1,5 @@ +use std::collections::VecDeque; + use ariadne::Fmt; use scopeguard::defer; use yansi::Color; @@ -10,7 +12,7 @@ use crate::{ prelude::*, procio::{IoMode, borrow_fd}, signal::{disable_reaping, enable_reaping}, - state::{self, ShellParam, set_status, write_jobs, write_vars}, + state::{self, ShellParam, Var, VarFlags, VarKind, set_status, write_jobs, write_vars}, }; pub const SIG_EXIT_OFFSET: i32 = 128; @@ -596,6 +598,26 @@ impl Job { .map(|chld| chld.stat()) .collect::>() } + pub fn pipe_status(stats: &[WtStat]) -> Option> { + if stats.iter() + .any(|stat| matches!(stat, WtStat::StillAlive | WtStat::Continued(_) | WtStat::PtraceSyscall(_))) + || stats.len() <= 1 { + return None; + } + Some(stats.iter() + .map(|stat| { + match stat { + WtStat::Exited(_, code) => *code, + WtStat::Signaled(_, signal, _) => SIG_EXIT_OFFSET + *signal as i32, + WtStat::Stopped(_, signal) => SIG_EXIT_OFFSET + *signal as i32, + WtStat::PtraceEvent(_, signal, _) => SIG_EXIT_OFFSET + *signal as i32, + WtStat::PtraceSyscall(_) | + WtStat::Continued(_) | + WtStat::StillAlive => unreachable!() + } + }) + .collect()) + } pub fn get_pids(&self) -> Vec { self .children @@ -839,22 +861,30 @@ pub fn wait_fg(job: Job, interactive: bool) -> ShResult<()> { enable_reaping(); } let statuses = write_jobs(|j| j.new_fg(job))?; - for status in statuses { - code = code_from_status(&status).unwrap_or(0); + for status in &statuses { + code = code_from_status(status).unwrap_or(0); match status { WtStat::Stopped(_, _) => { was_stopped = true; - write_jobs(|j| j.fg_to_bg(status))?; + write_jobs(|j| j.fg_to_bg(*status))?; } WtStat::Signaled(_, sig, _) => { - if sig == Signal::SIGTSTP { + if *sig == Signal::SIGTSTP { was_stopped = true; - write_jobs(|j| j.fg_to_bg(status))?; + write_jobs(|j| j.fg_to_bg(*status))?; } } _ => { /* Do nothing */ } } } + if let Some(pipe_status) = Job::pipe_status(&statuses) { + let pipe_status = pipe_status + .into_iter() + .map(|s| s.to_string()) + .collect::>(); + + write_vars(|v| v.set_var("PIPESTATUS", VarKind::Arr(pipe_status), VarFlags::NONE))?; + } // If job wasn't stopped (moved to bg), clear the fg slot if !was_stopped { write_jobs(|j| { diff --git a/src/signal.rs b/src/signal.rs index 615a6cd..9870e80 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -1,14 +1,14 @@ -use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}; +use std::{collections::VecDeque, sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}}; use nix::sys::signal::{SaFlags, SigAction, sigaction}; use crate::{ builtin::trap::TrapTarget, - jobs::{JobCmdFlags, JobID, take_term}, + jobs::{Job, JobCmdFlags, JobID, take_term}, libsh::error::{ShErr, ShErrKind, ShResult}, parse::execute::exec_input, prelude::*, - state::{AutoCmd, AutoCmdKind, read_jobs, read_logic, write_jobs, write_meta}, + state::{AutoCmd, AutoCmdKind, VarFlags, VarKind, read_jobs, read_logic, write_jobs, write_meta, write_vars}, }; static SIGNALS: AtomicU64 = AtomicU64::new(0); @@ -316,6 +316,16 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> { let result = read_jobs(|j| j.query(JobID::Pgid(pgid)).cloned()); if let Some(job) = result { let job_complete_msg = job.display(&job_order, JobCmdFlags::PIDS).to_string(); + let statuses = job.get_stats(); + + if let Some(pipe_status) = Job::pipe_status(&statuses) { + let pipe_status = pipe_status + .into_iter() + .map(|s| s.to_string()) + .collect::>(); + + write_vars(|v| v.set_var("PIPESTATUS", VarKind::Arr(pipe_status), VarFlags::NONE))?; + } let post_job_hooks = read_logic(|l| l.get_autocmds(AutoCmdKind::OnJobFinish)); for cmd in post_job_hooks {