1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "sysinfo.h"
21 #include "log.h"
22 #include "perfmon.h"
23 #include "cfg.h"
24 #pragma comment(lib, "user32.lib")
25 
26 /******************************************************************************
27  *                                                                            *
28  * Function: read_registry_value                                              *
29  *                                                                            *
30  * Purpose: read value from Windows registry                                  *
31  *                                                                            *
32  ******************************************************************************/
read_registry_value(HKEY hKey,LPCTSTR name)33 static wchar_t	*read_registry_value(HKEY hKey, LPCTSTR name)
34 {
35 	DWORD	szData;
36 	wchar_t	*value = NULL;
37 
38 	if (ERROR_SUCCESS == RegQueryValueEx(hKey, name, NULL, NULL, NULL, &szData))
39 	{
40 		value = zbx_malloc(NULL, szData);
41 		if (ERROR_SUCCESS != RegQueryValueEx(hKey, name, NULL, NULL, (LPBYTE)value, &szData))
42 			zbx_free(value);
43 	}
44 
45 	return value;
46 }
47 
48 /******************************************************************************
49  *                                                                            *
50  * Function: zbx_win_getversion                                               *
51  *                                                                            *
52  * Purpose: get Windows version information                                   *
53  *                                                                            *
54  ******************************************************************************/
zbx_win_getversion(void)55 const OSVERSIONINFOEX		*zbx_win_getversion(void)
56 {
57 #	define ZBX_REGKEY_VERSION		"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
58 #	define ZBX_REGVALUE_CURRENTVERSION	"CurrentVersion"
59 #	define ZBX_REGVALUE_CURRENTBUILDNUMBER	"CurrentBuildNumber"
60 #	define ZBX_REGVALUE_CSDVERSION		"CSDVersion"
61 
62 #	define ZBX_REGKEY_PRODUCT		"System\\CurrentControlSet\\Control\\ProductOptions"
63 #	define ZBX_REGVALUE_PRODUCTTYPE		"ProductType"
64 
65 	static OSVERSIONINFOEX	vi = {sizeof(OSVERSIONINFOEX)};
66 
67 	OSVERSIONINFOEX		*pvi = NULL;
68 	HKEY			h_key_registry = NULL;
69 	wchar_t			*key_value = NULL, *ptr;
70 
71 	if (0 != vi.dwMajorVersion)
72 		return &vi;
73 
74 	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(ZBX_REGKEY_VERSION), 0, KEY_READ, &h_key_registry))
75 	{
76 		zabbix_log(LOG_LEVEL_DEBUG, "failed to open registry key '%s'", ZBX_REGKEY_VERSION);
77 		goto out;
78 	}
79 
80 	if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CURRENTVERSION))))
81 	{
82 		zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'", ZBX_REGVALUE_CURRENTVERSION);
83 		goto out;
84 	}
85 
86 	if (NULL != (ptr = wcschr(key_value, TEXT('.'))))
87 	{
88 		*ptr++ = L'\0';
89 		vi.dwMinorVersion = _wtoi(ptr);
90 	}
91 
92 	vi.dwMajorVersion = _wtoi(key_value);
93 
94 	zbx_free(key_value);
95 
96 	if (6 > vi.dwMajorVersion || 2 > vi.dwMinorVersion)
97 	{
98 		GetVersionEx((OSVERSIONINFO *)&vi);
99 	}
100 	else
101 	{
102 		if (NULL != (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CSDVERSION))))
103 		{
104 			wcscpy_s(vi.szCSDVersion, sizeof(vi.szCSDVersion) / sizeof(*vi.szCSDVersion), key_value);
105 
106 			zbx_free(key_value);
107 		}
108 
109 		if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_CURRENTBUILDNUMBER))))
110 		{
111 			zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'",
112 					ZBX_REGVALUE_CURRENTBUILDNUMBER);
113 			goto out;
114 		}
115 
116 		vi.dwBuildNumber = _wtoi(key_value);
117 		zbx_free(key_value);
118 
119 		RegCloseKey(h_key_registry);
120 		h_key_registry = NULL;
121 
122 		if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(ZBX_REGKEY_PRODUCT), 0, KEY_READ,
123 				&h_key_registry))
124 		{
125 			zabbix_log(LOG_LEVEL_DEBUG, "failed to open registry key '%s'", ZBX_REGKEY_PRODUCT);
126 			goto out;
127 		}
128 
129 		if (NULL == (key_value = read_registry_value(h_key_registry, TEXT(ZBX_REGVALUE_PRODUCTTYPE))))
130 		{
131 			zabbix_log(LOG_LEVEL_DEBUG, "failed to read registry value '%s'", ZBX_REGVALUE_PRODUCTTYPE);
132 			goto out;
133 		}
134 
135 		if (0 == wcscmp(key_value, L"WinNT"))
136 			vi.wProductType = 1;
137 		else if (0 == wcscmp(key_value, L"LenmanNT"))
138 			vi.wProductType = 2;
139 		else if (0 == wcscmp(key_value, L"ServerNT"))
140 			vi.wProductType = 3;
141 
142 		zbx_free(key_value);
143 
144 		vi.dwPlatformId = VER_PLATFORM_WIN32_NT;
145 	}
146 
147 	pvi = &vi;
148 out:
149 	if (NULL != h_key_registry)
150 		RegCloseKey(h_key_registry);
151 
152 	return pvi;
153 }
154 
get_wmi_check_timeout(const char * wmi_namespace,const char * query,char ** var,double time_first_query_started,double * time_previous_query_finished)155 static void	get_wmi_check_timeout(const char *wmi_namespace, const char *query, char **var,
156 		double time_first_query_started, double *time_previous_query_finished)
157 {
158 	double	time_left = CONFIG_TIMEOUT - (*time_previous_query_finished - time_first_query_started);
159 
160 	if (0 >= time_left)
161 		return;
162 
163 	zbx_wmi_get(wmi_namespace, query, time_left, var);
164 	*time_previous_query_finished = zbx_time();
165 }
166 
SYSTEM_UNAME(AGENT_REQUEST * request,AGENT_RESULT * result)167 int	SYSTEM_UNAME(AGENT_REQUEST *request, AGENT_RESULT *result)
168 {
169 	char	*os = NULL;
170 	size_t	os_alloc = 0, os_offset = 0;
171 	char	*sysname = "Windows";
172 	char	*os_csname = NULL;
173 	char	*os_version = NULL;
174 	char	*os_caption = NULL;
175 	char	*os_csdversion = NULL;
176 	char	*proc_architecture = NULL;
177 	char	*proc_addresswidth = NULL;
178 	char	*wmi_namespace = "root\\cimv2";
179 	char	*arch = "<unknown architecture>";
180 	int	ret = SYSINFO_RET_FAIL;
181 	double	start_time = zbx_time();
182 	double	time_previous_query_finished = start_time;
183 
184 	/* Emulates uname(2) (POSIX) since it is not provided natively by Windows by taking */
185 	/* the relevant values from Win32_OperatingSystem and Win32_Processor WMI classes.  */
186 	/* It was decided that in context of Windows OS ISA is more useful information than */
187 	/* CPU architecture. This is contrary to POSIX and uname(2) in Unix.                */
188 
189 	get_wmi_check_timeout(wmi_namespace, "select CSName from Win32_OperatingSystem", &os_csname, start_time,
190 			&time_previous_query_finished);
191 	get_wmi_check_timeout(wmi_namespace, "select Version from Win32_OperatingSystem", &os_version, start_time,
192 			&time_previous_query_finished);
193 	get_wmi_check_timeout(wmi_namespace, "select Caption from Win32_OperatingSystem", &os_caption, start_time,
194 			&time_previous_query_finished);
195 	get_wmi_check_timeout(wmi_namespace, "select CSDVersion from Win32_OperatingSystem", &os_csdversion, start_time,
196 			&time_previous_query_finished);
197 	get_wmi_check_timeout(wmi_namespace, "select Architecture from Win32_Processor", &proc_architecture, start_time,
198 			&time_previous_query_finished);
199 	get_wmi_check_timeout(wmi_namespace, "select AddressWidth from Win32_Processor", &proc_addresswidth, start_time,
200 			&time_previous_query_finished);
201 
202 	if (0 >= CONFIG_TIMEOUT - (time_previous_query_finished - start_time))
203 	{
204 		SET_MSG_RESULT(result, zbx_strdup(NULL, "WMI aggregate query timeout"));
205 	}
206 	else
207 	{
208 		if (NULL != proc_architecture)
209 		{
210 			switch (atoi(proc_architecture))
211 			{
212 				case 0: arch = "x86"; break;
213 				case 6: arch = "ia64"; break;
214 				case 9:
215 					if (NULL != proc_addresswidth)
216 					{
217 						if (32 == atoi(proc_addresswidth))
218 							arch = "x86";
219 						else
220 							arch = "x64";
221 					}
222 
223 					break;
224 			}
225 		}
226 
227 		/* The comments indicate the relevant field in struct utsname (POSIX) that is used in uname(2). */
228 		zbx_snprintf_alloc(&os, &os_alloc, &os_offset, "%s %s %s %s%s%s %s",
229 				sysname,						/* sysname */
230 				os_csname ? os_csname : "<unknown nodename>",		/* nodename */
231 				os_version ? os_version : "<unknown release>",		/* release */
232 				os_caption ? os_caption : "<unknown version>",		/* version */
233 				os_caption && os_csdversion ? " " : "",
234 				os_caption && os_csdversion ? os_csdversion : "",	/* version (cont.) */
235 				arch);							/* machine */
236 
237 		SET_STR_RESULT(result, os);
238 
239 		ret = SYSINFO_RET_OK;
240 	}
241 
242 	zbx_free(os_csname);
243 	zbx_free(os_version);
244 	zbx_free(os_caption);
245 	zbx_free(os_csdversion);
246 	zbx_free(proc_architecture);
247 	zbx_free(proc_addresswidth);
248 
249 	return ret;
250 }
251