|
160
|
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
2 *
|
|
|
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
4 * of this software and associated documentation files (the "Software"), to
|
|
|
5 * deal in the Software without restriction, including without limitation the
|
|
|
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
7 * sell copies of the Software, and to permit persons to whom the Software is
|
|
|
8 * furnished to do so, subject to the following conditions:
|
|
|
9 *
|
|
|
10 * The above copyright notice and this permission notice shall be included in
|
|
|
11 * all copies or substantial portions of the Software.
|
|
|
12 *
|
|
|
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
19 * IN THE SOFTWARE.
|
|
|
20 */
|
|
|
21
|
|
|
22 #include <assert.h>
|
|
|
23 #include <direct.h>
|
|
|
24 #include <limits.h>
|
|
|
25 #include <stdio.h>
|
|
|
26 #include <string.h>
|
|
|
27 #include <time.h>
|
|
|
28 #include <wchar.h>
|
|
|
29
|
|
|
30 #include "uv.h"
|
|
|
31 #include "internal.h"
|
|
|
32
|
|
|
33 /* clang-format off */
|
|
|
34 #include <sysinfoapi.h>
|
|
|
35 #include <winsock2.h>
|
|
|
36 #include <winperf.h>
|
|
|
37 #include <iphlpapi.h>
|
|
|
38 #include <psapi.h>
|
|
|
39 #include <tlhelp32.h>
|
|
|
40 #include <windows.h>
|
|
|
41 /* clang-format on */
|
|
|
42 #include <userenv.h>
|
|
|
43 #include <math.h>
|
|
|
44
|
|
|
45 /*
|
|
|
46 * Max title length; the only thing MSDN tells us about the maximum length
|
|
|
47 * of the console title is that it is smaller than 64K. However in practice
|
|
|
48 * it is much smaller, and there is no way to figure out what the exact length
|
|
|
49 * of the title is or can be, at least not on XP. To make it even more
|
|
|
50 * annoying, GetConsoleTitle fails when the buffer to be read into is bigger
|
|
|
51 * than the actual maximum length. So we make a conservative guess here;
|
|
|
52 * just don't put the novel you're writing in the title, unless the plot
|
|
|
53 * survives truncation.
|
|
|
54 */
|
|
|
55 #define MAX_TITLE_LENGTH 8192
|
|
|
56
|
|
|
57 /* The number of nanoseconds in one second. */
|
|
|
58 #define UV__NANOSEC 1000000000
|
|
|
59
|
|
|
60 /* Max user name length, from iphlpapi.h */
|
|
|
61 #ifndef UNLEN
|
|
|
62 # define UNLEN 256
|
|
|
63 #endif
|
|
|
64
|
|
|
65
|
|
|
66 /* A RtlGenRandom() by any other name... */
|
|
|
67 extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
|
|
|
68
|
|
|
69 /* Cached copy of the process title, plus a mutex guarding it. */
|
|
|
70 static char *process_title;
|
|
|
71 static CRITICAL_SECTION process_title_lock;
|
|
|
72
|
|
|
73 /* Frequency of the high-resolution clock. */
|
|
|
74 static uint64_t hrtime_frequency_ = 0;
|
|
|
75
|
|
|
76
|
|
|
77 /*
|
|
|
78 * One-time initialization code for functionality defined in util.c.
|
|
|
79 */
|
|
|
80 void uv__util_init(void) {
|
|
|
81 LARGE_INTEGER perf_frequency;
|
|
|
82
|
|
|
83 /* Initialize process title access mutex. */
|
|
|
84 InitializeCriticalSection(&process_title_lock);
|
|
|
85
|
|
|
86 /* Retrieve high-resolution timer frequency
|
|
|
87 * and precompute its reciprocal.
|
|
|
88 */
|
|
|
89 if (QueryPerformanceFrequency(&perf_frequency)) {
|
|
|
90 hrtime_frequency_ = perf_frequency.QuadPart;
|
|
|
91 } else {
|
|
|
92 uv_fatal_error(GetLastError(), "QueryPerformanceFrequency");
|
|
|
93 }
|
|
|
94 }
|
|
|
95
|
|
|
96
|
|
|
97 int uv_exepath(char* buffer, size_t* size_ptr) {
|
|
|
98 size_t utf8_len, utf16_buffer_len, utf16_len;
|
|
|
99 WCHAR* utf16_buffer;
|
|
|
100 int err;
|
|
|
101
|
|
|
102 if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) {
|
|
|
103 return UV_EINVAL;
|
|
|
104 }
|
|
|
105
|
|
|
106 if (*size_ptr > 32768) {
|
|
|
107 /* Windows paths can never be longer than this. */
|
|
|
108 utf16_buffer_len = 32768;
|
|
|
109 } else {
|
|
|
110 utf16_buffer_len = (int) *size_ptr;
|
|
|
111 }
|
|
|
112
|
|
|
113 utf16_buffer = (WCHAR*) uv__malloc(sizeof(WCHAR) * utf16_buffer_len);
|
|
|
114 if (!utf16_buffer) {
|
|
|
115 return UV_ENOMEM;
|
|
|
116 }
|
|
|
117
|
|
|
118 /* Get the path as UTF-16. */
|
|
|
119 utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len);
|
|
|
120 if (utf16_len <= 0) {
|
|
|
121 err = GetLastError();
|
|
|
122 goto error;
|
|
|
123 }
|
|
|
124
|
|
|
125 /* Convert to UTF-8 */
|
|
|
126 utf8_len = *size_ptr - 1; /* Reserve space for NUL */
|
|
|
127 err = uv_utf16_to_wtf8(utf16_buffer, utf16_len, &buffer, &utf8_len);
|
|
|
128 if (err == UV_ENOBUFS) {
|
|
|
129 utf8_len = *size_ptr - 1;
|
|
|
130 err = 0;
|
|
|
131 }
|
|
|
132 *size_ptr = utf8_len;
|
|
|
133
|
|
|
134 uv__free(utf16_buffer);
|
|
|
135
|
|
|
136 return err;
|
|
|
137
|
|
|
138 error:
|
|
|
139 uv__free(utf16_buffer);
|
|
|
140 return uv_translate_sys_error(err);
|
|
|
141 }
|
|
|
142
|
|
|
143
|
|
|
144 static int uv__cwd(WCHAR** buf, DWORD *len) {
|
|
|
145 WCHAR* p;
|
|
|
146 DWORD n;
|
|
|
147 DWORD t;
|
|
|
148
|
|
|
149 t = GetCurrentDirectoryW(0, NULL);
|
|
|
150 for (;;) {
|
|
|
151 if (t == 0)
|
|
|
152 return uv_translate_sys_error(GetLastError());
|
|
|
153
|
|
|
154 /* |t| is the size of the buffer _including_ nul. */
|
|
|
155 p = uv__malloc(t * sizeof(*p));
|
|
|
156 if (p == NULL)
|
|
|
157 return UV_ENOMEM;
|
|
|
158
|
|
|
159 /* |n| is the size of the buffer _excluding_ nul but _only on success_.
|
|
|
160 * If |t| was too small because another thread changed the working
|
|
|
161 * directory, |n| is the size the buffer should be _including_ nul.
|
|
|
162 * It therefore follows we must resize when n >= t and fail when n == 0.
|
|
|
163 */
|
|
|
164 n = GetCurrentDirectoryW(t, p);
|
|
|
165 if (n > 0)
|
|
|
166 if (n < t)
|
|
|
167 break;
|
|
|
168
|
|
|
169 uv__free(p);
|
|
|
170 t = n;
|
|
|
171 }
|
|
|
172
|
|
|
173 /* The returned directory should not have a trailing slash, unless it points
|
|
|
174 * at a drive root, like c:\. Remove it if needed.
|
|
|
175 */
|
|
|
176 t = n - 1;
|
|
|
177 if (p[t] == L'\\' && !(n == 3 && p[1] == L':')) {
|
|
|
178 p[t] = L'\0';
|
|
|
179 n = t;
|
|
|
180 }
|
|
|
181
|
|
|
182 *buf = p;
|
|
|
183 *len = n;
|
|
|
184
|
|
|
185 return 0;
|
|
|
186 }
|
|
|
187
|
|
|
188
|
|
|
189 int uv_cwd(char* buffer, size_t* size) {
|
|
|
190 DWORD utf16_len;
|
|
|
191 WCHAR *utf16_buffer;
|
|
|
192 int r;
|
|
|
193
|
|
|
194 if (buffer == NULL || size == NULL || *size == 0) {
|
|
|
195 return UV_EINVAL;
|
|
|
196 }
|
|
|
197
|
|
|
198 r = uv__cwd(&utf16_buffer, &utf16_len);
|
|
|
199 if (r < 0)
|
|
|
200 return r;
|
|
|
201
|
|
|
202 r = uv__copy_utf16_to_utf8(utf16_buffer, utf16_len, buffer, size);
|
|
|
203
|
|
|
204 uv__free(utf16_buffer);
|
|
|
205
|
|
|
206 return r;
|
|
|
207 }
|
|
|
208
|
|
|
209
|
|
|
210 int uv_chdir(const char* dir) {
|
|
|
211 WCHAR *utf16_buffer;
|
|
|
212 DWORD utf16_len;
|
|
|
213 WCHAR drive_letter, env_var[4];
|
|
|
214 int r;
|
|
|
215
|
|
|
216 /* Convert to UTF-16 */
|
|
|
217 r = uv__convert_utf8_to_utf16(dir, &utf16_buffer);
|
|
|
218 if (r)
|
|
|
219 return r;
|
|
|
220
|
|
|
221 if (!SetCurrentDirectoryW(utf16_buffer)) {
|
|
|
222 uv__free(utf16_buffer);
|
|
|
223 return uv_translate_sys_error(GetLastError());
|
|
|
224 }
|
|
|
225
|
|
|
226 /* uv__cwd() will return a new buffer. */
|
|
|
227 uv__free(utf16_buffer);
|
|
|
228 utf16_buffer = NULL;
|
|
|
229
|
|
|
230 /* Windows stores the drive-local path in an "hidden" environment variable,
|
|
|
231 * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update
|
|
|
232 * this, so we'll have to do it. */
|
|
|
233 r = uv__cwd(&utf16_buffer, &utf16_len);
|
|
|
234 if (r == UV_ENOMEM) {
|
|
|
235 /* When updating the environment variable fails, return UV_OK anyway.
|
|
|
236 * We did successfully change current working directory, only updating
|
|
|
237 * hidden env variable failed. */
|
|
|
238 return 0;
|
|
|
239 }
|
|
|
240 if (r < 0) {
|
|
|
241 return r;
|
|
|
242 }
|
|
|
243
|
|
|
244 if (utf16_len < 2 || utf16_buffer[1] != L':') {
|
|
|
245 /* Doesn't look like a drive letter could be there - probably an UNC path.
|
|
|
246 * TODO: Need to handle win32 namespaces like \\?\C:\ ? */
|
|
|
247 drive_letter = 0;
|
|
|
248 } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') {
|
|
|
249 drive_letter = utf16_buffer[0];
|
|
|
250 } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') {
|
|
|
251 /* Convert to uppercase. */
|
|
|
252 drive_letter = utf16_buffer[0] - L'a' + L'A';
|
|
|
253 } else {
|
|
|
254 /* Not valid. */
|
|
|
255 drive_letter = 0;
|
|
|
256 }
|
|
|
257
|
|
|
258 if (drive_letter != 0) {
|
|
|
259 /* Construct the environment variable name and set it. */
|
|
|
260 env_var[0] = L'=';
|
|
|
261 env_var[1] = drive_letter;
|
|
|
262 env_var[2] = L':';
|
|
|
263 env_var[3] = L'\0';
|
|
|
264
|
|
|
265 SetEnvironmentVariableW(env_var, utf16_buffer);
|
|
|
266 }
|
|
|
267
|
|
|
268 uv__free(utf16_buffer);
|
|
|
269 return 0;
|
|
|
270 }
|
|
|
271
|
|
|
272
|
|
|
273 void uv_loadavg(double avg[3]) {
|
|
|
274 /* Can't be implemented */
|
|
|
275 avg[0] = avg[1] = avg[2] = 0;
|
|
|
276 }
|
|
|
277
|
|
|
278
|
|
|
279 uint64_t uv_get_free_memory(void) {
|
|
|
280 MEMORYSTATUSEX memory_status;
|
|
|
281 memory_status.dwLength = sizeof(memory_status);
|
|
|
282
|
|
|
283 if (!GlobalMemoryStatusEx(&memory_status)) {
|
|
|
284 return 0;
|
|
|
285 }
|
|
|
286
|
|
|
287 return (uint64_t)memory_status.ullAvailPhys;
|
|
|
288 }
|
|
|
289
|
|
|
290
|
|
|
291 uint64_t uv_get_total_memory(void) {
|
|
|
292 MEMORYSTATUSEX memory_status;
|
|
|
293 memory_status.dwLength = sizeof(memory_status);
|
|
|
294
|
|
|
295 if (!GlobalMemoryStatusEx(&memory_status)) {
|
|
|
296 return 0;
|
|
|
297 }
|
|
|
298
|
|
|
299 return (uint64_t)memory_status.ullTotalPhys;
|
|
|
300 }
|
|
|
301
|
|
|
302
|
|
|
303 uint64_t uv_get_constrained_memory(void) {
|
|
|
304 return 0; /* Memory constraints are unknown. */
|
|
|
305 }
|
|
|
306
|
|
|
307
|
|
|
308 uint64_t uv_get_available_memory(void) {
|
|
|
309 return uv_get_free_memory();
|
|
|
310 }
|
|
|
311
|
|
|
312
|
|
|
313 uv_pid_t uv_os_getpid(void) {
|
|
|
314 return GetCurrentProcessId();
|
|
|
315 }
|
|
|
316
|
|
|
317
|
|
|
318 uv_pid_t uv_os_getppid(void) {
|
|
|
319 NTSTATUS nt_status;
|
|
|
320 PROCESS_BASIC_INFORMATION basic_info;
|
|
|
321
|
|
|
322 nt_status = pNtQueryInformationProcess(GetCurrentProcess(),
|
|
|
323 ProcessBasicInformation,
|
|
|
324 &basic_info,
|
|
|
325 sizeof(basic_info),
|
|
|
326 NULL);
|
|
|
327 if (NT_SUCCESS(nt_status)) {
|
|
|
328 return basic_info.InheritedFromUniqueProcessId;
|
|
|
329 } else {
|
|
|
330 return -1;
|
|
|
331 }
|
|
|
332 }
|
|
|
333
|
|
|
334
|
|
|
335 char** uv_setup_args(int argc, char** argv) {
|
|
|
336 return argv;
|
|
|
337 }
|
|
|
338
|
|
|
339
|
|
|
340 void uv__process_title_cleanup(void) {
|
|
|
341 }
|
|
|
342
|
|
|
343
|
|
|
344 int uv_set_process_title(const char* title) {
|
|
|
345 int err;
|
|
|
346 int length;
|
|
|
347 WCHAR* title_w = NULL;
|
|
|
348
|
|
|
349 uv__once_init();
|
|
|
350
|
|
|
351 err = uv__convert_utf8_to_utf16(title, &title_w);
|
|
|
352 if (err)
|
|
|
353 return err;
|
|
|
354
|
|
|
355 /* If the title must be truncated insert a \0 terminator there */
|
|
|
356 length = wcslen(title_w);
|
|
|
357 if (length >= MAX_TITLE_LENGTH)
|
|
|
358 title_w[MAX_TITLE_LENGTH - 1] = L'\0';
|
|
|
359
|
|
|
360 if (!SetConsoleTitleW(title_w)) {
|
|
|
361 err = GetLastError();
|
|
|
362 goto done;
|
|
|
363 }
|
|
|
364
|
|
|
365 EnterCriticalSection(&process_title_lock);
|
|
|
366 uv__free(process_title);
|
|
|
367 process_title = uv__strdup(title);
|
|
|
368 LeaveCriticalSection(&process_title_lock);
|
|
|
369
|
|
|
370 err = 0;
|
|
|
371
|
|
|
372 done:
|
|
|
373 uv__free(title_w);
|
|
|
374 return uv_translate_sys_error(err);
|
|
|
375 }
|
|
|
376
|
|
|
377
|
|
|
378 static int uv__get_process_title(void) {
|
|
|
379 WCHAR title_w[MAX_TITLE_LENGTH];
|
|
|
380 DWORD wlen;
|
|
|
381
|
|
|
382 wlen = GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR));
|
|
|
383 if (wlen == 0)
|
|
|
384 return uv_translate_sys_error(GetLastError());
|
|
|
385
|
|
|
386 return uv__convert_utf16_to_utf8(title_w, wlen, &process_title);
|
|
|
387 }
|
|
|
388
|
|
|
389
|
|
|
390 int uv_get_process_title(char* buffer, size_t size) {
|
|
|
391 size_t len;
|
|
|
392 int r;
|
|
|
393
|
|
|
394 if (buffer == NULL || size == 0)
|
|
|
395 return UV_EINVAL;
|
|
|
396
|
|
|
397 uv__once_init();
|
|
|
398
|
|
|
399 EnterCriticalSection(&process_title_lock);
|
|
|
400 /*
|
|
|
401 * If the process_title was never read before nor explicitly set,
|
|
|
402 * we must query it with getConsoleTitleW
|
|
|
403 */
|
|
|
404 if (process_title == NULL) {
|
|
|
405 r = uv__get_process_title();
|
|
|
406 if (r) {
|
|
|
407 LeaveCriticalSection(&process_title_lock);
|
|
|
408 return r;
|
|
|
409 }
|
|
|
410 }
|
|
|
411
|
|
|
412 assert(process_title);
|
|
|
413 len = strlen(process_title) + 1;
|
|
|
414
|
|
|
415 if (size < len) {
|
|
|
416 LeaveCriticalSection(&process_title_lock);
|
|
|
417 return UV_ENOBUFS;
|
|
|
418 }
|
|
|
419
|
|
|
420 memcpy(buffer, process_title, len);
|
|
|
421 LeaveCriticalSection(&process_title_lock);
|
|
|
422
|
|
|
423 return 0;
|
|
|
424 }
|
|
|
425
|
|
|
426
|
|
|
427 /* https://github.com/libuv/libuv/issues/1674 */
|
|
|
428 int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
|
|
|
429 FILETIME ft;
|
|
|
430 int64_t t;
|
|
|
431
|
|
|
432 if (ts == NULL)
|
|
|
433 return UV_EFAULT;
|
|
|
434
|
|
|
435 switch (clock_id) {
|
|
|
436 case UV_CLOCK_MONOTONIC:
|
|
|
437 uv__once_init();
|
|
|
438 t = uv__hrtime(UV__NANOSEC);
|
|
|
439 ts->tv_sec = t / 1000000000;
|
|
|
440 ts->tv_nsec = t % 1000000000;
|
|
|
441 return 0;
|
|
|
442 case UV_CLOCK_REALTIME:
|
|
|
443 GetSystemTimePreciseAsFileTime(&ft);
|
|
|
444 /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */
|
|
|
445 t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime;
|
|
|
446 /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */
|
|
|
447 t -= 116444736000000000ll;
|
|
|
448 /* Now convert to seconds and nanoseconds. */
|
|
|
449 ts->tv_sec = t / 10000000;
|
|
|
450 ts->tv_nsec = t % 10000000 * 100;
|
|
|
451 return 0;
|
|
|
452 }
|
|
|
453
|
|
|
454 return UV_EINVAL;
|
|
|
455 }
|
|
|
456
|
|
|
457
|
|
|
458 uint64_t uv_hrtime(void) {
|
|
|
459 uv__once_init();
|
|
|
460 return uv__hrtime(UV__NANOSEC);
|
|
|
461 }
|
|
|
462
|
|
|
463
|
|
|
464 uint64_t uv__hrtime(unsigned int scale) {
|
|
|
465 LARGE_INTEGER counter;
|
|
|
466 double scaled_freq;
|
|
|
467 double result;
|
|
|
468
|
|
|
469 assert(hrtime_frequency_ != 0);
|
|
|
470 assert(scale != 0);
|
|
|
471 if (!QueryPerformanceCounter(&counter)) {
|
|
|
472 uv_fatal_error(GetLastError(), "QueryPerformanceCounter");
|
|
|
473 }
|
|
|
474 assert(counter.QuadPart != 0);
|
|
|
475
|
|
|
476 /* Because we have no guarantee about the order of magnitude of the
|
|
|
477 * performance counter interval, integer math could cause this computation
|
|
|
478 * to overflow. Therefore we resort to floating point math.
|
|
|
479 */
|
|
|
480 scaled_freq = (double) hrtime_frequency_ / scale;
|
|
|
481 result = (double) counter.QuadPart / scaled_freq;
|
|
|
482 return (uint64_t) result;
|
|
|
483 }
|
|
|
484
|
|
|
485
|
|
|
486 int uv_resident_set_memory(size_t* rss) {
|
|
|
487 HANDLE current_process;
|
|
|
488 PROCESS_MEMORY_COUNTERS pmc;
|
|
|
489
|
|
|
490 current_process = GetCurrentProcess();
|
|
|
491
|
|
|
492 if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) {
|
|
|
493 return uv_translate_sys_error(GetLastError());
|
|
|
494 }
|
|
|
495
|
|
|
496 *rss = pmc.WorkingSetSize;
|
|
|
497
|
|
|
498 return 0;
|
|
|
499 }
|
|
|
500
|
|
|
501
|
|
|
502 int uv_uptime(double* uptime) {
|
|
|
503 *uptime = GetTickCount64() / 1000.0;
|
|
|
504 return 0;
|
|
|
505 }
|
|
|
506
|
|
|
507
|
|
|
508 unsigned int uv_available_parallelism(void) {
|
|
|
509 DWORD_PTR procmask;
|
|
|
510 DWORD_PTR sysmask;
|
|
|
511 int count;
|
|
|
512 int i;
|
|
|
513
|
|
|
514 /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
|
|
|
515 * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
|
|
|
516 */
|
|
|
517 count = 0;
|
|
|
518 if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
|
|
|
519 for (i = 0; i < 8 * sizeof(procmask); i++)
|
|
|
520 count += 1 & (procmask >> i);
|
|
|
521
|
|
|
522 if (count > 0)
|
|
|
523 return count;
|
|
|
524
|
|
|
525 return 1;
|
|
|
526 }
|
|
|
527
|
|
|
528
|
|
|
529 int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
|
|
|
530 uv_cpu_info_t* cpu_infos;
|
|
|
531 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
|
|
|
532 DWORD sppi_size;
|
|
|
533 SYSTEM_INFO system_info;
|
|
|
534 DWORD cpu_count, i;
|
|
|
535 NTSTATUS status;
|
|
|
536 ULONG result_size;
|
|
|
537 int err;
|
|
|
538 uv_cpu_info_t* cpu_info;
|
|
|
539
|
|
|
540 cpu_infos = NULL;
|
|
|
541 cpu_count = 0;
|
|
|
542 sppi = NULL;
|
|
|
543
|
|
|
544 uv__once_init();
|
|
|
545
|
|
|
546 GetSystemInfo(&system_info);
|
|
|
547 cpu_count = system_info.dwNumberOfProcessors;
|
|
|
548
|
|
|
549 cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos);
|
|
|
550 if (cpu_infos == NULL) {
|
|
|
551 err = ERROR_OUTOFMEMORY;
|
|
|
552 goto error;
|
|
|
553 }
|
|
|
554
|
|
|
555 sppi_size = cpu_count * sizeof(*sppi);
|
|
|
556 sppi = uv__malloc(sppi_size);
|
|
|
557 if (sppi == NULL) {
|
|
|
558 err = ERROR_OUTOFMEMORY;
|
|
|
559 goto error;
|
|
|
560 }
|
|
|
561
|
|
|
562 status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
|
|
|
563 sppi,
|
|
|
564 sppi_size,
|
|
|
565 &result_size);
|
|
|
566 if (!NT_SUCCESS(status)) {
|
|
|
567 err = pRtlNtStatusToDosError(status);
|
|
|
568 goto error;
|
|
|
569 }
|
|
|
570
|
|
|
571 assert(result_size == sppi_size);
|
|
|
572
|
|
|
573 for (i = 0; i < cpu_count; i++) {
|
|
|
574 WCHAR key_name[128];
|
|
|
575 HKEY processor_key;
|
|
|
576 DWORD cpu_speed;
|
|
|
577 DWORD cpu_speed_size = sizeof(cpu_speed);
|
|
|
578 WCHAR cpu_brand[256];
|
|
|
579 DWORD cpu_brand_size = sizeof(cpu_brand);
|
|
|
580 size_t len;
|
|
|
581
|
|
|
582 len = _snwprintf(key_name,
|
|
|
583 ARRAY_SIZE(key_name),
|
|
|
584 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d",
|
|
|
585 i);
|
|
|
586
|
|
|
587 assert(len > 0 && len < ARRAY_SIZE(key_name));
|
|
|
588
|
|
|
589 err = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
|
590 key_name,
|
|
|
591 0,
|
|
|
592 KEY_QUERY_VALUE,
|
|
|
593 &processor_key);
|
|
|
594 if (err != ERROR_SUCCESS) {
|
|
|
595 goto error;
|
|
|
596 }
|
|
|
597
|
|
|
598 err = RegQueryValueExW(processor_key,
|
|
|
599 L"~MHz",
|
|
|
600 NULL,
|
|
|
601 NULL,
|
|
|
602 (BYTE*)&cpu_speed,
|
|
|
603 &cpu_speed_size);
|
|
|
604 if (err != ERROR_SUCCESS) {
|
|
|
605 RegCloseKey(processor_key);
|
|
|
606 goto error;
|
|
|
607 }
|
|
|
608
|
|
|
609 err = RegQueryValueExW(processor_key,
|
|
|
610 L"ProcessorNameString",
|
|
|
611 NULL,
|
|
|
612 NULL,
|
|
|
613 (BYTE*)&cpu_brand,
|
|
|
614 &cpu_brand_size);
|
|
|
615 RegCloseKey(processor_key);
|
|
|
616 if (err != ERROR_SUCCESS)
|
|
|
617 goto error;
|
|
|
618
|
|
|
619 cpu_info = &cpu_infos[i];
|
|
|
620 cpu_info->speed = cpu_speed;
|
|
|
621 cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000;
|
|
|
622 cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart -
|
|
|
623 sppi[i].IdleTime.QuadPart) / 10000;
|
|
|
624 cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000;
|
|
|
625 cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000;
|
|
|
626 cpu_info->cpu_times.nice = 0;
|
|
|
627
|
|
|
628 uv__convert_utf16_to_utf8(cpu_brand,
|
|
|
629 cpu_brand_size / sizeof(WCHAR),
|
|
|
630 &(cpu_info->model));
|
|
|
631 }
|
|
|
632
|
|
|
633 uv__free(sppi);
|
|
|
634
|
|
|
635 *cpu_count_ptr = cpu_count;
|
|
|
636 *cpu_infos_ptr = cpu_infos;
|
|
|
637
|
|
|
638 return 0;
|
|
|
639
|
|
|
640 error:
|
|
|
641 if (cpu_infos != NULL) {
|
|
|
642 /* This is safe because the cpu_infos array is zeroed on allocation. */
|
|
|
643 for (i = 0; i < cpu_count; i++)
|
|
|
644 uv__free(cpu_infos[i].model);
|
|
|
645 }
|
|
|
646
|
|
|
647 uv__free(cpu_infos);
|
|
|
648 uv__free(sppi);
|
|
|
649
|
|
|
650 return uv_translate_sys_error(err);
|
|
|
651 }
|
|
|
652
|
|
|
653
|
|
|
654 int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
|
|
|
655 int* count_ptr) {
|
|
|
656 IP_ADAPTER_ADDRESSES* win_address_buf;
|
|
|
657 ULONG win_address_buf_size;
|
|
|
658 IP_ADAPTER_ADDRESSES* adapter;
|
|
|
659
|
|
|
660 uv_interface_address_t* uv_address_buf;
|
|
|
661 char* name_buf;
|
|
|
662 size_t uv_address_buf_size;
|
|
|
663 uv_interface_address_t* uv_address;
|
|
|
664
|
|
|
665 int count;
|
|
|
666 ULONG flags;
|
|
|
667
|
|
|
668 *addresses_ptr = NULL;
|
|
|
669 *count_ptr = 0;
|
|
|
670
|
|
|
671 flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
|
|
672 GAA_FLAG_SKIP_DNS_SERVER;
|
|
|
673
|
|
|
674 /* Fetch the size of the adapters reported by windows, and then get the list
|
|
|
675 * itself. */
|
|
|
676 win_address_buf_size = 0;
|
|
|
677 win_address_buf = NULL;
|
|
|
678
|
|
|
679 for (;;) {
|
|
|
680 ULONG r;
|
|
|
681
|
|
|
682 /* If win_address_buf is 0, then GetAdaptersAddresses will fail with.
|
|
|
683 * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in
|
|
|
684 * win_address_buf_size. */
|
|
|
685 r = GetAdaptersAddresses(AF_UNSPEC,
|
|
|
686 flags,
|
|
|
687 NULL,
|
|
|
688 win_address_buf,
|
|
|
689 &win_address_buf_size);
|
|
|
690
|
|
|
691 if (r == ERROR_SUCCESS)
|
|
|
692 break;
|
|
|
693
|
|
|
694 uv__free(win_address_buf);
|
|
|
695
|
|
|
696 switch (r) {
|
|
|
697 case ERROR_BUFFER_OVERFLOW:
|
|
|
698 /* This happens when win_address_buf is NULL or too small to hold all
|
|
|
699 * adapters. */
|
|
|
700 win_address_buf = uv__malloc(win_address_buf_size);
|
|
|
701 if (win_address_buf == NULL)
|
|
|
702 return UV_ENOMEM;
|
|
|
703
|
|
|
704 continue;
|
|
|
705
|
|
|
706 case ERROR_NO_DATA: {
|
|
|
707 /* No adapters were found. */
|
|
|
708 uv_address_buf = uv__malloc(1);
|
|
|
709 if (uv_address_buf == NULL)
|
|
|
710 return UV_ENOMEM;
|
|
|
711
|
|
|
712 *count_ptr = 0;
|
|
|
713 *addresses_ptr = uv_address_buf;
|
|
|
714
|
|
|
715 return 0;
|
|
|
716 }
|
|
|
717
|
|
|
718 case ERROR_ADDRESS_NOT_ASSOCIATED:
|
|
|
719 return UV_EAGAIN;
|
|
|
720
|
|
|
721 case ERROR_INVALID_PARAMETER:
|
|
|
722 /* MSDN says:
|
|
|
723 * "This error is returned for any of the following conditions: the
|
|
|
724 * SizePointer parameter is NULL, the Address parameter is not
|
|
|
725 * AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
|
|
|
726 * the parameters requested is greater than ULONG_MAX."
|
|
|
727 * Since the first two conditions are not met, it must be that the
|
|
|
728 * adapter data is too big.
|
|
|
729 */
|
|
|
730 return UV_ENOBUFS;
|
|
|
731
|
|
|
732 default:
|
|
|
733 /* Other (unspecified) errors can happen, but we don't have any special
|
|
|
734 * meaning for them. */
|
|
|
735 assert(r != ERROR_SUCCESS);
|
|
|
736 return uv_translate_sys_error(r);
|
|
|
737 }
|
|
|
738 }
|
|
|
739
|
|
|
740 /* Count the number of enabled interfaces and compute how much space is
|
|
|
741 * needed to store their info. */
|
|
|
742 count = 0;
|
|
|
743 uv_address_buf_size = 0;
|
|
|
744
|
|
|
745 for (adapter = win_address_buf;
|
|
|
746 adapter != NULL;
|
|
|
747 adapter = adapter->Next) {
|
|
|
748 IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
|
|
|
749 int name_size;
|
|
|
750
|
|
|
751 /* Interfaces that are not 'up' should not be reported. Also skip
|
|
|
752 * interfaces that have no associated unicast address, as to avoid
|
|
|
753 * allocating space for the name for this interface. */
|
|
|
754 if (adapter->OperStatus != IfOperStatusUp ||
|
|
|
755 adapter->FirstUnicastAddress == NULL)
|
|
|
756 continue;
|
|
|
757
|
|
|
758 /* Compute the size of the interface name. */
|
|
|
759 name_size = uv_utf16_length_as_wtf8(adapter->FriendlyName, -1);
|
|
|
760 uv_address_buf_size += name_size + 1;
|
|
|
761
|
|
|
762 /* Count the number of addresses associated with this interface, and
|
|
|
763 * compute the size. */
|
|
|
764 for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
|
|
|
765 adapter->FirstUnicastAddress;
|
|
|
766 unicast_address != NULL;
|
|
|
767 unicast_address = unicast_address->Next) {
|
|
|
768 count++;
|
|
|
769 uv_address_buf_size += sizeof(uv_interface_address_t);
|
|
|
770 }
|
|
|
771 }
|
|
|
772
|
|
|
773 /* Allocate space to store interface data plus adapter names. */
|
|
|
774 uv_address_buf = uv__malloc(uv_address_buf_size);
|
|
|
775 if (uv_address_buf == NULL) {
|
|
|
776 uv__free(win_address_buf);
|
|
|
777 return UV_ENOMEM;
|
|
|
778 }
|
|
|
779
|
|
|
780 /* Compute the start of the uv_interface_address_t array, and the place in
|
|
|
781 * the buffer where the interface names will be stored. */
|
|
|
782 uv_address = uv_address_buf;
|
|
|
783 name_buf = (char*) (uv_address_buf + count);
|
|
|
784
|
|
|
785 /* Fill out the output buffer. */
|
|
|
786 for (adapter = win_address_buf;
|
|
|
787 adapter != NULL;
|
|
|
788 adapter = adapter->Next) {
|
|
|
789 IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
|
|
|
790 size_t name_size;
|
|
|
791 int r;
|
|
|
792
|
|
|
793 if (adapter->OperStatus != IfOperStatusUp ||
|
|
|
794 adapter->FirstUnicastAddress == NULL)
|
|
|
795 continue;
|
|
|
796
|
|
|
797 /* Convert the interface name to UTF8. */
|
|
|
798 name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
|
|
|
799 r = uv__copy_utf16_to_utf8(adapter->FriendlyName,
|
|
|
800 -1,
|
|
|
801 name_buf,
|
|
|
802 &name_size);
|
|
|
803 if (r) {
|
|
|
804 uv__free(win_address_buf);
|
|
|
805 uv__free(uv_address_buf);
|
|
|
806 return r;
|
|
|
807 }
|
|
|
808 name_size += 1; /* Add NUL byte. */
|
|
|
809
|
|
|
810 /* Add an uv_interface_address_t element for every unicast address. */
|
|
|
811 for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
|
|
|
812 adapter->FirstUnicastAddress;
|
|
|
813 unicast_address != NULL;
|
|
|
814 unicast_address = unicast_address->Next) {
|
|
|
815 struct sockaddr* sa;
|
|
|
816 ULONG prefix_len;
|
|
|
817
|
|
|
818 sa = unicast_address->Address.lpSockaddr;
|
|
|
819
|
|
|
820 prefix_len =
|
|
|
821 ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
|
|
|
822
|
|
|
823 memset(uv_address, 0, sizeof *uv_address);
|
|
|
824
|
|
|
825 uv_address->name = name_buf;
|
|
|
826
|
|
|
827 if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) {
|
|
|
828 memcpy(uv_address->phys_addr,
|
|
|
829 adapter->PhysicalAddress,
|
|
|
830 sizeof(uv_address->phys_addr));
|
|
|
831 }
|
|
|
832
|
|
|
833 uv_address->is_internal =
|
|
|
834 (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
|
|
|
835
|
|
|
836 if (sa->sa_family == AF_INET6) {
|
|
|
837 uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
|
|
|
838
|
|
|
839 uv_address->netmask.netmask6.sin6_family = AF_INET6;
|
|
|
840 memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3);
|
|
|
841 /* This check ensures that we don't write past the size of the data. */
|
|
|
842 if (prefix_len % 8) {
|
|
|
843 uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] =
|
|
|
844 0xff << (8 - prefix_len % 8);
|
|
|
845 }
|
|
|
846
|
|
|
847 } else {
|
|
|
848 uv_address->address.address4 = *((struct sockaddr_in *) sa);
|
|
|
849
|
|
|
850 uv_address->netmask.netmask4.sin_family = AF_INET;
|
|
|
851 uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ?
|
|
|
852 htonl(0xffffffff << (32 - prefix_len)) : 0;
|
|
|
853 }
|
|
|
854
|
|
|
855 uv_address++;
|
|
|
856 }
|
|
|
857
|
|
|
858 name_buf += name_size;
|
|
|
859 }
|
|
|
860
|
|
|
861 uv__free(win_address_buf);
|
|
|
862
|
|
|
863 *addresses_ptr = uv_address_buf;
|
|
|
864 *count_ptr = count;
|
|
|
865
|
|
|
866 return 0;
|
|
|
867 }
|
|
|
868
|
|
|
869
|
|
|
870 void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
|
|
871 int count) {
|
|
|
872 uv__free(addresses);
|
|
|
873 }
|
|
|
874
|
|
|
875
|
|
|
876 int uv_getrusage(uv_rusage_t *uv_rusage) {
|
|
|
877 FILETIME create_time, exit_time, kernel_time, user_time;
|
|
|
878 SYSTEMTIME kernel_system_time, user_system_time;
|
|
|
879 PROCESS_MEMORY_COUNTERS mem_counters;
|
|
|
880 IO_COUNTERS io_counters;
|
|
|
881 int ret;
|
|
|
882
|
|
|
883 ret = GetProcessTimes(GetCurrentProcess(),
|
|
|
884 &create_time,
|
|
|
885 &exit_time,
|
|
|
886 &kernel_time,
|
|
|
887 &user_time);
|
|
|
888 if (ret == 0) {
|
|
|
889 return uv_translate_sys_error(GetLastError());
|
|
|
890 }
|
|
|
891
|
|
|
892 ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
|
|
893 if (ret == 0) {
|
|
|
894 return uv_translate_sys_error(GetLastError());
|
|
|
895 }
|
|
|
896
|
|
|
897 ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
|
|
898 if (ret == 0) {
|
|
|
899 return uv_translate_sys_error(GetLastError());
|
|
|
900 }
|
|
|
901
|
|
|
902 ret = GetProcessMemoryInfo(GetCurrentProcess(),
|
|
|
903 &mem_counters,
|
|
|
904 sizeof(mem_counters));
|
|
|
905 if (ret == 0) {
|
|
|
906 return uv_translate_sys_error(GetLastError());
|
|
|
907 }
|
|
|
908
|
|
|
909 ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters);
|
|
|
910 if (ret == 0) {
|
|
|
911 return uv_translate_sys_error(GetLastError());
|
|
|
912 }
|
|
|
913
|
|
|
914 memset(uv_rusage, 0, sizeof(*uv_rusage));
|
|
|
915
|
|
|
916 uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
|
|
917 user_system_time.wMinute * 60 +
|
|
|
918 user_system_time.wSecond;
|
|
|
919 uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
|
|
920
|
|
|
921 uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
|
|
922 kernel_system_time.wMinute * 60 +
|
|
|
923 kernel_system_time.wSecond;
|
|
|
924 uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
|
|
925
|
|
|
926 uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount;
|
|
|
927 uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024;
|
|
|
928
|
|
|
929 uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount;
|
|
|
930 uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount;
|
|
|
931
|
|
|
932 return 0;
|
|
|
933 }
|
|
|
934
|
|
|
935
|
|
|
936 int uv_getrusage_thread(uv_rusage_t* uv_rusage) {
|
|
|
937 FILETIME create_time, exit_time, kernel_time, user_time;
|
|
|
938 SYSTEMTIME kernel_system_time, user_system_time;
|
|
|
939 int ret;
|
|
|
940
|
|
|
941 ret = GetThreadTimes(GetCurrentThread(),
|
|
|
942 &create_time,
|
|
|
943 &exit_time,
|
|
|
944 &kernel_time,
|
|
|
945 &user_time);
|
|
|
946 if (ret == 0) {
|
|
|
947 return uv_translate_sys_error(GetLastError());
|
|
|
948 }
|
|
|
949
|
|
|
950 ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
|
|
951 if (ret == 0) {
|
|
|
952 return uv_translate_sys_error(GetLastError());
|
|
|
953 }
|
|
|
954
|
|
|
955 ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
|
|
956 if (ret == 0) {
|
|
|
957 return uv_translate_sys_error(GetLastError());
|
|
|
958 }
|
|
|
959
|
|
|
960 memset(uv_rusage, 0, sizeof(*uv_rusage));
|
|
|
961
|
|
|
962 uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
|
|
963 user_system_time.wMinute * 60 +
|
|
|
964 user_system_time.wSecond;
|
|
|
965 uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
|
|
966
|
|
|
967 uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
|
|
968 kernel_system_time.wMinute * 60 +
|
|
|
969 kernel_system_time.wSecond;
|
|
|
970 uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
|
|
971
|
|
|
972 return 0;
|
|
|
973 }
|
|
|
974
|
|
|
975
|
|
|
976 int uv_os_homedir(char* buffer, size_t* size) {
|
|
|
977 uv_passwd_t pwd;
|
|
|
978 size_t len;
|
|
|
979 int r;
|
|
|
980
|
|
|
981 /* Check if the USERPROFILE environment variable is set first. The task of
|
|
|
982 performing input validation on buffer and size is taken care of by
|
|
|
983 uv_os_getenv(). */
|
|
|
984 r = uv_os_getenv("USERPROFILE", buffer, size);
|
|
|
985
|
|
|
986 /* Don't return an error if USERPROFILE was not found. */
|
|
|
987 if (r != UV_ENOENT) {
|
|
|
988 /* USERPROFILE is empty or invalid */
|
|
|
989 if (r == 0 && *size < 3) {
|
|
|
990 return UV_ENOENT;
|
|
|
991 }
|
|
|
992 return r;
|
|
|
993 }
|
|
|
994
|
|
|
995 /* USERPROFILE is not set, so call uv_os_get_passwd() */
|
|
|
996 r = uv_os_get_passwd(&pwd);
|
|
|
997
|
|
|
998 if (r != 0) {
|
|
|
999 return r;
|
|
|
1000 }
|
|
|
1001
|
|
|
1002 len = strlen(pwd.homedir);
|
|
|
1003
|
|
|
1004 if (len >= *size) {
|
|
|
1005 *size = len + 1;
|
|
|
1006 uv_os_free_passwd(&pwd);
|
|
|
1007 return UV_ENOBUFS;
|
|
|
1008 }
|
|
|
1009
|
|
|
1010 memcpy(buffer, pwd.homedir, len + 1);
|
|
|
1011 *size = len;
|
|
|
1012 uv_os_free_passwd(&pwd);
|
|
|
1013
|
|
|
1014 return 0;
|
|
|
1015 }
|
|
|
1016
|
|
|
1017
|
|
|
1018 int uv_os_tmpdir(char* buffer, size_t* size) {
|
|
|
1019 int r;
|
|
|
1020 wchar_t *path;
|
|
|
1021 size_t len;
|
|
|
1022
|
|
|
1023 if (buffer == NULL || size == NULL || *size == 0)
|
|
|
1024 return UV_EINVAL;
|
|
|
1025
|
|
|
1026 len = 0;
|
|
|
1027 len = GetTempPathW(0, NULL);
|
|
|
1028 if (len == 0) {
|
|
|
1029 return uv_translate_sys_error(GetLastError());
|
|
|
1030 }
|
|
|
1031
|
|
|
1032 /* tmp path is empty or invalid */
|
|
|
1033 if (len < 3) {
|
|
|
1034 return UV_ENOENT;
|
|
|
1035 }
|
|
|
1036
|
|
|
1037 /* Include space for terminating null char. */
|
|
|
1038 len += 1;
|
|
|
1039 path = uv__malloc(len * sizeof(wchar_t));
|
|
|
1040 if (path == NULL) {
|
|
|
1041 return UV_ENOMEM;
|
|
|
1042 }
|
|
|
1043 len = GetTempPathW(len, path);
|
|
|
1044
|
|
|
1045 if (len == 0) {
|
|
|
1046 uv__free(path);
|
|
|
1047 return uv_translate_sys_error(GetLastError());
|
|
|
1048 }
|
|
|
1049
|
|
|
1050 /* The returned directory should not have a trailing slash, unless it points
|
|
|
1051 * at a drive root, like c:\. Remove it if needed. */
|
|
|
1052 if (path[len - 1] == L'\\' &&
|
|
|
1053 !(len == 3 && path[1] == L':')) {
|
|
|
1054 len--;
|
|
|
1055 path[len] = L'\0';
|
|
|
1056 }
|
|
|
1057
|
|
|
1058 r = uv__copy_utf16_to_utf8(path, len, buffer, size);
|
|
|
1059 uv__free(path);
|
|
|
1060 return r;
|
|
|
1061 }
|
|
|
1062
|
|
|
1063
|
|
|
1064 /*
|
|
|
1065 * Converts a UTF-16 string into a UTF-8 one. The resulting string is
|
|
|
1066 * null-terminated.
|
|
|
1067 *
|
|
|
1068 * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
|
|
|
1069 * be specified.
|
|
|
1070 */
|
|
|
1071 int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8) {
|
|
|
1072 size_t utf8_len = 0;
|
|
|
1073
|
|
|
1074 if (utf16 == NULL)
|
|
|
1075 return UV_EINVAL;
|
|
|
1076
|
|
|
1077 *utf8 = NULL;
|
|
|
1078 return uv_utf16_to_wtf8(utf16, utf16len, utf8, &utf8_len);
|
|
|
1079 }
|
|
|
1080
|
|
|
1081
|
|
|
1082 /*
|
|
|
1083 * Converts a UTF-8 string into a UTF-16 one. The resulting string is
|
|
|
1084 * null-terminated.
|
|
|
1085 */
|
|
|
1086 int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16) {
|
|
|
1087 int bufsize;
|
|
|
1088
|
|
|
1089 if (utf8 == NULL)
|
|
|
1090 return UV_EINVAL;
|
|
|
1091
|
|
|
1092 /* Check how much space we need (including NUL). */
|
|
|
1093 bufsize = uv_wtf8_length_as_utf16(utf8);
|
|
|
1094 if (bufsize < 0)
|
|
|
1095 return UV__EINVAL;
|
|
|
1096
|
|
|
1097 /* Allocate the destination buffer. */
|
|
|
1098 *utf16 = uv__malloc(sizeof(WCHAR) * bufsize);
|
|
|
1099
|
|
|
1100 if (*utf16 == NULL)
|
|
|
1101 return UV_ENOMEM;
|
|
|
1102
|
|
|
1103 /* Convert to UTF-16 */
|
|
|
1104 uv_wtf8_to_utf16(utf8, *utf16, bufsize);
|
|
|
1105
|
|
|
1106 return 0;
|
|
|
1107 }
|
|
|
1108
|
|
|
1109
|
|
|
1110 /*
|
|
|
1111 * Converts a UTF-16 string into a UTF-8 one in an existing buffer. The
|
|
|
1112 * resulting string is null-terminated.
|
|
|
1113 *
|
|
|
1114 * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
|
|
|
1115 * be specified.
|
|
|
1116 */
|
|
|
1117 int uv__copy_utf16_to_utf8(const WCHAR* utf16buffer, size_t utf16len, char* utf8, size_t *size) {
|
|
|
1118 int r;
|
|
|
1119
|
|
|
1120 if (utf8 == NULL || size == NULL)
|
|
|
1121 return UV_EINVAL;
|
|
|
1122
|
|
|
1123 if (*size == 0) {
|
|
|
1124 *size = uv_utf16_length_as_wtf8(utf16buffer, utf16len);
|
|
|
1125 r = UV_ENOBUFS;
|
|
|
1126 } else {
|
|
|
1127 *size -= 1; /* Reserve space for NUL. */
|
|
|
1128 r = uv_utf16_to_wtf8(utf16buffer, utf16len, &utf8, size);
|
|
|
1129 }
|
|
|
1130 if (r == UV_ENOBUFS)
|
|
|
1131 *size += 1; /* Add space for NUL. */
|
|
|
1132 return r;
|
|
|
1133 }
|
|
|
1134
|
|
|
1135
|
|
|
1136 static int uv__getpwuid_r(uv_passwd_t* pwd) {
|
|
|
1137 HANDLE token;
|
|
|
1138 wchar_t username[UNLEN + 1];
|
|
|
1139 wchar_t *path;
|
|
|
1140 DWORD bufsize;
|
|
|
1141 int r;
|
|
|
1142
|
|
|
1143 if (pwd == NULL)
|
|
|
1144 return UV_EINVAL;
|
|
|
1145
|
|
|
1146 /* Get the home directory using GetUserProfileDirectoryW() */
|
|
|
1147 if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
|
|
|
1148 return uv_translate_sys_error(GetLastError());
|
|
|
1149
|
|
|
1150 bufsize = 0;
|
|
|
1151 GetUserProfileDirectoryW(token, NULL, &bufsize);
|
|
|
1152 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
|
1153 r = GetLastError();
|
|
|
1154 CloseHandle(token);
|
|
|
1155 return uv_translate_sys_error(r);
|
|
|
1156 }
|
|
|
1157
|
|
|
1158 path = uv__malloc(bufsize * sizeof(wchar_t));
|
|
|
1159 if (path == NULL) {
|
|
|
1160 CloseHandle(token);
|
|
|
1161 return UV_ENOMEM;
|
|
|
1162 }
|
|
|
1163
|
|
|
1164 if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
|
|
|
1165 r = GetLastError();
|
|
|
1166 CloseHandle(token);
|
|
|
1167 uv__free(path);
|
|
|
1168 return uv_translate_sys_error(r);
|
|
|
1169 }
|
|
|
1170
|
|
|
1171 CloseHandle(token);
|
|
|
1172
|
|
|
1173 /* Get the username using GetUserNameW() */
|
|
|
1174 bufsize = ARRAY_SIZE(username);
|
|
|
1175 if (!GetUserNameW(username, &bufsize)) {
|
|
|
1176 r = GetLastError();
|
|
|
1177 uv__free(path);
|
|
|
1178
|
|
|
1179 /* This should not be possible */
|
|
|
1180 if (r == ERROR_INSUFFICIENT_BUFFER)
|
|
|
1181 return UV_ENOMEM;
|
|
|
1182
|
|
|
1183 return uv_translate_sys_error(r);
|
|
|
1184 }
|
|
|
1185
|
|
|
1186 pwd->homedir = NULL;
|
|
|
1187 r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir);
|
|
|
1188 uv__free(path);
|
|
|
1189
|
|
|
1190 if (r != 0)
|
|
|
1191 return r;
|
|
|
1192
|
|
|
1193 pwd->username = NULL;
|
|
|
1194 r = uv__convert_utf16_to_utf8(username, -1, &pwd->username);
|
|
|
1195
|
|
|
1196 if (r != 0) {
|
|
|
1197 uv__free(pwd->homedir);
|
|
|
1198 return r;
|
|
|
1199 }
|
|
|
1200
|
|
|
1201 pwd->shell = NULL;
|
|
|
1202 pwd->uid = -1;
|
|
|
1203 pwd->gid = -1;
|
|
|
1204
|
|
|
1205 return 0;
|
|
|
1206 }
|
|
|
1207
|
|
|
1208
|
|
|
1209 int uv_os_get_passwd(uv_passwd_t* pwd) {
|
|
|
1210 return uv__getpwuid_r(pwd);
|
|
|
1211 }
|
|
|
1212
|
|
|
1213
|
|
|
1214 int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
|
|
|
1215 return UV_ENOTSUP;
|
|
|
1216 }
|
|
|
1217
|
|
|
1218
|
|
|
1219 int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
|
|
|
1220 return UV_ENOTSUP;
|
|
|
1221 }
|
|
|
1222
|
|
|
1223
|
|
|
1224 int uv_os_environ(uv_env_item_t** envitems, int* count) {
|
|
|
1225 wchar_t* env;
|
|
|
1226 wchar_t* penv;
|
|
|
1227 int i, cnt;
|
|
|
1228 uv_env_item_t* envitem;
|
|
|
1229
|
|
|
1230 *envitems = NULL;
|
|
|
1231 *count = 0;
|
|
|
1232
|
|
|
1233 env = GetEnvironmentStringsW();
|
|
|
1234 if (env == NULL)
|
|
|
1235 return 0;
|
|
|
1236
|
|
|
1237 for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
|
|
|
1238
|
|
|
1239 *envitems = uv__calloc(i, sizeof(**envitems));
|
|
|
1240 if (*envitems == NULL) {
|
|
|
1241 FreeEnvironmentStringsW(env);
|
|
|
1242 return UV_ENOMEM;
|
|
|
1243 }
|
|
|
1244
|
|
|
1245 penv = env;
|
|
|
1246 cnt = 0;
|
|
|
1247
|
|
|
1248 while (*penv != L'\0' && cnt < i) {
|
|
|
1249 char* buf;
|
|
|
1250 char* ptr;
|
|
|
1251
|
|
|
1252 if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0)
|
|
|
1253 goto fail;
|
|
|
1254
|
|
|
1255 /* Using buf + 1 here because we know that `buf` has length at least 1,
|
|
|
1256 * and some special environment variables on Windows start with a = sign. */
|
|
|
1257 ptr = strchr(buf + 1, '=');
|
|
|
1258 if (ptr == NULL) {
|
|
|
1259 uv__free(buf);
|
|
|
1260 goto do_continue;
|
|
|
1261 }
|
|
|
1262
|
|
|
1263 *ptr = '\0';
|
|
|
1264
|
|
|
1265 envitem = &(*envitems)[cnt];
|
|
|
1266 envitem->name = buf;
|
|
|
1267 envitem->value = ptr + 1;
|
|
|
1268
|
|
|
1269 cnt++;
|
|
|
1270
|
|
|
1271 do_continue:
|
|
|
1272 penv += wcslen(penv) + 1;
|
|
|
1273 }
|
|
|
1274
|
|
|
1275 FreeEnvironmentStringsW(env);
|
|
|
1276
|
|
|
1277 *count = cnt;
|
|
|
1278 return 0;
|
|
|
1279
|
|
|
1280 fail:
|
|
|
1281 FreeEnvironmentStringsW(env);
|
|
|
1282
|
|
|
1283 for (i = 0; i < cnt; i++) {
|
|
|
1284 envitem = &(*envitems)[cnt];
|
|
|
1285 uv__free(envitem->name);
|
|
|
1286 }
|
|
|
1287 uv__free(*envitems);
|
|
|
1288
|
|
|
1289 *envitems = NULL;
|
|
|
1290 *count = 0;
|
|
|
1291 return UV_ENOMEM;
|
|
|
1292 }
|
|
|
1293
|
|
|
1294
|
|
|
1295 int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
|
|
1296 wchar_t fastvar[512];
|
|
|
1297 wchar_t* var;
|
|
|
1298 DWORD varlen;
|
|
|
1299 wchar_t* name_w;
|
|
|
1300 size_t len;
|
|
|
1301 int r;
|
|
|
1302
|
|
|
1303 if (name == NULL || buffer == NULL || size == NULL || *size == 0)
|
|
|
1304 return UV_EINVAL;
|
|
|
1305
|
|
|
1306 r = uv__convert_utf8_to_utf16(name, &name_w);
|
|
|
1307
|
|
|
1308 if (r != 0)
|
|
|
1309 return r;
|
|
|
1310
|
|
|
1311 var = fastvar;
|
|
|
1312 varlen = ARRAY_SIZE(fastvar);
|
|
|
1313
|
|
|
1314 for (;;) {
|
|
|
1315 SetLastError(ERROR_SUCCESS);
|
|
|
1316 len = GetEnvironmentVariableW(name_w, var, varlen);
|
|
|
1317
|
|
|
1318 if (len == 0)
|
|
|
1319 r = uv_translate_sys_error(GetLastError());
|
|
|
1320
|
|
|
1321 if (len < varlen)
|
|
|
1322 break;
|
|
|
1323
|
|
|
1324 /* Try repeatedly because we might have been preempted by another thread
|
|
|
1325 * modifying the environment variable just as we're trying to read it.
|
|
|
1326 */
|
|
|
1327 if (var != fastvar)
|
|
|
1328 uv__free(var);
|
|
|
1329
|
|
|
1330 varlen = 1 + len;
|
|
|
1331 var = uv__malloc(varlen * sizeof(*var));
|
|
|
1332
|
|
|
1333 if (var == NULL) {
|
|
|
1334 r = UV_ENOMEM;
|
|
|
1335 goto fail;
|
|
|
1336 }
|
|
|
1337 }
|
|
|
1338
|
|
|
1339 uv__free(name_w);
|
|
|
1340 name_w = NULL;
|
|
|
1341
|
|
|
1342 if (r == 0)
|
|
|
1343 r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
|
|
1344
|
|
|
1345 fail:
|
|
|
1346
|
|
|
1347 if (name_w != NULL)
|
|
|
1348 uv__free(name_w);
|
|
|
1349
|
|
|
1350 if (var != fastvar)
|
|
|
1351 uv__free(var);
|
|
|
1352
|
|
|
1353 return r;
|
|
|
1354 }
|
|
|
1355
|
|
|
1356
|
|
|
1357 int uv_os_setenv(const char* name, const char* value) {
|
|
|
1358 wchar_t* name_w;
|
|
|
1359 wchar_t* value_w;
|
|
|
1360 int r;
|
|
|
1361
|
|
|
1362 if (name == NULL || value == NULL)
|
|
|
1363 return UV_EINVAL;
|
|
|
1364
|
|
|
1365 r = uv__convert_utf8_to_utf16(name, &name_w);
|
|
|
1366
|
|
|
1367 if (r != 0)
|
|
|
1368 return r;
|
|
|
1369
|
|
|
1370 r = uv__convert_utf8_to_utf16(value, &value_w);
|
|
|
1371
|
|
|
1372 if (r != 0) {
|
|
|
1373 uv__free(name_w);
|
|
|
1374 return r;
|
|
|
1375 }
|
|
|
1376
|
|
|
1377 r = SetEnvironmentVariableW(name_w, value_w);
|
|
|
1378 uv__free(name_w);
|
|
|
1379 uv__free(value_w);
|
|
|
1380
|
|
|
1381 if (r == 0)
|
|
|
1382 return uv_translate_sys_error(GetLastError());
|
|
|
1383
|
|
|
1384 return 0;
|
|
|
1385 }
|
|
|
1386
|
|
|
1387
|
|
|
1388 int uv_os_unsetenv(const char* name) {
|
|
|
1389 wchar_t* name_w;
|
|
|
1390 int r;
|
|
|
1391
|
|
|
1392 if (name == NULL)
|
|
|
1393 return UV_EINVAL;
|
|
|
1394
|
|
|
1395 r = uv__convert_utf8_to_utf16(name, &name_w);
|
|
|
1396
|
|
|
1397 if (r != 0)
|
|
|
1398 return r;
|
|
|
1399
|
|
|
1400 r = SetEnvironmentVariableW(name_w, NULL);
|
|
|
1401 uv__free(name_w);
|
|
|
1402
|
|
|
1403 if (r == 0)
|
|
|
1404 return uv_translate_sys_error(GetLastError());
|
|
|
1405
|
|
|
1406 return 0;
|
|
|
1407 }
|
|
|
1408
|
|
|
1409
|
|
|
1410 int uv_os_gethostname(char* buffer, size_t* size) {
|
|
|
1411 WCHAR buf[UV_MAXHOSTNAMESIZE];
|
|
|
1412
|
|
|
1413 if (buffer == NULL || size == NULL || *size == 0)
|
|
|
1414 return UV_EINVAL;
|
|
|
1415
|
|
|
1416 uv__once_init(); /* Initialize winsock */
|
|
|
1417
|
|
|
1418 if (pGetHostNameW == NULL)
|
|
|
1419 return UV_ENOSYS;
|
|
|
1420
|
|
|
1421 if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0)
|
|
|
1422 return uv_translate_sys_error(WSAGetLastError());
|
|
|
1423
|
|
|
1424 return uv__copy_utf16_to_utf8(buf, -1, buffer, size);
|
|
|
1425 }
|
|
|
1426
|
|
|
1427
|
|
|
1428 static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) {
|
|
|
1429 int r;
|
|
|
1430
|
|
|
1431 if (pid == 0)
|
|
|
1432 *handle = GetCurrentProcess();
|
|
|
1433 else
|
|
|
1434 *handle = OpenProcess(access, FALSE, pid);
|
|
|
1435
|
|
|
1436 if (*handle == NULL) {
|
|
|
1437 r = GetLastError();
|
|
|
1438
|
|
|
1439 if (r == ERROR_INVALID_PARAMETER)
|
|
|
1440 return UV_ESRCH;
|
|
|
1441 else
|
|
|
1442 return uv_translate_sys_error(r);
|
|
|
1443 }
|
|
|
1444
|
|
|
1445 return 0;
|
|
|
1446 }
|
|
|
1447
|
|
|
1448
|
|
|
1449 int uv_os_getpriority(uv_pid_t pid, int* priority) {
|
|
|
1450 HANDLE handle;
|
|
|
1451 int r;
|
|
|
1452
|
|
|
1453 if (priority == NULL)
|
|
|
1454 return UV_EINVAL;
|
|
|
1455
|
|
|
1456 r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle);
|
|
|
1457
|
|
|
1458 if (r != 0)
|
|
|
1459 return r;
|
|
|
1460
|
|
|
1461 r = GetPriorityClass(handle);
|
|
|
1462
|
|
|
1463 if (r == 0) {
|
|
|
1464 r = uv_translate_sys_error(GetLastError());
|
|
|
1465 } else {
|
|
|
1466 /* Map Windows priority classes to Unix nice values. */
|
|
|
1467 if (r == REALTIME_PRIORITY_CLASS)
|
|
|
1468 *priority = UV_PRIORITY_HIGHEST;
|
|
|
1469 else if (r == HIGH_PRIORITY_CLASS)
|
|
|
1470 *priority = UV_PRIORITY_HIGH;
|
|
|
1471 else if (r == ABOVE_NORMAL_PRIORITY_CLASS)
|
|
|
1472 *priority = UV_PRIORITY_ABOVE_NORMAL;
|
|
|
1473 else if (r == NORMAL_PRIORITY_CLASS)
|
|
|
1474 *priority = UV_PRIORITY_NORMAL;
|
|
|
1475 else if (r == BELOW_NORMAL_PRIORITY_CLASS)
|
|
|
1476 *priority = UV_PRIORITY_BELOW_NORMAL;
|
|
|
1477 else /* IDLE_PRIORITY_CLASS */
|
|
|
1478 *priority = UV_PRIORITY_LOW;
|
|
|
1479
|
|
|
1480 r = 0;
|
|
|
1481 }
|
|
|
1482
|
|
|
1483 CloseHandle(handle);
|
|
|
1484 return r;
|
|
|
1485 }
|
|
|
1486
|
|
|
1487
|
|
|
1488 int uv_os_setpriority(uv_pid_t pid, int priority) {
|
|
|
1489 HANDLE handle;
|
|
|
1490 int priority_class;
|
|
|
1491 int r;
|
|
|
1492
|
|
|
1493 /* Map Unix nice values to Windows priority classes. */
|
|
|
1494 if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)
|
|
|
1495 return UV_EINVAL;
|
|
|
1496 else if (priority < UV_PRIORITY_HIGH)
|
|
|
1497 priority_class = REALTIME_PRIORITY_CLASS;
|
|
|
1498 else if (priority < UV_PRIORITY_ABOVE_NORMAL)
|
|
|
1499 priority_class = HIGH_PRIORITY_CLASS;
|
|
|
1500 else if (priority < UV_PRIORITY_NORMAL)
|
|
|
1501 priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
|
|
|
1502 else if (priority < UV_PRIORITY_BELOW_NORMAL)
|
|
|
1503 priority_class = NORMAL_PRIORITY_CLASS;
|
|
|
1504 else if (priority < UV_PRIORITY_LOW)
|
|
|
1505 priority_class = BELOW_NORMAL_PRIORITY_CLASS;
|
|
|
1506 else
|
|
|
1507 priority_class = IDLE_PRIORITY_CLASS;
|
|
|
1508
|
|
|
1509 r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle);
|
|
|
1510
|
|
|
1511 if (r != 0)
|
|
|
1512 return r;
|
|
|
1513
|
|
|
1514 if (SetPriorityClass(handle, priority_class) == 0)
|
|
|
1515 r = uv_translate_sys_error(GetLastError());
|
|
|
1516
|
|
|
1517 CloseHandle(handle);
|
|
|
1518 return r;
|
|
|
1519 }
|
|
|
1520
|
|
|
1521 int uv_thread_getpriority(uv_thread_t tid, int* priority) {
|
|
|
1522 int r;
|
|
|
1523
|
|
|
1524 if (priority == NULL)
|
|
|
1525 return UV_EINVAL;
|
|
|
1526
|
|
|
1527 r = GetThreadPriority(tid);
|
|
|
1528 if (r == THREAD_PRIORITY_ERROR_RETURN)
|
|
|
1529 return uv_translate_sys_error(GetLastError());
|
|
|
1530
|
|
|
1531 *priority = r;
|
|
|
1532 return 0;
|
|
|
1533 }
|
|
|
1534
|
|
|
1535 int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
|
|
1536 int r;
|
|
|
1537
|
|
|
1538 switch (priority) {
|
|
|
1539 case UV_THREAD_PRIORITY_HIGHEST:
|
|
|
1540 r = SetThreadPriority(tid, THREAD_PRIORITY_HIGHEST);
|
|
|
1541 break;
|
|
|
1542 case UV_THREAD_PRIORITY_ABOVE_NORMAL:
|
|
|
1543 r = SetThreadPriority(tid, THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
1544 break;
|
|
|
1545 case UV_THREAD_PRIORITY_NORMAL:
|
|
|
1546 r = SetThreadPriority(tid, THREAD_PRIORITY_NORMAL);
|
|
|
1547 break;
|
|
|
1548 case UV_THREAD_PRIORITY_BELOW_NORMAL:
|
|
|
1549 r = SetThreadPriority(tid, THREAD_PRIORITY_BELOW_NORMAL);
|
|
|
1550 break;
|
|
|
1551 case UV_THREAD_PRIORITY_LOWEST:
|
|
|
1552 r = SetThreadPriority(tid, THREAD_PRIORITY_LOWEST);
|
|
|
1553 break;
|
|
|
1554 default:
|
|
|
1555 return 0;
|
|
|
1556 }
|
|
|
1557
|
|
|
1558 if (r == 0)
|
|
|
1559 return uv_translate_sys_error(GetLastError());
|
|
|
1560
|
|
|
1561 return 0;
|
|
|
1562 }
|
|
|
1563
|
|
|
1564 int uv_os_uname(uv_utsname_t* buffer) {
|
|
|
1565 /* Implementation loosely based on
|
|
|
1566 https://github.com/gagern/gnulib/blob/master/lib/uname.c */
|
|
|
1567 OSVERSIONINFOW os_info;
|
|
|
1568 SYSTEM_INFO system_info;
|
|
|
1569 HKEY registry_key;
|
|
|
1570 WCHAR product_name_w[256];
|
|
|
1571 DWORD product_name_w_size;
|
|
|
1572 size_t version_size;
|
|
|
1573 int processor_level;
|
|
|
1574 int r;
|
|
|
1575
|
|
|
1576 if (buffer == NULL)
|
|
|
1577 return UV_EINVAL;
|
|
|
1578
|
|
|
1579 uv__once_init();
|
|
|
1580 os_info.dwOSVersionInfoSize = sizeof(os_info);
|
|
|
1581 os_info.szCSDVersion[0] = L'\0';
|
|
|
1582
|
|
|
1583 pRtlGetVersion(&os_info);
|
|
|
1584
|
|
|
1585 /* Populate the version field. */
|
|
|
1586 version_size = 0;
|
|
|
1587 r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
|
1588 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
|
|
|
1589 0,
|
|
|
1590 KEY_QUERY_VALUE | KEY_WOW64_64KEY,
|
|
|
1591 ®istry_key);
|
|
|
1592
|
|
|
1593 if (r == ERROR_SUCCESS) {
|
|
|
1594 product_name_w_size = sizeof(product_name_w);
|
|
|
1595 r = RegGetValueW(registry_key,
|
|
|
1596 NULL,
|
|
|
1597 L"ProductName",
|
|
|
1598 RRF_RT_REG_SZ,
|
|
|
1599 NULL,
|
|
|
1600 (PVOID) product_name_w,
|
|
|
1601 &product_name_w_size);
|
|
|
1602 RegCloseKey(registry_key);
|
|
|
1603
|
|
|
1604 if (r == ERROR_SUCCESS) {
|
|
|
1605 /* Windows 11 shares dwMajorVersion with Windows 10
|
|
|
1606 * this workaround tries to disambiguate that by checking
|
|
|
1607 * if the dwBuildNumber is from Windows 11 releases (>= 22000).
|
|
|
1608 *
|
|
|
1609 * This workaround replaces the ProductName key value
|
|
|
1610 * from "Windows 10 *" to "Windows 11 *" */
|
|
|
1611 if (os_info.dwMajorVersion == 10 &&
|
|
|
1612 os_info.dwBuildNumber >= 22000 &&
|
|
|
1613 product_name_w_size >= ARRAY_SIZE(L"Windows 10")) {
|
|
|
1614 /* If ProductName starts with "Windows 10" */
|
|
|
1615 if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) {
|
|
|
1616 /* Bump 10 to 11 */
|
|
|
1617 product_name_w[9] = '1';
|
|
|
1618 }
|
|
|
1619 }
|
|
|
1620
|
|
|
1621 version_size = sizeof(buffer->version);
|
|
|
1622 r = uv__copy_utf16_to_utf8(product_name_w,
|
|
|
1623 -1,
|
|
|
1624 buffer->version,
|
|
|
1625 &version_size);
|
|
|
1626 if (r)
|
|
|
1627 goto error;
|
|
|
1628 }
|
|
|
1629 }
|
|
|
1630
|
|
|
1631 /* Append service pack information to the version if present. */
|
|
|
1632 if (os_info.szCSDVersion[0] != L'\0') {
|
|
|
1633 if (version_size > 0)
|
|
|
1634 buffer->version[version_size++] = ' ';
|
|
|
1635
|
|
|
1636 version_size = sizeof(buffer->version) - version_size;
|
|
|
1637 r = uv__copy_utf16_to_utf8(os_info.szCSDVersion,
|
|
|
1638 -1,
|
|
|
1639 buffer->version +
|
|
|
1640 sizeof(buffer->version) - version_size,
|
|
|
1641 &version_size);
|
|
|
1642 if (r)
|
|
|
1643 goto error;
|
|
|
1644 }
|
|
|
1645
|
|
|
1646 /* Populate the sysname field. */
|
|
|
1647 #ifdef __MINGW32__
|
|
|
1648 r = snprintf(buffer->sysname,
|
|
|
1649 sizeof(buffer->sysname),
|
|
|
1650 "MINGW32_NT-%u.%u",
|
|
|
1651 (unsigned int) os_info.dwMajorVersion,
|
|
|
1652 (unsigned int) os_info.dwMinorVersion);
|
|
|
1653 assert((size_t)r < sizeof(buffer->sysname));
|
|
|
1654 #else
|
|
|
1655 uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname));
|
|
|
1656 #endif
|
|
|
1657
|
|
|
1658 /* Populate the release field. */
|
|
|
1659 r = snprintf(buffer->release,
|
|
|
1660 sizeof(buffer->release),
|
|
|
1661 "%d.%d.%d",
|
|
|
1662 (unsigned int) os_info.dwMajorVersion,
|
|
|
1663 (unsigned int) os_info.dwMinorVersion,
|
|
|
1664 (unsigned int) os_info.dwBuildNumber);
|
|
|
1665 assert((size_t)r < sizeof(buffer->release));
|
|
|
1666
|
|
|
1667 /* Populate the machine field. */
|
|
|
1668 GetSystemInfo(&system_info);
|
|
|
1669
|
|
|
1670 switch (system_info.wProcessorArchitecture) {
|
|
|
1671 case PROCESSOR_ARCHITECTURE_AMD64:
|
|
|
1672 uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine));
|
|
|
1673 break;
|
|
|
1674 case PROCESSOR_ARCHITECTURE_IA64:
|
|
|
1675 uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine));
|
|
|
1676 break;
|
|
|
1677 case PROCESSOR_ARCHITECTURE_INTEL:
|
|
|
1678 uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine));
|
|
|
1679
|
|
|
1680 if (system_info.wProcessorLevel > 3) {
|
|
|
1681 processor_level = system_info.wProcessorLevel < 6 ?
|
|
|
1682 system_info.wProcessorLevel : 6;
|
|
|
1683 buffer->machine[1] = '0' + processor_level;
|
|
|
1684 }
|
|
|
1685
|
|
|
1686 break;
|
|
|
1687 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
|
|
|
1688 uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine));
|
|
|
1689 break;
|
|
|
1690 case PROCESSOR_ARCHITECTURE_MIPS:
|
|
|
1691 uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine));
|
|
|
1692 break;
|
|
|
1693 case PROCESSOR_ARCHITECTURE_ALPHA:
|
|
|
1694 case PROCESSOR_ARCHITECTURE_ALPHA64:
|
|
|
1695 uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine));
|
|
|
1696 break;
|
|
|
1697 case PROCESSOR_ARCHITECTURE_PPC:
|
|
|
1698 uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine));
|
|
|
1699 break;
|
|
|
1700 case PROCESSOR_ARCHITECTURE_SHX:
|
|
|
1701 uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine));
|
|
|
1702 break;
|
|
|
1703 case PROCESSOR_ARCHITECTURE_ARM:
|
|
|
1704 uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine));
|
|
|
1705 break;
|
|
|
1706 default:
|
|
|
1707 uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine));
|
|
|
1708 break;
|
|
|
1709 }
|
|
|
1710
|
|
|
1711 return 0;
|
|
|
1712
|
|
|
1713 error:
|
|
|
1714 buffer->sysname[0] = '\0';
|
|
|
1715 buffer->release[0] = '\0';
|
|
|
1716 buffer->version[0] = '\0';
|
|
|
1717 buffer->machine[0] = '\0';
|
|
|
1718 return r;
|
|
|
1719 }
|
|
|
1720
|
|
|
1721 int uv_gettimeofday(uv_timeval64_t* tv) {
|
|
|
1722 /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
|
|
|
1723 const uint64_t epoch = (uint64_t) 116444736000000000ULL;
|
|
|
1724 FILETIME file_time;
|
|
|
1725 ULARGE_INTEGER ularge;
|
|
|
1726
|
|
|
1727 if (tv == NULL)
|
|
|
1728 return UV_EINVAL;
|
|
|
1729
|
|
|
1730 GetSystemTimeAsFileTime(&file_time);
|
|
|
1731 ularge.LowPart = file_time.dwLowDateTime;
|
|
|
1732 ularge.HighPart = file_time.dwHighDateTime;
|
|
|
1733 tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
|
|
|
1734 tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
|
|
|
1735 return 0;
|
|
|
1736 }
|
|
|
1737
|
|
|
1738 int uv__random_rtlgenrandom(void* buf, size_t buflen) {
|
|
|
1739 if (buflen == 0)
|
|
|
1740 return 0;
|
|
|
1741
|
|
|
1742 if (SystemFunction036(buf, buflen) == FALSE)
|
|
|
1743 return UV_EIO;
|
|
|
1744
|
|
|
1745 return 0;
|
|
|
1746 }
|
|
|
1747
|
|
|
1748 void uv_sleep(unsigned int msec) {
|
|
|
1749 Sleep(msec);
|
|
|
1750 }
|