3 const util = require('util');
4 const internalUtil = require('internal/util');
5 const debug = util.debuglog('child_process');
6 const constants = require('constants');
8 const uv = process.binding('uv');
9 const spawn_sync = process.binding('spawn_sync');
10 const Buffer = require('buffer').Buffer;
11 const Pipe = process.binding('pipe_wrap').Pipe;
12 const child_process = require('internal/child_process');
14 const errnoException = util._errnoException;
15 const _validateStdio = child_process._validateStdio;
16 const setupChannel = child_process.setupChannel;
17 const ChildProcess = exports.ChildProcess = child_process.ChildProcess;
19 exports.fork = function(modulePath /*, args, options*/) {
21 // Get options and args arguments.
22 var options, args, execArgv;
23 if (Array.isArray(arguments[1])) {
25 options = util._extend({}, arguments[2]);
26 } else if (arguments[1] && typeof arguments[1] !== 'object') {
27 throw new TypeError('Incorrect value of args option');
30 options = util._extend({}, arguments[1]);
33 // Prepare arguments for fork:
34 execArgv = options.execArgv || process.execArgv;
36 if (execArgv === process.execArgv && process._eval != null) {
37 const index = execArgv.lastIndexOf(process._eval);
39 // Remove the -e switch to avoid fork bombing ourselves.
40 execArgv = execArgv.slice();
41 execArgv.splice(index - 1, 2);
45 args = execArgv.concat([modulePath], args);
47 // Leave stdin open for the IPC channel. stdout and stderr should be the
48 // same as the parent's if silent isn't set.
49 options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
52 options.execPath = options.execPath || process.execPath;
54 return spawn(options.execPath, args, options);
58 exports._forkChild = function(fd) {
60 var p = new Pipe(true);
63 const control = setupChannel(process, p);
64 process.on('newListener', function(name) {
65 if (name === 'message' || name === 'disconnect') control.ref();
67 process.on('removeListener', function(name) {
68 if (name === 'message' || name === 'disconnect') control.unref();
73 function normalizeExecArgs(command /*, options, callback*/) {
74 var file, args, options, callback;
76 if (typeof arguments[1] === 'function') {
78 callback = arguments[1];
80 options = arguments[1];
81 callback = arguments[2];
84 if (process.platform === 'win32') {
85 file = process.env.comspec || 'cmd.exe';
86 args = ['/s', '/c', '"' + command + '"'];
87 // Make a shallow copy before patching so we don't clobber the user's
89 options = util._extend({}, options);
90 options.windowsVerbatimArguments = true;
93 args = ['-c', command];
96 if (options && options.shell)
109 exports.exec = function(command /*, options, callback*/) {
110 var opts = normalizeExecArgs.apply(null, arguments);
111 return exports.execFile(opts.file,
118 exports.execFile = function(file /*, args, options, callback*/) {
119 var args = [], callback;
123 maxBuffer: 200 * 1024,
124 killSignal: 'SIGTERM',
129 // Parse the optional positional parameters.
131 if (pos < arguments.length && Array.isArray(arguments[pos])) {
132 args = arguments[pos++];
133 } else if (pos < arguments.length && arguments[pos] == null) {
137 if (pos < arguments.length && typeof arguments[pos] === 'object') {
138 options = util._extend(options, arguments[pos++]);
139 } else if (pos < arguments.length && arguments[pos] == null) {
143 if (pos < arguments.length && typeof arguments[pos] === 'function') {
144 callback = arguments[pos++];
147 if (pos === 1 && arguments.length > 1) {
148 throw new TypeError('Incorrect value of args option');
151 var child = spawn(file, args, {
156 windowsVerbatimArguments: !!options.windowsVerbatimArguments
162 if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) {
163 encoding = options.encoding;
179 function exithandler(code, signal) {
184 clearTimeout(timeoutId);
188 if (!callback) return;
194 stdout = Buffer.concat(_stdout);
195 stderr = Buffer.concat(_stderr);
202 // Will be handled later
203 } else if (code === 0 && signal === null) {
204 callback(null, stdout, stderr);
209 if (args.length !== 0)
210 cmd += ' ' + args.join(' ');
213 ex = new Error('Command failed: ' + cmd + '\n' + stderr);
214 ex.killed = child.killed || killed;
215 ex.code = code < 0 ? uv.errname(code) : code;
220 callback(ex, stdout, stderr);
223 function errorhandler(e) {
227 child.stdout.destroy();
230 child.stderr.destroy();
237 child.stdout.destroy();
240 child.stderr.destroy();
244 child.kill(options.killSignal);
251 if (options.timeout > 0) {
252 timeoutId = setTimeout(function() {
260 child.stdout.setEncoding(encoding);
262 child.stdout.addListener('data', function(chunk) {
263 stdoutLen += chunk.length;
265 if (stdoutLen > options.maxBuffer) {
266 ex = new Error('stdout maxBuffer exceeded');
279 child.stderr.setEncoding(encoding);
281 child.stderr.addListener('data', function(chunk) {
282 stderrLen += chunk.length;
284 if (stderrLen > options.maxBuffer) {
285 ex = new Error('stderr maxBuffer exceeded');
296 child.addListener('close', exithandler);
297 child.addListener('error', errorhandler);
302 var _deprecatedCustomFds = internalUtil.deprecate(function(options) {
303 options.stdio = options.customFds.map(function(fd) {
304 return fd === -1 ? 'pipe' : fd;
306 }, 'child_process: options.customFds option is deprecated. ' +
307 'Use options.stdio instead.');
309 function _convertCustomFds(options) {
310 if (options && options.customFds && !options.stdio) {
311 _deprecatedCustomFds(options);
315 function normalizeSpawnArguments(file /*, args, options*/) {
318 if (Array.isArray(arguments[1])) {
319 args = arguments[1].slice(0);
320 options = arguments[2];
321 } else if (arguments[1] !== undefined &&
322 (arguments[1] === null || typeof arguments[1] !== 'object')) {
323 throw new TypeError('Incorrect value of args option');
326 options = arguments[1];
329 if (options === undefined)
331 else if (options === null || typeof options !== 'object')
332 throw new TypeError('options argument must be an object');
334 options = util._extend({}, options);
337 var env = options.env || process.env;
340 for (var key in env) {
341 envPairs.push(key + '=' + env[key]);
344 _convertCustomFds(options);
355 var spawn = exports.spawn = function(/*file, args, options*/) {
356 var opts = normalizeSpawnArguments.apply(null, arguments);
357 var options = opts.options;
358 var child = new ChildProcess();
360 debug('spawn', opts.args, options);
366 windowsVerbatimArguments: !!options.windowsVerbatimArguments,
367 detached: !!options.detached,
368 envPairs: opts.envPairs,
369 stdio: options.stdio,
378 function lookupSignal(signal) {
379 if (typeof signal === 'number')
382 if (!(signal in constants))
383 throw new Error('Unknown signal: ' + signal);
385 return constants[signal];
389 function spawnSync(/*file, args, options*/) {
390 var opts = normalizeSpawnArguments.apply(null, arguments);
392 var options = opts.options;
396 debug('spawnSync', opts.args, options);
398 options.file = opts.file;
399 options.args = opts.args;
400 options.envPairs = opts.envPairs;
402 if (options.killSignal)
403 options.killSignal = lookupSignal(options.killSignal);
405 options.stdio = _validateStdio(options.stdio || 'pipe', true).stdio;
408 var stdin = options.stdio[0] = util._extend({}, options.stdio[0]);
409 stdin.input = options.input;
412 // We may want to pass data in on any given fd, ensure it is a valid buffer
413 for (i = 0; i < options.stdio.length; i++) {
414 var input = options.stdio[i] && options.stdio[i].input;
416 var pipe = options.stdio[i] = util._extend({}, options.stdio[i]);
417 if (Buffer.isBuffer(input))
419 else if (typeof input === 'string')
420 pipe.input = new Buffer(input, options.encoding);
422 throw new TypeError(util.format(
423 'stdio[%d] should be Buffer or string not %s',
429 var result = spawn_sync.spawn(options);
431 if (result.output && options.encoding) {
432 for (i = 0; i < result.output.length; i++) {
433 if (!result.output[i])
435 result.output[i] = result.output[i].toString(options.encoding);
439 result.stdout = result.output && result.output[1];
440 result.stderr = result.output && result.output[2];
443 result.error = errnoException(result.error, 'spawnSync ' + opts.file);
444 result.error.path = opts.file;
445 result.error.spawnargs = opts.args.slice(1);
448 util._extend(result, opts);
452 exports.spawnSync = spawnSync;
455 function checkExecSyncError(ret) {
456 if (ret.error || ret.status !== 0) {
461 var msg = 'Command failed: ' +
462 (ret.cmd ? ret.cmd : ret.args.join(' ')) +
463 (ret.stderr ? '\n' + ret.stderr.toString() : '');
464 err = new Error(msg);
467 util._extend(err, ret);
475 function execFileSync(/*command, args, options*/) {
476 var opts = normalizeSpawnArguments.apply(null, arguments);
477 var inheritStderr = !opts.options.stdio;
479 var ret = spawnSync(opts.file, opts.args.slice(1), opts.options);
482 process.stderr.write(ret.stderr);
484 var err = checkExecSyncError(ret);
491 exports.execFileSync = execFileSync;
494 function execSync(/*command, options*/) {
495 var opts = normalizeExecArgs.apply(null, arguments);
496 var inheritStderr = opts.options ? !opts.options.stdio : true;
498 var ret = spawnSync(opts.file, opts.args, opts.options);
502 process.stderr.write(ret.stderr);
504 var err = checkExecSyncError(ret);
511 exports.execSync = execSync;