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, NewStringType::kNormal)
75 .ToLocalChecked());
76 }
77
78
GetOSInformation(const FunctionCallbackInfo<Value> & args)79 static void GetOSInformation(const FunctionCallbackInfo<Value>& args) {
80 Environment* env = Environment::GetCurrent(args);
81 uv_utsname_t info;
82 int err = uv_os_uname(&info);
83
84 if (err != 0) {
85 CHECK_GE(args.Length(), 1);
86 env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_uname");
87 return args.GetReturnValue().SetUndefined();
88 }
89
90 // [sysname, version, release]
91 Local<Value> osInformation[] = {
92 String::NewFromUtf8(
93 env->isolate(), info.sysname, NewStringType::kNormal).ToLocalChecked(),
94 String::NewFromUtf8(
95 env->isolate(), info.version, NewStringType::kNormal).ToLocalChecked(),
96 String::NewFromUtf8(
97 env->isolate(), info.release, NewStringType::kNormal).ToLocalChecked()
98 };
99
100 args.GetReturnValue().Set(Array::New(env->isolate(),
101 osInformation,
102 arraysize(osInformation)));
103 }
104
105
GetCPUInfo(const FunctionCallbackInfo<Value> & args)106 static void GetCPUInfo(const FunctionCallbackInfo<Value>& args) {
107 Environment* env = Environment::GetCurrent(args);
108 Isolate* isolate = env->isolate();
109
110 uv_cpu_info_t* cpu_infos;
111 int count;
112
113 int err = uv_cpu_info(&cpu_infos, &count);
114 if (err)
115 return;
116
117 // It's faster to create an array packed with all the data and
118 // assemble them into objects in JS than to call Object::Set() repeatedly
119 // The array is in the format
120 // [model, speed, (5 entries of cpu_times), model2, speed2, ...]
121 std::vector<Local<Value>> result(count * 7);
122 for (int i = 0, j = 0; i < count; i++) {
123 uv_cpu_info_t* ci = cpu_infos + i;
124 result[j++] = OneByteString(isolate, ci->model);
125 result[j++] = Number::New(isolate, ci->speed);
126 result[j++] = Number::New(isolate, ci->cpu_times.user);
127 result[j++] = Number::New(isolate, ci->cpu_times.nice);
128 result[j++] = Number::New(isolate, ci->cpu_times.sys);
129 result[j++] = Number::New(isolate, ci->cpu_times.idle);
130 result[j++] = Number::New(isolate, ci->cpu_times.irq);
131 }
132
133 uv_free_cpu_info(cpu_infos, count);
134 args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
135 }
136
137
GetFreeMemory(const FunctionCallbackInfo<Value> & args)138 static void GetFreeMemory(const FunctionCallbackInfo<Value>& args) {
139 double amount = uv_get_free_memory();
140 args.GetReturnValue().Set(amount);
141 }
142
143
GetTotalMemory(const FunctionCallbackInfo<Value> & args)144 static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
145 double amount = uv_get_total_memory();
146 args.GetReturnValue().Set(amount);
147 }
148
149
GetUptime(const FunctionCallbackInfo<Value> & args)150 static void GetUptime(const FunctionCallbackInfo<Value>& args) {
151 double uptime;
152 int err = uv_uptime(&uptime);
153 if (err == 0)
154 args.GetReturnValue().Set(uptime);
155 }
156
157
GetLoadAvg(const FunctionCallbackInfo<Value> & args)158 static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {
159 CHECK(args[0]->IsFloat64Array());
160 Local<Float64Array> array = args[0].As<Float64Array>();
161 CHECK_EQ(array->Length(), 3);
162 Local<ArrayBuffer> ab = array->Buffer();
163 double* loadavg = static_cast<double*>(ab->GetContents().Data());
164 uv_loadavg(loadavg);
165 }
166
167
GetInterfaceAddresses(const FunctionCallbackInfo<Value> & args)168 static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {
169 Environment* env = Environment::GetCurrent(args);
170 Isolate* isolate = env->isolate();
171 uv_interface_address_t* interfaces;
172 int count, i;
173 char ip[INET6_ADDRSTRLEN];
174 char netmask[INET6_ADDRSTRLEN];
175 std::array<char, 18> mac;
176 Local<String> name, family;
177
178 int err = uv_interface_addresses(&interfaces, &count);
179
180 if (err == UV_ENOSYS)
181 return args.GetReturnValue().SetUndefined();
182
183 if (err) {
184 CHECK_GE(args.Length(), 1);
185 env->CollectUVExceptionInfo(args[args.Length() - 1], errno,
186 "uv_interface_addresses");
187 return args.GetReturnValue().SetUndefined();
188 }
189
190 Local<Value> no_scope_id = Integer::New(isolate, -1);
191 std::vector<Local<Value>> result(count * 7);
192 for (i = 0; i < count; i++) {
193 const char* const raw_name = interfaces[i].name;
194
195 // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX
196 // systems are somewhat encoding-agnostic here, it’s more than reasonable
197 // to assume UTF8 as the default as well. It’s what people will expect if
198 // they name the interface from any input that uses UTF-8, which should be
199 // the most frequent case by far these days.)
200 name = String::NewFromUtf8(isolate, raw_name,
201 NewStringType::kNormal).ToLocalChecked();
202
203 snprintf(mac.data(),
204 mac.size(),
205 "%02x:%02x:%02x:%02x:%02x:%02x",
206 static_cast<unsigned char>(interfaces[i].phys_addr[0]),
207 static_cast<unsigned char>(interfaces[i].phys_addr[1]),
208 static_cast<unsigned char>(interfaces[i].phys_addr[2]),
209 static_cast<unsigned char>(interfaces[i].phys_addr[3]),
210 static_cast<unsigned char>(interfaces[i].phys_addr[4]),
211 static_cast<unsigned char>(interfaces[i].phys_addr[5]));
212
213 if (interfaces[i].address.address4.sin_family == AF_INET) {
214 uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
215 uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
216 family = env->ipv4_string();
217 } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
218 uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
219 uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
220 family = env->ipv6_string();
221 } else {
222 strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
223 family = env->unknown_string();
224 }
225
226 result[i * 7] = name;
227 result[i * 7 + 1] = OneByteString(isolate, ip);
228 result[i * 7 + 2] = OneByteString(isolate, netmask);
229 result[i * 7 + 3] = family;
230 result[i * 7 + 4] = FIXED_ONE_BYTE_STRING(isolate, mac);
231 result[i * 7 + 5] =
232 interfaces[i].is_internal ? True(isolate) : False(isolate);
233 if (interfaces[i].address.address4.sin_family == AF_INET6) {
234 uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
235 result[i * 7 + 6] = Integer::NewFromUnsigned(isolate, scopeid);
236 } else {
237 result[i * 7 + 6] = no_scope_id;
238 }
239 }
240
241 uv_free_interface_addresses(interfaces, count);
242 args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
243 }
244
245
GetHomeDirectory(const FunctionCallbackInfo<Value> & args)246 static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
247 Environment* env = Environment::GetCurrent(args);
248 char buf[PATH_MAX];
249
250 size_t len = sizeof(buf);
251 const int err = uv_os_homedir(buf, &len);
252
253 if (err) {
254 CHECK_GE(args.Length(), 1);
255 env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
256 return args.GetReturnValue().SetUndefined();
257 }
258
259 Local<String> home = String::NewFromUtf8(env->isolate(),
260 buf,
261 NewStringType::kNormal,
262 len).ToLocalChecked();
263 args.GetReturnValue().Set(home);
264 }
265
266
GetUserInfo(const FunctionCallbackInfo<Value> & args)267 static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
268 Environment* env = Environment::GetCurrent(args);
269 uv_passwd_t pwd;
270 enum encoding encoding;
271
272 if (args[0]->IsObject()) {
273 Local<Object> options = args[0].As<Object>();
274 MaybeLocal<Value> maybe_encoding = options->Get(env->context(),
275 env->encoding_string());
276 Local<Value> encoding_opt;
277 if (!maybe_encoding.ToLocal(&encoding_opt))
278 return;
279
280 encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
281 } else {
282 encoding = UTF8;
283 }
284
285 const int err = uv_os_get_passwd(&pwd);
286
287 if (err) {
288 CHECK_GE(args.Length(), 2);
289 env->CollectUVExceptionInfo(args[args.Length() - 1], err,
290 "uv_os_get_passwd");
291 return args.GetReturnValue().SetUndefined();
292 }
293
294 auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); });
295
296 Local<Value> error;
297
298 Local<Value> uid = Number::New(env->isolate(), pwd.uid);
299 Local<Value> gid = Number::New(env->isolate(), pwd.gid);
300 MaybeLocal<Value> username = StringBytes::Encode(env->isolate(),
301 pwd.username,
302 encoding,
303 &error);
304 MaybeLocal<Value> homedir = StringBytes::Encode(env->isolate(),
305 pwd.homedir,
306 encoding,
307 &error);
308 MaybeLocal<Value> shell;
309
310 if (pwd.shell == nullptr)
311 shell = Null(env->isolate());
312 else
313 shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
314
315 if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
316 CHECK(!error.IsEmpty());
317 env->isolate()->ThrowException(error);
318 return;
319 }
320
321 Local<Object> entry = Object::New(env->isolate());
322
323 entry->Set(env->context(), env->uid_string(), uid).Check();
324 entry->Set(env->context(), env->gid_string(), gid).Check();
325 entry->Set(env->context(),
326 env->username_string(),
327 username.ToLocalChecked()).Check();
328 entry->Set(env->context(),
329 env->homedir_string(),
330 homedir.ToLocalChecked()).Check();
331 entry->Set(env->context(),
332 env->shell_string(),
333 shell.ToLocalChecked()).Check();
334
335 args.GetReturnValue().Set(entry);
336 }
337
338
SetPriority(const FunctionCallbackInfo<Value> & args)339 static void SetPriority(const FunctionCallbackInfo<Value>& args) {
340 Environment* env = Environment::GetCurrent(args);
341
342 CHECK_EQ(args.Length(), 3);
343 CHECK(args[0]->IsInt32());
344 CHECK(args[1]->IsInt32());
345
346 const int pid = args[0].As<Int32>()->Value();
347 const int priority = args[1].As<Int32>()->Value();
348 const int err = uv_os_setpriority(pid, priority);
349
350 if (err) {
351 CHECK(args[2]->IsObject());
352 env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
353 }
354
355 args.GetReturnValue().Set(err);
356 }
357
358
GetPriority(const FunctionCallbackInfo<Value> & args)359 static void GetPriority(const FunctionCallbackInfo<Value>& args) {
360 Environment* env = Environment::GetCurrent(args);
361
362 CHECK_EQ(args.Length(), 2);
363 CHECK(args[0]->IsInt32());
364
365 const int pid = args[0].As<Int32>()->Value();
366 int priority;
367 const int err = uv_os_getpriority(pid, &priority);
368
369 if (err) {
370 CHECK(args[1]->IsObject());
371 env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
372 return;
373 }
374
375 args.GetReturnValue().Set(priority);
376 }
377
378
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)379 void Initialize(Local<Object> target,
380 Local<Value> unused,
381 Local<Context> context,
382 void* priv) {
383 Environment* env = Environment::GetCurrent(context);
384 env->SetMethod(target, "getHostname", GetHostname);
385 env->SetMethod(target, "getLoadAvg", GetLoadAvg);
386 env->SetMethod(target, "getUptime", GetUptime);
387 env->SetMethod(target, "getTotalMem", GetTotalMemory);
388 env->SetMethod(target, "getFreeMem", GetFreeMemory);
389 env->SetMethod(target, "getCPUs", GetCPUInfo);
390 env->SetMethod(target, "getOSInformation", GetOSInformation);
391 env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);
392 env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);
393 env->SetMethod(target, "getUserInfo", GetUserInfo);
394 env->SetMethod(target, "setPriority", SetPriority);
395 env->SetMethod(target, "getPriority", GetPriority);
396 target->Set(env->context(),
397 FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
398 Boolean::New(env->isolate(), IsBigEndian())).Check();
399 }
400
401 } // namespace os
402 } // namespace node
403
404 NODE_MODULE_CONTEXT_AWARE_INTERNAL(os, node::os::Initialize)
405