From 19a920564585f1c223fb137408d615932609da0b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sun, 16 Dec 2018 01:32:39 +0800 Subject: process: move environment variable proxy code into node_env_var.cc Instead of exposing all the NamedPropertyHandlerConfiguration() parameters in node_internals, simply expose a CreateEnvVarProxy() method that returns a Local that implements process.env, and mark all the property handlers static in node_env_var.cc. This makes the code more encapsulated. PR-URL: https://github.com/nodejs/node/pull/25067 Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- src/node_env_var.cc | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 src/node_env_var.cc (limited to 'src/node_env_var.cc') diff --git a/src/node_env_var.cc b/src/node_env_var.cc new file mode 100644 index 0000000000..de0991bcab --- /dev/null +++ b/src/node_env_var.cc @@ -0,0 +1,214 @@ +#include "node_internals.h" +#include "node_errors.h" + +#ifdef __APPLE__ +#include +#define environ (*_NSGetEnviron()) +#elif !defined(_MSC_VER) +extern char** environ; +#endif + +namespace node { +using v8::Array; +using v8::Boolean; +using v8::Context; +using v8::EscapableHandleScope; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Name; +using v8::NamedPropertyHandlerConfiguration; +using v8::NewStringType; +using v8::Object; +using v8::ObjectTemplate; +using v8::PropertyCallbackInfo; +using v8::String; +using v8::Value; + +static void EnvGetter(Local property, + const PropertyCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + if (property->IsSymbol()) { + return info.GetReturnValue().SetUndefined(); + } + Mutex::ScopedLock lock(environ_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()); + } +#else // _WIN32 + node::TwoByteValue key(isolate, property); + WCHAR buffer[32767]; // The maximum size allowed for environment variables. + SetLastError(ERROR_SUCCESS); + DWORD result = GetEnvironmentVariableW( + reinterpret_cast(*key), buffer, arraysize(buffer)); + // If result >= sizeof buffer the buffer was too small. That should never + // happen. If result == 0 and result != ERROR_SUCCESS the variable was not + // found. + if ((result > 0 || GetLastError() == ERROR_SUCCESS) && + result < arraysize(buffer)) { + const uint16_t* two_byte_buffer = reinterpret_cast(buffer); + v8::MaybeLocal rc = String::NewFromTwoByte( + isolate, two_byte_buffer, NewStringType::kNormal); + if (rc.IsEmpty()) { + isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); + return; + } + return info.GetReturnValue().Set(rc.ToLocalChecked()); + } +#endif +} + +static void EnvSetter(Local property, + Local value, + const PropertyCallbackInfo& info) { + Environment* env = Environment::GetCurrent(info); + if (env->options()->pending_deprecation && env->EmitProcessEnvWarning() && + !value->IsString() && !value->IsNumber() && !value->IsBoolean()) { + 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; + } + + Mutex::ScopedLock lock(environ_mutex); +#ifdef __POSIX__ + node::Utf8Value key(info.GetIsolate(), property); + node::Utf8Value val(info.GetIsolate(), value); + setenv(*key, *val, 1); +#else // _WIN32 + node::TwoByteValue key(info.GetIsolate(), property); + node::TwoByteValue val(info.GetIsolate(), value); + WCHAR* key_ptr = reinterpret_cast(*key); + // Environment variables that start with '=' are read-only. + if (key_ptr[0] != L'=') { + SetEnvironmentVariableW(key_ptr, reinterpret_cast(*val)); + } +#endif + // Whether it worked or not, always return value. + info.GetReturnValue().Set(value); +} + +static void EnvQuery(Local property, + const PropertyCallbackInfo& info) { + Mutex::ScopedLock lock(environ_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; +#else // _WIN32 + node::TwoByteValue key(info.GetIsolate(), property); + WCHAR* key_ptr = reinterpret_cast(*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(v8::ReadOnly) | + static_cast(v8::DontDelete) | + static_cast(v8::DontEnum); + } + } +#endif + } + if (rc != -1) info.GetReturnValue().Set(rc); +} + +static void EnvDeleter(Local property, + const PropertyCallbackInfo& info) { + Mutex::ScopedLock lock(environ_mutex); + if (property->IsString()) { +#ifdef __POSIX__ + node::Utf8Value key(info.GetIsolate(), property); + unsetenv(*key); +#else + node::TwoByteValue key(info.GetIsolate(), property); + WCHAR* key_ptr = reinterpret_cast(*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& info) { + Environment* env = Environment::GetCurrent(info); + Isolate* isolate = env->isolate(); + + Mutex::ScopedLock lock(environ_mutex); + Local envarr; + int env_size = 0; +#ifdef __POSIX__ + while (environ[env_size]) { + env_size++; + } + std::vector> env_v(env_size); + + for (int i = 0; i < env_size; ++i) { + const char* var = environ[i]; + const char* s = strchr(var, '='); + const int length = s ? s - var : strlen(var); + env_v[i] = String::NewFromUtf8(isolate, var, NewStringType::kNormal, length) + .ToLocalChecked(); + } +#else // _WIN32 + std::vector> env_v; + WCHAR* environment = GetEnvironmentStringsW(); + if (environment == nullptr) return; // This should not happen. + WCHAR* p = environment; + while (*p) { + WCHAR* s; + if (*p == L'=') { + // If the key starts with '=' it is a hidden environment variable. + p += wcslen(p) + 1; + continue; + } else { + s = wcschr(p, L'='); + } + if (!s) { + s = p + wcslen(p); + } + const uint16_t* two_byte_buffer = reinterpret_cast(p); + const size_t two_byte_buffer_len = s - p; + v8::MaybeLocal rc = String::NewFromTwoByte( + isolate, two_byte_buffer, NewStringType::kNormal, two_byte_buffer_len); + if (rc.IsEmpty()) { + isolate->ThrowException(ERR_STRING_TOO_LONG(isolate)); + FreeEnvironmentStringsW(environment); + return; + } + env_v.push_back(rc.ToLocalChecked()); + p = s + wcslen(s) + 1; + } + FreeEnvironmentStringsW(environment); +#endif + + envarr = Array::New(isolate, env_v.data(), env_v.size()); + info.GetReturnValue().Set(envarr); +} + +Local CreateEnvVarProxy(Local context, + Isolate* isolate, + Local data) { + EscapableHandleScope scope(isolate); + Local env_proxy_template = ObjectTemplate::New(isolate); + env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration( + EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, data)); + Local env_proxy = + env_proxy_template->NewInstance(context).ToLocalChecked(); + return scope.Escape(env_proxy); +} +} // namespace node -- cgit v1.2.3