1 use crate::convert
::{TryFrom, TryInto}
;
3 use crate::io
::{self, Error, ErrorKind}
;
4 use crate::num
::NonZeroI32
;
7 use crate::sys
::process
::process_common
::*;
8 use crate::sys_common
::thread
;
9 use core
::ffi
::NonZero_c_int
;
11 use libc
::{self, c_char, c_int}
;
13 ////////////////////////////////////////////////////////////////////////////////
15 ////////////////////////////////////////////////////////////////////////////////
22 ) -> io
::Result
<(Process
, StdioPipes
)> {
23 use crate::sys
::cvt_r
;
24 let envp
= self.capture_env();
27 return Err(io
::const_io_error
!(
28 ErrorKind
::InvalidInput
,
29 "nul byte found in provided data",
32 let (ours
, theirs
) = self.setup_io(default, needs_stdin
)?
;
33 let mut p
= Process { pid: 0, status: None }
;
40 Err(e
) => return Err(e
.into()),
45 let mut orig_stdin
= libc
::STDIN_FILENO
;
46 let mut orig_stdout
= libc
::STDOUT_FILENO
;
47 let mut orig_stderr
= libc
::STDERR_FILENO
;
49 if let Some(fd
) = theirs
.stdin
.fd() {
50 orig_stdin
= t
!(cvt_r(|| libc
::dup(libc
::STDIN_FILENO
)));
51 t
!(cvt_r(|| libc
::dup2(fd
, libc
::STDIN_FILENO
)));
53 if let Some(fd
) = theirs
.stdout
.fd() {
54 orig_stdout
= t
!(cvt_r(|| libc
::dup(libc
::STDOUT_FILENO
)));
55 t
!(cvt_r(|| libc
::dup2(fd
, libc
::STDOUT_FILENO
)));
57 if let Some(fd
) = theirs
.stderr
.fd() {
58 orig_stderr
= t
!(cvt_r(|| libc
::dup(libc
::STDERR_FILENO
)));
59 t
!(cvt_r(|| libc
::dup2(fd
, libc
::STDERR_FILENO
)));
62 if let Some(ref cwd
) = *self.get_cwd() {
63 t
!(cvt(libc
::chdir(cwd
.as_ptr())));
66 // pre_exec closures are ignored on VxWorks
67 let _
= self.get_closures();
72 .unwrap_or_else(|| *sys
::os
::environ() as *const _
);
73 let stack_size
= thread
::min_stack();
75 // ensure that access to the environment is synchronized
76 let _lock
= sys
::os
::env_read_lock();
78 let ret
= libc
::rtpSpawn(
79 self.get_program_cstr().as_ptr(),
80 self.get_argv().as_ptr() as *mut *const c_char
, // argv
81 c_envp
as *mut *const c_char
,
82 100 as c_int
, // initial priority
83 stack_size
, // initial stack size.
88 // Because FileDesc was not used, each duplicated file descriptor
89 // needs to be closed manually
90 if orig_stdin
!= libc
::STDIN_FILENO
{
91 t
!(cvt_r(|| libc
::dup2(orig_stdin
, libc
::STDIN_FILENO
)));
92 libc
::close(orig_stdin
);
94 if orig_stdout
!= libc
::STDOUT_FILENO
{
95 t
!(cvt_r(|| libc
::dup2(orig_stdout
, libc
::STDOUT_FILENO
)));
96 libc
::close(orig_stdout
);
98 if orig_stderr
!= libc
::STDERR_FILENO
{
99 t
!(cvt_r(|| libc
::dup2(orig_stderr
, libc
::STDERR_FILENO
)));
100 libc
::close(orig_stderr
);
103 if ret
!= libc
::RTP_ID_ERROR
{
107 Err(io
::Error
::last_os_error())
112 pub fn exec(&mut self, default: Stdio
) -> io
::Error
{
113 let ret
= Command
::spawn(self, default, false);
116 let mut status
= 0 as c_int
;
117 libc
::waitpid(t
.0.pid
, &mut status
, 0);
125 ////////////////////////////////////////////////////////////////////////////////
127 ////////////////////////////////////////////////////////////////////////////////
129 /// The unique id of the process (this should never be negative).
132 status
: Option
<ExitStatus
>,
136 pub fn id(&self) -> u32 {
140 pub fn kill(&mut self) -> io
::Result
<()> {
141 // If we've already waited on this process then the pid can be recycled
142 // and used for another process, and we probably shouldn't be killing
143 // random processes, so just return an error.
144 if self.status
.is_some() {
145 Err(io
::const_io_error
!(
146 ErrorKind
::InvalidInput
,
147 "invalid argument: can't kill an exited process",
150 cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }
).map(drop
)
154 pub fn wait(&mut self) -> io
::Result
<ExitStatus
> {
155 use crate::sys
::cvt_r
;
156 if let Some(status
) = self.status
{
159 let mut status
= 0 as c_int
;
160 cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }
)?
;
161 self.status
= Some(ExitStatus
::new(status
));
162 Ok(ExitStatus
::new(status
))
165 pub fn try_wait(&mut self) -> io
::Result
<Option
<ExitStatus
>> {
166 if let Some(status
) = self.status
{
167 return Ok(Some(status
));
169 let mut status
= 0 as c_int
;
170 let pid
= cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) }
)?
;
174 self.status
= Some(ExitStatus
::new(status
));
175 Ok(Some(ExitStatus
::new(status
)))
180 /// Unix exit statuses
181 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
182 pub struct ExitStatus(c_int
);
185 pub fn new(status
: c_int
) -> ExitStatus
{
189 fn exited(&self) -> bool
{
190 libc
::WIFEXITED(self.0)
193 pub fn exit_ok(&self) -> Result
<(), ExitStatusError
> {
194 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
195 // true on all actual versions of Unix, is widely assumed, and is specified in SuS
196 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
197 // true for a platform pretending to be Unix, the tests (our doctests, and also
198 // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
199 match NonZero_c_int
::try_from(self.0) {
200 Ok(failure
) => Err(ExitStatusError(failure
)),
205 pub fn code(&self) -> Option
<i32> {
206 if self.exited() { Some(libc::WEXITSTATUS(self.0)) }
else { None }
209 pub fn signal(&self) -> Option
<i32> {
210 if !self.exited() { Some(libc::WTERMSIG(self.0)) }
else { None }
213 pub fn core_dumped(&self) -> bool
{
214 // This method is not yet properly implemented on VxWorks
218 pub fn stopped_signal(&self) -> Option
<i32> {
219 if libc
::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) }
else { None }
222 pub fn continued(&self) -> bool
{
223 // This method is not yet properly implemented on VxWorks
227 pub fn into_raw(&self) -> c_int
{
232 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
233 impl From
<c_int
> for ExitStatus
{
234 fn from(a
: c_int
) -> ExitStatus
{
239 impl fmt
::Display
for ExitStatus
{
240 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
241 if let Some(code
) = self.code() {
242 write
!(f
, "exit code: {code}")
244 let signal
= self.signal().unwrap();
245 write
!(f
, "signal: {signal}")
250 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
251 pub struct ExitStatusError(NonZero_c_int
);
253 impl Into
<ExitStatus
> for ExitStatusError
{
254 fn into(self) -> ExitStatus
{
255 ExitStatus(self.0.into
())
259 impl ExitStatusError
{
260 pub fn code(self) -> Option
<NonZeroI32
> {
261 ExitStatus(self.0.into
()).code().map(|st
| st
.try_into().unwrap())