1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 #include "env-inl.h"
23 #include "string_bytes.h"
24
25 #ifdef __MINGW32__
26 # include <io.h>
27 #endif // __MINGW32__
28
29 #ifdef __POSIX__
30 # include <unistd.h> // gethostname, sysconf
31 # include <climits> // PATH_MAX on Solaris.
32 #endif // __POSIX__
33
34 #include <array>
35 #include <cerrno>
36 #include <cstring>
37
38 namespace node {
39 namespace os {
40
41 using v8::Array;
42 using v8::ArrayBuffer;
43 using v8::Boolean;
44 using v8::Context;
45 using v8::Float64Array;
46 using v8::FunctionCallbackInfo;
47 using v8::Int32;
48 using v8::Integer;
49 using v8::Isolate;
50 using v8::Local;
51 using v8::MaybeLocal;
52 using v8::NewStringType;
53 using v8::Null;
54 using v8::Number;
55 using v8::Object;
56 using v8::String;
57 using v8::Value;
58
59
GetHostname(const FunctionCallbackInfo<Value> & args)60 static void GetHostname(const FunctionCallbackInfo<Value>& args) {
61 Environment* env = Environment::GetCurrent(args);
62 char buf[UV_MAXHOSTNAMESIZE];
63 size_t size = sizeof(buf);
64 int r = uv_os_gethostname(buf, &size);
65
66 if (r != 0) {
67 CHECK_GE(args.Length(), 1);
68 env->CollectUVExceptionInfo(args[args.Length() - 1], r,
69 "uv_os_gethostname");
70 return args.GetReturnValue().SetUndefined();
71 }
72
73 args.GetReturnValue().Set(
74 String::NewFromUtf8(env->isolate(), buf).ToLocalChecked());
75 }
76
GetOSInformation(const FunctionCallbackInfo<Value> & args)77 static void GetOSInformation(const FunctionCallbackInfo<Value>& args) {
78 Environment* env = Environment::GetCurrent(args);
79 uv_utsname_t info;
80 int err = uv_os_uname(&info);
81
82 if (err != 0) {
83 CHECK_GE(args.Length(), 1);
84 env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_uname");
85 return args.GetReturnValue().SetUndefined();
86 }
87
88 // [sysname, version, release]
89 Local<Value> osInformation[] = {
90 String::NewFromUtf8(env->isolate(), info.sysname).ToLocalChecked(),
91 String::NewFromUtf8(env->isolate(), info.version).ToLocalChecked(),
92 String::NewFromUtf8(env->isolate(), info.release).ToLocalChecked()
93 };
94
95 args.GetReturnValue().Set(Array::New(env->isolate(),
96 osInformation,
97 arraysize(osInformation)));
98 }
99
GetCPUInfo(const FunctionCallbackInfo<Value> & args)100 static void GetCPUInfo(const FunctionCallbackInfo<Value>& args) {
101 Environment* env = Environment::GetCurrent(args);
102 Isolate* isolate = env->isolate();
103
104 uv_cpu_info_t* cpu_infos;
105 int count;
106
107 int err = uv_cpu_info(&cpu_infos, &count);
108 if (err)
109 return;
110
111 // It's faster to create an array packed with all the data and
112 // assemble them into objects in JS than to call Object::Set() repeatedly
113 // The array is in the format
114 // [model, speed, (5 entries of cpu_times), model2, speed2, ...]
115 std::vector<Local<Value>> result;
116 result.reserve(count * 7);
117 for (int i = 0; i < count; i++) {
118 uv_cpu_info_t* ci = cpu_infos + i;
119 result.emplace_back(OneByteString(isolate, ci->model));
120 result.emplace_back(Number::New(isolate, ci->speed));
121 result.emplace_back(Number::New(isolate, ci->cpu_times.user));
122 result.emplace_back(Number::New(isolate, ci->cpu_times.nice));
123 result.emplace_back(Number::New(isolate, ci->cpu_times.sys));
124 result.emplace_back(Number::New(isolate, ci->cpu_times.idle));
125 result.emplace_back(Number::New(isolate, ci->cpu_times.irq));
126 }
127
128 uv_free_cpu_info(cpu_infos, count);
129 args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
130 }
131
132
GetFreeMemory(const FunctionCallbackInfo<Value> & args)133 static void GetFreeMemory(const FunctionCallbackInfo<Value>& args) {
134 double amount = uv_get_free_memory();
135 args.GetReturnValue().Set(amount);
136 }
137
138
GetTotalMemory(const FunctionCallbackInfo<Value> & args)139 static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
140 double amount = uv_get_total_memory();
141 args.GetReturnValue().Set(amount);
142 }
143
144
GetUptime(const FunctionCallbackInfo<Value> & args)145 static void GetUptime(const FunctionCallbackInfo<Value>& args) {
146 double uptime;
147 int err = uv_uptime(&uptime);
148 if (err == 0)
149 args.GetReturnValue().Set(uptime);
150 }
151
152
GetLoadAvg(const FunctionCallbackInfo<Value> & args)153 static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {
154 CHECK(args[0]->IsFloat64Array());
155 Local<Float64Array> array = args[0].As<Float64Array>();
156 CHECK_EQ(array->Length(), 3);
157 Local<ArrayBuffer> ab = array->Buffer();
158 double* loadavg = static_cast<double*>(ab->GetBackingStore()->Data());
159 uv_loadavg(loadavg);
160 }
161
162
GetInterfaceAddresses(const FunctionCallbackInfo<Value> & args)163 static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {
164 Environment* env = Environment::GetCurrent(args);
165 Isolate* isolate = env->isolate();
166 uv_interface_address_t* interfaces;
167 int count, i;
168 char ip[INET6_ADDRSTRLEN];
169 char netmask[INET6_ADDRSTRLEN];
170 std::array<char, 18> mac;
171 Local<String> name, family;
172
173 int err = uv_interface_addresses(&interfaces, &count);
174
175 if (err == UV_ENOSYS)
176 return args.GetReturnValue().SetUndefined();
177
178 if (err) {
179 CHECK_GE(args.Length(), 1);
180 env->CollectUVExceptionInfo(args[args.Length() - 1], errno,
181 "uv_interface_addresses");
182 return args.GetReturnValue().SetUndefined();
183 }
184
185 Local<Value> no_scope_id = Integer::New(isolate, -1);
186 std::vector<Local<Value>> result;
187 result.reserve(count * 7);
188 for (i = 0; i < count; i++) {
189 const char* const raw_name = interfaces[i].name;
190
191 // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX
192 // systems are somewhat encoding-agnostic here, it’s more than reasonable
193 // to assume UTF8 as the default as well. It’s what people will expect if
194 // they name the interface from any input that uses UTF-8, which should be
195 // the most frequent case by far these days.)
196 name = String::NewFromUtf8(isolate, raw_name).ToLocalChecked();
197
198 snprintf(mac.data(),
199 mac.size(),
200 "%02x:%02x:%02x:%02x:%02x:%02x",
201 static_cast<unsigned char>(interfaces[i].phys_addr[0]),
202 static_cast<unsigned char>(interfaces[i].phys_addr[1]),
203 static_cast<unsigned char>(interfaces[i].phys_addr[2]),
204 static_cast<unsigned char>(interfaces[i].phys_addr[3]),
205 static_cast<unsigned char>(interfaces[i].phys_addr[4]),
206 static_cast<unsigned char>(interfaces[i].phys_addr[5]));
207
208 if (interfaces[i].address.address4.sin_family == AF_INET) {
209 uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
210 uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
211 family = env->ipv4_string();
212 } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
213 uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
214 uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
215 family = env->ipv6_string();
216 } else {
217 strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
218 family = env->unknown_string();
219 }
220
221 result.emplace_back(name);
222 result.emplace_back(OneByteString(isolate, ip));
223 result.emplace_back(OneByteString(isolate, netmask));
224 result.emplace_back(family);
225 result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac));
226 result.emplace_back(
227 interfaces[i].is_internal ? True(isolate) : False(isolate));
228 if (interfaces[i].address.address4.sin_family == AF_INET6) {
229 uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
230 result.emplace_back(Integer::NewFromUnsigned(isolate, scopeid));
231 } else {
232 result.emplace_back(no_scope_id);
233 }
234 }
235
236 uv_free_interface_addresses(interfaces, count);
237 args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
238 }
239
240
GetHomeDirectory(const FunctionCallbackInfo<Value> & args)241 static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
242 Environment* env = Environment::GetCurrent(args);
243 char buf[PATH_MAX];
244
245 size_t len = sizeof(buf);
246 const int err = uv_os_homedir(buf, &len);
247
248 if (err) {
249 CHECK_GE(args.Length(), 1);
250 env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
251 return args.GetReturnValue().SetUndefined();
252 }
253
254 Local<String> home = String::NewFromUtf8(env->isolate(),
255 buf,
256 NewStringType::kNormal,
257 len).ToLocalChecked();
258 args.GetReturnValue().Set(home);
259 }
260
261
GetUserInfo(const FunctionCallbackInfo<Value> & args)262 static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
263 Environment* env = Environment::GetCurrent(args);
264 uv_passwd_t pwd;
265 enum encoding encoding;
266
267 if (args[0]->IsObject()) {
268 Local<Object> options = args[0].As<Object>();
269 MaybeLocal<Value> maybe_encoding = options->Get(env->context(),
270 env->encoding_string());
271 Local<Value> encoding_opt;
272 if (!maybe_encoding.ToLocal(&encoding_opt))
273 return;
274
275 encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
276 } else {
277 encoding = UTF8;
278 }
279
280 const int err = uv_os_get_passwd(&pwd);
281
282 if (err) {
283 CHECK_GE(args.Length(), 2);
284 env->CollectUVExceptionInfo(args[args.Length() - 1], err,
285 "uv_os_get_passwd");
286 return args.GetReturnValue().SetUndefined();
287 }
288
289 auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); });
290
291 Local<Value> error;
292
293 Local<Value> uid = Number::New(env->isolate(), pwd.uid);
294 Local<Value> gid = Number::New(env->isolate(), pwd.gid);
295 MaybeLocal<Value> username = StringBytes::Encode(env->isolate(),
296 pwd.username,
297 encoding,
298 &error);
299 MaybeLocal<Value> homedir = StringBytes::Encode(env->isolate(),
300 pwd.homedir,
301 encoding,
302 &error);
303 MaybeLocal<Value> shell;
304
305 if (pwd.shell == nullptr)
306 shell = Null(env->isolate());
307 else
308 shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
309
310 if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
311 CHECK(!error.IsEmpty());
312 env->isolate()->ThrowException(error);
313 return;
314 }
315
316 Local<Object> entry = Object::New(env->isolate());
317
318 entry->Set(env->context(), env->uid_string(), uid).Check();
319 entry->Set(env->context(), env->gid_string(), gid).Check();
320 entry->Set(env->context(),
321 env->username_string(),
322 username.ToLocalChecked()).Check();
323 entry->Set(env->context(),
324 env->homedir_string(),
325 homedir.ToLocalChecked()).Check();
326 entry->Set(env->context(),
327 env->shell_string(),
328 shell.ToLocalChecked()).Check();
329
330 args.GetReturnValue().Set(entry);
331 }
332
333
SetPriority(const FunctionCallbackInfo<Value> & args)334 static void SetPriority(const FunctionCallbackInfo<Value>& args) {
335 Environment* env = Environment::GetCurrent(args);
336
337 CHECK_EQ(args.Length(), 3);
338 CHECK(args[0]->IsInt32());
339 CHECK(args[1]->IsInt32());
340
341 const int pid = args[0].As<Int32>()->Value();
342 const int priority = args[1].As<Int32>()->Value();
343 const int err = uv_os_setpriority(pid, priority);
344
345 if (err) {
346 CHECK(args[2]->IsObject());
347 env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
348 }
349
350 args.GetReturnValue().Set(err);
351 }
352
353
GetPriority(const FunctionCallbackInfo<Value> & args)354 static void GetPriority(const FunctionCallbackInfo<Value>& args) {
355 Environment* env = Environment::GetCurrent(args);
356
357 CHECK_EQ(args.Length(), 2);
358 CHECK(args[0]->IsInt32());
359
360 const int pid = args[0].As<Int32>()->Value();
361 int priority;
362 const int err = uv_os_getpriority(pid, &priority);
363
364 if (err) {
365 CHECK(args[1]->IsObject());
366 env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
367 return;
368 }
369
370 args.GetReturnValue().Set(priority);
371 }
372
373
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)374 void Initialize(Local<Object> target,
375 Local<Value> unused,
376 Local<Context> context,
377 void* priv) {
378 Environment* env = Environment::GetCurrent(context);
379 env->SetMethod(target, "getHostname", GetHostname);
380 env->SetMethod(target, "getLoadAvg", GetLoadAvg);
381 env->SetMethod(target, "getUptime", GetUptime);
382 env->SetMethod(target, "getTotalMem", GetTotalMemory);
383 env->SetMethod(target, "getFreeMem", GetFreeMemory);
384 env->SetMethod(target, "getCPUs", GetCPUInfo);
385 env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);
386 env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);
387 env->SetMethod(target, "getUserInfo", GetUserInfo);
388 env->SetMethod(target, "setPriority", SetPriority);
389 env->SetMethod(target, "getPriority", GetPriority);
390 env->SetMethod(target, "getOSInformation", GetOSInformation);
391 target->Set(env->context(),
392 FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
393 Boolean::New(env->isolate(), IsBigEndian())).Check();
394 }
395
396 } // namespace os
397 } // namespace node
398
399 NODE_MODULE_CONTEXT_AWARE_INTERNAL(os, node::os::Initialize)
400