From 75c6d9dd95bbdb926d84cb3a9f518eff88651709 Mon Sep 17 00:00:00 2001
From: cjihrig <cjihrig@gmail.com>
Date: Fri, 22 Jul 2016 12:05:27 -0400
Subject: [PATCH] cluster: support stdio option for workers

This commit allows setupMaster() to configure the stdio channels
for worker processes.

Refs: https://github.com/nodejs/node-v0.x-archive/issues/5727
Refs: https://github.com/nodejs/node/pull/7811
PR-URL: https://github.com/nodejs/node/pull/7838
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
 doc/api/cluster.md                       |  5 +++
 lib/cluster.js                           |  1 +
 test/parallel/test-cluster-fork-stdio.js | 40 ++++++++++++++++++++++++
 3 files changed, 46 insertions(+)
 create mode 100644 test/parallel/test-cluster-fork-stdio.js

diff --git a/doc/api/cluster.md b/doc/api/cluster.md
index 6f0b424e05e083..616be9b59afd14 100644
--- a/doc/api/cluster.md
+++ b/doc/api/cluster.md
@@ -626,6 +626,9 @@ values are `"rr"` and `"none"`.
     (Default=`process.argv.slice(2)`)
   * `silent` {Boolean} whether or not to send output to parent's stdio.
     (Default=`false`)
+  * `stdio` {Array} Configures the stdio of forked processes. Because the
+    cluster module relies on IPC to function, this configuration must contain an
+    `'ipc'` entry. 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).)
 
@@ -642,6 +645,8 @@ This object is not supposed to be changed or set manually, by you.
     (Default=`process.argv.slice(2)`)
   * `silent` {Boolean} whether or not to send output to parent's stdio.
     (Default=`false`)
+  * `stdio` {Array} Configures the stdio of forked processes. When this option
+    is provided, it overrides `silent`.
 
 `setupMaster` is used to change the default 'fork' behavior. Once called,
 the settings will be present in `cluster.settings`.
diff --git a/lib/cluster.js b/lib/cluster.js
index 38e6885f6757e9..05397ca69fba7f 100644
--- a/lib/cluster.js
+++ b/lib/cluster.js
@@ -322,6 +322,7 @@ function masterInit() {
       env: workerEnv,
       silent: cluster.settings.silent,
       execArgv: execArgv,
+      stdio: cluster.settings.stdio,
       gid: cluster.settings.gid,
       uid: cluster.settings.uid
     });
diff --git a/test/parallel/test-cluster-fork-stdio.js b/test/parallel/test-cluster-fork-stdio.js
new file mode 100644
index 00000000000000..1d00e0768e5746
--- /dev/null
+++ b/test/parallel/test-cluster-fork-stdio.js
@@ -0,0 +1,40 @@
+'use strict';
+const common = require('../common');
+const assert = require('assert');
+const cluster = require('cluster');
+const net = require('net');
+
+if (cluster.isMaster) {
+  const buf = Buffer.from('foobar');
+
+  cluster.setupMaster({
+    stdio: ['pipe', 'pipe', 'pipe', 'ipc', 'pipe']
+  });
+
+  const worker = cluster.fork();
+  const channel = worker.process.stdio[4];
+  let response = '';
+
+  worker.on('exit', common.mustCall((code, signal) => {
+    assert.strictEqual(code, 0);
+    assert.strictEqual(signal, null);
+  }));
+
+  channel.setEncoding('utf8');
+  channel.on('data', (data) => {
+    response += data;
+
+    if (response === buf.toString()) {
+      worker.disconnect();
+    }
+  });
+  channel.write(buf);
+} else {
+  const pipe = new net.Socket({ fd: 4 });
+
+  pipe.unref();
+  pipe.on('data', (data) => {
+    assert.ok(data instanceof Buffer);
+    pipe.write(data);
+  });
+}