Skip to content

Commit aacb26d

Browse files
authored
Rollup merge of rust-lang#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 rust-lang#73128 and fix rust-lang#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 rust-lang#73125 next - I have an idea for how to do it.
2 parents 05f5db4 + a8d0161 commit aacb26d

File tree

4 files changed

+131
-3
lines changed

4 files changed

+131
-3
lines changed

library/std/src/sys/unix/ext/process.rs

+55-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ use crate::process;
99
use crate::sys;
1010
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
1111

12+
mod private {
13+
/// This trait being unreachable from outside the crate
14+
/// prevents other implementations of the `ExitStatusExt` trait,
15+
/// which allows potentially adding more trait methods in the future.
16+
#[stable(feature = "none", since = "1.51.0")]
17+
pub trait Sealed {}
18+
}
19+
1220
/// Unix-specific extensions to the [`process::Command`] builder.
1321
#[stable(feature = "rust1", since = "1.0.0")]
1422
pub trait CommandExt {
@@ -163,18 +171,48 @@ impl CommandExt for process::Command {
163171
}
164172

165173
/// Unix-specific extensions to [`process::ExitStatus`].
174+
///
175+
/// This trait is sealed: it cannot be implemented outside the standard library.
176+
/// This is so that future additional methods are not breaking changes.
166177
#[stable(feature = "rust1", since = "1.0.0")]
167-
pub trait ExitStatusExt {
178+
pub trait ExitStatusExt: private::Sealed {
168179
/// Creates a new `ExitStatus` from the raw underlying `i32` return value of
169180
/// a process.
170181
#[stable(feature = "exit_status_from", since = "1.12.0")]
171182
fn from_raw(raw: i32) -> Self;
172183

173184
/// If the process was terminated by a signal, returns that signal.
185+
///
186+
/// In other words, if `WIFSIGNALED`, this returns `WTERMSIG`.
174187
#[stable(feature = "rust1", since = "1.0.0")]
175188
fn signal(&self) -> Option<i32>;
189+
190+
/// If the process was terminated by a signal, says whether it dumped core.
191+
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
192+
fn core_dumped(&self) -> bool;
193+
194+
/// If the process was stopped by a signal, returns that signal.
195+
///
196+
/// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`. This is only possible if the status came from
197+
/// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`.
198+
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
199+
fn stopped_signal(&self) -> Option<i32>;
200+
201+
/// Whether the process was continued from a stopped status.
202+
///
203+
/// Ie, `WIFCONTINUED`. This is only possible if the status came from a `wait` system call
204+
/// which was passed `WCONTINUED`, was then converted into an `ExitStatus`.
205+
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
206+
fn continued(&self) -> bool;
207+
208+
/// Returns the underlying raw `wait` status.
209+
#[unstable(feature = "unix_process_wait_more", issue = "80695")]
210+
fn into_raw(self) -> i32;
176211
}
177212

213+
#[stable(feature = "none", since = "1.51.0")]
214+
impl private::Sealed for process::ExitStatus {}
215+
178216
#[stable(feature = "rust1", since = "1.0.0")]
179217
impl ExitStatusExt for process::ExitStatus {
180218
fn from_raw(raw: i32) -> Self {
@@ -184,6 +222,22 @@ impl ExitStatusExt for process::ExitStatus {
184222
fn signal(&self) -> Option<i32> {
185223
self.as_inner().signal()
186224
}
225+
226+
fn core_dumped(&self) -> bool {
227+
self.as_inner().core_dumped()
228+
}
229+
230+
fn stopped_signal(&self) -> Option<i32> {
231+
self.as_inner().stopped_signal()
232+
}
233+
234+
fn continued(&self) -> bool {
235+
self.as_inner().continued()
236+
}
237+
238+
fn into_raw(self) -> i32 {
239+
self.as_inner().into_raw().into()
240+
}
187241
}
188242

189243
#[stable(feature = "process_extensions", since = "1.2.0")]

library/std/src/sys/unix/process/process_fuchsia.rs

+44
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,50 @@ impl ExitStatus {
245245
pub fn signal(&self) -> Option<i32> {
246246
None
247247
}
248+
249+
// FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al.
250+
// I infer from the implementation of `success`, `code` and `signal` above that these are not
251+
// available on Fuchsia.
252+
//
253+
// It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
254+
// other things from std::os::unix) properly. This veneer is always going to be a bodge. So
255+
// while I don't know if these implementations are actually correct, I think they will do for
256+
// now at least.
257+
pub fn core_dumped(&self) -> bool {
258+
false
259+
}
260+
pub fn stopped_signal(&self) -> Option<i32> {
261+
None
262+
}
263+
pub fn continued(&self) -> bool {
264+
false
265+
}
266+
267+
pub fn into_raw(&self) -> c_int {
268+
// We don't know what someone who calls into_raw() will do with this value, but it should
269+
// have the conventional Unix representation. Despite the fact that this is not
270+
// standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
271+
// same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
272+
// Unix.)
273+
//
274+
// The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
275+
// do their own shifting and masking, or even pass the status to another computer running a
276+
// different Unix variant.
277+
//
278+
// The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
279+
// will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
280+
// not possible here becaause we must return a c_int because that's what Unix (including
281+
// SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
282+
// necessarily fit.
283+
//
284+
// It seems to me that that the right answer would be to provide std::os::fuchsia with its
285+
// own ExitStatusExt, rather that trying to provide a not very convincing imitation of
286+
// Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
287+
// fixing this up that is beyond the scope of my efforts now.
288+
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.");
289+
let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
290+
wait_status_as_if_unix
291+
}
248292
}
249293

250294
/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.

library/std/src/sys/unix/process/process_unix.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,23 @@ impl ExitStatus {
479479
}
480480

481481
pub fn signal(&self) -> Option<i32> {
482-
if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
482+
if libc::WIFSIGNALED(self.0) { Some(libc::WTERMSIG(self.0)) } else { None }
483+
}
484+
485+
pub fn core_dumped(&self) -> bool {
486+
libc::WIFSIGNALED(self.0) && libc::WCOREDUMP(self.0)
487+
}
488+
489+
pub fn stopped_signal(&self) -> Option<i32> {
490+
if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
491+
}
492+
493+
pub fn continued(&self) -> bool {
494+
libc::WIFCONTINUED(self.0)
495+
}
496+
497+
pub fn into_raw(&self) -> c_int {
498+
self.0
483499
}
484500
}
485501

library/std/src/sys/windows/ext/process.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ use crate::process;
77
use crate::sys;
88
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
99

10+
mod private {
11+
/// This trait being unreachable from outside the crate
12+
/// prevents other implementations of the `ExitStatusExt` trait,
13+
/// which allows potentially adding more trait methods in the future.
14+
#[stable(feature = "none", since = "1.51.0")]
15+
pub trait Sealed {}
16+
}
17+
1018
#[stable(feature = "process_extensions", since = "1.2.0")]
1119
impl FromRawHandle for process::Stdio {
1220
unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio {
@@ -73,8 +81,11 @@ impl IntoRawHandle for process::ChildStderr {
7381
}
7482

7583
/// Windows-specific extensions to [`process::ExitStatus`].
84+
///
85+
/// This trait is sealed: it cannot be implemented outside the standard library.
86+
/// This is so that future additional methods are not breaking changes.
7687
#[stable(feature = "exit_status_from", since = "1.12.0")]
77-
pub trait ExitStatusExt {
88+
pub trait ExitStatusExt: private::Sealed {
7889
/// Creates a new `ExitStatus` from the raw underlying `u32` return value of
7990
/// a process.
8091
#[stable(feature = "exit_status_from", since = "1.12.0")]
@@ -88,6 +99,9 @@ impl ExitStatusExt for process::ExitStatus {
8899
}
89100
}
90101

102+
#[stable(feature = "none", since = "1.51.0")]
103+
impl private::Sealed for process::ExitStatus {}
104+
91105
/// Windows-specific extensions to the [`process::Command`] builder.
92106
#[stable(feature = "windows_process_extensions", since = "1.16.0")]
93107
pub trait CommandExt {

0 commit comments

Comments
 (0)