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 "common.h"
21 #include "stats.h"
22 #include "perfmon.h"
23 #include "log.h"
24 
25 static ZBX_THREAD_LOCAL zbx_perf_counter_id_t	*PerfCounterList = NULL;
26 
27 /* This struct contains mapping between built-in English object names and PDH indexes. */
28 /* If you change it then you also need to add enum values to zbx_builtin_object_ref_t. */
29 static struct builtin_object_ref
30 {
31 	unsigned long	pdhIndex;
32 	wchar_t		eng_name[PDH_MAX_COUNTER_NAME];
33 	DWORD		minSupported_dwMajorVersion;
34 	DWORD		minSupported_dwMinorVersion;
35 }
36 builtin_object_map[] =
37 {
38 	{ 0, L"System", 0, 0 },
39 	{ 0, L"Processor", 0, 0 },
40 	{ 0, L"Processor Information", 6, 1 },
41 	{ 0, L"Terminal Services", 0, 0 }
42 };
43 
44 /* this enum must be only modified along with builtin_object_map[] */
45 typedef enum
46 {
47 	POI_SYSTEM = 0,
48 	POI_PROCESSOR,
49 	POI_PROCESSOR_INFORMATION,
50 	POI_TERMINAL_SERVICES,
51 	POI_MAX_INDEX = POI_TERMINAL_SERVICES
52 }
53 zbx_builtin_object_ref_t;
54 
55 /* This struct contains mapping between built-in English counter names and PDH indexes. */
56 /* If you change it then you also need to add enum values to zbx_builtin_counter_ref_t. */
57 static struct builtin_counter_ref
58 {
59 	unsigned long			pdhIndex;
60 	zbx_builtin_object_ref_t	object;
61 	wchar_t				eng_name[PDH_MAX_COUNTER_NAME];
62 	DWORD				minSupported_dwMajorVersion;
63 	DWORD				minSupported_dwMinorVersion;
64 }
65 builtin_counter_map[] =
66 {
67 	{ 0,	POI_SYSTEM,			L"Processor Queue Length",	0,	0},
68 	{ 0,	POI_SYSTEM,			L"System Up Time",		0,	0},
69 	{ 0,	POI_PROCESSOR,			L"% Processor Time",		0, 	0},
70 	{ 0,	POI_PROCESSOR_INFORMATION,	L"% Processor Time",		6,	1},
71 	{ 0,	POI_TERMINAL_SERVICES,		L"Total Sessions",		0,	0}
72 };
73 
zbx_PdhMakeCounterPath(const char * function,PDH_COUNTER_PATH_ELEMENTS * cpe,char * counterpath)74 PDH_STATUS	zbx_PdhMakeCounterPath(const char *function, PDH_COUNTER_PATH_ELEMENTS *cpe, char *counterpath)
75 {
76 	DWORD		dwSize = PDH_MAX_COUNTER_PATH;
77 	wchar_t		*wcounterPath = NULL;
78 	PDH_STATUS	pdh_status;
79 
80 	wcounterPath = zbx_malloc(wcounterPath, sizeof(wchar_t) * PDH_MAX_COUNTER_PATH);
81 
82 	if (ERROR_SUCCESS != (pdh_status = PdhMakeCounterPath(cpe, wcounterPath, &dwSize, 0)))
83 	{
84 		char	*object, *counter;
85 
86 		object = zbx_unicode_to_utf8(cpe->szObjectName);
87 		counter = zbx_unicode_to_utf8(cpe->szCounterName);
88 
89 		zabbix_log(LOG_LEVEL_ERR, "%s(): cannot make counterpath for \"\\%s\\%s\": %s",
90 				function, object, counter, strerror_from_module(pdh_status, L"PDH.DLL"));
91 
92 		zbx_free(counter);
93 		zbx_free(object);
94 	}
95 	else
96 		zbx_unicode_to_utf8_static(wcounterPath, counterpath, PDH_MAX_COUNTER_PATH);
97 
98 	zbx_free(wcounterPath);
99 
100 	return pdh_status;
101 }
102 
zbx_PdhOpenQuery(const char * function,PDH_HQUERY query)103 PDH_STATUS	zbx_PdhOpenQuery(const char *function, PDH_HQUERY query)
104 {
105 	PDH_STATUS	pdh_status;
106 
107 	if (ERROR_SUCCESS != (pdh_status = PdhOpenQuery(NULL, 0, query)))
108 	{
109 		zabbix_log(LOG_LEVEL_ERR, "%s(): call to PdhOpenQuery() failed: %s",
110 				function, strerror_from_module(pdh_status, L"PDH.DLL"));
111 	}
112 
113 	return pdh_status;
114 }
115 
116 /******************************************************************************
117  *                                                                            *
118  * Comments: counter is NULL if it is not in the collector,                   *
119  *           do not call it for PERF_COUNTER_ACTIVE counters                  *
120  *                                                                            *
121  ******************************************************************************/
zbx_PdhAddCounter(const char * function,zbx_perf_counter_data_t * counter,PDH_HQUERY query,const char * counterpath,zbx_perf_counter_lang_t lang,PDH_HCOUNTER * handle)122 PDH_STATUS	zbx_PdhAddCounter(const char *function, zbx_perf_counter_data_t *counter, PDH_HQUERY query,
123 		const char *counterpath, zbx_perf_counter_lang_t lang, PDH_HCOUNTER *handle)
124 {
125 	/* pointer type to PdhAddEnglishCounterW() */
126 	typedef PDH_STATUS (WINAPI *ADD_ENG_COUNTER)(PDH_HQUERY, LPCWSTR, DWORD_PTR, PDH_HCOUNTER);
127 
128 	PDH_STATUS	pdh_status = ERROR_SUCCESS;
129 	wchar_t		*wcounterPath = NULL;
130 	int		need_english;
131 
132 	ZBX_THREAD_LOCAL static ADD_ENG_COUNTER add_eng_counter;
133 	ZBX_THREAD_LOCAL static int 		first_call = 1;
134 
135 	need_english = PERF_COUNTER_LANG_DEFAULT != lang ||
136 			(NULL != counter && PERF_COUNTER_LANG_DEFAULT != counter->lang);
137 
138 	/* PdhAddEnglishCounterW() is only available on Windows 2008/Vista and onwards, */
139 	/* so we need to resolve it dynamically and fail if it's not available */
140 	if (0 != first_call && 0 != need_english)
141 	{
142 		if (NULL == (add_eng_counter = (ADD_ENG_COUNTER)GetProcAddress(GetModuleHandle(L"PDH.DLL"),
143 				"PdhAddEnglishCounterW")))
144 		{
145 			zabbix_log(LOG_LEVEL_WARNING, "PdhAddEnglishCounter() is not available, "
146 					"perf_counter_en[] is not supported");
147 		}
148 
149 		first_call = 0;
150 	}
151 
152 	if (0 != need_english && NULL == add_eng_counter)
153 	{
154 		pdh_status = PDH_NOT_IMPLEMENTED;
155 	}
156 
157 	if (ERROR_SUCCESS == pdh_status)
158 	{
159 		wcounterPath = zbx_utf8_to_unicode(counterpath);
160 	}
161 
162 	if (ERROR_SUCCESS == pdh_status && NULL == *handle)
163 	{
164 		pdh_status = need_english ?
165 			add_eng_counter(query, wcounterPath, 0, handle) :
166 			PdhAddCounter(query, wcounterPath, 0, handle);
167 	}
168 
169 	if (ERROR_SUCCESS != pdh_status && NULL != *handle)
170 	{
171 		if (ERROR_SUCCESS == PdhRemoveCounter(*handle))
172 			*handle = NULL;
173 	}
174 
175 	if (ERROR_SUCCESS == pdh_status)
176 	{
177 		if (NULL != counter)
178 			counter->status = PERF_COUNTER_INITIALIZED;
179 
180 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): PerfCounter '%s' successfully added", function, counterpath);
181 	}
182 	else
183 	{
184 		if (NULL != counter)
185 			counter->status = PERF_COUNTER_NOTSUPPORTED;
186 
187 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): unable to add PerfCounter '%s': %s",
188 				function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
189 	}
190 
191 	zbx_free(wcounterPath);
192 
193 	return pdh_status;
194 }
195 
zbx_PdhCollectQueryData(const char * function,const char * counterpath,PDH_HQUERY query)196 PDH_STATUS	zbx_PdhCollectQueryData(const char *function, const char *counterpath, PDH_HQUERY query)
197 {
198 	PDH_STATUS	pdh_status;
199 
200 	if (ERROR_SUCCESS != (pdh_status = PdhCollectQueryData(query)))
201 	{
202 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot collect data '%s': %s",
203 				function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
204 	}
205 
206 	return pdh_status;
207 }
208 
zbx_PdhGetRawCounterValue(const char * function,const char * counterpath,PDH_HCOUNTER handle,PPDH_RAW_COUNTER value)209 PDH_STATUS	zbx_PdhGetRawCounterValue(const char *function, const char *counterpath, PDH_HCOUNTER handle, PPDH_RAW_COUNTER value)
210 {
211 	PDH_STATUS	pdh_status;
212 
213 	if (ERROR_SUCCESS != (pdh_status = PdhGetRawCounterValue(handle, NULL, value)) ||
214 		(PDH_CSTATUS_VALID_DATA != value->CStatus && PDH_CSTATUS_NEW_DATA != value->CStatus))
215 	{
216 		if (ERROR_SUCCESS == pdh_status)
217 			pdh_status = value->CStatus;
218 
219 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot get counter value '%s': %s",
220 				function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
221 	}
222 
223 	return pdh_status;
224 }
225 
226 /******************************************************************************
227  *                                                                            *
228  * Comments: Get the value of a counter. If it is a rate counter,             *
229  *           sleep 1 second to get the second raw value.                      *
230  *                                                                            *
231  ******************************************************************************/
calculate_counter_value(const char * function,const char * counterpath,zbx_perf_counter_lang_t lang,double * value)232 PDH_STATUS	calculate_counter_value(const char *function, const char *counterpath,
233 		zbx_perf_counter_lang_t lang, double *value)
234 {
235 	PDH_HQUERY		query;
236 	PDH_HCOUNTER		handle = NULL;
237 	PDH_STATUS		pdh_status;
238 	PDH_RAW_COUNTER		rawData, rawData2;
239 	PDH_FMT_COUNTERVALUE	counterValue;
240 
241 	if (ERROR_SUCCESS != (pdh_status = zbx_PdhOpenQuery(function, &query)))
242 		return pdh_status;
243 
244 	if (ERROR_SUCCESS != (pdh_status = zbx_PdhAddCounter(function, NULL, query, counterpath, lang, &handle)))
245 		goto close_query;
246 
247 	if (ERROR_SUCCESS != (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query)))
248 		goto remove_counter;
249 
250 	if (ERROR_SUCCESS != (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath, handle, &rawData)))
251 		goto remove_counter;
252 
253 	if (PDH_CSTATUS_INVALID_DATA == (pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE |
254 			PDH_FMT_NOCAP100, &rawData, NULL, &counterValue)))
255 	{
256 		/* some (e.g., rate) counters require two raw values, MSDN lacks documentation */
257 		/* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */
258 
259 		zbx_sleep(1);
260 
261 		if (ERROR_SUCCESS == (pdh_status = zbx_PdhCollectQueryData(function, counterpath, query)) &&
262 				ERROR_SUCCESS == (pdh_status = zbx_PdhGetRawCounterValue(function, counterpath,
263 				handle, &rawData2)))
264 		{
265 			pdh_status = PdhCalculateCounterFromRawValue(handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100,
266 					&rawData2, &rawData, &counterValue);
267 		}
268 	}
269 
270 	if (ERROR_SUCCESS != pdh_status || (PDH_CSTATUS_VALID_DATA != counterValue.CStatus &&
271 			PDH_CSTATUS_NEW_DATA != counterValue.CStatus))
272 	{
273 		if (ERROR_SUCCESS == pdh_status)
274 			pdh_status = counterValue.CStatus;
275 
276 		zabbix_log(LOG_LEVEL_DEBUG, "%s(): cannot calculate counter value '%s': %s",
277 				function, counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
278 	}
279 	else
280 	{
281 		*value = counterValue.doubleValue;
282 	}
283 remove_counter:
284 	PdhRemoveCounter(handle);
285 close_query:
286 	PdhCloseQuery(query);
287 
288 	return pdh_status;
289 }
290 
291 /******************************************************************************
292  *                                                                            *
293  * Function: get_builtin_object_index                                         *
294  *                                                                            *
295  * Purpose: get performance object index by reference value described by      *
296  *          zbx_builtin_counter_ref_t enum                                    *
297  *                                                                            *
298  * Parameters: counter_ref - [IN] built-in performance object                 *
299  *                                                                            *
300  * Comments: Performance object index values can differ across Windows        *
301  *           installations for the same names                                 *
302  *                                                                            *
303  ******************************************************************************/
get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)304 DWORD	get_builtin_object_index(zbx_builtin_counter_ref_t counter_ref)
305 {
306 	return builtin_object_map[builtin_counter_map[counter_ref].object].pdhIndex;
307 }
308 
309 /******************************************************************************
310  *                                                                            *
311  * Function: get_builtin_counter_index                                        *
312  *                                                                            *
313  * Purpose: get performance counter index by reference value described by     *
314  *          zbx_builtin_counter_ref_t enum                                    *
315  *                                                                            *
316  * Parameters: counter_ref - [IN] built-in performance counter                *
317  *                                                                            *
318  * Comments: Performance counter index values can differ across Windows       *
319  *           installations for the same names                                 *
320  *                                                                            *
321  ******************************************************************************/
get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)322 DWORD	get_builtin_counter_index(zbx_builtin_counter_ref_t counter_ref)
323 {
324 	return builtin_counter_map[counter_ref].pdhIndex;
325 }
326 
327 /******************************************************************************
328  *                                                                            *
329  * Function: get_all_counter_names                                            *
330  *                                                                            *
331  * Purpose: function to read counter names/help from registry                 *
332  *                                                                            *
333  * Parameters: reg_key           - [IN] registry key                          *
334  *             reg_value_name    - [IN] name of the registry value            *
335  *                                                                            *
336  * Return value: wchar_t* buffer with list of strings on success,             *
337  *               NULL on failure                                              *
338  *                                                                            *
339  * Comments: This function should be normally called with reg_key parameter   *
340  *           set to HKEY_PERFORMANCE_NLSTEXT (localized names) or             *
341  *           HKEY_PERFORMANCE_TEXT (English names); and reg_value_name        *
342  *           parameter set to L"Counter" parameter. It returns a list of      *
343  *           null-terminated string pairs. Last string is followed by         *
344  *           an additional null-terminator. The return buffer must be freed   *
345  *           by the caller.                                                   *
346  *                                                                            *
347  ******************************************************************************/
get_all_counter_names(HKEY reg_key,wchar_t * reg_value_name)348 wchar_t	*get_all_counter_names(HKEY reg_key, wchar_t *reg_value_name)
349 {
350 	wchar_t		*buffer = NULL;
351 	DWORD		buffer_size = 0;
352 	LSTATUS		status = ERROR_SUCCESS;
353 
354 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
355 
356 	/* query the size of the text data for further buffer allocation */
357 	if (ERROR_SUCCESS != (status = RegQueryValueEx(reg_key, reg_value_name, NULL, NULL, NULL, &buffer_size)))
358 	{
359 		zabbix_log(LOG_LEVEL_ERR, "RegQueryValueEx() failed at getting buffer size, 0x%lx",
360 				(unsigned long)status);
361 		goto finish;
362 	}
363 
364 	buffer = (wchar_t*)zbx_malloc(buffer, (size_t)buffer_size);
365 
366 	if (ERROR_SUCCESS != (status = RegQueryValueEx(reg_key, reg_value_name, NULL, NULL, (LPBYTE)buffer,
367 			&buffer_size)))
368 	{
369 		zabbix_log(LOG_LEVEL_ERR, "RegQueryValueEx() failed with 0x%lx", (unsigned long)status);
370 		zbx_free(buffer);
371 		goto finish;
372 	}
373 finish:
374 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
375 
376 	return buffer;
377 }
378 
379 /******************************************************************************
380  *                                                                            *
381  * Function: get_perf_name_by_index                                           *
382  *                                                                            *
383  * Purpose: fills performance counter name based on its index                 *
384  *                                                                            *
385  * Parameters: index - [IN]  PDH counter index                                *
386  *             name  - [OUT] counter name buffer                              *
387  *             size  - [IN]  counter name buffer size                         *
388  *                                                                            *
389  * Return value: SUCCEED if counter data is valid,                            *
390  *               FAIL otherwise                                               *
391  *                                                                            *
392  ******************************************************************************/
get_perf_name_by_index(DWORD index,wchar_t * name,DWORD size)393 static int	get_perf_name_by_index(DWORD index, wchar_t *name, DWORD size)
394 {
395 	int		ret = SUCCEED;
396 	PDH_STATUS	pdh_status;
397 
398 	if (ERROR_SUCCESS != (pdh_status = PdhLookupPerfNameByIndex(NULL, index, name, &size)))
399 	{
400 		zabbix_log(LOG_LEVEL_ERR, "PdhLookupPerfNameByIndex() failed: %s",
401 				strerror_from_module(pdh_status, L"PDH.DLL"));
402 		ret = FAIL;
403 	}
404 
405 	return ret;
406 }
407 
408 /******************************************************************************
409  *                                                                            *
410  * Function: validate_counter_path                                            *
411  *                                                                            *
412  * Purpose: checks if a specified counter path data is pointing to a valid    *
413  *          counter                                                           *
414  *                                                                            *
415  * Parameters: cpe - [IN] PDH counter path data                               *
416  *                                                                            *
417  * Return value: SUCCEED if counter data is valid,                            *
418  *               FAIL otherwise                                               *
419  *                                                                            *
420  ******************************************************************************/
validate_counter_path(PDH_COUNTER_PATH_ELEMENTS * cpe)421 static int	validate_counter_path(PDH_COUNTER_PATH_ELEMENTS	*cpe)
422 {
423 	int		ret = FAIL;
424 	DWORD		s = 0;
425 	PDH_STATUS	pdh_status;
426 	wchar_t		*path = NULL;
427 
428 	if (PDH_MORE_DATA == (pdh_status = PdhMakeCounterPath(cpe, NULL, &s, 0)))
429 	{
430 		path = zbx_malloc(path, sizeof(wchar_t) * s);
431 
432 		if (ERROR_SUCCESS != (pdh_status = PdhMakeCounterPath(cpe, path, &s, 0)))
433 		{
434 			zabbix_log(LOG_LEVEL_WARNING, "PdhMakeCounterPath() failed: %s",
435 					strerror_from_module(pdh_status, L"PDH.DLL"));
436 		}
437 		else if (ERROR_SUCCESS != (pdh_status = PdhValidatePath(path)))
438 		{
439 			if (PDH_CSTATUS_NO_COUNTER != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status)
440 			{
441 				zabbix_log(LOG_LEVEL_DEBUG, "PdhValidatePath() szObjectName:%s szCounterName:%s"
442 						" failed: %s", cpe->szObjectName, cpe->szCounterName,
443 						strerror_from_module(pdh_status, L"PDH.DLL"));
444 			}
445 		}
446 		else
447 		{
448 			ret = SUCCEED;
449 		}
450 
451 		zbx_free(path);
452 	}
453 	else
454 	{
455 		zabbix_log(LOG_LEVEL_DEBUG, "PdhMakeCounterPath() failed: %s",
456 				strerror_from_module(pdh_status, L"PDH.DLL"));
457 	}
458 
459 	return ret;
460 }
461 
462 /******************************************************************************
463  *                                                                            *
464  * Function: validate_object_counter                                          *
465  *                                                                            *
466  * Purpose: checks if specified counter is valid successor of the object      *
467  *                                                                            *
468  * Parameters: object  - [IN] PDH object index                                *
469  *             counter - [IN] PDH counter index                               *
470  *                                                                            *
471  * Return value: SUCCEED if object - counter combination is valid,            *
472  *               FAIL otherwise                                               *
473  *                                                                            *
474  ******************************************************************************/
validate_object_counter(DWORD object,DWORD counter)475 static int	validate_object_counter(DWORD object, DWORD counter)
476 {
477 	PDH_COUNTER_PATH_ELEMENTS	*cpe;
478 	int				ret = SUCCEED;
479 
480 	cpe = (PDH_COUNTER_PATH_ELEMENTS *)zbx_malloc(NULL, sizeof(PDH_COUNTER_PATH_ELEMENTS));
481 	memset(cpe, 0, sizeof(PDH_COUNTER_PATH_ELEMENTS));
482 
483 	cpe->szObjectName = zbx_malloc(NULL, sizeof(wchar_t) * PDH_MAX_COUNTER_NAME);
484 	cpe->szCounterName = zbx_malloc(NULL, sizeof(wchar_t) * PDH_MAX_COUNTER_NAME);
485 
486 	if (SUCCEED != get_perf_name_by_index(object, cpe->szObjectName, PDH_MAX_COUNTER_NAME) ||
487 			SUCCEED != get_perf_name_by_index(counter, cpe->szCounterName, PDH_MAX_COUNTER_NAME))
488 	{
489 		ret = FAIL;
490 		goto out;
491 	}
492 
493 	if (SUCCEED != validate_counter_path(cpe))
494 	{
495 		/* try with "any" instance name */
496 		cpe->szInstanceName = L"*";
497 
498 		if (SUCCEED != validate_counter_path(cpe))
499 			ret = FAIL;
500 	}
501 
502 out:
503 	zbx_free(cpe->szCounterName);
504 	zbx_free(cpe->szObjectName);
505 	zbx_free(cpe);
506 
507 	return ret;
508 }
509 
510 /******************************************************************************
511  *                                                                            *
512  * Function: init_builtin_counter_indexes                                     *
513  *                                                                            *
514  * Purpose: Scans registry key with all performance counter English names     *
515  *          and obtains system-dependent PDH counter indexes for further      *
516  *          use by corresponding items.                                       *
517  *                                                                            *
518  * Return value: SUCCEED/FAIL                                                 *
519  *                                                                            *
520  * Comments: This function should be normally called during agent             *
521  *           initialization from init_perf_collector().                       *
522  *                                                                            *
523  ******************************************************************************/
init_builtin_counter_indexes(void)524 int	init_builtin_counter_indexes(void)
525 {
526 	int 				ret = SUCCEED, i;
527 	wchar_t 			*counter_text, *eng_names, *counter_base;
528 	DWORD				counter_index;
529 	static const OSVERSIONINFOEX	*vi = NULL;
530 
531 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
532 
533 	if (NULL == vi && NULL == (vi = zbx_win_getversion()))
534 	{
535 		zabbix_log(LOG_LEVEL_ERR, "Failed to get windows version");
536 		ret = FAIL;
537 		goto out;
538 	}
539 
540 	/* Get buffer holding a list of performance counter indexes and English counter names. */
541 	/* L"Counter" stores names, L"Help" stores descriptions ("Help" is not used).          */
542 	if (NULL == (counter_base = eng_names = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")))
543 	{
544 		ret = FAIL;
545 		goto out;
546 	}
547 
548 	/* bypass first pair of counter data elements - these contain number of records */
549 	counter_base += wcslen(counter_base) + 1;
550 	counter_base += wcslen(counter_base) + 1;
551 
552 	/* get builtin object names */
553 	for (counter_text = counter_base; 0 != *counter_text; counter_text += wcslen(counter_text) + 1)
554 	{
555 		counter_index = (DWORD)_wtoi(counter_text);
556 		counter_text += wcslen(counter_text) + 1;
557 
558 		for (i = 0; i < ARRSIZE(builtin_object_map); i++)
559 		{
560 			if (0 == builtin_object_map[i].pdhIndex && vi->dwMajorVersion >=
561 					builtin_object_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=
562 					builtin_object_map[i].minSupported_dwMinorVersion && 0 ==
563 					wcscmp(builtin_object_map[i].eng_name, counter_text))
564 			{
565 				builtin_object_map[i].pdhIndex = counter_index;
566 				break;
567 			}
568 		}
569 	}
570 
571 	/* Get builtin counter names. There may be counter name duplicates. */
572 	/* Validate them in combination with parent object.                 */
573 	for (counter_text = counter_base; 0 != *counter_text; counter_text += wcslen(counter_text) + 1)
574 	{
575 		counter_index = (DWORD)_wtoi(counter_text);
576 		counter_text += wcslen(counter_text) + 1;
577 
578 		for (i = 0; i < ARRSIZE(builtin_counter_map); i++)
579 		{
580 			if (0 == builtin_counter_map[i].pdhIndex && vi->dwMajorVersion >=
581 					builtin_counter_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=
582 					builtin_counter_map[i].minSupported_dwMinorVersion && 0 ==
583 					wcscmp(builtin_counter_map[i].eng_name, counter_text) && SUCCEED ==
584 					validate_object_counter(get_builtin_object_index(i), counter_index))
585 			{
586 				builtin_counter_map[i].pdhIndex = counter_index;
587 				break;
588 			}
589 		}
590 	}
591 
592 	zbx_free(eng_names);
593 
594 #define CHECK_COUNTER_INDICES(index_map)								\
595 	for (i = 0; i < ARRSIZE(index_map); i++)							\
596 	{												\
597 		if (0 == index_map[i].pdhIndex && vi->dwMajorVersion >=					\
598 				index_map[i].minSupported_dwMajorVersion && vi->dwMinorVersion >=       \
599 				builtin_counter_map[i].minSupported_dwMinorVersion)                     \
600 		{											\
601 			char	*counter;								\
602 													\
603 			counter = zbx_unicode_to_utf8(index_map[i].eng_name);				\
604 			zabbix_log(LOG_LEVEL_ERR, "Failed to initialize builtin counter: %s", counter);	\
605 			zbx_free(counter);								\
606 		}											\
607 	}
608 
609 	/* check if all builtin counter indices are filled */
610 	CHECK_COUNTER_INDICES(builtin_object_map);
611 	CHECK_COUNTER_INDICES(builtin_counter_map);
612 
613 #undef CHECK_COUNTER_INDICES
614 out:
615 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
616 
617 	return ret;
618 }
619 
620 /******************************************************************************
621  *                                                                            *
622  * Function: get_counter_name                                                 *
623  *                                                                            *
624  * Purpose: get performance object or counter name by PDH index               *
625  *                                                                            *
626  * Parameters: pdhIndex - [IN] built-in performance counter index             *
627  *                                                                            *
628  * Return value: PDH performance counter name                                 *
629  *               or "UnknownPerformanceCounter" on failure                    *
630  *                                                                            *
631  * Comments: Performance counter index values can differ across Windows       *
632  *           installations for the same names                                 *
633  *                                                                            *
634  ******************************************************************************/
get_counter_name(DWORD pdhIndex)635 wchar_t	*get_counter_name(DWORD pdhIndex)
636 {
637 	zbx_perf_counter_id_t	*counterName;
638 
639 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() pdhIndex:%u", __func__, pdhIndex);
640 
641 	counterName = PerfCounterList;
642 	while (NULL != counterName)
643 	{
644 		if (counterName->pdhIndex == pdhIndex)
645 			break;
646 		counterName = counterName->next;
647 	}
648 
649 	if (NULL == counterName)
650 	{
651 		counterName = (zbx_perf_counter_id_t *)zbx_malloc(counterName, sizeof(zbx_perf_counter_id_t));
652 
653 		memset(counterName, 0, sizeof(zbx_perf_counter_id_t));
654 		counterName->pdhIndex = pdhIndex;
655 		counterName->next = PerfCounterList;
656 
657 		if (SUCCEED == get_perf_name_by_index(pdhIndex, counterName->name, PDH_MAX_COUNTER_NAME))
658 		{
659 			PerfCounterList = counterName;
660 		}
661 		else
662 		{
663 			zbx_free(counterName);
664 			zabbix_log(LOG_LEVEL_DEBUG, "End of %s():FAIL", __func__);
665 			return L"UnknownPerformanceCounter";
666 		}
667 	}
668 
669 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():SUCCEED", __func__);
670 
671 	return counterName->name;
672 }
673 
check_counter_path(char * counterPath,int convert_from_numeric)674 int	check_counter_path(char *counterPath, int convert_from_numeric)
675 {
676 	PDH_COUNTER_PATH_ELEMENTS	*cpe = NULL;
677 	PDH_STATUS			status;
678 	int				ret = FAIL;
679 	DWORD				dwSize = 0;
680 	wchar_t				*wcounterPath;
681 
682 	wcounterPath = zbx_utf8_to_unicode(counterPath);
683 
684 	status = PdhParseCounterPath(wcounterPath, NULL, &dwSize, 0);
685 	if (PDH_MORE_DATA == status || ERROR_SUCCESS == status)
686 	{
687 		cpe = (PDH_COUNTER_PATH_ELEMENTS *)zbx_malloc(cpe, dwSize);
688 	}
689 	else
690 	{
691 		zabbix_log(LOG_LEVEL_ERR, "cannot get required buffer size for counter path '%s': %s",
692 				counterPath, strerror_from_module(status, L"PDH.DLL"));
693 		goto clean;
694 	}
695 
696 	if (ERROR_SUCCESS != (status = PdhParseCounterPath(wcounterPath, cpe, &dwSize, 0)))
697 	{
698 		zabbix_log(LOG_LEVEL_ERR, "cannot parse counter path '%s': %s",
699 				counterPath, strerror_from_module(status, L"PDH.DLL"));
700 		goto clean;
701 	}
702 
703 	if (0 != convert_from_numeric)
704 	{
705 		int is_numeric = (SUCCEED == _wis_uint(cpe->szObjectName) ? 0x01 : 0);
706 		is_numeric |= (SUCCEED == _wis_uint(cpe->szCounterName) ? 0x02 : 0);
707 
708 		if (0 != is_numeric)
709 		{
710 			if (0x01 & is_numeric)
711 				cpe->szObjectName = get_counter_name(_wtoi(cpe->szObjectName));
712 			if (0x02 & is_numeric)
713 				cpe->szCounterName = get_counter_name(_wtoi(cpe->szCounterName));
714 
715 			if (ERROR_SUCCESS != zbx_PdhMakeCounterPath(__func__, cpe, counterPath))
716 				goto clean;
717 
718 			zabbix_log(LOG_LEVEL_DEBUG, "counter path converted to '%s'", counterPath);
719 		}
720 	}
721 
722 	ret = SUCCEED;
723 clean:
724 	zbx_free(cpe);
725 	zbx_free(wcounterPath);
726 
727 	return ret;
728 }
729