summaryrefslogtreecommitdiff
path: root/Utilities/cmlibuv/src/win/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmlibuv/src/win/util.c')
-rw-r--r--Utilities/cmlibuv/src/win/util.c1909
1 files changed, 1909 insertions, 0 deletions
diff --git a/Utilities/cmlibuv/src/win/util.c b/Utilities/cmlibuv/src/win/util.c
new file mode 100644
index 0000000000..99432053cc
--- /dev/null
+++ b/Utilities/cmlibuv/src/win/util.c
@@ -0,0 +1,1909 @@
+/* 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"
+
+/* clang-format off */
+#include <winsock2.h>
+#include <winperf.h>
+#include <iphlpapi.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <windows.h>
+/* clang-format on */
+#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
+
+
+/* A RtlGenRandom() by any other name... */
+extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength);
+
+/* Cached copy of the process title, plus a mutex guarding it. */
+static char *process_title;
+static CRITICAL_SECTION process_title_lock;
+
+/* Frequency of the high-resolution clock. */
+static uint64_t hrtime_frequency_ = 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_frequency_ = perf_frequency.QuadPart;
+ } else {
+ uv_fatal_error(GetLastError(), "QueryPerformanceFrequency");
+ }
+}
+
+
+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;
+ int r;
+
+ if (buffer == NULL || size == NULL) {
+ return UV_EINVAL;
+ }
+
+ utf16_len = GetCurrentDirectoryW(0, NULL);
+ if (utf16_len == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+ utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
+ if (utf16_buffer == NULL) {
+ return UV_ENOMEM;
+ }
+
+ utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+ if (utf16_len == 0) {
+ uv__free(utf16_buffer);
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ /* 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) {
+ uv__free(utf16_buffer);
+ return uv_translate_sys_error(GetLastError());
+ } else if (r > (int) *size) {
+ uv__free(utf16_buffer);
+ *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);
+ uv__free(utf16_buffer);
+
+ if (r == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ *size = r - 1;
+ return 0;
+}
+
+
+int uv_chdir(const char* dir) {
+ WCHAR *utf16_buffer;
+ size_t utf16_len, new_utf16_len;
+ WCHAR drive_letter, env_var[4];
+
+ if (dir == NULL) {
+ return UV_EINVAL;
+ }
+
+ utf16_len = MultiByteToWideChar(CP_UTF8,
+ 0,
+ dir,
+ -1,
+ NULL,
+ 0);
+ if (utf16_len == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+ utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR));
+ if (utf16_buffer == NULL) {
+ return UV_ENOMEM;
+ }
+
+ if (MultiByteToWideChar(CP_UTF8,
+ 0,
+ dir,
+ -1,
+ utf16_buffer,
+ utf16_len) == 0) {
+ uv__free(utf16_buffer);
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ if (!SetCurrentDirectoryW(utf16_buffer)) {
+ uv__free(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. */
+ new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer);
+ if (new_utf16_len > utf16_len ) {
+ uv__free(utf16_buffer);
+ utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR));
+ if (utf16_buffer == NULL) {
+ /* When updating the environment variable fails, return UV_OK anyway.
+ * We did successfully change current working directory, only updating
+ * hidden env variable failed. */
+ return 0;
+ }
+ new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer);
+ }
+ if (utf16_len == 0) {
+ uv__free(utf16_buffer);
+ return 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';
+ }
+
+ 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';
+
+ SetEnvironmentVariableW(env_var, utf16_buffer);
+ }
+
+ uv__free(utf16_buffer);
+ 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;
+}
+
+
+void uv__process_title_cleanup(void) {
+}
+
+
+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(unsigned int scale) {
+ LARGE_INTEGER counter;
+ double scaled_freq;
+ double result;
+
+ assert(hrtime_frequency_ != 0);
+ assert(scale != 0);
+ if (!QueryPerformanceCounter(&counter)) {
+ uv_fatal_error(GetLastError(), "QueryPerformanceCounter");
+ }
+ assert(counter.QuadPart != 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.
+ */
+ scaled_freq = (double) hrtime_frequency_ / scale;
+ result = (double) counter.QuadPart / scaled_freq;
+ return (uint64_t) result;
+}
+
+
+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) {
+ *uptime = GetTickCount64() / 1000.0;
+ return 0;
+}
+
+
+unsigned int uv_available_parallelism(void) {
+ SYSTEM_INFO info;
+ unsigned rc;
+
+ /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
+ * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
+ */
+ GetSystemInfo(&info);
+
+ rc = info.dwNumberOfProcessors;
+ if (rc < 1)
+ rc = 1;
+
+ return rc;
+}
+
+
+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);
+}
+
+
+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;
+ DWORD bufsize;
+ size_t len;
+
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
+ len = 0;
+ len = GetTempPathW(0, NULL);
+ if (len == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+ /* Include space for terminating null char. */
+ len += 1;
+ path = uv__malloc(len * sizeof(wchar_t));
+ if (path == NULL) {
+ return UV_ENOMEM;
+ }
+ len = GetTempPathW(len, path);
+
+ if (len == 0) {
+ uv__free(path);
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ /* 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) {
+ uv__free(path);
+ return uv_translate_sys_error(GetLastError());
+ } else if (bufsize > *size) {
+ uv__free(path);
+ *size = bufsize;
+ return UV_ENOBUFS;
+ }
+
+ /* Convert to UTF-8 */
+ bufsize = WideCharToMultiByte(CP_UTF8,
+ 0,
+ path,
+ -1,
+ buffer,
+ *size,
+ NULL,
+ NULL);
+ uv__free(path);
+
+ 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] = L'\0';
+ return 0;
+}
+
+
+int uv__getpwuid_r(uv_passwd_t* pwd) {
+ HANDLE token;
+ wchar_t username[UNLEN + 1];
+ wchar_t *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 = 0;
+ GetUserProfileDirectoryW(token, NULL, &bufsize);
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ r = GetLastError();
+ CloseHandle(token);
+ return uv_translate_sys_error(r);
+ }
+
+ path = uv__malloc(bufsize * sizeof(wchar_t));
+ if (path == NULL) {
+ CloseHandle(token);
+ return UV_ENOMEM;
+ }
+
+ if (!GetUserProfileDirectoryW(token, path, &bufsize)) {
+ r = GetLastError();
+ CloseHandle(token);
+ uv__free(path);
+ return uv_translate_sys_error(r);
+ }
+
+ CloseHandle(token);
+
+ /* Get the username using GetUserNameW() */
+ bufsize = ARRAY_SIZE(username);
+ if (!GetUserNameW(username, &bufsize)) {
+ r = GetLastError();
+ uv__free(path);
+
+ /* 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);
+ uv__free(path);
+
+ 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_environ(uv_env_item_t** envitems, int* count) {
+ wchar_t* env;
+ wchar_t* penv;
+ int i, cnt;
+ uv_env_item_t* envitem;
+
+ *envitems = NULL;
+ *count = 0;
+
+ env = GetEnvironmentStringsW();
+ if (env == NULL)
+ return 0;
+
+ for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++);
+
+ *envitems = uv__calloc(i, sizeof(**envitems));
+ if (*envitems == NULL) {
+ FreeEnvironmentStringsW(env);
+ return UV_ENOMEM;
+ }
+
+ penv = env;
+ cnt = 0;
+
+ while (*penv != L'\0' && cnt < i) {
+ char* buf;
+ char* ptr;
+
+ if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0)
+ goto fail;
+
+ /* Using buf + 1 here because we know that `buf` has length at least 1,
+ * and some special environment variables on Windows start with a = sign. */
+ ptr = strchr(buf + 1, '=');
+ if (ptr == NULL) {
+ uv__free(buf);
+ goto do_continue;
+ }
+
+ *ptr = '\0';
+
+ envitem = &(*envitems)[cnt];
+ envitem->name = buf;
+ envitem->value = ptr + 1;
+
+ cnt++;
+
+ do_continue:
+ penv += wcslen(penv) + 1;
+ }
+
+ FreeEnvironmentStringsW(env);
+
+ *count = cnt;
+ return 0;
+
+fail:
+ FreeEnvironmentStringsW(env);
+
+ for (i = 0; i < cnt; i++) {
+ envitem = &(*envitems)[cnt];
+ uv__free(envitem->name);
+ }
+ uv__free(*envitems);
+
+ *envitems = NULL;
+ *count = 0;
+ return UV_ENOMEM;
+}
+
+
+int uv_os_getenv(const char* name, char* buffer, size_t* size) {
+ wchar_t fastvar[512];
+ wchar_t* var;
+ DWORD varlen;
+ 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;
+
+ var = fastvar;
+ varlen = ARRAY_SIZE(fastvar);
+
+ for (;;) {
+ SetLastError(ERROR_SUCCESS);
+ len = GetEnvironmentVariableW(name_w, var, varlen);
+
+ if (len < varlen)
+ break;
+
+ /* Try repeatedly because we might have been preempted by another thread
+ * modifying the environment variable just as we're trying to read it.
+ */
+ if (var != fastvar)
+ uv__free(var);
+
+ varlen = 1 + len;
+ var = uv__malloc(varlen * sizeof(*var));
+
+ if (var == NULL) {
+ r = UV_ENOMEM;
+ goto fail;
+ }
+ }
+
+ uv__free(name_w);
+ name_w = NULL;
+
+ if (len == 0) {
+ r = GetLastError();
+ if (r != ERROR_SUCCESS) {
+ r = uv_translate_sys_error(r);
+ goto fail;
+ }
+ }
+
+ /* Check how much space we need */
+ bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL);
+
+ if (bufsize == 0) {
+ r = uv_translate_sys_error(GetLastError());
+ goto fail;
+ } else if (bufsize > *size) {
+ *size = bufsize;
+ r = UV_ENOBUFS;
+ goto fail;
+ }
+
+ /* Convert to UTF-8 */
+ bufsize = WideCharToMultiByte(CP_UTF8,
+ 0,
+ var,
+ -1,
+ buffer,
+ *size,
+ NULL,
+ NULL);
+
+ if (bufsize == 0) {
+ r = uv_translate_sys_error(GetLastError());
+ goto fail;
+ }
+
+ *size = bufsize - 1;
+ r = 0;
+
+fail:
+
+ if (name_w != NULL)
+ uv__free(name_w);
+
+ if (var != fastvar)
+ uv__free(var);
+
+ return r;
+}
+
+
+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) {
+ WCHAR buf[UV_MAXHOSTNAMESIZE];
+ size_t len;
+ char* utf8_str;
+ int convert_result;
+
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
+ uv__once_init(); /* Initialize winsock */
+
+ if (pGetHostNameW == NULL)
+ return UV_ENOSYS;
+
+ if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0)
+ return uv_translate_sys_error(WSAGetLastError());
+
+ convert_result = uv__convert_utf16_to_utf8(buf, -1, &utf8_str);
+
+ if (convert_result != 0)
+ return convert_result;
+
+ len = strlen(utf8_str);
+ if (len >= *size) {
+ *size = len + 1;
+ uv__free(utf8_str);
+ return UV_ENOBUFS;
+ }
+
+ memcpy(buffer, utf8_str, len + 1);
+ uv__free(utf8_str);
+ *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. */
+ #ifdef _MSC_VER
+ #pragma warning(suppress : 4996)
+ #endif
+ 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,
+ &registry_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((size_t)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((size_t)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;
+}
+
+int uv__random_rtlgenrandom(void* buf, size_t buflen) {
+ if (buflen == 0)
+ return 0;
+
+ if (SystemFunction036(buf, buflen) == FALSE)
+ return UV_EIO;
+
+ return 0;
+}
+
+void uv_sleep(unsigned int msec) {
+ Sleep(msec);
+}