GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: node_os.cc Lines: 178 204 87.3 %
Date: 2021-10-03 04:15:53 Branches: 44 96 45.8 %

Line Branch Exec Source
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
60
32
static void GetHostname(const FunctionCallbackInfo<Value>& args) {
61
32
  Environment* env = Environment::GetCurrent(args);
62
  char buf[UV_MAXHOSTNAMESIZE];
63
32
  size_t size = sizeof(buf);
64
32
  int r = uv_os_gethostname(buf, &size);
65
66
32
  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
96
  args.GetReturnValue().Set(
74
64
      String::NewFromUtf8(env->isolate(), buf).ToLocalChecked());
75
}
76
77
544
static void GetOSInformation(const FunctionCallbackInfo<Value>& args) {
78
544
  Environment* env = Environment::GetCurrent(args);
79
  uv_utsname_t info;
80
544
  int err = uv_os_uname(&info);
81
82
544
  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
544
    String::NewFromUtf8(env->isolate(), info.sysname).ToLocalChecked(),
91
544
    String::NewFromUtf8(env->isolate(), info.version).ToLocalChecked(),
92
544
    String::NewFromUtf8(env->isolate(), info.release).ToLocalChecked()
93
2176
  };
94
95
1088
  args.GetReturnValue().Set(Array::New(env->isolate(),
96
                                       osInformation,
97
                                       arraysize(osInformation)));
98
}
99
100
17
static void GetCPUInfo(const FunctionCallbackInfo<Value>& args) {
101
17
  Environment* env = Environment::GetCurrent(args);
102
17
  Isolate* isolate = env->isolate();
103
104
  uv_cpu_info_t* cpu_infos;
105
  int count;
106
107
17
  int err = uv_cpu_info(&cpu_infos, &count);
108
17
  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
17
  std::vector<Local<Value>> result;
116
17
  result.reserve(count * 7);
117
1513
  for (int i = 0; i < count; i++) {
118
1496
    uv_cpu_info_t* ci = cpu_infos + i;
119
1496
    result.emplace_back(OneByteString(isolate, ci->model));
120
1496
    result.emplace_back(Number::New(isolate, ci->speed));
121
1496
    result.emplace_back(
122
1496
        Number::New(isolate, static_cast<double>(ci->cpu_times.user)));
123
1496
    result.emplace_back(
124
1496
        Number::New(isolate, static_cast<double>(ci->cpu_times.nice)));
125
1496
    result.emplace_back(
126
1496
        Number::New(isolate, static_cast<double>(ci->cpu_times.sys)));
127
1496
    result.emplace_back(
128
1496
        Number::New(isolate, static_cast<double>(ci->cpu_times.idle)));
129
1496
    result.emplace_back(
130
2992
        Number::New(isolate, static_cast<double>(ci->cpu_times.irq)));
131
  }
132
133
17
  uv_free_cpu_info(cpu_infos, count);
134
34
  args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
135
}
136
137
138
3
static void GetFreeMemory(const FunctionCallbackInfo<Value>& args) {
139
3
  double amount = static_cast<double>(uv_get_free_memory());
140
3
  args.GetReturnValue().Set(amount);
141
3
}
142
143
144
76
static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {
145
76
  double amount = static_cast<double>(uv_get_total_memory());
146
76
  args.GetReturnValue().Set(amount);
147
76
}
148
149
150
3
static void GetUptime(const FunctionCallbackInfo<Value>& args) {
151
  double uptime;
152
3
  int err = uv_uptime(&uptime);
153
3
  if (err == 0)
154
6
    args.GetReturnValue().Set(uptime);
155
3
}
156
157
158
1
static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {
159
1
  CHECK(args[0]->IsFloat64Array());
160
2
  Local<Float64Array> array = args[0].As<Float64Array>();
161
1
  CHECK_EQ(array->Length(), 3);
162
1
  Local<ArrayBuffer> ab = array->Buffer();
163
1
  double* loadavg = static_cast<double*>(ab->GetBackingStore()->Data());
164
1
  uv_loadavg(loadavg);
165
1
}
166
167
168
107
static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {
169
107
  Environment* env = Environment::GetCurrent(args);
170
107
  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
107
  int err = uv_interface_addresses(&interfaces, &count);
179
180
107
  if (err == UV_ENOSYS)
181
    return args.GetReturnValue().SetUndefined();
182
183
107
  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
107
  Local<Value> no_scope_id = Integer::New(isolate, -1);
191
107
  std::vector<Local<Value>> result;
192
107
  result.reserve(count * 7);
193
535
  for (i = 0; i < count; i++) {
194
428
    const char* const raw_name = interfaces[i].name;
195
196
    // Use UTF-8 on both Windows and Unixes (While it may be true that UNIX
197
    // systems are somewhat encoding-agnostic here, it’s more than reasonable
198
    // to assume UTF8 as the default as well. It’s what people will expect if
199
    // they name the interface from any input that uses UTF-8, which should be
200
    // the most frequent case by far these days.)
201
428
    name = String::NewFromUtf8(isolate, raw_name).ToLocalChecked();
202
203
428
    snprintf(mac.data(),
204
             mac.size(),
205
             "%02x:%02x:%02x:%02x:%02x:%02x",
206
428
             static_cast<unsigned char>(interfaces[i].phys_addr[0]),
207
428
             static_cast<unsigned char>(interfaces[i].phys_addr[1]),
208
428
             static_cast<unsigned char>(interfaces[i].phys_addr[2]),
209
428
             static_cast<unsigned char>(interfaces[i].phys_addr[3]),
210
428
             static_cast<unsigned char>(interfaces[i].phys_addr[4]),
211
428
             static_cast<unsigned char>(interfaces[i].phys_addr[5]));
212
213
428
    if (interfaces[i].address.address4.sin_family == AF_INET) {
214
214
      uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
215
214
      uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
216
214
      family = env->ipv4_string();
217
214
    } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
218
214
      uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
219
214
      uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
220
214
      family = env->ipv6_string();
221
    } else {
222
      strncpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
223
      family = env->unknown_string();
224
    }
225
226
428
    result.emplace_back(name);
227
428
    result.emplace_back(OneByteString(isolate, ip));
228
428
    result.emplace_back(OneByteString(isolate, netmask));
229
428
    result.emplace_back(family);
230
428
    result.emplace_back(FIXED_ONE_BYTE_STRING(isolate, mac));
231
428
    result.emplace_back(
232
856
        interfaces[i].is_internal ? True(isolate) : False(isolate));
233
428
    if (interfaces[i].address.address4.sin_family == AF_INET6) {
234
214
      uint32_t scopeid = interfaces[i].address.address6.sin6_scope_id;
235
214
      result.emplace_back(Integer::NewFromUnsigned(isolate, scopeid));
236
    } else {
237
214
      result.emplace_back(no_scope_id);
238
    }
239
  }
240
241
107
  uv_free_interface_addresses(interfaces, count);
242
214
  args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size()));
243
}
244
245
246
294
static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {
247
294
  Environment* env = Environment::GetCurrent(args);
248
  char buf[PATH_MAX];
249
250
294
  size_t len = sizeof(buf);
251
294
  const int err = uv_os_homedir(buf, &len);
252
253
294
  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
294
  Local<String> home = String::NewFromUtf8(env->isolate(),
260
                                           buf,
261
                                           NewStringType::kNormal,
262
588
                                           len).ToLocalChecked();
263
588
  args.GetReturnValue().Set(home);
264
}
265
266
267
3
static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
268
3
  Environment* env = Environment::GetCurrent(args);
269
  uv_passwd_t pwd;
270
  enum encoding encoding;
271
272
3
  if (args[0]->IsObject()) {
273
4
    Local<Object> options = args[0].As<Object>();
274
    MaybeLocal<Value> maybe_encoding = options->Get(env->context(),
275
4
                                                    env->encoding_string());
276
    Local<Value> encoding_opt;
277
2
    if (!maybe_encoding.ToLocal(&encoding_opt))
278
1
        return;
279
280
1
    encoding = ParseEncoding(env->isolate(), encoding_opt, UTF8);
281
  } else {
282
1
    encoding = UTF8;
283
  }
284
285
2
  const int err = uv_os_get_passwd(&pwd);
286
287
2
  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
4
  auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); });
295
296
  Local<Value> error;
297
298
2
  Local<Value> uid = Number::New(env->isolate(), pwd.uid);
299
2
  Local<Value> gid = Number::New(env->isolate(), pwd.gid);
300
  MaybeLocal<Value> username = StringBytes::Encode(env->isolate(),
301
2
                                                   pwd.username,
302
                                                   encoding,
303
2
                                                   &error);
304
  MaybeLocal<Value> homedir = StringBytes::Encode(env->isolate(),
305
2
                                                  pwd.homedir,
306
                                                  encoding,
307
2
                                                  &error);
308
  MaybeLocal<Value> shell;
309
310
2
  if (pwd.shell == nullptr)
311
    shell = Null(env->isolate());
312
  else
313
2
    shell = StringBytes::Encode(env->isolate(), pwd.shell, encoding, &error);
314
315


6
  if (username.IsEmpty() || homedir.IsEmpty() || shell.IsEmpty()) {
316
    CHECK(!error.IsEmpty());
317
    env->isolate()->ThrowException(error);
318
    return;
319
  }
320
321
2
  Local<Object> entry = Object::New(env->isolate());
322
323
6
  entry->Set(env->context(), env->uid_string(), uid).Check();
324
6
  entry->Set(env->context(), env->gid_string(), gid).Check();
325
2
  entry->Set(env->context(),
326
             env->username_string(),
327
8
             username.ToLocalChecked()).Check();
328
2
  entry->Set(env->context(),
329
             env->homedir_string(),
330
8
             homedir.ToLocalChecked()).Check();
331
2
  entry->Set(env->context(),
332
             env->shell_string(),
333
8
             shell.ToLocalChecked()).Check();
334
335
4
  args.GetReturnValue().Set(entry);
336
}
337
338
339
80
static void SetPriority(const FunctionCallbackInfo<Value>& args) {
340
80
  Environment* env = Environment::GetCurrent(args);
341
342
80
  CHECK_EQ(args.Length(), 3);
343
80
  CHECK(args[0]->IsInt32());
344
80
  CHECK(args[1]->IsInt32());
345
346
160
  const int pid = args[0].As<Int32>()->Value();
347
160
  const int priority = args[1].As<Int32>()->Value();
348
80
  const int err = uv_os_setpriority(pid, priority);
349
350
80
  if (err) {
351
20
    CHECK(args[2]->IsObject());
352
20
    env->CollectUVExceptionInfo(args[2], err, "uv_os_setpriority");
353
  }
354
355
80
  args.GetReturnValue().Set(err);
356
80
}
357
358
359
60
static void GetPriority(const FunctionCallbackInfo<Value>& args) {
360
60
  Environment* env = Environment::GetCurrent(args);
361
362
60
  CHECK_EQ(args.Length(), 2);
363
60
  CHECK(args[0]->IsInt32());
364
365
120
  const int pid = args[0].As<Int32>()->Value();
366
  int priority;
367
60
  const int err = uv_os_getpriority(pid, &priority);
368
369
60
  if (err) {
370
    CHECK(args[1]->IsObject());
371
    env->CollectUVExceptionInfo(args[1], err, "uv_os_getpriority");
372
    return;
373
  }
374
375
120
  args.GetReturnValue().Set(priority);
376
}
377
378
379
545
void Initialize(Local<Object> target,
380
                Local<Value> unused,
381
                Local<Context> context,
382
                void* priv) {
383
545
  Environment* env = Environment::GetCurrent(context);
384
545
  env->SetMethod(target, "getHostname", GetHostname);
385
545
  env->SetMethod(target, "getLoadAvg", GetLoadAvg);
386
545
  env->SetMethod(target, "getUptime", GetUptime);
387
545
  env->SetMethod(target, "getTotalMem", GetTotalMemory);
388
545
  env->SetMethod(target, "getFreeMem", GetFreeMemory);
389
545
  env->SetMethod(target, "getCPUs", GetCPUInfo);
390
545
  env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);
391
545
  env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);
392
545
  env->SetMethod(target, "getUserInfo", GetUserInfo);
393
545
  env->SetMethod(target, "setPriority", SetPriority);
394
545
  env->SetMethod(target, "getPriority", GetPriority);
395
545
  env->SetMethod(target, "getOSInformation", GetOSInformation);
396
545
  target->Set(env->context(),
397
              FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),
398
1635
              Boolean::New(env->isolate(), IsBigEndian())).Check();
399
545
}
400
401
}  // namespace os
402
}  // namespace node
403
404
4926
NODE_MODULE_CONTEXT_AWARE_INTERNAL(os, node::os::Initialize)