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;
35 args = execArgv.concat([modulePath], args);
37 // Leave stdin open for the IPC channel. stdout and stderr should be the
38 // same as the parent's if silent isn't set.
39 options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] :
42 options.execPath = options.execPath || process.execPath;
44 return spawn(options.execPath, args, options);
48 exports._forkChild = function(fd) {
50 var p = new Pipe(true);
53 const control = setupChannel(process, p);
54 process.on('newListener', function(name) {
55 if (name === 'message' || name === 'disconnect') control.ref();
57 process.on('removeListener', function(name) {
58 if (name === 'message' || name === 'disconnect') control.unref();
63 function normalizeExecArgs(command /*, options, callback*/) {
64 var file, args, options, callback;
66 if (typeof arguments[1] === 'function') {
68 callback = arguments[1];
70 options = arguments[1];
71 callback = arguments[2];
74 if (process.platform === 'win32') {
75 file = process.env.comspec || 'cmd.exe';
76 args = ['/s', '/c', '"' + command + '"'];
77 // Make a shallow copy before patching so we don't clobber the user's
79 options = util._extend({}, options);
80 options.windowsVerbatimArguments = true;
83 args = ['-c', command];
86 if (options && options.shell)
99 exports.exec = function(command /*, options, callback*/) {
100 var opts = normalizeExecArgs.apply(null, arguments);
101 return exports.execFile(opts.file,
108 exports.execFile = function(file /*, args, options, callback*/) {
109 var args = [], callback;
113 maxBuffer: 200 * 1024,
114 killSignal: 'SIGTERM',
119 // Parse the optional positional parameters.
121 if (pos < arguments.length && Array.isArray(arguments[pos])) {
122 args = arguments[pos++];
123 } else if (pos < arguments.length && arguments[pos] == null) {
127 if (pos < arguments.length && typeof arguments[pos] === 'object') {
128 options = util._extend(options, arguments[pos++]);
129 } else if (pos < arguments.length && arguments[pos] == null) {
133 if (pos < arguments.length && typeof arguments[pos] === 'function') {
134 callback = arguments[pos++];
137 if (pos === 1 && arguments.length > 1) {
138 throw new TypeError('Incorrect value of args option');
141 var child = spawn(file, args, {
146 windowsVerbatimArguments: !!options.windowsVerbatimArguments
152 if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) {
153 encoding = options.encoding;
169 function exithandler(code, signal) {
174 clearTimeout(timeoutId);
178 if (!callback) return;
184 stdout = Buffer.concat(_stdout);
185 stderr = Buffer.concat(_stderr);
192 // Will be handled later
193 } else if (code === 0 && signal === null) {
194 callback(null, stdout, stderr);
199 if (args.length !== 0)
200 cmd += ' ' + args.join(' ');
203 ex = new Error('Command failed: ' + cmd + '\n' + stderr);
204 ex.killed = child.killed || killed;
205 ex.code = code < 0 ? uv.errname(code) : code;
210 callback(ex, stdout, stderr);
213 function errorhandler(e) {
215 child.stdout.destroy();
216 child.stderr.destroy();
221 child.stdout.destroy();
222 child.stderr.destroy();
226 child.kill(options.killSignal);
233 if (options.timeout > 0) {
234 timeoutId = setTimeout(function() {
240 child.stdout.addListener('data', function(chunk) {
241 stdoutLen += chunk.length;
243 if (stdoutLen > options.maxBuffer) {
244 ex = new Error('stdout maxBuffer exceeded.');
254 child.stderr.addListener('data', function(chunk) {
255 stderrLen += chunk.length;
257 if (stderrLen > options.maxBuffer) {
258 ex = new Error('stderr maxBuffer exceeded.');
269 child.stderr.setEncoding(encoding);
270 child.stdout.setEncoding(encoding);
273 child.addListener('close', exithandler);
274 child.addListener('error', errorhandler);
279 var _deprecatedCustomFds = internalUtil.deprecate(function(options) {
280 options.stdio = options.customFds.map(function(fd) {
281 return fd === -1 ? 'pipe' : fd;
283 }, 'child_process: options.customFds option is deprecated. ' +
284 'Use options.stdio instead.');
286 function _convertCustomFds(options) {
287 if (options && options.customFds && !options.stdio) {
288 _deprecatedCustomFds(options);
292 function normalizeSpawnArguments(file /*, args, options*/) {
295 if (Array.isArray(arguments[1])) {
296 args = arguments[1].slice(0);
297 options = arguments[2];
298 } else if (arguments[1] !== undefined &&
299 (arguments[1] === null || typeof arguments[1] !== 'object')) {
300 throw new TypeError('Incorrect value of args option');
303 options = arguments[1];
306 if (options === undefined)
308 else if (options === null || typeof options !== 'object')
309 throw new TypeError('options argument must be an object');
311 options = util._extend({}, options);
314 var env = options.env || process.env;
317 for (var key in env) {
318 envPairs.push(key + '=' + env[key]);
321 _convertCustomFds(options);
332 var spawn = exports.spawn = function(/*file, args, options*/) {
333 var opts = normalizeSpawnArguments.apply(null, arguments);
334 var options = opts.options;
335 var child = new ChildProcess();
337 debug('spawn', opts.args, options);
343 windowsVerbatimArguments: !!options.windowsVerbatimArguments,
344 detached: !!options.detached,
345 envPairs: opts.envPairs,
346 stdio: options.stdio,
355 function lookupSignal(signal) {
356 if (typeof signal === 'number')
359 if (!(signal in constants))
360 throw new Error('Unknown signal: ' + signal);
362 return constants[signal];
366 function spawnSync(/*file, args, options*/) {
367 var opts = normalizeSpawnArguments.apply(null, arguments);
369 var options = opts.options;
373 debug('spawnSync', opts.args, options);
375 options.file = opts.file;
376 options.args = opts.args;
377 options.envPairs = opts.envPairs;
379 if (options.killSignal)
380 options.killSignal = lookupSignal(options.killSignal);
382 options.stdio = _validateStdio(options.stdio || 'pipe', true).stdio;
385 var stdin = options.stdio[0] = util._extend({}, options.stdio[0]);
386 stdin.input = options.input;
389 // We may want to pass data in on any given fd, ensure it is a valid buffer
390 for (i = 0; i < options.stdio.length; i++) {
391 var input = options.stdio[i] && options.stdio[i].input;
393 var pipe = options.stdio[i] = util._extend({}, options.stdio[i]);
394 if (Buffer.isBuffer(input))
396 else if (typeof input === 'string')
397 pipe.input = new Buffer(input, options.encoding);
399 throw new TypeError(util.format(
400 'stdio[%d] should be Buffer or string not %s',
406 var result = spawn_sync.spawn(options);
408 if (result.output && options.encoding) {
409 for (i = 0; i < result.output.length; i++) {
410 if (!result.output[i])
412 result.output[i] = result.output[i].toString(options.encoding);
416 result.stdout = result.output && result.output[1];
417 result.stderr = result.output && result.output[2];
420 result.error = errnoException(result.error, 'spawnSync ' + opts.file);
421 result.error.path = opts.file;
422 result.error.spawnargs = opts.args.slice(1);
425 util._extend(result, opts);
429 exports.spawnSync = spawnSync;
432 function checkExecSyncError(ret) {
433 if (ret.error || ret.status !== 0) {
438 var msg = 'Command failed: ' +
439 (ret.cmd ? ret.cmd : ret.args.join(' ')) +
440 (ret.stderr ? '\n' + ret.stderr.toString() : '');
441 err = new Error(msg);
444 util._extend(err, ret);
452 function execFileSync(/*command, args, options*/) {
453 var opts = normalizeSpawnArguments.apply(null, arguments);
454 var inheritStderr = !opts.options.stdio;
456 var ret = spawnSync(opts.file, opts.args.slice(1), opts.options);
459 process.stderr.write(ret.stderr);
461 var err = checkExecSyncError(ret);
468 exports.execFileSync = execFileSync;
471 function execSync(/*command, options*/) {
472 var opts = normalizeExecArgs.apply(null, arguments);
473 var inheritStderr = opts.options ? !opts.options.stdio : true;
475 var ret = spawnSync(opts.file, opts.args, opts.options);
479 process.stderr.write(ret.stderr);
481 var err = checkExecSyncError(ret);
488 exports.execSync = execSync;