diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WaitPid.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WaitPid.cs
index 2f4eae4584c0a9..0474a11b0bde9e 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WaitPid.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.WaitPid.cs
@@ -18,6 +18,6 @@ internal static partial class Sys
/// 4) on error, -1 is returned.
///
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_WaitPidExitedNoHang", SetLastError = true)]
- internal static partial int WaitPidExitedNoHang(int pid, out int exitCode);
+ internal static partial int WaitPidExitedNoHang(int pid, out int exitCode, out int terminatingSignal);
}
}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
index 6abe345f996f0c..b385cd6df6f7d1 100644
--- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs
@@ -223,11 +223,11 @@ public ProcessModule? MainModule
/// Checks whether the process has exited and updates state accordingly.
private void UpdateHasExited()
{
- int? exitCode;
- _exited = GetWaitState().GetExited(out exitCode, refresh: true);
- if (_exited && exitCode != null)
+ ProcessExitStatus? exitStatus;
+ _exited = GetWaitState().GetExited(out exitStatus, refresh: true);
+ if (_exited && exitStatus is not null)
{
- _exitCode = exitCode.Value;
+ _exitCode = exitStatus.ExitCode;
}
}
diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs
index ab1dd7e46bf2f6..2890f0b421d97d 100644
--- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs
+++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/ProcessWaitState.Unix.cs
@@ -208,8 +208,8 @@ internal void ReleaseRef()
/// Whether the associated process exited.
private bool _exited;
- /// If the process exited, it's exit code, or null if we were unable to determine one.
- private int? _exitCode;
+ /// If the process exited, its exit status, or null if we were unable to determine one.
+ private ProcessExitStatus? _exitStatus;
///
/// The approximate time the process exited. We do not have the ability to know exact time a process
/// exited, so we approximate it by storing the time that we discovered it exited.
@@ -310,14 +310,14 @@ internal bool HasExited
}
}
- internal bool GetExited(out int? exitCode, bool refresh)
+ internal bool GetExited(out ProcessExitStatus? exitStatus, bool refresh)
{
lock (_gate)
{
// Have we already exited? If so, return the cached results.
if (_exited)
{
- exitCode = _exitCode;
+ exitStatus = _exitStatus;
return true;
}
@@ -325,7 +325,7 @@ internal bool GetExited(out int? exitCode, bool refresh)
// and that task owns the right to call CheckForNonChildExit.
if (!_waitInProgress.IsCompleted)
{
- exitCode = null;
+ exitStatus = null;
return false;
}
@@ -338,7 +338,7 @@ internal bool GetExited(out int? exitCode, bool refresh)
// We now have an up-to-date snapshot for whether we've exited,
// and if we have, what the exit code is (if we were able to find out).
- exitCode = _exitCode;
+ exitStatus = _exitStatus;
return _exited;
}
}
@@ -539,13 +539,14 @@ private async Task WaitForExitAsync(CancellationToken cancellationToken)
}
}
- private void ChildReaped(int exitCode, bool configureConsole)
+ private void ChildReaped(int exitCode, int terminatingSignal, bool configureConsole)
{
lock (_gate)
{
Debug.Assert(!_exited);
- _exitCode = exitCode;
+ PosixSignal? signal = terminatingSignal != 0 ? (PosixSignal)terminatingSignal : null;
+ _exitStatus = new ProcessExitStatus(exitCode, canceled: false, signal);
if (_usesTerminal)
{
@@ -568,11 +569,12 @@ private bool TryReapChild(bool configureConsole)
// Try to get the state of the child process
int exitCode;
- int waitResult = Interop.Sys.WaitPidExitedNoHang(_processId, out exitCode);
+ int terminatingSignal;
+ int waitResult = Interop.Sys.WaitPidExitedNoHang(_processId, out exitCode, out terminatingSignal);
if (waitResult == _processId)
{
- ChildReaped(exitCode, configureConsole);
+ ChildReaped(exitCode, terminatingSignal, configureConsole);
return true;
}
else if (waitResult == 0)
@@ -673,7 +675,8 @@ internal static void CheckChildren(bool reapAll, bool configureConsole)
do
{
int exitCode;
- pid = Interop.Sys.WaitPidExitedNoHang(-1, out exitCode);
+ int terminatingSignal;
+ pid = Interop.Sys.WaitPidExitedNoHang(-1, out exitCode, out terminatingSignal);
if (pid <= 0)
{
break;
@@ -682,7 +685,7 @@ internal static void CheckChildren(bool reapAll, bool configureConsole)
// Check if the process is a child that has just terminated.
if (s_childProcessWaitStates.TryGetValue(pid, out ProcessWaitState? pws))
{
- pws.ChildReaped(exitCode, configureConsole);
+ pws.ChildReaped(exitCode, terminatingSignal, configureConsole);
pws.ReleaseRef();
}
} while (true);
diff --git a/src/native/libs/System.Native/pal_process.c b/src/native/libs/System.Native/pal_process.c
index 82c00925143113..349b02ec7ae665 100644
--- a/src/native/libs/System.Native/pal_process.c
+++ b/src/native/libs/System.Native/pal_process.c
@@ -3,6 +3,7 @@
#include "pal_config.h"
#include "pal_process.h"
+#include "pal_signal.h"
#include "pal_io.h"
#include "pal_utilities.h"
@@ -929,9 +930,10 @@ int32_t SystemNative_WaitIdAnyExitedNoHangNoWait(void)
return result;
}
-int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode)
+int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode, int32_t* terminatingSignal)
{
assert(exitCode != NULL);
+ assert(terminatingSignal != NULL);
int32_t result;
int status;
@@ -942,11 +944,16 @@ int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode)
{
// the child terminated normally.
*exitCode = WEXITSTATUS(status);
+ *terminatingSignal = 0;
}
else if (WIFSIGNALED(status))
{
// child process was terminated by a signal.
- *exitCode = 128 + WTERMSIG(status);
+ int sig = WTERMSIG(status);
+ *exitCode = 128 + sig;
+ PosixSignal posixSignal = PosixSignalInvalid;
+ TryConvertSignalCodeToPosixSignal(sig, &posixSignal);
+ *terminatingSignal = (int32_t)posixSignal;
}
else
{
diff --git a/src/native/libs/System.Native/pal_process.h b/src/native/libs/System.Native/pal_process.h
index 00bddcd17d78f4..abcc7d87439cd7 100644
--- a/src/native/libs/System.Native/pal_process.h
+++ b/src/native/libs/System.Native/pal_process.h
@@ -186,8 +186,12 @@ PALEXPORT int32_t SystemNative_WaitIdAnyExitedNoHangNoWait(void);
* 2) if pid is not a child or there are no unwaited-for children, -1 is returned (errno=ECHILD)
* 3) if the child has not yet terminated, 0 is returned
* 4) on error, -1 is returned.
+ *
+ * exitCode: set to WEXITSTATUS on normal exit, or 128 + signal number on signal termination.
+ * terminatingSignal: set to 0 on normal exit, or the PosixSignal value on signal termination.
+ * For signals not in the known PosixSignal set, this is the raw signal number.
*/
-PALEXPORT int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode);
+PALEXPORT int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode, int32_t* terminatingSignal);
/**
* Gets the configurable limit or variable for system path or file descriptor options.
diff --git a/src/native/libs/System.Native/pal_process_wasi.c b/src/native/libs/System.Native/pal_process_wasi.c
index ff32ba0a39bba5..1ad9f888bbc04c 100644
--- a/src/native/libs/System.Native/pal_process_wasi.c
+++ b/src/native/libs/System.Native/pal_process_wasi.c
@@ -76,7 +76,7 @@ int32_t SystemNative_WaitIdAnyExitedNoHangNoWait(void)
return -1;
}
-int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode)
+int32_t SystemNative_WaitPidExitedNoHang(int32_t pid, int32_t* exitCode, int32_t* terminatingSignal)
{
return -1;
}
diff --git a/src/native/libs/System.Native/pal_signal.c b/src/native/libs/System.Native/pal_signal.c
index 18254d590d9fe9..3e02b2466fc65d 100644
--- a/src/native/libs/System.Native/pal_signal.c
+++ b/src/native/libs/System.Native/pal_signal.c
@@ -83,7 +83,7 @@ static bool IsSigIgn(struct sigaction* action)
action->sa_handler == SIG_IGN;
}
-static bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal)
+bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal)
{
assert(posixSignal != NULL);
diff --git a/src/native/libs/System.Native/pal_signal.h b/src/native/libs/System.Native/pal_signal.h
index 074c77315f5edc..801251746031c9 100644
--- a/src/native/libs/System.Native/pal_signal.h
+++ b/src/native/libs/System.Native/pal_signal.h
@@ -83,6 +83,14 @@ PALEXPORT void SystemNative_DisablePosixSignalHandling(int signalCode);
*/
PALEXPORT void SystemNative_HandleNonCanceledPosixSignal(int32_t signalCode);
+/**
+ * Converts a native signal code to PosixSignal.
+ *
+ * Returns true if the signal code was mapped to a known PosixSignal.
+ * Returns false if the signal is not in the known set (posixSignal is set to the raw signal code).
+ */
+bool TryConvertSignalCodeToPosixSignal(int signalCode, PosixSignal* posixSignal);
+
typedef void (*ConsoleSigTtouHandler)(void);
/**