mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
process: add execve
PR-URL: https://github.com/nodejs/node/pull/56496 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Bryan English <bryan@bryanenglish.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
1 parent
1b5b019de1
commit
13a9d4c160
14 files changed
+470
-2
No files matched your search
@@ -1319,6 +1319,14 @@ added: v16.18.0
|
||||
|
||||
Emitted when a new process is created.
|
||||
|
||||
`execve`
|
||||
|
||||
* `execPath` {string}
|
||||
* `args` {string\[]}
|
||||
* `env` {string\[]}
|
||||
|
||||
Emitted when [`process.execve()`][] is invoked.
|
||||
|
||||
#### Worker Thread
|
||||
|
||||
<!-- YAML
|
||||
@@ -1348,5 +1356,6 @@ Emitted when a new thread is created.
|
||||
[`end` event]: #endevent
|
||||
[`error` event]: #errorevent
|
||||
[`net.Server.listen()`]: net.md#serverlisten
|
||||
[`process.execve()`]: process.md#processexecvefile-args-env
|
||||
[`start` event]: #startevent
|
||||
[context loss]: async_context.md#troubleshooting-context-loss
|
||||
+28
-2
@@ -2519,8 +2519,7 @@ if (process.getuid) {
|
||||
}
|
||||
```
|
||||
|
||||
This function is only available on POSIX platforms (i.e. not Windows or
|
||||
Android).
|
||||
This function not available on Windows.
|
||||
|
||||
## `process.hasUncaughtExceptionCaptureCallback()`
|
||||
|
||||
@@ -3346,6 +3345,33 @@ In custom builds from non-release versions of the source tree, only the
|
||||
`name` property may be present. The additional properties should not be
|
||||
relied upon to exist.
|
||||
|
||||
## `process.execve(file[, args[, env]])`
|
||||
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
> Stability: 1 - Experimental
|
||||
|
||||
* `file` {string} The name or path of the executable file to run.
|
||||
* `args` {string\[]} List of string arguments. No argument can contain a null-byte (`\u0000`).
|
||||
* `env` {Object} Environment key-value pairs.
|
||||
No key or value can contain a null-byte (`\u0000`).
|
||||
**Default:** `process.env`.
|
||||
|
||||
Replaces the current process with a new process.
|
||||
|
||||
This is achieved by using the `execve` POSIX function and therefore no memory or other
|
||||
resources from the current process are preserved, except for the standard input,
|
||||
standard output and standard error file descriptor.
|
||||
|
||||
All other resources are discarded by the system when the processes are swapped, without triggering
|
||||
any exit or close events and without running any cleanup handler.
|
||||
|
||||
This function will never return, unless an error occurred.
|
||||
|
||||
This function is only available on POSIX platforms (i.e. not Windows or Android).
|
||||
|
||||
## `process.report`
|
||||
|
||||
<!-- YAML
|
||||
|
||||
@@ -177,6 +177,7 @@ const rawMethods = internalBinding('process_methods');
|
||||
process.availableMemory = rawMethods.availableMemory;
|
||||
process.kill = wrapped.kill;
|
||||
process.exit = wrapped.exit;
|
||||
process.execve = wrapped.execve;
|
||||
process.ref = perThreadSetup.ref;
|
||||
process.unref = perThreadSetup.unref;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ const {
|
||||
FunctionPrototypeCall,
|
||||
NumberMAX_SAFE_INTEGER,
|
||||
ObjectDefineProperty,
|
||||
ObjectEntries,
|
||||
ObjectFreeze,
|
||||
ReflectApply,
|
||||
RegExpPrototypeExec,
|
||||
@@ -24,6 +25,7 @@ const {
|
||||
SetPrototypeEntries,
|
||||
SetPrototypeValues,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeIncludes,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeSlice,
|
||||
Symbol,
|
||||
@@ -34,20 +36,27 @@ const {
|
||||
const {
|
||||
ErrnoException,
|
||||
codes: {
|
||||
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_INVALID_ARG_VALUE,
|
||||
ERR_OPERATION_FAILED,
|
||||
ERR_OUT_OF_RANGE,
|
||||
ERR_UNKNOWN_SIGNAL,
|
||||
ERR_WORKER_UNSUPPORTED_OPERATION,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
const { emitExperimentalWarning } = require('internal/util');
|
||||
const format = require('internal/util/inspect').format;
|
||||
const {
|
||||
validateArray,
|
||||
validateNumber,
|
||||
validateObject,
|
||||
validateString,
|
||||
} = require('internal/validators');
|
||||
|
||||
const dc = require('diagnostics_channel');
|
||||
const execveDiagnosticChannel = dc.channel('process.execve');
|
||||
|
||||
const constants = internalBinding('constants').os.signals;
|
||||
|
||||
let getValidatedPath; // We need to lazy load it because of the circular dependency.
|
||||
@@ -103,6 +112,7 @@ function wrapProcessMethods(binding) {
|
||||
rss,
|
||||
resourceUsage: _resourceUsage,
|
||||
loadEnvFile: _loadEnvFile,
|
||||
execve: _execve,
|
||||
} = binding;
|
||||
|
||||
function _rawDebug(...args) {
|
||||
@@ -269,6 +279,55 @@ function wrapProcessMethods(binding) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function execve(execPath, args, env) {
|
||||
emitExperimentalWarning('process.execve');
|
||||
|
||||
const { isMainThread } = require('internal/worker');
|
||||
|
||||
if (!isMainThread) {
|
||||
throw new ERR_WORKER_UNSUPPORTED_OPERATION('Calling process.execve');
|
||||
} else if (process.platform === 'win32') {
|
||||
throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('process.execve');
|
||||
}
|
||||
|
||||
validateString(execPath, 'execPath');
|
||||
validateArray(args, 'args');
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
if (typeof arg !== 'string' || StringPrototypeIncludes(arg, '\u0000')) {
|
||||
throw new ERR_INVALID_ARG_VALUE(`args[${i}]`, arg, 'must be a string without null bytes');
|
||||
}
|
||||
}
|
||||
|
||||
const envArray = [];
|
||||
if (env !== undefined) {
|
||||
validateObject(env, 'env');
|
||||
|
||||
for (const { 0: key, 1: value } of ObjectEntries(env)) {
|
||||
if (
|
||||
typeof key !== 'string' ||
|
||||
typeof value !== 'string' ||
|
||||
StringPrototypeIncludes(key, '\u0000') ||
|
||||
StringPrototypeIncludes(value, '\u0000')
|
||||
) {
|
||||
throw new ERR_INVALID_ARG_VALUE(
|
||||
'env', env, 'must be an object with string keys and values without null bytes',
|
||||
);
|
||||
} else {
|
||||
ArrayPrototypePush(envArray, `${key}=${value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (execveDiagnosticChannel.hasSubscribers) {
|
||||
execveDiagnosticChannel.publish({ execPath, args, env: envArray });
|
||||
}
|
||||
|
||||
// Perform the system call
|
||||
_execve(execPath, args, envArray);
|
||||
}
|
||||
|
||||
const resourceValues = new Float64Array(16);
|
||||
function resourceUsage() {
|
||||
_resourceUsage(resourceValues);
|
||||
@@ -314,6 +373,7 @@ function wrapProcessMethods(binding) {
|
||||
memoryUsage,
|
||||
kill,
|
||||
exit,
|
||||
execve,
|
||||
loadEnvFile,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
#include <sstream>
|
||||
|
||||
namespace node {
|
||||
// This forward declaration is required to have the method
|
||||
// available in error messages.
|
||||
namespace errors {
|
||||
const char* errno_string(int errorno);
|
||||
}
|
||||
|
||||
enum ErrorHandlingMode { CONTEXTIFY_ERROR, FATAL_ERROR, MODULE_ERROR };
|
||||
void AppendExceptionLine(Environment* env,
|
||||
|
||||
@@ -27,12 +27,14 @@
|
||||
#if defined(_MSC_VER)
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include <process.h>
|
||||
#define umask _umask
|
||||
typedef int mode_t;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <sys/resource.h> // getrlimit, setrlimit
|
||||
#include <termios.h> // tcgetattr, tcsetattr
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
@@ -495,6 +497,95 @@ static void ReallyExit(const FunctionCallbackInfo<Value>& args) {
|
||||
env->Exit(code);
|
||||
}
|
||||
|
||||
#ifdef __POSIX__
|
||||
inline int persist_standard_stream(int fd) {
|
||||
int flags = fcntl(fd, F_GETFD, 0);
|
||||
|
||||
if (flags < 0) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
flags &= ~FD_CLOEXEC;
|
||||
return fcntl(fd, F_SETFD, flags);
|
||||
}
|
||||
|
||||
static void Execve(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
Local<Context> context = env->context();
|
||||
|
||||
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||
env, permission::PermissionScope::kChildProcess, "");
|
||||
|
||||
CHECK(args[0]->IsString());
|
||||
CHECK(args[1]->IsArray());
|
||||
CHECK(args[2]->IsArray());
|
||||
|
||||
Local<Array> argv_array = args[1].As<Array>();
|
||||
Local<Array> envp_array = args[2].As<Array>();
|
||||
|
||||
// Copy arguments and environment
|
||||
Utf8Value executable(isolate, args[0]);
|
||||
std::vector<std::string> argv_strings(argv_array->Length());
|
||||
std::vector<std::string> envp_strings(envp_array->Length());
|
||||
std::vector<char*> argv(argv_array->Length() + 1);
|
||||
std::vector<char*> envp(envp_array->Length() + 1);
|
||||
|
||||
for (unsigned int i = 0; i < argv_array->Length(); i++) {
|
||||
Local<Value> str;
|
||||
if (!argv_array->Get(context, i).ToLocal(&str)) {
|
||||
THROW_ERR_INVALID_ARG_VALUE(env, "Failed to deserialize argument.");
|
||||
return;
|
||||
}
|
||||
|
||||
argv_strings[i] = Utf8Value(isolate, str).ToString();
|
||||
argv[i] = argv_strings[i].data();
|
||||
}
|
||||
argv[argv_array->Length()] = nullptr;
|
||||
|
||||
for (unsigned int i = 0; i < envp_array->Length(); i++) {
|
||||
Local<Value> str;
|
||||
if (!envp_array->Get(context, i).ToLocal(&str)) {
|
||||
THROW_ERR_INVALID_ARG_VALUE(
|
||||
env, "Failed to deserialize environment variable.");
|
||||
return;
|
||||
}
|
||||
|
||||
envp_strings[i] = Utf8Value(isolate, str).ToString();
|
||||
envp[i] = envp_strings[i].data();
|
||||
}
|
||||
|
||||
envp[envp_array->Length()] = nullptr;
|
||||
|
||||
// Set stdin, stdout and stderr to be non-close-on-exec
|
||||
// so that the new process will inherit it.
|
||||
if (persist_standard_stream(0) < 0 || persist_standard_stream(1) < 0 ||
|
||||
persist_standard_stream(2) < 0) {
|
||||
env->ThrowErrnoException(errno, "fcntl");
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the execve operation.
|
||||
RunAtExit(env);
|
||||
execve(*executable, argv.data(), envp.data());
|
||||
|
||||
// If it returns, it means that the execve operation failed.
|
||||
// In that case we abort the process.
|
||||
auto error_message = std::string("process.execve failed with error code ") +
|
||||
errors::errno_string(errno);
|
||||
|
||||
// Abort the process
|
||||
Local<v8::Value> exception =
|
||||
ErrnoException(isolate, errno, "execve", *executable);
|
||||
Local<v8::Message> message = v8::Exception::CreateMessage(isolate, exception);
|
||||
|
||||
std::string info = FormatErrorMessage(
|
||||
isolate, context, error_message.c_str(), message, true);
|
||||
FPrintF(stderr, "%s\n", info);
|
||||
ABORT();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void LoadEnvFile(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
std::string path = ".env";
|
||||
@@ -687,6 +778,10 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
SetMethodNoSideEffect(isolate, target, "cwd", Cwd);
|
||||
SetMethod(isolate, target, "dlopen", binding::DLOpen);
|
||||
SetMethod(isolate, target, "reallyExit", ReallyExit);
|
||||
|
||||
#ifdef __POSIX__
|
||||
SetMethod(isolate, target, "execve", Execve);
|
||||
#endif
|
||||
SetMethodNoSideEffect(isolate, target, "uptime", Uptime);
|
||||
SetMethod(isolate, target, "patchProcessObject", PatchProcessObject);
|
||||
|
||||
@@ -730,6 +825,10 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(Cwd);
|
||||
registry->Register(binding::DLOpen);
|
||||
registry->Register(ReallyExit);
|
||||
|
||||
#ifdef __POSIX__
|
||||
registry->Register(Execve);
|
||||
#endif
|
||||
registry->Register(Uptime);
|
||||
registry->Register(PatchProcessObject);
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
'use strict';
|
||||
|
||||
const { skip, isWindows } = require('../common');
|
||||
const { ok } = require('assert');
|
||||
const { spawnSync } = require('child_process');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'child') {
|
||||
process.execve(
|
||||
process.execPath + '_non_existing',
|
||||
[__filename, 'replaced'],
|
||||
{ ...process.env, EXECVE_A: 'FIRST', EXECVE_B: 'SECOND', CWD: process.cwd() }
|
||||
);
|
||||
} else {
|
||||
const child = spawnSync(`${process.execPath}`, [`${__filename}`, 'child']);
|
||||
const stderr = child.stderr.toString();
|
||||
|
||||
ok(stderr.includes('process.execve failed with error code ENOENT'), stderr);
|
||||
ok(stderr.includes('execve (node:internal/process/per_thread'), stderr);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const { mustNotCall, skip, isWindows } = require('../common');
|
||||
const { strictEqual } = require('assert');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'replaced') {
|
||||
strictEqual(process.argv[2], 'replaced');
|
||||
} else {
|
||||
process.on('exit', mustNotCall());
|
||||
process.execve(process.execPath, [process.execPath, __filename, 'replaced'], process.env);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Flags: --permission --allow-fs-read=*
|
||||
|
||||
'use strict';
|
||||
|
||||
const { mustCall, skip, isWindows } = require('../common');
|
||||
const { fail, throws } = require('assert');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'replaced') {
|
||||
fail('process.execve should not have been allowed.');
|
||||
} else {
|
||||
throws(mustCall(() => {
|
||||
process.execve(process.execPath, [process.execPath, __filename, 'replaced'], process.env);
|
||||
}), { code: 'ERR_ACCESS_DENIED', permission: 'ChildProcess' });
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Flags: --permission --allow-fs-read=* --allow-child-process
|
||||
|
||||
'use strict';
|
||||
|
||||
const { skip, isWindows } = require('../common');
|
||||
const { deepStrictEqual } = require('assert');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'replaced') {
|
||||
deepStrictEqual(process.argv, [process.execPath, __filename, 'replaced']);
|
||||
} else {
|
||||
process.execve(process.execPath, [process.execPath, __filename, 'replaced'], process.env);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const { mustCall, mustNotCall, skip, isWindows } = require('../common');
|
||||
const { fail, ok } = require('assert');
|
||||
const { createServer } = require('net');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'replaced') {
|
||||
const port = parseInt(process.env.PORT, 10);
|
||||
ok(Number.isInteger(port));
|
||||
|
||||
const server = createServer();
|
||||
server.on('error', mustNotCall());
|
||||
server.listen(port, mustCall(() => {
|
||||
server.close();
|
||||
}));
|
||||
} else {
|
||||
// Create a new socket server
|
||||
const server = createServer();
|
||||
|
||||
server.on('close', mustNotCall());
|
||||
server.listen(0, mustCall(() => {
|
||||
const port = server.address().port;
|
||||
|
||||
// Try to create a second server on the same port. It should fail.
|
||||
const server2 = createServer();
|
||||
|
||||
server2.on('error', mustCall(() => {
|
||||
process.execve(
|
||||
process.execPath,
|
||||
[process.execPath, __filename, 'replaced'],
|
||||
{ ...process.env, PORT: port.toString() }
|
||||
);
|
||||
|
||||
// If process.execve succeed, this should never be executed.
|
||||
fail('process.execve failed');
|
||||
}));
|
||||
|
||||
server2.listen(port, mustCall(() => {
|
||||
fail('server2.listen unexpectedly succeeded');
|
||||
}));
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const { skip, isWindows } = require('../common');
|
||||
const { throws } = require('assert');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
}
|
||||
|
||||
if (!isWindows) {
|
||||
// Invalid path name
|
||||
{
|
||||
throws(() => {
|
||||
process.execve(123);
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: 'The "execPath" argument must be of type string. Received type number (123)'
|
||||
});
|
||||
}
|
||||
|
||||
// Invalid args
|
||||
{
|
||||
throws(() => {
|
||||
process.execve(process.execPath, '123');
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `The "args" argument must be an instance of Array. Received type string ('123')`
|
||||
});
|
||||
|
||||
throws(() => {
|
||||
process.execve(process.execPath, [123]);
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: "The argument 'args[0]' must be a string without null bytes. Received 123",
|
||||
});
|
||||
|
||||
throws(() => {
|
||||
process.execve(process.execPath, ['123', 'abc\u0000cde']);
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message: "The argument 'args[1]' must be a string without null bytes. Received 'abc\\x00cde'",
|
||||
});
|
||||
}
|
||||
|
||||
// Invalid env
|
||||
{
|
||||
throws(() => {
|
||||
process.execve(process.execPath, [], '123');
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
message: `The "env" argument must be of type object. Received type string ('123')`
|
||||
});
|
||||
|
||||
throws(() => {
|
||||
process.execve(process.execPath, [], { abc: 123 });
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message:
|
||||
"The argument 'env' must be an object with string keys and values without null bytes. Received { abc: 123 }"
|
||||
});
|
||||
|
||||
throws(() => {
|
||||
process.execve(process.execPath, [], { abc: '123', cde: 'abc\u0000cde' });
|
||||
}, {
|
||||
name: 'TypeError',
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
message:
|
||||
"The argument 'env' must be an object with string keys and values without null bytes. " +
|
||||
"Received { abc: '123', cde: 'abc\\x00cde' }",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throws(() => {
|
||||
process.execve(
|
||||
process.execPath,
|
||||
[__filename, 'replaced'],
|
||||
{ ...process.env, EXECVE_A: 'FIRST', EXECVE_B: 'SECOND', CWD: process.cwd() }
|
||||
);
|
||||
}, { name: 'TypeError', code: 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM' });
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
|
||||
const { isWindows, mustCall, skip } = require('../common');
|
||||
const { throws } = require('assert');
|
||||
const { isMainThread, Worker } = require('worker_threads');
|
||||
|
||||
if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (isMainThread) {
|
||||
new Worker(__filename);
|
||||
} else {
|
||||
throws(mustCall(() => {
|
||||
process.execve(
|
||||
process.execPath,
|
||||
[__filename, 'replaced'],
|
||||
{ ...process.env, EXECVE_A: 'FIRST', EXECVE_B: 'SECOND', CWD: process.cwd() }
|
||||
);
|
||||
}), { name: 'TypeError', code: 'ERR_WORKER_UNSUPPORTED_OPERATION' });
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const { isWindows, skip } = require('../common');
|
||||
const { deepStrictEqual, fail, strictEqual } = require('assert');
|
||||
const { isMainThread } = require('worker_threads');
|
||||
|
||||
if (!isMainThread) {
|
||||
skip('process.execve is not available in Workers');
|
||||
} else if (isWindows) {
|
||||
skip('process.execve is not available in Windows');
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'replaced') {
|
||||
deepStrictEqual(process.argv, [process.execPath, __filename, 'replaced']);
|
||||
strictEqual(process.env.CWD, process.cwd());
|
||||
strictEqual(process.env.EXECVE_A, 'FIRST');
|
||||
strictEqual(process.env.EXECVE_B, 'SECOND');
|
||||
} else {
|
||||
process.execve(
|
||||
process.execPath,
|
||||
[process.execPath, __filename, 'replaced'],
|
||||
{ ...process.env, EXECVE_A: 'FIRST', EXECVE_B: 'SECOND', CWD: process.cwd() }
|
||||
);
|
||||
|
||||
// If process.execve succeed, this should never be executed.
|
||||
fail('process.execve failed');
|
||||
}
|
||||
Reference in new issue
Block a user