diff --git a/core/process.py b/core/process.py index f41202a2bf..869a4ef128 100644 --- a/core/process.py +++ b/core/process.py @@ -13,6 +13,8 @@ from signal import (SIG_DFL, SIG_IGN, SIGINT, SIGPIPE, SIGQUIT, SIGTSTP, SIGTTOU, SIGTTIN, SIGWINCH) +import libc + from _devbuild.gen.id_kind_asdl import Id from _devbuild.gen.runtime_asdl import (job_state_e, job_state_t, job_state_str, wait_status, @@ -2070,6 +2072,17 @@ def NumRunning(self): NO_ARG = -20 + + + +def GetSignalMessage(sig_num): + # type: (int) -> Optional[str] + """Get signal message from libc.""" + if mylib.PYTHON: + return libc.strsignal(sig_num) + return None + + class Waiter(object): """A capability to wait for processes. @@ -2192,6 +2205,18 @@ def WaitForOne(self, waitpid_options=0): # Print newline after Ctrl-C. if term_sig == SIGINT: print('') + else: + msg = GetSignalMessage(term_sig) + if msg is not None: + if mylib.PYTHON: + # WCOREDUMP is only available on some systems + try: + from os import WCOREDUMP # type: ignore + if WCOREDUMP(status): + msg = msg + ' (core dumped)' + except (ImportError, AttributeError): + pass + print_stderr(msg) if proc: proc.WhenExited(pid, status) diff --git a/pyext/libc.c b/pyext/libc.c index e83dbaaf0b..9479944785 100644 --- a/pyext/libc.c +++ b/pyext/libc.c @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -379,6 +382,30 @@ func_sleep_until_error(PyObject *self, PyObject *args) { return PyInt_FromLong(result); } +static PyObject * +func_strsignal(PyObject *self, PyObject *args) { + int sig_num; + char *res; + + if (!PyArg_ParseTuple(args, "i:strsignal", &sig_num)) { + return NULL; + } + + if (sig_num < 1 || sig_num >= NSIG) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } + + errno = 0; + res = strsignal(sig_num); + + if (errno || res == NULL || strstr(res, "Unknown signal") != NULL) { + Py_RETURN_NONE; + } + + return PyString_FromString(res); +} + static PyMethodDef methods[] = { // Return the canonical version of a path with symlinks, or None if there is // an error. @@ -416,6 +443,8 @@ static PyMethodDef methods[] = { {"cpython_reset_locale", func_cpython_reset_locale, METH_NOARGS, ""}, {"sleep_until_error", func_sleep_until_error, METH_VARARGS, ""}, + + {"strsignal", func_strsignal, METH_VARARGS, ""}, {NULL, NULL}, }; diff --git a/pyext/libc.pyi b/pyext/libc.pyi index 525eaa9939..35bf83be9b 100644 --- a/pyext/libc.pyi +++ b/pyext/libc.pyi @@ -24,3 +24,5 @@ def realpath(path: str) -> str: ... def cpython_reset_locale() -> None: ... def sleep_until_error(seconds: float) -> int: ... + +def strsignal(sig_num: int) -> Optional[str]: ... diff --git a/pyext/libc_test.py b/pyext/libc_test.py index dc5e7843ee..856073dae5 100755 --- a/pyext/libc_test.py +++ b/pyext/libc_test.py @@ -12,6 +12,7 @@ """ import unittest import sys +import signal import libc # module under test @@ -367,6 +368,15 @@ def testSleepUntilError(self): # Not testing errno case + def testStrsignal(self): + self.assertEqual('Segmentation fault', libc.strsignal(11)) + self.assertEqual('Aborted', libc.strsignal(6)) + self.assertEqual('Illegal instruction', libc.strsignal(4)) + self.assertEqual('Terminated', libc.strsignal(signal.SIGTERM)) + + with self.assertRaises(ValueError): + libc.strsignal(999) + if __name__ == '__main__': # To simulate the OVM_MAIN patch in pythonrun.c diff --git a/spec/background.test.sh b/spec/background.test.sh index 5c7d2f2d12..9fce4eb3a8 100644 --- a/spec/background.test.sh +++ b/spec/background.test.sh @@ -1,4 +1,5 @@ ## oils_failures_allowed: 3 +## oils_cpp_failures_allowed: 4 ## compare_shells: dash bash mksh # Job control constructs: @@ -154,6 +155,8 @@ status=1 ## END #### Start background pipeline, wait %job_spec +case $SH in mksh) exit ;; esac # flakiness? + echo hi | { exit 99; } & echo status=$? wait %1 @@ -162,6 +165,8 @@ echo status=$? status=0 status=99 ## END +## BUG mksh STDOUT: +## END #### Wait for job and PIPESTATUS @@ -396,3 +401,22 @@ wait --all 1 ## N-I dash/bash/mksh STDOUT: ## END + +#### Signal message for killed background job +case $SH in dash|mksh) exit ;; esac + +sleep 1 & +kill -HUP $! +wait $! 2>err.txt +echo status=$? +grep -o "Hangup" err.txt +## status: 0 +## STDOUT: +status=129 +Hangup +## END +## STDERR: +## END + +## N-I dash/mksh STDOUT: +## END