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 */
uv__util_init(void)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
uv_exepath(char * buffer,size_t * size_ptr)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
uv__cwd(WCHAR ** buf,DWORD * len)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
uv_cwd(char * buffer,size_t * size)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) {
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
uv_chdir(const char * dir)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
uv_loadavg(double avg[3])273 void uv_loadavg(double avg[3]) {
274 /* Can't be implemented */
275 avg[0] = avg[1] = avg[2] = 0;
276 }
277
278
uv_get_free_memory(void)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
uv_get_total_memory(void)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
uv_get_constrained_memory(void)303 uint64_t uv_get_constrained_memory(void) {
304 return 0; /* Memory constraints are unknown. */
305 }
306
307
uv_get_available_memory(void)308 uint64_t uv_get_available_memory(void) {
309 return uv_get_free_memory();
310 }
311
312
uv_os_getpid(void)313 uv_pid_t uv_os_getpid(void) {
314 return GetCurrentProcessId();
315 }
316
317
uv_os_getppid(void)318 uv_pid_t uv_os_getppid(void) {
319 int parent_pid = -1;
320 HANDLE handle;
321 PROCESSENTRY32 pe;
322 DWORD current_pid = GetCurrentProcessId();
323
324 pe.dwSize = sizeof(PROCESSENTRY32);
325 handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
326
327 if (Process32First(handle, &pe)) {
328 do {
329 if (pe.th32ProcessID == current_pid) {
330 parent_pid = pe.th32ParentProcessID;
331 break;
332 }
333 } while( Process32Next(handle, &pe));
334 }
335
336 CloseHandle(handle);
337 return parent_pid;
338 }
339
340
uv_setup_args(int argc,char ** argv)341 char** uv_setup_args(int argc, char** argv) {
342 return argv;
343 }
344
345
uv__process_title_cleanup(void)346 void uv__process_title_cleanup(void) {
347 }
348
349
uv_set_process_title(const char * title)350 int uv_set_process_title(const char* title) {
351 int err;
352 int length;
353 WCHAR* title_w = NULL;
354
355 uv__once_init();
356
357 err = uv__convert_utf8_to_utf16(title, &title_w);
358 if (err)
359 return err;
360
361 /* If the title must be truncated insert a \0 terminator there */
362 length = wcslen(title_w);
363 if (length >= MAX_TITLE_LENGTH)
364 title_w[MAX_TITLE_LENGTH - 1] = L'\0';
365
366 if (!SetConsoleTitleW(title_w)) {
367 err = GetLastError();
368 goto done;
369 }
370
371 EnterCriticalSection(&process_title_lock);
372 uv__free(process_title);
373 process_title = uv__strdup(title);
374 LeaveCriticalSection(&process_title_lock);
375
376 err = 0;
377
378 done:
379 uv__free(title_w);
380 return uv_translate_sys_error(err);
381 }
382
383
uv__get_process_title(void)384 static int uv__get_process_title(void) {
385 WCHAR title_w[MAX_TITLE_LENGTH];
386 DWORD wlen;
387
388 wlen = GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR));
389 if (wlen == 0)
390 return uv_translate_sys_error(GetLastError());
391
392 return uv__convert_utf16_to_utf8(title_w, wlen, &process_title);
393 }
394
395
uv_get_process_title(char * buffer,size_t size)396 int uv_get_process_title(char* buffer, size_t size) {
397 size_t len;
398 int r;
399
400 if (buffer == NULL || size == 0)
401 return UV_EINVAL;
402
403 uv__once_init();
404
405 EnterCriticalSection(&process_title_lock);
406 /*
407 * If the process_title was never read before nor explicitly set,
408 * we must query it with getConsoleTitleW
409 */
410 if (process_title == NULL) {
411 r = uv__get_process_title();
412 if (r) {
413 LeaveCriticalSection(&process_title_lock);
414 return r;
415 }
416 }
417
418 assert(process_title);
419 len = strlen(process_title) + 1;
420
421 if (size < len) {
422 LeaveCriticalSection(&process_title_lock);
423 return UV_ENOBUFS;
424 }
425
426 memcpy(buffer, process_title, len);
427 LeaveCriticalSection(&process_title_lock);
428
429 return 0;
430 }
431
432
433 /* https://github.com/libuv/libuv/issues/1674 */
uv_clock_gettime(uv_clock_id clock_id,uv_timespec64_t * ts)434 int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) {
435 FILETIME ft;
436 int64_t t;
437
438 if (ts == NULL)
439 return UV_EFAULT;
440
441 switch (clock_id) {
442 case UV_CLOCK_MONOTONIC:
443 uv__once_init();
444 t = uv__hrtime(UV__NANOSEC);
445 ts->tv_sec = t / 1000000000;
446 ts->tv_nsec = t % 1000000000;
447 return 0;
448 case UV_CLOCK_REALTIME:
449 GetSystemTimePreciseAsFileTime(&ft);
450 /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */
451 t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime;
452 /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */
453 t -= 116444736000000000ll;
454 /* Now convert to seconds and nanoseconds. */
455 ts->tv_sec = t / 10000000;
456 ts->tv_nsec = t % 10000000 * 100;
457 return 0;
458 }
459
460 return UV_EINVAL;
461 }
462
463
uv_hrtime(void)464 uint64_t uv_hrtime(void) {
465 uv__once_init();
466 return uv__hrtime(UV__NANOSEC);
467 }
468
469
uv__hrtime(unsigned int scale)470 uint64_t uv__hrtime(unsigned int scale) {
471 LARGE_INTEGER counter;
472 double scaled_freq;
473 double result;
474
475 assert(hrtime_frequency_ != 0);
476 assert(scale != 0);
477 if (!QueryPerformanceCounter(&counter)) {
478 uv_fatal_error(GetLastError(), "QueryPerformanceCounter");
479 }
480 assert(counter.QuadPart != 0);
481
482 /* Because we have no guarantee about the order of magnitude of the
483 * performance counter interval, integer math could cause this computation
484 * to overflow. Therefore we resort to floating point math.
485 */
486 scaled_freq = (double) hrtime_frequency_ / scale;
487 result = (double) counter.QuadPart / scaled_freq;
488 return (uint64_t) result;
489 }
490
491
uv_resident_set_memory(size_t * rss)492 int uv_resident_set_memory(size_t* rss) {
493 HANDLE current_process;
494 PROCESS_MEMORY_COUNTERS pmc;
495
496 current_process = GetCurrentProcess();
497
498 if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) {
499 return uv_translate_sys_error(GetLastError());
500 }
501
502 *rss = pmc.WorkingSetSize;
503
504 return 0;
505 }
506
507
uv_uptime(double * uptime)508 int uv_uptime(double* uptime) {
509 *uptime = GetTickCount64() / 1000.0;
510 return 0;
511 }
512
513
uv_available_parallelism(void)514 unsigned int uv_available_parallelism(void) {
515 SYSTEM_INFO info;
516 unsigned rc;
517
518 /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
519 * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
520 */
521 GetSystemInfo(&info);
522
523 rc = info.dwNumberOfProcessors;
524 if (rc < 1)
525 rc = 1;
526
527 return rc;
528 }
529
530
uv_cpu_info(uv_cpu_info_t ** cpu_infos_ptr,int * cpu_count_ptr)531 int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
532 uv_cpu_info_t* cpu_infos;
533 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
534 DWORD sppi_size;
535 SYSTEM_INFO system_info;
536 DWORD cpu_count, i;
537 NTSTATUS status;
538 ULONG result_size;
539 int err;
540 uv_cpu_info_t* cpu_info;
541
542 cpu_infos = NULL;
543 cpu_count = 0;
544 sppi = NULL;
545
546 uv__once_init();
547
548 GetSystemInfo(&system_info);
549 cpu_count = system_info.dwNumberOfProcessors;
550
551 cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos);
552 if (cpu_infos == NULL) {
553 err = ERROR_OUTOFMEMORY;
554 goto error;
555 }
556
557 sppi_size = cpu_count * sizeof(*sppi);
558 sppi = uv__malloc(sppi_size);
559 if (sppi == NULL) {
560 err = ERROR_OUTOFMEMORY;
561 goto error;
562 }
563
564 status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
565 sppi,
566 sppi_size,
567 &result_size);
568 if (!NT_SUCCESS(status)) {
569 err = pRtlNtStatusToDosError(status);
570 goto error;
571 }
572
573 assert(result_size == sppi_size);
574
575 for (i = 0; i < cpu_count; i++) {
576 WCHAR key_name[128];
577 HKEY processor_key;
578 DWORD cpu_speed;
579 DWORD cpu_speed_size = sizeof(cpu_speed);
580 WCHAR cpu_brand[256];
581 DWORD cpu_brand_size = sizeof(cpu_brand);
582 size_t len;
583
584 len = _snwprintf(key_name,
585 ARRAY_SIZE(key_name),
586 L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d",
587 i);
588
589 assert(len > 0 && len < ARRAY_SIZE(key_name));
590
591 err = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
592 key_name,
593 0,
594 KEY_QUERY_VALUE,
595 &processor_key);
596 if (err != ERROR_SUCCESS) {
597 goto error;
598 }
599
600 err = RegQueryValueExW(processor_key,
601 L"~MHz",
602 NULL,
603 NULL,
604 (BYTE*)&cpu_speed,
605 &cpu_speed_size);
606 if (err != ERROR_SUCCESS) {
607 RegCloseKey(processor_key);
608 goto error;
609 }
610
611 err = RegQueryValueExW(processor_key,
612 L"ProcessorNameString",
613 NULL,
614 NULL,
615 (BYTE*)&cpu_brand,
616 &cpu_brand_size);
617 RegCloseKey(processor_key);
618 if (err != ERROR_SUCCESS)
619 goto error;
620
621 cpu_info = &cpu_infos[i];
622 cpu_info->speed = cpu_speed;
623 cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000;
624 cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart -
625 sppi[i].IdleTime.QuadPart) / 10000;
626 cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000;
627 cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000;
628 cpu_info->cpu_times.nice = 0;
629
630 uv__convert_utf16_to_utf8(cpu_brand,
631 cpu_brand_size / sizeof(WCHAR),
632 &(cpu_info->model));
633 }
634
635 uv__free(sppi);
636
637 *cpu_count_ptr = cpu_count;
638 *cpu_infos_ptr = cpu_infos;
639
640 return 0;
641
642 error:
643 if (cpu_infos != NULL) {
644 /* This is safe because the cpu_infos array is zeroed on allocation. */
645 for (i = 0; i < cpu_count; i++)
646 uv__free(cpu_infos[i].model);
647 }
648
649 uv__free(cpu_infos);
650 uv__free(sppi);
651
652 return uv_translate_sys_error(err);
653 }
654
655
uv_interface_addresses(uv_interface_address_t ** addresses_ptr,int * count_ptr)656 int uv_interface_addresses(uv_interface_address_t** addresses_ptr,
657 int* count_ptr) {
658 IP_ADAPTER_ADDRESSES* win_address_buf;
659 ULONG win_address_buf_size;
660 IP_ADAPTER_ADDRESSES* adapter;
661
662 uv_interface_address_t* uv_address_buf;
663 char* name_buf;
664 size_t uv_address_buf_size;
665 uv_interface_address_t* uv_address;
666
667 int count;
668 ULONG flags;
669
670 *addresses_ptr = NULL;
671 *count_ptr = 0;
672
673 flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
674 GAA_FLAG_SKIP_DNS_SERVER;
675
676 /* Fetch the size of the adapters reported by windows, and then get the list
677 * itself. */
678 win_address_buf_size = 0;
679 win_address_buf = NULL;
680
681 for (;;) {
682 ULONG r;
683
684 /* If win_address_buf is 0, then GetAdaptersAddresses will fail with.
685 * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in
686 * win_address_buf_size. */
687 r = GetAdaptersAddresses(AF_UNSPEC,
688 flags,
689 NULL,
690 win_address_buf,
691 &win_address_buf_size);
692
693 if (r == ERROR_SUCCESS)
694 break;
695
696 uv__free(win_address_buf);
697
698 switch (r) {
699 case ERROR_BUFFER_OVERFLOW:
700 /* This happens when win_address_buf is NULL or too small to hold all
701 * adapters. */
702 win_address_buf = uv__malloc(win_address_buf_size);
703 if (win_address_buf == NULL)
704 return UV_ENOMEM;
705
706 continue;
707
708 case ERROR_NO_DATA: {
709 /* No adapters were found. */
710 uv_address_buf = uv__malloc(1);
711 if (uv_address_buf == NULL)
712 return UV_ENOMEM;
713
714 *count_ptr = 0;
715 *addresses_ptr = uv_address_buf;
716
717 return 0;
718 }
719
720 case ERROR_ADDRESS_NOT_ASSOCIATED:
721 return UV_EAGAIN;
722
723 case ERROR_INVALID_PARAMETER:
724 /* MSDN says:
725 * "This error is returned for any of the following conditions: the
726 * SizePointer parameter is NULL, the Address parameter is not
727 * AF_INET, AF_INET6, or AF_UNSPEC, or the address information for
728 * the parameters requested is greater than ULONG_MAX."
729 * Since the first two conditions are not met, it must be that the
730 * adapter data is too big.
731 */
732 return UV_ENOBUFS;
733
734 default:
735 /* Other (unspecified) errors can happen, but we don't have any special
736 * meaning for them. */
737 assert(r != ERROR_SUCCESS);
738 return uv_translate_sys_error(r);
739 }
740 }
741
742 /* Count the number of enabled interfaces and compute how much space is
743 * needed to store their info. */
744 count = 0;
745 uv_address_buf_size = 0;
746
747 for (adapter = win_address_buf;
748 adapter != NULL;
749 adapter = adapter->Next) {
750 IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
751 int name_size;
752
753 /* Interfaces that are not 'up' should not be reported. Also skip
754 * interfaces that have no associated unicast address, as to avoid
755 * allocating space for the name for this interface. */
756 if (adapter->OperStatus != IfOperStatusUp ||
757 adapter->FirstUnicastAddress == NULL)
758 continue;
759
760 /* Compute the size of the interface name. */
761 name_size = uv_utf16_length_as_wtf8(adapter->FriendlyName, -1);
762 uv_address_buf_size += name_size + 1;
763
764 /* Count the number of addresses associated with this interface, and
765 * compute the size. */
766 for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
767 adapter->FirstUnicastAddress;
768 unicast_address != NULL;
769 unicast_address = unicast_address->Next) {
770 count++;
771 uv_address_buf_size += sizeof(uv_interface_address_t);
772 }
773 }
774
775 /* Allocate space to store interface data plus adapter names. */
776 uv_address_buf = uv__malloc(uv_address_buf_size);
777 if (uv_address_buf == NULL) {
778 uv__free(win_address_buf);
779 return UV_ENOMEM;
780 }
781
782 /* Compute the start of the uv_interface_address_t array, and the place in
783 * the buffer where the interface names will be stored. */
784 uv_address = uv_address_buf;
785 name_buf = (char*) (uv_address_buf + count);
786
787 /* Fill out the output buffer. */
788 for (adapter = win_address_buf;
789 adapter != NULL;
790 adapter = adapter->Next) {
791 IP_ADAPTER_UNICAST_ADDRESS* unicast_address;
792 size_t name_size;
793 int r;
794
795 if (adapter->OperStatus != IfOperStatusUp ||
796 adapter->FirstUnicastAddress == NULL)
797 continue;
798
799 /* Convert the interface name to UTF8. */
800 name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf;
801 r = uv__copy_utf16_to_utf8(adapter->FriendlyName,
802 -1,
803 name_buf,
804 &name_size);
805 if (r) {
806 uv__free(win_address_buf);
807 uv__free(uv_address_buf);
808 return r;
809 }
810 name_size += 1; /* Add NUL byte. */
811
812 /* Add an uv_interface_address_t element for every unicast address. */
813 for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*)
814 adapter->FirstUnicastAddress;
815 unicast_address != NULL;
816 unicast_address = unicast_address->Next) {
817 struct sockaddr* sa;
818 ULONG prefix_len;
819
820 sa = unicast_address->Address.lpSockaddr;
821
822 prefix_len =
823 ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength;
824
825 memset(uv_address, 0, sizeof *uv_address);
826
827 uv_address->name = name_buf;
828
829 if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) {
830 memcpy(uv_address->phys_addr,
831 adapter->PhysicalAddress,
832 sizeof(uv_address->phys_addr));
833 }
834
835 uv_address->is_internal =
836 (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK);
837
838 if (sa->sa_family == AF_INET6) {
839 uv_address->address.address6 = *((struct sockaddr_in6 *) sa);
840
841 uv_address->netmask.netmask6.sin6_family = AF_INET6;
842 memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3);
843 /* This check ensures that we don't write past the size of the data. */
844 if (prefix_len % 8) {
845 uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] =
846 0xff << (8 - prefix_len % 8);
847 }
848
849 } else {
850 uv_address->address.address4 = *((struct sockaddr_in *) sa);
851
852 uv_address->netmask.netmask4.sin_family = AF_INET;
853 uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ?
854 htonl(0xffffffff << (32 - prefix_len)) : 0;
855 }
856
857 uv_address++;
858 }
859
860 name_buf += name_size;
861 }
862
863 uv__free(win_address_buf);
864
865 *addresses_ptr = uv_address_buf;
866 *count_ptr = count;
867
868 return 0;
869 }
870
871
uv_free_interface_addresses(uv_interface_address_t * addresses,int count)872 void uv_free_interface_addresses(uv_interface_address_t* addresses,
873 int count) {
874 uv__free(addresses);
875 }
876
877
uv_getrusage(uv_rusage_t * uv_rusage)878 int uv_getrusage(uv_rusage_t *uv_rusage) {
879 FILETIME createTime, exitTime, kernelTime, userTime;
880 SYSTEMTIME kernelSystemTime, userSystemTime;
881 PROCESS_MEMORY_COUNTERS memCounters;
882 IO_COUNTERS ioCounters;
883 int ret;
884
885 ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
886 if (ret == 0) {
887 return uv_translate_sys_error(GetLastError());
888 }
889
890 ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
891 if (ret == 0) {
892 return uv_translate_sys_error(GetLastError());
893 }
894
895 ret = FileTimeToSystemTime(&userTime, &userSystemTime);
896 if (ret == 0) {
897 return uv_translate_sys_error(GetLastError());
898 }
899
900 ret = GetProcessMemoryInfo(GetCurrentProcess(),
901 &memCounters,
902 sizeof(memCounters));
903 if (ret == 0) {
904 return uv_translate_sys_error(GetLastError());
905 }
906
907 ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
908 if (ret == 0) {
909 return uv_translate_sys_error(GetLastError());
910 }
911
912 memset(uv_rusage, 0, sizeof(*uv_rusage));
913
914 uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
915 userSystemTime.wMinute * 60 +
916 userSystemTime.wSecond;
917 uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
918
919 uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
920 kernelSystemTime.wMinute * 60 +
921 kernelSystemTime.wSecond;
922 uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
923
924 uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
925 uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
926
927 uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
928 uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
929
930 return 0;
931 }
932
933
uv_os_homedir(char * buffer,size_t * size)934 int uv_os_homedir(char* buffer, size_t* size) {
935 uv_passwd_t pwd;
936 size_t len;
937 int r;
938
939 /* Check if the USERPROFILE environment variable is set first. The task of
940 performing input validation on buffer and size is taken care of by
941 uv_os_getenv(). */
942 r = uv_os_getenv("USERPROFILE", buffer, size);
943
944 /* Don't return an error if USERPROFILE was not found. */
945 if (r != UV_ENOENT)
946 return r;
947
948 /* USERPROFILE is not set, so call uv_os_get_passwd() */
949 r = uv_os_get_passwd(&pwd);
950
951 if (r != 0) {
952 return r;
953 }
954
955 len = strlen(pwd.homedir);
956
957 if (len >= *size) {
958 *size = len + 1;
959 uv_os_free_passwd(&pwd);
960 return UV_ENOBUFS;
961 }
962
963 memcpy(buffer, pwd.homedir, len + 1);
964 *size = len;
965 uv_os_free_passwd(&pwd);
966
967 return 0;
968 }
969
970
uv_os_tmpdir(char * buffer,size_t * size)971 int uv_os_tmpdir(char* buffer, size_t* size) {
972 wchar_t *path;
973 size_t len;
974
975 if (buffer == NULL || size == NULL || *size == 0)
976 return UV_EINVAL;
977
978 len = 0;
979 len = GetTempPathW(0, NULL);
980 if (len == 0) {
981 return uv_translate_sys_error(GetLastError());
982 }
983 /* Include space for terminating null char. */
984 len += 1;
985 path = uv__malloc(len * sizeof(wchar_t));
986 if (path == NULL) {
987 return UV_ENOMEM;
988 }
989 len = GetTempPathW(len, path);
990
991 if (len == 0) {
992 uv__free(path);
993 return uv_translate_sys_error(GetLastError());
994 }
995
996 /* The returned directory should not have a trailing slash, unless it points
997 * at a drive root, like c:\. Remove it if needed. */
998 if (path[len - 1] == L'\\' &&
999 !(len == 3 && path[1] == L':')) {
1000 len--;
1001 path[len] = L'\0';
1002 }
1003
1004 return uv__copy_utf16_to_utf8(path, len, buffer, size);
1005 }
1006
1007
1008 /*
1009 * Converts a UTF-16 string into a UTF-8 one. The resulting string is
1010 * null-terminated.
1011 *
1012 * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
1013 * be specified.
1014 */
uv__convert_utf16_to_utf8(const WCHAR * utf16,size_t utf16len,char ** utf8)1015 int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8) {
1016 size_t utf8_len = 0;
1017
1018 if (utf16 == NULL)
1019 return UV_EINVAL;
1020
1021 *utf8 = NULL;
1022 return uv_utf16_to_wtf8(utf16, utf16len, utf8, &utf8_len);
1023 }
1024
1025
1026 /*
1027 * Converts a UTF-8 string into a UTF-16 one. The resulting string is
1028 * null-terminated.
1029 */
uv__convert_utf8_to_utf16(const char * utf8,WCHAR ** utf16)1030 int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16) {
1031 int bufsize;
1032
1033 if (utf8 == NULL)
1034 return UV_EINVAL;
1035
1036 /* Check how much space we need (including NUL). */
1037 bufsize = uv_wtf8_length_as_utf16(utf8);
1038 if (bufsize < 0)
1039 return UV__EINVAL;
1040
1041 /* Allocate the destination buffer. */
1042 *utf16 = uv__malloc(sizeof(WCHAR) * bufsize);
1043
1044 if (*utf16 == NULL)
1045 return UV_ENOMEM;
1046
1047 /* Convert to UTF-16 */
1048 uv_wtf8_to_utf16(utf8, *utf16, bufsize);
1049
1050 return 0;
1051 }
1052
1053
1054 /*
1055 * Converts a UTF-16 string into a UTF-8 one in an existing buffer. The
1056 * resulting string is null-terminated.
1057 *
1058 * If utf16 is null terminated, utf16len can be set to -1, otherwise it must
1059 * be specified.
1060 */
uv__copy_utf16_to_utf8(const WCHAR * utf16buffer,size_t utf16len,char * utf8,size_t * size)1061 int uv__copy_utf16_to_utf8(const WCHAR* utf16buffer, size_t utf16len, char* utf8, size_t *size) {
1062 int r;
1063
1064 if (utf8 == NULL || size == NULL)
1065 return UV_EINVAL;
1066
1067 if (*size == 0) {
1068 *size = uv_utf16_length_as_wtf8(utf16buffer, utf16len);
1069 r = UV_ENOBUFS;
1070 } else {
1071 *size -= 1; /* Reserve space for NUL. */
1072 r = uv_utf16_to_wtf8(utf16buffer, utf16len, &utf8, size);
1073 }
1074 if (r == UV_ENOBUFS)
1075 *size += 1; /* Add space for NUL. */
1076 return r;
1077 }
1078
1079
uv__getpwuid_r(uv_passwd_t * pwd)1080 static int uv__getpwuid_r(uv_passwd_t* pwd) {
1081 HANDLE token;
1082 wchar_t username[UNLEN + 1];
1083 wchar_t *path;
1084 DWORD bufsize;
1085 int r;
1086
1087 if (pwd == NULL)
1088 return UV_EINVAL;
1089
1090 /* Get the home directory using GetUserProfileDirectoryW() */
1091 if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0)
1092 return uv_translate_sys_error(GetLastError());
1093
1094 bufsize = 0;
1095 GetUserProfileDirectoryW(token, NULL, &bufsize);
1096 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1097 r = GetLastError();
1098 CloseHandle(token);
1099 return uv_translate_sys_error(r);
1100 }
1101
1102 path = uv__malloc(bufsize * sizeof(wchar_t));
1103 if (path == NULL) {
1104 CloseHandle(token);
1105 return UV_ENOMEM;
1106 }
1107
1108 if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
1109 r = GetLastError();
1110 CloseHandle(token);
1111 uv__free(path);
1112 return uv_translate_sys_error(r);
1113 }
1114
1115 CloseHandle(token);
1116
1117 /* Get the username using GetUserNameW() */
1118 bufsize = ARRAY_SIZE(username);
1119 if (!GetUserNameW(username, &bufsize)) {
1120 r = GetLastError();
1121 uv__free(path);
1122
1123 /* This should not be possible */
1124 if (r == ERROR_INSUFFICIENT_BUFFER)
1125 return UV_ENOMEM;
1126
1127 return uv_translate_sys_error(r);
1128 }
1129
1130 pwd->homedir = NULL;
1131 r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir);
1132 uv__free(path);
1133
1134 if (r != 0)
1135 return r;
1136
1137 pwd->username = NULL;
1138 r = uv__convert_utf16_to_utf8(username, -1, &pwd->username);
1139
1140 if (r != 0) {
1141 uv__free(pwd->homedir);
1142 return r;
1143 }
1144
1145 pwd->shell = NULL;
1146 pwd->uid = -1;
1147 pwd->gid = -1;
1148
1149 return 0;
1150 }
1151
1152
uv_os_get_passwd(uv_passwd_t * pwd)1153 int uv_os_get_passwd(uv_passwd_t* pwd) {
1154 return uv__getpwuid_r(pwd);
1155 }
1156
1157
uv_os_get_passwd2(uv_passwd_t * pwd,uv_uid_t uid)1158 int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) {
1159 return UV_ENOTSUP;
1160 }
1161
1162
uv_os_get_group(uv_group_t * grp,uv_uid_t gid)1163 int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) {
1164 return UV_ENOTSUP;
1165 }
1166
1167
uv_os_environ(uv_env_item_t ** envitems,int * count)1168 int uv_os_environ(uv_env_item_t** envitems, int* count) {
1169 wchar_t* env;
1170 wchar_t* penv;
1171 int i, cnt;
1172 uv_env_item_t* envitem;
1173
1174 *envitems = NULL;
1175 *count = 0;
1176
1177 env = GetEnvironmentStringsW();
1178 if (env == NULL)
1179 return 0;
1180
1181 for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
1182
1183 *envitems = uv__calloc(i, sizeof(**envitems));
1184 if (*envitems == NULL) {
1185 FreeEnvironmentStringsW(env);
1186 return UV_ENOMEM;
1187 }
1188
1189 penv = env;
1190 cnt = 0;
1191
1192 while (*penv != L'\0' && cnt < i) {
1193 char* buf;
1194 char* ptr;
1195
1196 if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0)
1197 goto fail;
1198
1199 /* Using buf + 1 here because we know that `buf` has length at least 1,
1200 * and some special environment variables on Windows start with a = sign. */
1201 ptr = strchr(buf + 1, '=');
1202 if (ptr == NULL) {
1203 uv__free(buf);
1204 goto do_continue;
1205 }
1206
1207 *ptr = '\0';
1208
1209 envitem = &(*envitems)[cnt];
1210 envitem->name = buf;
1211 envitem->value = ptr + 1;
1212
1213 cnt++;
1214
1215 do_continue:
1216 penv += wcslen(penv) + 1;
1217 }
1218
1219 FreeEnvironmentStringsW(env);
1220
1221 *count = cnt;
1222 return 0;
1223
1224 fail:
1225 FreeEnvironmentStringsW(env);
1226
1227 for (i = 0; i < cnt; i++) {
1228 envitem = &(*envitems)[cnt];
1229 uv__free(envitem->name);
1230 }
1231 uv__free(*envitems);
1232
1233 *envitems = NULL;
1234 *count = 0;
1235 return UV_ENOMEM;
1236 }
1237
1238
uv_os_getenv(const char * name,char * buffer,size_t * size)1239 int uv_os_getenv(const char* name, char* buffer, size_t* size) {
1240 wchar_t fastvar[512];
1241 wchar_t* var;
1242 DWORD varlen;
1243 wchar_t* name_w;
1244 size_t len;
1245 int r;
1246
1247 if (name == NULL || buffer == NULL || size == NULL || *size == 0)
1248 return UV_EINVAL;
1249
1250 r = uv__convert_utf8_to_utf16(name, &name_w);
1251
1252 if (r != 0)
1253 return r;
1254
1255 var = fastvar;
1256 varlen = ARRAY_SIZE(fastvar);
1257
1258 for (;;) {
1259 SetLastError(ERROR_SUCCESS);
1260 len = GetEnvironmentVariableW(name_w, var, varlen);
1261
1262 if (len < varlen)
1263 break;
1264
1265 /* Try repeatedly because we might have been preempted by another thread
1266 * modifying the environment variable just as we're trying to read it.
1267 */
1268 if (var != fastvar)
1269 uv__free(var);
1270
1271 varlen = 1 + len;
1272 var = uv__malloc(varlen * sizeof(*var));
1273
1274 if (var == NULL) {
1275 r = UV_ENOMEM;
1276 goto fail;
1277 }
1278 }
1279
1280 uv__free(name_w);
1281 name_w = NULL;
1282
1283 if (len == 0) {
1284 r = GetLastError();
1285 if (r != ERROR_SUCCESS) {
1286 r = uv_translate_sys_error(r);
1287 goto fail;
1288 }
1289 }
1290
1291 r = uv__copy_utf16_to_utf8(var, len, buffer, size);
1292
1293 fail:
1294
1295 if (name_w != NULL)
1296 uv__free(name_w);
1297
1298 if (var != fastvar)
1299 uv__free(var);
1300
1301 return r;
1302 }
1303
1304
uv_os_setenv(const char * name,const char * value)1305 int uv_os_setenv(const char* name, const char* value) {
1306 wchar_t* name_w;
1307 wchar_t* value_w;
1308 int r;
1309
1310 if (name == NULL || value == NULL)
1311 return UV_EINVAL;
1312
1313 r = uv__convert_utf8_to_utf16(name, &name_w);
1314
1315 if (r != 0)
1316 return r;
1317
1318 r = uv__convert_utf8_to_utf16(value, &value_w);
1319
1320 if (r != 0) {
1321 uv__free(name_w);
1322 return r;
1323 }
1324
1325 r = SetEnvironmentVariableW(name_w, value_w);
1326 uv__free(name_w);
1327 uv__free(value_w);
1328
1329 if (r == 0)
1330 return uv_translate_sys_error(GetLastError());
1331
1332 return 0;
1333 }
1334
1335
uv_os_unsetenv(const char * name)1336 int uv_os_unsetenv(const char* name) {
1337 wchar_t* name_w;
1338 int r;
1339
1340 if (name == NULL)
1341 return UV_EINVAL;
1342
1343 r = uv__convert_utf8_to_utf16(name, &name_w);
1344
1345 if (r != 0)
1346 return r;
1347
1348 r = SetEnvironmentVariableW(name_w, NULL);
1349 uv__free(name_w);
1350
1351 if (r == 0)
1352 return uv_translate_sys_error(GetLastError());
1353
1354 return 0;
1355 }
1356
1357
uv_os_gethostname(char * buffer,size_t * size)1358 int uv_os_gethostname(char* buffer, size_t* size) {
1359 WCHAR buf[UV_MAXHOSTNAMESIZE];
1360
1361 if (buffer == NULL || size == NULL || *size == 0)
1362 return UV_EINVAL;
1363
1364 uv__once_init(); /* Initialize winsock */
1365
1366 if (pGetHostNameW == NULL)
1367 return UV_ENOSYS;
1368
1369 if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0)
1370 return uv_translate_sys_error(WSAGetLastError());
1371
1372 return uv__copy_utf16_to_utf8(buf, -1, buffer, size);
1373 }
1374
1375
uv__get_handle(uv_pid_t pid,int access,HANDLE * handle)1376 static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) {
1377 int r;
1378
1379 if (pid == 0)
1380 *handle = GetCurrentProcess();
1381 else
1382 *handle = OpenProcess(access, FALSE, pid);
1383
1384 if (*handle == NULL) {
1385 r = GetLastError();
1386
1387 if (r == ERROR_INVALID_PARAMETER)
1388 return UV_ESRCH;
1389 else
1390 return uv_translate_sys_error(r);
1391 }
1392
1393 return 0;
1394 }
1395
1396
uv_os_getpriority(uv_pid_t pid,int * priority)1397 int uv_os_getpriority(uv_pid_t pid, int* priority) {
1398 HANDLE handle;
1399 int r;
1400
1401 if (priority == NULL)
1402 return UV_EINVAL;
1403
1404 r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle);
1405
1406 if (r != 0)
1407 return r;
1408
1409 r = GetPriorityClass(handle);
1410
1411 if (r == 0) {
1412 r = uv_translate_sys_error(GetLastError());
1413 } else {
1414 /* Map Windows priority classes to Unix nice values. */
1415 if (r == REALTIME_PRIORITY_CLASS)
1416 *priority = UV_PRIORITY_HIGHEST;
1417 else if (r == HIGH_PRIORITY_CLASS)
1418 *priority = UV_PRIORITY_HIGH;
1419 else if (r == ABOVE_NORMAL_PRIORITY_CLASS)
1420 *priority = UV_PRIORITY_ABOVE_NORMAL;
1421 else if (r == NORMAL_PRIORITY_CLASS)
1422 *priority = UV_PRIORITY_NORMAL;
1423 else if (r == BELOW_NORMAL_PRIORITY_CLASS)
1424 *priority = UV_PRIORITY_BELOW_NORMAL;
1425 else /* IDLE_PRIORITY_CLASS */
1426 *priority = UV_PRIORITY_LOW;
1427
1428 r = 0;
1429 }
1430
1431 CloseHandle(handle);
1432 return r;
1433 }
1434
1435
uv_os_setpriority(uv_pid_t pid,int priority)1436 int uv_os_setpriority(uv_pid_t pid, int priority) {
1437 HANDLE handle;
1438 int priority_class;
1439 int r;
1440
1441 /* Map Unix nice values to Windows priority classes. */
1442 if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW)
1443 return UV_EINVAL;
1444 else if (priority < UV_PRIORITY_HIGH)
1445 priority_class = REALTIME_PRIORITY_CLASS;
1446 else if (priority < UV_PRIORITY_ABOVE_NORMAL)
1447 priority_class = HIGH_PRIORITY_CLASS;
1448 else if (priority < UV_PRIORITY_NORMAL)
1449 priority_class = ABOVE_NORMAL_PRIORITY_CLASS;
1450 else if (priority < UV_PRIORITY_BELOW_NORMAL)
1451 priority_class = NORMAL_PRIORITY_CLASS;
1452 else if (priority < UV_PRIORITY_LOW)
1453 priority_class = BELOW_NORMAL_PRIORITY_CLASS;
1454 else
1455 priority_class = IDLE_PRIORITY_CLASS;
1456
1457 r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle);
1458
1459 if (r != 0)
1460 return r;
1461
1462 if (SetPriorityClass(handle, priority_class) == 0)
1463 r = uv_translate_sys_error(GetLastError());
1464
1465 CloseHandle(handle);
1466 return r;
1467 }
1468
uv_thread_getpriority(uv_thread_t tid,int * priority)1469 int uv_thread_getpriority(uv_thread_t tid, int* priority) {
1470 int r;
1471
1472 if (priority == NULL)
1473 return UV_EINVAL;
1474
1475 r = GetThreadPriority(tid);
1476 if (r == THREAD_PRIORITY_ERROR_RETURN)
1477 return uv_translate_sys_error(GetLastError());
1478
1479 *priority = r;
1480 return 0;
1481 }
1482
uv_thread_setpriority(uv_thread_t tid,int priority)1483 int uv_thread_setpriority(uv_thread_t tid, int priority) {
1484 int r;
1485
1486 switch (priority) {
1487 case UV_THREAD_PRIORITY_HIGHEST:
1488 r = SetThreadPriority(tid, THREAD_PRIORITY_HIGHEST);
1489 break;
1490 case UV_THREAD_PRIORITY_ABOVE_NORMAL:
1491 r = SetThreadPriority(tid, THREAD_PRIORITY_ABOVE_NORMAL);
1492 break;
1493 case UV_THREAD_PRIORITY_NORMAL:
1494 r = SetThreadPriority(tid, THREAD_PRIORITY_NORMAL);
1495 break;
1496 case UV_THREAD_PRIORITY_BELOW_NORMAL:
1497 r = SetThreadPriority(tid, THREAD_PRIORITY_BELOW_NORMAL);
1498 break;
1499 case UV_THREAD_PRIORITY_LOWEST:
1500 r = SetThreadPriority(tid, THREAD_PRIORITY_LOWEST);
1501 break;
1502 default:
1503 return 0;
1504 }
1505
1506 if (r == 0)
1507 return uv_translate_sys_error(GetLastError());
1508
1509 return 0;
1510 }
1511
uv_os_uname(uv_utsname_t * buffer)1512 int uv_os_uname(uv_utsname_t* buffer) {
1513 /* Implementation loosely based on
1514 https://github.com/gagern/gnulib/blob/master/lib/uname.c */
1515 OSVERSIONINFOW os_info;
1516 SYSTEM_INFO system_info;
1517 HKEY registry_key;
1518 WCHAR product_name_w[256];
1519 DWORD product_name_w_size;
1520 size_t version_size;
1521 int processor_level;
1522 int r;
1523
1524 if (buffer == NULL)
1525 return UV_EINVAL;
1526
1527 uv__once_init();
1528 os_info.dwOSVersionInfoSize = sizeof(os_info);
1529 os_info.szCSDVersion[0] = L'\0';
1530
1531 /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
1532 if RtlGetVersion() is not available. */
1533 if (pRtlGetVersion) {
1534 pRtlGetVersion(&os_info);
1535 } else {
1536 /* Silence GetVersionEx() deprecation warning. */
1537 #ifdef _MSC_VER
1538 #pragma warning(suppress : 4996)
1539 #endif
1540 if (GetVersionExW(&os_info) == 0) {
1541 r = uv_translate_sys_error(GetLastError());
1542 goto error;
1543 }
1544 }
1545
1546 /* Populate the version field. */
1547 version_size = 0;
1548 r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1549 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
1550 0,
1551 KEY_QUERY_VALUE | KEY_WOW64_64KEY,
1552 ®istry_key);
1553
1554 if (r == ERROR_SUCCESS) {
1555 product_name_w_size = sizeof(product_name_w);
1556 r = RegGetValueW(registry_key,
1557 NULL,
1558 L"ProductName",
1559 RRF_RT_REG_SZ,
1560 NULL,
1561 (PVOID) product_name_w,
1562 &product_name_w_size);
1563 RegCloseKey(registry_key);
1564
1565 if (r == ERROR_SUCCESS) {
1566 /* Windows 11 shares dwMajorVersion with Windows 10
1567 * this workaround tries to disambiguate that by checking
1568 * if the dwBuildNumber is from Windows 11 releases (>= 22000).
1569 *
1570 * This workaround replaces the ProductName key value
1571 * from "Windows 10 *" to "Windows 11 *" */
1572 if (os_info.dwMajorVersion == 10 &&
1573 os_info.dwBuildNumber >= 22000 &&
1574 product_name_w_size >= ARRAY_SIZE(L"Windows 10")) {
1575 /* If ProductName starts with "Windows 10" */
1576 if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) {
1577 /* Bump 10 to 11 */
1578 product_name_w[9] = '1';
1579 }
1580 }
1581
1582 version_size = sizeof(buffer->version);
1583 r = uv__copy_utf16_to_utf8(product_name_w,
1584 -1,
1585 buffer->version,
1586 &version_size);
1587 if (r)
1588 goto error;
1589 }
1590 }
1591
1592 /* Append service pack information to the version if present. */
1593 if (os_info.szCSDVersion[0] != L'\0') {
1594 if (version_size > 0)
1595 buffer->version[version_size++] = ' ';
1596
1597 version_size = sizeof(buffer->version) - version_size;
1598 r = uv__copy_utf16_to_utf8(os_info.szCSDVersion,
1599 -1,
1600 buffer->version +
1601 sizeof(buffer->version) - version_size,
1602 &version_size);
1603 if (r)
1604 goto error;
1605 }
1606
1607 /* Populate the sysname field. */
1608 #ifdef __MINGW32__
1609 r = snprintf(buffer->sysname,
1610 sizeof(buffer->sysname),
1611 "MINGW32_NT-%u.%u",
1612 (unsigned int) os_info.dwMajorVersion,
1613 (unsigned int) os_info.dwMinorVersion);
1614 assert((size_t)r < sizeof(buffer->sysname));
1615 #else
1616 uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname));
1617 #endif
1618
1619 /* Populate the release field. */
1620 r = snprintf(buffer->release,
1621 sizeof(buffer->release),
1622 "%d.%d.%d",
1623 (unsigned int) os_info.dwMajorVersion,
1624 (unsigned int) os_info.dwMinorVersion,
1625 (unsigned int) os_info.dwBuildNumber);
1626 assert((size_t)r < sizeof(buffer->release));
1627
1628 /* Populate the machine field. */
1629 GetSystemInfo(&system_info);
1630
1631 switch (system_info.wProcessorArchitecture) {
1632 case PROCESSOR_ARCHITECTURE_AMD64:
1633 uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine));
1634 break;
1635 case PROCESSOR_ARCHITECTURE_IA64:
1636 uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine));
1637 break;
1638 case PROCESSOR_ARCHITECTURE_INTEL:
1639 uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine));
1640
1641 if (system_info.wProcessorLevel > 3) {
1642 processor_level = system_info.wProcessorLevel < 6 ?
1643 system_info.wProcessorLevel : 6;
1644 buffer->machine[1] = '0' + processor_level;
1645 }
1646
1647 break;
1648 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
1649 uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine));
1650 break;
1651 case PROCESSOR_ARCHITECTURE_MIPS:
1652 uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine));
1653 break;
1654 case PROCESSOR_ARCHITECTURE_ALPHA:
1655 case PROCESSOR_ARCHITECTURE_ALPHA64:
1656 uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine));
1657 break;
1658 case PROCESSOR_ARCHITECTURE_PPC:
1659 uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine));
1660 break;
1661 case PROCESSOR_ARCHITECTURE_SHX:
1662 uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine));
1663 break;
1664 case PROCESSOR_ARCHITECTURE_ARM:
1665 uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine));
1666 break;
1667 default:
1668 uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine));
1669 break;
1670 }
1671
1672 return 0;
1673
1674 error:
1675 buffer->sysname[0] = '\0';
1676 buffer->release[0] = '\0';
1677 buffer->version[0] = '\0';
1678 buffer->machine[0] = '\0';
1679 return r;
1680 }
1681
uv_gettimeofday(uv_timeval64_t * tv)1682 int uv_gettimeofday(uv_timeval64_t* tv) {
1683 /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
1684 const uint64_t epoch = (uint64_t) 116444736000000000ULL;
1685 FILETIME file_time;
1686 ULARGE_INTEGER ularge;
1687
1688 if (tv == NULL)
1689 return UV_EINVAL;
1690
1691 GetSystemTimeAsFileTime(&file_time);
1692 ularge.LowPart = file_time.dwLowDateTime;
1693 ularge.HighPart = file_time.dwHighDateTime;
1694 tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
1695 tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
1696 return 0;
1697 }
1698
uv__random_rtlgenrandom(void * buf,size_t buflen)1699 int uv__random_rtlgenrandom(void* buf, size_t buflen) {
1700 if (buflen == 0)
1701 return 0;
1702
1703 if (SystemFunction036(buf, buflen) == FALSE)
1704 return UV_EIO;
1705
1706 return 0;
1707 }
1708
uv_sleep(unsigned int msec)1709 void uv_sleep(unsigned int msec) {
1710 Sleep(msec);
1711 }
1712