Skip to content

Commit d3840bc

Browse files
addaleaxBethGriggs
authored andcommittedApr 9, 2019
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: #26544 Fixes: #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> Signed-off-by: Beth Griggs <Bethany.Griggs@uk.ibm.com>
1 parent 78fad32 commit d3840bc

File tree

6 files changed

+176
-84
lines changed

6 files changed

+176
-84
lines changed
 

‎src/env-inl.h

+8
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,14 @@ inline uint64_t Environment::timer_base() const {
447447
return timer_base_;
448448
}
449449

450+
inline std::shared_ptr<KVStore> Environment::envvars() {
451+
return envvars_;
452+
}
453+
454+
inline void Environment::set_envvars(std::shared_ptr<KVStore> envvars) {
455+
envvars_ = envvars;
456+
}
457+
450458
inline bool Environment::printed_error() const {
451459
return printed_error_;
452460
}

‎src/env.cc

+3-1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ Environment::Environment(IsolateData* isolate_data,
207207
set_as_callback_data_template(templ);
208208
}
209209

210+
set_envvars(per_process::real_environment);
211+
210212
// We create new copies of the per-Environment option sets, so that it is
211213
// easier to modify them after Environment creation. The defaults are
212214
// part of the per-Isolate option set, for which in turn the defaults are
@@ -250,7 +252,7 @@ Environment::Environment(IsolateData* isolate_data,
250252
should_abort_on_uncaught_toggle_[0] = 1;
251253

252254
std::string debug_cats;
253-
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats);
255+
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &debug_cats, this);
254256
set_debug_categories(debug_cats, true);
255257

256258
isolate()->GetHeapProfiler()->AddBuildEmbedderGraphCallback(

‎src/env.h

+20
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,23 @@ class AsyncRequest : public MemoryRetainer {
532532
std::atomic_bool stopped_ {true};
533533
};
534534

535+
class KVStore {
536+
public:
537+
virtual v8::Local<v8::String> Get(v8::Isolate* isolate,
538+
v8::Local<v8::String> key) const = 0;
539+
virtual void Set(v8::Isolate* isolate,
540+
v8::Local<v8::String> key,
541+
v8::Local<v8::String> value) = 0;
542+
virtual int32_t Query(v8::Isolate* isolate,
543+
v8::Local<v8::String> key) const = 0;
544+
virtual void Delete(v8::Isolate* isolate, v8::Local<v8::String> key) = 0;
545+
virtual v8::Local<v8::Array> Enumerate(v8::Isolate* isolate) const = 0;
546+
};
547+
548+
namespace per_process {
549+
extern std::shared_ptr<KVStore> real_environment;
550+
}
551+
535552
class AsyncHooks {
536553
public:
537554
// Reason for both UidFields and Fields are that one is stored as a double*
@@ -781,6 +798,8 @@ class Environment {
781798
inline ImmediateInfo* immediate_info();
782799
inline TickInfo* tick_info();
783800
inline uint64_t timer_base() const;
801+
inline std::shared_ptr<KVStore> envvars();
802+
inline void set_envvars(std::shared_ptr<KVStore> envvars);
784803

785804
inline IsolateData* isolate_data() const;
786805

@@ -1075,6 +1094,7 @@ class Environment {
10751094
ImmediateInfo immediate_info_;
10761095
TickInfo tick_info_;
10771096
const uint64_t timer_base_;
1097+
std::shared_ptr<KVStore> envvars_;
10781098
bool printed_error_ = false;
10791099
bool emit_env_nonstring_warning_ = true;
10801100
bool emit_err_name_warning_ = true;

‎src/node_credentials.cc

+21-3
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ using v8::Array;
1515
using v8::Context;
1616
using v8::Function;
1717
using v8::FunctionCallbackInfo;
18+
using v8::HandleScope;
1819
using v8::Isolate;
1920
using v8::Local;
2021
using v8::MaybeLocal;
22+
using v8::NewStringType;
2123
using v8::Object;
2224
using v8::String;
25+
using v8::TryCatch;
2326
using v8::Uint32;
2427
using v8::Value;
2528

@@ -30,13 +33,27 @@ bool linux_at_secure = false;
3033
namespace credentials {
3134

3235
// Look up environment variable unless running as setuid root.
33-
bool SafeGetenv(const char* key, std::string* text) {
36+
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
3437
#if !defined(__CloudABI__) && !defined(_WIN32)
3538
if (per_process::linux_at_secure || getuid() != geteuid() ||
3639
getgid() != getegid())
3740
goto fail;
3841
#endif
3942

43+
if (env != nullptr) {
44+
HandleScope handle_scope(env->isolate());
45+
TryCatch ignore_errors(env->isolate());
46+
MaybeLocal<String> value = env->envvars()->Get(
47+
env->isolate(),
48+
String::NewFromUtf8(env->isolate(), key, NewStringType::kNormal)
49+
.ToLocalChecked());
50+
if (value.IsEmpty()) goto fail;
51+
String::Utf8Value utf8_value(env->isolate(), value.ToLocalChecked());
52+
if (*utf8_value == nullptr) goto fail;
53+
*text = std::string(*utf8_value, utf8_value.length());
54+
return true;
55+
}
56+
4057
{
4158
Mutex::ScopedLock lock(per_process::env_var_mutex);
4259
if (const char* value = getenv(key)) {
@@ -52,10 +69,11 @@ bool SafeGetenv(const char* key, std::string* text) {
5269

5370
static void SafeGetenv(const FunctionCallbackInfo<Value>& args) {
5471
CHECK(args[0]->IsString());
55-
Isolate* isolate = args.GetIsolate();
72+
Environment* env = Environment::GetCurrent(args);
73+
Isolate* isolate = env->isolate();
5674
Utf8Value strenvtag(isolate, args[0]);
5775
std::string text;
58-
if (!SafeGetenv(*strenvtag, &text)) return;
76+
if (!SafeGetenv(*strenvtag, &text, env)) return;
5977
Local<Value> result =
6078
ToV8Value(isolate->GetCurrentContext(), text).ToLocalChecked();
6179
args.GetReturnValue().Set(result);

‎src/node_env_var.cc

+123-79
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,29 @@ using v8::PropertyCallbackInfo;
2727
using v8::String;
2828
using v8::Value;
2929

30+
class RealEnvStore final : public KVStore {
31+
public:
32+
Local<String> Get(Isolate* isolate, Local<String> key) const override;
33+
void Set(Isolate* isolate, Local<String> key, Local<String> value) override;
34+
int32_t Query(Isolate* isolate, Local<String> key) const override;
35+
void Delete(Isolate* isolate, Local<String> key) override;
36+
Local<Array> Enumerate(Isolate* isolate) const override;
37+
};
38+
3039
namespace per_process {
3140
Mutex env_var_mutex;
41+
std::shared_ptr<KVStore> real_environment = std::make_shared<RealEnvStore>();
3242
} // namespace per_process
3343

34-
static void EnvGetter(Local<Name> property,
35-
const PropertyCallbackInfo<Value>& info) {
36-
Isolate* isolate = info.GetIsolate();
37-
if (property->IsSymbol()) {
38-
return info.GetReturnValue().SetUndefined();
39-
}
44+
Local<String> RealEnvStore::Get(Isolate* isolate,
45+
Local<String> property) const {
4046
Mutex::ScopedLock lock(per_process::env_var_mutex);
4147
#ifdef __POSIX__
4248
node::Utf8Value key(isolate, property);
4349
const char* val = getenv(*key);
4450
if (val) {
45-
return info.GetReturnValue().Set(
46-
String::NewFromUtf8(isolate, val, NewStringType::kNormal)
47-
.ToLocalChecked());
51+
return String::NewFromUtf8(isolate, val, NewStringType::kNormal)
52+
.ToLocalChecked();
4853
}
4954
#else // _WIN32
5055
node::TwoByteValue key(isolate, property);
@@ -62,106 +67,72 @@ static void EnvGetter(Local<Name> property,
6267
isolate, two_byte_buffer, NewStringType::kNormal);
6368
if (rc.IsEmpty()) {
6469
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
65-
return;
70+
return Local<String>();
6671
}
67-
return info.GetReturnValue().Set(rc.ToLocalChecked());
72+
return rc.ToLocalChecked();
6873
}
6974
#endif
75+
return Local<String>();
7076
}
7177

72-
static void EnvSetter(Local<Name> property,
73-
Local<Value> value,
74-
const PropertyCallbackInfo<Value>& info) {
75-
Environment* env = Environment::GetCurrent(info);
76-
// calling env->EmitProcessEnvWarning() sets a variable indicating that
77-
// warnings have been emitted. It should be called last after other
78-
// conditions leading to a warning have been met.
79-
if (env->options()->pending_deprecation && !value->IsString() &&
80-
!value->IsNumber() && !value->IsBoolean() &&
81-
env->EmitProcessEnvWarning()) {
82-
if (ProcessEmitDeprecationWarning(
83-
env,
84-
"Assigning any value other than a string, number, or boolean to a "
85-
"process.env property is deprecated. Please make sure to convert "
86-
"the "
87-
"value to a string before setting process.env with it.",
88-
"DEP0104")
89-
.IsNothing())
90-
return;
91-
}
92-
78+
void RealEnvStore::Set(Isolate* isolate,
79+
Local<String> property,
80+
Local<String> value) {
9381
Mutex::ScopedLock lock(per_process::env_var_mutex);
9482
#ifdef __POSIX__
95-
node::Utf8Value key(info.GetIsolate(), property);
96-
node::Utf8Value val(info.GetIsolate(), value);
83+
node::Utf8Value key(isolate, property);
84+
node::Utf8Value val(isolate, value);
9785
setenv(*key, *val, 1);
9886
#else // _WIN32
99-
node::TwoByteValue key(info.GetIsolate(), property);
100-
node::TwoByteValue val(info.GetIsolate(), value);
87+
node::TwoByteValue key(isolate, property);
88+
node::TwoByteValue val(isolate, value);
10189
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
10290
// Environment variables that start with '=' are read-only.
10391
if (key_ptr[0] != L'=') {
10492
SetEnvironmentVariableW(key_ptr, reinterpret_cast<WCHAR*>(*val));
10593
}
10694
#endif
107-
// Whether it worked or not, always return value.
108-
info.GetReturnValue().Set(value);
10995
}
11096

111-
static void EnvQuery(Local<Name> property,
112-
const PropertyCallbackInfo<Integer>& info) {
97+
int32_t RealEnvStore::Query(Isolate* isolate, Local<String> property) const {
11398
Mutex::ScopedLock lock(per_process::env_var_mutex);
114-
int32_t rc = -1; // Not found unless proven otherwise.
115-
if (property->IsString()) {
11699
#ifdef __POSIX__
117-
node::Utf8Value key(info.GetIsolate(), property);
118-
if (getenv(*key)) rc = 0;
100+
node::Utf8Value key(isolate, property);
101+
if (getenv(*key)) return 0;
119102
#else // _WIN32
120-
node::TwoByteValue key(info.GetIsolate(), property);
121-
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
122-
SetLastError(ERROR_SUCCESS);
123-
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
124-
GetLastError() == ERROR_SUCCESS) {
125-
rc = 0;
126-
if (key_ptr[0] == L'=') {
127-
// Environment variables that start with '=' are hidden and read-only.
128-
rc = static_cast<int32_t>(v8::ReadOnly) |
103+
node::TwoByteValue key(isolate, property);
104+
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
105+
SetLastError(ERROR_SUCCESS);
106+
if (GetEnvironmentVariableW(key_ptr, nullptr, 0) > 0 ||
107+
GetLastError() == ERROR_SUCCESS) {
108+
if (key_ptr[0] == L'=') {
109+
// Environment variables that start with '=' are hidden and read-only.
110+
return static_cast<int32_t>(v8::ReadOnly) |
129111
static_cast<int32_t>(v8::DontDelete) |
130112
static_cast<int32_t>(v8::DontEnum);
131-
}
132113
}
133-
#endif
114+
return 0;
134115
}
135-
if (rc != -1) info.GetReturnValue().Set(rc);
116+
#endif
117+
return -1;
136118
}
137119

138-
static void EnvDeleter(Local<Name> property,
139-
const PropertyCallbackInfo<Boolean>& info) {
120+
void RealEnvStore::Delete(Isolate* isolate, Local<String> property) {
140121
Mutex::ScopedLock lock(per_process::env_var_mutex);
141-
if (property->IsString()) {
142122
#ifdef __POSIX__
143-
node::Utf8Value key(info.GetIsolate(), property);
144-
unsetenv(*key);
123+
node::Utf8Value key(isolate, property);
124+
unsetenv(*key);
145125
#else
146-
node::TwoByteValue key(info.GetIsolate(), property);
147-
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
148-
SetEnvironmentVariableW(key_ptr, nullptr);
126+
node::TwoByteValue key(isolate, property);
127+
WCHAR* key_ptr = reinterpret_cast<WCHAR*>(*key);
128+
SetEnvironmentVariableW(key_ptr, nullptr);
149129
#endif
150-
}
151-
152-
// process.env never has non-configurable properties, so always
153-
// return true like the tc39 delete operator.
154-
info.GetReturnValue().Set(true);
155130
}
156131

157-
static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
158-
Environment* env = Environment::GetCurrent(info);
159-
Isolate* isolate = env->isolate();
160-
132+
Local<Array> RealEnvStore::Enumerate(Isolate* isolate) const {
161133
Mutex::ScopedLock lock(per_process::env_var_mutex);
162-
Local<Array> envarr;
163-
int env_size = 0;
164134
#ifdef __POSIX__
135+
int env_size = 0;
165136
while (environ[env_size]) {
166137
env_size++;
167138
}
@@ -177,7 +148,8 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
177148
#else // _WIN32
178149
std::vector<Local<Value>> env_v;
179150
WCHAR* environment = GetEnvironmentStringsW();
180-
if (environment == nullptr) return; // This should not happen.
151+
if (environment == nullptr)
152+
return Array::New(isolate); // This should not happen.
181153
WCHAR* p = environment;
182154
while (*p) {
183155
WCHAR* s;
@@ -198,16 +170,88 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
198170
if (rc.IsEmpty()) {
199171
isolate->ThrowException(ERR_STRING_TOO_LONG(isolate));
200172
FreeEnvironmentStringsW(environment);
201-
return;
173+
return Local<Array>();
202174
}
203175
env_v.push_back(rc.ToLocalChecked());
204176
p = s + wcslen(s) + 1;
205177
}
206178
FreeEnvironmentStringsW(environment);
207179
#endif
208180

209-
envarr = Array::New(isolate, env_v.data(), env_v.size());
210-
info.GetReturnValue().Set(envarr);
181+
return Array::New(isolate, env_v.data(), env_v.size());
182+
}
183+
184+
static void EnvGetter(Local<Name> property,
185+
const PropertyCallbackInfo<Value>& info) {
186+
Environment* env = Environment::GetCurrent(info);
187+
if (property->IsSymbol()) {
188+
return info.GetReturnValue().SetUndefined();
189+
}
190+
CHECK(property->IsString());
191+
info.GetReturnValue().Set(
192+
env->envvars()->Get(env->isolate(), property.As<String>()));
193+
}
194+
195+
static void EnvSetter(Local<Name> property,
196+
Local<Value> value,
197+
const PropertyCallbackInfo<Value>& info) {
198+
Environment* env = Environment::GetCurrent(info);
199+
// calling env->EmitProcessEnvWarning() sets a variable indicating that
200+
// warnings have been emitted. It should be called last after other
201+
// conditions leading to a warning have been met.
202+
if (env->options()->pending_deprecation && !value->IsString() &&
203+
!value->IsNumber() && !value->IsBoolean() &&
204+
env->EmitProcessEnvWarning()) {
205+
if (ProcessEmitDeprecationWarning(
206+
env,
207+
"Assigning any value other than a string, number, or boolean to a "
208+
"process.env property is deprecated. Please make sure to convert "
209+
"the "
210+
"value to a string before setting process.env with it.",
211+
"DEP0104")
212+
.IsNothing())
213+
return;
214+
}
215+
216+
Local<String> key;
217+
Local<String> value_string;
218+
if (!property->ToString(env->context()).ToLocal(&key) ||
219+
!value->ToString(env->context()).ToLocal(&value_string)) {
220+
return;
221+
}
222+
223+
env->envvars()->Set(env->isolate(), key, value_string);
224+
225+
// Whether it worked or not, always return value.
226+
info.GetReturnValue().Set(value);
227+
}
228+
229+
static void EnvQuery(Local<Name> property,
230+
const PropertyCallbackInfo<Integer>& info) {
231+
Environment* env = Environment::GetCurrent(info);
232+
if (property->IsString()) {
233+
int32_t rc = env->envvars()->Query(env->isolate(), property.As<String>());
234+
if (rc != -1) info.GetReturnValue().Set(rc);
235+
}
236+
}
237+
238+
static void EnvDeleter(Local<Name> property,
239+
const PropertyCallbackInfo<Boolean>& info) {
240+
Environment* env = Environment::GetCurrent(info);
241+
if (property->IsString()) {
242+
env->envvars()->Delete(env->isolate(), property.As<String>());
243+
}
244+
245+
// process.env never has non-configurable properties, so always
246+
// return true like the tc39 delete operator.
247+
info.GetReturnValue().Set(true);
248+
}
249+
250+
static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
251+
Environment* env = Environment::GetCurrent(info);
252+
253+
info.GetReturnValue().Set(
254+
env->envvars()->Enumerate(env->isolate()));
211255
}
212256

213257
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context,

‎src/node_internals.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ int ThreadPoolWork::CancelWork() {
293293
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
294294

295295
namespace credentials {
296-
bool SafeGetenv(const char* key, std::string* text);
296+
bool SafeGetenv(const char* key, std::string* text, Environment* env = nullptr);
297297
} // namespace credentials
298298

299299
void DefineZlibConstants(v8::Local<v8::Object> target);

0 commit comments

Comments
 (0)
Please sign in to comment.