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 "perfstat.h"
23 #include "alias.h"
24 #include "log.h"
25 #include "mutexs.h"
26 #include "sysinfo.h"
27 
28 #define OBJECT_CACHE_REFRESH_INTERVAL	60
29 #define NAMES_UPDATE_INTERVAL		60
30 
31 struct object_name_ref
32 {
33 	char		*eng_name;
34 	wchar_t		*loc_name;
35 };
36 
37 typedef struct
38 {
39 	zbx_perf_counter_data_t	*pPerfCounterList;
40 	PDH_HQUERY		pdh_query;
41 	time_t			lastrefresh_objects;	/* last refresh time of object cache */
42 	time_t			lastupdate_names;	/* last update time of object names */
43 }
44 ZBX_PERF_STAT_DATA;
45 
46 static ZBX_PERF_STAT_DATA	ppsd;
47 static zbx_mutex_t		perfstat_access = ZBX_MUTEX_NULL;
48 
49 static struct object_name_ref	*object_names = NULL;
50 static int			object_num = 0;
51 
52 #define LOCK_PERFCOUNTERS	zbx_mutex_lock(perfstat_access)
53 #define UNLOCK_PERFCOUNTERS	zbx_mutex_unlock(perfstat_access)
54 
perf_collector_started(void)55 static int	perf_collector_started(void)
56 {
57 	return (NULL != ppsd.pdh_query ? SUCCEED : FAIL);
58 }
59 
60 /******************************************************************************
61  *                                                                            *
62  * Comments: counter failed or disappeared, dismiss all previous values       *
63  *                                                                            *
64  ******************************************************************************/
deactivate_perf_counter(zbx_perf_counter_data_t * counter)65 static void	deactivate_perf_counter(zbx_perf_counter_data_t *counter)
66 {
67 	zabbix_log(LOG_LEVEL_DEBUG, "deactivate_perf_counter() counterpath:'%s'", counter->counterpath);
68 
69 	counter->status = PERF_COUNTER_NOTSUPPORTED;
70 	counter->value_count = 0;
71 	counter->value_current = -1;
72 	counter->olderRawValue = 0;
73 	counter->sum = 0;
74 }
75 
76 /******************************************************************************
77  *                                                                            *
78  * Comments: if the specified counter exists or a new is successfully         *
79  *           added, a pointer to that counter is returned, NULL otherwise     *
80  *                                                                            *
81  ******************************************************************************/
add_perf_counter(const char * name,const char * counterpath,int interval,zbx_perf_counter_lang_t lang,char ** error)82 zbx_perf_counter_data_t	*add_perf_counter(const char *name, const char *counterpath, int interval,
83 		zbx_perf_counter_lang_t lang, char **error)
84 {
85 	zbx_perf_counter_data_t	*cptr = NULL;
86 	PDH_STATUS		pdh_status;
87 	int			added = FAIL;
88 
89 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() counter:'%s' interval:%d", __func__, counterpath, interval);
90 
91 	LOCK_PERFCOUNTERS;
92 
93 	if (SUCCEED != perf_collector_started())
94 	{
95 		*error = zbx_strdup(*error, "Performance collector is not started.");
96 		goto out;
97 	}
98 
99 	for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next)
100 	{
101 		/* add new parameters */
102 		if (NULL == cptr)
103 		{
104 			cptr = (zbx_perf_counter_data_t *)zbx_malloc(cptr, sizeof(zbx_perf_counter_data_t));
105 
106 			/* initialize the counter */
107 			memset(cptr, 0, sizeof(zbx_perf_counter_data_t));
108 			if (NULL != name)
109 				cptr->name = zbx_strdup(NULL, name);
110 			cptr->counterpath = zbx_strdup(NULL, counterpath);
111 			cptr->interval = interval;
112 			cptr->lang = lang;
113 			cptr->value_current = -1;
114 			cptr->value_array = (double *)zbx_malloc(cptr->value_array, sizeof(double) * interval);
115 
116 			/* add the counter to the query */
117 			pdh_status = zbx_PdhAddCounter(__func__, cptr, ppsd.pdh_query, counterpath,
118 					lang, &cptr->handle);
119 
120 			cptr->next = ppsd.pPerfCounterList;
121 			ppsd.pPerfCounterList = cptr;
122 
123 			if (ERROR_SUCCESS != pdh_status && PDH_CSTATUS_NO_INSTANCE != pdh_status)
124 			{
125 				*error = zbx_dsprintf(*error, "Invalid performance counter format.");
126 				cptr = NULL;	/* indicate a failure */
127 			}
128 
129 			added = SUCCEED;
130 			break;
131 		}
132 
133 		if (NULL != name)
134 		{
135 			if (0 == strcmp(cptr->name, name))
136 				break;
137 		}
138 		else if (0 == strcmp(cptr->counterpath, counterpath) &&
139 				cptr->interval == interval && cptr->lang == lang)
140 		{
141 			break;
142 		}
143 	}
144 
145 	if (FAIL == added)
146 	{
147 		zabbix_log(LOG_LEVEL_DEBUG, "%s() counter '%s' already exists", __func__, counterpath);
148 	}
149 	else if (NULL != name && NULL != cptr)
150 	{
151 		char	*alias_name;
152 
153 		alias_name = zbx_dsprintf(NULL, "__UserPerfCounter[%s]", name);
154 		add_alias(name, alias_name);
155 		zbx_free(alias_name);
156 	}
157 out:
158 	UNLOCK_PERFCOUNTERS;
159 
160 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s(): %s", __func__, NULL == cptr ? "FAIL" : "SUCCEED");
161 
162 	return cptr;
163 }
164 
165 /******************************************************************************
166  *                                                                            *
167  * Function: extend_perf_counter_interval                                     *
168  *                                                                            *
169  * Purpose: extends the performance counter buffer to store the new data      *
170  *          interval                                                          *
171  *                                                                            *
172  * Parameters: result    - [IN] the performance counter                       *
173  *             interval  - [IN] the new data collection interval in seconds   *
174  *                                                                            *
175  ******************************************************************************/
extend_perf_counter_interval(zbx_perf_counter_data_t * counter,int interval)176 static void	extend_perf_counter_interval(zbx_perf_counter_data_t *counter, int interval)
177 {
178 	if (interval <= counter->interval)
179 		return;
180 
181 	counter->value_array = (double *)zbx_realloc(counter->value_array, sizeof(double) * interval);
182 
183 	/* move the data to the end to keep the ring buffer intact */
184 	if (counter->value_current < counter->value_count)
185 	{
186 		int	i;
187 		double	*src, *dst;
188 
189 		src = &counter->value_array[counter->interval - 1];
190 		dst = &counter->value_array[interval - 1];
191 
192 		for (i = 0; i < counter->value_count - counter->value_current; i++)
193 			*dst-- = *src--;
194 	}
195 
196 	counter->interval = interval;
197 }
198 
free_object_names(void)199 static void	free_object_names(void)
200 {
201 	int	i;
202 
203 	for (i = 0; i < object_num; i++)
204 	{
205 		zbx_free(object_names[i].eng_name);
206 		zbx_free(object_names[i].loc_name);
207 	}
208 
209 	zbx_free(object_names);
210 	object_num = 0;
211 }
212 
213 /******************************************************************************
214  *                                                                            *
215  * Function: set_object_names                                                 *
216  *                                                                            *
217  * Purpose: obtains PDH object localized names and associates them with       *
218  *          English names, to be used by perf_instance_en.discovery           *
219  *                                                                            *
220  * Return value: SUCCEED/FAIL                                                 *
221  *                                                                            *
222  ******************************************************************************/
set_object_names(void)223 static int	set_object_names(void)
224 {
225 	wchar_t		*names_eng, *names_loc, *eng_name, *loc_name, *objects, *object, *p_eng = NULL, *p_loc = NULL;
226 	DWORD		sz = 0;
227 	PDH_STATUS	pdh_status;
228 	BOOL		refresh;
229 	int		ret = FAIL;
230 
231 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
232 
233 	LOCK_PERFCOUNTERS;
234 
235 	if (ppsd.lastupdate_names + NAMES_UPDATE_INTERVAL >= time(NULL))
236 	{
237 		ret = SUCCEED;
238 		goto out;
239 	}
240 
241 	if (ppsd.lastrefresh_objects + OBJECT_CACHE_REFRESH_INTERVAL > time(NULL))
242 		refresh = FALSE;
243 	else
244 		refresh = TRUE;
245 
246 	if (PDH_MORE_DATA != (pdh_status = PdhEnumObjects(NULL, NULL, NULL, &sz, PERF_DETAIL_WIZARD, refresh)))
247 	{
248 		zabbix_log(LOG_LEVEL_ERR, "cannot obtain required buffer size: %s",
249 				strerror_from_module(pdh_status, L"PDH.DLL"));
250 		goto out;
251 	}
252 
253 	if (TRUE == refresh)
254 		ppsd.lastrefresh_objects = time(NULL);
255 
256 	if (NULL == (p_eng = names_eng = get_all_counter_names(HKEY_PERFORMANCE_TEXT, L"Counter")) ||
257 			NULL == (p_loc = names_loc = get_all_counter_names(HKEY_PERFORMANCE_NLSTEXT, L"Counter")))
258 	{
259 		goto out;
260 	}
261 
262 	/* skip fields with number of records */
263 	names_eng += wcslen(names_eng) + 1;
264 	names_eng += wcslen(names_eng) + 1;
265 	names_loc += wcslen(names_loc) + 1;
266 	names_loc += wcslen(names_loc) + 1;
267 
268 	objects = zbx_malloc(NULL, (++sz) * sizeof(wchar_t));
269 
270 	if (ERROR_SUCCESS != (pdh_status = PdhEnumObjects(NULL, NULL, objects, &sz, PERF_DETAIL_WIZARD, FALSE)))
271 	{
272 		zabbix_log(LOG_LEVEL_ERR, "cannot obtain objects list: %s",
273 				strerror_from_module(pdh_status, L"PDH.DLL"));
274 		zbx_free(objects);
275 		goto out;
276 	}
277 
278 	free_object_names();
279 
280 	for (object = objects; L'\0' != *object; object += sz)
281 	{
282 		DWORD	idx_eng, idx_loc;
283 
284 		sz = (DWORD)wcslen(object) + 1;
285 		object_names = zbx_realloc(object_names, sizeof(struct object_name_ref) * (object_num + 1));
286 
287 		object_names[object_num].eng_name = NULL;
288 		object_names[object_num].loc_name = zbx_malloc(NULL, sizeof(wchar_t) * sz);
289 		memcpy(object_names[object_num].loc_name, object, sizeof(wchar_t) * sz);
290 
291 		/* For some objects the localized name might be missing and PdhEnumObjects() will return English    */
292 		/* name instead. In that case for localized name use name returned by PdhEnumObjects() if such name */
293 		/* exists in English names registry (HKEY_PERFORMANCE_TEXT).                                        */
294 
295 		idx_loc = 0;
296 
297 		for (loc_name = names_loc; L'\0' != *loc_name; loc_name += wcslen(loc_name) + 1)
298 		{
299 			DWORD	idx;
300 
301 			idx = (DWORD)_wtoi(loc_name);
302 			loc_name += wcslen(loc_name) + 1;
303 
304 			if (0 == wcscmp(object, loc_name))
305 			{
306 				idx_loc = idx;
307 				break;
308 			}
309 		}
310 
311 		for (eng_name = names_eng; L'\0' != *eng_name; eng_name += wcslen(eng_name) + 1)
312 		{
313 			idx_eng = (DWORD)_wtoi(eng_name);
314 			eng_name += wcslen(eng_name) + 1;
315 
316 			if (idx_loc == idx_eng ||
317 					(0 == idx_loc && 0 == wcscmp(object_names[object_num].loc_name, eng_name)))
318 			{
319 				object_names[object_num].eng_name = zbx_unicode_to_utf8(eng_name);
320 				break;
321 			}
322 		}
323 
324 		object_num++;
325 	}
326 
327 	zbx_free(objects);
328 	ppsd.lastupdate_names = time(NULL);
329 	ret = SUCCEED;
330 out:
331 	zbx_free(p_eng);
332 	zbx_free(p_loc);
333 
334 	UNLOCK_PERFCOUNTERS;
335 
336 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
337 
338 	return ret;
339 }
340 
341 /******************************************************************************
342  *                                                                            *
343  * Comments: counter is removed from the collector and                        *
344  *           the memory is freed - do not use it again                        *
345  *                                                                            *
346  ******************************************************************************/
remove_perf_counter(zbx_perf_counter_data_t * counter)347 void	remove_perf_counter(zbx_perf_counter_data_t *counter)
348 {
349 	zbx_perf_counter_data_t	*cptr;
350 
351 	LOCK_PERFCOUNTERS;
352 
353 	if (NULL == counter || NULL == ppsd.pPerfCounterList)
354 		goto out;
355 
356 	if (counter == ppsd.pPerfCounterList)
357 	{
358 		ppsd.pPerfCounterList = counter->next;
359 	}
360 	else
361 	{
362 		for (cptr = ppsd.pPerfCounterList; ; cptr = cptr->next)
363 		{
364 			if (cptr->next == counter)
365 			{
366 				cptr->next = counter->next;
367 				break;
368 			}
369 		}
370 	}
371 
372 	PdhRemoveCounter(counter->handle);
373 	zbx_free(counter->name);
374 	zbx_free(counter->counterpath);
375 	zbx_free(counter->value_array);
376 	zbx_free(counter);
377 out:
378 	UNLOCK_PERFCOUNTERS;
379 
380 }
381 
free_perf_counter_list(void)382 static void	free_perf_counter_list(void)
383 {
384 	zbx_perf_counter_data_t	*cptr;
385 
386 	while (NULL != ppsd.pPerfCounterList)
387 	{
388 		cptr = ppsd.pPerfCounterList;
389 		ppsd.pPerfCounterList = cptr->next;
390 
391 		zbx_free(cptr->name);
392 		zbx_free(cptr->counterpath);
393 		zbx_free(cptr->value_array);
394 		zbx_free(cptr);
395 	}
396 }
397 
398 /******************************************************************************
399  *                                                                            *
400  * Comments: must be called only for PERF_COUNTER_ACTIVE counters,            *
401  *           interval must be less than or equal to counter->interval         *
402  *                                                                            *
403  ******************************************************************************/
compute_average_value(zbx_perf_counter_data_t * counter,int interval)404 static double	compute_average_value(zbx_perf_counter_data_t *counter, int interval)
405 {
406 	double	sum = 0;
407 	int	i, j, count;
408 
409 	if (PERF_COUNTER_ACTIVE != counter->status || interval > counter->interval)
410 		return 0;
411 
412 	if (counter->interval == interval)
413 		return counter->sum / (double)counter->value_count;
414 
415 	/* compute the average manually for custom intervals */
416 	i = counter->value_current;
417 	count = (counter->value_count < interval ? counter->value_count : interval);
418 
419 	/* cycle backwards through the circular buffer of values */
420 	for (j = 0; j < count; j++, i = (0 < i ? i - 1 : counter->interval - 1))
421 		sum += counter->value_array[i];
422 
423 	return sum / (double)count;
424 }
425 
init_perf_collector(zbx_threadedness_t threadedness,char ** error)426 int	init_perf_collector(zbx_threadedness_t threadedness, char **error)
427 {
428 	int	ret = FAIL;
429 
430 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
431 
432 	switch (threadedness)
433 	{
434 		case ZBX_SINGLE_THREADED:
435 			break;
436 		case ZBX_MULTI_THREADED:
437 			if (SUCCEED != zbx_mutex_create(&perfstat_access, ZBX_MUTEX_PERFSTAT, error))
438 				goto out;
439 			break;
440 		default:
441 			THIS_SHOULD_NEVER_HAPPEN;
442 			*error = zbx_strdup(*error, "internal error");
443 			goto out;
444 	}
445 
446 	if (ERROR_SUCCESS != zbx_PdhOpenQuery(__func__, &ppsd.pdh_query))
447 	{
448 		*error = zbx_strdup(*error, "cannot open performance data query");
449 		goto out;
450 	}
451 
452 	ppsd.lastrefresh_objects = 0;
453 	ppsd.lastupdate_names = 0;
454 
455 	if (SUCCEED != init_builtin_counter_indexes())
456 	{
457 		*error = zbx_strdup(*error, "cannot initialize built-in counter indexes");
458 		goto out;
459 	}
460 
461 	if (SUCCEED != set_object_names())
462 	{
463 		*error = zbx_strdup(*error, "cannot initialize object names");
464 		goto out;
465 	}
466 
467 	ret = SUCCEED;
468 out:
469 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
470 
471 	return ret;
472 }
473 
free_perf_collector(void)474 void	free_perf_collector(void)
475 {
476 	zbx_perf_counter_data_t	*cptr;
477 
478 	if (SUCCEED != perf_collector_started())
479 		return;
480 
481 	for (cptr = ppsd.pPerfCounterList; cptr != NULL; cptr = cptr->next)
482 	{
483 		if (NULL != cptr->handle)
484 		{
485 			PdhRemoveCounter(cptr->handle);
486 			cptr->handle = NULL;
487 		}
488 	}
489 
490 	PdhCloseQuery(ppsd.pdh_query);
491 	ppsd.pdh_query = NULL;
492 
493 	LOCK_PERFCOUNTERS;
494 
495 	free_perf_counter_list();
496 	free_object_names();
497 
498 	UNLOCK_PERFCOUNTERS;
499 
500 	zbx_mutex_destroy(&perfstat_access);
501 }
502 
collect_perfstat(void)503 void	collect_perfstat(void)
504 {
505 	zbx_perf_counter_data_t	*cptr;
506 	PDH_STATUS		pdh_status;
507 	time_t			now;
508 	PDH_FMT_COUNTERVALUE	value;
509 
510 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
511 
512 	LOCK_PERFCOUNTERS;
513 
514 	if (SUCCEED != perf_collector_started())
515 		goto out;
516 
517 	if (NULL == ppsd.pPerfCounterList)	/* no counters */
518 		goto out;
519 
520 	now = time(NULL);
521 
522 	/* refresh unsupported counters */
523 	for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
524 	{
525 		if (PERF_COUNTER_NOTSUPPORTED != cptr->status)
526 			continue;
527 
528 		zbx_PdhAddCounter(__func__, cptr, ppsd.pdh_query, cptr->counterpath,
529 				cptr->lang, &cptr->handle);
530 	}
531 
532 	/* query for new data */
533 	if (ERROR_SUCCESS != (pdh_status = PdhCollectQueryData(ppsd.pdh_query)))
534 	{
535 		for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
536 		{
537 			if (PERF_COUNTER_NOTSUPPORTED != cptr->status)
538 				deactivate_perf_counter(cptr);
539 		}
540 
541 		zabbix_log(LOG_LEVEL_DEBUG, "%s() call to PdhCollectQueryData() failed: %s",
542 				__func__, strerror_from_module(pdh_status, L"PDH.DLL"));
543 
544 		goto out;
545 	}
546 
547 	/* get the raw values */
548 	for (cptr = ppsd.pPerfCounterList; NULL != cptr; cptr = cptr->next)
549 	{
550 		if (PERF_COUNTER_NOTSUPPORTED == cptr->status)
551 			continue;
552 
553 		if (ERROR_SUCCESS != zbx_PdhGetRawCounterValue(__func__, cptr->counterpath,
554 				cptr->handle, &cptr->rawValues[cptr->olderRawValue]))
555 		{
556 			deactivate_perf_counter(cptr);
557 			continue;
558 		}
559 
560 		cptr->olderRawValue = (cptr->olderRawValue + 1) & 1;
561 
562 		pdh_status = PdhCalculateCounterFromRawValue(cptr->handle, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100,
563 				&cptr->rawValues[(cptr->olderRawValue + 1) & 1],
564 				(PERF_COUNTER_INITIALIZED < cptr->status ?
565 						&cptr->rawValues[cptr->olderRawValue] : NULL), &value);
566 
567 		if (ERROR_SUCCESS == pdh_status && PDH_CSTATUS_VALID_DATA != value.CStatus &&
568 				PDH_CSTATUS_NEW_DATA != value.CStatus)
569 		{
570 			pdh_status = value.CStatus;
571 		}
572 
573 		if (PDH_CSTATUS_INVALID_DATA == pdh_status)
574 		{
575 			/* some (e.g., rate) counters require two raw values, MSDN lacks documentation */
576 			/* about what happens but tests show that PDH_CSTATUS_INVALID_DATA is returned */
577 
578 			cptr->status = PERF_COUNTER_GET_SECOND_VALUE;
579 			continue;
580 		}
581 
582 		/* Negative values can occur when a counter rolls over. By default, this value entry does not appear  */
583 		/* in the registry and Performance Monitor does not log data errors or notify the user that it has    */
584 		/* received bad data; More info: https://support.microsoft.com/kb/177655/EN-US                        */
585 
586 		if (PDH_CALC_NEGATIVE_DENOMINATOR == pdh_status)
587 		{
588 			zabbix_log(LOG_LEVEL_DEBUG, "PDH_CALC_NEGATIVE_DENOMINATOR error occurred in counterpath '%s'."
589 					" Value ignored", cptr->counterpath);
590 			continue;
591 		}
592 
593 		if (PDH_CALC_NEGATIVE_VALUE == pdh_status)
594 		{
595 			zabbix_log(LOG_LEVEL_DEBUG, "PDH_CALC_NEGATIVE_VALUE error occurred in counterpath '%s'."
596 					" Value ignored", cptr->counterpath);
597 			continue;
598 		}
599 
600 		if (ERROR_SUCCESS == pdh_status)
601 		{
602 			cptr->status = PERF_COUNTER_ACTIVE;
603 			cptr->value_current = (cptr->value_current + 1) % cptr->interval
604 
605 			/* remove the oldest value, value_count will not increase */;
606 			if (cptr->value_count == cptr->interval)
607 				cptr->sum -= cptr->value_array[cptr->value_current];
608 
609 			cptr->value_array[cptr->value_current] = value.doubleValue;
610 			cptr->sum += cptr->value_array[cptr->value_current];
611 			if (cptr->value_count < cptr->interval)
612 				cptr->value_count++;
613 		}
614 		else
615 		{
616 			zabbix_log(LOG_LEVEL_WARNING, "cannot calculate performance counter value \"%s\": %s",
617 					cptr->counterpath, strerror_from_module(pdh_status, L"PDH.DLL"));
618 
619 			deactivate_perf_counter(cptr);
620 		}
621 	}
622 out:
623 	UNLOCK_PERFCOUNTERS;
624 
625 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
626 }
627 
628 /******************************************************************************
629  *                                                                            *
630  * Function: get_perf_counter_value_by_name                                   *
631  *                                                                            *
632  * Purpose: gets average named performance counter value                      *
633  *                                                                            *
634  * Parameters: name  - [IN] the performance counter name                      *
635  *             value - [OUT] the calculated value                             *
636  *             error - [OUT] the error message, it is not always produced     *
637  *                     when FAIL is returned. It is a caller responsibility   *
638  *                     to check if the error message is not NULL.             *
639  *                                                                            *
640  * Returns:  SUCCEED - the value was retrieved successfully                   *
641  *           FAIL    - otherwise                                              *
642  *                                                                            *
643  * Comments: The value is retrieved from collector (if it has been requested  *
644  *           before) or directly from Windows performance counters if         *
645  *           possible.                                                        *
646  *                                                                            *
647  ******************************************************************************/
get_perf_counter_value_by_name(const char * name,double * value,char ** error)648 int	get_perf_counter_value_by_name(const char *name, double *value, char **error)
649 {
650 	int			ret = FAIL;
651 	zbx_perf_counter_data_t	*perfs = NULL;
652 	char			*counterpath = NULL;
653 
654 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() name:%s", __func__, name);
655 
656 	LOCK_PERFCOUNTERS;
657 
658 	if (SUCCEED != perf_collector_started())
659 	{
660 		*error = zbx_strdup(*error, "Performance collector is not started.");
661 		goto out;
662 	}
663 
664 	for (perfs = ppsd.pPerfCounterList; NULL != perfs; perfs = perfs->next)
665 	{
666 		if (NULL != perfs->name && 0 == strcmp(perfs->name, name))
667 		{
668 			if (PERF_COUNTER_ACTIVE != perfs->status)
669 				break;
670 
671 			/* the counter data is already being collected, return it */
672 			*value = compute_average_value(perfs, perfs->interval);
673 			ret = SUCCEED;
674 			goto out;
675 		}
676 	}
677 
678 	/* we can retrieve named counter data only if it has been registered before */
679 	if (NULL == perfs)
680 	{
681 		*error = zbx_dsprintf(*error, "Unknown performance counter name: %s.", name);
682 		goto out;
683 	}
684 
685 	counterpath = zbx_strdup(counterpath, perfs->counterpath);
686 out:
687 	UNLOCK_PERFCOUNTERS;
688 
689 	if (NULL != counterpath)
690 	{
691 		/* request counter value directly from Windows performance counters */
692 		PDH_STATUS pdh_status = calculate_counter_value(__func__, counterpath, perfs->lang, value);
693 
694 		if (PDH_NOT_IMPLEMENTED == pdh_status)
695 			*error = zbx_strdup(*error, "Counter is not supported for this Microsoft Windows version");
696 		else if (ERROR_SUCCESS == pdh_status)
697 			ret = SUCCEED;
698 
699 		zbx_free(counterpath);
700 	}
701 
702 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
703 
704 	return ret;
705 }
706 
707 /******************************************************************************
708  *                                                                            *
709  * Function: get_perf_counter_value_by_path                                   *
710  *                                                                            *
711  * Purpose: gets average performance counter value                            *
712  *                                                                            *
713  * Parameters: counterpath - [IN] the performance counter path                *
714  *             interval    - [IN] the data collection interval in seconds     *
715  *             lang        - [IN] counterpath language (default or English)   *
716  *             value       - [OUT] the calculated value                       *
717  *             error       - [OUT] the error message                          *
718  *                                                                            *
719  * Returns:  SUCCEED - the value was retrieved successfully                   *
720  *           FAIL    - otherwise                                              *
721  *                                                                            *
722  * Comments: The value is retrieved from collector (if it has been requested  *
723  *           before) or directly from Windows performance counters if         *
724  *           possible.                                                        *
725  *                                                                            *
726  ******************************************************************************/
get_perf_counter_value_by_path(const char * counterpath,int interval,zbx_perf_counter_lang_t lang,double * value,char ** error)727 int	get_perf_counter_value_by_path(const char *counterpath, int interval, zbx_perf_counter_lang_t lang,
728 		double *value, char **error)
729 {
730 	int			ret = FAIL;
731 	zbx_perf_counter_data_t	*perfs = NULL;
732 
733 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() path:%s interval:%d lang:%d", __func__, counterpath,
734 			interval, lang);
735 
736 	LOCK_PERFCOUNTERS;
737 
738 	if (SUCCEED != perf_collector_started())
739 	{
740 		*error = zbx_strdup(*error, "Performance collector is not started.");
741 		goto out;
742 	}
743 
744 	for (perfs = ppsd.pPerfCounterList; NULL != perfs; perfs = perfs->next)
745 	{
746 		if (0 == strcmp(perfs->counterpath, counterpath) && perfs->lang == lang)
747 		{
748 			if (perfs->interval < interval)
749 				extend_perf_counter_interval(perfs, interval);
750 
751 			if (PERF_COUNTER_ACTIVE != perfs->status)
752 				break;
753 
754 			/* the counter data is already being collected, return it */
755 			*value = compute_average_value(perfs, interval);
756 			ret = SUCCEED;
757 			goto out;
758 		}
759 	}
760 
761 	/* if the requested counter is not already being monitored - start monitoring */
762 	if (NULL == perfs)
763 		perfs = add_perf_counter(NULL, counterpath, interval, lang, error);
764 out:
765 	UNLOCK_PERFCOUNTERS;
766 
767 	if (SUCCEED != ret && NULL != perfs)
768 	{
769 		/* request counter value directly from Windows performance counters */
770 		if (ERROR_SUCCESS == calculate_counter_value(__func__, counterpath, lang, value))
771 			ret = SUCCEED;
772 	}
773 
774 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
775 
776 	return ret;
777 }
778 
779 /******************************************************************************
780  *                                                                            *
781  * Function: get_perf_counter_value                                           *
782  *                                                                            *
783  * Purpose: gets average value of the specified performance counter interval  *
784  *                                                                            *
785  * Parameters: counter  - [IN] the performance counter                        *
786  *             interval - [IN] the data collection interval in seconds        *
787  *             value    - [OUT] the calculated value                          *
788  *             error    - [OUT] the error message                             *
789  *                                                                            *
790  * Returns:  SUCCEED - the value was retrieved successfully                   *
791  *           FAIL    - otherwise                                              *
792  *                                                                            *
793  ******************************************************************************/
get_perf_counter_value(zbx_perf_counter_data_t * counter,int interval,double * value,char ** error)794 int	get_perf_counter_value(zbx_perf_counter_data_t *counter, int interval, double *value, char **error)
795 {
796 	int	ret = FAIL;
797 
798 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() path:%s interval:%d", __func__, counter->counterpath, interval);
799 
800 	LOCK_PERFCOUNTERS;
801 
802 	if (SUCCEED != perf_collector_started())
803 	{
804 		*error = zbx_strdup(*error, "Performance collector is not started.");
805 		goto out;
806 	}
807 
808 	if (PERF_COUNTER_ACTIVE != counter->status)
809 	{
810 		*error = zbx_strdup(*error, "Performance counter is not ready.");
811 		goto out;
812 	}
813 
814 	*value = compute_average_value(counter, interval);
815 	ret = SUCCEED;
816 out:
817 	UNLOCK_PERFCOUNTERS;
818 
819 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
820 
821 	return ret;
822 }
823 
refresh_object_cache(void)824 int	refresh_object_cache(void)
825 {
826 	DWORD	sz = 0;
827 	int	ret = SUCCEED;
828 
829 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
830 
831 	LOCK_PERFCOUNTERS;
832 
833 	if (ppsd.lastrefresh_objects + OBJECT_CACHE_REFRESH_INTERVAL < time(NULL))
834 	{
835 		if (PDH_MORE_DATA != PdhEnumObjects(NULL, NULL, NULL, &sz, PERF_DETAIL_WIZARD, TRUE))
836 		{
837 			ret = FAIL;
838 			goto out;
839 		}
840 
841 		ppsd.lastrefresh_objects = time(NULL);
842 	}
843 out:
844 	UNLOCK_PERFCOUNTERS;
845 
846 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
847 
848 	return ret;
849 }
850 
get_object_name(char * eng_name)851 static wchar_t	*get_object_name(char *eng_name)
852 {
853 	wchar_t	*loc_name = NULL;
854 	int	i;
855 	size_t	len;
856 
857 	LOCK_PERFCOUNTERS;
858 
859 	len = strlen(eng_name);
860 
861 	for (i = 0; i < object_num; i++)
862 	{
863 		if (NULL != object_names[i].eng_name && len == strlen(object_names[i].eng_name) &&
864 				0 == zbx_strncasecmp(object_names[i].eng_name, eng_name, len))
865 		{
866 			size_t	sz;
867 
868 			sz = (wcslen(object_names[i].loc_name) + 1) * sizeof(wchar_t);
869 			loc_name = zbx_malloc(NULL, sz);
870 			memcpy(loc_name, object_names[i].loc_name, sz);
871 			break;
872 		}
873 	}
874 
875 	UNLOCK_PERFCOUNTERS;
876 
877 	return loc_name;
878 }
879 
880 /******************************************************************************
881  *                                                                            *
882  * Function: get_object_name_local                                            *
883  *                                                                            *
884  * Purpose: get localized name of the object                                  *
885  *                                                                            *
886  * Parameters: eng_name - [IN] english name                                   *
887  *                                                                            *
888  * Returns:  localized name of the object                                     *
889  *                                                                            *
890  ******************************************************************************/
get_object_name_local(char * eng_name)891 wchar_t	*get_object_name_local(char *eng_name)
892 {
893 	wchar_t	*name;
894 
895 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
896 
897 	if (NULL == (name = get_object_name(eng_name)) && SUCCEED == set_object_names())
898 		name = get_object_name(eng_name);
899 
900 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
901 
902 	return name;
903 }
904