• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "node_external_reference.h"
24 #include "string_bytes.h"
25 
26 #ifdef __MINGW32__
27 # include <io.h>
28 #endif  // __MINGW32__
29 
30 #ifdef __POSIX__
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, machine]
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       String::NewFromUtf8(env->isolate(), info.machine).ToLocalChecked()};
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(
122         Number::New(isolate, static_cast<double>(ci->cpu_times.user)));
123     result.emplace_back(
124         Number::New(isolate, static_cast<double>(ci->cpu_times.nice)));
125     result.emplace_back(
126         Number::New(isolate, static_cast<double>(ci->cpu_times.sys)));
127     result.emplace_back(
128         Number::New(isolate, static_cast<double>(ci->cpu_times.idle)));
129     result.emplace_back(
130         Number::New(isolate, static_cast<double>(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 = static_cast<double>(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 = static_cast<double>(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   Environment* env = Environment::GetCurrent(args);
152   double uptime;
153   int err = uv_uptime(&uptime);
154   if (err != 0) {
155     env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_uptime");
156     return args.GetReturnValue().SetUndefined();
157   }
158 
159   args.GetReturnValue().Set(uptime);
160 }
161 
162 
GetLoadAvg(const FunctionCallbackInfo<Value> & args)163 static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {
164   CHECK(args[0]->IsFloat64Array());
165   Local<Float64Array> array = args[0].As<Float64Array>();
166   CHECK_EQ(array->Length(), 3);
167   Local<ArrayBuffer> ab = array->Buffer();
168   double* loadavg = static_cast<double*>(ab->Data());
169   uv_loadavg(loadavg);
170 }
171 
172 
GetInterfaceAddresses(const FunctionCallbackInfo<Value> & args)173 static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {
174   Environment* env = Environment::GetCurrent(args);
175   Isolate* isolate = env->isolate();
176   uv_interface_address_t* interfaces;
177   int count, i;
178   char ip[INET6_ADDRSTRLEN];
179   char netmask[INET6_ADDRSTRLEN];
180   std::array<char, 18> mac;
181   Local<String> name, family;
182 
183   int err = uv_interface_addresses(&interfaces, &count);
184 
185   if (err == UV_ENOSYS)
186     return args.GetReturnValue().SetUndefined();
187 
188   if (err) {
189     CHECK_GE(args.Length(), 1);
190     env->CollectUVExceptionInfo(args[args.Length() - 1], errno,
191                                 "uv_interface_addresses");
192     return args.GetReturnValue().SetUndefined();
193   }
194 
195   Local<Value> no_scope_id = Integer::New(isolate, -1);
196   std::vector<Local<Value>> result;
197   result.reserve(count * 7);
198   for (i = 0; i < count; i++) {
199     const char* const raw_name = interfaces[i].name;
200 
201     // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX
202     // systems are somewhat encoding-agnostic here, it’s more than reasonable
203     // to assume UTF8 as the default as well. It’s what people will expect if
204     // they name the interface from any input that uses UTF-8, which should be
205     // the most frequent case by far these days.)
206     name = String::NewFromUtf8(isolate, raw_name).ToLocalChecked();
207 
208     snprintf(mac.data(),
209              mac.size(),
210              "%02x:%02x:%02x:%02x:%02x:%02x",
211              static_cast<unsigned char>(interfaces[i].phys_addr[0]),
212              static_cast<unsigned char>(interfaces[i].phys_addr[1]),
213              static_cast<unsigned char>(interfaces[i].phys_addr[2]),
214              static_cast<unsigned char>(interfaces[i].phys_addr[3]),
215              static_cast<unsigned char>(interfaces[i].phys_addr[4]),
216              static_cast<unsigned char>(interfaces[i].phys_addr[5]));
217 
218     if (interfaces[i].address.address4.sin_family == AF_INET) {
219       uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
220       uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
221       family = env->ipv4_string();
222     } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
223       uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
224       uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
225       family = env->ipv6_string();
226     } else {
227       strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
228       family = env->unknown_string();
229     }
230 
231     result.emplace_back(name);
232     result.emplace_back(OneByteString(isolate, ip));
233     result.emplace_back(OneByteString(isolate, netmask));
234     result.emplace_back(family);
235     result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac));
236     result.emplace_back(
237         Boolean::New(env->isolate(), interfaces[i].is_internal));
238     if (interfaces[i].address.address4.sin_family == AF_INET6) {
239       uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
240       result.emplace_back(Integer::NewFromUnsigned(isolate, scopeid));
241     } else {
242       result.emplace_back(no_scope_id);
243     }
244   }
245 
246   uv_free_interface_addresses(interfaces, count);
247   args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
248 }
249 
250 
GetHomeDirectory(const FunctionCallbackInfo<Value> & args)251 static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
252   Environment* env = Environment::GetCurrent(args);
253   char buf[PATH_MAX];
254 
255   size_t len = sizeof(buf);
256   const int err = uv_os_homedir(buf, &len);
257 
258   if (err) {
259     CHECK_GE(args.Length(), 1);
260     env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
261     return args.GetReturnValue().SetUndefined();
262   }
263 
264   Local<String> home = String::NewFromUtf8(env->isolate(),
265                                            buf,
266                                            NewStringType::kNormal,
267                                            len).ToLocalChecked();
268   args.GetReturnValue().Set(home);
269 }
270 
271 
GetUserInfo(const FunctionCallbackInfo<Value> & args)272 static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
273   Environment* env = Environment::GetCurrent(args);
274   uv_passwd_t pwd;
275   enum encoding encoding;
276 
277   if (args[0]->IsObject()) {
278     Local<Object> options = args[0].As<Object>();
279     MaybeLocal<Value> maybe_encoding = options->Get(env->context(),
280                                                     env->encoding_string());
281     Local<Value> encoding_opt;
282     if (!maybe_encoding.ToLocal(&encoding_opt))
283         return;
284 
285     encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
286   } else {
287     encoding = UTF8;
288   }
289 
290   const int err = uv_os_get_passwd(&pwd);
291 
292   if (err) {
293     CHECK_GE(args.Length(), 2);
294     env->CollectUVExceptionInfo(args[args.Length() - 1], err,
295                                 "uv_os_get_passwd");
296     return args.GetReturnValue().SetUndefined();
297   }
298 
299   auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); });
300 
301   Local<Value> error;
302 
303   Local<Value> uid = Number::New(env->isolate(), pwd.uid);
304   Local<Value> gid = Number::New(env->isolate(), pwd.gid);
305   MaybeLocal<Value> username = StringBytes::Encode(env->isolate(),
306                                                    pwd.username,
307                                                    encoding,
308                                                    &error);
309   MaybeLocal<Value> homedir = StringBytes::Encode(env->isolate(),
310                                                   pwd.homedir,
311                                                   encoding,
312                                                   &error);
313   MaybeLocal<Value> shell;
314 
315   if (pwd.shell == nullptr)
316     shell = Null(env->isolate());
317   else
318     shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
319 
320   if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
321     CHECK(!error.IsEmpty());
322     env->isolate()->ThrowException(error);
323     return;
324   }
325 
326   Local<Object> entry = Object::New(env->isolate());
327 
328   entry->Set(env->context(), env->uid_string(), uid).Check();
329   entry->Set(env->context(), env->gid_string(), gid).Check();
330   entry->Set(env->context(),
331              env->username_string(),
332              username.ToLocalChecked()).Check();
333   entry->Set(env->context(),
334              env->homedir_string(),
335              homedir.ToLocalChecked()).Check();
336   entry->Set(env->context(),
337              env->shell_string(),
338              shell.ToLocalChecked()).Check();
339 
340   args.GetReturnValue().Set(entry);
341 }
342 
343 
SetPriority(const FunctionCallbackInfo<Value> & args)344 static void SetPriority(const FunctionCallbackInfo<Value>& args) {
345   Environment* env = Environment::GetCurrent(args);
346 
347   CHECK_EQ(args.Length(), 3);
348   CHECK(args[0]->IsInt32());
349   CHECK(args[1]->IsInt32());
350 
351   const int pid = args[0].As<Int32>()->Value();
352   const int priority = args[1].As<Int32>()->Value();
353   const int err = uv_os_setpriority(pid, priority);
354 
355   if (err) {
356     CHECK(args[2]->IsObject());
357     env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
358   }
359 
360   args.GetReturnValue().Set(err);
361 }
362 
363 
GetPriority(const FunctionCallbackInfo<Value> & args)364 static void GetPriority(const FunctionCallbackInfo<Value>& args) {
365   Environment* env = Environment::GetCurrent(args);
366 
367   CHECK_EQ(args.Length(), 2);
368   CHECK(args[0]->IsInt32());
369 
370   const int pid = args[0].As<Int32>()->Value();
371   int priority;
372   const int err = uv_os_getpriority(pid, &priority);
373 
374   if (err) {
375     CHECK(args[1]->IsObject());
376     env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
377     return;
378   }
379 
380   args.GetReturnValue().Set(priority);
381 }
382 
GetAvailableParallelism(const FunctionCallbackInfo<Value> & args)383 static void GetAvailableParallelism(const FunctionCallbackInfo<Value>& args) {
384   unsigned int parallelism = uv_available_parallelism();
385   args.GetReturnValue().Set(parallelism);
386 }
387 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context,void * priv)388 void Initialize(Local<Object> target,
389                 Local<Value> unused,
390                 Local<Context> context,
391                 void* priv) {
392   Environment* env = Environment::GetCurrent(context);
393   SetMethod(context, target, "getHostname", GetHostname);
394   SetMethod(context, target, "getLoadAvg", GetLoadAvg);
395   SetMethod(context, target, "getUptime", GetUptime);
396   SetMethod(context, target, "getTotalMem", GetTotalMemory);
397   SetMethod(context, target, "getFreeMem", GetFreeMemory);
398   SetMethod(context, target, "getCPUs", GetCPUInfo);
399   SetMethod(context, target, "getInterfaceAddresses", GetInterfaceAddresses);
400   SetMethod(context, target, "getHomeDirectory", GetHomeDirectory);
401   SetMethod(context, target, "getUserInfo", GetUserInfo);
402   SetMethod(context, target, "setPriority", SetPriority);
403   SetMethod(context, target, "getPriority", GetPriority);
404   SetMethod(
405       context, target, "getAvailableParallelism", GetAvailableParallelism);
406   SetMethod(context, target, "getOSInformation", GetOSInformation);
407   target
408       ->Set(context,
409             FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
410             Boolean::New(env->isolate(), IsBigEndian()))
411       .Check();
412 }
413 
RegisterExternalReferences(ExternalReferenceRegistry * registry)414 void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
415   registry->Register(GetHostname);
416   registry->Register(GetLoadAvg);
417   registry->Register(GetUptime);
418   registry->Register(GetTotalMemory);
419   registry->Register(GetFreeMemory);
420   registry->Register(GetCPUInfo);
421   registry->Register(GetInterfaceAddresses);
422   registry->Register(GetHomeDirectory);
423   registry->Register(GetUserInfo);
424   registry->Register(SetPriority);
425   registry->Register(GetPriority);
426   registry->Register(GetAvailableParallelism);
427   registry->Register(GetOSInformation);
428 }
429 
430 }  // namespace os
431 }  // namespace node
432 
433 NODE_BINDING_CONTEXT_AWARE_INTERNAL(os, node::os::Initialize)
434 NODE_BINDING_EXTERNAL_REFERENCE(os, node::os::RegisterExternalReferences)
435