// Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "env-inl.h" #include "node_external_reference.h" #include "string_bytes.h" #ifdef __MINGW32__ # include #endif // __MINGW32__ #ifdef __POSIX__ # include // PATH_MAX on Solaris. #endif // __POSIX__ #include #include #include namespace node { namespace os { using v8::Array; using v8::ArrayBuffer; using v8::Boolean; using v8::Context; using v8::Float64Array; using v8::FunctionCallbackInfo; using v8::Int32; using v8::Integer; using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::NewStringType; using v8::Null; using v8::Number; using v8::Object; using v8::String; using v8::Value; static void GetHostname(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); char buf[UV_MAXHOSTNAMESIZE]; size_t size = sizeof(buf); int r = uv_os_gethostname(buf, &size); if (r != 0) { CHECK_GE(args.Length(), 1); env->CollectUVExceptionInfo(args[args.Length() - 1], r, "uv_os_gethostname"); return args.GetReturnValue().SetUndefined(); } args.GetReturnValue().Set( String::NewFromUtf8(env->isolate(), buf).ToLocalChecked()); } static void GetOSInformation(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); uv_utsname_t info; int err = uv_os_uname(&info); if (err != 0) { CHECK_GE(args.Length(), 1); env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_uname"); return args.GetReturnValue().SetUndefined(); } // [sysname, version, release, machine] Local osInformation[] = { String::NewFromUtf8(env->isolate(), info.sysname).ToLocalChecked(), String::NewFromUtf8(env->isolate(), info.version).ToLocalChecked(), String::NewFromUtf8(env->isolate(), info.release).ToLocalChecked(), String::NewFromUtf8(env->isolate(), info.machine).ToLocalChecked()}; args.GetReturnValue().Set(Array::New(env->isolate(), osInformation, arraysize(osInformation))); } static void GetCPUInfo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); uv_cpu_info_t* cpu_infos; int count; int err = uv_cpu_info(&cpu_infos, &count); if (err) return; // It's faster to create an array packed with all the data and // assemble them into objects in JS than to call Object::Set() repeatedly // The array is in the format // [model, speed, (5 entries of cpu_times), model2, speed2, ...] std::vector> result; result.reserve(count * 7); for (int i = 0; i < count; i++) { uv_cpu_info_t* ci = cpu_infos + i; result.emplace_back(OneByteString(isolate, ci->model)); result.emplace_back(Number::New(isolate, ci->speed)); result.emplace_back( Number::New(isolate, static_cast(ci->cpu_times.user))); result.emplace_back( Number::New(isolate, static_cast(ci->cpu_times.nice))); result.emplace_back( Number::New(isolate, static_cast(ci->cpu_times.sys))); result.emplace_back( Number::New(isolate, static_cast(ci->cpu_times.idle))); result.emplace_back( Number::New(isolate, static_cast(ci->cpu_times.irq))); } uv_free_cpu_info(cpu_infos, count); args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size())); } static void GetFreeMemory(const FunctionCallbackInfo& args) { double amount = static_cast(uv_get_free_memory()); args.GetReturnValue().Set(amount); } static void GetTotalMemory(const FunctionCallbackInfo& args) { double amount = static_cast(uv_get_total_memory()); args.GetReturnValue().Set(amount); } static void GetUptime(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); double uptime; int err = uv_uptime(&uptime); if (err != 0) { env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_uptime"); return args.GetReturnValue().SetUndefined(); } args.GetReturnValue().Set(uptime); } static void GetLoadAvg(const FunctionCallbackInfo& args) { CHECK(args[0]->IsFloat64Array()); Local array = args[0].As(); CHECK_EQ(array->Length(), 3); Local ab = array->Buffer(); double* loadavg = static_cast(ab->Data()); uv_loadavg(loadavg); } static void GetInterfaceAddresses(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); uv_interface_address_t* interfaces; int count, i; char ip[INET6_ADDRSTRLEN]; char netmask[INET6_ADDRSTRLEN]; std::array mac; Local name, family; int err = uv_interface_addresses(&interfaces, &count); if (err == UV_ENOSYS) return args.GetReturnValue().SetUndefined(); if (err) { CHECK_GE(args.Length(), 1); env->CollectUVExceptionInfo(args[args.Length() - 1], errno, "uv_interface_addresses"); return args.GetReturnValue().SetUndefined(); } Local no_scope_id = Integer::New(isolate, -1); std::vector> result; result.reserve(count * 7); for (i = 0; i < count; i++) { const char* const raw_name = interfaces[i].name; // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX // systems are somewhat encoding-agnostic here, it’s more than reasonable // to assume UTF8 as the default as well. It’s what people will expect if // they name the interface from any input that uses UTF-8, which should be // the most frequent case by far these days.) name = String::NewFromUtf8(isolate, raw_name).ToLocalChecked(); snprintf(mac.data(), mac.size(), "%02x:%02x:%02x:%02x:%02x:%02x", static_cast(interfaces[i].phys_addr[0]), static_cast(interfaces[i].phys_addr[1]), static_cast(interfaces[i].phys_addr[2]), static_cast(interfaces[i].phys_addr[3]), static_cast(interfaces[i].phys_addr[4]), static_cast(interfaces[i].phys_addr[5])); if (interfaces[i].address.address4.sin_family == AF_INET) { uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip)); uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask)); family = env->ipv4_string(); } else if (interfaces[i].address.address4.sin_family == AF_INET6) { uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip)); uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask)); family = env->ipv6_string(); } else { strncpy(ip, "", INET6_ADDRSTRLEN); family = env->unknown_string(); } result.emplace_back(name); result.emplace_back(OneByteString(isolate, ip)); result.emplace_back(OneByteString(isolate, netmask)); result.emplace_back(family); result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac)); result.emplace_back( interfaces[i].is_internal ? True(isolate) : False(isolate)); if (interfaces[i].address.address4.sin_family == AF_INET6) { uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id; result.emplace_back(Integer::NewFromUnsigned(isolate, scopeid)); } else { result.emplace_back(no_scope_id); } } uv_free_interface_addresses(interfaces, count); args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size())); } static void GetHomeDirectory(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); char buf[PATH_MAX]; size_t len = sizeof(buf); const int err = uv_os_homedir(buf, &len); if (err) { CHECK_GE(args.Length(), 1); env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir"); return args.GetReturnValue().SetUndefined(); } Local home = String::NewFromUtf8(env->isolate(), buf, NewStringType::kNormal, len).ToLocalChecked(); args.GetReturnValue().Set(home); } static void GetUserInfo(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); uv_passwd_t pwd; enum encoding encoding; if (args[0]->IsObject()) { Local options = args[0].As(); MaybeLocal maybe_encoding = options->Get(env->context(), env->encoding_string()); Local encoding_opt; if (!maybe_encoding.ToLocal(&encoding_opt)) return; encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8); } else { encoding = UTF8; } const int err = uv_os_get_passwd(&pwd); if (err) { CHECK_GE(args.Length(), 2); env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_get_passwd"); return args.GetReturnValue().SetUndefined(); } auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); }); Local error; Local uid = Number::New(env->isolate(), pwd.uid); Local gid = Number::New(env->isolate(), pwd.gid); MaybeLocal username = StringBytes::Encode(env->isolate(), pwd.username, encoding, &error); MaybeLocal homedir = StringBytes::Encode(env->isolate(), pwd.homedir, encoding, &error); MaybeLocal shell; if (pwd.shell == nullptr) shell = Null(env->isolate()); else shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error); if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) { CHECK(!error.IsEmpty()); env->isolate()->ThrowException(error); return; } Local entry = Object::New(env->isolate()); entry->Set(env->context(), env->uid_string(), uid).Check(); entry->Set(env->context(), env->gid_string(), gid).Check(); entry->Set(env->context(), env->username_string(), username.ToLocalChecked()).Check(); entry->Set(env->context(), env->homedir_string(), homedir.ToLocalChecked()).Check(); entry->Set(env->context(), env->shell_string(), shell.ToLocalChecked()).Check(); args.GetReturnValue().Set(entry); } static void SetPriority(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 3); CHECK(args[0]->IsInt32()); CHECK(args[1]->IsInt32()); const int pid = args[0].As()->Value(); const int priority = args[1].As()->Value(); const int err = uv_os_setpriority(pid, priority); if (err) { CHECK(args[2]->IsObject()); env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority"); } args.GetReturnValue().Set(err); } static void GetPriority(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 2); CHECK(args[0]->IsInt32()); const int pid = args[0].As()->Value(); int priority; const int err = uv_os_getpriority(pid, &priority); if (err) { CHECK(args[1]->IsObject()); env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority"); return; } args.GetReturnValue().Set(priority); } static void GetAvailableParallelism(const FunctionCallbackInfo& args) { unsigned int parallelism = uv_available_parallelism(); args.GetReturnValue().Set(parallelism); } void Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); SetMethod(context, target, "getHostname", GetHostname); SetMethod(context, target, "getLoadAvg", GetLoadAvg); SetMethod(context, target, "getUptime", GetUptime); SetMethod(context, target, "getTotalMem", GetTotalMemory); SetMethod(context, target, "getFreeMem", GetFreeMemory); SetMethod(context, target, "getCPUs", GetCPUInfo); SetMethod(context, target, "getInterfaceAddresses", GetInterfaceAddresses); SetMethod(context, target, "getHomeDirectory", GetHomeDirectory); SetMethod(context, target, "getUserInfo", GetUserInfo); SetMethod(context, target, "setPriority", SetPriority); SetMethod(context, target, "getPriority", GetPriority); SetMethod( context, target, "getAvailableParallelism", GetAvailableParallelism); SetMethod(context, target, "getOSInformation", GetOSInformation); target ->Set(context, FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"), Boolean::New(env->isolate(), IsBigEndian())) .Check(); } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(GetHostname); registry->Register(GetLoadAvg); registry->Register(GetUptime); registry->Register(GetTotalMemory); registry->Register(GetFreeMemory); registry->Register(GetCPUInfo); registry->Register(GetInterfaceAddresses); registry->Register(GetHomeDirectory); registry->Register(GetUserInfo); registry->Register(SetPriority); registry->Register(GetPriority); registry->Register(GetAvailableParallelism); registry->Register(GetOSInformation); } } // namespace os } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL(os, node::os::Initialize) NODE_BINDING_EXTERNAL_REFERENCE(os, node::os::RegisterExternalReferences)