// 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 "node_internals.h"
#include "string_bytes.h"
#include
#include
#ifdef __MINGW32__
# include
#endif // __MINGW32__
#ifdef __POSIX__
# include // PATH_MAX on Solaris.
# include // MAXHOSTNAMELEN on Solaris.
# include // gethostname, sysconf
# include // MAXHOSTNAMELEN on Linux and the BSDs.
# include
#endif // __POSIX__
// Add Windows fallback.
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 256
#endif // MAXHOSTNAMELEN
namespace node {
namespace os {
using v8::Array;
using v8::ArrayBuffer;
using v8::Boolean;
using v8::Context;
using v8::Float64Array;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Integer;
using v8::Local;
using v8::MaybeLocal;
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[MAXHOSTNAMELEN + 1];
if (gethostname(buf, sizeof(buf))) {
#ifdef __POSIX__
int errorno = errno;
#else // __MINGW32__
int errorno = WSAGetLastError();
#endif // __POSIX__
return env->ThrowErrnoException(errorno, "gethostname");
}
buf[sizeof(buf) - 1] = '\0';
args.GetReturnValue().Set(OneByteString(env->isolate(), buf));
}
static void GetOSType(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
const char* rval;
#ifdef __POSIX__
struct utsname info;
if (uname(&info) < 0) {
return env->ThrowErrnoException(errno, "uname");
}
rval = info.sysname;
#else // __MINGW32__
rval = "Windows_NT";
#endif // __POSIX__
args.GetReturnValue().Set(OneByteString(env->isolate(), rval));
}
static void GetOSRelease(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
const char* rval;
#ifdef __POSIX__
struct utsname info;
if (uname(&info) < 0) {
return env->ThrowErrnoException(errno, "uname");
}
# ifdef _AIX
char release[256];
snprintf(release, sizeof(release),
"%s.%s", info.version, info.release);
rval = release;
# else
rval = info.release;
# endif
#else // Windows
char release[256];
OSVERSIONINFOW info;
info.dwOSVersionInfoSize = sizeof(info);
// Don't complain that GetVersionEx is deprecated; there is no alternative.
#pragma warning(suppress : 4996)
if (GetVersionExW(&info) == 0)
return;
snprintf(release,
sizeof(release),
"%d.%d.%d",
static_cast(info.dwMajorVersion),
static_cast(info.dwMinorVersion),
static_cast(info.dwBuildNumber));
rval = release;
#endif // __POSIX__
args.GetReturnValue().Set(OneByteString(env->isolate(), rval));
}
static void GetCPUInfo(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
uv_cpu_info_t* cpu_infos;
int count, i, field_idx;
int err = uv_cpu_info(&cpu_infos, &count);
if (err)
return;
CHECK(args[0]->IsFunction());
Local addfn = args[0].As();
CHECK(args[1]->IsFloat64Array());
Local array = args[1].As();
CHECK_EQ(array->Length(), 6 * NODE_PUSH_VAL_TO_ARRAY_MAX);
Local ab = array->Buffer();
double* fields = static_cast(ab->GetContents().Data());
CHECK(args[2]->IsArray());
Local cpus = args[2].As();
Local model_argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
int model_idx = 0;
for (i = 0, field_idx = 0; i < count; i++) {
uv_cpu_info_t* ci = cpu_infos + i;
fields[field_idx++] = ci->speed;
fields[field_idx++] = ci->cpu_times.user;
fields[field_idx++] = ci->cpu_times.nice;
fields[field_idx++] = ci->cpu_times.sys;
fields[field_idx++] = ci->cpu_times.idle;
fields[field_idx++] = ci->cpu_times.irq;
model_argv[model_idx++] = OneByteString(env->isolate(), ci->model);
if (model_idx >= NODE_PUSH_VAL_TO_ARRAY_MAX) {
addfn->Call(env->context(), cpus, model_idx, model_argv).ToLocalChecked();
model_idx = 0;
field_idx = 0;
}
}
if (model_idx > 0) {
addfn->Call(env->context(), cpus, model_idx, model_argv).ToLocalChecked();
}
uv_free_cpu_info(cpu_infos, count);
args.GetReturnValue().Set(cpus);
}
static void GetFreeMemory(const FunctionCallbackInfo& args) {
double amount = uv_get_free_memory();
if (amount < 0)
return;
args.GetReturnValue().Set(amount);
}
static void GetTotalMemory(const FunctionCallbackInfo& args) {
double amount = uv_get_total_memory();
if (amount < 0)
return;
args.GetReturnValue().Set(amount);
}
static void GetUptime(const FunctionCallbackInfo& args) {
double uptime;
int err = uv_uptime(&uptime);
if (err == 0)
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->GetContents().Data());
uv_loadavg(loadavg);
}
static void GetInterfaceAddresses(const FunctionCallbackInfo& args) {
Environment* env = Environment::GetCurrent(args);
uv_interface_address_t* interfaces;
int count, i;
char ip[INET6_ADDRSTRLEN];
char netmask[INET6_ADDRSTRLEN];
char mac[18];
Local ret, o;
Local name, family;
Local ifarr;
int err = uv_interface_addresses(&interfaces, &count);
ret = Object::New(env->isolate());
if (err == UV_ENOSYS) {
return args.GetReturnValue().Set(ret);
} else if (err) {
return env->ThrowUVException(err, "uv_interface_addresses");
}
for (i = 0; i < count; i++) {
const char* const raw_name = interfaces[i].name;
// On Windows, the interface name is the UTF8-encoded friendly name and may
// contain non-ASCII characters. On UNIX, it's just a binary string with
// no particular encoding but we treat it as a one-byte Latin-1 string.
#ifdef _WIN32
name = String::NewFromUtf8(env->isolate(), raw_name);
#else
name = OneByteString(env->isolate(), raw_name);
#endif
if (ret->Has(env->context(), name).FromJust()) {
ifarr = Local::Cast(ret->Get(name));
} else {
ifarr = Array::New(env->isolate());
ret->Set(name, ifarr);
}
snprintf(mac,
18,
"%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();
}
o = Object::New(env->isolate());
o->Set(env->address_string(), OneByteString(env->isolate(), ip));
o->Set(env->netmask_string(), OneByteString(env->isolate(), netmask));
o->Set(env->family_string(), family);
o->Set(env->mac_string(), FIXED_ONE_BYTE_STRING(env->isolate(), mac));
if (interfaces[i].address.address4.sin_family == AF_INET6) {
uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
o->Set(env->scopeid_string(),
Integer::NewFromUnsigned(env->isolate(), scopeid));
}
const bool internal = interfaces[i].is_internal;
o->Set(env->internal_string(),
internal ? True(env->isolate()) : False(env->isolate()));
ifarr->Set(ifarr->Length(), o);
}
uv_free_interface_addresses(interfaces, count);
args.GetReturnValue().Set(ret);
}
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) {
return env->ThrowUVException(err, "uv_os_homedir");
}
Local home = String::NewFromUtf8(env->isolate(),
buf,
String::kNormalString,
len);
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());
if (maybe_encoding.IsEmpty())
return;
Local encoding_opt = maybe_encoding.ToLocalChecked();
encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
} else {
encoding = UTF8;
}
const int err = uv_os_get_passwd(&pwd);
if (err) {
return env->ThrowUVException(err, "uv_os_get_passwd");
}
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 == NULL)
shell = Null(env->isolate());
else
shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
CHECK(!error.IsEmpty());
uv_os_free_passwd(&pwd);
env->isolate()->ThrowException(error);
return;
}
Local entry = Object::New(env->isolate());
entry->Set(env->uid_string(), uid);
entry->Set(env->gid_string(), gid);
entry->Set(env->username_string(), username.ToLocalChecked());
entry->Set(env->homedir_string(), homedir.ToLocalChecked());
entry->Set(env->shell_string(), shell.ToLocalChecked());
args.GetReturnValue().Set(entry);
}
void Initialize(Local target,
Local unused,
Local context) {
Environment* env = Environment::GetCurrent(context);
env->SetMethod(target, "getHostname", GetHostname);
env->SetMethod(target, "getLoadAvg", GetLoadAvg);
env->SetMethod(target, "getUptime", GetUptime);
env->SetMethod(target, "getTotalMem", GetTotalMemory);
env->SetMethod(target, "getFreeMem", GetFreeMemory);
env->SetMethod(target, "getCPUs", GetCPUInfo);
env->SetMethod(target, "getOSType", GetOSType);
env->SetMethod(target, "getOSRelease", GetOSRelease);
env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);
env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);
env->SetMethod(target, "getUserInfo", GetUserInfo);
target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
Boolean::New(env->isolate(), IsBigEndian()));
}
} // namespace os
} // namespace node
NODE_MODULE_CONTEXT_AWARE_BUILTIN(os, node::os::Initialize)