/* 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
/*
Max hostname length. The Windows gethostname() documentation states that 256
bytes will always be large enough to hold the null-terminated hostname.
*/
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 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;
}
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);
if (err != ERROR_SUCCESS) {
RegCloseKey(processor_key);
goto error;
}
RegCloseKey(processor_key);
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:
/* 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;
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[MAXHOSTNAMELEN + 1];
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;
}