unix,win: add uv_os_uname()

Fixes: https://github.com/libuv/libuv/issues/2126
PR-URL: https://github.com/libuv/libuv/pull/2128
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
This commit is contained in:
cjihrig 2019-01-04 14:04:32 -05:00
parent d39959c80d
commit d4288bbeab
No known key found for this signature in database
GPG Key ID: 7434390BDBE9B9C5
12 changed files with 303 additions and 3 deletions

View File

@ -167,6 +167,7 @@ set(uv_test_sources
test/test-udp-send-immediate.c
test/test-udp-send-unreachable.c
test/test-udp-try-send.c
test/test-uname.c
test/test-walk-handles.c
test/test-watcher-cross-stop.c)

View File

@ -293,6 +293,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-udp-send-immediate.c \
test/test-udp-send-unreachable.c \
test/test-udp-try-send.c \
test/test-uname.c \
test/test-walk-handles.c \
test/test-watcher-cross-stop.c
test_run_tests_LDADD = libuv.la

View File

@ -145,6 +145,19 @@ Data types
char* homedir;
} uv_passwd_t;
.. c:type:: uv_utsname_t
Data type for operating system name and version information.
::
typedef struct uv_utsname_s {
char sysname[256];
char release[256];
char version[256];
char machine[256];
} uv_utsname_t;
API
---
@ -549,3 +562,12 @@ API
for others it will be silently reduced to `PRIORITY_HIGH`.
.. versionadded:: 1.23.0
.. c:function:: int uv_os_uname(uv_utsname_t* buffer)
Retrieves system information in `buffer`. The populated data includes the
operating system name, release, version, and machine. On non-Windows
systems, `uv_os_uname()` is a thin wrapper around :man:`uname(3)`. Returns
zero on success, and a non-zero error value otherwise.
.. versionadded:: 1.25.0

View File

@ -234,6 +234,7 @@ typedef struct uv_cpu_info_s uv_cpu_info_t;
typedef struct uv_interface_address_s uv_interface_address_t;
typedef struct uv_dirent_s uv_dirent_t;
typedef struct uv_passwd_s uv_passwd_t;
typedef struct uv_utsname_s uv_utsname_t;
typedef enum {
UV_LOOP_BLOCK_SIGNAL
@ -1054,6 +1055,16 @@ struct uv_passwd_s {
char* homedir;
};
struct uv_utsname_s {
char sysname[256];
char release[256];
char version[256];
char machine[256];
/* This struct does not contain the nodename and domainname fields present in
the utsname type. domainname is a GNU extension. Both fields are referred
to as meaningless in the docs. */
};
typedef enum {
UV_DIRENT_UNKNOWN,
UV_DIRENT_FILE,
@ -1135,6 +1146,8 @@ UV_EXTERN int uv_os_unsetenv(const char* name);
UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size);
UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
typedef enum {
UV_FS_UNKNOWN = -1,

View File

@ -40,6 +40,7 @@
#include <sys/uio.h> /* writev */
#include <sys/resource.h> /* getrusage */
#include <pwd.h>
#include <sys/utsname.h>
#ifdef __sun
# include <netdb.h> /* MAXHOSTNAMELEN on Solaris */
@ -1357,3 +1358,59 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
return 0;
}
int uv_os_uname(uv_utsname_t* buffer) {
struct utsname buf;
int r;
if (buffer == NULL)
return UV_EINVAL;
if (uname(&buf) == -1) {
r = UV__ERR(errno);
goto error;
}
r = uv__strscpy(buffer->sysname, buf.sysname, sizeof(buffer->sysname));
if (r == UV_E2BIG)
goto error;
#ifdef _AIX
r = snprintf(buffer->release,
sizeof(buffer->release),
"%s.%s",
buf.version,
buf.release);
if (r >= sizeof(buffer->release)) {
r = UV_E2BIG;
goto error;
}
#else
r = uv__strscpy(buffer->release, buf.release, sizeof(buffer->release));
if (r == UV_E2BIG)
goto error;
#endif
r = uv__strscpy(buffer->version, buf.version, sizeof(buffer->version));
if (r == UV_E2BIG)
goto error;
#if defined(_AIX) || defined(__PASE__)
r = uv__strscpy(buffer->machine, "ppc64", sizeof(buffer->machine));
#else
r = uv__strscpy(buffer->machine, buf.machine, sizeof(buffer->machine));
#endif
if (r == UV_E2BIG)
goto error;
return 0;
error:
buffer->sysname[0] = '\0';
buffer->release[0] = '\0';
buffer->version[0] = '\0';
buffer->machine[0] = '\0';
return r;
}

View File

@ -1627,3 +1627,120 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
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;
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. */
if (WideCharToMultiByte(CP_UTF8,
0,
os_info.szCSDVersion,
-1,
buffer->version,
sizeof(buffer->version),
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;
}

View File

@ -26,6 +26,7 @@
/* Ntdll function pointers */
sRtlGetVersion pRtlGetVersion;
sRtlNtStatusToDosError pRtlNtStatusToDosError;
sNtDeviceIoControlFile pNtDeviceIoControlFile;
sNtQueryInformationFile pNtQueryInformationFile;
@ -55,6 +56,9 @@ void uv_winapi_init(void) {
uv_fatal_error(GetLastError(), "GetModuleHandleA");
}
pRtlGetVersion = (sRtlGetVersion) GetProcAddress(ntdll_module,
"RtlGetVersion");
pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress(
ntdll_module,
"RtlNtStatusToDosError");

View File

@ -4519,6 +4519,9 @@ typedef VOID (NTAPI *PIO_APC_ROUTINE)
PIO_STATUS_BLOCK IoStatusBlock,
ULONG Reserved);
typedef NTSTATUS (NTAPI *sRtlGetVersion)
(PRTL_OSVERSIONINFOW lpVersionInformation);
typedef ULONG (NTAPI *sRtlNtStatusToDosError)
(NTSTATUS Status);
@ -4707,6 +4710,7 @@ typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook)
/* Ntdll function pointers */
extern sRtlGetVersion pRtlGetVersion;
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
extern sNtQueryInformationFile pNtQueryInformationFile;

View File

@ -450,6 +450,7 @@ TEST_DECLARE (fork_threadpool_queue_work_simple)
TEST_DECLARE (idna_toascii)
TEST_DECLARE (utf8_decode1)
TEST_DECLARE (uname)
TASK_LIST_START
TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000)
@ -960,6 +961,7 @@ TASK_LIST_START
#endif
TEST_ENTRY (utf8_decode1)
TEST_ENTRY (uname)
/* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */
#ifndef __MVS__

View File

@ -35,6 +35,7 @@ TEST_IMPL(platform_output) {
uv_cpu_info_t* cpus;
uv_interface_address_t* interfaces;
uv_passwd_t pwd;
uv_utsname_t uname;
int count;
int i;
int err;
@ -153,5 +154,13 @@ TEST_IMPL(platform_output) {
ASSERT(ppid > 0);
printf("uv_os_getppid: %d\n", (int) ppid);
err = uv_os_uname(&uname);
ASSERT(err == 0);
printf("uv_os_uname:\n");
printf(" sysname: %s\n", uname.sysname);
printf(" release: %s\n", uname.release);
printf(" version: %s\n", uname.version);
printf(" machine: %s\n", uname.machine);
return 0;
}

69
test/test-uname.c Normal file
View File

@ -0,0 +1,69 @@
/* Copyright libuv project 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 "uv.h"
#include "task.h"
#include <string.h>
#ifndef _WIN32
# include <sys/utsname.h>
#endif
TEST_IMPL(uname) {
#ifndef _WIN32
struct utsname buf;
#endif
#ifdef _AIX
char temp[256];
#endif
uv_utsname_t buffer;
int r;
/* Verify that NULL is handled properly. */
r = uv_os_uname(NULL);
ASSERT(r == UV_EINVAL);
/* Verify the happy path. */
r = uv_os_uname(&buffer);
ASSERT(r == 0);
#ifndef _WIN32
ASSERT(uname(&buf) != -1);
ASSERT(strcmp(buffer.sysname, buf.sysname) == 0);
ASSERT(strcmp(buffer.version, buf.version) == 0);
# ifdef _AIX
snprintf(temp, sizeof(temp), "%s.%s", buf.version, buf.release);
ASSERT(strcmp(buffer.release, temp) == 0);
# else
ASSERT(strcmp(buffer.release, buf.release) == 0);
# endif /* _AIX */
# if defined(_AIX) || defined(__PASE__)
ASSERT(strcmp(buffer.machine, "ppc64") == 0);
# else
ASSERT(strcmp(buffer.machine, buf.machine) == 0);
# endif /* defined(_AIX) || defined(__PASE__) */
#endif /* _WIN32 */
return 0;
}

View File

@ -154,6 +154,7 @@
'test-udp-multicast-interface.c',
'test-udp-multicast-interface6.c',
'test-udp-try-send.c',
'test-uname.c',
],
'conditions': [
[ 'OS=="win"', {