| /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to |
| * deal in the Software without restriction, including without limitation the |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| * sell copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include <assert.h> |
| #include <direct.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| #include <wchar.h> |
| |
| #include "uv.h" |
| #include "internal.h" |
| |
| #include <winsock2.h> |
| #include <winperf.h> |
| #include <iphlpapi.h> |
| #include <psapi.h> |
| #include <tlhelp32.h> |
| #include <windows.h> |
| #include <userenv.h> |
| #include <math.h> |
| |
| /* |
| * Max title length; the only thing MSDN tells us about the maximum length |
| * of the console title is that it is smaller than 64K. However in practice |
| * it is much smaller, and there is no way to figure out what the exact length |
| * of the title is or can be, at least not on XP. To make it even more |
| * annoying, GetConsoleTitle fails when the buffer to be read into is bigger |
| * than the actual maximum length. So we make a conservative guess here; |
| * just don't put the novel you're writing in the title, unless the plot |
| * survives truncation. |
| */ |
| #define MAX_TITLE_LENGTH 8192 |
| |
| /* The number of nanoseconds in one second. */ |
| #define UV__NANOSEC 1000000000 |
| |
| /* Max user name length, from iphlpapi.h */ |
| #ifndef UNLEN |
| # define UNLEN 256 |
| #endif |
| |
| |
| /* Maximum environment variable size, including the terminating null */ |
| #define MAX_ENV_VAR_LENGTH 32767 |
| |
| /* Cached copy of the process title, plus a mutex guarding it. */ |
| static char *process_title; |
| static CRITICAL_SECTION process_title_lock; |
| |
| /* Interval (in seconds) of the high-resolution clock. */ |
| static double hrtime_interval_ = 0; |
| |
| |
| /* |
| * One-time initialization code for functionality defined in util.c. |
| */ |
| void uv__util_init(void) { |
| LARGE_INTEGER perf_frequency; |
| |
| /* Initialize process title access mutex. */ |
| InitializeCriticalSection(&process_title_lock); |
| |
| /* Retrieve high-resolution timer frequency |
| * and precompute its reciprocal. |
| */ |
| if (QueryPerformanceFrequency(&perf_frequency)) { |
| hrtime_interval_ = 1.0 / perf_frequency.QuadPart; |
| } else { |
| hrtime_interval_= 0; |
| } |
| } |
| |
| |
| int uv_exepath(char* buffer, size_t* size_ptr) { |
| int utf8_len, utf16_buffer_len, utf16_len; |
| WCHAR* utf16_buffer; |
| int err; |
| |
| if (buffer == NULL || size_ptr == NULL || *size_ptr == 0) { |
| return UV_EINVAL; |
| } |
| |
| if (*size_ptr > 32768) { |
| /* Windows paths can never be longer than this. */ |
| utf16_buffer_len = 32768; |
| } else { |
| utf16_buffer_len = (int) *size_ptr; |
| } |
| |
| utf16_buffer = (WCHAR*) uv__malloc(sizeof(WCHAR) * utf16_buffer_len); |
| if (!utf16_buffer) { |
| return UV_ENOMEM; |
| } |
| |
| /* Get the path as UTF-16. */ |
| utf16_len = GetModuleFileNameW(NULL, utf16_buffer, utf16_buffer_len); |
| if (utf16_len <= 0) { |
| err = GetLastError(); |
| goto error; |
| } |
| |
| /* utf16_len contains the length, *not* including the terminating null. */ |
| utf16_buffer[utf16_len] = L'\0'; |
| |
| /* Convert to UTF-8 */ |
| utf8_len = WideCharToMultiByte(CP_UTF8, |
| 0, |
| utf16_buffer, |
| -1, |
| buffer, |
| (int) *size_ptr, |
| NULL, |
| NULL); |
| if (utf8_len == 0) { |
| err = GetLastError(); |
| goto error; |
| } |
| |
| uv__free(utf16_buffer); |
| |
| /* utf8_len *does* include the terminating null at this point, but the |
| * returned size shouldn't. */ |
| *size_ptr = utf8_len - 1; |
| return 0; |
| |
| error: |
| uv__free(utf16_buffer); |
| return uv_translate_sys_error(err); |
| } |
| |
| |
| int uv_cwd(char* buffer, size_t* size) { |
| DWORD utf16_len; |
| WCHAR utf16_buffer[MAX_PATH]; |
| int r; |
| |
| if (buffer == NULL || size == NULL) { |
| return UV_EINVAL; |
| } |
| |
| utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); |
| if (utf16_len == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (utf16_len > MAX_PATH) { |
| /* This should be impossible; however the CRT has a code path to deal with |
| * this scenario, so I added a check anyway. */ |
| return UV_EIO; |
| } |
| |
| /* utf16_len contains the length, *not* including the terminating null. */ |
| utf16_buffer[utf16_len] = L'\0'; |
| |
| /* The returned directory should not have a trailing slash, unless it points |
| * at a drive root, like c:\. Remove it if needed. */ |
| if (utf16_buffer[utf16_len - 1] == L'\\' && |
| !(utf16_len == 3 && utf16_buffer[1] == L':')) { |
| utf16_len--; |
| utf16_buffer[utf16_len] = L'\0'; |
| } |
| |
| /* Check how much space we need */ |
| r = WideCharToMultiByte(CP_UTF8, |
| 0, |
| utf16_buffer, |
| -1, |
| NULL, |
| 0, |
| NULL, |
| NULL); |
| if (r == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (r > (int) *size) { |
| *size = r; |
| return UV_ENOBUFS; |
| } |
| |
| /* Convert to UTF-8 */ |
| r = WideCharToMultiByte(CP_UTF8, |
| 0, |
| utf16_buffer, |
| -1, |
| buffer, |
| *size > INT_MAX ? INT_MAX : (int) *size, |
| NULL, |
| NULL); |
| if (r == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| *size = r - 1; |
| return 0; |
| } |
| |
| |
| int uv_chdir(const char* dir) { |
| WCHAR utf16_buffer[MAX_PATH]; |
| size_t utf16_len; |
| WCHAR drive_letter, env_var[4]; |
| |
| if (dir == NULL) { |
| return UV_EINVAL; |
| } |
| |
| if (MultiByteToWideChar(CP_UTF8, |
| 0, |
| dir, |
| -1, |
| utf16_buffer, |
| MAX_PATH) == 0) { |
| DWORD error = GetLastError(); |
| /* The maximum length of the current working directory is 260 chars, |
| * including terminating null. If it doesn't fit, the path name must be too |
| * long. */ |
| if (error == ERROR_INSUFFICIENT_BUFFER) { |
| return UV_ENAMETOOLONG; |
| } else { |
| return uv_translate_sys_error(error); |
| } |
| } |
| |
| if (!SetCurrentDirectoryW(utf16_buffer)) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| /* Windows stores the drive-local path in an "hidden" environment variable, |
| * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update |
| * this, so we'll have to do it. */ |
| utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); |
| if (utf16_len == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (utf16_len > MAX_PATH) { |
| return UV_EIO; |
| } |
| |
| /* The returned directory should not have a trailing slash, unless it points |
| * at a drive root, like c:\. Remove it if needed. */ |
| if (utf16_buffer[utf16_len - 1] == L'\\' && |
| !(utf16_len == 3 && utf16_buffer[1] == L':')) { |
| utf16_len--; |
| utf16_buffer[utf16_len] = L'\0'; |
| } |
| |
| if (utf16_len < 2 || utf16_buffer[1] != L':') { |
| /* Doesn't look like a drive letter could be there - probably an UNC path. |
| * TODO: Need to handle win32 namespaces like \\?\C:\ ? */ |
| drive_letter = 0; |
| } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') { |
| drive_letter = utf16_buffer[0]; |
| } else if (utf16_buffer[0] >= L'a' && utf16_buffer[0] <= L'z') { |
| /* Convert to uppercase. */ |
| drive_letter = utf16_buffer[0] - L'a' + L'A'; |
| } else { |
| /* Not valid. */ |
| drive_letter = 0; |
| } |
| |
| if (drive_letter != 0) { |
| /* Construct the environment variable name and set it. */ |
| env_var[0] = L'='; |
| env_var[1] = drive_letter; |
| env_var[2] = L':'; |
| env_var[3] = L'\0'; |
| |
| if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| void uv_loadavg(double avg[3]) { |
| /* Can't be implemented */ |
| avg[0] = avg[1] = avg[2] = 0; |
| } |
| |
| |
| uint64_t uv_get_free_memory(void) { |
| MEMORYSTATUSEX memory_status; |
| memory_status.dwLength = sizeof(memory_status); |
| |
| if (!GlobalMemoryStatusEx(&memory_status)) { |
| return -1; |
| } |
| |
| return (uint64_t)memory_status.ullAvailPhys; |
| } |
| |
| |
| uint64_t uv_get_total_memory(void) { |
| MEMORYSTATUSEX memory_status; |
| memory_status.dwLength = sizeof(memory_status); |
| |
| if (!GlobalMemoryStatusEx(&memory_status)) { |
| return -1; |
| } |
| |
| return (uint64_t)memory_status.ullTotalPhys; |
| } |
| |
| |
| uint64_t uv_get_constrained_memory(void) { |
| return 0; /* Memory constraints are unknown. */ |
| } |
| |
| |
| uv_pid_t uv_os_getpid(void) { |
| return GetCurrentProcessId(); |
| } |
| |
| |
| uv_pid_t uv_os_getppid(void) { |
| int parent_pid = -1; |
| HANDLE handle; |
| PROCESSENTRY32 pe; |
| DWORD current_pid = GetCurrentProcessId(); |
| |
| pe.dwSize = sizeof(PROCESSENTRY32); |
| handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
| |
| if (Process32First(handle, &pe)) { |
| do { |
| if (pe.th32ProcessID == current_pid) { |
| parent_pid = pe.th32ParentProcessID; |
| break; |
| } |
| } while( Process32Next(handle, &pe)); |
| } |
| |
| CloseHandle(handle); |
| return parent_pid; |
| } |
| |
| |
| char** uv_setup_args(int argc, char** argv) { |
| return argv; |
| } |
| |
| |
| int uv_set_process_title(const char* title) { |
| int err; |
| int length; |
| WCHAR* title_w = NULL; |
| |
| uv__once_init(); |
| |
| /* Find out how big the buffer for the wide-char title must be */ |
| length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); |
| if (!length) { |
| err = GetLastError(); |
| goto done; |
| } |
| |
| /* Convert to wide-char string */ |
| title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length); |
| if (!title_w) { |
| uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); |
| } |
| |
| length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length); |
| if (!length) { |
| err = GetLastError(); |
| goto done; |
| } |
| |
| /* If the title must be truncated insert a \0 terminator there */ |
| if (length > MAX_TITLE_LENGTH) { |
| title_w[MAX_TITLE_LENGTH - 1] = L'\0'; |
| } |
| |
| if (!SetConsoleTitleW(title_w)) { |
| err = GetLastError(); |
| goto done; |
| } |
| |
| EnterCriticalSection(&process_title_lock); |
| uv__free(process_title); |
| process_title = uv__strdup(title); |
| LeaveCriticalSection(&process_title_lock); |
| |
| err = 0; |
| |
| done: |
| uv__free(title_w); |
| return uv_translate_sys_error(err); |
| } |
| |
| |
| static int uv__get_process_title(void) { |
| WCHAR title_w[MAX_TITLE_LENGTH]; |
| |
| if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { |
| return -1; |
| } |
| |
| if (uv__convert_utf16_to_utf8(title_w, -1, &process_title) != 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| |
| int uv_get_process_title(char* buffer, size_t size) { |
| size_t len; |
| |
| if (buffer == NULL || size == 0) |
| return UV_EINVAL; |
| |
| uv__once_init(); |
| |
| EnterCriticalSection(&process_title_lock); |
| /* |
| * If the process_title was never read before nor explicitly set, |
| * we must query it with getConsoleTitleW |
| */ |
| if (!process_title && uv__get_process_title() == -1) { |
| LeaveCriticalSection(&process_title_lock); |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| assert(process_title); |
| len = strlen(process_title) + 1; |
| |
| if (size < len) { |
| LeaveCriticalSection(&process_title_lock); |
| return UV_ENOBUFS; |
| } |
| |
| memcpy(buffer, process_title, len); |
| LeaveCriticalSection(&process_title_lock); |
| |
| return 0; |
| } |
| |
| |
| uint64_t uv_hrtime(void) { |
| uv__once_init(); |
| return uv__hrtime(UV__NANOSEC); |
| } |
| |
| uint64_t uv__hrtime(double scale) { |
| LARGE_INTEGER counter; |
| |
| /* If the performance interval is zero, there's no support. */ |
| if (hrtime_interval_ == 0) { |
| return 0; |
| } |
| |
| if (!QueryPerformanceCounter(&counter)) { |
| return 0; |
| } |
| |
| /* Because we have no guarantee about the order of magnitude of the |
| * performance counter interval, integer math could cause this computation |
| * to overflow. Therefore we resort to floating point math. |
| */ |
| return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale); |
| } |
| |
| |
| int uv_resident_set_memory(size_t* rss) { |
| HANDLE current_process; |
| PROCESS_MEMORY_COUNTERS pmc; |
| |
| current_process = GetCurrentProcess(); |
| |
| if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| *rss = pmc.WorkingSetSize; |
| |
| return 0; |
| } |
| |
| |
| int uv_uptime(double* uptime) { |
| BYTE stack_buffer[4096]; |
| BYTE* malloced_buffer = NULL; |
| BYTE* buffer = (BYTE*) stack_buffer; |
| size_t buffer_size = sizeof(stack_buffer); |
| DWORD data_size; |
| |
| PERF_DATA_BLOCK* data_block; |
| PERF_OBJECT_TYPE* object_type; |
| PERF_COUNTER_DEFINITION* counter_definition; |
| |
| DWORD i; |
| |
| for (;;) { |
| LONG result; |
| |
| data_size = (DWORD) buffer_size; |
| result = RegQueryValueExW(HKEY_PERFORMANCE_DATA, |
| L"2", |
| NULL, |
| NULL, |
| buffer, |
| &data_size); |
| if (result == ERROR_SUCCESS) { |
| break; |
| } else if (result != ERROR_MORE_DATA) { |
| *uptime = 0; |
| return uv_translate_sys_error(result); |
| } |
| |
| buffer_size *= 2; |
| /* Don't let the buffer grow infinitely. */ |
| if (buffer_size > 1 << 20) { |
| goto internalError; |
| } |
| |
| uv__free(malloced_buffer); |
| |
| buffer = malloced_buffer = (BYTE*) uv__malloc(buffer_size); |
| if (malloced_buffer == NULL) { |
| *uptime = 0; |
| return UV_ENOMEM; |
| } |
| } |
| |
| if (data_size < sizeof(*data_block)) |
| goto internalError; |
| |
| data_block = (PERF_DATA_BLOCK*) buffer; |
| |
| if (wmemcmp(data_block->Signature, L"PERF", 4) != 0) |
| goto internalError; |
| |
| if (data_size < data_block->HeaderLength + sizeof(*object_type)) |
| goto internalError; |
| |
| object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength); |
| |
| if (object_type->NumInstances != PERF_NO_INSTANCES) |
| goto internalError; |
| |
| counter_definition = (PERF_COUNTER_DEFINITION*) (buffer + |
| data_block->HeaderLength + object_type->HeaderLength); |
| for (i = 0; i < object_type->NumCounters; i++) { |
| if ((BYTE*) counter_definition + sizeof(*counter_definition) > |
| buffer + data_size) { |
| break; |
| } |
| |
| if (counter_definition->CounterNameTitleIndex == 674 && |
| counter_definition->CounterSize == sizeof(uint64_t)) { |
| if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size || |
| !(counter_definition->CounterType & PERF_OBJECT_TIMER)) { |
| goto internalError; |
| } else { |
| BYTE* address = (BYTE*) object_type + object_type->DefinitionLength + |
| counter_definition->CounterOffset; |
| uint64_t value = *((uint64_t*) address); |
| *uptime = floor((double) (object_type->PerfTime.QuadPart - value) / |
| (double) object_type->PerfFreq.QuadPart); |
| uv__free(malloced_buffer); |
| return 0; |
| } |
| } |
| |
| counter_definition = (PERF_COUNTER_DEFINITION*) |
| ((BYTE*) counter_definition + counter_definition->ByteLength); |
| } |
| |
| /* If we get here, the uptime value was not found. */ |
| uv__free(malloced_buffer); |
| *uptime = 0; |
| return UV_ENOSYS; |
| |
| internalError: |
| uv__free(malloced_buffer); |
| *uptime = 0; |
| return UV_EIO; |
| } |
| |
| |
| int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { |
| uv_cpu_info_t* cpu_infos; |
| SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi; |
| DWORD sppi_size; |
| SYSTEM_INFO system_info; |
| DWORD cpu_count, i; |
| NTSTATUS status; |
| ULONG result_size; |
| int err; |
| uv_cpu_info_t* cpu_info; |
| |
| cpu_infos = NULL; |
| cpu_count = 0; |
| sppi = NULL; |
| |
| uv__once_init(); |
| |
| GetSystemInfo(&system_info); |
| cpu_count = system_info.dwNumberOfProcessors; |
| |
| cpu_infos = uv__calloc(cpu_count, sizeof *cpu_infos); |
| if (cpu_infos == NULL) { |
| err = ERROR_OUTOFMEMORY; |
| goto error; |
| } |
| |
| sppi_size = cpu_count * sizeof(*sppi); |
| sppi = uv__malloc(sppi_size); |
| if (sppi == NULL) { |
| err = ERROR_OUTOFMEMORY; |
| goto error; |
| } |
| |
| status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, |
| sppi, |
| sppi_size, |
| &result_size); |
| if (!NT_SUCCESS(status)) { |
| err = pRtlNtStatusToDosError(status); |
| goto error; |
| } |
| |
| assert(result_size == sppi_size); |
| |
| for (i = 0; i < cpu_count; i++) { |
| WCHAR key_name[128]; |
| HKEY processor_key; |
| DWORD cpu_speed; |
| DWORD cpu_speed_size = sizeof(cpu_speed); |
| WCHAR cpu_brand[256]; |
| DWORD cpu_brand_size = sizeof(cpu_brand); |
| size_t len; |
| |
| len = _snwprintf(key_name, |
| ARRAY_SIZE(key_name), |
| L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", |
| i); |
| |
| assert(len > 0 && len < ARRAY_SIZE(key_name)); |
| |
| err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
| key_name, |
| 0, |
| KEY_QUERY_VALUE, |
| &processor_key); |
| if (err != ERROR_SUCCESS) { |
| goto error; |
| } |
| |
| err = RegQueryValueExW(processor_key, |
| L"~MHz", |
| NULL, |
| NULL, |
| (BYTE*)&cpu_speed, |
| &cpu_speed_size); |
| if (err != ERROR_SUCCESS) { |
| RegCloseKey(processor_key); |
| goto error; |
| } |
| |
| err = RegQueryValueExW(processor_key, |
| L"ProcessorNameString", |
| NULL, |
| NULL, |
| (BYTE*)&cpu_brand, |
| &cpu_brand_size); |
| RegCloseKey(processor_key); |
| if (err != ERROR_SUCCESS) |
| goto error; |
| |
| cpu_info = &cpu_infos[i]; |
| cpu_info->speed = cpu_speed; |
| cpu_info->cpu_times.user = sppi[i].UserTime.QuadPart / 10000; |
| cpu_info->cpu_times.sys = (sppi[i].KernelTime.QuadPart - |
| sppi[i].IdleTime.QuadPart) / 10000; |
| cpu_info->cpu_times.idle = sppi[i].IdleTime.QuadPart / 10000; |
| cpu_info->cpu_times.irq = sppi[i].InterruptTime.QuadPart / 10000; |
| cpu_info->cpu_times.nice = 0; |
| |
| uv__convert_utf16_to_utf8(cpu_brand, |
| cpu_brand_size / sizeof(WCHAR), |
| &(cpu_info->model)); |
| } |
| |
| uv__free(sppi); |
| |
| *cpu_count_ptr = cpu_count; |
| *cpu_infos_ptr = cpu_infos; |
| |
| return 0; |
| |
| error: |
| if (cpu_infos != NULL) { |
| /* This is safe because the cpu_infos array is zeroed on allocation. */ |
| for (i = 0; i < cpu_count; i++) |
| uv__free(cpu_infos[i].model); |
| } |
| |
| uv__free(cpu_infos); |
| uv__free(sppi); |
| |
| return uv_translate_sys_error(err); |
| } |
| |
| |
| void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { |
| int i; |
| |
| for (i = 0; i < count; i++) { |
| uv__free(cpu_infos[i].model); |
| } |
| |
| uv__free(cpu_infos); |
| } |
| |
| |
| static int is_windows_version_or_greater(DWORD os_major, |
| DWORD os_minor, |
| WORD service_pack_major, |
| WORD service_pack_minor) { |
| OSVERSIONINFOEX osvi; |
| DWORDLONG condition_mask = 0; |
| int op = VER_GREATER_EQUAL; |
| |
| /* Initialize the OSVERSIONINFOEX structure. */ |
| ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); |
| osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
| osvi.dwMajorVersion = os_major; |
| osvi.dwMinorVersion = os_minor; |
| osvi.wServicePackMajor = service_pack_major; |
| osvi.wServicePackMinor = service_pack_minor; |
| |
| /* Initialize the condition mask. */ |
| VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op); |
| VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op); |
| VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op); |
| VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op); |
| |
| /* Perform the test. */ |
| return (int) VerifyVersionInfo( |
| &osvi, |
| VER_MAJORVERSION | VER_MINORVERSION | |
| VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, |
| condition_mask); |
| } |
| |
| |
| static int address_prefix_match(int family, |
| struct sockaddr* address, |
| struct sockaddr* prefix_address, |
| int prefix_len) { |
| uint8_t* address_data; |
| uint8_t* prefix_address_data; |
| int i; |
| |
| assert(address->sa_family == family); |
| assert(prefix_address->sa_family == family); |
| |
| if (family == AF_INET6) { |
| address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr); |
| prefix_address_data = |
| (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr); |
| } else { |
| address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr); |
| prefix_address_data = |
| (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr); |
| } |
| |
| for (i = 0; i < prefix_len >> 3; i++) { |
| if (address_data[i] != prefix_address_data[i]) |
| return 0; |
| } |
| |
| if (prefix_len % 8) |
| return prefix_address_data[i] == |
| (address_data[i] & (0xff << (8 - prefix_len % 8))); |
| |
| return 1; |
| } |
| |
| |
| int uv_interface_addresses(uv_interface_address_t** addresses_ptr, |
| int* count_ptr) { |
| IP_ADAPTER_ADDRESSES* win_address_buf; |
| ULONG win_address_buf_size; |
| IP_ADAPTER_ADDRESSES* adapter; |
| |
| uv_interface_address_t* uv_address_buf; |
| char* name_buf; |
| size_t uv_address_buf_size; |
| uv_interface_address_t* uv_address; |
| |
| int count; |
| |
| int is_vista_or_greater; |
| ULONG flags; |
| |
| *addresses_ptr = NULL; |
| *count_ptr = 0; |
| |
| is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0); |
| if (is_vista_or_greater) { |
| flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | |
| GAA_FLAG_SKIP_DNS_SERVER; |
| } else { |
| /* We need at least XP SP1. */ |
| if (!is_windows_version_or_greater(5, 1, 1, 0)) |
| return UV_ENOTSUP; |
| |
| flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | |
| GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX; |
| } |
| |
| |
| /* Fetch the size of the adapters reported by windows, and then get the list |
| * itself. */ |
| win_address_buf_size = 0; |
| win_address_buf = NULL; |
| |
| for (;;) { |
| ULONG r; |
| |
| /* If win_address_buf is 0, then GetAdaptersAddresses will fail with. |
| * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in |
| * win_address_buf_size. */ |
| r = GetAdaptersAddresses(AF_UNSPEC, |
| flags, |
| NULL, |
| win_address_buf, |
| &win_address_buf_size); |
| |
| if (r == ERROR_SUCCESS) |
| break; |
| |
| uv__free(win_address_buf); |
| |
| switch (r) { |
| case ERROR_BUFFER_OVERFLOW: |
| /* This happens when win_address_buf is NULL or too small to hold all |
| * adapters. */ |
| win_address_buf = uv__malloc(win_address_buf_size); |
| if (win_address_buf == NULL) |
| return UV_ENOMEM; |
| |
| continue; |
| |
| case ERROR_NO_DATA: { |
| /* No adapters were found. */ |
| uv_address_buf = uv__malloc(1); |
| if (uv_address_buf == NULL) |
| return UV_ENOMEM; |
| |
| *count_ptr = 0; |
| *addresses_ptr = uv_address_buf; |
| |
| return 0; |
| } |
| |
| case ERROR_ADDRESS_NOT_ASSOCIATED: |
| return UV_EAGAIN; |
| |
| case ERROR_INVALID_PARAMETER: |
| /* MSDN says: |
| * "This error is returned for any of the following conditions: the |
| * SizePointer parameter is NULL, the Address parameter is not |
| * AF_INET, AF_INET6, or AF_UNSPEC, or the address information for |
| * the parameters requested is greater than ULONG_MAX." |
| * Since the first two conditions are not met, it must be that the |
| * adapter data is too big. |
| */ |
| return UV_ENOBUFS; |
| |
| default: |
| /* Other (unspecified) errors can happen, but we don't have any special |
| * meaning for them. */ |
| assert(r != ERROR_SUCCESS); |
| return uv_translate_sys_error(r); |
| } |
| } |
| |
| /* Count the number of enabled interfaces and compute how much space is |
| * needed to store their info. */ |
| count = 0; |
| uv_address_buf_size = 0; |
| |
| for (adapter = win_address_buf; |
| adapter != NULL; |
| adapter = adapter->Next) { |
| IP_ADAPTER_UNICAST_ADDRESS* unicast_address; |
| int name_size; |
| |
| /* Interfaces that are not 'up' should not be reported. Also skip |
| * interfaces that have no associated unicast address, as to avoid |
| * allocating space for the name for this interface. */ |
| if (adapter->OperStatus != IfOperStatusUp || |
| adapter->FirstUnicastAddress == NULL) |
| continue; |
| |
| /* Compute the size of the interface name. */ |
| name_size = WideCharToMultiByte(CP_UTF8, |
| 0, |
| adapter->FriendlyName, |
| -1, |
| NULL, |
| 0, |
| NULL, |
| FALSE); |
| if (name_size <= 0) { |
| uv__free(win_address_buf); |
| return uv_translate_sys_error(GetLastError()); |
| } |
| uv_address_buf_size += name_size; |
| |
| /* Count the number of addresses associated with this interface, and |
| * compute the size. */ |
| for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) |
| adapter->FirstUnicastAddress; |
| unicast_address != NULL; |
| unicast_address = unicast_address->Next) { |
| count++; |
| uv_address_buf_size += sizeof(uv_interface_address_t); |
| } |
| } |
| |
| /* Allocate space to store interface data plus adapter names. */ |
| uv_address_buf = uv__malloc(uv_address_buf_size); |
| if (uv_address_buf == NULL) { |
| uv__free(win_address_buf); |
| return UV_ENOMEM; |
| } |
| |
| /* Compute the start of the uv_interface_address_t array, and the place in |
| * the buffer where the interface names will be stored. */ |
| uv_address = uv_address_buf; |
| name_buf = (char*) (uv_address_buf + count); |
| |
| /* Fill out the output buffer. */ |
| for (adapter = win_address_buf; |
| adapter != NULL; |
| adapter = adapter->Next) { |
| IP_ADAPTER_UNICAST_ADDRESS* unicast_address; |
| int name_size; |
| size_t max_name_size; |
| |
| if (adapter->OperStatus != IfOperStatusUp || |
| adapter->FirstUnicastAddress == NULL) |
| continue; |
| |
| /* Convert the interface name to UTF8. */ |
| max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; |
| if (max_name_size > (size_t) INT_MAX) |
| max_name_size = INT_MAX; |
| name_size = WideCharToMultiByte(CP_UTF8, |
| 0, |
| adapter->FriendlyName, |
| -1, |
| name_buf, |
| (int) max_name_size, |
| NULL, |
| FALSE); |
| if (name_size <= 0) { |
| uv__free(win_address_buf); |
| uv__free(uv_address_buf); |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| /* Add an uv_interface_address_t element for every unicast address. */ |
| for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) |
| adapter->FirstUnicastAddress; |
| unicast_address != NULL; |
| unicast_address = unicast_address->Next) { |
| struct sockaddr* sa; |
| ULONG prefix_len; |
| |
| sa = unicast_address->Address.lpSockaddr; |
| |
| /* XP has no OnLinkPrefixLength field. */ |
| if (is_vista_or_greater) { |
| prefix_len = |
| ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; |
| } else { |
| /* Prior to Windows Vista the FirstPrefix pointed to the list with |
| * single prefix for each IP address assigned to the adapter. |
| * Order of FirstPrefix does not match order of FirstUnicastAddress, |
| * so we need to find corresponding prefix. |
| */ |
| IP_ADAPTER_PREFIX* prefix; |
| prefix_len = 0; |
| |
| for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) { |
| /* We want the longest matching prefix. */ |
| if (prefix->Address.lpSockaddr->sa_family != sa->sa_family || |
| prefix->PrefixLength <= prefix_len) |
| continue; |
| |
| if (address_prefix_match(sa->sa_family, sa, |
| prefix->Address.lpSockaddr, prefix->PrefixLength)) { |
| prefix_len = prefix->PrefixLength; |
| } |
| } |
| |
| /* If there is no matching prefix information, return a single-host |
| * subnet mask (e.g. 255.255.255.255 for IPv4). |
| */ |
| if (!prefix_len) |
| prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32; |
| } |
| |
| memset(uv_address, 0, sizeof *uv_address); |
| |
| uv_address->name = name_buf; |
| |
| if (adapter->PhysicalAddressLength == sizeof(uv_address->phys_addr)) { |
| memcpy(uv_address->phys_addr, |
| adapter->PhysicalAddress, |
| sizeof(uv_address->phys_addr)); |
| } |
| |
| uv_address->is_internal = |
| (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK); |
| |
| if (sa->sa_family == AF_INET6) { |
| uv_address->address.address6 = *((struct sockaddr_in6 *) sa); |
| |
| uv_address->netmask.netmask6.sin6_family = AF_INET6; |
| memset(uv_address->netmask.netmask6.sin6_addr.s6_addr, 0xff, prefix_len >> 3); |
| /* This check ensures that we don't write past the size of the data. */ |
| if (prefix_len % 8) { |
| uv_address->netmask.netmask6.sin6_addr.s6_addr[prefix_len >> 3] = |
| 0xff << (8 - prefix_len % 8); |
| } |
| |
| } else { |
| uv_address->address.address4 = *((struct sockaddr_in *) sa); |
| |
| uv_address->netmask.netmask4.sin_family = AF_INET; |
| uv_address->netmask.netmask4.sin_addr.s_addr = (prefix_len > 0) ? |
| htonl(0xffffffff << (32 - prefix_len)) : 0; |
| } |
| |
| uv_address++; |
| } |
| |
| name_buf += name_size; |
| } |
| |
| uv__free(win_address_buf); |
| |
| *addresses_ptr = uv_address_buf; |
| *count_ptr = count; |
| |
| return 0; |
| } |
| |
| |
| void uv_free_interface_addresses(uv_interface_address_t* addresses, |
| int count) { |
| uv__free(addresses); |
| } |
| |
| |
| int uv_getrusage(uv_rusage_t *uv_rusage) { |
| FILETIME createTime, exitTime, kernelTime, userTime; |
| SYSTEMTIME kernelSystemTime, userSystemTime; |
| PROCESS_MEMORY_COUNTERS memCounters; |
| IO_COUNTERS ioCounters; |
| int ret; |
| |
| ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime); |
| if (ret == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime); |
| if (ret == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| ret = FileTimeToSystemTime(&userTime, &userSystemTime); |
| if (ret == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| ret = GetProcessMemoryInfo(GetCurrentProcess(), |
| &memCounters, |
| sizeof(memCounters)); |
| if (ret == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); |
| if (ret == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| memset(uv_rusage, 0, sizeof(*uv_rusage)); |
| |
| uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 + |
| userSystemTime.wMinute * 60 + |
| userSystemTime.wSecond; |
| uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000; |
| |
| uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 + |
| kernelSystemTime.wMinute * 60 + |
| kernelSystemTime.wSecond; |
| uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000; |
| |
| uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount; |
| uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024; |
| |
| uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount; |
| uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount; |
| |
| return 0; |
| } |
| |
| |
| int uv_os_homedir(char* buffer, size_t* size) { |
| uv_passwd_t pwd; |
| size_t len; |
| int r; |
| |
| /* Check if the USERPROFILE environment variable is set first. The task of |
| performing input validation on buffer and size is taken care of by |
| uv_os_getenv(). */ |
| r = uv_os_getenv("USERPROFILE", buffer, size); |
| |
| /* Don't return an error if USERPROFILE was not found. */ |
| if (r != UV_ENOENT) |
| return r; |
| |
| /* USERPROFILE is not set, so call uv__getpwuid_r() */ |
| r = uv__getpwuid_r(&pwd); |
| |
| if (r != 0) { |
| return r; |
| } |
| |
| len = strlen(pwd.homedir); |
| |
| if (len >= *size) { |
| *size = len + 1; |
| uv_os_free_passwd(&pwd); |
| return UV_ENOBUFS; |
| } |
| |
| memcpy(buffer, pwd.homedir, len + 1); |
| *size = len; |
| uv_os_free_passwd(&pwd); |
| |
| return 0; |
| } |
| |
| |
| int uv_os_tmpdir(char* buffer, size_t* size) { |
| wchar_t path[MAX_PATH + 1]; |
| DWORD bufsize; |
| size_t len; |
| |
| if (buffer == NULL || size == NULL || *size == 0) |
| return UV_EINVAL; |
| |
| len = GetTempPathW(MAX_PATH + 1, path); |
| |
| if (len == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (len > MAX_PATH + 1) { |
| /* This should not be possible */ |
| return UV_EIO; |
| } |
| |
| /* The returned directory should not have a trailing slash, unless it points |
| * at a drive root, like c:\. Remove it if needed. */ |
| if (path[len - 1] == L'\\' && |
| !(len == 3 && path[1] == L':')) { |
| len--; |
| path[len] = L'\0'; |
| } |
| |
| /* Check how much space we need */ |
| bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); |
| |
| if (bufsize == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (bufsize > *size) { |
| *size = bufsize; |
| return UV_ENOBUFS; |
| } |
| |
| /* Convert to UTF-8 */ |
| bufsize = WideCharToMultiByte(CP_UTF8, |
| 0, |
| path, |
| -1, |
| buffer, |
| *size, |
| NULL, |
| NULL); |
| |
| if (bufsize == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| *size = bufsize - 1; |
| return 0; |
| } |
| |
| |
| void uv_os_free_passwd(uv_passwd_t* pwd) { |
| if (pwd == NULL) |
| return; |
| |
| uv__free(pwd->username); |
| uv__free(pwd->homedir); |
| pwd->username = NULL; |
| pwd->homedir = NULL; |
| } |
| |
| |
| /* |
| * Converts a UTF-16 string into a UTF-8 one. The resulting string is |
| * null-terminated. |
| * |
| * If utf16 is null terminated, utf16len can be set to -1, otherwise it must |
| * be specified. |
| */ |
| int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) { |
| DWORD bufsize; |
| |
| if (utf16 == NULL) |
| return UV_EINVAL; |
| |
| /* Check how much space we need */ |
| bufsize = WideCharToMultiByte(CP_UTF8, |
| 0, |
| utf16, |
| utf16len, |
| NULL, |
| 0, |
| NULL, |
| NULL); |
| |
| if (bufsize == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| /* Allocate the destination buffer adding an extra byte for the terminating |
| * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so |
| * we do it ourselves always, just in case. */ |
| *utf8 = uv__malloc(bufsize + 1); |
| |
| if (*utf8 == NULL) |
| return UV_ENOMEM; |
| |
| /* Convert to UTF-8 */ |
| bufsize = WideCharToMultiByte(CP_UTF8, |
| 0, |
| utf16, |
| utf16len, |
| *utf8, |
| bufsize, |
| NULL, |
| NULL); |
| |
| if (bufsize == 0) { |
| uv__free(*utf8); |
| *utf8 = NULL; |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| (*utf8)[bufsize] = '\0'; |
| return 0; |
| } |
| |
| |
| /* |
| * Converts a UTF-8 string into a UTF-16 one. The resulting string is |
| * null-terminated. |
| * |
| * If utf8 is null terminated, utf8len can be set to -1, otherwise it must |
| * be specified. |
| */ |
| int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { |
| int bufsize; |
| |
| if (utf8 == NULL) |
| return UV_EINVAL; |
| |
| /* Check how much space we need */ |
| bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0); |
| |
| if (bufsize == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| /* Allocate the destination buffer adding an extra byte for the terminating |
| * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so |
| * we do it ourselves always, just in case. */ |
| *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1)); |
| |
| if (*utf16 == NULL) |
| return UV_ENOMEM; |
| |
| /* Convert to UTF-16 */ |
| bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize); |
| |
| if (bufsize == 0) { |
| uv__free(*utf16); |
| *utf16 = NULL; |
| return uv_translate_sys_error(GetLastError()); |
| } |
| |
| (*utf16)[bufsize] = '\0'; |
| return 0; |
| } |
| |
| |
| int uv__getpwuid_r(uv_passwd_t* pwd) { |
| HANDLE token; |
| wchar_t username[UNLEN + 1]; |
| wchar_t path[MAX_PATH]; |
| DWORD bufsize; |
| int r; |
| |
| if (pwd == NULL) |
| return UV_EINVAL; |
| |
| /* Get the home directory using GetUserProfileDirectoryW() */ |
| if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| bufsize = ARRAY_SIZE(path); |
| if (!GetUserProfileDirectoryW(token, path, &bufsize)) { |
| r = GetLastError(); |
| CloseHandle(token); |
| |
| /* This should not be possible */ |
| if (r == ERROR_INSUFFICIENT_BUFFER) |
| return UV_ENOMEM; |
| |
| return uv_translate_sys_error(r); |
| } |
| |
| CloseHandle(token); |
| |
| /* Get the username using GetUserNameW() */ |
| bufsize = ARRAY_SIZE(username); |
| if (!GetUserNameW(username, &bufsize)) { |
| r = GetLastError(); |
| |
| /* This should not be possible */ |
| if (r == ERROR_INSUFFICIENT_BUFFER) |
| return UV_ENOMEM; |
| |
| return uv_translate_sys_error(r); |
| } |
| |
| pwd->homedir = NULL; |
| r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir); |
| |
| if (r != 0) |
| return r; |
| |
| pwd->username = NULL; |
| r = uv__convert_utf16_to_utf8(username, -1, &pwd->username); |
| |
| if (r != 0) { |
| uv__free(pwd->homedir); |
| return r; |
| } |
| |
| pwd->shell = NULL; |
| pwd->uid = -1; |
| pwd->gid = -1; |
| |
| return 0; |
| } |
| |
| |
| int uv_os_get_passwd(uv_passwd_t* pwd) { |
| return uv__getpwuid_r(pwd); |
| } |
| |
| |
| int uv_os_getenv(const char* name, char* buffer, size_t* size) { |
| wchar_t var[MAX_ENV_VAR_LENGTH]; |
| wchar_t* name_w; |
| DWORD bufsize; |
| size_t len; |
| int r; |
| |
| if (name == NULL || buffer == NULL || size == NULL || *size == 0) |
| return UV_EINVAL; |
| |
| r = uv__convert_utf8_to_utf16(name, -1, &name_w); |
| |
| if (r != 0) |
| return r; |
| |
| len = GetEnvironmentVariableW(name_w, var, MAX_ENV_VAR_LENGTH); |
| uv__free(name_w); |
| assert(len < MAX_ENV_VAR_LENGTH); /* len does not include the null */ |
| |
| if (len == 0) { |
| r = GetLastError(); |
| |
| if (r == ERROR_ENVVAR_NOT_FOUND) |
| return UV_ENOENT; |
| |
| return uv_translate_sys_error(r); |
| } |
| |
| /* Check how much space we need */ |
| bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL); |
| |
| if (bufsize == 0) { |
| return uv_translate_sys_error(GetLastError()); |
| } else if (bufsize > *size) { |
| *size = bufsize; |
| return UV_ENOBUFS; |
| } |
| |
| /* Convert to UTF-8 */ |
| bufsize = WideCharToMultiByte(CP_UTF8, |
| 0, |
| var, |
| -1, |
| buffer, |
| *size, |
| NULL, |
| NULL); |
| |
| if (bufsize == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| *size = bufsize - 1; |
| return 0; |
| } |
| |
| |
| int uv_os_setenv(const char* name, const char* value) { |
| wchar_t* name_w; |
| wchar_t* value_w; |
| int r; |
| |
| if (name == NULL || value == NULL) |
| return UV_EINVAL; |
| |
| r = uv__convert_utf8_to_utf16(name, -1, &name_w); |
| |
| if (r != 0) |
| return r; |
| |
| r = uv__convert_utf8_to_utf16(value, -1, &value_w); |
| |
| if (r != 0) { |
| uv__free(name_w); |
| return r; |
| } |
| |
| r = SetEnvironmentVariableW(name_w, value_w); |
| uv__free(name_w); |
| uv__free(value_w); |
| |
| if (r == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| return 0; |
| } |
| |
| |
| int uv_os_unsetenv(const char* name) { |
| wchar_t* name_w; |
| int r; |
| |
| if (name == NULL) |
| return UV_EINVAL; |
| |
| r = uv__convert_utf8_to_utf16(name, -1, &name_w); |
| |
| if (r != 0) |
| return r; |
| |
| r = SetEnvironmentVariableW(name_w, NULL); |
| uv__free(name_w); |
| |
| if (r == 0) |
| return uv_translate_sys_error(GetLastError()); |
| |
| return 0; |
| } |
| |
| |
| int uv_os_gethostname(char* buffer, size_t* size) { |
| char buf[UV_MAXHOSTNAMESIZE]; |
| size_t len; |
| |
| if (buffer == NULL || size == NULL || *size == 0) |
| return UV_EINVAL; |
| |
| uv__once_init(); /* Initialize winsock */ |
| |
| if (gethostname(buf, sizeof(buf)) != 0) |
| return uv_translate_sys_error(WSAGetLastError()); |
| |
| buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ |
| len = strlen(buf); |
| |
| if (len >= *size) { |
| *size = len + 1; |
| return UV_ENOBUFS; |
| } |
| |
| memcpy(buffer, buf, len + 1); |
| *size = len; |
| return 0; |
| } |
| |
| |
| static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) { |
| int r; |
| |
| if (pid == 0) |
| *handle = GetCurrentProcess(); |
| else |
| *handle = OpenProcess(access, FALSE, pid); |
| |
| if (*handle == NULL) { |
| r = GetLastError(); |
| |
| if (r == ERROR_INVALID_PARAMETER) |
| return UV_ESRCH; |
| else |
| return uv_translate_sys_error(r); |
| } |
| |
| return 0; |
| } |
| |
| |
| int uv_os_getpriority(uv_pid_t pid, int* priority) { |
| HANDLE handle; |
| int r; |
| |
| if (priority == NULL) |
| return UV_EINVAL; |
| |
| r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle); |
| |
| if (r != 0) |
| return r; |
| |
| r = GetPriorityClass(handle); |
| |
| if (r == 0) { |
| r = uv_translate_sys_error(GetLastError()); |
| } else { |
| /* Map Windows priority classes to Unix nice values. */ |
| if (r == REALTIME_PRIORITY_CLASS) |
| *priority = UV_PRIORITY_HIGHEST; |
| else if (r == HIGH_PRIORITY_CLASS) |
| *priority = UV_PRIORITY_HIGH; |
| else if (r == ABOVE_NORMAL_PRIORITY_CLASS) |
| *priority = UV_PRIORITY_ABOVE_NORMAL; |
| else if (r == NORMAL_PRIORITY_CLASS) |
| *priority = UV_PRIORITY_NORMAL; |
| else if (r == BELOW_NORMAL_PRIORITY_CLASS) |
| *priority = UV_PRIORITY_BELOW_NORMAL; |
| else /* IDLE_PRIORITY_CLASS */ |
| *priority = UV_PRIORITY_LOW; |
| |
| r = 0; |
| } |
| |
| CloseHandle(handle); |
| return r; |
| } |
| |
| |
| int uv_os_setpriority(uv_pid_t pid, int priority) { |
| HANDLE handle; |
| int priority_class; |
| int r; |
| |
| /* Map Unix nice values to Windows priority classes. */ |
| if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) |
| return UV_EINVAL; |
| else if (priority < UV_PRIORITY_HIGH) |
| priority_class = REALTIME_PRIORITY_CLASS; |
| else if (priority < UV_PRIORITY_ABOVE_NORMAL) |
| priority_class = HIGH_PRIORITY_CLASS; |
| else if (priority < UV_PRIORITY_NORMAL) |
| priority_class = ABOVE_NORMAL_PRIORITY_CLASS; |
| else if (priority < UV_PRIORITY_BELOW_NORMAL) |
| priority_class = NORMAL_PRIORITY_CLASS; |
| else if (priority < UV_PRIORITY_LOW) |
| priority_class = BELOW_NORMAL_PRIORITY_CLASS; |
| else |
| priority_class = IDLE_PRIORITY_CLASS; |
| |
| r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle); |
| |
| if (r != 0) |
| return r; |
| |
| if (SetPriorityClass(handle, priority_class) == 0) |
| r = uv_translate_sys_error(GetLastError()); |
| |
| CloseHandle(handle); |
| return r; |
| } |
| |
| |
| int uv_os_uname(uv_utsname_t* buffer) { |
| /* Implementation loosely based on |
| https://github.com/gagern/gnulib/blob/master/lib/uname.c */ |
| OSVERSIONINFOW os_info; |
| SYSTEM_INFO system_info; |
| HKEY registry_key; |
| WCHAR product_name_w[256]; |
| DWORD product_name_w_size; |
| int version_size; |
| int processor_level; |
| int r; |
| |
| if (buffer == NULL) |
| return UV_EINVAL; |
| |
| uv__once_init(); |
| os_info.dwOSVersionInfoSize = sizeof(os_info); |
| os_info.szCSDVersion[0] = L'\0'; |
| |
| /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx() |
| if RtlGetVersion() is not available. */ |
| if (pRtlGetVersion) { |
| pRtlGetVersion(&os_info); |
| } else { |
| /* Silence GetVersionEx() deprecation warning. */ |
| #pragma warning(suppress : 4996) |
| if (GetVersionExW(&os_info) == 0) { |
| r = uv_translate_sys_error(GetLastError()); |
| goto error; |
| } |
| } |
| |
| /* Populate the version field. */ |
| version_size = 0; |
| r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
| L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", |
| 0, |
| KEY_QUERY_VALUE, |
| ®istry_key); |
| |
| if (r == ERROR_SUCCESS) { |
| product_name_w_size = sizeof(product_name_w); |
| r = RegGetValueW(registry_key, |
| NULL, |
| L"ProductName", |
| RRF_RT_REG_SZ, |
| NULL, |
| (PVOID) product_name_w, |
| &product_name_w_size); |
| RegCloseKey(registry_key); |
| |
| if (r == ERROR_SUCCESS) { |
| version_size = WideCharToMultiByte(CP_UTF8, |
| 0, |
| product_name_w, |
| -1, |
| buffer->version, |
| sizeof(buffer->version), |
| NULL, |
| NULL); |
| if (version_size == 0) { |
| r = uv_translate_sys_error(GetLastError()); |
| goto error; |
| } |
| } |
| } |
| |
| /* Append service pack information to the version if present. */ |
| if (os_info.szCSDVersion[0] != L'\0') { |
| if (version_size > 0) |
| buffer->version[version_size - 1] = ' '; |
| |
| if (WideCharToMultiByte(CP_UTF8, |
| 0, |
| os_info.szCSDVersion, |
| -1, |
| buffer->version + version_size, |
| sizeof(buffer->version) - version_size, |
| NULL, |
| NULL) == 0) { |
| r = uv_translate_sys_error(GetLastError()); |
| goto error; |
| } |
| } |
| |
| /* Populate the sysname field. */ |
| #ifdef __MINGW32__ |
| r = snprintf(buffer->sysname, |
| sizeof(buffer->sysname), |
| "MINGW32_NT-%u.%u", |
| (unsigned int) os_info.dwMajorVersion, |
| (unsigned int) os_info.dwMinorVersion); |
| assert(r < sizeof(buffer->sysname)); |
| #else |
| uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname)); |
| #endif |
| |
| /* Populate the release field. */ |
| r = snprintf(buffer->release, |
| sizeof(buffer->release), |
| "%d.%d.%d", |
| (unsigned int) os_info.dwMajorVersion, |
| (unsigned int) os_info.dwMinorVersion, |
| (unsigned int) os_info.dwBuildNumber); |
| assert(r < sizeof(buffer->release)); |
| |
| /* Populate the machine field. */ |
| GetSystemInfo(&system_info); |
| |
| switch (system_info.wProcessorArchitecture) { |
| case PROCESSOR_ARCHITECTURE_AMD64: |
| uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_IA64: |
| uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_INTEL: |
| uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine)); |
| |
| if (system_info.wProcessorLevel > 3) { |
| processor_level = system_info.wProcessorLevel < 6 ? |
| system_info.wProcessorLevel : 6; |
| buffer->machine[1] = '0' + processor_level; |
| } |
| |
| break; |
| case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: |
| uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_MIPS: |
| uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_ALPHA: |
| case PROCESSOR_ARCHITECTURE_ALPHA64: |
| uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_PPC: |
| uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_SHX: |
| uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine)); |
| break; |
| case PROCESSOR_ARCHITECTURE_ARM: |
| uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine)); |
| break; |
| default: |
| uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine)); |
| break; |
| } |
| |
| return 0; |
| |
| error: |
| buffer->sysname[0] = '\0'; |
| buffer->release[0] = '\0'; |
| buffer->version[0] = '\0'; |
| buffer->machine[0] = '\0'; |
| return r; |
| } |
| |
| int uv_gettimeofday(uv_timeval64_t* tv) { |
| /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */ |
| const uint64_t epoch = (uint64_t) 116444736000000000ULL; |
| FILETIME file_time; |
| ULARGE_INTEGER ularge; |
| |
| if (tv == NULL) |
| return UV_EINVAL; |
| |
| GetSystemTimeAsFileTime(&file_time); |
| ularge.LowPart = file_time.dwLowDateTime; |
| ularge.HighPart = file_time.dwHighDateTime; |
| tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L); |
| tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10); |
| return 0; |
| } |