• 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 "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