ngspice/src/frontend/com_sysinfo.c

993 lines
32 KiB
C

/* Provide system information
LINUX: /proc file system
Windows: GlobalMemoryStatusEx, GetSystemInfo, GetVersionExA, RegQueryValueExA
Authors: Holger Vogt, Hendrik Vogt
*/
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/fteext.h"
#include "com_commands.h"
#ifdef _WIN32
#include <windows.h>
#include <psapi.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ngspice/dstring.h"
/* system info */
typedef struct TSI {
char *cpuModelName;
char *osName;
unsigned int numPhysicalProcessors;
unsigned int numLogicalProcessors;
} TesSystemInfo;
/* Struture with info about system */
static TesSystemInfo system_info;
/* memory info */
struct sys_memory {
unsigned long long size_m; /* Total memory size */
unsigned long long free_m; /* Free memory */
unsigned long long swap_t; /* Swap total */
unsigned long long swap_f; /* Swap free */
};
static void fprintmem(FILE *stream, unsigned long long memory);
static void free_static_system_info(void);
static int get_sysmem(struct sys_memory *memall);
static void set_static_system_info(void);
#ifdef _WIN32
static inline void get_logical_processor_count(void);
static void get_os_info(void);
static void get_physical_processor_count(void);
static void get_processor_name(void);
#endif
/* Print the available system info */
void com_sysinfo(wordlist *wl)
{
NG_IGNORE(wl);
/* Invariant system data such as OS name */
{
/* Flag that have at least some system info */
bool f_have_system_info = FALSE;
static bool f_first_call = TRUE;
if (f_first_call) {
/* Obtain the system info when this function is called the
* first time */
set_static_system_info();
/* Free the allocations on exit. Not really necessary since they
* will be cleaned up then, but it may be useful when checking for
* memory leaks */
if (atexit(&free_static_system_info) != 0) {
fprintf(cp_err,
"Unable to set handler to clean up system info.\n");
}
/* Mark that first-call init is done. Note that since the calls to
* set_static_system_info() and atexit define sequence points, the
* flag will not be set until after they complete, so the code is
* safe for reentrant calls. */
f_first_call = FALSE;
}
if (system_info.osName != (char *) NULL) {
fprintf(cp_out, "\nOS: %s\n", system_info.osName);
f_have_system_info = TRUE;
}
if (system_info.cpuModelName != (char *) NULL) {
fprintf(cp_out, "CPU: %s\n", system_info.cpuModelName);
f_have_system_info = TRUE;
}
if (system_info.numPhysicalProcessors > 0) {
fprintf(cp_out, "Physical processors: %u, ",
system_info.numPhysicalProcessors);
f_have_system_info = TRUE;
}
if (system_info.numLogicalProcessors > 0) {
fprintf(cp_out, "Logical processors: %u\n",
system_info.numLogicalProcessors);
f_have_system_info = TRUE;
}
/* Print something if no system info available */
if (!f_have_system_info) {
fprintf(cp_err, "No system info available!\n");
}
} /* end of block getting invariant system info */
/* Get memory information */
{
struct sys_memory mem_t_act;
if (get_sysmem(&mem_t_act) == 0) {
/* get_sysmem returns bytes */
fprintf(cp_out, "Total DRAM available = ");
fprintmem(cp_out, mem_t_act.size_m);
fprintf(cp_out, ".\n");
fprintf(cp_out, "DRAM currently available = ");
fprintmem(cp_out, mem_t_act.free_m);
fprintf(cp_out, ".\n\n");
}
else {
fprintf(cp_err, "Memory info is unavailable! \n");
}
}
return;
} /* end of function com_sysinfo */
/* This function frees the buffers used to store system allocation strings */
static void free_static_system_info(void)
{
tfree(system_info.cpuModelName);
tfree(system_info.osName);
} /* end of fuction free_system_info */
/* Print to stream the given memory size in a human friendly format */
static void fprintmem(FILE *stream, unsigned long long memory)
{
if (memory > 1048576) {
fprintf(stream, "%8.6f MB", (double) memory /1048576.);
}
else if (memory > 1024) {
fprintf(stream, "%5.3f kB", (double) memory / 1024.);
}
else {
fprintf(stream, "%u bytes", (unsigned) memory);
}
} /* end of funtion fprintmem */
/*** Get processor and memory information as appropriate for the system ***/
#ifdef HAVE__PROC_MEMINFO
/* Get memory information */
static int get_sysmem(struct sys_memory *memall)
{
FILE *fp;
char buffer[2048];
size_t bytes_read;
char *match;
unsigned long mem_got;
if ((fp = fopen("/proc/meminfo", "r")) == NULL) {
perror("fopen(\"/proc/meminfo\")");
return -1;
}
bytes_read = fread(buffer, 1, sizeof(buffer), fp);
fclose(fp);
if (bytes_read == 0 || bytes_read == sizeof(buffer))
return -1;
buffer[bytes_read] = '\0';
/* Search for string "MemTotal" */
match = strstr(buffer, "MemTotal");
if (match == NULL) /* not found */
return -1;
sscanf(match, "MemTotal: %ld", &mem_got);
memall->size_m = mem_got*1024; /* 1MB = 1024KB */
/* Search for string "MemFree" */
match = strstr(buffer, "MemFree");
if (match == NULL) /* not found */
return -1;
sscanf(match, "MemFree: %ld", &mem_got);
memall->free_m = mem_got*1024; /* 1MB = 1024KB */
/* Search for string "SwapTotal" */
match = strstr(buffer, "SwapTotal");
if (match == NULL) /* not found */
return -1;
sscanf(match, "SwapTotal: %ld", &mem_got);
memall->swap_t = mem_got*1024; /* 1MB = 1024KB */
/* Search for string "SwapFree" */
match = strstr(buffer, "SwapFree");
if (match == NULL) /* not found */
return -1;
sscanf(match, "SwapFree: %ld", &mem_got);
memall->swap_f = mem_got*1024; /* 1MB = 1024KB */
return 0;
}
/* Return length of first line in a string */
static inline size_t getLineLength(const char *str)
{
const char *p = str;
while (*p && (*p != '\n')) {
p++;
}
return (size_t) (p - str);
}
/* Checks if number 'match' is found in a vector 'set' of size 'size'
Returns 1 if yes, otherwise, 0 */
static int searchInSet(const int *set, unsigned size, int match)
{
unsigned index;
for (index = 0; index < size; index++)
if (match == set[index])
return 1;
return 0;
}
/* Get system information */
static void set_static_system_info(void)
{
FILE *file;
/* Init to all information unailable */
system_info.cpuModelName = (char *) NULL;
system_info.osName = (char *) NULL;
system_info.numLogicalProcessors = system_info.numPhysicalProcessors = 0;
/* get kernel version string */
file = fopen("/proc/version", "rb");
if (file != NULL) {
size_t size;
/* read bytes and find end of file */
for (size = 0; ; size++) {
if (EOF == fgetc(file)) {
break;
}
}
system_info.osName = TMALLOC(char, size + 1);
rewind(file);
if (fread(system_info.osName, sizeof(char), size, file) != size) {
(void) fprintf(cp_err, "Unable to read \"/proc/version\".\n");
fclose(file);
tfree(system_info.osName);
return;
}
fclose(file);
system_info.osName[size] = '\0';
}
/* get cpu information */
file = fopen("/proc/cpuinfo", "rb");
if (file != NULL) {
size_t size;
char *inStr;
/* read bytes and find end of file */
for (size = 0; ; size++) {
if (EOF == fgetc(file)) {
break;
}
}
/* get complete string */
inStr = TMALLOC(char, size+1);
rewind(file);
if (fread(inStr, sizeof(char), size, file) != size) {
(void) fprintf(cp_err, "Unable to read \"/proc/cpuinfo\".\n");
fclose(file);
txfree(inStr);
return;
}
inStr[size] = '\0';
{
const char *matchStr = "model name";
/* pointer to first occurrence of model name*/
const char *modelStr = strstr(inStr, matchStr);
if (modelStr != NULL) {
/* search for ':' */
const char *modelPtr = strchr(modelStr, ':');
if (modelPtr != NULL) {
/*length of string from ':' till end of line */
size_t numToEOL = getLineLength(modelPtr);
if (numToEOL > 2) {
/* skip ": "*/
numToEOL -= 2;
system_info.cpuModelName = TMALLOC(char, numToEOL+1);
memcpy(system_info.cpuModelName, modelPtr+2, numToEOL);
system_info.cpuModelName[numToEOL] = '\0';
}
}
}
}
{
const char *matchStrProc = "processor";
const char *matchStrPhys = "physical id";
char *strPtr = inStr;
unsigned numProcs = 0;
int *physIDs;
/* get number of logical processors */
while ((strPtr = strstr(strPtr, matchStrProc)) != NULL) {
// numProcs++;
strPtr += strlen(matchStrProc);
if (isblank_c(*strPtr)) numProcs++;
}
system_info.numLogicalProcessors = numProcs;
physIDs = TMALLOC(int, numProcs);
/* get number of physical CPUs */
numProcs = 0;
strPtr = inStr;
while ((strPtr = strstr(strPtr, matchStrProc)) != NULL) {
/* search for first occurrence of physical id */
strPtr = strstr(strPtr, matchStrPhys);
if (strPtr != NULL) {
/* go to ';' */
strPtr = strchr(strPtr, ':');
if (strPtr != NULL) {
int buffer = 0;
/* skip ": " */
strPtr += 2;
/* get number */
sscanf(strPtr, "%d", &buffer);
/* If this physical id is unique,
we have another physically available CPU */
if (searchInSet(physIDs, numProcs, buffer) == 0) {
physIDs[numProcs] = buffer;
numProcs++;
}
}
else {
break;
}
}
else {
break;
}
}
system_info.numPhysicalProcessors = numProcs;
tfree(physIDs);
}
/* another test to get number of logical processors
* if (system_info.numLogicalProcessors == 0) {
* char *token;
* char *cpustr = copy(inStr);
* while (cpustr && !*cpustr)
* if (cieq(gettok(&cpustr), "processor")) {
* gettok(&cpustr);
* token = gettok(&cpustr);
* }
*
* system_info.numLogicalProcessors = atoi(token) + 1;
* tfree(cpustr);
* }
*/
txfree(inStr);
fclose(file);
} /* end of case that file was opened OK */
return;
} /* end of function set_static_system_info */
#elif defined(__APPLE__) && defined(__MACH__)
/* Get memory information */
static int get_sysmem(struct sys_memory *memall)
{
fprintf(stderr, "System memory info is not available\n");
return -1;
}
/* Get system information */
static void set_static_system_info(void)
{
}
#elif defined(_WIN32)
/* Get memory information */
static int get_sysmem(struct sys_memory *memall)
{
MEMORYSTATUSEX ms;
ms.dwLength = sizeof(MEMORYSTATUSEX);
if (GlobalMemoryStatusEx(&ms) == FALSE) {
return -1;
}
memall->size_m = ms.ullTotalPhys;
memall->free_m = ms.ullAvailPhys;
memall->swap_t = ms.ullTotalPageFile;
memall->swap_f = ms.ullAvailPageFile;
return 0;
} /* end of function get_sysmem */
/* This function gets system information about the version of Windows and
* the number processors available, and save this information in the static
* TesSystemInfo structure. If an item cannot be obtained, it is set to
* 0/NULL. This allows callers to check for valid data since neither of these
* values are valid */
static void set_static_system_info(void)
{
get_processor_name(); /* name of processor */
get_os_info(); /* name of OS with build and service pack, if any */
get_logical_processor_count(); /* Get number of logical cores */
get_physical_processor_count(); /* # hardware components */
return;
} /* end of function set_static_system_info */
/* Copy data at HKLM/sz_subkey/sz_val_name to an allocated buffer that is
* 1 byte longer and always null-termianted, possibly with 2 nulls
*
* Parameters
* sz_subkey: Subkey string
* sz_val_name: Name of value to get
* p_ds: Address of dstring to receive data
*
* Return codes
* 0: Data obtained OK
* -1: Data not obtained.
*/
static int registry_value_to_ds(const char *sz_subkey,
const char *sz_val_name, DSTRING *p_ds)
{
int xrc = 0;
DWORD n_byte_data = 0;
HKEY hk;
bool f_key_open = FALSE;
/* Opwn the key with the processor details */
{
DWORD rc;
if ((rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
sz_subkey, 0, KEY_READ, &hk)) != ERROR_SUCCESS) {
fprintf(cp_err,
"Unable to open key for registry data \"%s\". "
"System code = %lu\n",
sz_subkey, rc);
xrc = -1;
goto EXITPOINT;
}
}
f_key_open = TRUE;
/* Get size of the name string. Strings in the registry need not be
* null-terminated, but if they are, the null is included in the
* size. */
{
DWORD rc;
if ((rc = RegQueryValueExA(hk, sz_val_name,
0, 0, NULL, &n_byte_data)) != ERROR_SUCCESS) {
fprintf(cp_err,
"Unable to get the size of value for \"%s\". "
"System code = %lu\n",
sz_val_name, rc);
xrc = -1;
goto EXITPOINT;
}
}
/* Ensure dstring buffer is large enough for the data + 1 byte to add
* a null to the end */
{
size_t n_byte_reserve = (size_t) n_byte_data + 1;
if (ds_reserve(p_ds, n_byte_reserve) != 0) {
(void) fprintf(cp_err,
"Unable to reserve a buffer of %u bytes for data.\n",
n_byte_reserve);
xrc = -1;
goto EXITPOINT;
}
}
/* Retrieve the value using the dstring buffer to receive it */
{
DWORD rc;
char *p_buf = ds_get_buf(p_ds);
if ((rc = RegQueryValueExA(hk, sz_val_name, 0, 0,
(LPBYTE) p_buf, &n_byte_data)) != ERROR_SUCCESS) {
(void) fprintf(cp_err,
"Unable to get the value for \"%s\". "
"System code = %lu\n",
sz_val_name, rc);
xrc = -1;
goto EXITPOINT;
}
}
/* Set the dstring length */
(void) ds_set_length(p_ds, n_byte_data);
EXITPOINT:
/* Indicate error if failure */
if (xrc != 0) {
ds_clear(p_ds);
}
if (f_key_open) { /* close key if opened */
RegCloseKey(hk);
}
return xrc;
} /* end of function registry_value_to_ds */
/* Gets the name of the processor from the registry and sets field
* cpuModelName in system_info. On failure, the field is set to NULL */
static void get_processor_name(void)
{
DS_CREATE(ds, 200);
system_info.cpuModelName = NULL; /* init in case of failure */
if (registry_value_to_ds(
"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
"ProcessorNameString",
&ds) != 0) {
(void) fprintf(cp_err,
"Unable to get processor name data from the registry.\n");
return;
}
/* Step past any leading blanks and copy name to cpuModelName */
{
const char *proc_name = ds_get_buf(&ds);
while (*proc_name == ' ') {
++proc_name;
} /* end of loop finding first non-blank of processor name */
/* Make a copy of the string at cpuModelName field of system_info */
system_info.cpuModelName = copy(proc_name);
}
ds_free(&ds); /* Free resources */
return;
} /* end of function get_processor_name */
/* This function gets the release details to distinguish between
* 2016 and 2019 servers. If necessary, it can be extended to return
* codes for other servers in the future.
*
* See
* https://techcommunity.microsoft.com/t5/Windows-Server-Insiders/Windows-Server-2019-version-info/m-p/234472
*
* Return codes
* -1: Failure
* +1: 2016 server
* +2: 2019 server (probably)
*
* Remarks
* Calling this function alone is not sufficient to identify a server.
* Rather it should be called given that a server OS is present to identify
* the serer version.
*/
static int get_server_id(void)
{
DS_CREATE(ds, 25);
if (registry_value_to_ds(
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"ReleaseId",
&ds) != 0) {
(void) fprintf(cp_err,
"Unable to get release ID data from the registry.\n");
return -1;
}
int id_code = -1; /* Set to failure until found */
/* Convert the release ID to a number */
{
char *p_end;
errno = 0;
const char *p_buf = ds_get_buf(&ds);
unsigned long id_val = strtoul(p_buf, &p_end, 10);
if (errno || *p_end != '\0') {
fprintf(cp_err,
"Unable to convert \"%s\" to a release ID number.\n",
p_buf);
goto EXITPOINT;
}
if (id_val == 1607ul) { /* code for 2016 server */
id_code = 1;
}
else if (id_code > 1607ul) { /* Probably 2019 server */
id_code = 2;
}
/* Else unknown ID */
}
EXITPOINT:
ds_free(&ds); /* Free resources */
return id_code;
} /* end of function get_server_id */
/* This function creates a name of the form <OS name> ' ' <Service pack>,
* allocates a buffer for it, and stores it in system_info.osNname. On
* failure an error is reported and the string is set to NULL.
*
* Remarks
* Getting the version has been complicated greatly in later versions of
* Windows. A good discussion of the issue can be found at
* https://stackoverflow.com/questions/47581146/getting-os-build-version-from-win32-api-c
*
* First, the function GetVersionEx() has been deprecated, so the
* straightforward call to retrieve the version is not the recommended
* approach any longer and will output a message to this effect during
* compilation. Also, it may be removed at some later time. Even if it is
* called, since Windows 8.0, the value returned depends not on the version
* of the OS, but the manifested version of the calling program.
*
* As an alternative function RtlGetVersion() always returns version info
* the same version as GetVersionEx() prior to Windows 8.1, and it
* is not deprecated. Unfortunately, the simple solution is made less
* simple because the header providing a prototype for RtlGetVersion()
* is part of the Windows DDK and the function is not directly exposed
* by ntdll.lib. Also, the DDK only works with UTF-16, so the name string
* must be converted.
*
* The following link has a table showing how to determine the all operating
* systems from Windows 2000 through Windows 10/Windows Server 2016.
* https://web.archive.org/web/20190501082653/https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa
*
* OS ver Other OSV=OSVERSIONINFOEX
* Windows Server 2016 10.0 OSV.wProductType != VER_NT_WORKSTATION
* Windows 10 10.0 OSV.wProductType == VER_NT_WORKSTATION
* Windows Server 2008 6.0 OSV.wProductType != VER_NT_WORKSTATION
* Windows Vista 6.0 OSV.wProductType == VER_NT_WORKSTATION
* Windows Server 2008 R2 6.1 OSV.wProductType != VER_NT_WORKSTATION
* Windows 7 6.1 OSV.wProductType == VER_NT_WORKSTATION
* Windows Server 2012 6.2 OSV.wProductType != VER_NT_WORKSTATION
* Windows 8 6.2 OSV.wProductType == VER_NT_WORKSTATION
* Windows Server 2012 R2 6.3 OSV.wProductType != VER_NT_WORKSTATION
* Windows 8.1 6.3 OSV.wProductType == VER_NT_WORKSTATION
* Windows 2000 5.0 Not applicable
* Windows XP 5.1 Not applicable
* Windows Home Server 5.2 OSV.wSuiteMask & VER_SUITE_WH_SERVER
* Windows XP Professional
* x64 Edition 5.2 (OSV.wProductType == VER_NT_WORKSTATION) &&
* (SYSTEM_INFO.wProcessorArchitecture ==
* PROESSOR_ARCHITECTURE_AMD64)
* Windows Server 2003 5.2 GetSystemMetrics(SM_SERVERR2) == 0
* Windows Server 2003 R2 5.2 GetSystemMetrics(SM_SERVERR2) != 0
* Information on distinguishing between Windows Server 2016 and 2019 does
* not appear to have been provided as of early 2019:
* https://stackoverflow.com/questions/53393150/c-how-to-detect-windows-server-2019
* Hopefully this issue will be resolved in the future.
*/
static void get_os_info(void)
{
OSVERSIONINFOEXW ver_info;
/* the name of the OS. Init to prevent compiler warning */
const char *sz_os_name = NULL;
/* Load library containing RtlGetVersion() */
HMODULE lib = LoadLibraryExW(L"ntdll.dll", NULL, 0);
if (lib == (HMODULE) NULL) { /* Not loaded OK */
(void) fprintf(cp_err,
"Unable to load ntdll.dll. "
"System code = %lu\n",
(unsigned long) GetLastError());
system_info.osName = (char *) NULL;
return;
}
/* Locate RtlGetVersion() */
FARPROC p_get_ver = GetProcAddress(lib, "RtlGetVersion");
if (p_get_ver == (FARPROC) NULL) { /* Did not get function addr OK */
(void) fprintf(cp_err,
"Unable to locate function RtlGetVersion. "
"System code = %lu\n",
(unsigned long) GetLastError());
system_info.osName = (char *) NULL;
return;
}
/* Get version info. RtlGetVersion cannot fail. */
ver_info.dwOSVersionInfoSize = sizeof(ver_info);
(void) ((DWORD (WINAPI *)(OSVERSIONINFOEXW *)) p_get_ver)(
&ver_info);
switch (ver_info.dwMajorVersion) {
case 10: {
static const char OS_srvr[] = "Windows Server 2016/2019/other";
static const char OS_10[] = "Windows 10";
static const char OS_2016[] = "Windows Server 2016";
static const char OS_2019[] = "Windows Server 2019";
static const char * const p_str[] = {
OS_srvr, OS_10, OS_2016, OS_2019
};
if (ver_info.dwMinorVersion != 0) { /* only know 10.0 */
system_info.osName = (char *) NULL;
return;
}
sz_os_name = p_str[ver_info.wProductType == VER_NT_WORKSTATION ?
1 : get_server_id() + 1];
break;
}
case 6: {
static const char OS_2008[] = "Windows Server 2008";
static const char OS_vista[] = "Windows Vista";
static const char OS_2008R2[] = "Windows Server 2008 R2";
static const char OS_7[] = "Windows 7";
static const char OS_2012[] = "Windows Server 2012";
static const char OS_8[] = "Windows 8";
static const char OS_2012R2[] = "Windows Server 2012 R2";
static const char OS_8_1[] = "Windows 8.1";
static const char * const p_str[] = {
OS_2008, OS_vista,
OS_2008R2, OS_7,
OS_2012, OS_8,
OS_2012R2, OS_8_1
};
if (ver_info.dwMinorVersion > 3) { /* know 6.0 through 6.3 */
(void) fprintf(cp_err, "Unknown Windows version 6.%lu. ",
(unsigned long) ver_info.dwMinorVersion);
system_info.osName = (char *) NULL;
return;
}
sz_os_name = p_str[2 * ver_info.dwMinorVersion +
ver_info.wProductType == VER_NT_WORKSTATION];
break;
}
case 5: { /* an assortment of other conditions must be checked */
switch (ver_info.dwMinorVersion) { /* filter by minor verson */
case 0: {
static const char OS_2k[] = "Windows 2000";
sz_os_name = OS_2k;
break;
}
case 1: {
static const char OS_xp[] = "Windows XP";
sz_os_name = OS_xp;
break;
}
case 2:
if (ver_info.wSuiteMask & VER_SUITE_WH_SERVER) {
static const char OS_home_server[] = "Windows Home Server";
sz_os_name = OS_home_server;
}
else if (ver_info.wProductType == VER_NT_WORKSTATION) {
SYSTEM_INFO si;
GetSystemInfo(&si);
if (si.wProcessorArchitecture ==
PROCESSOR_ARCHITECTURE_AMD64) {
static const char OS_xp64[] =
"Windows XP Professional x64 Edition";
sz_os_name = OS_xp64;
}
}
else { /* Server 2003 or 2003 R2 */
static const char OS_2003R2[] = "Windows Server 2003 R2";
static const char OS_2003[] = "Windows Server 2003";
static const char * const p_str[] = {OS_2003R2, OS_2003};
sz_os_name = p_str[!GetSystemMetrics(SM_SERVERR2)];
}
break;
default:
(void) fprintf(cp_err, "Unknown Windows version 5.%lu. ",
(unsigned long) ver_info.dwMinorVersion);
system_info.osName = (char *) NULL;
return;
} /* end of switch over minor version for major version 5 */
break;
}
case 4:
switch (ver_info.dwMinorVersion) {
case 0: {
static const char OS_95[] = "Windows 95";
static const char OS_nt4[] = "Windows NT 4.0";
static const char * const p_str[] = {OS_95, OS_nt4};
sz_os_name = p_str[ver_info.wProductType == VER_NT_WORKSTATION];
}
case 10: {
static const char OS_98[] = "Windows 98";
sz_os_name = OS_98;
break;
}
case 90: {
static const char OS_me[] = "Windows ME";
sz_os_name = OS_me;
break;
}
default:
(void) fprintf(cp_err, "Unknown Windows version 4.%lu. ",
(unsigned long) ver_info.dwMinorVersion);
system_info.osName = (char *) NULL;
return;
} /* end of switch over minor version for major version 4 */
default:
(void) fprintf(cp_err, "Unknown Windows version %lu.%lu. ",
(unsigned long) ver_info.dwMajorVersion,
(unsigned long) ver_info.dwMinorVersion);
system_info.osName = (char *) NULL;
return;
}/* end of switch over major version */
/* Have the base version name. Now must add service pack, if any */
if (ver_info.wServicePackMajor == 0) { /* no service pack */
system_info.osName = tprintf("%s, Build %lu",
sz_os_name, (unsigned long) ver_info.dwBuildNumber);
}
else if (ver_info.wServicePackMinor == 0) { /* major # only */
system_info.osName = tprintf("%s, Build %lu, Service Pack %u",
sz_os_name, (unsigned long) ver_info.dwBuildNumber,
(unsigned) ver_info.wServicePackMajor);
}
else { /* service pack has major and minor versions */
system_info.osName = tprintf("%s, Build %lu, Service Pack %u.%u",
sz_os_name, (unsigned long) ver_info.dwBuildNumber,
(unsigned) ver_info.wServicePackMajor,
(unsigned) ver_info.wServicePackMinor);
}
return;
} /* end of function get_os_info */
/* This function sets the number of processors field in system_info */
static inline void get_logical_processor_count(void)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
system_info.numLogicalProcessors = si.dwNumberOfProcessors;
} /* end of function get_logical_processor_count */
/* This funtion sets the field storing the number of physical processors
* in system_info */
typedef bool (WINAPI *glp_t)(LOGICAL_PROCESSOR_RELATIONSHIP,
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
static void get_physical_processor_count(void)
{
DWORD n_byte_buf = 0;
system_info.numPhysicalProcessors = 0; /* Init to 0 until found */
/* Get a handle to the DLL with the required function. Since the
* functdion GetModuleHandleW() is in kernel32.dll, it is safe to
* assume that kernel32.dll is already loaded. Not using
* LoadLibraryExW() simplifies error handling. */
HMODULE lib = GetModuleHandleW(L"kernel32.dll");
if (lib == (HMODULE) NULL) { /* Handle not obtained */
(void) fprintf(cp_err,
"Unable to obtain a handle to kernel32.dll. "
"System code = %lu\n",
(unsigned long) GetLastError());
return;
}
/* Locate GetLogicalProcessorInformationEx(). It must be
* dynamically loaded since it is only present in
* Windows 7/Server 2008 R2 and later OS versions */
FARPROC p_glp = GetProcAddress(lib,
"GetLogicalProcessorInformationEx");
if (p_glp == (FARPROC) NULL) { /* Did not get function addr OK */
(void) fprintf(cp_err,
"Unable to locate function "
"GetLogicalProcessorInformationEx. "
"System code = %lu\n",
(unsigned long) GetLastError());
return;
}
/* Find requried size. Should return FALSE/ERROR_INSUFFICIENT_BUFFER if
* working properly */
if (((glp_t) (*p_glp))(RelationProcessorPackage,
NULL, &n_byte_buf) != 0) {
fprintf(cp_err,
"Unexpected error getting logical processor buffer size.\n");
return;
}
{
DWORD rc;
if ((rc = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) {
fprintf(cp_err,
"Unable to get the logical processor bufer size. "
"System code = %lu.\n",
(unsigned long) rc);
return;
}
}
/* Allocate buffer to get the info */
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * const buf =
(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)TMALLOC(char, n_byte_buf);
if (buf == (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *) NULL) {
fprintf(cp_err,
"Unable to allocate a buffer of %lu bytes "
"for logical processor information.\n",
n_byte_buf);
return;
}
/* Try again with a buffer and the size obtained before */
{
DWORD rc;
if ((rc = ((glp_t) (*p_glp))(RelationProcessorPackage,
buf, &n_byte_buf)) == 0) {
fprintf(cp_err,
"Unable to get the logical processor info. "
"System code = %lu.\n",
(unsigned long) rc);
return;
}
}
/* Count the number of processor packages */
{
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * p_buf_cur = buf;
SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX * const p_buf_end =
(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)
((char *) buf + n_byte_buf);
unsigned int n_processor_package = 0;
for ( ; p_buf_cur < p_buf_end;
p_buf_cur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)
((char *) p_buf_cur + p_buf_cur->Size)) {
++n_processor_package;
}
system_info.numPhysicalProcessors = n_processor_package;
}
return;
} /* end of function get_physical_processor_count */
#else /* no Windows OS, no proc info file system */
static int get_sysmem(struct sys_memory *memall)
{
return -1; // Return N/A
}
void set_static_system_info(void)
{
/* Set to no data available */
system_info.osName = (char *) NULL;
system_info.cpuModelName = (char *) NULL;
system_info.numPhysicalProcessors = 0;
system_info.numLogicalProcessors = 0;
return;
} /* end of function set_static_system_info */
#endif