Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions lib/internal/child_process.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const dgram = require('dgram');
const inspect = require('internal/util/inspect').inspect;
const assert = require('internal/assert');

const { Process } = internalBinding('process_wrap');
const { Process, constants: processConstants } = internalBinding('process_wrap');
const {
WriteWrap,
kReadBytesOrError,
Expand Down Expand Up @@ -397,7 +397,24 @@ ChildProcess.prototype.spawn = function spawn(options) {
childProcessSpawn.start.publish({ process: this, options });
}

const err = this._handle.spawn(options);
let spawnFlags = 0;
if (options.detached)
spawnFlags |= processConstants.kProcessFlagDetached;
if (options.windowsHide)
spawnFlags |= processConstants.kProcessFlagWindowsHide;
if (options.windowsVerbatimArguments)
spawnFlags |= processConstants.kProcessFlagWindowsVerbatimArguments;

const err = this._handle.spawn(
options.file,
options.args,
options.cwd,
options.envPairs,
options.stdio,
spawnFlags,
options.uid,
options.gid,
);

// Run-time errors should emit an error, not throw an exception.
if (err === UV_EACCES ||
Expand Down
112 changes: 38 additions & 74 deletions src/process_wrap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ using v8::Nothing;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Uint32;
using v8::Value;

namespace {

enum ProcessFlags : uint32_t {
kProcessFlagNone = 0,
kProcessFlagDetached = 1 << 0,
kProcessFlagWindowsHide = 1 << 1,
kProcessFlagWindowsVerbatimArguments = 1 << 2,
};

class ProcessWrap : public HandleWrap {
public:
static void Initialize(Local<Object> target,
Expand All @@ -71,6 +79,12 @@ class ProcessWrap : public HandleWrap {
SetProtoMethod(isolate, constructor, "kill", Kill);

SetConstructorFunction(context, target, "Process", constructor);

Local<Object> constants = Object::New(isolate);
NODE_DEFINE_CONSTANT(constants, kProcessFlagDetached);
NODE_DEFINE_CONSTANT(constants, kProcessFlagWindowsHide);
NODE_DEFINE_CONSTANT(constants, kProcessFlagWindowsVerbatimArguments);
target->Set(context, env->constants_string(), constants).Check();
}

static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
Expand Down Expand Up @@ -118,14 +132,9 @@ class ProcessWrap : public HandleWrap {

static Maybe<void> ParseStdioOptions(
Environment* env,
Local<Object> js_options,
Local<Value> stdios_val,
std::vector<uv_stdio_container_t>* options_stdio) {
Local<Context> context = env->context();
Local<String> stdio_key = env->stdio_string();
Local<Value> stdios_val;
if (!js_options->Get(context, stdio_key).ToLocal(&stdios_val)) {
return Nothing<void>();
}
if (!stdios_val->IsArray()) {
THROW_ERR_INVALID_ARG_TYPE(env, "options.stdio must be an array");
return Nothing<void>();
Expand Down Expand Up @@ -188,46 +197,30 @@ class ProcessWrap : public HandleWrap {
ASSIGN_OR_RETURN_UNWRAP(&wrap, args.This());
int err = 0;

if (!args[0]->IsObject()) {
return THROW_ERR_INVALID_ARG_TYPE(env, "options must be an object");
}

Local<Object> js_options = args[0].As<Object>();

uv_process_options_t options;
memset(&options, 0, sizeof(uv_process_options_t));

options.exit_cb = OnExit;

// options.file
Local<Value> file_v;
if (!js_options->Get(context, env->file_string()).ToLocal(&file_v)) {
return;
}
CHECK(file_v->IsString());
node::Utf8Value file(env->isolate(), file_v);
// args[0] file
CHECK(args[0]->IsString());
node::Utf8Value file(env->isolate(), args[0]);
options.file = *file;

THROW_IF_INSUFFICIENT_PERMISSIONS(
env, permission::PermissionScope::kChildProcess, file.ToStringView());

// options.uid
Local<Value> uid_v;
if (!js_options->Get(context, env->uid_string()).ToLocal(&uid_v)) {
return;
}
// args[6] uid
Local<Value> uid_v = args[6];
if (!uid_v->IsUndefined() && !uid_v->IsNull()) {
CHECK(uid_v->IsInt32());
const int32_t uid = uid_v.As<Int32>()->Value();
options.flags |= UV_PROCESS_SETUID;
options.uid = static_cast<uv_uid_t>(uid);
}

// options.gid
Local<Value> gid_v;
if (!js_options->Get(context, env->gid_string()).ToLocal(&gid_v)) {
return;
}
// args[7] gid
Local<Value> gid_v = args[7];
if (!gid_v->IsUndefined() && !gid_v->IsNull()) {
CHECK(gid_v->IsInt32());
const int32_t gid = gid_v.As<Int32>()->Value();
Expand All @@ -244,15 +237,11 @@ class ProcessWrap : public HandleWrap {
err = UV_EINVAL;
#endif

// options.args
Local<Value> argv_v;
if (!js_options->Get(context, env->args_string()).ToLocal(&argv_v)) {
return;
}
// args[1] args
std::vector<char*> options_args;
std::vector<std::string> args_vals;
if (argv_v->IsArray()) {
Local<Array> js_argv = argv_v.As<Array>();
if (args[1]->IsArray()) {
Local<Array> js_argv = args[1].As<Array>();
int argc = js_argv->Length();
CHECK_LT(argc, INT_MAX); // Check for overflow.
args_vals.reserve(argc);
Expand All @@ -273,26 +262,18 @@ class ProcessWrap : public HandleWrap {
options.args = options_args.data();
}

// options.cwd
Local<Value> cwd_v;
if (!js_options->Get(context, env->cwd_string()).ToLocal(&cwd_v)) {
return;
}
// args[2] cwd
node::Utf8Value cwd(env->isolate(),
cwd_v->IsString() ? cwd_v : Local<Value>());
args[2]->IsString() ? args[2] : Local<Value>());
if (cwd.length() > 0) {
options.cwd = *cwd;
}

// options.env
Local<Value> env_v;
if (!js_options->Get(context, env->env_pairs_string()).ToLocal(&env_v)) {
return;
}
// args[3] envPairs
std::vector<char*> options_env;
std::vector<std::string> env_vals;
if (env_v->IsArray()) {
Local<Array> env_opt = env_v.As<Array>();
if (args[3]->IsArray()) {
Local<Array> env_opt = args[3].As<Array>();
int envc = env_opt->Length();
CHECK_LT(envc, INT_MAX); // Check for overflow.
env_vals.reserve(envc);
Expand All @@ -313,48 +294,31 @@ class ProcessWrap : public HandleWrap {
options.env = options_env.data();
}

// options.stdio
// args[4] stdio
std::vector<uv_stdio_container_t> options_stdio;
if (ParseStdioOptions(env, js_options, &options_stdio).IsNothing()) {
if (ParseStdioOptions(env, args[4], &options_stdio).IsNothing()) {
return;
}
options.stdio = options_stdio.data();
options.stdio_count = options_stdio.size();

// options.windowsHide
Local<Value> hide_v;
if (!js_options->Get(context, env->windows_hide_string())
.ToLocal(&hide_v)) {
return;
}
// args[5] flags (detached, windowsHide, windowsVerbatimArguments)
CHECK(args[5]->IsUint32());
const uint32_t flags = args[5].As<Uint32>()->Value();

if (hide_v->IsTrue()) {
if (flags & kProcessFlagWindowsHide) {
options.flags |= UV_PROCESS_WINDOWS_HIDE;
}

if (env->hide_console_windows()) {
options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE;
}

// options.windows_verbatim_arguments
Local<Value> wva_v;
if (!js_options->Get(context, env->windows_verbatim_arguments_string())
.ToLocal(&wva_v)) {
return;
}

if (wva_v->IsTrue()) {
if (flags & kProcessFlagWindowsVerbatimArguments) {
options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
}

// options.detached
Local<Value> detached_v;
if (!js_options->Get(context, env->detached_string())
.ToLocal(&detached_v)) {
return;
}

if (detached_v->IsTrue()) {
if (flags & kProcessFlagDetached) {
options.flags |= UV_PROCESS_DETACHED;
}

Expand Down
2 changes: 2 additions & 0 deletions typings/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { MessagingBinding } from './internalBinding/messaging';
import { OptionsBinding } from './internalBinding/options';
import { OSBinding } from './internalBinding/os';
import { ProcessBinding } from './internalBinding/process';
import { ProcessWrapBinding } from './internalBinding/process_wrap';
import { SeaBinding } from './internalBinding/sea';
import { SerdesBinding } from './internalBinding/serdes';
import { StringDecoderBinding } from './internalBinding/string_decoder';
Expand Down Expand Up @@ -53,6 +54,7 @@ interface InternalBindingMap {
options: OptionsBinding;
os: OSBinding;
process: ProcessBinding;
process_wrap: ProcessWrapBinding;
sea: SeaBinding;
serdes: SerdesBinding;
string_decoder: StringDecoderBinding;
Expand Down
43 changes: 43 additions & 0 deletions typings/internalBinding/process_wrap.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { owner_symbol } from './symbols';

declare namespace InternalProcessWrapBinding {
type StdioType = 'ignore' | 'pipe' | 'overlapped' | 'wrap' | 'inherit' | 'fd';

interface StdioContainer {
type: StdioType;
handle?: object;
fd?: number;
}

class Process {
constructor();
[owner_symbol]?: object;
pid: number;
onexit: (exitCode: number, signalCode: string) => void;
spawn(
file: string,
args: string[] | undefined,
cwd: string | undefined,
envPairs: string[] | undefined,
stdio: StdioContainer[],
flags: number,
uid: number | null | undefined,
gid: number | null | undefined,
): number;
kill(signal: number): number;
ref(): void;
unref(): void;
close(callback?: () => void): void;
}

interface ProcessConstants {
kProcessFlagDetached: number;
kProcessFlagWindowsHide: number;
kProcessFlagWindowsVerbatimArguments: number;
}
}

export interface ProcessWrapBinding {
Process: typeof InternalProcessWrapBinding.Process;
constants: InternalProcessWrapBinding.ProcessConstants;
}
Loading