From 0610232fe2c7f913c13a4d033e9e68048b53559e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Jul 2018 09:35:21 -0700 Subject: [PATCH 1/3] src: refactor bootstrap to use bootstrap object Begin backporting bootstrapper.cc to 8.x from https://github.com/nodejs/node/pull/20917 --- lib/internal/bootstrap_node.js | 27 ++++-- lib/internal/process.js | 18 ++-- lib/internal/process/next_tick.js | 7 +- lib/internal/process/promises.js | 4 +- node.gyp | 1 + src/bootstrapper.cc | 97 ++++++++++++++++++++ src/node.cc | 146 +++++++----------------------- src/node_internals.h | 32 +++++++ 8 files changed, 196 insertions(+), 136 deletions(-) create mode 100644 src/bootstrapper.cc diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 6b7ac044f638af..1af12795513083 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -7,7 +7,19 @@ 'use strict'; -(function(process) { +(function(process, + // bootstrapper properties... destructured to + // avoid retaining a reference to the bootstrap + // object. + { + _setupProcessObject, + _setupNextTick, + _setupPromises, + _cpuUsage, + _hrtime, + _memoryUsage, + _rawDebug + }) { function startup() { const EventEmitter = NativeModule.require('events'); @@ -31,7 +43,8 @@ _process.setupConfig(NativeModule._source); _process.setupSignalHandlers(); NativeModule.require('internal/process/warning').setup(); - NativeModule.require('internal/process/next_tick').setup(); + NativeModule.require('internal/process/next_tick').setup(_setupNextTick, + _setupPromises); NativeModule.require('internal/process/stdio').setup(); const perf = process.binding('performance'); @@ -47,10 +60,10 @@ NODE_PERFORMANCE_MILESTONE_PRELOAD_MODULE_LOAD_END } = perf.constants; - _process.setup_hrtime(); + _process.setup_hrtime(_hrtime); _process.setup_performance(); - _process.setup_cpuUsage(); - _process.setupMemoryUsage(); + _process.setup_cpuUsage(_cpuUsage); + _process.setupMemoryUsage(_memoryUsage); _process.setupKillAndExit(); if (global.__coverage__) NativeModule.require('internal/process/write-coverage').setup(); @@ -59,7 +72,7 @@ NativeModule.require('internal/inspector_async_hook').setup(); _process.setupChannel(); - _process.setupRawDebug(); + _process.setupRawDebug(_rawDebug); const browserGlobals = !process._noBrowserGlobals; if (browserGlobals) { @@ -244,7 +257,7 @@ } function setupProcessObject() { - process._setupProcessObject(pushValueToArray); + _setupProcessObject(pushValueToArray); function pushValueToArray() { for (var i = 0; i < arguments.length; i++) diff --git a/lib/internal/process.js b/lib/internal/process.js index 1613ae6bfddd66..4573b9429ed0cd 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -17,10 +17,7 @@ function setup_performance() { } // Set up the process.cpuUsage() function. -function setup_cpuUsage() { - // Get the native function, which will be replaced with a JS version. - const _cpuUsage = process.cpuUsage; - +function setup_cpuUsage(_cpuUsage) { // Create the argument array that will be passed to the native function. const cpuValues = new Float64Array(2); @@ -71,8 +68,7 @@ function setup_cpuUsage() { // The 3 entries filled in by the original process.hrtime contains // the upper/lower 32 bits of the second part of the value, // and the remaining nanoseconds of the value. -function setup_hrtime() { - const _hrtime = process.hrtime; +function setup_hrtime(_hrtime) { const hrValues = new Uint32Array(3); process.hrtime = function hrtime(time) { @@ -96,12 +92,11 @@ function setup_hrtime() { }; } -function setupMemoryUsage() { - const memoryUsage_ = process.memoryUsage; +function setupMemoryUsage(_memoryUsage) { const memValues = new Float64Array(4); process.memoryUsage = function memoryUsage() { - memoryUsage_(memValues); + _memoryUsage(memValues); return { rss: memValues[0], heapTotal: memValues[1], @@ -251,10 +246,9 @@ function setupChannel() { } -function setupRawDebug() { - const rawDebug = process._rawDebug; +function setupRawDebug(_rawDebug) { process._rawDebug = function() { - rawDebug(util.format.apply(null, arguments)); + _rawDebug(util.format.apply(null, arguments)); }; } diff --git a/lib/internal/process/next_tick.js b/lib/internal/process/next_tick.js index 54a6bdd0298c85..849565bba71997 100644 --- a/lib/internal/process/next_tick.js +++ b/lib/internal/process/next_tick.js @@ -46,12 +46,13 @@ class NextTickQueue { } } -function setupNextTick() { +function setupNextTick(_setupNextTick, _setupPromises) { const async_wrap = process.binding('async_wrap'); const async_hooks = require('internal/async_hooks'); const promises = require('internal/process/promises'); const errors = require('internal/errors'); - const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks); + const emitPendingUnhandledRejections = promises.setup(scheduleMicrotasks, + _setupPromises); const getDefaultTriggerAsyncId = async_hooks.getDefaultTriggerAsyncId; // Two arrays that share state between C++ and JS. const { async_hook_fields, async_id_fields } = async_wrap; @@ -81,7 +82,7 @@ function setupNextTick() { // This tickInfo thing is used so that the C++ code in src/node.cc // can have easy access to our nextTick state, and avoid unnecessary // calls into JS land. - const tickInfo = process._setupNextTick(_tickCallback, _runMicrotasks); + const tickInfo = _setupNextTick(_tickCallback, _runMicrotasks); _runMicrotasks = _runMicrotasks.runMicrotasks; diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 82f15f9f9df51e..33c9354c908e9c 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -15,10 +15,10 @@ function getAsynchronousRejectionWarningObject(uid) { `asynchronously (rejection id: ${uid})`); } -function setupPromises(scheduleMicrotasks) { +function setupPromises(scheduleMicrotasks, _setupPromises) { let deprecationWarned = false; - process._setupPromises(function(event, promise, reason) { + _setupPromises(function(event, promise, reason) { if (event === promiseRejectEvent.unhandled) unhandledRejection(promise, reason); else if (event === promiseRejectEvent.handled) diff --git a/node.gyp b/node.gyp index c1079283a052f7..60522114920b87 100644 --- a/node.gyp +++ b/node.gyp @@ -265,6 +265,7 @@ 'sources': [ 'src/async_wrap.cc', + 'src/bootstrapper.cc', 'src/cares_wrap.cc', 'src/connection_wrap.cc', 'src/connect_wrap.cc', diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc new file mode 100644 index 00000000000000..f1084ab03aebce --- /dev/null +++ b/src/bootstrapper.cc @@ -0,0 +1,97 @@ +#include "node.h" +#include "env-inl.h" +#include "node_internals.h" +#include "v8.h" + +namespace node { + +using v8::Array; +using v8::ArrayBuffer; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Object; +using v8::Promise; +using v8::PromiseRejectMessage; +using v8::Uint32Array; +using v8::Value; + +void SetupProcessObject(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsFunction()); + env->set_push_values_to_array_function(args[0].As()); +} + +void RunMicrotasks(const FunctionCallbackInfo& args) { + args.GetIsolate()->RunMicrotasks(); +} + +void SetupNextTick(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsFunction()); + CHECK(args[1]->IsObject()); + + env->set_tick_callback_function(args[0].As()); + + env->SetMethod(args[1].As(), "runMicrotasks", RunMicrotasks); + + // Values use to cross communicate with processNextTick. + uint32_t* const fields = env->tick_info()->fields(); + uint32_t const fields_count = env->tick_info()->fields_count(); + + Local array_buffer = + ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); + + args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); +} + +void PromiseRejectCallback(PromiseRejectMessage message) { + Local promise = message.GetPromise(); + Isolate* isolate = promise->GetIsolate(); + Local value = message.GetValue(); + Local event = Integer::New(isolate, message.GetEvent()); + + Environment* env = Environment::GetCurrent(isolate); + Local callback = env->promise_reject_function(); + + if (value.IsEmpty()) + value = Undefined(isolate); + + Local args[] = { event, promise, value }; + Local process = env->process_object(); + + callback->Call(process, arraysize(args), args); +} + +void SetupPromises(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + Isolate* isolate = env->isolate(); + + CHECK(args[0]->IsFunction()); + + isolate->SetPromiseRejectCallback(PromiseRejectCallback); + env->set_promise_reject_function(args[0].As()); +} + +#define BOOTSTRAP_METHOD(name, fn) env->SetMethod(bootstrapper, #name, fn) + +// The Bootstrapper object is an ephemeral object that is used only during +// the bootstrap process of the Node.js environment. A reference to the +// bootstrap object must not be kept around after the bootstrap process +// completes so that it can be gc'd as soon as possible. +void SetupBootstrapObject(Environment* env, + Local bootstrapper) { + BOOTSTRAP_METHOD(_setupProcessObject, SetupProcessObject); + BOOTSTRAP_METHOD(_setupNextTick, SetupNextTick); + BOOTSTRAP_METHOD(_setupPromises, SetupPromises); + BOOTSTRAP_METHOD(_cpuUsage, CPUUsage); + BOOTSTRAP_METHOD(_hrtime, Hrtime); + BOOTSTRAP_METHOD(_memoryUsage, MemoryUsage); + BOOTSTRAP_METHOD(_rawDebug, RawDebug); +} +#undef BOOTSTRAP_METHOD + +} // namespace node diff --git a/src/node.cc b/src/node.cc index 621e59fcb64570..6507ed22b5e35a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -157,7 +157,6 @@ using v8::Object; using v8::ObjectTemplate; using v8::Promise; using v8::PromiseHookType; -using v8::PromiseRejectMessage; using v8::PropertyCallbackInfo; using v8::ScriptOrigin; using v8::SealHandleScope; @@ -264,7 +263,7 @@ bool v8_initialized = false; bool linux_at_secure = false; // process-relative uptime base, initialized at start-up -static double prog_start_time; +double prog_start_time; static Mutex node_isolate_mutex; static v8::Isolate* node_isolate; @@ -367,7 +366,7 @@ static struct { static const unsigned kMaxSignal = 32; #endif -static void PrintErrorString(const char* format, ...) { +void PrintErrorString(const char* format, ...) { va_list ap; va_start(ap, format); #ifdef _WIN32 @@ -1277,6 +1276,8 @@ void SetupDomainUse(const FunctionCallbackInfo& args) { env->set_domains_stack_array(args[1].As()); // Do a little housekeeping. + // TODO(jasnell): This should be moved to bootstrapper.cc and the bootstrap + // object at some point, but doing so requires a bit more refactoring. env->process_object()->Delete( env->context(), FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust(); @@ -1292,81 +1293,6 @@ void SetupDomainUse(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); } - -void RunMicrotasks(const FunctionCallbackInfo& args) { - args.GetIsolate()->RunMicrotasks(); -} - - -void SetupProcessObject(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsFunction()); - - env->set_push_values_to_array_function(args[0].As()); - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(env->isolate(), "_setupProcessObject")).FromJust(); -} - - -void SetupNextTick(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsFunction()); - CHECK(args[1]->IsObject()); - - env->set_tick_callback_function(args[0].As()); - - env->SetMethod(args[1].As(), "runMicrotasks", RunMicrotasks); - - // Do a little housekeeping. - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")).FromJust(); - - // Values use to cross communicate with processNextTick. - uint32_t* const fields = env->tick_info()->fields(); - uint32_t const fields_count = env->tick_info()->fields_count(); - - Local array_buffer = - ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); - - args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); -} - -void PromiseRejectCallback(PromiseRejectMessage message) { - Local promise = message.GetPromise(); - Isolate* isolate = promise->GetIsolate(); - Local value = message.GetValue(); - Local event = Integer::New(isolate, message.GetEvent()); - - Environment* env = Environment::GetCurrent(isolate); - Local callback = env->promise_reject_function(); - - if (value.IsEmpty()) - value = Undefined(isolate); - - Local args[] = { event, promise, value }; - Local process = env->process_object(); - - callback->Call(process, arraysize(args), args); -} - -void SetupPromises(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Isolate* isolate = env->isolate(); - - CHECK(args[0]->IsFunction()); - - isolate->SetPromiseRejectCallback(PromiseRejectCallback); - env->set_promise_reject_function(args[0].As()); - - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(isolate, "_setupPromises")).FromJust(); -} - } // anonymous namespace @@ -2058,12 +1984,12 @@ NO_RETURN void Assert(const char* const (*args)[4]) { } -static void Abort(const FunctionCallbackInfo& args) { +void Abort(const FunctionCallbackInfo& args) { Abort(); } -static void Chdir(const FunctionCallbackInfo& args) { +void Chdir(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (args.Length() != 1 || !args[0]->IsString()) { @@ -2078,7 +2004,7 @@ static void Chdir(const FunctionCallbackInfo& args) { } -static void Cwd(const FunctionCallbackInfo& args) { +void Cwd(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); #ifdef _WIN32 /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ @@ -2101,7 +2027,7 @@ static void Cwd(const FunctionCallbackInfo& args) { } -static void Umask(const FunctionCallbackInfo& args) { +void Umask(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); uint32_t old; @@ -2238,31 +2164,31 @@ static gid_t gid_by_name(Isolate* isolate, Local value) { } } -static void GetUid(const FunctionCallbackInfo& args) { +void GetUid(const FunctionCallbackInfo& args) { // uid_t is an uint32_t on all supported platforms. args.GetReturnValue().Set(static_cast(getuid())); } -static void GetGid(const FunctionCallbackInfo& args) { +void GetGid(const FunctionCallbackInfo& args) { // gid_t is an uint32_t on all supported platforms. args.GetReturnValue().Set(static_cast(getgid())); } -static void GetEUid(const FunctionCallbackInfo& args) { +void GetEUid(const FunctionCallbackInfo& args) { // uid_t is an uint32_t on all supported platforms. args.GetReturnValue().Set(static_cast(geteuid())); } -static void GetEGid(const FunctionCallbackInfo& args) { +void GetEGid(const FunctionCallbackInfo& args) { // gid_t is an uint32_t on all supported platforms. args.GetReturnValue().Set(static_cast(getegid())); } -static void SetGid(const FunctionCallbackInfo& args) { +void SetGid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint32() && !args[0]->IsString()) { @@ -2281,7 +2207,7 @@ static void SetGid(const FunctionCallbackInfo& args) { } -static void SetEGid(const FunctionCallbackInfo& args) { +void SetEGid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint32() && !args[0]->IsString()) { @@ -2300,7 +2226,7 @@ static void SetEGid(const FunctionCallbackInfo& args) { } -static void SetUid(const FunctionCallbackInfo& args) { +void SetUid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint32() && !args[0]->IsString()) { @@ -2319,7 +2245,7 @@ static void SetUid(const FunctionCallbackInfo& args) { } -static void SetEUid(const FunctionCallbackInfo& args) { +void SetEUid(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint32() && !args[0]->IsString()) { @@ -2338,7 +2264,7 @@ static void SetEUid(const FunctionCallbackInfo& args) { } -static void GetGroups(const FunctionCallbackInfo& args) { +void GetGroups(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); int ngroups = getgroups(0, nullptr); @@ -2376,7 +2302,7 @@ static void GetGroups(const FunctionCallbackInfo& args) { } -static void SetGroups(const FunctionCallbackInfo& args) { +void SetGroups(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsArray()) { @@ -2407,7 +2333,7 @@ static void SetGroups(const FunctionCallbackInfo& args) { } -static void InitGroups(const FunctionCallbackInfo& args) { +void InitGroups(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!args[0]->IsUint32() && !args[0]->IsString()) { @@ -2487,7 +2413,7 @@ static void Exit(const FunctionCallbackInfo& args) { } -static void Uptime(const FunctionCallbackInfo& args) { +void Uptime(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); double uptime; @@ -2498,7 +2424,7 @@ static void Uptime(const FunctionCallbackInfo& args) { } -static void MemoryUsage(const FunctionCallbackInfo& args) { +void MemoryUsage(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); size_t rss; @@ -2526,7 +2452,7 @@ static void MemoryUsage(const FunctionCallbackInfo& args) { } -static void Kill(const FunctionCallbackInfo& args) { +void Kill(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (args.Length() != 2) { @@ -2550,7 +2476,7 @@ static void Kill(const FunctionCallbackInfo& args) { // 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. -static void Hrtime(const FunctionCallbackInfo& args) { +void Hrtime(const FunctionCallbackInfo& args) { uint64_t t = uv_hrtime(); Local ab = args[0].As()->Buffer(); @@ -2569,7 +2495,7 @@ static void Hrtime(const FunctionCallbackInfo& args) { // 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. -static void CPUUsage(const FunctionCallbackInfo& args) { +void CPUUsage(const FunctionCallbackInfo& args) { uv_rusage_t rusage; // Call libuv to get the values we'll return. @@ -3687,22 +3613,14 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "_debugPause", DebugPause); env->SetMethod(process, "_debugEnd", DebugEnd); - env->SetMethod(process, "hrtime", Hrtime); - - env->SetMethod(process, "cpuUsage", CPUUsage); - env->SetMethod(process, "dlopen", DLOpen); env->SetMethod(process, "uptime", Uptime); - env->SetMethod(process, "memoryUsage", MemoryUsage); env->SetMethod(process, "binding", Binding); env->SetMethod(process, "_linkedBinding", LinkedBinding); env->SetMethod(process, "_internalBinding", InternalBinding); - env->SetMethod(process, "_setupProcessObject", SetupProcessObject); - env->SetMethod(process, "_setupNextTick", SetupNextTick); - env->SetMethod(process, "_setupPromises", SetupPromises); env->SetMethod(process, "_setupDomainUse", SetupDomainUse); // pre-set _events object for faster emit checks @@ -3736,7 +3654,7 @@ void SignalExit(int signo) { // 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. -static void RawDebug(const FunctionCallbackInfo& args) { +void RawDebug(const FunctionCallbackInfo& args) { CHECK(args.Length() == 1 && args[0]->IsString() && "must be called with a single string"); node::Utf8Value message(args.GetIsolate(), args[0]); @@ -3792,8 +3710,6 @@ void LoadEnvironment(Environment* env) { // thrown during process startup. try_catch.SetVerbose(true); - env->SetMethod(env->process_object(), "_rawDebug", RawDebug); - // Expose the global object as a property on itself // (Allows you to set stuff on `global` from anywhere in JavaScript.) global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global); @@ -3805,9 +3721,15 @@ void LoadEnvironment(Environment* env) { // We start the process this way in order to be more modular. Developers // who do not like how bootstrap_node.js sets up the module system but do // like Node's I/O bindings may want to replace 'f' with their own function. - Local arg = env->process_object(); - - auto ret = f->Call(env->context(), Null(env->isolate()), 1, &arg); + Local bootstrapper = Object::New(env->isolate()); + SetupBootstrapObject(env, bootstrapper); + Local args[] = { + env->process_object(), + bootstrapper + }; + auto ret = f->Call(env->context(), + Null(env->isolate()), + arraysize(args), args); // If there was an error during bootstrap then it was either handled by the // FatalException handler or it's unrecoverable (e.g. max call stack // exceeded). Either way, clear the stack so that the AsyncCallbackScope diff --git a/src/node_internals.h b/src/node_internals.h index b2cef8c9abcad5..ff98995a19873c 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -262,6 +262,8 @@ void ProcessEmitWarning(Environment* env, const char* fmt, ...); void FillStatsArray(double* fields, const uv_stat_t* s); +void SetupBootstrapObject(Environment* env, + v8::Local bootstrapper); void SetupProcessObject(Environment* env, int argc, const char* const* argv, @@ -413,6 +415,36 @@ class InternalCallbackScope { #define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) +// Functions defined in node.cc that are exposed via the bootstrapper object + +extern double prog_start_time; +void PrintErrorString(const char* format, ...); + +void Abort(const v8::FunctionCallbackInfo& args); +void Chdir(const v8::FunctionCallbackInfo& args); +void CPUUsage(const v8::FunctionCallbackInfo& args); +void Cwd(const v8::FunctionCallbackInfo& args); +void Hrtime(const v8::FunctionCallbackInfo& args); +void Kill(const v8::FunctionCallbackInfo& args); +void MemoryUsage(const v8::FunctionCallbackInfo& args); +void RawDebug(const v8::FunctionCallbackInfo& args); +void Umask(const v8::FunctionCallbackInfo& args); +void Uptime(const v8::FunctionCallbackInfo& args); + +#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) +void SetGid(const v8::FunctionCallbackInfo& args); +void SetEGid(const v8::FunctionCallbackInfo& args); +void SetUid(const v8::FunctionCallbackInfo& args); +void SetEUid(const v8::FunctionCallbackInfo& args); +void SetGroups(const v8::FunctionCallbackInfo& args); +void InitGroups(const v8::FunctionCallbackInfo& args); +void GetUid(const v8::FunctionCallbackInfo& args); +void GetGid(const v8::FunctionCallbackInfo& args); +void GetEUid(const v8::FunctionCallbackInfo& args); +void GetEGid(const v8::FunctionCallbackInfo& args); +void GetGroups(const v8::FunctionCallbackInfo& args); +#endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) + } // namespace node From f367b4d2392b1b13ee3358bd532d44fba8ded6bc Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Jul 2018 10:43:40 -0700 Subject: [PATCH 2/3] src: add node_process.cc Backport of https://github.com/nodejs/node/pull/21105 --- node.gyp | 1 + src/node.cc | 564 +------------------------------------------ src/node_internals.h | 2 + src/node_process.cc | 560 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 574 insertions(+), 553 deletions(-) create mode 100644 src/node_process.cc diff --git a/node.gyp b/node.gyp index 60522114920b87..c6566aec08dc92 100644 --- a/node.gyp +++ b/node.gyp @@ -290,6 +290,7 @@ 'src/node_platform.cc', 'src/node_perf.cc', 'src/node_postmortem_metadata.cc', + 'src/node_process.cc', 'src/node_serdes.cc', 'src/node_trace_events.cc', 'src/node_url.cc', diff --git a/src/node.cc b/src/node.cc index 6507ed22b5e35a..3fce0c420b5ac7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -138,11 +138,9 @@ using v8::Boolean; using v8::Context; using v8::EscapableHandleScope; using v8::Exception; -using v8::Float64Array; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; -using v8::HeapStatistics; using v8::Integer; using v8::Isolate; using v8::Local; @@ -1983,406 +1981,6 @@ NO_RETURN void Assert(const char* const (*args)[4]) { Abort(); } - -void Abort(const FunctionCallbackInfo& args) { - Abort(); -} - - -void Chdir(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 1 || !args[0]->IsString()) { - return env->ThrowTypeError("Bad argument."); - } - - node::Utf8Value path(args.GetIsolate(), args[0]); - int err = uv_chdir(*path); - if (err) { - return env->ThrowUVException(err, "uv_chdir"); - } -} - - -void Cwd(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); -#ifdef _WIN32 - /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ - char buf[MAX_PATH * 4]; -#else - char buf[PATH_MAX]; -#endif - - size_t cwd_len = sizeof(buf); - int err = uv_cwd(buf, &cwd_len); - if (err) { - return env->ThrowUVException(err, "uv_cwd"); - } - - Local cwd = String::NewFromUtf8(env->isolate(), - buf, - String::kNormalString, - cwd_len); - args.GetReturnValue().Set(cwd); -} - - -void Umask(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - uint32_t old; - - if (args.Length() < 1 || args[0]->IsUndefined()) { - old = umask(0); - umask(static_cast(old)); - } else if (!args[0]->IsInt32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument must be an integer or octal string."); - } else { - int oct; - if (args[0]->IsInt32()) { - oct = args[0]->Uint32Value(); - } else { - oct = 0; - node::Utf8Value str(env->isolate(), args[0]); - - // Parse the octal string. - for (size_t i = 0; i < str.length(); i++) { - char c = (*str)[i]; - if (c > '7' || c < '0') { - return env->ThrowTypeError("invalid octal string"); - } - oct *= 8; - oct += c - '0'; - } - } - old = umask(static_cast(oct)); - } - - args.GetReturnValue().Set(old); -} - - -#if defined(__POSIX__) && !defined(__ANDROID__) - -static const uid_t uid_not_found = static_cast(-1); -static const gid_t gid_not_found = static_cast(-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) { - if (value->IsUint32()) { - return static_cast(value->Uint32Value()); - } else { - node::Utf8Value name(isolate, value); - return uid_by_name(*name); - } -} - - -static gid_t gid_by_name(Isolate* isolate, Local value) { - if (value->IsUint32()) { - return static_cast(value->Uint32Value()); - } else { - node::Utf8Value name(isolate, value); - return gid_by_name(*name); - } -} - -void GetUid(const FunctionCallbackInfo& args) { - // uid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getuid())); -} - - -void GetGid(const FunctionCallbackInfo& args) { - // gid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getgid())); -} - - -void GetEUid(const FunctionCallbackInfo& args) { - // uid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(geteuid())); -} - - -void GetEGid(const FunctionCallbackInfo& args) { - // gid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getegid())); -} - - -void SetGid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setgid argument must be a number or a string"); - } - - gid_t gid = gid_by_name(env->isolate(), args[0]); - - if (gid == gid_not_found) { - return env->ThrowError("setgid group id does not exist"); - } - - if (setgid(gid)) { - return env->ThrowErrnoException(errno, "setgid"); - } -} - - -void SetEGid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setegid argument must be a number or string"); - } - - gid_t gid = gid_by_name(env->isolate(), args[0]); - - if (gid == gid_not_found) { - return env->ThrowError("setegid group id does not exist"); - } - - if (setegid(gid)) { - return env->ThrowErrnoException(errno, "setegid"); - } -} - - -void SetUid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("setuid argument must be a number or a string"); - } - - uid_t uid = uid_by_name(env->isolate(), args[0]); - - if (uid == uid_not_found) { - return env->ThrowError("setuid user id does not exist"); - } - - if (setuid(uid)) { - return env->ThrowErrnoException(errno, "setuid"); - } -} - - -void SetEUid(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("seteuid argument must be a number or string"); - } - - uid_t uid = uid_by_name(env->isolate(), args[0]); - - if (uid == uid_not_found) { - return env->ThrowError("seteuid user id does not exist"); - } - - if (seteuid(uid)) { - return env->ThrowErrnoException(errno, "seteuid"); - } -} - - -void GetGroups(const FunctionCallbackInfo& 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 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& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsArray()) { - return env->ThrowTypeError("argument 1 must be an array"); - } - - Local groups_list = args[0].As(); - 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; - return env->ThrowError("group name not found"); - } - - groups[i] = gid; - } - - int rc = setgroups(size, groups); - delete[] groups; - - if (rc == -1) { - return env->ThrowErrnoException(errno, "setgroups"); - } -} - - -void InitGroups(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (!args[0]->IsUint32() && !args[0]->IsString()) { - return env->ThrowTypeError("argument 1 must be a number or a string"); - } - - if (!args[1]->IsUint32() && !args[1]->IsString()) { - return env->ThrowTypeError("argument 2 must be a number or a string"); - } - - node::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]->Uint32Value()); - must_free = true; - } else { - user = *arg0; - must_free = false; - } - - if (user == nullptr) { - return env->ThrowError("initgroups user not found"); - } - - extra_group = gid_by_name(env->isolate(), args[1]); - - if (extra_group == gid_not_found) { - if (must_free) - free(user); - return env->ThrowError("initgroups extra group not found"); - } - - int rc = initgroups(user, extra_group); - - if (must_free) { - free(user); - } - - if (rc) { - return env->ThrowErrnoException(errno, "initgroups"); - } -} - -#endif // __POSIX__ && !defined(__ANDROID__) - - static void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR if (env->inspector_agent()->IsConnected()) { @@ -2412,113 +2010,6 @@ static void Exit(const FunctionCallbackInfo& args) { exit(args[0]->Int32Value()); } - -void Uptime(const FunctionCallbackInfo& 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)); -} - - -void MemoryUsage(const FunctionCallbackInfo& 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 array = args[0].As(); - CHECK_EQ(array->Length(), 4); - Local ab = array->Buffer(); - double* fields = static_cast(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); -} - - -void Kill(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (args.Length() != 2) { - return env->ThrowError("Bad argument."); - } - - int pid = args[0]->Int32Value(); - int sig = args[1]->Int32Value(); - int err = uv_kill(pid, sig); - args.GetReturnValue().Set(err); -} - -// used in Hrtime() below -#define NANOS_PER_SEC 1000000000 - -// Hrtime exposes libuv's uv_hrtime() high-resolution timer. -// 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& args) { - uint64_t t = uv_hrtime(); - - Local ab = args[0].As()->Buffer(); - uint32_t* fields = static_cast(ab->GetContents().Data()); - - fields[0] = (t / NANOS_PER_SEC) >> 32; - fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; - fields[2] = t % NANOS_PER_SEC; -} - -// Microseconds in a second, as a float, used in CPUUsage() below -#define MICROS_PER_SEC 1e6 - -// 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& 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 errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); - args.GetReturnValue().Set(errmsg); - return; - } - - // Get the double array pointer from the Float64Array argument. - CHECK(args[0]->IsFloat64Array()); - Local array = args[0].As(); - CHECK_EQ(array->Length(), 2); - Local ab = array->Buffer(); - double* fields = static_cast(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; -} - extern "C" void node_module_register(void* m) { struct node_module* mp = reinterpret_cast(m); @@ -3214,19 +2705,6 @@ void ActivateImmediateCheck(const FunctionCallbackInfo& args) { env->ActivateImmediateCheck(); } - -void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StartProfilerIdleNotifier(); -} - - -void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - env->StopProfilerIdleNotifier(); -} - - #define READONLY_PROPERTY(obj, str, var) \ do { \ obj->DefineOwnProperty(env->context(), \ @@ -3582,14 +3060,23 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "_stopProfilerIdleNotifier", StopProfilerIdleNotifier); + env->SetMethod(process, "_debugProcess", DebugProcess); + env->SetMethod(process, "_debugPause", DebugPause); + env->SetMethod(process, "_debugEnd", DebugEnd); env->SetMethod(process, "_getActiveRequests", GetActiveRequests); env->SetMethod(process, "_getActiveHandles", GetActiveHandles); - env->SetMethod(process, "reallyExit", Exit); + env->SetMethod(process, "_internalBinding", InternalBinding); + env->SetMethod(process, "_kill", Kill); + env->SetMethod(process, "_linkedBinding", LinkedBinding); + env->SetMethod(process, "_setupDomainUse", SetupDomainUse); + env->SetMethod(process, "binding", Binding); + env->SetMethod(process, "dlopen", DLOpen); env->SetMethod(process, "abort", Abort); env->SetMethod(process, "chdir", Chdir); env->SetMethod(process, "cwd", Cwd); - + env->SetMethod(process, "reallyExit", Exit); env->SetMethod(process, "umask", Umask); + env->SetMethod(process, "uptime", Uptime); #if defined(__POSIX__) && !defined(__ANDROID__) env->SetMethod(process, "getuid", GetUid); @@ -3607,22 +3094,6 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "initgroups", InitGroups); #endif // __POSIX__ && !defined(__ANDROID__) - env->SetMethod(process, "_kill", Kill); - - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugPause", DebugPause); - env->SetMethod(process, "_debugEnd", DebugEnd); - - env->SetMethod(process, "dlopen", DLOpen); - - env->SetMethod(process, "uptime", Uptime); - - env->SetMethod(process, "binding", Binding); - env->SetMethod(process, "_linkedBinding", LinkedBinding); - env->SetMethod(process, "_internalBinding", InternalBinding); - - env->SetMethod(process, "_setupDomainUse", SetupDomainUse); - // pre-set _events object for faster emit checks Local events_obj = Object::New(env->isolate()); CHECK(events_obj->SetPrototype(env->context(), @@ -3649,19 +3120,6 @@ void SignalExit(int signo) { raise(signo); } - -// 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& args) { - CHECK(args.Length() == 1 && args[0]->IsString() && - "must be called with a single string"); - node::Utf8Value message(args.GetIsolate(), args[0]); - PrintErrorString("%s\n", *message); - fflush(stderr); -} - void LoadEnvironment(Environment* env) { HandleScope handle_scope(env->isolate()); diff --git a/src/node_internals.h b/src/node_internals.h index ff98995a19873c..a891b1b846678c 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -428,6 +428,8 @@ void Hrtime(const v8::FunctionCallbackInfo& args); void Kill(const v8::FunctionCallbackInfo& args); void MemoryUsage(const v8::FunctionCallbackInfo& args); void RawDebug(const v8::FunctionCallbackInfo& args); +void StartProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); +void StopProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); void Umask(const v8::FunctionCallbackInfo& args); void Uptime(const v8::FunctionCallbackInfo& args); diff --git a/src/node_process.cc b/src/node_process.cc new file mode 100644 index 00000000000000..f953f5fec3740a --- /dev/null +++ b/src/node_process.cc @@ -0,0 +1,560 @@ +#include "node.h" +#include "node_internals.h" +#include "env.h" +#include "env-inl.h" +#include "util.h" +#include "util-inl.h" +#include "uv.h" +#include "v8.h" + +#include // PATH_MAX +#include + +#if defined(_MSC_VER) +#include +#include +#define umask _umask +typedef int mode_t; +#else +#include +#include // getrlimit, setrlimit +#include // tcgetattr, tcsetattr +#include // setuid, getuid +#endif + +#if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) +#include // getpwnam() +#include // getgrnam() +#endif + +namespace node { + +using v8::Array; +using v8::ArrayBuffer; +using v8::Float64Array; +using v8::FunctionCallbackInfo; +using v8::HeapStatistics; +using v8::Integer; +using v8::Isolate; +using v8::Local; +using v8::Number; +using v8::String; +using v8::Uint32; +using v8::Uint32Array; +using v8::Value; + +// 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 + +void Abort(const FunctionCallbackInfo& args) { + Abort(); +} + +void Chdir(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 1 || !args[0]->IsString()) + return env->ThrowTypeError("Bad argument."); + + Utf8Value path(args.GetIsolate(), args[0]); + int err = uv_chdir(*path); + if (err) + return env->ThrowUVException(err, "uv_chdir"); +} + +// 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& 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 errmsg = OneByteString(args.GetIsolate(), uv_strerror(err)); + args.GetReturnValue().Set(errmsg); + return; + } + + // Get the double array pointer from the Float64Array argument. + CHECK(args[0]->IsFloat64Array()); + Local array = args[0].As(); + CHECK_EQ(array->Length(), 2); + Local ab = array->Buffer(); + double* fields = static_cast(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& args) { + Environment* env = Environment::GetCurrent(args); +#ifdef _WIN32 + /* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ + char buf[MAX_PATH * 4]; +#else + char buf[PATH_MAX]; +#endif + + size_t cwd_len = sizeof(buf); + int err = uv_cwd(buf, &cwd_len); + if (err) + return env->ThrowUVException(err, "uv_cwd"); + + Local cwd = String::NewFromUtf8(env->isolate(), + buf, + String::kNormalString, + cwd_len); + args.GetReturnValue().Set(cwd); +} + +// Hrtime exposes libuv's uv_hrtime() high-resolution timer. +// 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& args) { + uint64_t t = uv_hrtime(); + + Local ab = args[0].As()->Buffer(); + uint32_t* fields = static_cast(ab->GetContents().Data()); + + fields[0] = (t / NANOS_PER_SEC) >> 32; + fields[1] = (t / NANOS_PER_SEC) & 0xffffffff; + fields[2] = t % NANOS_PER_SEC; +} + +void Kill(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() != 2) + return env->ThrowError("Bad argument."); + + int pid = args[0]->Int32Value(); + int sig = args[1]->Int32Value(); + int err = uv_kill(pid, sig); + args.GetReturnValue().Set(err); +} + +void MemoryUsage(const FunctionCallbackInfo& 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 array = args[0].As(); + CHECK_EQ(array->Length(), 4); + Local ab = array->Buffer(); + double* fields = static_cast(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& 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& args) { + Environment* env = Environment::GetCurrent(args); + env->StartProfilerIdleNotifier(); +} + +void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + env->StopProfilerIdleNotifier(); +} + +void Umask(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + uint32_t old; + + if (args.Length() < 1 || args[0]->IsUndefined()) { + old = umask(0); + umask(static_cast(old)); + } else if (!args[0]->IsInt32() && !args[0]->IsString()) { + return env->ThrowTypeError("argument must be an integer or octal string."); + } else { + int oct; + if (args[0]->IsInt32()) { + oct = args[0]->Uint32Value(); + } else { + oct = 0; + Utf8Value str(env->isolate(), args[0]); + + // Parse the octal string. + for (size_t i = 0; i < str.length(); i++) { + char c = (*str)[i]; + if (c > '7' || c < '0') + return env->ThrowTypeError("invalid octal string"); + oct *= 8; + oct += c - '0'; + } + } + old = umask(static_cast(oct)); + } + + args.GetReturnValue().Set(old); +} + +void Uptime(const FunctionCallbackInfo& 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__) + +static const uid_t uid_not_found = static_cast(-1); +static const gid_t gid_not_found = static_cast(-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) { + if (value->IsUint32()) { + return static_cast(value->Uint32Value()); + } else { + Utf8Value name(isolate, value); + return uid_by_name(*name); + } +} + + +static gid_t gid_by_name(Isolate* isolate, Local value) { + if (value->IsUint32()) { + return static_cast(value->Uint32Value()); + } else { + Utf8Value name(isolate, value); + return gid_by_name(*name); + } +} + +void GetUid(const FunctionCallbackInfo& args) { + // uid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getuid())); +} + + +void GetGid(const FunctionCallbackInfo& args) { + // gid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getgid())); +} + + +void GetEUid(const FunctionCallbackInfo& args) { + // uid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(geteuid())); +} + + +void GetEGid(const FunctionCallbackInfo& args) { + // gid_t is an uint32_t on all supported platforms. + args.GetReturnValue().Set(static_cast(getegid())); +} + + +void SetGid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) { + return env->ThrowTypeError("setgid argument must be a number or a string"); + } + + gid_t gid = gid_by_name(env->isolate(), args[0]); + + if (gid == gid_not_found) { + return env->ThrowError("setgid group id does not exist"); + } + + if (setgid(gid)) { + return env->ThrowErrnoException(errno, "setgid"); + } +} + + +void SetEGid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) { + return env->ThrowTypeError("setegid argument must be a number or string"); + } + + gid_t gid = gid_by_name(env->isolate(), args[0]); + + if (gid == gid_not_found) { + return env->ThrowError("setegid group id does not exist"); + } + + if (setegid(gid)) { + return env->ThrowErrnoException(errno, "setegid"); + } +} + + +void SetUid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) { + return env->ThrowTypeError("setuid argument must be a number or a string"); + } + + uid_t uid = uid_by_name(env->isolate(), args[0]); + + if (uid == uid_not_found) { + return env->ThrowError("setuid user id does not exist"); + } + + if (setuid(uid)) { + return env->ThrowErrnoException(errno, "setuid"); + } +} + + +void SetEUid(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) { + return env->ThrowTypeError("seteuid argument must be a number or string"); + } + + uid_t uid = uid_by_name(env->isolate(), args[0]); + + if (uid == uid_not_found) { + return env->ThrowError("seteuid user id does not exist"); + } + + if (seteuid(uid)) { + return env->ThrowErrnoException(errno, "seteuid"); + } +} + + +void GetGroups(const FunctionCallbackInfo& 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 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& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsArray()) { + return env->ThrowTypeError("argument 1 must be an array"); + } + + Local groups_list = args[0].As(); + 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; + return env->ThrowError("group name not found"); + } + + groups[i] = gid; + } + + int rc = setgroups(size, groups); + delete[] groups; + + if (rc == -1) { + return env->ThrowErrnoException(errno, "setgroups"); + } +} + + +void InitGroups(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (!args[0]->IsUint32() && !args[0]->IsString()) { + return env->ThrowTypeError("argument 1 must be a number or a string"); + } + + if (!args[1]->IsUint32() && !args[1]->IsString()) { + return env->ThrowTypeError("argument 2 must be a number or a string"); + } + + 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]->Uint32Value()); + must_free = true; + } else { + user = *arg0; + must_free = false; + } + + if (user == nullptr) { + return env->ThrowError("initgroups user not found"); + } + + extra_group = gid_by_name(env->isolate(), args[1]); + + if (extra_group == gid_not_found) { + if (must_free) + free(user); + return env->ThrowError("initgroups extra group not found"); + } + + int rc = initgroups(user, extra_group); + + if (must_free) { + free(user); + } + + if (rc) { + return env->ThrowErrnoException(errno, "initgroups"); + } +} + +#endif // __POSIX__ && !defined(__ANDROID__) + +} // namespace node From 7ac8c454cb166f4d115bddb6522bce6697598e2e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Fri, 13 Jul 2018 11:12:24 -0700 Subject: [PATCH 3/3] src: add node_encoding.cc Backport of https://github.com/nodejs/node/pull/21112 --- node.gyp | 1 + src/node.cc | 123 -------------------------------------- src/node_encoding.cc | 138 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 123 deletions(-) create mode 100644 src/node_encoding.cc diff --git a/node.gyp b/node.gyp index c6566aec08dc92..fe87bc3ea144ed 100644 --- a/node.gyp +++ b/node.gyp @@ -283,6 +283,7 @@ 'src/node_constants.cc', 'src/node_contextify.cc', 'src/node_debug_options.cc', + 'src/node_encoding.cc', 'src/node_file.cc', 'src/node_http2.cc', 'src/node_http_parser.cc', diff --git a/src/node.cc b/src/node.cc index 3fce0c420b5ac7..9d7e6a5873c9dd 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1532,129 +1532,6 @@ Local MakeCallback(Isolate* isolate, .FromMaybe(Local())); } - -enum encoding ParseEncoding(const char* encoding, - enum encoding default_encoding) { - switch (encoding[0]) { - case 'u': - // utf8, utf16le - if (encoding[1] == 't' && encoding[2] == 'f') { - // Skip `-` - encoding += encoding[3] == '-' ? 4 : 3; - if (encoding[0] == '8' && encoding[1] == '\0') - return UTF8; - if (strncmp(encoding, "16le", 4) == 0) - return UCS2; - - // ucs2 - } else if (encoding[1] == 'c' && encoding[2] == 's') { - encoding += encoding[3] == '-' ? 4 : 3; - if (encoding[0] == '2' && encoding[1] == '\0') - return UCS2; - } - break; - case 'l': - // latin1 - if (encoding[1] == 'a') { - if (strncmp(encoding + 2, "tin1", 4) == 0) - return LATIN1; - } - break; - case 'b': - // binary - if (encoding[1] == 'i') { - if (strncmp(encoding + 2, "nary", 4) == 0) - return LATIN1; - - // buffer - } else if (encoding[1] == 'u') { - if (strncmp(encoding + 2, "ffer", 4) == 0) - return BUFFER; - } - break; - case '\0': - return default_encoding; - default: - break; - } - - if (StringEqualNoCase(encoding, "utf8")) { - return UTF8; - } else if (StringEqualNoCase(encoding, "utf-8")) { - return UTF8; - } else if (StringEqualNoCase(encoding, "ascii")) { - return ASCII; - } else if (StringEqualNoCase(encoding, "base64")) { - return BASE64; - } else if (StringEqualNoCase(encoding, "ucs2")) { - return UCS2; - } else if (StringEqualNoCase(encoding, "ucs-2")) { - return UCS2; - } else if (StringEqualNoCase(encoding, "utf16le")) { - return UCS2; - } else if (StringEqualNoCase(encoding, "utf-16le")) { - return UCS2; - } else if (StringEqualNoCase(encoding, "latin1")) { - return LATIN1; - } else if (StringEqualNoCase(encoding, "binary")) { - return LATIN1; // BINARY is a deprecated alias of LATIN1. - } else if (StringEqualNoCase(encoding, "buffer")) { - return BUFFER; - } else if (StringEqualNoCase(encoding, "hex")) { - return HEX; - } else { - return default_encoding; - } -} - - -enum encoding ParseEncoding(Isolate* isolate, - Local encoding_v, - enum encoding default_encoding) { - CHECK(!encoding_v.IsEmpty()); - - if (!encoding_v->IsString()) - return default_encoding; - - node::Utf8Value encoding(isolate, encoding_v); - - return ParseEncoding(*encoding, default_encoding); -} - -Local Encode(Isolate* isolate, - const char* buf, - size_t len, - enum encoding encoding) { - CHECK_NE(encoding, UCS2); - Local error; - return StringBytes::Encode(isolate, buf, len, encoding, &error) - .ToLocalChecked(); -} - -Local Encode(Isolate* isolate, const uint16_t* buf, size_t len) { - Local error; - return StringBytes::Encode(isolate, buf, len, &error) - .ToLocalChecked(); -} - -// Returns -1 if the handle was not valid for decoding -ssize_t DecodeBytes(Isolate* isolate, - Local val, - enum encoding encoding) { - HandleScope scope(isolate); - - return StringBytes::Size(isolate, val, encoding); -} - -// Returns number of bytes written. -ssize_t DecodeWrite(Isolate* isolate, - char* buf, - size_t buflen, - Local val, - enum encoding encoding) { - return StringBytes::Write(isolate, buf, buflen, val, encoding, nullptr); -} - bool IsExceptionDecorated(Environment* env, Local er) { if (!er.IsEmpty() && er->IsObject()) { Local err_obj = er.As(); diff --git a/src/node_encoding.cc b/src/node_encoding.cc new file mode 100644 index 00000000000000..0476cec45680c9 --- /dev/null +++ b/src/node_encoding.cc @@ -0,0 +1,138 @@ +#include "node.h" +#include "env.h" +#include "env-inl.h" +#include "string_bytes.h" +#include "util.h" +#include "util-inl.h" +#include "v8.h" + +namespace node { + +using v8::HandleScope; +using v8::Isolate; +using v8::Local; +using v8::Value; + +enum encoding ParseEncoding(const char* encoding, + enum encoding default_encoding) { + switch (encoding[0]) { + case 'u': + // utf8, utf16le + if (encoding[1] == 't' && encoding[2] == 'f') { + // Skip `-` + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '8' && encoding[1] == '\0') + return UTF8; + if (strncmp(encoding, "16le", 4) == 0) + return UCS2; + + // ucs2 + } else if (encoding[1] == 'c' && encoding[2] == 's') { + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '2' && encoding[1] == '\0') + return UCS2; + } + break; + case 'l': + // latin1 + if (encoding[1] == 'a') { + if (strncmp(encoding + 2, "tin1", 4) == 0) + return LATIN1; + } + break; + case 'b': + // binary + if (encoding[1] == 'i') { + if (strncmp(encoding + 2, "nary", 4) == 0) + return LATIN1; + + // buffer + } else if (encoding[1] == 'u') { + if (strncmp(encoding + 2, "ffer", 4) == 0) + return BUFFER; + } + break; + case '\0': + return default_encoding; + default: + break; + } + + if (StringEqualNoCase(encoding, "utf8")) { + return UTF8; + } else if (StringEqualNoCase(encoding, "utf-8")) { + return UTF8; + } else if (StringEqualNoCase(encoding, "ascii")) { + return ASCII; + } else if (StringEqualNoCase(encoding, "base64")) { + return BASE64; + } else if (StringEqualNoCase(encoding, "ucs2")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "ucs-2")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "utf16le")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "utf-16le")) { + return UCS2; + } else if (StringEqualNoCase(encoding, "latin1")) { + return LATIN1; + } else if (StringEqualNoCase(encoding, "binary")) { + return LATIN1; // BINARY is a deprecated alias of LATIN1. + } else if (StringEqualNoCase(encoding, "buffer")) { + return BUFFER; + } else if (StringEqualNoCase(encoding, "hex")) { + return HEX; + } else { + return default_encoding; + } +} + + +enum encoding ParseEncoding(Isolate* isolate, + Local encoding_v, + enum encoding default_encoding) { + CHECK(!encoding_v.IsEmpty()); + + if (!encoding_v->IsString()) + return default_encoding; + + node::Utf8Value encoding(isolate, encoding_v); + + return ParseEncoding(*encoding, default_encoding); +} + +Local Encode(Isolate* isolate, + const char* buf, + size_t len, + enum encoding encoding) { + CHECK_NE(encoding, UCS2); + Local error; + return StringBytes::Encode(isolate, buf, len, encoding, &error) + .ToLocalChecked(); +} + +Local Encode(Isolate* isolate, const uint16_t* buf, size_t len) { + Local error; + return StringBytes::Encode(isolate, buf, len, &error) + .ToLocalChecked(); +} + +// Returns -1 if the handle was not valid for decoding +ssize_t DecodeBytes(Isolate* isolate, + Local val, + enum encoding encoding) { + HandleScope scope(isolate); + + return StringBytes::Size(isolate, val, encoding); +} + +// Returns number of bytes written. +ssize_t DecodeWrite(Isolate* isolate, + char* buf, + size_t buflen, + Local val, + enum encoding encoding) { + return StringBytes::Write(isolate, buf, buflen, val, encoding, nullptr); +} + +} // namespace node