I'm writing a wrapper command that invokes a child process, waits for it to exit, and propagates its exit code. Simple enough.
I want to propagate the exit status of the child process to my own process's exit code even if the child exited with a signal. If WIFSIGNALED(cmdret)
is true for some int cmdret
that contains the result of a waitpid(...)
call or similar, I want to set my process exit code the WTERMSIG(cmdret)
such that WIFSIGNALED
will return true on the exit code of my own process.
How do I do it? Lets assume I don't want to send my own process a fatal signal that matches that of the child process - if the child exited with SIGABRT
or SIGSEGV
I don't want to send an abort or segfault signal to my wrapper.
glibc
defines the relevant macros as
/* If WIFEXITED(STATUS), the low-order 8 bits of the status. */
#define __WEXITSTATUS(status) (((status) & 0xff00) >> 8)
/* If WIFSIGNALED(STATUS), the terminating signal. */
#define __WTERMSIG(status) ((status) & 0x7f)
/* If WIFSTOPPED(STATUS), the signal that stopped the child. */
#define __WSTOPSIG(status) __WEXITSTATUS(status)
/* Nonzero if STATUS indicates normal termination. */
#define __WIFEXITED(status) (__WTERMSIG(status) == 0)
/* Nonzero if STATUS indicates termination by a signal. */
#define __WIFSIGNALED(status) \
(((signed char) (((status) & 0x7f) + 1) >> 1) > 0)
and exit(...)
docs say:
The
exit()
function causes normal process termination and the least significant byte ofstatus
(i.e.,status & 0xFF
) is returned to the parent (seewait(2)
).
Similarly _exit(...)
returns status & 0xFF
, and the return value from main
uses the low 8 bits for normal exit codes.
The shell convention is to use exit codes less than 125 for the child process exit, 126 as "found but not executable", 127 as "not found", and 128+N as "exited with signal N". That works, but requires the caller to be aware of whether they're invoking the process via an intermediate shell or not to interpret the exit code correctly.
This makes it impossible to differentiate between processes that themselves exited with codes greater than 125 and a signal exit or failure of the wrapper or shell. It could break commands that invoke the wrapped command if they use test macros like WIFEXITED
, WTERMSIG
etc on the return code of the original command. The wrapper will appear to have exited normally even if the child exited on a signal.
I want my wrapper to be completely transparent to the invoker, so calling WEXITSIG(ret)
on my wrapper's return code will return the signal if the wrapped process in turn exited with a signal.
Is that possible at all?
If you want to replicate the wait status of a child in the parent,
then if WIFEXITED(ws)
returns true, you can_exit(WEXITSTATUS(ws))
and if WIFSIGNALED(ws)
returns true, you can do signal(WTERMSIG(ws),SIG_DFL); raise(WTERMSIG(ws));
(i.e., raise the same signal after resetting it to its default disposition so that raising it surely will kill the process).
I don't think Unix kernels will let you fake signal death without actually going through signal death. If you're trying to avoid it to prevent coredumps, then you could possibly use platform-specific solutions to disable coredumps just for your wrapper process. E.g., on Linux, you can call prctl(PR_SET_DUMPABLE,0);
.