/ src / lib.rs
lib.rs
  1  //! Explain a wait status.
  2  //!
  3  //! A wait status is returned by one of the `wait` family of system
  4  //! calls. It contains the exit code (given to the `_exit` system
  5  //! call), but also other information.
  6  //!
  7  //! This crate, or its tests, may be Linux specific.
  8  
  9  use serde::Serialize;
 10  
 11  /// Explained wait status.
 12  #[derive(Debug, Serialize)]
 13  pub struct Explained {
 14      /// The wait status given to [`Explained::new`].
 15      pub wait_status: i32,
 16  
 17      /// Did the process exit normally? (If not, it may be running, or
 18      /// was terminated by a signal.)
 19      pub exited: bool,
 20  
 21      /// Exit status code, if `exited` is true.
 22      pub exit_status: Option<i32>,
 23  
 24      /// Was the process terminated by signal?
 25      pub signaled: bool,
 26  
 27      /// If `signaled` is `true`, what signal?
 28      pub termsig: Option<i32>,
 29  
 30      /// If `signaled` is `true`, did process produce a core dump?
 31      pub coredump: bool,
 32  
 33      /// Was the process stopped by a signal?
 34      pub stopped: bool,
 35  
 36      /// If `stopped` is `true`, what signal?
 37      pub stopsig: Option<i32>,
 38  
 39      /// Was stopped process resumed by signal **SIGCONT**?
 40      pub continued: bool,
 41  }
 42  
 43  impl Explained {
 44      pub fn new(wait_status: i32) -> Self {
 45          let exited = libc::WIFEXITED(wait_status);
 46          let signaled = libc::WIFSIGNALED(wait_status);
 47          let stopped = libc::WIFSTOPPED(wait_status);
 48  
 49          Self {
 50              wait_status,
 51              exited,
 52              exit_status: if exited {
 53                  Some(libc::WEXITSTATUS(wait_status))
 54              } else {
 55                  None
 56              },
 57              signaled,
 58              termsig: if signaled {
 59                  Some(libc::WTERMSIG(wait_status))
 60              } else {
 61                  None
 62              },
 63              coredump: signaled && libc::WCOREDUMP(wait_status),
 64              stopped,
 65              stopsig: if stopped {
 66                  Some(libc::WSTOPSIG(wait_status))
 67              } else {
 68                  None
 69              },
 70              continued: libc::WIFCONTINUED(wait_status),
 71          }
 72      }
 73  }
 74  
 75  #[cfg(test)]
 76  mod test {
 77      use super::*;
 78  
 79      #[test]
 80      fn normal_exits() {
 81          for i in 0..255 {
 82              // On Linux, for normal exit codes, wait status is shifted by 8 bits.
 83              let wait_status = i << 8;
 84              let ex = Explained::new(wait_status);
 85  
 86              // Print the result to help debug test failures.
 87              println!("{i} => {ex:#?}");
 88  
 89              assert_eq!(ex.wait_status, wait_status);
 90              assert!(ex.exited);
 91              assert_eq!(ex.exit_status, Some(i));
 92              assert!(!ex.signaled);
 93              assert_eq!(ex.termsig, None);
 94              assert!(!ex.coredump);
 95              assert!(!ex.stopped);
 96              assert_eq!(ex.stopsig, None);
 97              assert!(!ex.continued);
 98          }
 99      }
100  }