/*
** Copyright (C) 2001-2024 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see .
**/
#include "zbxsysinfo.h"
#include "../sysinfo.h"
#include "zbxcfg.h"
#include "zbxtime.h"
#include "zbxwin32.h"
#pragma comment(lib, "user32.lib")
/******************************************************************************
* *
* Purpose: reads value from Windows registry *
* *
******************************************************************************/
static wchar_t *read_registry_value(HKEY hKey, LPCTSTR name)
{
DWORD szData = 0;
wchar_t *value = NULL;
if (ERROR_SUCCESS == RegQueryValueEx(hKey, name, NULL, NULL, NULL, &szData))
{
value = zbx_malloc(NULL, szData);
if (ERROR_SUCCESS != RegQueryValueEx(hKey, name, NULL, NULL, (LPBYTE)value, &szData))
zbx_free(value);
}
return value;
}
/******************************************************************************
* *
* Purpose: gets Windows version information *
* *
******************************************************************************/
const OSVERSIONINFOEX *zbx_win_getversion(void)
{
#define ZBX_REGKEY_VERSION "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
#define ZBX_REGVALUE_CURRENTVERSION "CurrentVersion"
#define ZBX_REGVALUE_CURRENTBUILDNUMBER "CurrentBuildNumber"
#define ZBX_REGVALUE_CSDVERSION "CSDVersion"
#define ZBX_REGKEY_PRODUCT "System\\CurrentControlSet\\Control\\ProductOptions"
#define ZBX_REGVALUE_PRODUCTTYPE "ProductType"
static OSVERSIONINFOEX vi = {sizeof(OSVERSIONINFOEX)};
OSVERSIONINFOEX *pvi = NULL;
HKEY h_key_registry = NULL;
wchar_t *key_value = NULL, *ptr;
if (0 != vi.dwMajorVersion)
return &vi;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(ZBX_REGKEY_VERSION), 0, KEY_READ, &h_key_registry))
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to open registry key '%s'", ZBX_REGKEY_VERSION);
goto out;
}
if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CURRENTVERSION))))
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'", ZBX_REGVALUE_CURRENTVERSION);
goto out;
}
if (NULL != (ptr = wcschr(key_value, TEXT('.'))))
{
*ptr++ = L'\0';
vi.dwMinorVersion = _wtoi(ptr);
}
vi.dwMajorVersion = _wtoi(key_value);
zbx_free(key_value);
if (6 > vi.dwMajorVersion || 2 > vi.dwMinorVersion)
{
GetVersionEx((OSVERSIONINFO *)&vi);
}
else
{
if (NULL != (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CSDVERSION))))
{
wcscpy_s(vi.szCSDVersion, sizeof(vi.szCSDVersion) / sizeof(*vi.szCSDVersion), key_value);
zbx_free(key_value);
}
if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CURRENTBUILDNUMBER))))
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'",
ZBX_REGVALUE_CURRENTBUILDNUMBER);
goto out;
}
vi.dwBuildNumber = _wtoi(key_value);
zbx_free(key_value);
RegCloseKey(h_key_registry);
h_key_registry = NULL;
if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(ZBX_REGKEY_PRODUCT), 0, KEY_READ,
&h_key_registry))
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to open registry key '%s'", ZBX_REGKEY_PRODUCT);
goto out;
}
if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_PRODUCTTYPE))))
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'", ZBX_REGVALUE_PRODUCTTYPE);
goto out;
}
if (0 == wcscmp(key_value, L"WinNT"))
vi.wProductType = 1;
else if (0 == wcscmp(key_value, L"LenmanNT"))
vi.wProductType = 2;
else if (0 == wcscmp(key_value, L"ServerNT"))
vi.wProductType = 3;
zbx_free(key_value);
vi.dwPlatformId = VER_PLATFORM_WIN32_NT;
}
pvi = &vi;
out:
if (NULL != h_key_registry)
RegCloseKey(h_key_registry);
return pvi;
#undef ZBX_REGKEY_VERSION
#undef ZBX_REGVALUE_CURRENTVERSION
#undef ZBX_REGVALUE_CURRENTBUILDNUMBER
#undef ZBX_REGVALUE_CSDVERSION
#undef ZBX_REGKEY_PRODUCT
#undef ZBX_REGVALUE_PRODUCTTYPE
}
static void get_wmi_check_timeout(const char *wmi_namespace, const char *query, char **var,
double time_first_query_started, double *time_previous_query_finished)
{
double time_left = sysinfo_get_config_timeout() - (*time_previous_query_finished -
time_first_query_started);
if (0 >= time_left)
return;
zbx_wmi_get(wmi_namespace, query, time_left, var);
*time_previous_query_finished = zbx_time();
}
int system_uname(AGENT_REQUEST *request, AGENT_RESULT *result)
{
char *os = NULL;
size_t os_alloc = 0, os_offset = 0;
char *sysname = "Windows", *os_csname = NULL, *os_version = NULL, *os_caption = NULL, *os_csdversion = NULL,
*proc_architecture = NULL, *proc_addresswidth = NULL, *wmi_namespace = "root\\cimv2",
*arch = "";
int ret = SYSINFO_RET_FAIL;
double start_time = zbx_time();
double time_previous_query_finished = start_time;
/* Emulates uname(2) (POSIX) since it is not provided natively by Windows by taking */
/* the relevant values from Win32_OperatingSystem and Win32_Processor WMI classes. */
/* It was decided that in context of Windows OS ISA is more useful information than */
/* CPU architecture. This is contrary to POSIX and uname(2) in Unix. */
get_wmi_check_timeout(wmi_namespace, "select CSName from Win32_OperatingSystem", &os_csname, start_time,
&time_previous_query_finished);
get_wmi_check_timeout(wmi_namespace, "select Version from Win32_OperatingSystem", &os_version, start_time,
&time_previous_query_finished);
get_wmi_check_timeout(wmi_namespace, "select Caption from Win32_OperatingSystem", &os_caption, start_time,
&time_previous_query_finished);
get_wmi_check_timeout(wmi_namespace, "select CSDVersion from Win32_OperatingSystem", &os_csdversion, start_time,
&time_previous_query_finished);
get_wmi_check_timeout(wmi_namespace, "select Architecture from Win32_Processor", &proc_architecture, start_time,
&time_previous_query_finished);
get_wmi_check_timeout(wmi_namespace, "select AddressWidth from Win32_Processor", &proc_addresswidth, start_time,
&time_previous_query_finished);
if (0 >= sysinfo_get_config_timeout() - (time_previous_query_finished - start_time))
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "WMI aggregate query timeout"));
}
else
{
if (NULL != proc_architecture)
{
switch (atoi(proc_architecture))
{
case 0: arch = "x86"; break;
case 6: arch = "ia64"; break;
case 9:
if (NULL != proc_addresswidth)
{
if (32 == atoi(proc_addresswidth))
arch = "x86";
else
arch = "x64";
}
break;
}
}
/* The comments indicate the relevant field in struct utsname (POSIX) that is used in uname(2). */
zbx_snprintf_alloc(&os, &os_alloc, &os_offset, "%s %s %s %s%s%s %s",
sysname, /* sysname */
os_csname ? os_csname : "", /* nodename */
os_version ? os_version : "", /* release */
os_caption ? os_caption : "", /* version */
os_caption && os_csdversion ? " " : "",
os_caption && os_csdversion ? os_csdversion : "", /* version (cont.) */
arch); /* machine */
SET_STR_RESULT(result, os);
ret = SYSINFO_RET_OK;
}
zbx_free(os_csname);
zbx_free(os_version);
zbx_free(os_caption);
zbx_free(os_csdversion);
zbx_free(proc_architecture);
zbx_free(proc_addresswidth);
return ret;
}