From ff3ce11894797d031956ca0cb144e600d2b36aa3 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Wed, 20 Jul 2016 12:49:41 -0400 Subject: [PATCH] child_process: support stdio option in fork() This commit allows child_process.fork() to pass stdio options to spawn(). This allows fork() to more easily take advantage of additional stdio channels. Refs: https://github.com/nodejs/node-v0.x-archive/issues/5727 PR-URL: https://github.com/nodejs/node/pull/7811 Reviewed-By: Myles Borins Reviewed-By: Minwoo Jung Reviewed-By: Santiago Gimeno --- doc/api/child_process.md | 2 + lib/child_process.js | 12 +++-- .../parallel/test-child-process-fork-stdio.js | 54 +++++++++++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 test/parallel/test-child-process-fork-stdio.js diff --git a/doc/api/child_process.md b/doc/api/child_process.md index e79b411e91eacf..7c01e34cecb7b2 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -253,6 +253,8 @@ added: v0.5.0 piped to the parent, otherwise they will be inherited from the parent, see the `'pipe'` and `'inherit'` options for [`child_process.spawn()`][]'s [`stdio`][] for more details (Default: `false`) + * `stdio` {Array} Supports the array version of [`child_process.spawn()`][]'s + [`stdio`][] option. When this option is provided, it overrides `silent`. * `uid` {Number} Sets the user identity of the process. (See setuid(2).) * `gid` {Number} Sets the group identity of the process. (See setgid(2).) * Return: {ChildProcess} diff --git a/lib/child_process.js b/lib/child_process.js index b6768c654ad592..6e77caba23f9d9 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -49,10 +49,14 @@ exports.fork = function(modulePath /*, args, options*/) { args = execArgv.concat([modulePath], args); - // Leave stdin open for the IPC channel. stdout and stderr should be the - // same as the parent's if silent isn't set. - options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] : - [0, 1, 2, 'ipc']; + if (!Array.isArray(options.stdio)) { + // Leave stdin open for the IPC channel. stdout and stderr should be the + // same as the parent's if silent isn't set. + options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] : + [0, 1, 2, 'ipc']; + } else if (options.stdio.indexOf('ipc') === -1) { + throw new TypeError('Forked processes must have an IPC channel'); + } options.execPath = options.execPath || process.execPath; diff --git a/test/parallel/test-child-process-fork-stdio.js b/test/parallel/test-child-process-fork-stdio.js new file mode 100644 index 00000000000000..89a5ff98e5f8a8 --- /dev/null +++ b/test/parallel/test-child-process-fork-stdio.js @@ -0,0 +1,54 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const net = require('net'); + +if (process.argv[2] === 'child') { + process.stdout.write('this should be ignored'); + process.stderr.write('this should not be ignored'); + + const pipe = new net.Socket({ fd: 4 }); + + process.on('disconnect', () => { + pipe.unref(); + }); + + pipe.setEncoding('utf8'); + pipe.on('data', (data) => { + process.send(data); + }); +} else { + assert.throws(() => { + cp.fork(__filename, {stdio: ['pipe', 'pipe', 'pipe', 'pipe']}); + }, /Forked processes must have an IPC channel/); + + let ipc = ''; + let stderr = ''; + const buf = Buffer.from('data to send via pipe'); + const child = cp.fork(__filename, ['child'], { + stdio: [0, 'ignore', 'pipe', 'ipc', 'pipe'] + }); + + assert.strictEqual(child.stdout, null); + + child.on('message', (msg) => { + ipc += msg; + + if (ipc === buf.toString()) { + child.disconnect(); + } + }); + + child.stderr.on('data', (chunk) => { + stderr += chunk; + }); + + child.on('exit', common.mustCall((code, signal) => { + assert.strictEqual(code, 0); + assert.strictEqual(signal, null); + assert.strictEqual(stderr, 'this should not be ignored'); + })); + + child.stdio[4].write(buf); +}