Rollup merge of #79982 - ijackson:exit-status, r=dtolnay

Add missing methods to unix ExitStatusExt

These are the methods corresponding to the remaining exit status examination macros from `wait.h`.  `WCOREDUMP` isn't in SuS but is it is very standard.  I have not done portability testing to see if this builds everywhere, so I may need to Do Something if it doesn't.

There is also a bugfix and doc improvement to `.signal()`, and an `.into_raw()` accessor.

This would fix #73128 and fix #73129.  Please let me know if you like this direction, and if so I will open the tracking issue and so on.

If this MR goes well, I may tackle #73125 next - I have an idea for how to do it.
This commit is contained in:
Mara Bos 2021-01-14 17:59:53 +00:00 committed by GitHub
commit 8ac21fb201
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 131 additions and 3 deletions

View File

@ -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<i32>;
/// 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<i32>;
/// 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<i32> {
self.as_inner().signal()
}
fn core_dumped(&self) -> bool {
self.as_inner().core_dumped()
}
fn stopped_signal(&self) -> Option<i32> {
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")]

View File

@ -245,6 +245,50 @@ impl ExitStatus {
pub fn signal(&self) -> Option<i32> {
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<i32> {
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.

View File

@ -479,7 +479,23 @@ impl ExitStatus {
}
pub fn signal(&self) -> Option<i32> {
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<i32> {
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
}
}

View File

@ -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 {