From 94ef44c1cf53b4fb7737d68f14728917b13e8db4 Mon Sep 17 00:00:00 2001
From: Cheng Zhao <zcbenz@gmail.com>
Date: Wed, 6 Oct 2021 10:22:21 +0900
Subject: [PATCH] src: add flags for controlling process behavior

---
 src/node.cc          | 108 +++++++++++++++++++++++++------------------
 src/node.h           |  27 +++++++++--
 src/node_internals.h |   3 +-
 3 files changed, 88 insertions(+), 50 deletions(-)

diff --git a/src/node.cc b/src/node.cc
index e942c108a15a3c..0fa51b269764f4 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -843,6 +843,14 @@ static std::atomic_bool init_called{false};
 int InitializeNodeWithArgs(std::vector<std::string>* argv,
                            std::vector<std::string>* exec_argv,
                            std::vector<std::string>* errors) {
+  return InitializeNodeWithArgs(argv, exec_argv, errors,
+                                ProcessFlags::kNoFlags);
+}
+
+int InitializeNodeWithArgs(std::vector<std::string>* argv,
+                           std::vector<std::string>* exec_argv,
+                           std::vector<std::string>* errors,
+                           ProcessFlags::Flags flags) {
   // Make sure InitializeNodeWithArgs() is called only once.
   CHECK(!init_called.exchange(true));
 
@@ -853,7 +861,8 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
   binding::RegisterBuiltinModules();
 
   // Make inherited handles noninheritable.
-  uv_disable_stdio_inheritance();
+  if (!(flags & ProcessFlags::kEnableStdioInheritance))
+    uv_disable_stdio_inheritance();
 
   // Cache the original command line to be
   // used in diagnostic reports.
@@ -869,67 +878,73 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
   HandleEnvOptions(per_process::cli_options->per_isolate->per_env);
 
 #if !defined(NODE_WITHOUT_NODE_OPTIONS)
-  std::string node_options;
+  if (!(flags & ProcessFlags::kDisableNodeOptionsEnv)) {
+    std::string node_options;
 
-  if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
-    std::vector<std::string> env_argv =
-        ParseNodeOptionsEnvVar(node_options, errors);
+    if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
+      std::vector<std::string> env_argv =
+          ParseNodeOptionsEnvVar(node_options, errors);
 
-    if (!errors->empty()) return 9;
+      if (!errors->empty()) return 9;
 
-    // [0] is expected to be the program name, fill it in from the real argv.
-    env_argv.insert(env_argv.begin(), argv->at(0));
+      // [0] is expected to be the program name, fill it in from the real argv.
+      env_argv.insert(env_argv.begin(), argv->at(0));
 
-    const int exit_code = ProcessGlobalArgs(&env_argv,
-                                            nullptr,
-                                            errors,
-                                            kAllowedInEnvironment);
-    if (exit_code != 0) return exit_code;
+      const int exit_code = ProcessGlobalArgs(&env_argv,
+                                              nullptr,
+                                              errors,
+                                              kAllowedInEnvironment);
+      if (exit_code != 0) return exit_code;
+    }
   }
 #endif
 
-  const int exit_code = ProcessGlobalArgs(argv,
-                                          exec_argv,
-                                          errors,
-                                          kDisallowedInEnvironment);
-  if (exit_code != 0) return exit_code;
+  if (!(flags & ProcessFlags::kDisableCLIOptions)) {
+    const int exit_code = ProcessGlobalArgs(argv,
+                                            exec_argv,
+                                            errors,
+                                            kDisallowedInEnvironment);
+    if (exit_code != 0) return exit_code;
+  }
 
   // Set the process.title immediately after processing argv if --title is set.
   if (!per_process::cli_options->title.empty())
     uv_set_process_title(per_process::cli_options->title.c_str());
 
 #if defined(NODE_HAVE_I18N_SUPPORT)
-  // If the parameter isn't given, use the env variable.
-  if (per_process::cli_options->icu_data_dir.empty())
-    credentials::SafeGetenv("NODE_ICU_DATA",
-                            &per_process::cli_options->icu_data_dir);
+  if (!(flags & ProcessFlags::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",
+                              &per_process::cli_options->icu_data_dir);
 
 #ifdef NODE_ICU_DEFAULT_DATA_DIR
-  // If neither the CLI option nor the environment variable was specified,
-  // fall back to the configured default
-  if (per_process::cli_options->icu_data_dir.empty()) {
-    // Check whether the NODE_ICU_DEFAULT_DATA_DIR contains the right data
-    // file and can be read.
-    static const char full_path[] =
-        NODE_ICU_DEFAULT_DATA_DIR "/" U_ICUDATA_NAME ".dat";
-
-    FILE* f = fopen(full_path, "rb");
-
-    if (f != nullptr) {
-      fclose(f);
-      per_process::cli_options->icu_data_dir = NODE_ICU_DEFAULT_DATA_DIR;
+    // If neither the CLI option nor the environment variable was specified,
+    // fall back to the configured default
+    if (per_process::cli_options->icu_data_dir.empty()) {
+      // Check whether the NODE_ICU_DEFAULT_DATA_DIR contains the right data
+      // file and can be read.
+      static const char full_path[] =
+          NODE_ICU_DEFAULT_DATA_DIR "/" U_ICUDATA_NAME ".dat";
+
+      FILE* f = fopen(full_path, "rb");
+
+      if (f != nullptr) {
+        fclose(f);
+        per_process::cli_options->icu_data_dir = NODE_ICU_DEFAULT_DATA_DIR;
+      }
     }
-  }
 #endif  // NODE_ICU_DEFAULT_DATA_DIR
 
-  // Initialize ICU.
-  // If icu_data_dir is empty here, it will load the 'minimal' data.
-  if (!i18n::InitializeICUDirectory(per_process::cli_options->icu_data_dir)) {
-    errors->push_back("could not initialize ICU "
-                      "(check NODE_ICU_DATA or --icu-data-dir parameters)\n");
-    return 9;
+    // Initialize ICU.
+    // If icu_data_dir is empty here, it will load the 'minimal' data.
+    if (!i18n::InitializeICUDirectory(per_process::cli_options->icu_data_dir)) {
+      errors->push_back("could not initialize ICU "
+                        "(check NODE_ICU_DATA or --icu-data-dir parameters)\n");
+      return 9;
+    }
+    per_process::metadata.versions.InitializeIntlVersions();
   }
-  per_process::metadata.versions.InitializeIntlVersions();
 
 # ifndef __POSIX__
   std::string tz;
@@ -956,7 +971,8 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
 InitializationResult InitializeOncePerProcess(
   int argc,
   char** argv,
-  InitializationSettingsFlags flags) {
+  InitializationSettingsFlags flags,
+  ProcessFlags::Flags process_flags) {
   uint64_t init_flags = flags;
   if (init_flags & kDefaultInitialization) {
     init_flags = init_flags | kInitializeV8 | kInitOpenSSL | kRunPlatformInit;
@@ -982,8 +998,8 @@ InitializationResult InitializeOncePerProcess(
 
   // This needs to run *before* V8::Initialize().
   {
-    result.exit_code =
-        InitializeNodeWithArgs(&(result.args), &(result.exec_args), &errors);
+    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) {
diff --git a/src/node.h b/src/node.h
index f34ed3939704bc..8d9e9935258c35 100644
--- a/src/node.h
+++ b/src/node.h
@@ -214,6 +214,20 @@ namespace node {
 class IsolateData;
 class Environment;
 
+namespace ProcessFlags {
+enum Flags : uint64_t {
+  kNoFlags = 0,
+  // Enable stdio inheritance, which is disabled by default.
+  kEnableStdioInheritance = 1 << 0,
+  // Disable reading the NODE_OPTIONS environment variable.
+  kDisableNodeOptionsEnv = 1 << 1,
+  // Do not parse CLI options.
+  kDisableCLIOptions = 1 << 2,
+  // Do not initialize ICU.
+  kNoICU = 1 << 3,
+};
+}  // namespace ProcessFlags
+
 // TODO(addaleax): Officially deprecate this and replace it with something
 // better suited for a public embedder API.
 NODE_EXTERN int Start(int argc, char* argv[]);
@@ -226,9 +240,16 @@ 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);
+NODE_EXTERN int InitializeNodeWithArgs(
+    std::vector<std::string>* argv,
+    std::vector<std::string>* exec_argv,
+    std::vector<std::string>* errors);
+// TODO(zcbenz): Turn above overloaded version into below's default argument.
+NODE_EXTERN int InitializeNodeWithArgs(
+    std::vector<std::string>* argv,
+    std::vector<std::string>* exec_argv,
+    std::vector<std::string>* errors,
+    ProcessFlags::Flags flags);
 
 enum OptionEnvvarSettings {
   kAllowedInEnvironment,
diff --git a/src/node_internals.h b/src/node_internals.h
index c996723db5700c..2671bec87463e1 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -324,7 +324,8 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv);
 InitializationResult InitializeOncePerProcess(
   int argc,
   char** argv,
-  InitializationSettingsFlags flags);
+  InitializationSettingsFlags flags,
+  ProcessFlags::Flags process_flags = ProcessFlags::kNoFlags);
 void TearDownOncePerProcess();
 void SetIsolateErrorHandlers(v8::Isolate* isolate, const IsolateSettings& s);
 void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s);