mirror of
https://github.com/zebrajr/node.git
synced 2026-01-15 12:15:26 +00:00
PR-URL: https://github.com/nodejs/node/pull/22758 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Refael Ackermann <refack@gmail.com>
875 lines
24 KiB
C++
875 lines
24 KiB
C++
#include "node.h"
|
|
#include "node_internals.h"
|
|
#include "base_object.h"
|
|
#include "base_object-inl.h"
|
|
#include "env-inl.h"
|
|
#include "util-inl.h"
|
|
#include "uv.h"
|
|
#include "v8.h"
|
|
|
|
#if HAVE_INSPECTOR
|
|
#include "inspector_io.h"
|
|
#endif
|
|
|
|
#include <limits.h> // PATH_MAX
|
|
#include <stdio.h>
|
|
|
|
#if defined(_MSC_VER)
|
|
#include <direct.h>
|
|
#include <io.h>
|
|
#define umask _umask
|
|
typedef int mode_t;
|
|
#else
|
|
#include <pthread.h>
|
|
#include <sys/resource.h> // getrlimit, setrlimit
|
|
#include <termios.h> // tcgetattr, tcsetattr
|
|
#include <unistd.h> // setuid, getuid
|
|
#endif
|
|
|
|
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
#include <pwd.h> // getpwnam()
|
|
#include <grp.h> // getgrnam()
|
|
#endif
|
|
|
|
#ifdef __APPLE__
|
|
#include <crt_externs.h>
|
|
#define environ (*_NSGetEnviron())
|
|
#elif !defined(_MSC_VER)
|
|
extern char **environ;
|
|
#endif
|
|
|
|
namespace node {
|
|
|
|
using v8::Array;
|
|
using v8::ArrayBuffer;
|
|
using v8::BigUint64Array;
|
|
using v8::Boolean;
|
|
using v8::Context;
|
|
using v8::Float64Array;
|
|
using v8::Function;
|
|
using v8::FunctionCallbackInfo;
|
|
using v8::HeapStatistics;
|
|
using v8::Integer;
|
|
using v8::Isolate;
|
|
using v8::Local;
|
|
using v8::Name;
|
|
using v8::Number;
|
|
using v8::PropertyCallbackInfo;
|
|
using v8::String;
|
|
using v8::Uint32;
|
|
using v8::Uint32Array;
|
|
using v8::Value;
|
|
|
|
Mutex process_mutex;
|
|
Mutex environ_mutex;
|
|
|
|
// Microseconds in a second, as a float, used in CPUUsage() below
|
|
#define MICROS_PER_SEC 1e6
|
|
// used in Hrtime() below
|
|
#define NANOS_PER_SEC 1000000000
|
|
|
|
#ifdef _WIN32
|
|
/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
|
|
#define CHDIR_BUFSIZE (MAX_PATH * 4)
|
|
#else
|
|
#define CHDIR_BUFSIZE (PATH_MAX)
|
|
#endif
|
|
|
|
void Abort(const FunctionCallbackInfo<Value>& args) {
|
|
Abort();
|
|
}
|
|
|
|
void Chdir(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
CHECK(env->is_main_thread());
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsString());
|
|
Utf8Value path(env->isolate(), args[0]);
|
|
int err = uv_chdir(*path);
|
|
if (err) {
|
|
// Also include the original working directory, since that will usually
|
|
// be helpful information when debugging a `chdir()` failure.
|
|
char buf[CHDIR_BUFSIZE];
|
|
size_t cwd_len = sizeof(buf);
|
|
uv_cwd(buf, &cwd_len);
|
|
return env->ThrowUVException(err, "chdir", nullptr, buf, *path);
|
|
}
|
|
}
|
|
|
|
// CPUUsage use libuv's uv_getrusage() this-process resource usage accessor,
|
|
// to access ru_utime (user CPU time used) and ru_stime (system CPU time used),
|
|
// which are uv_timeval_t structs (long tv_sec, long tv_usec).
|
|
// Returns those values as Float64 microseconds in the elements of the array
|
|
// passed to the function.
|
|
void CPUUsage(const FunctionCallbackInfo<Value>& args) {
|
|
uv_rusage_t rusage;
|
|
|
|
// Call libuv to get the values we'll return.
|
|
int err = uv_getrusage(&rusage);
|
|
if (err) {
|
|
// On error, return the strerror version of the error code.
|
|
Local<String> errmsg = OneByteString(args.GetIsolate(), uv_strerror(err));
|
|
return args.GetReturnValue().Set(errmsg);
|
|
}
|
|
|
|
// Get the double array pointer from the Float64Array argument.
|
|
CHECK(args[0]->IsFloat64Array());
|
|
Local<Float64Array> array = args[0].As<Float64Array>();
|
|
CHECK_EQ(array->Length(), 2);
|
|
Local<ArrayBuffer> ab = array->Buffer();
|
|
double* fields = static_cast<double*>(ab->GetContents().Data());
|
|
|
|
// Set the Float64Array elements to be user / system values in microseconds.
|
|
fields[0] = MICROS_PER_SEC * rusage.ru_utime.tv_sec + rusage.ru_utime.tv_usec;
|
|
fields[1] = MICROS_PER_SEC * rusage.ru_stime.tv_sec + rusage.ru_stime.tv_usec;
|
|
}
|
|
|
|
void Cwd(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
char buf[CHDIR_BUFSIZE];
|
|
size_t cwd_len = sizeof(buf);
|
|
int err = uv_cwd(buf, &cwd_len);
|
|
if (err)
|
|
return env->ThrowUVException(err, "uv_cwd");
|
|
|
|
Local<String> cwd = String::NewFromUtf8(env->isolate(),
|
|
buf,
|
|
v8::NewStringType::kNormal,
|
|
cwd_len).ToLocalChecked();
|
|
args.GetReturnValue().Set(cwd);
|
|
}
|
|
|
|
|
|
// Hrtime exposes libuv's uv_hrtime() high-resolution timer.
|
|
|
|
// This is the legacy version of hrtime before BigInt was introduced in
|
|
// JavaScript.
|
|
// The value returned by uv_hrtime() is a 64-bit int representing nanoseconds,
|
|
// so this function instead fills in an Uint32Array with 3 entries,
|
|
// to avoid any integer overflow possibility.
|
|
// The first two entries contain the second part of the value
|
|
// broken into the upper/lower 32 bits to be converted back in JS,
|
|
// because there is no Uint64Array in JS.
|
|
// The third entry contains the remaining nanosecond part of the value.
|
|
void Hrtime(const FunctionCallbackInfo<Value>& args) {
|
|
uint64_t t = uv_hrtime();
|
|
|
|
Local<ArrayBuffer> ab = args[0].As<Uint32Array>()->Buffer();
|
|
uint32_t* fields = static_cast<uint32_t*>(ab->GetContents().Data());
|
|
|
|
fields[0] = (t / NANOS_PER_SEC) >> 32;
|
|
fields[1] = (t / NANOS_PER_SEC) & 0xffffffff;
|
|
fields[2] = t % NANOS_PER_SEC;
|
|
}
|
|
|
|
void HrtimeBigInt(const FunctionCallbackInfo<Value>& args) {
|
|
Local<ArrayBuffer> ab = args[0].As<BigUint64Array>()->Buffer();
|
|
uint64_t* fields = static_cast<uint64_t*>(ab->GetContents().Data());
|
|
fields[0] = uv_hrtime();
|
|
}
|
|
|
|
void Kill(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
Local<Context> context = env->context();
|
|
|
|
if (args.Length() != 2)
|
|
return env->ThrowError("Bad argument.");
|
|
|
|
int pid;
|
|
if (!args[0]->Int32Value(context).To(&pid)) return;
|
|
int sig;
|
|
if (!args[1]->Int32Value(context).To(&sig)) return;
|
|
int err = uv_kill(pid, sig);
|
|
args.GetReturnValue().Set(err);
|
|
}
|
|
|
|
|
|
void MemoryUsage(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
size_t rss;
|
|
int err = uv_resident_set_memory(&rss);
|
|
if (err)
|
|
return env->ThrowUVException(err, "uv_resident_set_memory");
|
|
|
|
Isolate* isolate = env->isolate();
|
|
// V8 memory usage
|
|
HeapStatistics v8_heap_stats;
|
|
isolate->GetHeapStatistics(&v8_heap_stats);
|
|
|
|
// Get the double array pointer from the Float64Array argument.
|
|
CHECK(args[0]->IsFloat64Array());
|
|
Local<Float64Array> array = args[0].As<Float64Array>();
|
|
CHECK_EQ(array->Length(), 4);
|
|
Local<ArrayBuffer> ab = array->Buffer();
|
|
double* fields = static_cast<double*>(ab->GetContents().Data());
|
|
|
|
fields[0] = rss;
|
|
fields[1] = v8_heap_stats.total_heap_size();
|
|
fields[2] = v8_heap_stats.used_heap_size();
|
|
fields[3] = isolate->AdjustAmountOfExternalAllocatedMemory(0);
|
|
}
|
|
|
|
// Most of the time, it's best to use `console.error` to write
|
|
// to the process.stderr stream. However, in some cases, such as
|
|
// when debugging the stream.Writable class or the process.nextTick
|
|
// function, it is useful to bypass JavaScript entirely.
|
|
void RawDebug(const FunctionCallbackInfo<Value>& args) {
|
|
CHECK(args.Length() == 1 && args[0]->IsString() &&
|
|
"must be called with a single string");
|
|
Utf8Value message(args.GetIsolate(), args[0]);
|
|
PrintErrorString("%s\n", *message);
|
|
fflush(stderr);
|
|
}
|
|
|
|
void StartProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
env->StartProfilerIdleNotifier();
|
|
}
|
|
|
|
|
|
void StopProfilerIdleNotifier(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
env->StopProfilerIdleNotifier();
|
|
}
|
|
|
|
void Umask(const FunctionCallbackInfo<Value>& args) {
|
|
uint32_t old;
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsUndefined() || args[0]->IsUint32());
|
|
|
|
if (args[0]->IsUndefined()) {
|
|
old = umask(0);
|
|
umask(static_cast<mode_t>(old));
|
|
} else {
|
|
int oct = args[0].As<Uint32>()->Value();
|
|
old = umask(static_cast<mode_t>(oct));
|
|
}
|
|
|
|
args.GetReturnValue().Set(old);
|
|
}
|
|
|
|
void Uptime(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
double uptime;
|
|
|
|
uv_update_time(env->event_loop());
|
|
uptime = uv_now(env->event_loop()) - prog_start_time;
|
|
|
|
args.GetReturnValue().Set(Number::New(env->isolate(), uptime / 1000));
|
|
}
|
|
|
|
|
|
#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
|
|
static const uid_t uid_not_found = static_cast<uid_t>(-1);
|
|
static const gid_t gid_not_found = static_cast<gid_t>(-1);
|
|
|
|
|
|
static uid_t uid_by_name(const char* name) {
|
|
struct passwd pwd;
|
|
struct passwd* pp;
|
|
char buf[8192];
|
|
|
|
errno = 0;
|
|
pp = nullptr;
|
|
|
|
if (getpwnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
|
return pp->pw_uid;
|
|
|
|
return uid_not_found;
|
|
}
|
|
|
|
|
|
static char* name_by_uid(uid_t uid) {
|
|
struct passwd pwd;
|
|
struct passwd* pp;
|
|
char buf[8192];
|
|
int rc;
|
|
|
|
errno = 0;
|
|
pp = nullptr;
|
|
|
|
if ((rc = getpwuid_r(uid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
pp != nullptr) {
|
|
return strdup(pp->pw_name);
|
|
}
|
|
|
|
if (rc == 0)
|
|
errno = ENOENT;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
static gid_t gid_by_name(const char* name) {
|
|
struct group pwd;
|
|
struct group* pp;
|
|
char buf[8192];
|
|
|
|
errno = 0;
|
|
pp = nullptr;
|
|
|
|
if (getgrnam_r(name, &pwd, buf, sizeof(buf), &pp) == 0 && pp != nullptr)
|
|
return pp->gr_gid;
|
|
|
|
return gid_not_found;
|
|
}
|
|
|
|
|
|
#if 0 // For future use.
|
|
static const char* name_by_gid(gid_t gid) {
|
|
struct group pwd;
|
|
struct group* pp;
|
|
char buf[8192];
|
|
int rc;
|
|
|
|
errno = 0;
|
|
pp = nullptr;
|
|
|
|
if ((rc = getgrgid_r(gid, &pwd, buf, sizeof(buf), &pp)) == 0 &&
|
|
pp != nullptr) {
|
|
return strdup(pp->gr_name);
|
|
}
|
|
|
|
if (rc == 0)
|
|
errno = ENOENT;
|
|
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
|
|
static uid_t uid_by_name(Isolate* isolate, Local<Value> value) {
|
|
if (value->IsUint32()) {
|
|
return static_cast<uid_t>(value.As<Uint32>()->Value());
|
|
} else {
|
|
Utf8Value name(isolate, value);
|
|
return uid_by_name(*name);
|
|
}
|
|
}
|
|
|
|
|
|
static gid_t gid_by_name(Isolate* isolate, Local<Value> value) {
|
|
if (value->IsUint32()) {
|
|
return static_cast<gid_t>(value.As<Uint32>()->Value());
|
|
} else {
|
|
Utf8Value name(isolate, value);
|
|
return gid_by_name(*name);
|
|
}
|
|
}
|
|
|
|
void GetUid(const FunctionCallbackInfo<Value>& args) {
|
|
// uid_t is an uint32_t on all supported platforms.
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getuid()));
|
|
}
|
|
|
|
|
|
void GetGid(const FunctionCallbackInfo<Value>& args) {
|
|
// gid_t is an uint32_t on all supported platforms.
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getgid()));
|
|
}
|
|
|
|
|
|
void GetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
// uid_t is an uint32_t on all supported platforms.
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(geteuid()));
|
|
}
|
|
|
|
|
|
void GetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
// gid_t is an uint32_t on all supported platforms.
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(getegid()));
|
|
}
|
|
|
|
|
|
void SetGid(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
CHECK(env->is_main_thread());
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
|
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
|
|
if (gid == gid_not_found) {
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
args.GetReturnValue().Set(1);
|
|
} else if (setgid(gid)) {
|
|
env->ThrowErrnoException(errno, "setgid");
|
|
} else {
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
}
|
|
|
|
|
|
void SetEGid(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
CHECK(env->is_main_thread());
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
|
|
gid_t gid = gid_by_name(env->isolate(), args[0]);
|
|
|
|
if (gid == gid_not_found) {
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
args.GetReturnValue().Set(1);
|
|
} else if (setegid(gid)) {
|
|
env->ThrowErrnoException(errno, "setegid");
|
|
} else {
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
}
|
|
|
|
|
|
void SetUid(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
CHECK(env->is_main_thread());
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
|
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
|
|
if (uid == uid_not_found) {
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
args.GetReturnValue().Set(1);
|
|
} else if (setuid(uid)) {
|
|
env->ThrowErrnoException(errno, "setuid");
|
|
} else {
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
}
|
|
|
|
|
|
void SetEUid(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
CHECK(env->is_main_thread());
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
|
|
uid_t uid = uid_by_name(env->isolate(), args[0]);
|
|
|
|
if (uid == uid_not_found) {
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
args.GetReturnValue().Set(1);
|
|
} else if (seteuid(uid)) {
|
|
env->ThrowErrnoException(errno, "seteuid");
|
|
} else {
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
}
|
|
|
|
|
|
void GetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
int ngroups = getgroups(0, nullptr);
|
|
|
|
if (ngroups == -1)
|
|
return env->ThrowErrnoException(errno, "getgroups");
|
|
|
|
gid_t* groups = new gid_t[ngroups];
|
|
|
|
ngroups = getgroups(ngroups, groups);
|
|
|
|
if (ngroups == -1) {
|
|
delete[] groups;
|
|
return env->ThrowErrnoException(errno, "getgroups");
|
|
}
|
|
|
|
Local<Array> groups_list = Array::New(env->isolate(), ngroups);
|
|
bool seen_egid = false;
|
|
gid_t egid = getegid();
|
|
|
|
for (int i = 0; i < ngroups; i++) {
|
|
groups_list->Set(i, Integer::New(env->isolate(), groups[i]));
|
|
if (groups[i] == egid)
|
|
seen_egid = true;
|
|
}
|
|
|
|
delete[] groups;
|
|
|
|
if (seen_egid == false)
|
|
groups_list->Set(ngroups, Integer::New(env->isolate(), egid));
|
|
|
|
args.GetReturnValue().Set(groups_list);
|
|
}
|
|
|
|
|
|
void SetGroups(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
CHECK_EQ(args.Length(), 1);
|
|
CHECK(args[0]->IsArray());
|
|
|
|
Local<Array> groups_list = args[0].As<Array>();
|
|
size_t size = groups_list->Length();
|
|
gid_t* groups = new gid_t[size];
|
|
|
|
for (size_t i = 0; i < size; i++) {
|
|
gid_t gid = gid_by_name(env->isolate(), groups_list->Get(i));
|
|
|
|
if (gid == gid_not_found) {
|
|
delete[] groups;
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
args.GetReturnValue().Set(static_cast<uint32_t>(i + 1));
|
|
return;
|
|
}
|
|
|
|
groups[i] = gid;
|
|
}
|
|
|
|
int rc = setgroups(size, groups);
|
|
delete[] groups;
|
|
|
|
if (rc == -1)
|
|
return env->ThrowErrnoException(errno, "setgroups");
|
|
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
|
|
|
|
void InitGroups(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
CHECK_EQ(args.Length(), 2);
|
|
CHECK(args[0]->IsUint32() || args[0]->IsString());
|
|
CHECK(args[1]->IsUint32() || args[1]->IsString());
|
|
|
|
Utf8Value arg0(env->isolate(), args[0]);
|
|
gid_t extra_group;
|
|
bool must_free;
|
|
char* user;
|
|
|
|
if (args[0]->IsUint32()) {
|
|
user = name_by_uid(args[0].As<Uint32>()->Value());
|
|
must_free = true;
|
|
} else {
|
|
user = *arg0;
|
|
must_free = false;
|
|
}
|
|
|
|
if (user == nullptr) {
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
return args.GetReturnValue().Set(1);
|
|
}
|
|
|
|
extra_group = gid_by_name(env->isolate(), args[1]);
|
|
|
|
if (extra_group == gid_not_found) {
|
|
if (must_free)
|
|
free(user);
|
|
// Tells JS to throw ERR_INVALID_CREDENTIAL
|
|
return args.GetReturnValue().Set(2);
|
|
}
|
|
|
|
int rc = initgroups(user, extra_group);
|
|
|
|
if (must_free)
|
|
free(user);
|
|
|
|
if (rc)
|
|
return env->ThrowErrnoException(errno, "initgroups");
|
|
|
|
args.GetReturnValue().Set(0);
|
|
}
|
|
|
|
#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__)
|
|
|
|
void ProcessTitleGetter(Local<Name> property,
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
char buffer[512];
|
|
uv_get_process_title(buffer, sizeof(buffer));
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buffer,
|
|
v8::NewStringType::kNormal).ToLocalChecked());
|
|
}
|
|
|
|
|
|
void ProcessTitleSetter(Local<Name> property,
|
|
Local<Value> value,
|
|
const PropertyCallbackInfo<void>& info) {
|
|
node::Utf8Value title(info.GetIsolate(), value);
|
|
TRACE_EVENT_METADATA1("__metadata", "process_name", "name",
|
|
TRACE_STR_COPY(*title));
|
|
uv_set_process_title(*title);
|
|
}
|
|
|
|
void EnvGetter(Local<Name> property,
|
|
const PropertyCallbackInfo<Value>& 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,
|
|
v8::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<WCHAR*>(*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<const uint16_t*>(buffer);
|
|
Local<String> rc = String::NewFromTwoByte(isolate, two_byte_buffer);
|
|
return info.GetReturnValue().Set(rc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
void EnvSetter(Local<Name> property,
|
|
Local<Value> value,
|
|
const PropertyCallbackInfo<Value>& 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<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);
|
|
}
|
|
|
|
|
|
void EnvQuery(Local<Name> property, const PropertyCallbackInfo<Integer>& 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<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) |
|
|
static_cast<int32_t>(v8::DontDelete) |
|
|
static_cast<int32_t>(v8::DontEnum);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
if (rc != -1)
|
|
info.GetReturnValue().Set(rc);
|
|
}
|
|
|
|
|
|
void EnvDeleter(Local<Name> property,
|
|
const PropertyCallbackInfo<Boolean>& 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<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);
|
|
}
|
|
|
|
|
|
void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
|
|
Environment* env = Environment::GetCurrent(info);
|
|
Isolate* isolate = env->isolate();
|
|
Local<Context> ctx = env->context();
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
size_t idx = 0;
|
|
|
|
Mutex::ScopedLock lock(environ_mutex);
|
|
#ifdef __POSIX__
|
|
int size = 0;
|
|
while (environ[size])
|
|
size++;
|
|
|
|
Local<Array> envarr = Array::New(isolate);
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
const char* var = environ[i];
|
|
const char* s = strchr(var, '=');
|
|
const int length = s ? s - var : strlen(var);
|
|
argv[idx] = String::NewFromUtf8(isolate,
|
|
var,
|
|
v8::NewStringType::kNormal,
|
|
length).ToLocalChecked();
|
|
if (++idx >= arraysize(argv)) {
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
idx = 0;
|
|
}
|
|
}
|
|
if (idx > 0) {
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
}
|
|
#else // _WIN32
|
|
WCHAR* environment = GetEnvironmentStringsW();
|
|
if (environment == nullptr)
|
|
return; // This should not happen.
|
|
Local<Array> envarr = Array::New(isolate);
|
|
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<const uint16_t*>(p);
|
|
const size_t two_byte_buffer_len = s - p;
|
|
argv[idx] = String::NewFromTwoByte(isolate,
|
|
two_byte_buffer,
|
|
String::kNormalString,
|
|
two_byte_buffer_len);
|
|
if (++idx >= arraysize(argv)) {
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
idx = 0;
|
|
}
|
|
p = s + wcslen(s) + 1;
|
|
}
|
|
if (idx > 0) {
|
|
fn->Call(ctx, envarr, idx, argv).ToLocalChecked();
|
|
}
|
|
FreeEnvironmentStringsW(environment);
|
|
#endif
|
|
|
|
info.GetReturnValue().Set(envarr);
|
|
}
|
|
|
|
void GetParentProcessId(Local<Name> property,
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
info.GetReturnValue().Set(Integer::New(info.GetIsolate(), uv_os_getppid()));
|
|
}
|
|
|
|
void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
Local<Array> ary = Array::New(args.GetIsolate());
|
|
Local<Context> ctx = env->context();
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
size_t idx = 0;
|
|
|
|
for (auto w : *env->req_wrap_queue()) {
|
|
if (w->persistent().IsEmpty())
|
|
continue;
|
|
argv[idx] = w->GetOwner();
|
|
if (++idx >= arraysize(argv)) {
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
idx = 0;
|
|
}
|
|
}
|
|
|
|
if (idx > 0) {
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
}
|
|
|
|
args.GetReturnValue().Set(ary);
|
|
}
|
|
|
|
|
|
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
|
|
// implemented here for consistency with GetActiveRequests().
|
|
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
|
|
Environment* env = Environment::GetCurrent(args);
|
|
|
|
Local<Array> ary = Array::New(env->isolate());
|
|
Local<Context> ctx = env->context();
|
|
Local<Function> fn = env->push_values_to_array_function();
|
|
Local<Value> argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
|
|
size_t idx = 0;
|
|
|
|
for (auto w : *env->handle_wrap_queue()) {
|
|
if (!HandleWrap::HasRef(w))
|
|
continue;
|
|
argv[idx] = w->GetOwner();
|
|
if (++idx >= arraysize(argv)) {
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
idx = 0;
|
|
}
|
|
}
|
|
if (idx > 0) {
|
|
fn->Call(ctx, ary, idx, argv).ToLocalChecked();
|
|
}
|
|
|
|
args.GetReturnValue().Set(ary);
|
|
}
|
|
|
|
void DebugPortGetter(Local<Name> property,
|
|
const PropertyCallbackInfo<Value>& info) {
|
|
Environment* env = Environment::GetCurrent(info);
|
|
Mutex::ScopedLock lock(process_mutex);
|
|
int port = env->options()->debug_options->port();
|
|
#if HAVE_INSPECTOR
|
|
if (port == 0) {
|
|
if (auto io = env->inspector_agent()->io())
|
|
port = io->port();
|
|
}
|
|
#endif // HAVE_INSPECTOR
|
|
info.GetReturnValue().Set(port);
|
|
}
|
|
|
|
|
|
void DebugPortSetter(Local<Name> property,
|
|
Local<Value> value,
|
|
const PropertyCallbackInfo<void>& info) {
|
|
Environment* env = Environment::GetCurrent(info);
|
|
Mutex::ScopedLock lock(process_mutex);
|
|
env->options()->debug_options->host_port.port =
|
|
value->Int32Value(env->context()).FromMaybe(0);
|
|
}
|
|
|
|
|
|
} // namespace node
|