Skip to content

Commit

Permalink
child_process: add windowsHide option
Browse files Browse the repository at this point in the history
This commit exposes the UV_PROCESS_WINDOWS_HIDE flag in Node
as a windowsHide option to the child_process methods. The
option is only applicable to Windows, and is ignored elsewhere.

Backport-PR-URL: #16425
Fixes: #15217
PR-URL: #15380
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
cjihrig authored and MylesBorins committed Oct 24, 2017
1 parent ff25ca7 commit 81d01bc
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 0 deletions.
32 changes: 32 additions & 0 deletions doc/api/child_process.md
Expand Up @@ -127,6 +127,10 @@ exec('"my script.cmd" a b', (err, stdout, stderr) => {
### child_process.exec(command[, options][, callback])
<!-- YAML
added: v0.1.90
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
-->

* `command` {string} The command to run, with space-separated arguments.
Expand All @@ -144,6 +148,8 @@ added: v0.1.90
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
* `uid` {number} Sets the user identity of the process (see setuid(2)).
* `gid` {number} Sets the group identity of the process (see setgid(2)).
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* `callback` {Function} called with the output when process terminates.
* `error` {Error}
* `stdout` {string|Buffer}
Expand Down Expand Up @@ -237,6 +243,10 @@ lsExample();
### child_process.execFile(file[, args][, options][, callback])
<!-- YAML
added: v0.1.91
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
-->

* `file` {string} The name or path of the executable file to run.
Expand All @@ -252,6 +262,8 @@ added: v0.1.91
* `killSignal` {string|integer} **Default:** `'SIGTERM'`
* `uid` {number} Sets the user identity of the process (see setuid(2)).
* `gid` {number} Sets the group identity of the process (see setgid(2)).
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* `callback` {Function} Called with the output when process terminates.
* `error` {Error}
* `stdout` {string|Buffer}
Expand Down Expand Up @@ -363,6 +375,9 @@ supported by `child_process.fork()` and will be ignored if set.
<!-- YAML
added: v0.1.90
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
- version: v6.4.0
pr-url: https://github.com/nodejs/node/pull/7696
description: The `argv0` option is supported now.
Expand All @@ -389,6 +404,8 @@ changes:
`'/bin/sh'` on UNIX, and `process.env.ComSpec` on Windows. A different
shell can be specified as a string. See [Shell Requirements][] and
[Default Windows Shell][]. **Default:** `false` (no shell).
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* Returns: {ChildProcess}

The `child_process.spawn()` method spawns a new process using the given
Expand Down Expand Up @@ -648,6 +665,9 @@ configuration at startup.
<!-- YAML
added: v0.11.12
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
- version: v8.0.0
pr-url: https://github.com/nodejs/node/pull/10653
description: The `input` option can now be a `Uint8Array`.
Expand Down Expand Up @@ -677,6 +697,8 @@ changes:
stderr. **Default:** `200*1024` If exceeded, the child process is terminated.
See caveat at [`maxBuffer` and Unicode][].
* `encoding` {string} The encoding used for all stdio inputs and outputs. **Default:** `'buffer'`
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* Returns: {Buffer|string} The stdout from the command.

The `child_process.execFileSync()` method is generally identical to
Expand All @@ -697,6 +719,9 @@ throw an [`Error`][] that will include the full result of the underlying
<!-- YAML
added: v0.11.12
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
- version: v8.0.0
pr-url: https://github.com/nodejs/node/pull/10653
description: The `input` option can now be a `Uint8Array`.
Expand Down Expand Up @@ -726,6 +751,8 @@ changes:
See caveat at [`maxBuffer` and Unicode][].
* `encoding` {string} The encoding used for all stdio inputs and outputs.
**Default:** `'buffer'`
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* Returns: {Buffer|string} The stdout from the command.

The `child_process.execSync()` method is generally identical to
Expand All @@ -748,6 +775,9 @@ execution.
<!-- YAML
added: v0.11.12
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/15380
description: The `windowsHide` option is supported now.
- version: v8.0.0
pr-url: https://github.com/nodejs/node/pull/10653
description: The `input` option can now be a `Uint8Array`.
Expand Down Expand Up @@ -783,6 +813,8 @@ changes:
`'/bin/sh'` on UNIX, and `process.env.ComSpec` on Windows. A different
shell can be specified as a string. See [Shell Requirements][] and
[Default Windows Shell][]. **Default:** `false` (no shell).
* `windowsHide` {boolean} Hide the subprocess console window that would
normally be created on Windows systems. **Default:** `false`.
* Returns: {Object}
* `pid` {number} Pid of the child process.
* `output` {Array} Array of results from stdio output.
Expand Down
8 changes: 8 additions & 0 deletions lib/child_process.js
Expand Up @@ -212,6 +212,7 @@ exports.execFile = function(file /*, args, options, callback*/) {
gid: options.gid,
uid: options.uid,
shell: options.shell,
windowsHide: !!options.windowsHide,
windowsVerbatimArguments: !!options.windowsVerbatimArguments
});

Expand Down Expand Up @@ -428,6 +429,12 @@ function normalizeSpawnArguments(file, args, options) {
throw new TypeError('"argv0" must be a string');
}

// Validate windowsHide, if present.
if (options.windowsHide != null &&
typeof options.windowsHide !== 'boolean') {
throw new TypeError('"windowsHide" must be a boolean');
}

// Validate windowsVerbatimArguments, if present.
if (options.windowsVerbatimArguments != null &&
typeof options.windowsVerbatimArguments !== 'boolean') {
Expand Down Expand Up @@ -493,6 +500,7 @@ var spawn = exports.spawn = function(/*file, args, options*/) {
file: opts.file,
args: opts.args,
cwd: options.cwd,
windowsHide: !!options.windowsHide,
windowsVerbatimArguments: !!options.windowsVerbatimArguments,
detached: !!options.detached,
envPairs: opts.envPairs,
Expand Down
1 change: 1 addition & 0 deletions src/env.h
Expand Up @@ -287,6 +287,7 @@ class ModuleWrap;
V(verify_error_string, "verifyError") \
V(version_string, "version") \
V(weight_string, "weight") \
V(windows_hide_string, "windowsHide") \
V(windows_verbatim_arguments_string, "windowsVerbatimArguments") \
V(wrap_string, "wrap") \
V(writable_string, "writable") \
Expand Down
7 changes: 7 additions & 0 deletions src/process_wrap.cc
Expand Up @@ -211,6 +211,13 @@ class ProcessWrap : public HandleWrap {
// options.stdio
ParseStdioOptions(env, js_options, &options);

// options.windowsHide
Local<String> windows_hide_key = env->windows_hide_string();

if (js_options->Get(windows_hide_key)->IsTrue()) {
options.flags |= UV_PROCESS_WINDOWS_HIDE;
}

// options.windows_verbatim_arguments
Local<String> windows_verbatim_arguments_key =
env->windows_verbatim_arguments_string();
Expand Down
5 changes: 5 additions & 0 deletions src/spawn_sync.cc
Expand Up @@ -777,6 +777,11 @@ int SyncProcessRunner::ParseOptions(Local<Value> js_value) {
if (js_options->Get(env()->detached_string())->BooleanValue())
uv_process_options_.flags |= UV_PROCESS_DETACHED;

Local<String> win_hide = env()->windows_hide_string();

if (js_options->Get(win_hide)->BooleanValue())
uv_process_options_.flags |= UV_PROCESS_WINDOWS_HIDE;

Local<String> wba = env()->windows_verbatim_arguments_string();

if (js_options->Get(wba)->BooleanValue())
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-child-process-spawnsync-shell.js
Expand Up @@ -60,6 +60,7 @@ assert.strictEqual(env.stdout.toString().trim(), 'buzz');
assert.strictEqual(result.options.shell, shell);
assert.strictEqual(result.options.file, result.file);
assert.deepStrictEqual(result.options.args, result.args);
assert.strictEqual(result.options.windowsHide, undefined);
assert.strictEqual(result.options.windowsVerbatimArguments,
windowsVerbatim);
}
Expand Down
16 changes: 16 additions & 0 deletions test/parallel/test-child-process-spawnsync-validation-errors.js
Expand Up @@ -124,6 +124,22 @@ if (!common.isWindows) {
fail('argv0', common.mustNotCall(), err);
}

{
// Validate the windowsHide option
const err = /^TypeError: "windowsHide" must be a boolean$/;

pass('windowsHide', undefined);
pass('windowsHide', null);
pass('windowsHide', true);
pass('windowsHide', false);
fail('windowsHide', 0, err);
fail('windowsHide', 1, err);
fail('windowsHide', __dirname, err);
fail('windowsHide', [], err);
fail('windowsHide', {}, err);
fail('windowsHide', common.mustNotCall(), err);
}

{
// Validate the windowsVerbatimArguments option
const err = /^TypeError: "windowsVerbatimArguments" must be a boolean$/;
Expand Down
48 changes: 48 additions & 0 deletions test/parallel/test-child-process-windows-hide.js
@@ -0,0 +1,48 @@
// Flags: --expose_internals
'use strict';
const common = require('../common');
const assert = require('assert');
const cp = require('child_process');
const internalCp = require('internal/child_process');
const cmd = process.execPath;
const args = ['-p', '42'];
const options = { windowsHide: true };

// Since windowsHide isn't really observable, monkey patch spawn() to verify
// the flag is being passed through correctly. spawnSync() is kind enough to
// give all of the parsed inputs back in the result.
const originalSpawn = internalCp.ChildProcess.prototype.spawn;

internalCp.ChildProcess.prototype.spawn = common.mustCall(function(options) {
assert.strictEqual(options.windowsHide, true);
return originalSpawn.apply(this, arguments);
}, 2);

{
const child = cp.spawnSync(cmd, args, options);

assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.options.windowsHide, true);
assert.strictEqual(child.stdout.toString().trim(), '42');
assert.strictEqual(child.stderr.toString().trim(), '');
}

{
const child = cp.spawn(cmd, args, options);

child.on('exit', common.mustCall((code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
}));
}

{
const callback = common.mustCall((err, stdout, stderr) => {
assert.ifError(err);
assert.strictEqual(stdout.trim(), '42');
assert.strictEqual(stderr.trim(), '');
});

cp.execFile(cmd, args, options, callback);
}

0 comments on commit 81d01bc

Please sign in to comment.