Mercurial
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 ®istry_key); | |
| 1592 | |
| 1593 if (r == ERROR_SUCCESS) { | |
| 1594 product_name_w_size = sizeof(product_name_w); | |
| 1595 r = RegGetValueW(registry_key, | |
| 1596 NULL, | |
| 1597 L"ProductName", | |
| 1598 RRF_RT_REG_SZ, | |
| 1599 NULL, | |
| 1600 (PVOID) product_name_w, | |
| 1601 &product_name_w_size); | |
| 1602 RegCloseKey(registry_key); | |
| 1603 | |
| 1604 if (r == ERROR_SUCCESS) { | |
| 1605 /* Windows 11 shares dwMajorVersion with Windows 10 | |
| 1606 * this workaround tries to disambiguate that by checking | |
| 1607 * if the dwBuildNumber is from Windows 11 releases (>= 22000). | |
| 1608 * | |
| 1609 * This workaround replaces the ProductName key value | |
| 1610 * from "Windows 10 *" to "Windows 11 *" */ | |
| 1611 if (os_info.dwMajorVersion == 10 && | |
| 1612 os_info.dwBuildNumber >= 22000 && | |
| 1613 product_name_w_size >= ARRAY_SIZE(L"Windows 10")) { | |
| 1614 /* If ProductName starts with "Windows 10" */ | |
| 1615 if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) { | |
| 1616 /* Bump 10 to 11 */ | |
| 1617 product_name_w[9] = '1'; | |
| 1618 } | |
| 1619 } | |
| 1620 | |
| 1621 version_size = sizeof(buffer->version); | |
| 1622 r = uv__copy_utf16_to_utf8(product_name_w, | |
| 1623 -1, | |
| 1624 buffer->version, | |
| 1625 &version_size); | |
| 1626 if (r) | |
| 1627 goto error; | |
| 1628 } | |
| 1629 } | |
| 1630 | |
| 1631 /* Append service pack information to the version if present. */ | |
| 1632 if (os_info.szCSDVersion[0] != L'\0') { | |
| 1633 if (version_size > 0) | |
| 1634 buffer->version[version_size++] = ' '; | |
| 1635 | |
| 1636 version_size = sizeof(buffer->version) - version_size; | |
| 1637 r = uv__copy_utf16_to_utf8(os_info.szCSDVersion, | |
| 1638 -1, | |
| 1639 buffer->version + | |
| 1640 sizeof(buffer->version) - version_size, | |
| 1641 &version_size); | |
| 1642 if (r) | |
| 1643 goto error; | |
| 1644 } | |
| 1645 | |
| 1646 /* Populate the sysname field. */ | |
| 1647 #ifdef __MINGW32__ | |
| 1648 r = snprintf(buffer->sysname, | |
| 1649 sizeof(buffer->sysname), | |
| 1650 "MINGW32_NT-%u.%u", | |
| 1651 (unsigned int) os_info.dwMajorVersion, | |
| 1652 (unsigned int) os_info.dwMinorVersion); | |
| 1653 assert((size_t)r < sizeof(buffer->sysname)); | |
| 1654 #else | |
| 1655 uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname)); | |
| 1656 #endif | |
| 1657 | |
| 1658 /* Populate the release field. */ | |
| 1659 r = snprintf(buffer->release, | |
| 1660 sizeof(buffer->release), | |
| 1661 "%d.%d.%d", | |
| 1662 (unsigned int) os_info.dwMajorVersion, | |
| 1663 (unsigned int) os_info.dwMinorVersion, | |
| 1664 (unsigned int) os_info.dwBuildNumber); | |
| 1665 assert((size_t)r < sizeof(buffer->release)); | |
| 1666 | |
| 1667 /* Populate the machine field. */ | |
| 1668 GetSystemInfo(&system_info); | |
| 1669 | |
| 1670 switch (system_info.wProcessorArchitecture) { | |
| 1671 case PROCESSOR_ARCHITECTURE_AMD64: | |
| 1672 uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine)); | |
| 1673 break; | |
| 1674 case PROCESSOR_ARCHITECTURE_IA64: | |
| 1675 uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine)); | |
| 1676 break; | |
| 1677 case PROCESSOR_ARCHITECTURE_INTEL: | |
| 1678 uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine)); | |
| 1679 | |
| 1680 if (system_info.wProcessorLevel > 3) { | |
| 1681 processor_level = system_info.wProcessorLevel < 6 ? | |
| 1682 system_info.wProcessorLevel : 6; | |
| 1683 buffer->machine[1] = '0' + processor_level; | |
| 1684 } | |
| 1685 | |
| 1686 break; | |
| 1687 case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: | |
| 1688 uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine)); | |
| 1689 break; | |
| 1690 case PROCESSOR_ARCHITECTURE_MIPS: | |
| 1691 uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine)); | |
| 1692 break; | |
| 1693 case PROCESSOR_ARCHITECTURE_ALPHA: | |
| 1694 case PROCESSOR_ARCHITECTURE_ALPHA64: | |
| 1695 uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine)); | |
| 1696 break; | |
| 1697 case PROCESSOR_ARCHITECTURE_PPC: | |
| 1698 uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine)); | |
| 1699 break; | |
| 1700 case PROCESSOR_ARCHITECTURE_SHX: | |
| 1701 uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine)); | |
| 1702 break; | |
| 1703 case PROCESSOR_ARCHITECTURE_ARM: | |
| 1704 uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine)); | |
| 1705 break; | |
| 1706 default: | |
| 1707 uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine)); | |
| 1708 break; | |
| 1709 } | |
| 1710 | |
| 1711 return 0; | |
| 1712 | |
| 1713 error: | |
| 1714 buffer->sysname[0] = '\0'; | |
| 1715 buffer->release[0] = '\0'; | |
| 1716 buffer->version[0] = '\0'; | |
| 1717 buffer->machine[0] = '\0'; | |
| 1718 return r; | |
| 1719 } | |
| 1720 | |
| 1721 int uv_gettimeofday(uv_timeval64_t* tv) { | |
| 1722 /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */ | |
| 1723 const uint64_t epoch = (uint64_t) 116444736000000000ULL; | |
| 1724 FILETIME file_time; | |
| 1725 ULARGE_INTEGER ularge; | |
| 1726 | |
| 1727 if (tv == NULL) | |
| 1728 return UV_EINVAL; | |
| 1729 | |
| 1730 GetSystemTimeAsFileTime(&file_time); | |
| 1731 ularge.LowPart = file_time.dwLowDateTime; | |
| 1732 ularge.HighPart = file_time.dwHighDateTime; | |
| 1733 tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L); | |
| 1734 tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10); | |
| 1735 return 0; | |
| 1736 } | |
| 1737 | |
| 1738 int uv__random_rtlgenrandom(void* buf, size_t buflen) { | |
| 1739 if (buflen == 0) | |
| 1740 return 0; | |
| 1741 | |
| 1742 if (SystemFunction036(buf, buflen) == FALSE) | |
| 1743 return UV_EIO; | |
| 1744 | |
| 1745 return 0; | |
| 1746 } | |
| 1747 | |
| 1748 void uv_sleep(unsigned int msec) { | |
| 1749 Sleep(msec); | |
| 1750 } |