util: add convertProcessSignalToExitCode utility

Add convertProcessSignalToExitCode() to convert signal names to POSIX
exit codes (128 + signal number). Exposed in public util API.

Refs: https://github.com/nodejs/node/pull/60720
PR-URL: https://github.com/nodejs/node/pull/60963
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
This commit is contained in:
Erick Wendel 2025-12-11 10:25:21 -03:00 committed by GitHub
parent 180c717714
commit e705603a6f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 125 additions and 0 deletions

View File

@ -1557,6 +1557,10 @@ re-raise the handled signal.
See waitpid(2).
When `code` is `null` due to signal termination, you can use
[`util.convertProcessSignalToExitCode()`][] to convert the signal to a POSIX
exit code.
### Event: `'message'`
<!-- YAML
@ -1671,6 +1675,11 @@ within the child process to close the IPC channel as well.
The `subprocess.exitCode` property indicates the exit code of the child process.
If the child process is still running, the field will be `null`.
When the child process is terminated by a signal, `subprocess.exitCode` will be
`null` and [`subprocess.signalCode`][] will be set. To get the corresponding
POSIX exit code, use
[`util.convertProcessSignalToExitCode(subprocess.signalCode)`][`util.convertProcessSignalToExitCode()`].
### `subprocess.kill([signal])`
<!-- YAML
@ -2107,6 +2116,10 @@ connection to the child.
The `subprocess.signalCode` property indicates the signal received by
the child process if any, else `null`.
When the child process is terminated by a signal, [`subprocess.exitCode`][] will be `null`.
To get the corresponding POSIX exit code, use
[`util.convertProcessSignalToExitCode(subprocess.signalCode)`][`util.convertProcessSignalToExitCode()`].
### `subprocess.spawnargs`
* Type: {Array}
@ -2387,12 +2400,15 @@ or [`child_process.fork()`][].
[`stdio`]: #optionsstdio
[`subprocess.connected`]: #subprocessconnected
[`subprocess.disconnect()`]: #subprocessdisconnect
[`subprocess.exitCode`]: #subprocessexitcode
[`subprocess.kill()`]: #subprocesskillsignal
[`subprocess.send()`]: #subprocesssendmessage-sendhandle-options-callback
[`subprocess.signalCode`]: #subprocesssignalcode
[`subprocess.stderr`]: #subprocessstderr
[`subprocess.stdin`]: #subprocessstdin
[`subprocess.stdio`]: #subprocessstdio
[`subprocess.stdout`]: #subprocessstdout
[`util.convertProcessSignalToExitCode()`]: util.md#utilconvertprocesssignaltoexitcodesignalcode
[`util.promisify()`]: util.md#utilpromisifyoriginal
[synchronous counterparts]: #synchronous-process-creation
[v8.serdes]: v8.md#serialization-api

View File

@ -89,6 +89,38 @@ callbackFunction((err, ret) => {
});
```
## `util.convertProcessSignalToExitCode(signalCode)`
<!-- YAML
added: REPLACEME
-->
* `signalCode` {string} A signal name (e.g., `'SIGTERM'`, `'SIGKILL'`).
* Returns: {number|null} The exit code, or `null` if the signal is invalid.
The `util.convertProcessSignalToExitCode()` method converts a signal name to its
corresponding POSIX exit code. Following the POSIX standard, the exit code
for a process terminated by a signal is calculated as `128 + signal number`.
```mjs
import { convertProcessSignalToExitCode } from 'node:util';
console.log(convertProcessSignalToExitCode('SIGTERM')); // 143 (128 + 15)
console.log(convertProcessSignalToExitCode('SIGKILL')); // 137 (128 + 9)
console.log(convertProcessSignalToExitCode('INVALID')); // null
```
```cjs
const { convertProcessSignalToExitCode } = require('node:util');
console.log(convertProcessSignalToExitCode('SIGTERM')); // 143 (128 + 15)
console.log(convertProcessSignalToExitCode('SIGKILL')); // 137 (128 + 9)
console.log(convertProcessSignalToExitCode('INVALID')); // null
```
This is particularly useful when working with processes to determine
the exit code based on the signal that terminated the process.
## `util.debuglog(section[, callback])`
<!-- YAML

View File

@ -16,6 +16,7 @@ const {
ObjectGetOwnPropertyDescriptor,
ObjectGetOwnPropertyDescriptors,
ObjectGetPrototypeOf,
ObjectKeys,
ObjectPrototypeHasOwnProperty,
ObjectSetPrototypeOf,
ObjectValues,
@ -106,6 +107,7 @@ function isError(e) {
const codesWarned = new SafeSet();
let validateString;
let validateOneOf;
function getDeprecationWarningEmitter(
code, msg, deprecated, useEmitSync,
@ -393,6 +395,17 @@ function convertToValidSignal(signal) {
throw new ERR_UNKNOWN_SIGNAL(signal);
}
function convertProcessSignalToExitCode(signalCode) {
// Lazy-load to avoid a circular dependency.
if (validateOneOf === undefined)
({ validateOneOf } = require('internal/validators'));
validateOneOf(signalCode, 'signalCode', ObjectKeys(signals));
// POSIX standard: exit code for signal termination is 128 + signal number.
return 128 + signals[signalCode];
}
function getConstructorOf(obj) {
while (obj) {
const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
@ -956,6 +969,7 @@ module.exports = {
assignFunctionName,
cachedResult,
constructSharedArrayBuffer,
convertProcessSignalToExitCode,
convertToValidSignal,
createClassWrapper,
decorateErrorStack,

View File

@ -84,6 +84,7 @@ const { getOptionValue } = require('internal/options');
const binding = internalBinding('util');
const {
convertProcessSignalToExitCode,
deprecate: internalDeprecate,
getLazy,
getSystemErrorMap,
@ -472,6 +473,7 @@ module.exports = {
'The `util._extend` API is deprecated. Please use Object.assign() instead.',
'DEP0060'),
callbackify,
convertProcessSignalToExitCode,
debug: debuglog,
debuglog,
deprecate,

View File

@ -0,0 +1,61 @@
import { mustCall, mustNotCall, isWindows } from '../common/index.mjs';
import assert from 'assert';
import { convertProcessSignalToExitCode } from 'util';
import { spawn } from 'child_process';
import { constants } from 'os';
const { signals } = constants;
{
assert.strictEqual(convertProcessSignalToExitCode('SIGTERM'), 128 + signals.SIGTERM);
assert.strictEqual(convertProcessSignalToExitCode('SIGKILL'), 128 + signals.SIGKILL);
assert.strictEqual(convertProcessSignalToExitCode('SIGINT'), 128 + signals.SIGINT);
assert.strictEqual(convertProcessSignalToExitCode('SIGHUP'), 128 + signals.SIGHUP);
assert.strictEqual(convertProcessSignalToExitCode('SIGABRT'), 128 + signals.SIGABRT);
}
{
[
'INVALID',
'',
'SIG',
undefined,
null,
123,
true,
false,
{},
[],
Symbol('test'),
() => {},
].forEach((value) => {
assert.throws(
() => convertProcessSignalToExitCode(value),
{
code: 'ERR_INVALID_ARG_VALUE',
name: 'TypeError',
}
);
});
}
{
const cat = spawn(isWindows ? 'cmd' : 'cat');
cat.stdout.on('end', mustCall());
cat.stderr.on('data', mustNotCall());
cat.stderr.on('end', mustCall());
cat.on('exit', mustCall((code, signal) => {
assert.strictEqual(code, null);
assert.strictEqual(signal, 'SIGTERM');
assert.strictEqual(cat.signalCode, 'SIGTERM');
const exitCode = convertProcessSignalToExitCode(signal);
assert.strictEqual(exitCode, 143);
}));
assert.strictEqual(cat.signalCode, null);
assert.strictEqual(cat.killed, false);
cat[Symbol.dispose]();
assert.strictEqual(cat.killed, true);
}