summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2019-03-13 12:24:27 +0000
committerAnna Henningsen <anna@addaleax.net>2019-03-30 22:25:33 +0100
commitf5b5fe3937ec353e43d30ae976725e0773e14c6f (patch)
tree57e532e5d82dbf3c553c83bfaa41bb50c76c4b71
parente4e2b0ce134ce781847272ae0b4bb889e75f223f (diff)
downloadandroid-node-v8-f5b5fe3937ec353e43d30ae976725e0773e14c6f.tar.gz
android-node-v8-f5b5fe3937ec353e43d30ae976725e0773e14c6f.tar.bz2
android-node-v8-f5b5fe3937ec353e43d30ae976725e0773e14c6f.zip
src: allow per-Environment set of env vars
Abstract the `process.env` backing mechanism in C++ to allow different kinds of backing stores for `process.env` for different Environments. PR-URL: https://github.com/nodejs/node/pull/26544 Fixes: https://github.com/nodejs/node/issues/24947 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
-rw-r--r--src/env-inl.h8
-rw-r--r--src/env.cc4
-rw-r--r--src/env.h20
-rw-r--r--src/node_credentials.cc24
-rw-r--r--src/node_env_var.cc202
-rw-r--r--src/node_internals.h2
6 files changed, 176 insertions, 84 deletions
diff --git a/src/env-inl.h b/src/env-inl.h
index 69c70804c8..3eedbb857b 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -447,6 +447,14 @@ inline uint64_t Environment::timer_base() const {
return timer_base_;
}
+inline std::shared_ptr<KVStore> Environment::envvars() {
+ return envvars_;
+}
+
+inline void Environment::set_envvars(std::shared_ptr<KVStore> envvars) {
+ envvars_ = envvars;
+}
+
inline bool Environment::printed_error() const {
return printed_error_;
}
diff --git a/src/env.cc b/src/env.cc
index 174cadcc04..ffdb4289bb 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -178,6 +178,8 @@ Environment::Environment(IsolateData* isolate_data,
set_as_callback_data_template(templ);
}
+ set_envvars(per_process::real_environment);
+
// We create new copies of the per-Environment option sets, so that it is
// easier to modify them after Environment creation. The defaults are
// part of the per-Isolate option set, for which in turn the defaults are
@@ -221,7 +223,7 @@ Environment::Environment(IsolateData* isolate_data,
should_abort_on_uncaught_toggle_[0] = 1;
std::string debug_cats;
- credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
+ credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats, this);
set_debug_categories(debug_cats, true);
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(
diff --git a/src/env.h b/src/env.h
index 743c236827..e6c3a1c037 100644
--- a/src/env.h
+++ b/src/env.h
@@ -540,6 +540,23 @@ class AsyncRequest : public MemoryRetainer {
std::atomic_bool stopped_ {true};
};
+class KVStore {
+ public:
+ virtual v8::Local<v8::String> Get(v8::Isolate* isolate,
+ v8::Local<v8::String> key) const = 0;
+ virtual void Set(v8::Isolate* isolate,
+ v8::Local<v8::String> key,
+ v8::Local<v8::String> value) = 0;
+ virtual int32_t Query(v8::Isolate* isolate,
+ v8::Local<v8::String> key) const = 0;
+ virtual void Delete(v8::Isolate* isolate, v8::Local<v8::String> key) = 0;
+ virtual v8::Local<v8::Array> Enumerate(v8::Isolate* isolate) const = 0;
+};
+
+namespace per_process {
+extern std::shared_ptr<KVStore> real_environment;
+}
+
class AsyncHooks {
public:
// Reason for both UidFields and Fields are that one is stored as a double*
@@ -789,6 +806,8 @@ class Environment {
inline ImmediateInfo* immediate_info();
inline TickInfo* tick_info();
inline uint64_t timer_base() const;
+ inline std::shared_ptr<KVStore> envvars();
+ inline void set_envvars(std::shared_ptr<KVStore> envvars);
inline IsolateData* isolate_data() const;
@@ -1075,6 +1094,7 @@ class Environment {
ImmediateInfo immediate_info_;
TickInfo tick_info_;
const uint64_t timer_base_;
+ std::shared_ptr<KVStore> envvars_;
bool printed_error_ = false;
bool emit_env_nonstring_warning_ = true;
bool emit_err_name_warning_ = true;
diff --git a/src/node_credentials.cc b/src/node_credentials.cc
index 8d38c38a0c..23b9ad2893 100644
--- a/src/node_credentials.cc
+++ b/src/node_credentials.cc
@@ -15,11 +15,14 @@ using v8::Array;
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
+using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::MaybeLocal;
+using v8::NewStringType;
using v8::Object;
using v8::String;
+using v8::TryCatch;
using v8::Uint32;
using v8::Value;
@@ -30,13 +33,27 @@ bool linux_at_secure = false;
namespace credentials {
// Look up environment variable unless running as setuid root.
-bool SafeGetenv(const char* key, std::string* text) {
+bool SafeGetenv(const char* key, std::string* text, Environment* env) {
#if !defined(__CloudABI__) && !defined(_WIN32)
if (per_process::linux_at_secure || getuid() != geteuid() ||
getgid() != getegid())
goto fail;
#endif
+ if (env != nullptr) {
+ HandleScope handle_scope(env->isolate());
+ TryCatch ignore_errors(env->isolate());
+ MaybeLocal<String> value = env->envvars()->Get(
+ env->isolate(),
+ String::NewFromUtf8(env->isolate(), key, NewStringType::kNormal)
+ .ToLocalChecked());
+ if (value.IsEmpty()) goto fail;
+ String::Utf8Value utf8_value(env->isolate(), value.ToLocalChecked());
+ if (*utf8_value == nullptr) goto fail;
+ *text = std::string(*utf8_value, utf8_value.length());
+ return true;
+ }
+
{
Mutex::ScopedLock lock(per_process::env_var_mutex);
if (const char* value = getenv(key)) {
@@ -52,10 +69,11 @@ fail:
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
- Isolate* isolate = args.GetIsolate();
+ Environment* env = Environment::GetCurrent(args);
+ Isolate* isolate = env->isolate();
Utf8Value strenvtag(isolate, args[0]);
std::string text;
- if (!SafeGetenv(*strenvtag, &text)) return;
+ if (!SafeGetenv(*strenvtag, &text, env)) return;
Local<Value> result =
ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
args.GetReturnValue().Set(result);
diff --git a/src/node_env_var.cc b/src/node_env_var.cc
index 7e4913702b..6bd799e19f 100644
--- a/src/node_env_var.cc
+++ b/src/node_env_var.cc
@@ -27,24 +27,29 @@ using v8::PropertyCallbackInfo;
using v8::String;
using v8::Value;
+class RealEnvStore final : public KVStore {
+ public:
+ Local<String> Get(Isolate* isolate, Local<String> key) const override;
+ void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
+ int32_t Query(Isolate* isolate, Local<String> key) const override;
+ void Delete(Isolate* isolate, Local<String> key) override;
+ Local<Array> Enumerate(Isolate* isolate) const override;
+};
+
namespace per_process {
Mutex env_var_mutex;
+std::shared_ptr<KVStore> real_environment = std::make_shared<RealEnvStore>();
} // namespace per_process
-static void EnvGetter(Local<Name> property,
- const PropertyCallbackInfo<Value>& info) {
- Isolate* isolate = info.GetIsolate();
- if (property->IsSymbol()) {
- return info.GetReturnValue().SetUndefined();
- }
+Local<String> RealEnvStore::Get(Isolate* isolate,
+ Local<String> property) const {
Mutex::ScopedLock lock(per_process::env_var_mutex);
#ifdef __POSIX__
node::Utf8Value key(isolate, property);
const char* val = getenv(*key);
if (val) {
- return info.GetReturnValue().Set(
- String::NewFromUtf8(isolate, val, NewStringType::kNormal)
- .ToLocalChecked());
+ return String::NewFromUtf8(isolate, val, NewStringType::kNormal)
+ .ToLocalChecked();
}
#else // _WIN32
node::TwoByteValue key(isolate, property);
@@ -62,106 +67,72 @@ static void EnvGetter(Local<Name> property,
isolate, two_byte_buffer, NewStringType::kNormal);
if (rc.IsEmpty()) {
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
- return;
+ return Local<String>();
}
- return info.GetReturnValue().Set(rc.ToLocalChecked());
+ return rc.ToLocalChecked();
}
#endif
+ return Local<String>();
}
-static void EnvSetter(Local<Name> property,
- Local<Value> value,
- const PropertyCallbackInfo<Value>& info) {
- Environment* env = Environment::GetCurrent(info);
- // calling env->EmitProcessEnvWarning() sets a variable indicating that
- // warnings have been emitted. It should be called last after other
- // conditions leading to a warning have been met.
- if (env->options()->pending_deprecation && !value->IsString() &&
- !value->IsNumber() && !value->IsBoolean() &&
- env->EmitProcessEnvWarning()) {
- if (ProcessEmitDeprecationWarning(
- env,
- "Assigning any value other than a string, number, or boolean to a "
- "process.env property is deprecated. Please make sure to convert "
- "the "
- "value to a string before setting process.env with it.",
- "DEP0104")
- .IsNothing())
- return;
- }
-
+void RealEnvStore::Set(Isolate* isolate,
+ Local<String> property,
+ Local<String> value) {
Mutex::ScopedLock lock(per_process::env_var_mutex);
#ifdef __POSIX__
- node::Utf8Value key(info.GetIsolate(), property);
- node::Utf8Value val(info.GetIsolate(), value);
+ node::Utf8Value key(isolate, property);
+ node::Utf8Value val(isolate, value);
setenv(*key, *val, 1);
#else // _WIN32
- node::TwoByteValue key(info.GetIsolate(), property);
- node::TwoByteValue val(info.GetIsolate(), value);
+ node::TwoByteValue key(isolate, property);
+ node::TwoByteValue val(isolate, value);
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
// Environment variables that start with '=' are read-only.
if (key_ptr[0] != L'=') {
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
}
#endif
- // Whether it worked or not, always return value.
- info.GetReturnValue().Set(value);
}
-static void EnvQuery(Local<Name> property,
- const PropertyCallbackInfo<Integer>& info) {
+int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
Mutex::ScopedLock lock(per_process::env_var_mutex);
- int32_t rc = -1; // Not found unless proven otherwise.
- if (property->IsString()) {
#ifdef __POSIX__
- node::Utf8Value key(info.GetIsolate(), property);
- if (getenv(*key)) rc = 0;
+ node::Utf8Value key(isolate, property);
+ if (getenv(*key)) return 0;
#else // _WIN32
- node::TwoByteValue key(info.GetIsolate(), property);
- WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
- SetLastError(ERROR_SUCCESS);
- if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
- GetLastError() == ERROR_SUCCESS) {
- rc = 0;
- if (key_ptr[0] == L'=') {
- // Environment variables that start with '=' are hidden and read-only.
- rc = static_cast<int32_t>(v8::ReadOnly) |
+ node::TwoByteValue key(isolate, property);
+ WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
+ SetLastError(ERROR_SUCCESS);
+ if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
+ GetLastError() == ERROR_SUCCESS) {
+ if (key_ptr[0] == L'=') {
+ // Environment variables that start with '=' are hidden and read-only.
+ return static_cast<int32_t>(v8::ReadOnly) |
static_cast<int32_t>(v8::DontDelete) |
static_cast<int32_t>(v8::DontEnum);
- }
}
-#endif
+ return 0;
}
- if (rc != -1) info.GetReturnValue().Set(rc);
+#endif
+ return -1;
}
-static void EnvDeleter(Local<Name> property,
- const PropertyCallbackInfo<Boolean>& info) {
+void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
Mutex::ScopedLock lock(per_process::env_var_mutex);
- if (property->IsString()) {
#ifdef __POSIX__
- node::Utf8Value key(info.GetIsolate(), property);
- unsetenv(*key);
+ node::Utf8Value key(isolate, property);
+ unsetenv(*key);
#else
- node::TwoByteValue key(info.GetIsolate(), property);
- WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
- SetEnvironmentVariableW(key_ptr, nullptr);
+ node::TwoByteValue key(isolate, property);
+ WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
+ SetEnvironmentVariableW(key_ptr, nullptr);
#endif
- }
-
- // process.env never has non-configurable properties, so always
- // return true like the tc39 delete operator.
- info.GetReturnValue().Set(true);
}
-static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
- Environment* env = Environment::GetCurrent(info);
- Isolate* isolate = env->isolate();
-
+Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
Mutex::ScopedLock lock(per_process::env_var_mutex);
- Local<Array> envarr;
- int env_size = 0;
#ifdef __POSIX__
+ int env_size = 0;
while (environ[env_size]) {
env_size++;
}
@@ -177,7 +148,8 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
#else // _WIN32
std::vector<Local<Value>> env_v;
WCHAR* environment = GetEnvironmentStringsW();
- if (environment == nullptr) return; // This should not happen.
+ if (environment == nullptr)
+ return Array::New(isolate); // This should not happen.
WCHAR* p = environment;
while (*p) {
WCHAR* s;
@@ -198,7 +170,7 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
if (rc.IsEmpty()) {
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
FreeEnvironmentStringsW(environment);
- return;
+ return Local<Array>();
}
env_v.push_back(rc.ToLocalChecked());
p = s + wcslen(s) + 1;
@@ -206,8 +178,80 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
FreeEnvironmentStringsW(environment);
#endif
- envarr = Array::New(isolate, env_v.data(), env_v.size());
- info.GetReturnValue().Set(envarr);
+ return Array::New(isolate, env_v.data(), env_v.size());
+}
+
+static void EnvGetter(Local<Name> property,
+ const PropertyCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ if (property->IsSymbol()) {
+ return info.GetReturnValue().SetUndefined();
+ }
+ CHECK(property->IsString());
+ info.GetReturnValue().Set(
+ env->envvars()->Get(env->isolate(), property.As<String>()));
+}
+
+static void EnvSetter(Local<Name> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<Value>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ // calling env->EmitProcessEnvWarning() sets a variable indicating that
+ // warnings have been emitted. It should be called last after other
+ // conditions leading to a warning have been met.
+ if (env->options()->pending_deprecation && !value->IsString() &&
+ !value->IsNumber() && !value->IsBoolean() &&
+ env->EmitProcessEnvWarning()) {
+ if (ProcessEmitDeprecationWarning(
+ env,
+ "Assigning any value other than a string, number, or boolean to a "
+ "process.env property is deprecated. Please make sure to convert "
+ "the "
+ "value to a string before setting process.env with it.",
+ "DEP0104")
+ .IsNothing())
+ return;
+ }
+
+ Local<String> key;
+ Local<String> value_string;
+ if (!property->ToString(env->context()).ToLocal(&key) ||
+ !value->ToString(env->context()).ToLocal(&value_string)) {
+ return;
+ }
+
+ env->envvars()->Set(env->isolate(), key, value_string);
+
+ // Whether it worked or not, always return value.
+ info.GetReturnValue().Set(value);
+}
+
+static void EnvQuery(Local<Name> property,
+ const PropertyCallbackInfo<Integer>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ if (property->IsString()) {
+ int32_t rc = env->envvars()->Query(env->isolate(), property.As<String>());
+ if (rc != -1) info.GetReturnValue().Set(rc);
+ }
+}
+
+static void EnvDeleter(Local<Name> property,
+ const PropertyCallbackInfo<Boolean>& info) {
+ Environment* env = Environment::GetCurrent(info);
+ if (property->IsString()) {
+ env->envvars()->Delete(env->isolate(), property.As<String>());
+ }
+
+ // process.env never has non-configurable properties, so always
+ // return true like the tc39 delete operator.
+ info.GetReturnValue().Set(true);
+}
+
+static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
+ Environment* env = Environment::GetCurrent(info);
+
+ info.GetReturnValue().Set(
+ env->envvars()->Enumerate(env->isolate()));
}
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context,
diff --git a/src/node_internals.h b/src/node_internals.h
index 8c2864aae8..012848491e 100644
--- a/src/node_internals.h
+++ b/src/node_internals.h
@@ -293,7 +293,7 @@ int ThreadPoolWork::CancelWork() {
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
namespace credentials {
-bool SafeGetenv(const char* key, std::string* text);
+bool SafeGetenv(const char* key, std::string* text, Environment* env = nullptr);
} // namespace credentials
void DefineZlibConstants(v8::Local<v8::Object> target);