Skip to content

Commit 54035da

Browse files
committed
util: add convertProcessSignalToExitCode utility
Add convertProcessSignalToExitCode() to convert signal names to POSIX exit codes (128 + signal number). Exposed in public util API. Refs: #60720
1 parent cbe0233 commit 54035da

File tree

4 files changed

+123
-0
lines changed

4 files changed

+123
-0
lines changed

doc/api/util.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,38 @@ callbackFunction((err, ret) => {
8989
});
9090
```
9191

92+
## `util.convertProcessSignalToExitCode(signalCode)`
93+
94+
<!-- YAML
95+
added: REPLACEME
96+
-->
97+
98+
* `signalCode` {string} A signal name (e.g., `'SIGTERM'`, `'SIGKILL'`).
99+
* Returns: {number|null} The exit code, or `null` if the signal is invalid.
100+
101+
The `util.convertProcessSignalToExitCode()` method converts a signal name to its
102+
corresponding POSIX exit code. Following the POSIX standard, the exit code
103+
for a process terminated by a signal is calculated as `128 + signal number`.
104+
105+
```mjs
106+
import { convertProcessSignalToExitCode } from 'node:util';
107+
108+
console.log(convertProcessSignalToExitCode('SIGTERM')); // 143 (128 + 15)
109+
console.log(convertProcessSignalToExitCode('SIGKILL')); // 137 (128 + 9)
110+
console.log(convertProcessSignalToExitCode('INVALID')); // null
111+
```
112+
113+
```cjs
114+
const { convertProcessSignalToExitCode } = require('node:util');
115+
116+
console.log(convertProcessSignalToExitCode('SIGTERM')); // 143 (128 + 15)
117+
console.log(convertProcessSignalToExitCode('SIGKILL')); // 137 (128 + 9)
118+
console.log(convertProcessSignalToExitCode('INVALID')); // null
119+
```
120+
121+
This is particularly useful when working with processes to determine
122+
the exit code based on the signal that terminated the process.
123+
92124
## `util.debuglog(section[, callback])`
93125

94126
<!-- YAML

lib/internal/util.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,18 @@ function convertToValidSignal(signal) {
393393
throw new ERR_UNKNOWN_SIGNAL(signal);
394394
}
395395

396+
function convertProcessSignalToExitCode(signalCode) {
397+
validateString(signalCode, 'signalCode');
398+
399+
const signalNumber = signals[signalCode];
400+
if (signalNumber === undefined) {
401+
return null;
402+
}
403+
404+
// POSIX standard: exit code for signal termination is 128 + signal number.
405+
return 128 + signalNumber;
406+
}
407+
396408
function getConstructorOf(obj) {
397409
while (obj) {
398410
const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
@@ -956,6 +968,7 @@ module.exports = {
956968
assignFunctionName,
957969
cachedResult,
958970
constructSharedArrayBuffer,
971+
convertProcessSignalToExitCode,
959972
convertToValidSignal,
960973
createClassWrapper,
961974
decorateErrorStack,

lib/util.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const { getOptionValue } = require('internal/options');
8484
const binding = internalBinding('util');
8585

8686
const {
87+
convertProcessSignalToExitCode,
8788
deprecate: internalDeprecate,
8889
getLazy,
8990
getSystemErrorMap,
@@ -472,6 +473,7 @@ module.exports = {
472473
'The `util._extend` API is deprecated. Please use Object.assign() instead.',
473474
'DEP0060'),
474475
callbackify,
476+
convertProcessSignalToExitCode,
475477
debug: debuglog,
476478
debuglog,
477479
deprecate,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { mustCall, mustNotCall, isWindows } from '../common/index.mjs';
2+
import assert from 'assert';
3+
import { convertProcessSignalToExitCode } from 'util';
4+
import { spawn } from 'child_process';
5+
import os from 'os';
6+
7+
// Test valid signal names
8+
{
9+
// SIGTERM = 15, so 128 + 15 = 143
10+
assert.strictEqual(convertProcessSignalToExitCode('SIGTERM'), 143);
11+
12+
// SIGKILL = 9, so 128 + 9 = 137
13+
assert.strictEqual(convertProcessSignalToExitCode('SIGKILL'), 137);
14+
15+
// SIGINT = 2, so 128 + 2 = 130
16+
assert.strictEqual(convertProcessSignalToExitCode('SIGINT'), 130);
17+
18+
// SIGHUP = 1, so 128 + 1 = 129
19+
assert.strictEqual(convertProcessSignalToExitCode('SIGHUP'), 129);
20+
21+
// SIGABRT = 6, so 128 + 6 = 134
22+
assert.strictEqual(convertProcessSignalToExitCode('SIGABRT'), 134);
23+
}
24+
25+
// Test invalid signal names return null
26+
{
27+
assert.strictEqual(convertProcessSignalToExitCode('INVALID'), null);
28+
assert.strictEqual(convertProcessSignalToExitCode(''), null);
29+
assert.strictEqual(convertProcessSignalToExitCode('SIG'), null);
30+
}
31+
32+
// Test invalid argument types
33+
{
34+
[
35+
undefined,
36+
null,
37+
123,
38+
true,
39+
false,
40+
{},
41+
[],
42+
Symbol('test'),
43+
() => {},
44+
].forEach((value) => {
45+
assert.throws(
46+
() => convertProcessSignalToExitCode(value),
47+
{
48+
code: 'ERR_INVALID_ARG_TYPE',
49+
name: 'TypeError',
50+
}
51+
);
52+
});
53+
}
54+
55+
// Test with actual child process termination
56+
{
57+
const cat = spawn(isWindows ? 'cmd' : 'cat');
58+
cat.stdout.on('end', mustCall());
59+
cat.stderr.on('data', mustNotCall());
60+
cat.stderr.on('end', mustCall());
61+
62+
cat.on('exit', mustCall((code, signal) => {
63+
assert.strictEqual(code, null);
64+
assert.strictEqual(signal, 'SIGTERM');
65+
assert.strictEqual(cat.signalCode, 'SIGTERM');
66+
67+
// Verify the utility function returns correct exit code
68+
const exitCode = convertProcessSignalToExitCode(signal);
69+
assert.strictEqual(exitCode, 143);
70+
}));
71+
72+
assert.strictEqual(cat.signalCode, null);
73+
assert.strictEqual(cat.killed, false);
74+
cat[Symbol.dispose]();
75+
assert.strictEqual(cat.killed, true);
76+
}

0 commit comments

Comments
 (0)