mirror of
https://github.com/nodejs/node.git
synced 2025-12-28 07:50:41 +00:00
src: add detailed embedder process initialization API
So far, process initialization has been a bit all over the place in Node.js. `InitializeNodeWithArgs()` is our main public API for this, but inclusion of items in it vs. `InitializeOncePerProcess()` and `PlatformInit()` has been random at best. Likewise, some pieces of initialization have been guarded by `NODE_SHARED_MODE`, but also fairly randomly and without any meaningful connection to shared library usage. This leaves embedders in a position to cherry-pick some of the initialization code into their own code to make their application behave like typical Node.js applications to the degree to which they desire it. Electron takes an alternative route and makes direct use of `InitializeOncePerProcess()` already while it is a private API, with a `TODO` to add it to the public API in Node.js. This commit addresses that `TODO`, and `TODO`s around the `NODE_SHARED_MODE` usage. Specifically: - `InitializeOncePerProcess()` and `TearDownOncePerProcess()` are added to the public API. - The `flags` option of these functions are merged with the `flags` option for `InitializeNodeWithArgs()`, since they essentially share the same semantics. - The return value of the function is made an abstract class, rather than a struct, for easier API/ABI stability. - Initialization code from `main()` is brought into these functions (since that makes sense in general). - Add a `TODO` for turning `InitializeNodeWithArgs()` into a small wrapper around `InitializeOncePerProcess()` and eventually removing it (at least one major release cycle each, presumably). - Remove `NODE_SHARED_MODE` guards and replace them with runtime options. PR-URL: https://github.com/nodejs/node/pull/44121 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
This commit is contained in:
parent
4c5b96b376
commit
b697160256
@ -37,15 +37,18 @@ the `node` and `v8` C++ namespaces, respectively.
|
||||
int main(int argc, char** argv) {
|
||||
argv = uv_setup_args(argc, argv);
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
std::vector<std::string> exec_args;
|
||||
std::vector<std::string> errors;
|
||||
// Parse Node.js CLI options, and print any errors that have occurred while
|
||||
// trying to parse them.
|
||||
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
|
||||
for (const std::string& error : errors)
|
||||
std::unique_ptr<node::InitializationResult> result =
|
||||
node::InitializeOncePerProcess(args, {
|
||||
node::ProcessInitializationFlags::kNoInitializeV8,
|
||||
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform
|
||||
});
|
||||
|
||||
for (const std::string& error : result->errors())
|
||||
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
if (result->early_return() != 0) {
|
||||
return result->exit_code();
|
||||
}
|
||||
|
||||
// Create a v8::Platform instance. `MultiIsolatePlatform::Create()` is a way
|
||||
@ -58,10 +61,13 @@ int main(int argc, char** argv) {
|
||||
V8::Initialize();
|
||||
|
||||
// See below for the contents of this function.
|
||||
int ret = RunNodeInstance(platform.get(), args, exec_args);
|
||||
int ret = RunNodeInstance(
|
||||
platform.get(), result->args(), result->exec_args());
|
||||
|
||||
V8::Dispose();
|
||||
V8::DisposePlatform();
|
||||
|
||||
node::TearDownOncePerProcess();
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
490
src/node.cc
490
src/node.cc
@ -567,37 +567,8 @@ static struct {
|
||||
} stdio[1 + STDERR_FILENO];
|
||||
#endif // __POSIX__
|
||||
|
||||
|
||||
inline void PlatformInit() {
|
||||
void ResetSignalHandlers() {
|
||||
#ifdef __POSIX__
|
||||
#if HAVE_INSPECTOR
|
||||
sigset_t sigmask;
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGUSR1);
|
||||
const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr);
|
||||
#endif // HAVE_INSPECTOR
|
||||
|
||||
// Make sure file descriptors 0-2 are valid before we start logging anything.
|
||||
for (auto& s : stdio) {
|
||||
const int fd = &s - stdio;
|
||||
if (fstat(fd, &s.stat) == 0)
|
||||
continue;
|
||||
// Anything but EBADF means something is seriously wrong. We don't
|
||||
// have to special-case EINTR, fstat() is not interruptible.
|
||||
if (errno != EBADF)
|
||||
ABORT();
|
||||
if (fd != open("/dev/null", O_RDWR))
|
||||
ABORT();
|
||||
if (fstat(fd, &s.stat) != 0)
|
||||
ABORT();
|
||||
}
|
||||
|
||||
#if HAVE_INSPECTOR
|
||||
CHECK_EQ(err, 0);
|
||||
#endif // HAVE_INSPECTOR
|
||||
|
||||
// TODO(addaleax): NODE_SHARED_MODE does not really make sense here.
|
||||
#ifndef NODE_SHARED_MODE
|
||||
// Restore signal dispositions, the parent process may have changed them.
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
@ -611,94 +582,150 @@ inline void PlatformInit() {
|
||||
act.sa_handler = (nr == SIGPIPE || nr == SIGXFSZ) ? SIG_IGN : SIG_DFL;
|
||||
CHECK_EQ(0, sigaction(nr, &act, nullptr));
|
||||
}
|
||||
#endif // !NODE_SHARED_MODE
|
||||
#endif // __POSIX__
|
||||
}
|
||||
|
||||
// Record the state of the stdio file descriptors so we can restore it
|
||||
// on exit. Needs to happen before installing signal handlers because
|
||||
// they make use of that information.
|
||||
for (auto& s : stdio) {
|
||||
const int fd = &s - stdio;
|
||||
int err;
|
||||
static std::atomic<uint64_t> init_process_flags = 0;
|
||||
|
||||
do
|
||||
s.flags = fcntl(fd, F_GETFL);
|
||||
while (s.flags == -1 && errno == EINTR); // NOLINT
|
||||
CHECK_NE(s.flags, -1);
|
||||
static void PlatformInit(ProcessInitializationFlags::Flags flags) {
|
||||
// init_process_flags is accessed in ResetStdio(),
|
||||
// which can be called from signal handlers.
|
||||
CHECK(init_process_flags.is_lock_free());
|
||||
init_process_flags.store(flags);
|
||||
|
||||
if (uv_guess_handle(fd) != UV_TTY) continue;
|
||||
s.isatty = true;
|
||||
|
||||
do
|
||||
err = tcgetattr(fd, &s.termios);
|
||||
while (err == -1 && errno == EINTR); // NOLINT
|
||||
CHECK_EQ(err, 0);
|
||||
if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
|
||||
atexit(ResetStdio);
|
||||
}
|
||||
|
||||
RegisterSignalHandler(SIGINT, SignalExit, true);
|
||||
RegisterSignalHandler(SIGTERM, SignalExit, true);
|
||||
#ifdef __POSIX__
|
||||
if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
|
||||
// Disable stdio buffering, it interacts poorly with printf()
|
||||
// calls elsewhere in the program (e.g., any logging from V8.)
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
|
||||
// Make sure file descriptors 0-2 are valid before we start logging
|
||||
// anything.
|
||||
for (auto& s : stdio) {
|
||||
const int fd = &s - stdio;
|
||||
if (fstat(fd, &s.stat) == 0) continue;
|
||||
// Anything but EBADF means something is seriously wrong. We don't
|
||||
// have to special-case EINTR, fstat() is not interruptible.
|
||||
if (errno != EBADF) ABORT();
|
||||
if (fd != open("/dev/null", O_RDWR)) ABORT();
|
||||
if (fstat(fd, &s.stat) != 0) ABORT();
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
|
||||
#if HAVE_INSPECTOR
|
||||
sigset_t sigmask;
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGUSR1);
|
||||
const int err = pthread_sigmask(SIG_SETMASK, &sigmask, nullptr);
|
||||
CHECK_EQ(err, 0);
|
||||
#endif // HAVE_INSPECTOR
|
||||
|
||||
ResetSignalHandlers();
|
||||
}
|
||||
|
||||
if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
|
||||
// Record the state of the stdio file descriptors so we can restore it
|
||||
// on exit. Needs to happen before installing signal handlers because
|
||||
// they make use of that information.
|
||||
for (auto& s : stdio) {
|
||||
const int fd = &s - stdio;
|
||||
int err;
|
||||
|
||||
do {
|
||||
s.flags = fcntl(fd, F_GETFL);
|
||||
} while (s.flags == -1 && errno == EINTR); // NOLINT
|
||||
CHECK_NE(s.flags, -1);
|
||||
|
||||
if (uv_guess_handle(fd) != UV_TTY) continue;
|
||||
s.isatty = true;
|
||||
|
||||
do {
|
||||
err = tcgetattr(fd, &s.termios);
|
||||
} while (err == -1 && errno == EINTR); // NOLINT
|
||||
CHECK_EQ(err, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
|
||||
RegisterSignalHandler(SIGINT, SignalExit, true);
|
||||
RegisterSignalHandler(SIGTERM, SignalExit, true);
|
||||
|
||||
#if NODE_USE_V8_WASM_TRAP_HANDLER
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
constexpr ULONG first = TRUE;
|
||||
per_process::old_vectored_exception_handler =
|
||||
AddVectoredExceptionHandler(first, TrapWebAssemblyOrContinue);
|
||||
}
|
||||
#else
|
||||
// Tell V8 to disable emitting WebAssembly
|
||||
// memory bounds checks. This means that we have
|
||||
// to catch the SIGSEGV in TrapWebAssemblyOrContinue
|
||||
// and pass the signal context to V8.
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = TrapWebAssemblyOrContinue;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0);
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
V8::EnableWebAssemblyTrapHandler(false);
|
||||
#endif // NODE_USE_V8_WASM_TRAP_HANDLER
|
||||
|
||||
// Raise the open file descriptor limit.
|
||||
struct rlimit lim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {
|
||||
// Do a binary search for the limit.
|
||||
rlim_t min = lim.rlim_cur;
|
||||
rlim_t max = 1 << 20;
|
||||
// But if there's a defined upper bound, don't search, just set it.
|
||||
if (lim.rlim_max != RLIM_INFINITY) {
|
||||
min = lim.rlim_max;
|
||||
max = lim.rlim_max;
|
||||
{
|
||||
constexpr ULONG first = TRUE;
|
||||
per_process::old_vectored_exception_handler =
|
||||
AddVectoredExceptionHandler(first, TrapWebAssemblyOrContinue);
|
||||
}
|
||||
do {
|
||||
lim.rlim_cur = min + (max - min) / 2;
|
||||
if (setrlimit(RLIMIT_NOFILE, &lim)) {
|
||||
max = lim.rlim_cur;
|
||||
} else {
|
||||
min = lim.rlim_cur;
|
||||
#else
|
||||
// Tell V8 to disable emitting WebAssembly
|
||||
// memory bounds checks. This means that we have
|
||||
// to catch the SIGSEGV in TrapWebAssemblyOrContinue
|
||||
// and pass the signal context to V8.
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = TrapWebAssemblyOrContinue;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
CHECK_EQ(sigaction(SIGSEGV, &sa, nullptr), 0);
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
V8::EnableWebAssemblyTrapHandler(false);
|
||||
#endif // NODE_USE_V8_WASM_TRAP_HANDLER
|
||||
}
|
||||
|
||||
if (!(flags & ProcessInitializationFlags::kNoAdjustResourceLimits)) {
|
||||
// Raise the open file descriptor limit.
|
||||
struct rlimit lim;
|
||||
if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {
|
||||
// Do a binary search for the limit.
|
||||
rlim_t min = lim.rlim_cur;
|
||||
rlim_t max = 1 << 20;
|
||||
// But if there's a defined upper bound, don't search, just set it.
|
||||
if (lim.rlim_max != RLIM_INFINITY) {
|
||||
min = lim.rlim_max;
|
||||
max = lim.rlim_max;
|
||||
}
|
||||
} while (min + 1 < max);
|
||||
do {
|
||||
lim.rlim_cur = min + (max - min) / 2;
|
||||
if (setrlimit(RLIMIT_NOFILE, &lim)) {
|
||||
max = lim.rlim_cur;
|
||||
} else {
|
||||
min = lim.rlim_cur;
|
||||
}
|
||||
} while (min + 1 < max);
|
||||
}
|
||||
}
|
||||
#endif // __POSIX__
|
||||
#ifdef _WIN32
|
||||
for (int fd = 0; fd <= 2; ++fd) {
|
||||
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
if (handle == INVALID_HANDLE_VALUE ||
|
||||
GetFileType(handle) == FILE_TYPE_UNKNOWN) {
|
||||
// Ignore _close result. If it fails or not depends on used Windows
|
||||
// version. We will just check _open result.
|
||||
_close(fd);
|
||||
if (fd != _open("nul", _O_RDWR))
|
||||
ABORT();
|
||||
if (!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
|
||||
for (int fd = 0; fd <= 2; ++fd) {
|
||||
auto handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
|
||||
if (handle == INVALID_HANDLE_VALUE ||
|
||||
GetFileType(handle) == FILE_TYPE_UNKNOWN) {
|
||||
// Ignore _close result. If it fails or not depends on used Windows
|
||||
// version. We will just check _open result.
|
||||
_close(fd);
|
||||
if (fd != _open("nul", _O_RDWR)) ABORT();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
|
||||
// Safe to call more than once and from signal handlers.
|
||||
void ResetStdio() {
|
||||
if (init_process_flags.load() &
|
||||
ProcessInitializationFlags::kNoStdioInitialization) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv_tty_reset_mode();
|
||||
#ifdef __POSIX__
|
||||
for (auto& s : stdio) {
|
||||
@ -834,10 +861,12 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
|
||||
|
||||
static std::atomic_bool init_called{false};
|
||||
|
||||
// TODO(addaleax): Turn this into a wrapper around InitializeOncePerProcess()
|
||||
// (with the corresponding additional flags set), then eventually remove this.
|
||||
int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
std::vector<std::string>* exec_argv,
|
||||
std::vector<std::string>* errors,
|
||||
ProcessFlags::Flags flags) {
|
||||
ProcessInitializationFlags::Flags flags) {
|
||||
// Make sure InitializeNodeWithArgs() is called only once.
|
||||
CHECK(!init_called.exchange(true));
|
||||
|
||||
@ -848,8 +877,10 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
binding::RegisterBuiltinModules();
|
||||
|
||||
// Make inherited handles noninheritable.
|
||||
if (!(flags & ProcessFlags::kEnableStdioInheritance))
|
||||
if (!(flags & ProcessInitializationFlags::kEnableStdioInheritance) &&
|
||||
!(flags & ProcessInitializationFlags::kNoStdioInitialization)) {
|
||||
uv_disable_stdio_inheritance();
|
||||
}
|
||||
|
||||
// Cache the original command line to be
|
||||
// used in diagnostic reports.
|
||||
@ -865,7 +896,7 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
HandleEnvOptions(per_process::cli_options->per_isolate->per_env);
|
||||
|
||||
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
|
||||
if (!(flags & ProcessFlags::kDisableNodeOptionsEnv)) {
|
||||
if (!(flags & ProcessInitializationFlags::kDisableNodeOptionsEnv)) {
|
||||
std::string node_options;
|
||||
|
||||
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
|
||||
@ -886,7 +917,7 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(flags & ProcessFlags::kDisableCLIOptions)) {
|
||||
if (!(flags & ProcessInitializationFlags::kDisableCLIOptions)) {
|
||||
const int exit_code = ProcessGlobalArgs(argv,
|
||||
exec_argv,
|
||||
errors,
|
||||
@ -899,7 +930,7 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
uv_set_process_title(per_process::cli_options->title.c_str());
|
||||
|
||||
#if defined(NODE_HAVE_I18N_SUPPORT)
|
||||
if (!(flags & ProcessFlags::kNoICU)) {
|
||||
if (!(flags & ProcessInitializationFlags::kNoICU)) {
|
||||
// If the parameter isn't given, use the env variable.
|
||||
if (per_process::cli_options->icu_data_dir.empty())
|
||||
credentials::SafeGetenv("NODE_ICU_DATA",
|
||||
@ -949,82 +980,78 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
InitializationResult InitializeOncePerProcess(int argc, char** argv) {
|
||||
return InitializeOncePerProcess(argc, argv, kDefaultInitialization);
|
||||
}
|
||||
std::unique_ptr<InitializationResult> InitializeOncePerProcess(
|
||||
const std::vector<std::string>& args,
|
||||
ProcessInitializationFlags::Flags flags) {
|
||||
auto result = std::make_unique<InitializationResultImpl>();
|
||||
result->args_ = args;
|
||||
|
||||
InitializationResult InitializeOncePerProcess(
|
||||
int argc,
|
||||
char** argv,
|
||||
InitializationSettingsFlags flags,
|
||||
ProcessFlags::Flags process_flags) {
|
||||
uint64_t init_flags = flags;
|
||||
if (init_flags & kDefaultInitialization) {
|
||||
init_flags = init_flags | kInitializeV8 | kInitOpenSSL | kRunPlatformInit;
|
||||
if (!(flags & ProcessInitializationFlags::kNoParseGlobalDebugVariables)) {
|
||||
// Initialized the enabled list for Debug() calls with system
|
||||
// environment variables.
|
||||
per_process::enabled_debug_list.Parse();
|
||||
}
|
||||
|
||||
// Initialized the enabled list for Debug() calls with system
|
||||
// environment variables.
|
||||
per_process::enabled_debug_list.Parse();
|
||||
|
||||
atexit(ResetStdio);
|
||||
|
||||
if (init_flags & kRunPlatformInit)
|
||||
PlatformInit();
|
||||
|
||||
CHECK_GT(argc, 0);
|
||||
|
||||
// Hack around with the argv pointer. Used for process.title = "blah".
|
||||
argv = uv_setup_args(argc, argv);
|
||||
|
||||
InitializationResult result;
|
||||
result.args = std::vector<std::string>(argv, argv + argc);
|
||||
std::vector<std::string> errors;
|
||||
PlatformInit(flags);
|
||||
|
||||
// This needs to run *before* V8::Initialize().
|
||||
{
|
||||
result.exit_code = InitializeNodeWithArgs(
|
||||
&(result.args), &(result.exec_args), &errors, process_flags);
|
||||
for (const std::string& error : errors)
|
||||
fprintf(stderr, "%s: %s\n", result.args.at(0).c_str(), error.c_str());
|
||||
if (result.exit_code != 0) {
|
||||
result.early_return = true;
|
||||
result->exit_code_ = InitializeNodeWithArgs(
|
||||
&result->args_, &result->exec_args_, &result->errors_, flags);
|
||||
if (result->exit_code() != 0) {
|
||||
result->early_return_ = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (per_process::cli_options->use_largepages == "on" ||
|
||||
per_process::cli_options->use_largepages == "silent") {
|
||||
int result = node::MapStaticCodeToLargePages();
|
||||
if (per_process::cli_options->use_largepages == "on" && result != 0) {
|
||||
fprintf(stderr, "%s\n", node::LargePagesError(result));
|
||||
if (!(flags & ProcessInitializationFlags::kNoUseLargePages) &&
|
||||
(per_process::cli_options->use_largepages == "on" ||
|
||||
per_process::cli_options->use_largepages == "silent")) {
|
||||
int lp_result = node::MapStaticCodeToLargePages();
|
||||
if (per_process::cli_options->use_largepages == "on" && lp_result != 0) {
|
||||
result->errors_.emplace_back(node::LargePagesError(lp_result));
|
||||
}
|
||||
}
|
||||
|
||||
if (per_process::cli_options->print_version) {
|
||||
printf("%s\n", NODE_VERSION);
|
||||
result.exit_code = 0;
|
||||
result.early_return = true;
|
||||
return result;
|
||||
if (!(flags & ProcessInitializationFlags::kNoPrintHelpOrVersionOutput)) {
|
||||
if (per_process::cli_options->print_version) {
|
||||
printf("%s\n", NODE_VERSION);
|
||||
result->exit_code_ = 0;
|
||||
result->early_return_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (per_process::cli_options->print_bash_completion) {
|
||||
std::string completion = options_parser::GetBashCompletion();
|
||||
printf("%s\n", completion.c_str());
|
||||
result->exit_code_ = 0;
|
||||
result->early_return_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (per_process::cli_options->print_v8_help) {
|
||||
V8::SetFlagsFromString("--help", static_cast<size_t>(6));
|
||||
result->exit_code_ = 0;
|
||||
result->early_return_ = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (per_process::cli_options->print_bash_completion) {
|
||||
std::string completion = options_parser::GetBashCompletion();
|
||||
printf("%s\n", completion.c_str());
|
||||
result.exit_code = 0;
|
||||
result.early_return = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (per_process::cli_options->print_v8_help) {
|
||||
V8::SetFlagsFromString("--help", static_cast<size_t>(6));
|
||||
result.exit_code = 0;
|
||||
result.early_return = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (init_flags & kInitOpenSSL) {
|
||||
if (!(flags & ProcessInitializationFlags::kNoInitOpenSSL)) {
|
||||
#if HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
|
||||
auto GetOpenSSLErrorString = []() -> std::string {
|
||||
std::string ret;
|
||||
ERR_print_errors_cb(
|
||||
[](const char* str, size_t len, void* opaque) {
|
||||
std::string* ret = static_cast<std::string*>(opaque);
|
||||
ret->append(str, len);
|
||||
ret->append("\n");
|
||||
return 0;
|
||||
},
|
||||
static_cast<void*>(&ret));
|
||||
return ret;
|
||||
};
|
||||
|
||||
{
|
||||
std::string extra_ca_certs;
|
||||
if (credentials::SafeGetenv("NODE_EXTRA_CA_CERTS", &extra_ca_certs))
|
||||
@ -1080,10 +1107,12 @@ InitializationResult InitializeOncePerProcess(
|
||||
OPENSSL_INIT_free(settings);
|
||||
|
||||
if (ERR_peek_error() != 0) {
|
||||
result.exit_code = ERR_GET_REASON(ERR_peek_error());
|
||||
result.early_return = true;
|
||||
fprintf(stderr, "OpenSSL configuration error:\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
// XXX: ERR_GET_REASON does not return something that is
|
||||
// useful as an exit code at all.
|
||||
result->exit_code_ = ERR_GET_REASON(ERR_peek_error());
|
||||
result->early_return_ = true;
|
||||
result->errors_.emplace_back("OpenSSL configuration error:\n" +
|
||||
GetOpenSSLErrorString());
|
||||
return result;
|
||||
}
|
||||
#else // OPENSSL_VERSION_MAJOR < 3
|
||||
@ -1091,22 +1120,30 @@ InitializationResult InitializeOncePerProcess(
|
||||
OPENSSL_init();
|
||||
}
|
||||
#endif
|
||||
if (!crypto::ProcessFipsOptions()) {
|
||||
result.exit_code = ERR_GET_REASON(ERR_peek_error());
|
||||
result.early_return = true;
|
||||
fprintf(stderr, "OpenSSL error when trying to enable FIPS:\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
if (!crypto::ProcessFipsOptions()) {
|
||||
// XXX: ERR_GET_REASON does not return something that is
|
||||
// useful as an exit code at all.
|
||||
result->exit_code_ = ERR_GET_REASON(ERR_peek_error());
|
||||
result->early_return_ = true;
|
||||
result->errors_.emplace_back(
|
||||
"OpenSSL error when trying to enable FIPS:\n" +
|
||||
GetOpenSSLErrorString());
|
||||
return result;
|
||||
}
|
||||
|
||||
// V8 on Windows doesn't have a good source of entropy. Seed it from
|
||||
// OpenSSL's pool.
|
||||
V8::SetEntropySource(crypto::EntropySource);
|
||||
#endif // HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
|
||||
}
|
||||
|
||||
// V8 on Windows doesn't have a good source of entropy. Seed it from
|
||||
// OpenSSL's pool.
|
||||
V8::SetEntropySource(crypto::EntropySource);
|
||||
#endif // HAVE_OPENSSL && !defined(OPENSSL_IS_BORINGSSL)
|
||||
}
|
||||
per_process::v8_platform.Initialize(
|
||||
static_cast<int>(per_process::cli_options->v8_thread_pool_size));
|
||||
if (init_flags & kInitializeV8) {
|
||||
if (!(flags & ProcessInitializationFlags::kNoInitializeNodeV8Platform)) {
|
||||
per_process::v8_platform.Initialize(
|
||||
static_cast<int>(per_process::cli_options->v8_thread_pool_size));
|
||||
result->platform_ = per_process::v8_platform.Platform();
|
||||
}
|
||||
|
||||
if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) {
|
||||
V8::Initialize();
|
||||
}
|
||||
|
||||
@ -1117,30 +1154,47 @@ InitializationResult InitializeOncePerProcess(
|
||||
}
|
||||
|
||||
void TearDownOncePerProcess() {
|
||||
const uint64_t flags = init_process_flags.load();
|
||||
ResetStdio();
|
||||
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
|
||||
ResetSignalHandlers();
|
||||
}
|
||||
|
||||
per_process::v8_initialized = false;
|
||||
V8::Dispose();
|
||||
if (!(flags & ProcessInitializationFlags::kNoInitializeV8)) {
|
||||
V8::Dispose();
|
||||
}
|
||||
|
||||
#if NODE_USE_V8_WASM_TRAP_HANDLER && defined(_WIN32)
|
||||
RemoveVectoredExceptionHandler(per_process::old_vectored_exception_handler);
|
||||
if (!(flags & ProcessInitializationFlags::kNoDefaultSignalHandling)) {
|
||||
RemoveVectoredExceptionHandler(per_process::old_vectored_exception_handler);
|
||||
}
|
||||
#endif
|
||||
|
||||
// uv_run cannot be called from the time before the beforeExit callback
|
||||
// runs until the program exits unless the event loop has any referenced
|
||||
// handles after beforeExit terminates. This prevents unrefed timers
|
||||
// that happen to terminate during shutdown from being run unsafely.
|
||||
// Since uv_run cannot be called, uv_async handles held by the platform
|
||||
// will never be fully cleaned up.
|
||||
per_process::v8_platform.Dispose();
|
||||
if (!(flags & ProcessInitializationFlags::kNoInitializeNodeV8Platform)) {
|
||||
V8::DisposePlatform();
|
||||
// uv_run cannot be called from the time before the beforeExit callback
|
||||
// runs until the program exits unless the event loop has any referenced
|
||||
// handles after beforeExit terminates. This prevents unrefed timers
|
||||
// that happen to terminate during shutdown from being run unsafely.
|
||||
// Since uv_run cannot be called, uv_async handles held by the platform
|
||||
// will never be fully cleaned up.
|
||||
per_process::v8_platform.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
InitializationResult::~InitializationResult() {}
|
||||
InitializationResultImpl::~InitializationResultImpl() {}
|
||||
|
||||
int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
|
||||
InitializationResult* result) {
|
||||
const InitializationResult* result) {
|
||||
int exit_code = result->exit_code();
|
||||
// nullptr indicates there's no snapshot data.
|
||||
DCHECK_NULL(*snapshot_data_ptr);
|
||||
|
||||
// node:embedded_snapshot_main indicates that we are using the
|
||||
// embedded snapshot and we are not supposed to clean it up.
|
||||
if (result->args[1] == "node:embedded_snapshot_main") {
|
||||
if (result->args()[1] == "node:embedded_snapshot_main") {
|
||||
*snapshot_data_ptr = SnapshotBuilder::GetEmbeddedSnapshotData();
|
||||
if (*snapshot_data_ptr == nullptr) {
|
||||
// The Node.js binary is built without embedded snapshot
|
||||
@ -1148,19 +1202,19 @@ int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
|
||||
"node:embedded_snapshot_main was specified as snapshot "
|
||||
"entry point but Node.js was built without embedded "
|
||||
"snapshot.\n");
|
||||
result->exit_code = 1;
|
||||
return result->exit_code;
|
||||
exit_code = 1;
|
||||
return exit_code;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, load and run the specified main script.
|
||||
std::unique_ptr<SnapshotData> generated_data =
|
||||
std::make_unique<SnapshotData>();
|
||||
result->exit_code = node::SnapshotBuilder::Generate(
|
||||
generated_data.get(), result->args, result->exec_args);
|
||||
if (result->exit_code == 0) {
|
||||
exit_code = node::SnapshotBuilder::Generate(
|
||||
generated_data.get(), result->args(), result->exec_args());
|
||||
if (exit_code == 0) {
|
||||
*snapshot_data_ptr = generated_data.release();
|
||||
} else {
|
||||
return result->exit_code;
|
||||
return exit_code;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1181,13 +1235,14 @@ int GenerateAndWriteSnapshotData(const SnapshotData** snapshot_data_ptr,
|
||||
fprintf(stderr,
|
||||
"Cannot open %s for writing a snapshot.\n",
|
||||
snapshot_blob_path.c_str());
|
||||
result->exit_code = 1;
|
||||
exit_code = 1;
|
||||
}
|
||||
return result->exit_code;
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
|
||||
InitializationResult* result) {
|
||||
const InitializationResult* result) {
|
||||
int exit_code = result->exit_code();
|
||||
// nullptr indicates there's no snapshot data.
|
||||
DCHECK_NULL(*snapshot_data_ptr);
|
||||
// --snapshot-blob indicates that we are reading a customized snapshot.
|
||||
@ -1196,8 +1251,8 @@ int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
|
||||
FILE* fp = fopen(filename.c_str(), "rb");
|
||||
if (fp == nullptr) {
|
||||
fprintf(stderr, "Cannot open %s", filename.c_str());
|
||||
result->exit_code = 1;
|
||||
return result->exit_code;
|
||||
exit_code = 1;
|
||||
return exit_code;
|
||||
}
|
||||
std::unique_ptr<SnapshotData> read_data = std::make_unique<SnapshotData>();
|
||||
SnapshotData::FromBlob(read_data.get(), fp);
|
||||
@ -1215,19 +1270,28 @@ int LoadSnapshotDataAndRun(const SnapshotData** snapshot_data_ptr,
|
||||
NodeMainInstance main_instance(*snapshot_data_ptr,
|
||||
uv_default_loop(),
|
||||
per_process::v8_platform.Platform(),
|
||||
result->args,
|
||||
result->exec_args);
|
||||
result->exit_code = main_instance.Run();
|
||||
return result->exit_code;
|
||||
result->args(),
|
||||
result->exec_args());
|
||||
exit_code = main_instance.Run();
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
int Start(int argc, char** argv) {
|
||||
InitializationResult result = InitializeOncePerProcess(argc, argv);
|
||||
if (result.early_return) {
|
||||
return result.exit_code;
|
||||
CHECK_GT(argc, 0);
|
||||
|
||||
// Hack around with the argv pointer. Used for process.title = "blah".
|
||||
argv = uv_setup_args(argc, argv);
|
||||
|
||||
std::unique_ptr<InitializationResult> result =
|
||||
InitializeOncePerProcess(std::vector<std::string>(argv, argv + argc));
|
||||
for (const std::string& error : result->errors()) {
|
||||
FPrintF(stderr, "%s: %s\n", result->args().at(0), error);
|
||||
}
|
||||
if (result->early_return()) {
|
||||
return result->exit_code();
|
||||
}
|
||||
|
||||
DCHECK_EQ(result.exit_code, 0);
|
||||
DCHECK_EQ(result->exit_code(), 0);
|
||||
const SnapshotData* snapshot_data = nullptr;
|
||||
|
||||
auto cleanup_process = OnScopeLeave([&]() {
|
||||
@ -1243,17 +1307,17 @@ int Start(int argc, char** argv) {
|
||||
|
||||
// --build-snapshot indicates that we are in snapshot building mode.
|
||||
if (per_process::cli_options->build_snapshot) {
|
||||
if (result.args.size() < 2) {
|
||||
if (result->args().size() < 2) {
|
||||
fprintf(stderr,
|
||||
"--build-snapshot must be used with an entry point script.\n"
|
||||
"Usage: node --build-snapshot /path/to/entry.js\n");
|
||||
return 9;
|
||||
}
|
||||
return GenerateAndWriteSnapshotData(&snapshot_data, &result);
|
||||
return GenerateAndWriteSnapshotData(&snapshot_data, result.get());
|
||||
}
|
||||
|
||||
// Without --build-snapshot, we are in snapshot loading mode.
|
||||
return LoadSnapshotDataAndRun(&snapshot_data, &result);
|
||||
return LoadSnapshotDataAndRun(&snapshot_data, result.get());
|
||||
}
|
||||
|
||||
int Stop(Environment* env) {
|
||||
|
||||
101
src/node.h
101
src/node.h
@ -223,11 +223,14 @@ namespace node {
|
||||
|
||||
class IsolateData;
|
||||
class Environment;
|
||||
class MultiIsolatePlatform;
|
||||
class InitializationResultImpl;
|
||||
|
||||
namespace ProcessFlags {
|
||||
enum Flags : uint64_t {
|
||||
kNoFlags = 0,
|
||||
// Enable stdio inheritance, which is disabled by default.
|
||||
// This flag is also implied by kNoStdioInitialization.
|
||||
kEnableStdioInheritance = 1 << 0,
|
||||
// Disable reading the NODE_OPTIONS environment variable.
|
||||
kDisableNodeOptionsEnv = 1 << 1,
|
||||
@ -235,8 +238,67 @@ enum Flags : uint64_t {
|
||||
kDisableCLIOptions = 1 << 2,
|
||||
// Do not initialize ICU.
|
||||
kNoICU = 1 << 3,
|
||||
// Do not modify stdio file descriptor or TTY state.
|
||||
kNoStdioInitialization = 1 << 4,
|
||||
// Do not register Node.js-specific signal handlers
|
||||
// and reset other signal handlers to default state.
|
||||
kNoDefaultSignalHandling = 1 << 5,
|
||||
// Do not perform V8 initialization.
|
||||
kNoInitializeV8 = 1 << 6,
|
||||
// Do not initialize a default Node.js-provided V8 platform instance.
|
||||
kNoInitializeNodeV8Platform = 1 << 7,
|
||||
// Do not initialize OpenSSL config.
|
||||
kNoInitOpenSSL = 1 << 8,
|
||||
// Do not initialize Node.js debugging based on environment variables.
|
||||
kNoParseGlobalDebugVariables = 1 << 9,
|
||||
// Do not adjust OS resource limits for this process.
|
||||
kNoAdjustResourceLimits = 1 << 10,
|
||||
// Do not map code segments into large pages for this process.
|
||||
kNoUseLargePages = 1 << 11,
|
||||
// Skip printing output for --help, --version, --v8-options.
|
||||
kNoPrintHelpOrVersionOutput = 1 << 12,
|
||||
|
||||
// Emulate the behavior of InitializeNodeWithArgs() when passing
|
||||
// a flags argument to the InitializeOncePerProcess() replacement
|
||||
// function.
|
||||
kLegacyInitializeNodeWithArgsBehavior =
|
||||
kNoStdioInitialization | kNoDefaultSignalHandling | kNoInitializeV8 |
|
||||
kNoInitializeNodeV8Platform | kNoInitOpenSSL |
|
||||
kNoParseGlobalDebugVariables | kNoAdjustResourceLimits |
|
||||
kNoUseLargePages | kNoPrintHelpOrVersionOutput,
|
||||
};
|
||||
} // namespace ProcessFlags
|
||||
// TODO(addaleax): Make this the canonical name, as it is more descriptive.
|
||||
namespace ProcessInitializationFlags = ProcessFlags;
|
||||
|
||||
class NODE_EXTERN InitializationResult {
|
||||
public:
|
||||
virtual ~InitializationResult();
|
||||
|
||||
// Returns a suggested process exit code.
|
||||
virtual int exit_code() const = 0;
|
||||
|
||||
// Returns 'true' if initialization was aborted early due to errors.
|
||||
virtual bool early_return() const = 0;
|
||||
|
||||
// Returns the parsed list of non-Node.js arguments.
|
||||
virtual const std::vector<std::string>& args() const = 0;
|
||||
|
||||
// Returns the parsed list of Node.js arguments.
|
||||
virtual const std::vector<std::string>& exec_args() const = 0;
|
||||
|
||||
// Returns an array of errors. Note that these may be warnings
|
||||
// whose existence does not imply a non-zero exit code.
|
||||
virtual const std::vector<std::string>& errors() const = 0;
|
||||
|
||||
// If kNoInitializeNodeV8Platform was not specified, the global Node.js
|
||||
// platform instance.
|
||||
virtual MultiIsolatePlatform* platform() const = 0;
|
||||
|
||||
private:
|
||||
InitializationResult() = default;
|
||||
friend class InitializationResultImpl;
|
||||
};
|
||||
|
||||
// TODO(addaleax): Officially deprecate this and replace it with something
|
||||
// better suited for a public embedder API.
|
||||
@ -250,11 +312,40 @@ NODE_EXTERN int Stop(Environment* env);
|
||||
// from argv, fill exec_argv, and possibly add errors resulting from parsing
|
||||
// the arguments to `errors`. The return value is a suggested exit code for the
|
||||
// program; If it is 0, then initializing Node.js succeeded.
|
||||
NODE_EXTERN int InitializeNodeWithArgs(
|
||||
std::vector<std::string>* argv,
|
||||
std::vector<std::string>* exec_argv,
|
||||
std::vector<std::string>* errors,
|
||||
ProcessFlags::Flags flags = ProcessFlags::kNoFlags);
|
||||
// This runs a subset of the initialization performed by
|
||||
// InitializeOncePerProcess(), which supersedes this function.
|
||||
// The subset is roughly equivalent to the one given by
|
||||
// `ProcessInitializationFlags::kLegacyInitializeNodeWithArgsBehavior`.
|
||||
NODE_DEPRECATED("Use InitializeOncePerProcess() instead",
|
||||
NODE_EXTERN int InitializeNodeWithArgs(
|
||||
std::vector<std::string>* argv,
|
||||
std::vector<std::string>* exec_argv,
|
||||
std::vector<std::string>* errors,
|
||||
ProcessInitializationFlags::Flags flags =
|
||||
ProcessInitializationFlags::kNoFlags));
|
||||
|
||||
// Set up per-process state needed to run Node.js. This will consume arguments
|
||||
// from args, and return information about the initialization success,
|
||||
// including the arguments split into argv/exec_argv, a list of potential
|
||||
// errors encountered during initialization, and a potential suggested
|
||||
// exit code.
|
||||
NODE_EXTERN std::unique_ptr<InitializationResult> InitializeOncePerProcess(
|
||||
const std::vector<std::string>& args,
|
||||
ProcessInitializationFlags::Flags flags =
|
||||
ProcessInitializationFlags::kNoFlags);
|
||||
// Undoes the initialization performed by InitializeOncePerProcess(),
|
||||
// where cleanup is necessary.
|
||||
NODE_EXTERN void TearDownOncePerProcess();
|
||||
// Convenience overload for specifying multiple flags without having
|
||||
// to worry about casts.
|
||||
inline std::unique_ptr<InitializationResult> InitializeOncePerProcess(
|
||||
const std::vector<std::string>& args,
|
||||
std::initializer_list<ProcessInitializationFlags::Flags> list) {
|
||||
uint64_t flags_accum = ProcessInitializationFlags::kNoFlags;
|
||||
for (const auto flag : list) flags_accum |= static_cast<uint64_t>(flag);
|
||||
return InitializeOncePerProcess(
|
||||
args, static_cast<ProcessInitializationFlags::Flags>(flags_accum));
|
||||
}
|
||||
|
||||
enum OptionEnvvarSettings {
|
||||
kAllowedInEnvironment,
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <linux/capability.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif // __linux__
|
||||
|
||||
@ -31,9 +32,18 @@ using v8::TryCatch;
|
||||
using v8::Uint32;
|
||||
using v8::Value;
|
||||
|
||||
namespace per_process {
|
||||
bool linux_at_secure = false;
|
||||
} // namespace per_process
|
||||
bool linux_at_secure() {
|
||||
// This could reasonably be a static variable, but this way
|
||||
// we can guarantee that this function is always usable
|
||||
// and returns the correct value, e.g. even in static
|
||||
// initialization code in other files.
|
||||
#ifdef __linux__
|
||||
static const bool value = getauxval(AT_SECURE);
|
||||
return value;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace credentials {
|
||||
|
||||
@ -70,11 +80,10 @@ bool SafeGetenv(const char* key,
|
||||
v8::Isolate* isolate) {
|
||||
#if !defined(__CloudABI__) && !defined(_WIN32)
|
||||
#if defined(__linux__)
|
||||
if ((!HasOnly(CAP_NET_BIND_SERVICE) && per_process::linux_at_secure) ||
|
||||
if ((!HasOnly(CAP_NET_BIND_SERVICE) && linux_at_secure()) ||
|
||||
getuid() != geteuid() || getgid() != getegid())
|
||||
#else
|
||||
if (per_process::linux_at_secure || getuid() != geteuid() ||
|
||||
getgid() != getegid())
|
||||
if (linux_at_secure() || getuid() != geteuid() || getgid() != getegid())
|
||||
#endif
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
@ -311,29 +311,24 @@ v8::MaybeLocal<v8::Value> ExecuteBootstrapper(
|
||||
std::vector<v8::Local<v8::Value>>* arguments);
|
||||
void MarkBootstrapComplete(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||
|
||||
struct InitializationResult {
|
||||
int exit_code = 0;
|
||||
std::vector<std::string> args;
|
||||
std::vector<std::string> exec_args;
|
||||
bool early_return = false;
|
||||
class InitializationResultImpl final : public InitializationResult {
|
||||
public:
|
||||
~InitializationResultImpl();
|
||||
int exit_code() const { return exit_code_; }
|
||||
bool early_return() const { return early_return_; }
|
||||
const std::vector<std::string>& args() const { return args_; }
|
||||
const std::vector<std::string>& exec_args() const { return exec_args_; }
|
||||
const std::vector<std::string>& errors() const { return errors_; }
|
||||
MultiIsolatePlatform* platform() const { return platform_; }
|
||||
|
||||
int exit_code_ = 0;
|
||||
std::vector<std::string> args_;
|
||||
std::vector<std::string> exec_args_;
|
||||
std::vector<std::string> errors_;
|
||||
bool early_return_ = false;
|
||||
MultiIsolatePlatform* platform_ = nullptr;
|
||||
};
|
||||
|
||||
enum InitializationSettingsFlags : uint64_t {
|
||||
kDefaultInitialization = 1 << 0,
|
||||
kInitializeV8 = 1 << 1,
|
||||
kRunPlatformInit = 1 << 2,
|
||||
kInitOpenSSL = 1 << 3
|
||||
};
|
||||
|
||||
// TODO(codebytere): eventually document and expose to embedders.
|
||||
InitializationResult NODE_EXTERN_PRIVATE InitializeOncePerProcess(int argc,
|
||||
char** argv);
|
||||
InitializationResult NODE_EXTERN_PRIVATE InitializeOncePerProcess(
|
||||
int argc,
|
||||
char** argv,
|
||||
InitializationSettingsFlags flags,
|
||||
ProcessFlags::Flags process_flags = ProcessFlags::kNoFlags);
|
||||
void NODE_EXTERN_PRIVATE TearDownOncePerProcess();
|
||||
void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s);
|
||||
void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s);
|
||||
void SetIsolateCreateParamsForNode(v8::Isolate::CreateParams* params);
|
||||
@ -424,6 +419,8 @@ namespace performance {
|
||||
std::ostream& operator<<(std::ostream& output,
|
||||
const PerformanceState::SerializeInfo& d);
|
||||
}
|
||||
|
||||
bool linux_at_secure();
|
||||
} // namespace node
|
||||
|
||||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
||||
|
||||
@ -88,42 +88,8 @@ int wmain(int argc, wchar_t* wargv[]) {
|
||||
}
|
||||
#else
|
||||
// UNIX
|
||||
#ifdef __linux__
|
||||
#include <sys/auxv.h>
|
||||
#endif // __linux__
|
||||
#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
namespace node {
|
||||
namespace per_process {
|
||||
extern bool linux_at_secure;
|
||||
} // namespace per_process
|
||||
} // namespace node
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
|
||||
// In node::PlatformInit(), we squash all signal handlers for non-shared lib
|
||||
// build. In order to run test cases against shared lib build, we also need
|
||||
// to do the same thing for shared lib build here, but only for SIGPIPE for
|
||||
// now. If node::PlatformInit() is moved to here, then this section could be
|
||||
// removed.
|
||||
{
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &act, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
node::per_process::linux_at_secure = getauxval(AT_SECURE);
|
||||
#endif
|
||||
// Disable stdio buffering, it interacts poorly with printf()
|
||||
// calls elsewhere in the program (e.g., any logging from V8.)
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
return node::Start(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -139,21 +139,6 @@ void NodeMainInstance::Run(int* exit_code, Environment* env) {
|
||||
*exit_code = SpinEventLoop(env).FromMaybe(1);
|
||||
}
|
||||
|
||||
ResetStdio();
|
||||
|
||||
// TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
|
||||
// make sense here.
|
||||
#if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
|
||||
if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
|
||||
continue;
|
||||
act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
|
||||
CHECK_EQ(0, sigaction(nr, &act, nullptr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(LEAK_SANITIZER)
|
||||
__lsan_do_leak_check();
|
||||
#endif
|
||||
|
||||
@ -23,13 +23,16 @@ static int RunNodeInstance(MultiIsolatePlatform* platform,
|
||||
int main(int argc, char** argv) {
|
||||
argv = uv_setup_args(argc, argv);
|
||||
std::vector<std::string> args(argv, argv + argc);
|
||||
std::vector<std::string> exec_args;
|
||||
std::vector<std::string> errors;
|
||||
int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
|
||||
for (const std::string& error : errors)
|
||||
std::unique_ptr<node::InitializationResult> result =
|
||||
node::InitializeOncePerProcess(
|
||||
args,
|
||||
{node::ProcessInitializationFlags::kNoInitializeV8,
|
||||
node::ProcessInitializationFlags::kNoInitializeNodeV8Platform});
|
||||
|
||||
for (const std::string& error : result->errors())
|
||||
fprintf(stderr, "%s: %s\n", args[0].c_str(), error.c_str());
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
if (result->early_return() != 0) {
|
||||
return result->exit_code();
|
||||
}
|
||||
|
||||
std::unique_ptr<MultiIsolatePlatform> platform =
|
||||
@ -37,10 +40,13 @@ int main(int argc, char** argv) {
|
||||
V8::InitializePlatform(platform.get());
|
||||
V8::Initialize();
|
||||
|
||||
int ret = RunNodeInstance(platform.get(), args, exec_args);
|
||||
int ret =
|
||||
RunNodeInstance(platform.get(), result->args(), result->exec_args());
|
||||
|
||||
V8::Dispose();
|
||||
V8::DisposePlatform();
|
||||
|
||||
node::TearDownOncePerProcess();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -64,17 +64,18 @@ int BuildSnapshot(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
node::InitializationResult result =
|
||||
node::InitializeOncePerProcess(argc, argv);
|
||||
std::unique_ptr<node::InitializationResult> result =
|
||||
node::InitializeOncePerProcess(
|
||||
std::vector<std::string>(argv, argv + argc));
|
||||
|
||||
CHECK(!result.early_return);
|
||||
CHECK_EQ(result.exit_code, 0);
|
||||
CHECK(!result->early_return());
|
||||
CHECK_EQ(result->exit_code(), 0);
|
||||
|
||||
std::string out_path;
|
||||
if (node::per_process::cli_options->build_snapshot) {
|
||||
out_path = result.args[2];
|
||||
out_path = result->args()[2];
|
||||
} else {
|
||||
out_path = result.args[1];
|
||||
out_path = result->args()[1];
|
||||
}
|
||||
|
||||
std::ofstream out(out_path, std::ios::out | std::ios::binary);
|
||||
@ -85,8 +86,8 @@ int BuildSnapshot(int argc, char* argv[]) {
|
||||
|
||||
int exit_code = 0;
|
||||
{
|
||||
exit_code =
|
||||
node::SnapshotBuilder::Generate(out, result.args, result.exec_args);
|
||||
exit_code = node::SnapshotBuilder::Generate(
|
||||
out, result->args(), result->exec_args());
|
||||
if (exit_code == 0) {
|
||||
if (!out) {
|
||||
std::cerr << "Failed to write " << out_path << "\n";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user