diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index 3615a8a5ee8b0..f4c67b225e6e1 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -9,6 +9,14 @@ use crate::process; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +mod private { + /// This trait being unreachable from outside the crate + /// prevents other implementations of the `ExitStatusExt` trait, + /// which allows potentially adding more trait methods in the future. + #[stable(feature = "none", since = "1.51.0")] + pub trait Sealed {} +} + /// Unix-specific extensions to the [`process::Command`] builder. #[stable(feature = "rust1", since = "1.0.0")] pub trait CommandExt { @@ -163,18 +171,48 @@ impl CommandExt for process::Command { } /// Unix-specific extensions to [`process::ExitStatus`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. #[stable(feature = "rust1", since = "1.0.0")] -pub trait ExitStatusExt { +pub trait ExitStatusExt: private::Sealed { /// Creates a new `ExitStatus` from the raw underlying `i32` return value of /// a process. #[stable(feature = "exit_status_from", since = "1.12.0")] fn from_raw(raw: i32) -> Self; /// If the process was terminated by a signal, returns that signal. + /// + /// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`. #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option; + + /// If the process was terminated by a signal, says whether it dumped core. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn core_dumped(&self) -> bool; + + /// If the process was stopped by a signal, returns that signal. + /// + /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from + /// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn stopped_signal(&self) -> Option; + + /// Whether the process was continued from a stopped status. + /// + /// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call + /// which was passed `WCONTINUED`, was then converted into an `ExitStatus`. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn continued(&self) -> bool; + + /// Returns the underlying raw `wait` status. + #[unstable(feature = "unix_process_wait_more", issue = "80695")] + fn into_raw(self) -> i32; } +#[stable(feature = "none", since = "1.51.0")] +impl private::Sealed for process::ExitStatus {} + #[stable(feature = "rust1", since = "1.0.0")] impl ExitStatusExt for process::ExitStatus { fn from_raw(raw: i32) -> Self { @@ -184,6 +222,22 @@ impl ExitStatusExt for process::ExitStatus { fn signal(&self) -> Option { self.as_inner().signal() } + + fn core_dumped(&self) -> bool { + self.as_inner().core_dumped() + } + + fn stopped_signal(&self) -> Option { + self.as_inner().stopped_signal() + } + + fn continued(&self) -> bool { + self.as_inner().continued() + } + + fn into_raw(self) -> i32 { + self.as_inner().into_raw().into() + } } #[stable(feature = "process_extensions", since = "1.2.0")] diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs index b64636c3f3d15..0d4703d7f503a 100644 --- a/library/std/src/sys/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/unix/process/process_fuchsia.rs @@ -245,6 +245,50 @@ impl ExitStatus { pub fn signal(&self) -> Option { None } + + // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al. + // I infer from the implementation of `success`, `code` and `signal` above that these are not + // available on Fuchsia. + // + // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many + // other things from std::os::unix) properly. This veneer is always going to be a bodge. So + // while I don't know if these implementations are actually correct, I think they will do for + // now at least. + pub fn core_dumped(&self) -> bool { + false + } + pub fn stopped_signal(&self) -> Option { + None + } + pub fn continued(&self) -> bool { + false + } + + pub fn into_raw(&self) -> c_int { + // We don't know what someone who calls into_raw() will do with this value, but it should + // have the conventional Unix representation. Despite the fact that this is not + // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the + // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every + // Unix.) + // + // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may + // do their own shifting and masking, or even pass the status to another computer running a + // different Unix variant. + // + // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` + // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is + // not possible here becaause we must return a c_int because that's what Unix (including + // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't + // necessarily fit. + // + // It seems to me that that the right answer would be to provide std::os::fuchsia with its + // own ExitStatusExt, rather that trying to provide a not very convincing imitation of + // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But + // fixing this up that is beyond the scope of my efforts now. + let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255."); + let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8; + wait_status_as_if_unix + } } /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index a590c74435639..945b43678a919 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -479,7 +479,23 @@ impl ExitStatus { } pub fn signal(&self) -> Option { - if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } + if libc::WIFSIGNALED(self.0) { Some(libc::WTERMSIG(self.0)) } else { None } + } + + pub fn core_dumped(&self) -> bool { + libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0) + } + + pub fn stopped_signal(&self) -> Option { + if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } + } + + pub fn continued(&self) -> bool { + libc::WIFCONTINUED(self.0) + } + + pub fn into_raw(&self) -> c_int { + self.0 } } diff --git a/library/std/src/sys/windows/ext/process.rs b/library/std/src/sys/windows/ext/process.rs index 61e4c6a1d1718..7a92381d6609b 100644 --- a/library/std/src/sys/windows/ext/process.rs +++ b/library/std/src/sys/windows/ext/process.rs @@ -7,6 +7,14 @@ use crate::process; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +mod private { + /// This trait being unreachable from outside the crate + /// prevents other implementations of the `ExitStatusExt` trait, + /// which allows potentially adding more trait methods in the future. + #[stable(feature = "none", since = "1.51.0")] + pub trait Sealed {} +} + #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { @@ -73,8 +81,11 @@ impl IntoRawHandle for process::ChildStderr { } /// Windows-specific extensions to [`process::ExitStatus`]. +/// +/// This trait is sealed: it cannot be implemented outside the standard library. +/// This is so that future additional methods are not breaking changes. #[stable(feature = "exit_status_from", since = "1.12.0")] -pub trait ExitStatusExt { +pub trait ExitStatusExt: private::Sealed { /// Creates a new `ExitStatus` from the raw underlying `u32` return value of /// a process. #[stable(feature = "exit_status_from", since = "1.12.0")] @@ -88,6 +99,9 @@ impl ExitStatusExt for process::ExitStatus { } } +#[stable(feature = "none", since = "1.51.0")] +impl private::Sealed for process::ExitStatus {} + /// Windows-specific extensions to the [`process::Command`] builder. #[stable(feature = "windows_process_extensions", since = "1.16.0")] pub trait CommandExt {