Skip to content

Commit 448c4c6

Browse files
committed
child_process: do not extend result for *Sync()
PR-URL: #13601 Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
1 parent 9dc3f93 commit 448c4c6

5 files changed

+113
-85
lines changed

lib/child_process.js

+19-47
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,11 @@ const { createPromise,
2828
const debug = util.debuglog('child_process');
2929

3030
const uv = process.binding('uv');
31-
const spawn_sync = process.binding('spawn_sync');
3231
const Buffer = require('buffer').Buffer;
3332
const Pipe = process.binding('pipe_wrap').Pipe;
3433
const { isUint8Array } = process.binding('util');
3534
const child_process = require('internal/child_process');
3635

37-
const errnoException = util._errnoException;
3836
const _validateStdio = child_process._validateStdio;
3937
const setupChannel = child_process.setupChannel;
4038
const ChildProcess = exports.ChildProcess = child_process.ChildProcess;
@@ -508,8 +506,6 @@ function spawnSync(/*file, args, options*/) {
508506

509507
var options = opts.options;
510508

511-
var i;
512-
513509
debug('spawnSync', opts.args, options);
514510

515511
// Validate the timeout, if present.
@@ -533,7 +529,7 @@ function spawnSync(/*file, args, options*/) {
533529
}
534530

535531
// We may want to pass data in on any given fd, ensure it is a valid buffer
536-
for (i = 0; i < options.stdio.length; i++) {
532+
for (var i = 0; i < options.stdio.length; i++) {
537533
var input = options.stdio[i] && options.stdio[i].input;
538534
if (input != null) {
539535
var pipe = options.stdio[i] = util._extend({}, options.stdio[i]);
@@ -549,50 +545,27 @@ function spawnSync(/*file, args, options*/) {
549545
}
550546
}
551547

552-
var result = spawn_sync.spawn(options);
553-
554-
if (result.output && options.encoding && options.encoding !== 'buffer') {
555-
for (i = 0; i < result.output.length; i++) {
556-
if (!result.output[i])
557-
continue;
558-
result.output[i] = result.output[i].toString(options.encoding);
559-
}
560-
}
561-
562-
result.stdout = result.output && result.output[1];
563-
result.stderr = result.output && result.output[2];
564-
565-
if (result.error) {
566-
result.error = errnoException(result.error, 'spawnSync ' + opts.file);
567-
result.error.path = opts.file;
568-
result.error.spawnargs = opts.args.slice(1);
569-
}
570-
571-
util._extend(result, opts);
572-
573-
return result;
548+
return child_process.spawnSync(opts);
574549
}
575550
exports.spawnSync = spawnSync;
576551

577552

578-
function checkExecSyncError(ret) {
579-
if (ret.error || ret.status !== 0) {
580-
var err = ret.error;
581-
ret.error = null;
582-
583-
if (!err) {
584-
var msg = 'Command failed: ';
585-
msg += ret.cmd || ret.args.join(' ');
586-
if (ret.stderr && ret.stderr.length > 0)
587-
msg += '\n' + ret.stderr.toString();
588-
err = new Error(msg);
589-
}
590-
591-
util._extend(err, ret);
592-
return err;
553+
function checkExecSyncError(ret, args, cmd) {
554+
var err;
555+
if (ret.error) {
556+
err = ret.error;
557+
} else if (ret.status !== 0) {
558+
var msg = 'Command failed: ';
559+
msg += cmd || args.join(' ');
560+
if (ret.stderr && ret.stderr.length > 0)
561+
msg += '\n' + ret.stderr.toString();
562+
err = new Error(msg);
593563
}
594-
595-
return false;
564+
if (err) {
565+
err.status = ret.status < 0 ? uv.errname(ret.status) : ret.status;
566+
err.signal = ret.signal;
567+
}
568+
return err;
596569
}
597570

598571

@@ -605,7 +578,7 @@ function execFileSync(/*command, args, options*/) {
605578
if (inheritStderr && ret.stderr)
606579
process.stderr.write(ret.stderr);
607580

608-
var err = checkExecSyncError(ret);
581+
var err = checkExecSyncError(ret, opts.args, undefined);
609582

610583
if (err)
611584
throw err;
@@ -620,12 +593,11 @@ function execSync(command /*, options*/) {
620593
var inheritStderr = !opts.options.stdio;
621594

622595
var ret = spawnSync(opts.file, opts.options);
623-
ret.cmd = command;
624596

625597
if (inheritStderr && ret.stderr)
626598
process.stderr.write(ret.stderr);
627599

628-
var err = checkExecSyncError(ret);
600+
var err = checkExecSyncError(ret, opts.args, command);
629601

630602
if (err)
631603
throw err;

lib/internal/child_process.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const UDP = process.binding('udp_wrap').UDP;
1818
const SocketList = require('internal/socket_list');
1919
const { isUint8Array } = process.binding('util');
2020
const { convertToValidSignal } = require('internal/util');
21+
const spawn_sync = process.binding('spawn_sync');
2122

2223
const errnoException = util._errnoException;
2324
const SocketListSend = SocketList.SocketListSend;
@@ -898,9 +899,34 @@ function maybeClose(subprocess) {
898899
}
899900
}
900901

902+
function spawnSync(opts) {
903+
var options = opts.options;
904+
var result = spawn_sync.spawn(options);
905+
906+
if (result.output && options.encoding && options.encoding !== 'buffer') {
907+
for (var i = 0; i < result.output.length; i++) {
908+
if (!result.output[i])
909+
continue;
910+
result.output[i] = result.output[i].toString(options.encoding);
911+
}
912+
}
913+
914+
result.stdout = result.output && result.output[1];
915+
result.stderr = result.output && result.output[2];
916+
917+
if (result.error) {
918+
result.error = errnoException(result.error, 'spawnSync ' + opts.file);
919+
result.error.path = opts.file;
920+
result.error.spawnargs = opts.args.slice(1);
921+
}
922+
923+
return result;
924+
}
925+
901926
module.exports = {
902927
ChildProcess,
903928
setupChannel,
904929
_validateStdio,
905-
getSocketList
930+
getSocketList,
931+
spawnSync
906932
};
+23-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
// Flags: --expose_internals
12
'use strict';
23
const common = require('../common');
34
const assert = require('assert');
5+
const internalCp = require('internal/child_process');
6+
const oldSpawnSync = internalCp.spawnSync;
47

58
// Verify that customFds is used if stdio is not provided.
69
{
@@ -9,25 +12,29 @@ const assert = require('assert');
912
common.expectWarning('DeprecationWarning', msg);
1013

1114
const customFds = [-1, process.stdout.fd, process.stderr.fd];
12-
const child = common.spawnSyncPwd({ customFds });
13-
14-
assert.deepStrictEqual(child.options.customFds, customFds);
15-
assert.deepStrictEqual(child.options.stdio, [
16-
{ type: 'pipe', readable: true, writable: false },
17-
{ type: 'fd', fd: process.stdout.fd },
18-
{ type: 'fd', fd: process.stderr.fd }
19-
]);
15+
internalCp.spawnSync = common.mustCall(function(opts) {
16+
assert.deepStrictEqual(opts.options.customFds, customFds);
17+
assert.deepStrictEqual(opts.options.stdio, [
18+
{ type: 'pipe', readable: true, writable: false },
19+
{ type: 'fd', fd: process.stdout.fd },
20+
{ type: 'fd', fd: process.stderr.fd }
21+
]);
22+
});
23+
common.spawnSyncPwd({ customFds });
24+
internalCp.spawnSync = oldSpawnSync;
2025
}
2126

2227
// Verify that customFds is ignored when stdio is present.
2328
{
2429
const customFds = [0, 1, 2];
25-
const child = common.spawnSyncPwd({ customFds, stdio: 'pipe' });
26-
27-
assert.deepStrictEqual(child.options.customFds, customFds);
28-
assert.deepStrictEqual(child.options.stdio, [
29-
{ type: 'pipe', readable: true, writable: false },
30-
{ type: 'pipe', readable: false, writable: true },
31-
{ type: 'pipe', readable: false, writable: true }
32-
]);
30+
internalCp.spawnSync = common.mustCall(function(opts) {
31+
assert.deepStrictEqual(opts.options.customFds, customFds);
32+
assert.deepStrictEqual(opts.options.stdio, [
33+
{ type: 'pipe', readable: true, writable: false },
34+
{ type: 'pipe', readable: false, writable: true },
35+
{ type: 'pipe', readable: false, writable: true }
36+
]);
37+
});
38+
common.spawnSyncPwd({ customFds, stdio: 'pipe' });
39+
internalCp.spawnSync = oldSpawnSync;
3340
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// Flags: --expose_internals
12
'use strict';
23
const common = require('../common');
34
const assert = require('assert');
@@ -6,13 +7,22 @@ const cp = require('child_process');
67
if (process.argv[2] === 'child') {
78
setInterval(common.noop, 1000);
89
} else {
10+
const internalCp = require('internal/child_process');
11+
const oldSpawnSync = internalCp.spawnSync;
912
const { SIGKILL } = process.binding('constants').os.signals;
1013

11-
function spawn(killSignal) {
14+
function spawn(killSignal, beforeSpawn) {
15+
if (beforeSpawn) {
16+
internalCp.spawnSync = common.mustCall(function(opts) {
17+
beforeSpawn(opts);
18+
return oldSpawnSync(opts);
19+
});
20+
}
1221
const child = cp.spawnSync(process.execPath,
1322
[__filename, 'child'],
1423
{killSignal, timeout: 100});
15-
24+
if (beforeSpawn)
25+
internalCp.spawnSync = oldSpawnSync;
1626
assert.strictEqual(child.status, null);
1727
assert.strictEqual(child.error.code, 'ETIMEDOUT');
1828
return child;
@@ -25,26 +35,30 @@ if (process.argv[2] === 'child') {
2535

2636
// Verify that the default kill signal is SIGTERM.
2737
{
28-
const child = spawn();
38+
const child = spawn(undefined, (opts) => {
39+
assert.strictEqual(opts.options.killSignal, undefined);
40+
});
2941

3042
assert.strictEqual(child.signal, 'SIGTERM');
31-
assert.strictEqual(child.options.killSignal, undefined);
3243
}
3344

3445
// Verify that a string signal name is handled properly.
3546
{
36-
const child = spawn('SIGKILL');
47+
const child = spawn('SIGKILL', (opts) => {
48+
assert.strictEqual(opts.options.killSignal, SIGKILL);
49+
});
3750

3851
assert.strictEqual(child.signal, 'SIGKILL');
39-
assert.strictEqual(child.options.killSignal, SIGKILL);
4052
}
4153

4254
// Verify that a numeric signal is handled properly.
4355
{
44-
const child = spawn(SIGKILL);
45-
4656
assert.strictEqual(typeof SIGKILL, 'number');
57+
58+
const child = spawn(SIGKILL, (opts) => {
59+
assert.strictEqual(opts.options.killSignal, SIGKILL);
60+
});
61+
4762
assert.strictEqual(child.signal, 'SIGKILL');
48-
assert.strictEqual(child.options.killSignal, SIGKILL);
4963
}
5064
}

test/parallel/test-child-process-spawnsync-shell.js

+21-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
// Flags: --expose_internals
12
'use strict';
23
const common = require('../common');
34
const assert = require('assert');
45
const cp = require('child_process');
6+
const internalCp = require('internal/child_process');
7+
const oldSpawnSync = internalCp.spawnSync;
58

69
// Verify that a shell is, in fact, executed
710
const doesNotExist = cp.spawnSync('does-not-exist', {shell: true});
@@ -16,10 +19,14 @@ else
1619
assert.strictEqual(doesNotExist.status, 127); // Exit code of /bin/sh
1720

1821
// Verify that passing arguments works
22+
internalCp.spawnSync = common.mustCall(function(opts) {
23+
assert.strictEqual(opts.args[opts.args.length - 1].replace(/"/g, ''),
24+
'echo foo');
25+
return oldSpawnSync(opts);
26+
});
1927
const echo = cp.spawnSync('echo', ['foo'], {shell: true});
28+
internalCp.spawnSync = oldSpawnSync;
2029

21-
assert.strictEqual(echo.args[echo.args.length - 1].replace(/"/g, ''),
22-
'echo foo');
2330
assert.strictEqual(echo.stdout.toString().trim(), 'foo');
2431

2532
// Verify that shell features can be used
@@ -52,16 +59,18 @@ assert.strictEqual(env.stdout.toString().trim(), 'buzz');
5259
const shellFlags = platform === 'win32' ? ['/d', '/s', '/c'] : ['-c'];
5360
const outputCmd = platform === 'win32' ? `"${cmd}"` : cmd;
5461
const windowsVerbatim = platform === 'win32' ? true : undefined;
55-
const result = cp.spawnSync(cmd, { shell });
56-
57-
assert.strictEqual(result.file, shellOutput);
58-
assert.deepStrictEqual(result.args,
59-
[shellOutput, ...shellFlags, outputCmd]);
60-
assert.strictEqual(result.options.shell, shell);
61-
assert.strictEqual(result.options.file, result.file);
62-
assert.deepStrictEqual(result.options.args, result.args);
63-
assert.strictEqual(result.options.windowsVerbatimArguments,
64-
windowsVerbatim);
62+
internalCp.spawnSync = common.mustCall(function(opts) {
63+
assert.strictEqual(opts.file, shellOutput);
64+
assert.deepStrictEqual(opts.args,
65+
[shellOutput, ...shellFlags, outputCmd]);
66+
assert.strictEqual(opts.options.shell, shell);
67+
assert.strictEqual(opts.options.file, opts.file);
68+
assert.deepStrictEqual(opts.options.args, opts.args);
69+
assert.strictEqual(opts.options.windowsVerbatimArguments,
70+
windowsVerbatim);
71+
});
72+
cp.spawnSync(cmd, { shell });
73+
internalCp.spawnSync = oldSpawnSync;
6574
}
6675

6776
// Test Unix platforms with the default shell.

0 commit comments

Comments
 (0)