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 "node_internals.h"
23 #include "string_bytes.h"
24 
25 #include <array>
26 #include <errno.h>
27 #include <string.h>
28 
29 #ifdef __MINGW32__
30 # include <io.h>
31 #endif  // __MINGW32__
32 
33 #ifdef __POSIX__
34 # include <limits.h>        // PATH_MAX on Solaris.
35 # include <netdb.h>         // MAXHOSTNAMELEN on Solaris.
36 # include <unistd.h>        // gethostname, sysconf
37 # include <sys/param.h>     // MAXHOSTNAMELEN on Linux and the BSDs.
38 # include <sys/utsname.h>
39 #endif  // __POSIX__
40 
41 // Add Windows fallback.
42 #ifndef MAXHOSTNAMELEN
43 # define MAXHOSTNAMELEN 256
44 #endif  // MAXHOSTNAMELEN
45 
46 namespace node {
47 namespace os {
48 
49 using v8::Array;
50 using v8::ArrayBuffer;
51 using v8::Boolean;
52 using v8::Context;
53 using v8::Float64Array;
54 using v8::Function;
55 using v8::FunctionCallbackInfo;
56 using v8::Int32;
57 using v8::Integer;
58 using v8::Local;
59 using v8::MaybeLocal;
60 using v8::Null;
61 using v8::Number;
62 using v8::Object;
63 using v8::String;
64 using v8::Value;
65 
66 
GetHostname(const FunctionCallbackInfo<Value> & args)67 static void GetHostname(const FunctionCallbackInfo<Value>& args) {
68   Environment* env = Environment::GetCurrent(args);
69   char buf[MAXHOSTNAMELEN + 1];
70   size_t size = sizeof(buf);
71   int r = uv_os_gethostname(buf, &size);
72 
73   if (r != 0) {
74     CHECK_GE(args.Length(), 1);
75     env->CollectUVExceptionInfo(args[args.Length() - 1], r,
76                                 "uv_os_gethostname");
77     return args.GetReturnValue().SetUndefined();
78   }
79 
80   args.GetReturnValue().Set(OneByteString(env->isolate(), buf));
81 }
82 
83 
GetOSType(const FunctionCallbackInfo<Value> & args)84 static void GetOSType(const FunctionCallbackInfo<Value>& args) {
85   Environment* env = Environment::GetCurrent(args);
86   const char* rval;
87 
88 #ifdef __POSIX__
89   struct utsname info;
90   if (uname(&info) < 0) {
91     CHECK_GE(args.Length(), 1);
92     env->CollectExceptionInfo(args[args.Length() - 1], errno, "uname");
93     return args.GetReturnValue().SetUndefined();
94   }
95   rval = info.sysname;
96 #else  // __MINGW32__
97   rval = "Windows_NT";
98 #endif  // __POSIX__
99 
100   args.GetReturnValue().Set(OneByteString(env->isolate(), rval));
101 }
102 
103 
GetOSRelease(const FunctionCallbackInfo<Value> & args)104 static void GetOSRelease(const FunctionCallbackInfo<Value>& args) {
105   Environment* env = Environment::GetCurrent(args);
106   uv_utsname_t info;
107   int err = uv_os_uname(&info);
108 
109   if (err != 0) {
110     CHECK_GE(args.Length(), 1);
111     env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_uname");
112     return args.GetReturnValue().SetUndefined();
113   }
114 
115   args.GetReturnValue().Set(OneByteString(env->isolate(), info.release));
116 }
117 
118 
GetCPUInfo(const FunctionCallbackInfo<Value> & args)119 static void GetCPUInfo(const FunctionCallbackInfo<Value>& args) {
120   Environment* env = Environment::GetCurrent(args);
121   uv_cpu_info_t* cpu_infos;
122   int count, i, field_idx;
123 
124   int err = uv_cpu_info(&cpu_infos, &count);
125   if (err)
126     return;
127 
128   CHECK(args[0]->IsFunction());
129   Local<Function> addfn = args[0].As<Function>();
130 
131   CHECK(args[1]->IsFloat64Array());
132   Local<Float64Array> array = args[1].As<Float64Array>();
133   CHECK_EQ(array->Length(), 6 * NODE_PUSH_VAL_TO_ARRAY_MAX);
134   Local<ArrayBuffer> ab = array->Buffer();
135   double* fields = static_cast<double*>(ab->GetContents().Data());
136 
137   CHECK(args[2]->IsArray());
138   Local<Array> cpus = args[2].As<Array>();
139 
140   Local<Value> model_argv[NODE_PUSH_VAL_TO_ARRAY_MAX];
141   int model_idx = 0;
142 
143   for (i = 0, field_idx = 0; i < count; i++) {
144     uv_cpu_info_t* ci = cpu_infos + i;
145 
146     fields[field_idx++] = ci->speed;
147     fields[field_idx++] = ci->cpu_times.user;
148     fields[field_idx++] = ci->cpu_times.nice;
149     fields[field_idx++] = ci->cpu_times.sys;
150     fields[field_idx++] = ci->cpu_times.idle;
151     fields[field_idx++] = ci->cpu_times.irq;
152     model_argv[model_idx++] = OneByteString(env->isolate(), ci->model);
153 
154     if (model_idx >= NODE_PUSH_VAL_TO_ARRAY_MAX) {
155       addfn->Call(env->context(), cpus, model_idx, model_argv).ToLocalChecked();
156       model_idx = 0;
157       field_idx = 0;
158     }
159   }
160 
161   if (model_idx > 0) {
162     addfn->Call(env->context(), cpus, model_idx, model_argv).ToLocalChecked();
163   }
164 
165   uv_free_cpu_info(cpu_infos, count);
166   args.GetReturnValue().Set(cpus);
167 }
168 
169 
GetFreeMemory(const FunctionCallbackInfo<Value> & args)170 static void GetFreeMemory(const FunctionCallbackInfo<Value>& args) {
171   double amount = uv_get_free_memory();
172   if (amount < 0)
173     return;
174   args.GetReturnValue().Set(amount);
175 }
176 
177 
GetTotalMemory(const FunctionCallbackInfo<Value> & args)178 static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
179   double amount = uv_get_total_memory();
180   if (amount < 0)
181     return;
182   args.GetReturnValue().Set(amount);
183 }
184 
185 
GetUptime(const FunctionCallbackInfo<Value> & args)186 static void GetUptime(const FunctionCallbackInfo<Value>& args) {
187   double uptime;
188   int err = uv_uptime(&uptime);
189   if (err == 0)
190     args.GetReturnValue().Set(uptime);
191 }
192 
193 
GetLoadAvg(const FunctionCallbackInfo<Value> & args)194 static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {
195   CHECK(args[0]->IsFloat64Array());
196   Local<Float64Array> array = args[0].As<Float64Array>();
197   CHECK_EQ(array->Length(), 3);
198   Local<ArrayBuffer> ab = array->Buffer();
199   double* loadavg = static_cast<double*>(ab->GetContents().Data());
200   uv_loadavg(loadavg);
201 }
202 
203 
GetInterfaceAddresses(const FunctionCallbackInfo<Value> & args)204 static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {
205   Environment* env = Environment::GetCurrent(args);
206   uv_interface_address_t* interfaces;
207   int count, i;
208   char ip[INET6_ADDRSTRLEN];
209   char netmask[INET6_ADDRSTRLEN];
210   std::array<char, 18> mac;
211   Local<Object> ret, o;
212   Local<String> name, family;
213   Local<Array> ifarr;
214 
215   int err = uv_interface_addresses(&interfaces, &count);
216 
217   ret = Object::New(env->isolate());
218 
219   if (err == UV_ENOSYS) {
220     return args.GetReturnValue().Set(ret);
221   } else if (err) {
222     CHECK_GE(args.Length(), 1);
223     env->CollectUVExceptionInfo(args[args.Length() - 1], errno,
224                                 "uv_interface_addresses");
225     return args.GetReturnValue().SetUndefined();
226   }
227 
228   for (i = 0; i < count; i++) {
229     const char* const raw_name = interfaces[i].name;
230 
231     // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX
232     // systems are somewhat encoding-agnostic here, it’s more than reasonable
233     // to assume UTF8 as the default as well. It’s what people will expect if
234     // they name the interface from any input that uses UTF-8, which should be
235     // the most frequent case by far these days.)
236     name = String::NewFromUtf8(env->isolate(), raw_name,
237         v8::NewStringType::kNormal).ToLocalChecked();
238 
239     if (ret->Has(env->context(), name).FromJust()) {
240       ifarr = Local<Array>::Cast(ret->Get(name));
241     } else {
242       ifarr = Array::New(env->isolate());
243       ret->Set(name, ifarr);
244     }
245 
246     snprintf(mac.data(),
247              mac.size(),
248              "%02x:%02x:%02x:%02x:%02x:%02x",
249              static_cast<unsigned char>(interfaces[i].phys_addr[0]),
250              static_cast<unsigned char>(interfaces[i].phys_addr[1]),
251              static_cast<unsigned char>(interfaces[i].phys_addr[2]),
252              static_cast<unsigned char>(interfaces[i].phys_addr[3]),
253              static_cast<unsigned char>(interfaces[i].phys_addr[4]),
254              static_cast<unsigned char>(interfaces[i].phys_addr[5]));
255 
256     if (interfaces[i].address.address4.sin_family == AF_INET) {
257       uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
258       uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
259       family = env->ipv4_string();
260     } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
261       uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
262       uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
263       family = env->ipv6_string();
264     } else {
265       strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
266       family = env->unknown_string();
267     }
268 
269     o = Object::New(env->isolate());
270     o->Set(env->address_string(), OneByteString(env->isolate(), ip));
271     o->Set(env->netmask_string(), OneByteString(env->isolate(), netmask));
272     o->Set(env->family_string(), family);
273     o->Set(env->mac_string(), FIXED_ONE_BYTE_STRING(env->isolate(), mac));
274 
275     if (interfaces[i].address.address4.sin_family == AF_INET6) {
276       uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
277       o->Set(env->scopeid_string(),
278              Integer::NewFromUnsigned(env->isolate(), scopeid));
279     }
280 
281     const bool internal = interfaces[i].is_internal;
282     o->Set(env->internal_string(),
283            internal ? True(env->isolate()) : False(env->isolate()));
284 
285     ifarr->Set(ifarr->Length(), o);
286   }
287 
288   uv_free_interface_addresses(interfaces, count);
289   args.GetReturnValue().Set(ret);
290 }
291 
292 
GetHomeDirectory(const FunctionCallbackInfo<Value> & args)293 static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
294   Environment* env = Environment::GetCurrent(args);
295   char buf[PATH_MAX];
296 
297   size_t len = sizeof(buf);
298   const int err = uv_os_homedir(buf, &len);
299 
300   if (err) {
301     CHECK_GE(args.Length(), 1);
302     env->CollectUVExceptionInfo(args[args.Length() - 1], err, "uv_os_homedir");
303     return args.GetReturnValue().SetUndefined();
304   }
305 
306   Local<String> home = String::NewFromUtf8(env->isolate(),
307                                            buf,
308                                            v8::NewStringType::kNormal,
309                                            len).ToLocalChecked();
310   args.GetReturnValue().Set(home);
311 }
312 
313 
GetUserInfo(const FunctionCallbackInfo<Value> & args)314 static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
315   Environment* env = Environment::GetCurrent(args);
316   uv_passwd_t pwd;
317   enum encoding encoding;
318 
319   if (args[0]->IsObject()) {
320     Local<Object> options = args[0].As<Object>();
321     MaybeLocal<Value> maybe_encoding = options->Get(env->context(),
322                                                     env->encoding_string());
323     if (maybe_encoding.IsEmpty())
324       return;
325 
326     Local<Value> encoding_opt = maybe_encoding.ToLocalChecked();
327     encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
328   } else {
329     encoding = UTF8;
330   }
331 
332   const int err = uv_os_get_passwd(&pwd);
333 
334   if (err) {
335     CHECK_GE(args.Length(), 2);
336     env->CollectUVExceptionInfo(args[args.Length() - 1], err,
337                                 "uv_os_get_passwd");
338     return args.GetReturnValue().SetUndefined();
339   }
340 
341   OnScopeLeave free_passwd([&]() { uv_os_free_passwd(&pwd); });
342 
343   Local<Value> error;
344 
345   Local<Value> uid = Number::New(env->isolate(), pwd.uid);
346   Local<Value> gid = Number::New(env->isolate(), pwd.gid);
347   MaybeLocal<Value> username = StringBytes::Encode(env->isolate(),
348                                                    pwd.username,
349                                                    encoding,
350                                                    &error);
351   MaybeLocal<Value> homedir = StringBytes::Encode(env->isolate(),
352                                                   pwd.homedir,
353                                                   encoding,
354                                                   &error);
355   MaybeLocal<Value> shell;
356 
357   if (pwd.shell == nullptr)
358     shell = Null(env->isolate());
359   else
360     shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
361 
362   if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
363     CHECK(!error.IsEmpty());
364     env->isolate()->ThrowException(error);
365     return;
366   }
367 
368   Local<Object> entry = Object::New(env->isolate());
369 
370   entry->Set(env->uid_string(), uid);
371   entry->Set(env->gid_string(), gid);
372   entry->Set(env->username_string(), username.ToLocalChecked());
373   entry->Set(env->homedir_string(), homedir.ToLocalChecked());
374   entry->Set(env->shell_string(), shell.ToLocalChecked());
375 
376   args.GetReturnValue().Set(entry);
377 }
378 
379 
SetPriority(const FunctionCallbackInfo<Value> & args)380 static void SetPriority(const FunctionCallbackInfo<Value>& args) {
381   Environment* env = Environment::GetCurrent(args);
382 
383   CHECK_EQ(args.Length(), 3);
384   CHECK(args[0]->IsInt32());
385   CHECK(args[1]->IsInt32());
386 
387   const int pid = args[0].As<Int32>()->Value();
388   const int priority = args[1].As<Int32>()->Value();
389   const int err = uv_os_setpriority(pid, priority);
390 
391   if (err) {
392     CHECK(args[2]->IsObject());
393     env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
394   }
395 
396   args.GetReturnValue().Set(err);
397 }
398 
399 
GetPriority(const FunctionCallbackInfo<Value> & args)400 static void GetPriority(const FunctionCallbackInfo<Value>& args) {
401   Environment* env = Environment::GetCurrent(args);
402 
403   CHECK_EQ(args.Length(), 2);
404   CHECK(args[0]->IsInt32());
405 
406   const int pid = args[0].As<Int32>()->Value();
407   int priority;
408   const int err = uv_os_getpriority(pid, &priority);
409 
410   if (err) {
411     CHECK(args[1]->IsObject());
412     env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
413     return;
414   }
415 
416   args.GetReturnValue().Set(priority);
417 }
418 
419 
Initialize(Local<Object> target,Local<Value> unused,Local<Context> context)420 void Initialize(Local<Object> target,
421                 Local<Value> unused,
422                 Local<Context> context) {
423   Environment* env = Environment::GetCurrent(context);
424   env->SetMethod(target, "getHostname", GetHostname);
425   env->SetMethod(target, "getLoadAvg", GetLoadAvg);
426   env->SetMethod(target, "getUptime", GetUptime);
427   env->SetMethod(target, "getTotalMem", GetTotalMemory);
428   env->SetMethod(target, "getFreeMem", GetFreeMemory);
429   env->SetMethod(target, "getCPUs", GetCPUInfo);
430   env->SetMethod(target, "getOSType", GetOSType);
431   env->SetMethod(target, "getOSRelease", GetOSRelease);
432   env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);
433   env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);
434   env->SetMethod(target, "getUserInfo", GetUserInfo);
435   env->SetMethod(target, "setPriority", SetPriority);
436   env->SetMethod(target, "getPriority", GetPriority);
437   target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
438               Boolean::New(env->isolate(), IsBigEndian()));
439 }
440 
441 }  // namespace os
442 }  // namespace node
443 
444 NODE_BUILTIN_MODULE_CONTEXT_AWARE(os, node::os::Initialize)
445