Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
util: add convertProcessSignalToExitCode utility
Add convertProcessSignalToExitCode() to convert signal names to POSIX
exit codes (128 + signal number). Exposed in public util API.

Refs: #60720
  • Loading branch information
ErickWendel committed Dec 5, 2025
commit 54035dacc583dbd29166778e7c3a5afd1848e28e
32 changes: 32 additions & 0 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,18 @@ function convertToValidSignal(signal) {
throw new ERR_UNKNOWN_SIGNAL(signal);
}

function convertProcessSignalToExitCode(signalCode) {
validateString(signalCode, 'signalCode');

const signalNumber = signals[signalCode];
if (signalNumber === undefined) {
Comment thread
ErickWendel marked this conversation as resolved.
Outdated
return null;
}

// POSIX standard: exit code for signal termination is 128 + signal number.
return 128 + signalNumber;
}

function getConstructorOf(obj) {
while (obj) {
const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
Expand Down Expand Up @@ -956,6 +968,7 @@ module.exports = {
assignFunctionName,
cachedResult,
constructSharedArrayBuffer,
convertProcessSignalToExitCode,
convertToValidSignal,
createClassWrapper,
decorateErrorStack,
Expand Down
2 changes: 2 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const { getOptionValue } = require('internal/options');
const binding = internalBinding('util');

const {
convertProcessSignalToExitCode,
deprecate: internalDeprecate,
getLazy,
getSystemErrorMap,
Expand Down Expand Up @@ -472,6 +473,7 @@ module.exports = {
'The `util._extend` API is deprecated. Please use Object.assign() instead.',
'DEP0060'),
callbackify,
convertProcessSignalToExitCode,
debug: debuglog,
debuglog,
deprecate,
Expand Down
76 changes: 76 additions & 0 deletions test/parallel/test-util-convert-signal-to-exit-code.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { mustCall, mustNotCall, isWindows } from '../common/index.mjs';
import assert from 'assert';
import { convertProcessSignalToExitCode } from 'util';
import { spawn } from 'child_process';
import os from 'os';
Comment thread
ErickWendel marked this conversation as resolved.
Outdated

// Test valid signal names
{
// SIGTERM = 15, so 128 + 15 = 143
assert.strictEqual(convertProcessSignalToExitCode('SIGTERM'), 143);

// SIGKILL = 9, so 128 + 9 = 137
assert.strictEqual(convertProcessSignalToExitCode('SIGKILL'), 137);

// SIGINT = 2, so 128 + 2 = 130
assert.strictEqual(convertProcessSignalToExitCode('SIGINT'), 130);

// SIGHUP = 1, so 128 + 1 = 129
assert.strictEqual(convertProcessSignalToExitCode('SIGHUP'), 129);

// SIGABRT = 6, so 128 + 6 = 134
assert.strictEqual(convertProcessSignalToExitCode('SIGABRT'), 134);
}

// Test invalid signal names return null
{
assert.strictEqual(convertProcessSignalToExitCode('INVALID'), null);
assert.strictEqual(convertProcessSignalToExitCode(''), null);
assert.strictEqual(convertProcessSignalToExitCode('SIG'), null);
}

// Test invalid argument types
{
[
undefined,
null,
123,
true,
false,
{},
[],
Symbol('test'),
() => {},
].forEach((value) => {
assert.throws(
() => convertProcessSignalToExitCode(value),
{
code: 'ERR_INVALID_ARG_TYPE',
name: 'TypeError',
}
);
});
}

// Test with actual child process termination
{
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');

// Verify the utility function returns correct exit code
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);
}