diff --git a/node.gyp b/node.gyp index e0bd57dde51686..89ebb0fb95c945 100644 --- a/node.gyp +++ b/node.gyp @@ -350,6 +350,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_types.cc', diff --git a/src/node.cc b/src/node.cc index e2d9b426d0cf97..ec2d79f9f33280 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::Just; @@ -164,7 +162,6 @@ using v8::ScriptOrigin; using v8::SealHandleScope; using v8::String; using v8::TryCatch; -using v8::Uint32Array; using v8::Undefined; using v8::V8; using v8::Value; @@ -284,7 +281,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; @@ -388,7 +385,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 @@ -1104,411 +1101,6 @@ NO_RETURN void Assert(const char* const (*args)[4]) { Abort(); } - -static void Abort(const FunctionCallbackInfo& args) { - Abort(); -} - - -void Chdir(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - CHECK(env->is_main_thread()); - - 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, "chdir", nullptr, *path, nullptr); - } -} - - -static 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__) && !defined(__CloudABI__) - -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); - } -} - -static 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) { - // gid_t is an uint32_t on all supported platforms. - args.GetReturnValue().Set(static_cast(getgid())); -} - - -static 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) { - // 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); - CHECK(env->is_main_thread()); - - 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); - CHECK(env->is_main_thread()); - - 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); - CHECK(env->is_main_thread()); - - 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); - CHECK(env->is_main_thread()); - - 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"); - } -} - - -static 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__) && !defined(__CloudABI__) - - static void WaitForInspectorDisconnect(Environment* env) { #if HAVE_INSPECTOR if (env->inspector_agent()->IsActive()) { @@ -1537,113 +1129,6 @@ static void Exit(const FunctionCallbackInfo& args) { env->Exit(args[0]->Int32Value()); } - -static 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); -} - - -static 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); @@ -2449,18 +1934,6 @@ static void DebugEnd(const FunctionCallbackInfo& args); namespace { -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(), \ @@ -2790,6 +2263,8 @@ void SetupProcessObject(Environment* env, // define various internal methods if (env->is_main_thread()) { + env->SetMethod(process, "_debugProcess", DebugProcess); + env->SetMethod(process, "_debugEnd", DebugEnd); env->SetMethod(process, "_startProfilerIdleNotifier", StartProfilerIdleNotifier); @@ -2800,11 +2275,14 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "chdir", Chdir); env->SetMethod(process, "umask", Umask); } - env->SetMethod(process, "_getActiveRequests", GetActiveRequests); env->SetMethod(process, "_getActiveHandles", GetActiveHandles); - env->SetMethod(process, "reallyExit", Exit); + env->SetMethod(process, "_kill", Kill); + env->SetMethod(process, "cwd", Cwd); + env->SetMethod(process, "dlopen", DLOpen); + env->SetMethod(process, "reallyExit", Exit); + env->SetMethod(process, "uptime", Uptime); #if defined(__POSIX__) && !defined(__ANDROID__) && !defined(__CloudABI__) env->SetMethod(process, "getuid", GetUid); @@ -2813,21 +2291,6 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "getegid", GetEGid); env->SetMethod(process, "getgroups", GetGroups); #endif // __POSIX__ && !defined(__ANDROID__) && !defined(__CloudABI__) - - env->SetMethod(process, "_kill", Kill); - env->SetMethod(process, "dlopen", DLOpen); - - if (env->is_main_thread()) { - env->SetMethod(process, "_debugProcess", DebugProcess); - env->SetMethod(process, "_debugEnd", DebugEnd); - } - - env->SetMethod(process, "hrtime", Hrtime); - - env->SetMethod(process, "cpuUsage", CPUUsage); - - env->SetMethod(process, "uptime", Uptime); - env->SetMethod(process, "memoryUsage", MemoryUsage); } @@ -2848,19 +2311,6 @@ void SignalExit(int 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); -} - - static MaybeLocal GetBootstrapper( Environment* env, Local source, diff --git a/src/node_internals.h b/src/node_internals.h index 042b685c5939ae..5371a5502174fd 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -931,12 +931,21 @@ static inline const char* errno_string(int errorno) { // 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 StartProfilerIdleNotifier(const v8::FunctionCallbackInfo& args); +void StopProfilerIdleNotifier(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); @@ -945,6 +954,11 @@ 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 diff --git a/src/node_process.cc b/src/node_process.cc new file mode 100644 index 00000000000000..3313f85627b16f --- /dev/null +++ b/src/node_process.cc @@ -0,0 +1,542 @@ +#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); + CHECK(env->is_main_thread()); + + 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, "chdir", nullptr, *path, nullptr); +} + +// 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)); + return args.GetReturnValue().Set(errmsg); + } + + // 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__) && !defined(__CloudABI__) + +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); + CHECK(env->is_main_thread()); + + 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); + CHECK(env->is_main_thread()); + + 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); + CHECK(env->is_main_thread()); + + 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); + CHECK(env->is_main_thread()); + + 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__) && !defined(__CloudABI__) + +} // namespace node