comparison third_party/libuv/src/win/util.c @ 160:948de3f54cea

[ThirdParty] Added libuv
author June Park <parkjune1995@gmail.com>
date Wed, 14 Jan 2026 19:39:52 -0800
parents
children
comparison
equal deleted inserted replaced
159:05cf9467a1c3 160:948de3f54cea
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 &registry_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 }