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 "module.h"
22 #include "sysinfo.h"
23 #include "log.h"
24 #include "cfg.h"
25 #include "alias.h"
26 #include "threads.h"
27 #include "sighandler.h"
28 
29 #ifdef WITH_AGENT_METRICS
30 #	include "agent/agent.h"
31 #endif
32 
33 #ifdef WITH_COMMON_METRICS
34 #	include "common/common.h"
35 #endif
36 
37 #ifdef WITH_SIMPLE_METRICS
38 #	include "simple/simple.h"
39 #endif
40 
41 #ifdef WITH_SPECIFIC_METRICS
42 #	include "specsysinfo.h"
43 #endif
44 
45 #ifdef WITH_HOSTNAME_METRIC
46 extern ZBX_METRIC      parameter_hostname;
47 #endif
48 
49 static ZBX_METRIC	*commands = NULL;
50 
51 #define ZBX_COMMAND_ERROR		0
52 #define ZBX_COMMAND_WITHOUT_PARAMS	1
53 #define ZBX_COMMAND_WITH_PARAMS		2
54 
55 /******************************************************************************
56  *                                                                            *
57  * Function: parse_command_dyn                                                *
58  *                                                                            *
59  * Purpose: parses item key and splits it into command and parameters         *
60  *                                                                            *
61  * Return value: ZBX_COMMAND_ERROR - error                                    *
62  *               ZBX_COMMAND_WITHOUT_PARAMS - command without parameters      *
63  *               ZBX_COMMAND_WITH_PARAMS - command with parameters            *
64  *                                                                            *
65  ******************************************************************************/
parse_command_dyn(const char * command,char ** cmd,char ** param)66 static int	parse_command_dyn(const char *command, char **cmd, char **param)
67 {
68 	const char	*pl, *pr;
69 	size_t		cmd_alloc = 0, param_alloc = 0,
70 			cmd_offset = 0, param_offset = 0;
71 
72 	for (pl = command; SUCCEED == is_key_char(*pl); pl++)
73 		;
74 
75 	if (pl == command)
76 		return ZBX_COMMAND_ERROR;
77 
78 	zbx_strncpy_alloc(cmd, &cmd_alloc, &cmd_offset, command, pl - command);
79 
80 	if ('\0' == *pl)	/* no parameters specified */
81 		return ZBX_COMMAND_WITHOUT_PARAMS;
82 
83 	if ('[' != *pl)		/* unsupported character */
84 		return ZBX_COMMAND_ERROR;
85 
86 	for (pr = ++pl; '\0' != *pr; pr++)
87 		;
88 
89 	if (']' != *--pr)
90 		return ZBX_COMMAND_ERROR;
91 
92 	zbx_strncpy_alloc(param, &param_alloc, &param_offset, pl, pr - pl);
93 
94 	return ZBX_COMMAND_WITH_PARAMS;
95 }
96 
97 /******************************************************************************
98  *                                                                            *
99  * Function: add_metric                                                       *
100  *                                                                            *
101  * Purpose: registers a new item key into the system                          *
102  *                                                                            *
103  ******************************************************************************/
add_metric(ZBX_METRIC * metric,char * error,size_t max_error_len)104 int	add_metric(ZBX_METRIC *metric, char *error, size_t max_error_len)
105 {
106 	int	i = 0;
107 
108 	while (NULL != commands[i].key)
109 	{
110 		if (0 == strcmp(commands[i].key, metric->key))
111 		{
112 			zbx_snprintf(error, max_error_len, "key \"%s\" already exists", metric->key);
113 			return FAIL;	/* metric already exists */
114 		}
115 		i++;
116 	}
117 
118 	commands[i].key = zbx_strdup(NULL, metric->key);
119 	commands[i].flags = metric->flags;
120 	commands[i].function = metric->function;
121 	commands[i].test_param = (NULL == metric->test_param ? NULL : zbx_strdup(NULL, metric->test_param));
122 
123 	commands = (ZBX_METRIC *)zbx_realloc(commands, (i + 2) * sizeof(ZBX_METRIC));
124 	memset(&commands[i + 1], 0, sizeof(ZBX_METRIC));
125 
126 	return SUCCEED;
127 }
128 
add_user_parameter(const char * itemkey,char * command,char * error,size_t max_error_len)129 int	add_user_parameter(const char *itemkey, char *command, char *error, size_t max_error_len)
130 {
131 	int		ret;
132 	unsigned	flags = CF_USERPARAMETER;
133 	ZBX_METRIC	metric;
134 	AGENT_REQUEST	request;
135 
136 	init_request(&request);
137 
138 	if (SUCCEED == (ret = parse_item_key(itemkey, &request)))
139 	{
140 		if (1 == get_rparams_num(&request) && 0 == strcmp("[*]", itemkey + strlen(get_rkey(&request))))
141 			flags |= CF_HAVEPARAMS;
142 		else if (0 != get_rparams_num(&request))
143 			ret = FAIL;
144 	}
145 
146 	if (SUCCEED == ret)
147 	{
148 		metric.key = get_rkey(&request);
149 		metric.flags = flags;
150 		metric.function = &EXECUTE_USER_PARAMETER;
151 		metric.test_param = command;
152 
153 		ret = add_metric(&metric, error, max_error_len);
154 	}
155 	else
156 		zbx_strlcpy(error, "syntax error", max_error_len);
157 
158 	free_request(&request);
159 
160 	return ret;
161 }
162 
init_metrics(void)163 void	init_metrics(void)
164 {
165 	int	i;
166 	char	error[MAX_STRING_LEN];
167 
168 	commands = (ZBX_METRIC *)zbx_malloc(commands, sizeof(ZBX_METRIC));
169 	commands[0].key = NULL;
170 
171 #ifdef WITH_AGENT_METRICS
172 	for (i = 0; NULL != parameters_agent[i].key; i++)
173 	{
174 		if (SUCCEED != add_metric(&parameters_agent[i], error, sizeof(error)))
175 		{
176 			zabbix_log(LOG_LEVEL_CRIT, "cannot add item key: %s", error);
177 			exit(EXIT_FAILURE);
178 		}
179 	}
180 #endif
181 
182 #ifdef WITH_COMMON_METRICS
183 	for (i = 0; NULL != parameters_common[i].key; i++)
184 	{
185 		if (SUCCEED != add_metric(&parameters_common[i], error, sizeof(error)))
186 		{
187 			zabbix_log(LOG_LEVEL_CRIT, "cannot add item key: %s", error);
188 			exit(EXIT_FAILURE);
189 		}
190 	}
191 #endif
192 
193 #ifdef WITH_SPECIFIC_METRICS
194 	for (i = 0; NULL != parameters_specific[i].key; i++)
195 	{
196 		if (SUCCEED != add_metric(&parameters_specific[i], error, sizeof(error)))
197 		{
198 			zabbix_log(LOG_LEVEL_CRIT, "cannot add item key: %s", error);
199 			exit(EXIT_FAILURE);
200 		}
201 	}
202 #endif
203 
204 #ifdef WITH_SIMPLE_METRICS
205 	for (i = 0; NULL != parameters_simple[i].key; i++)
206 	{
207 		if (SUCCEED != add_metric(&parameters_simple[i], error, sizeof(error)))
208 		{
209 			zabbix_log(LOG_LEVEL_CRIT, "cannot add item key: %s", error);
210 			exit(EXIT_FAILURE);
211 		}
212 	}
213 #endif
214 
215 #ifdef WITH_HOSTNAME_METRIC
216 	if (SUCCEED != add_metric(&parameter_hostname, error, sizeof(error)))
217 	{
218 		zabbix_log(LOG_LEVEL_CRIT, "cannot add item key: %s", error);
219 		exit(EXIT_FAILURE);
220 	}
221 #endif
222 }
223 
free_metrics(void)224 void	free_metrics(void)
225 {
226 	if (NULL != commands)
227 	{
228 		int	i;
229 
230 		for (i = 0; NULL != commands[i].key; i++)
231 		{
232 			zbx_free(commands[i].key);
233 			zbx_free(commands[i].test_param);
234 		}
235 
236 		zbx_free(commands);
237 	}
238 }
239 
zbx_log_init(zbx_log_t * log)240 static void	zbx_log_init(zbx_log_t *log)
241 {
242 	log->value = NULL;
243 	log->source = NULL;
244 	log->timestamp = 0;
245 	log->severity = 0;
246 	log->logeventid = 0;
247 }
248 
init_result(AGENT_RESULT * result)249 void	init_result(AGENT_RESULT *result)
250 {
251 	memset(result, 0, sizeof(AGENT_RESULT));
252 }
253 
zbx_log_clean(zbx_log_t * log)254 static void	zbx_log_clean(zbx_log_t *log)
255 {
256 	zbx_free(log->source);
257 	zbx_free(log->value);
258 }
259 
zbx_log_free(zbx_log_t * log)260 void	zbx_log_free(zbx_log_t *log)
261 {
262 	zbx_log_clean(log);
263 	zbx_free(log);
264 }
265 
free_result(AGENT_RESULT * result)266 void	free_result(AGENT_RESULT *result)
267 {
268 	UNSET_UI64_RESULT(result);
269 	UNSET_DBL_RESULT(result);
270 	UNSET_STR_RESULT(result);
271 	UNSET_TEXT_RESULT(result);
272 	UNSET_LOG_RESULT(result);
273 	UNSET_MSG_RESULT(result);
274 }
275 
276 /******************************************************************************
277  *                                                                            *
278  * Function: init_request                                                     *
279  *                                                                            *
280  * Purpose: initialize the request structure                                  *
281  *                                                                            *
282  * Parameters: request - pointer to the structure                             *
283  *                                                                            *
284  ******************************************************************************/
init_request(AGENT_REQUEST * request)285 void	init_request(AGENT_REQUEST *request)
286 {
287 	request->key = NULL;
288 	request->nparam = 0;
289 	request->params = NULL;
290 	request->types = NULL;
291 	request->lastlogsize = 0;
292 	request->mtime = 0;
293 }
294 
295 /******************************************************************************
296  *                                                                            *
297  * Function: free_request_params                                              *
298  *                                                                            *
299  * Purpose: free memory used by the request parameters                        *
300  *                                                                            *
301  * Parameters: request - pointer to the request structure                     *
302  *                                                                            *
303  ******************************************************************************/
free_request_params(AGENT_REQUEST * request)304 static void	free_request_params(AGENT_REQUEST *request)
305 {
306 	int	i;
307 
308 	for (i = 0; i < request->nparam; i++)
309 		zbx_free(request->params[i]);
310 	zbx_free(request->params);
311 	zbx_free(request->types);
312 
313 	request->nparam = 0;
314 }
315 
316 /******************************************************************************
317  *                                                                            *
318  * Function: free_request                                                     *
319  *                                                                            *
320  * Purpose: free memory used by the request                                   *
321  *                                                                            *
322  * Parameters: request - pointer to the request structure                     *
323  *                                                                            *
324  ******************************************************************************/
free_request(AGENT_REQUEST * request)325 void	free_request(AGENT_REQUEST *request)
326 {
327 	zbx_free(request->key);
328 	free_request_params(request);
329 }
330 
331 /******************************************************************************
332  *                                                                            *
333  * Function: add_request_param                                                *
334  *                                                                            *
335  * Purpose: add a new parameter                                               *
336  *                                                                            *
337  * Parameters: request - [OUT] pointer to the request structure               *
338  *             pvalue  - [IN]  parameter value string                         *
339  *             type    - [IN]  parameter type                                 *
340  *                                                                            *
341  ******************************************************************************/
add_request_param(AGENT_REQUEST * request,char * pvalue,zbx_request_parameter_type_t type)342 static void	add_request_param(AGENT_REQUEST *request, char *pvalue, zbx_request_parameter_type_t type)
343 {
344 	request->nparam++;
345 	request->params = (char **)zbx_realloc(request->params, request->nparam * sizeof(char *));
346 	request->params[request->nparam - 1] = pvalue;
347 	request->types = (zbx_request_parameter_type_t*)zbx_realloc(request->types,
348 			request->nparam * sizeof(zbx_request_parameter_type_t));
349 	request->types[request->nparam - 1] = type;
350 }
351 
352 /******************************************************************************
353  *                                                                            *
354  * Function: parse_item_key                                                   *
355  *                                                                            *
356  * Purpose: parse item command (key) and fill AGENT_REQUEST structure         *
357  *                                                                            *
358  * Parameters: itemkey - complete item key                                    *
359  *                                                                            *
360  * Return value: request - structure filled with data from item key           *
361  *                                                                            *
362  ******************************************************************************/
parse_item_key(const char * itemkey,AGENT_REQUEST * request)363 int	parse_item_key(const char *itemkey, AGENT_REQUEST *request)
364 {
365 	int	i, ret = FAIL;
366 	char	*key = NULL, *params = NULL;
367 
368 	switch (parse_command_dyn(itemkey, &key, &params))
369 	{
370 		case ZBX_COMMAND_WITH_PARAMS:
371 			if (0 == (request->nparam = num_param(params)))
372 				goto out;	/* key is badly formatted */
373 
374 			request->params = (char **)zbx_malloc(request->params, request->nparam * sizeof(char *));
375 			request->types = (zbx_request_parameter_type_t*)zbx_malloc(request->types,
376 					request->nparam * sizeof(zbx_request_parameter_type_t));
377 
378 			for (i = 0; i < request->nparam; i++)
379 				request->params[i] = get_param_dyn(params, i + 1, &request->types[i]);
380 			break;
381 		case ZBX_COMMAND_ERROR:
382 			goto out;	/* key is badly formatted */
383 	}
384 
385 	request->key = key;
386 	key = NULL;
387 
388 	ret = SUCCEED;
389 out:
390 	zbx_free(params);
391 	zbx_free(key);
392 
393 	return ret;
394 }
395 
test_parameter(const char * key)396 void	test_parameter(const char *key)
397 {
398 #define ZBX_KEY_COLUMN_WIDTH	45
399 
400 	AGENT_RESULT	result;
401 
402 	printf("%-*s", ZBX_KEY_COLUMN_WIDTH, key);
403 
404 	init_result(&result);
405 
406 	if (SUCCEED == process(key, PROCESS_WITH_ALIAS, &result))
407 	{
408 		if (0 != ISSET_UI64(&result))
409 			printf(" [u|" ZBX_FS_UI64 "]", result.ui64);
410 
411 		if (0 != ISSET_DBL(&result))
412 			printf(" [d|" ZBX_FS_DBL "]", result.dbl);
413 
414 		if (0 != ISSET_STR(&result))
415 			printf(" [s|%s]", result.str);
416 
417 		if (0 != ISSET_TEXT(&result))
418 			printf(" [t|%s]", result.text);
419 
420 		if (0 != ISSET_MSG(&result))
421 			printf(" [m|%s]", result.msg);
422 	}
423 	else
424 	{
425 		if (0 != ISSET_MSG(&result))
426 			printf(" [m|" ZBX_NOTSUPPORTED "] [%s]", result.msg);
427 		else
428 			printf(" [m|" ZBX_NOTSUPPORTED "]");
429 	}
430 
431 	free_result(&result);
432 
433 	printf("\n");
434 
435 	fflush(stdout);
436 
437 #undef ZBX_KEY_COLUMN_WIDTH
438 }
439 
test_parameters(void)440 void	test_parameters(void)
441 {
442 	int	i;
443 	char	*key = NULL;
444 	size_t	key_alloc = 0;
445 
446 	for (i = 0; NULL != commands[i].key; i++)
447 	{
448 		if (0 != strcmp(commands[i].key, "__UserPerfCounter"))
449 		{
450 			size_t	key_offset = 0;
451 
452 			zbx_strcpy_alloc(&key, &key_alloc, &key_offset, commands[i].key);
453 
454 			if (0 == (commands[i].flags & CF_USERPARAMETER) && NULL != commands[i].test_param)
455 			{
456 				zbx_chrcpy_alloc(&key, &key_alloc, &key_offset, '[');
457 				zbx_strcpy_alloc(&key, &key_alloc, &key_offset, commands[i].test_param);
458 				zbx_chrcpy_alloc(&key, &key_alloc, &key_offset, ']');
459 			}
460 
461 			test_parameter(key);
462 		}
463 	}
464 
465 	zbx_free(key);
466 
467 	test_aliases();
468 }
469 
zbx_check_user_parameter(const char * param,char * error,int max_error_len)470 static int	zbx_check_user_parameter(const char *param, char *error, int max_error_len)
471 {
472 	const char	suppressed_chars[] = "\\'\"`*?[]{}~$!&;()<>|#@\n", *c;
473 	char		*buf = NULL;
474 	size_t		buf_alloc = 128, buf_offset = 0;
475 
476 	if (0 != CONFIG_UNSAFE_USER_PARAMETERS)
477 		return SUCCEED;
478 
479 	for (c = suppressed_chars; '\0' != *c; c++)
480 	{
481 		if (NULL == strchr(param, *c))
482 			continue;
483 
484 		buf = (char *)zbx_malloc(buf, buf_alloc);
485 
486 		for (c = suppressed_chars; '\0' != *c; c++)
487 		{
488 			if (c != suppressed_chars)
489 				zbx_strcpy_alloc(&buf, &buf_alloc, &buf_offset, ", ");
490 
491 			if (0 != isprint(*c))
492 				zbx_chrcpy_alloc(&buf, &buf_alloc, &buf_offset, *c);
493 			else
494 				zbx_snprintf_alloc(&buf, &buf_alloc, &buf_offset, "0x%02x", (unsigned int)(*c));
495 		}
496 
497 		zbx_snprintf(error, max_error_len, "Special characters \"%s\" are not allowed in the parameters.", buf);
498 
499 		zbx_free(buf);
500 
501 		return FAIL;
502 	}
503 
504 	return SUCCEED;
505 }
506 
replace_param(const char * cmd,const AGENT_REQUEST * request,char ** out,char * error,int max_error_len)507 static int	replace_param(const char *cmd, const AGENT_REQUEST *request, char **out, char *error, int max_error_len)
508 {
509 	const char	*pl = cmd, *pr, *tmp;
510 	size_t		out_alloc = 0, out_offset = 0;
511 	int		num, ret = SUCCEED;
512 
513 	while (NULL != (pr = strchr(pl, '$')))
514 	{
515 		zbx_strncpy_alloc(out, &out_alloc, &out_offset, pl, pr - pl);
516 
517 		/* check if increasing pointer by 1 will not result in buffer overrun */
518 		if ('\0' != pr[1])
519 			pr++;
520 
521 		if ('0' == *pr)
522 		{
523 			zbx_strcpy_alloc(out, &out_alloc, &out_offset, cmd);
524 		}
525 		else if ('1' <= *pr && *pr <= '9')
526 		{
527 			num = (int)(*pr - '0');
528 
529 			if (request->nparam >= num)
530 			{
531 				tmp = get_rparam(request, num - 1);
532 
533 				if (SUCCEED != (ret = zbx_check_user_parameter(tmp, error, max_error_len)))
534 					break;
535 
536 				zbx_strcpy_alloc(out, &out_alloc, &out_offset, tmp);
537 			}
538 		}
539 		else
540 		{
541 			if ('$' != *pr)
542 				zbx_chrcpy_alloc(out, &out_alloc, &out_offset, '$');
543 			zbx_chrcpy_alloc(out, &out_alloc, &out_offset, *pr);
544 		}
545 
546 		pl = pr + 1;
547 	}
548 
549 	if (SUCCEED == ret)
550 		zbx_strcpy_alloc(out, &out_alloc, &out_offset, pl);
551 	else
552 		zbx_free(*out);
553 
554 	return ret;
555 }
556 
557 /******************************************************************************
558  *                                                                            *
559  * Function: process                                                          *
560  *                                                                            *
561  * Purpose: execute agent check                                               *
562  *                                                                            *
563  * Parameters: in_command - item key                                          *
564  *             flags - PROCESS_LOCAL_COMMAND, allow execution of system.run   *
565  *                     PROCESS_MODULE_COMMAND, execute item from a module     *
566  *                     PROCESS_WITH_ALIAS, substitute agent Alias             *
567  *                                                                            *
568  * Return value: SUCCEED - successful execution                               *
569  *               NOTSUPPORTED - item key is not supported or other error      *
570  *               result - contains item value or error message                *
571  *                                                                            *
572  ******************************************************************************/
process(const char * in_command,unsigned flags,AGENT_RESULT * result)573 int	process(const char *in_command, unsigned flags, AGENT_RESULT *result)
574 {
575 	int		ret = NOTSUPPORTED;
576 	ZBX_METRIC	*command = NULL;
577 	AGENT_REQUEST	request;
578 
579 	init_request(&request);
580 
581 	if (SUCCEED != parse_item_key((0 == (flags & PROCESS_WITH_ALIAS) ? in_command : zbx_alias_get(in_command)),
582 			&request))
583 	{
584 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid item key format."));
585 		goto notsupported;
586 	}
587 
588 	/* system.run is not allowed by default except for getting hostname for daemons */
589 	if (1 != CONFIG_ENABLE_REMOTE_COMMANDS && 0 == (flags & PROCESS_LOCAL_COMMAND) &&
590 			0 == strcmp(request.key, "system.run"))
591 	{
592 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Remote commands are not enabled."));
593 		goto notsupported;
594 	}
595 
596 	for (command = commands; NULL != command->key; command++)
597 	{
598 		if (0 == strcmp(command->key, request.key))
599 			break;
600 	}
601 
602 	/* item key not found */
603 	if (NULL == command->key)
604 	{
605 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Unsupported item key."));
606 		goto notsupported;
607 	}
608 
609 	/* expected item from a module */
610 	if (0 != (flags & PROCESS_MODULE_COMMAND) && 0 == (command->flags & CF_MODULE))
611 	{
612 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Unsupported item key."));
613 		goto notsupported;
614 	}
615 
616 	/* command does not accept parameters but was called with parameters */
617 	if (0 == (command->flags & CF_HAVEPARAMS) && 0 != request.nparam)
618 	{
619 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Item does not allow parameters."));
620 		goto notsupported;
621 	}
622 
623 	if (0 != (command->flags & CF_USERPARAMETER))
624 	{
625 		if (0 != (command->flags & CF_HAVEPARAMS))
626 		{
627 			char	*parameters = NULL, error[MAX_STRING_LEN];
628 
629 			if (FAIL == replace_param(command->test_param, &request, &parameters, error, sizeof(error)))
630 			{
631 				SET_MSG_RESULT(result, zbx_strdup(NULL, error));
632 				goto notsupported;
633 			}
634 
635 			free_request_params(&request);
636 			add_request_param(&request, parameters, REQUEST_PARAMETER_TYPE_STRING);
637 		}
638 		else
639 		{
640 			free_request_params(&request);
641 			add_request_param(&request, zbx_strdup(NULL, command->test_param),
642 					REQUEST_PARAMETER_TYPE_STRING);
643 		}
644 	}
645 
646 	if (SYSINFO_RET_OK != command->function(&request, result))
647 	{
648 		/* "return NOTSUPPORTED;" would be more appropriate here for preserving original error */
649 		/* message in "result" but would break things relying on ZBX_NOTSUPPORTED message. */
650 		if (0 != (command->flags & CF_MODULE) && 0 == ISSET_MSG(result))
651 			SET_MSG_RESULT(result, zbx_strdup(NULL, ZBX_NOTSUPPORTED_MSG));
652 
653 		goto notsupported;
654 	}
655 
656 	ret = SUCCEED;
657 
658 notsupported:
659 	free_request(&request);
660 
661 	return ret;
662 }
663 
add_log_result(AGENT_RESULT * result,const char * value)664 static void	add_log_result(AGENT_RESULT *result, const char *value)
665 {
666 	result->log = (zbx_log_t *)zbx_malloc(result->log, sizeof(zbx_log_t));
667 
668 	zbx_log_init(result->log);
669 
670 	result->log->value = zbx_strdup(result->log->value, value);
671 	result->type |= AR_LOG;
672 }
673 
set_result_type(AGENT_RESULT * result,int value_type,char * c)674 int	set_result_type(AGENT_RESULT *result, int value_type, char *c)
675 {
676 	zbx_uint64_t	value_uint64;
677 	int		ret = FAIL;
678 
679 	assert(result);
680 
681 	switch (value_type)
682 	{
683 		double	dbl_tmp;
684 
685 		case ITEM_VALUE_TYPE_UINT64:
686 			zbx_trim_integer(c);
687 			del_zeros(c);
688 
689 			if (SUCCEED == is_uint64(c, &value_uint64))
690 			{
691 				SET_UI64_RESULT(result, value_uint64);
692 				ret = SUCCEED;
693 			}
694 			break;
695 		case ITEM_VALUE_TYPE_FLOAT:
696 			zbx_trim_float(c);
697 
698 			if (SUCCEED == is_double(c, &dbl_tmp))
699 			{
700 				SET_DBL_RESULT(result, dbl_tmp);
701 				ret = SUCCEED;
702 			}
703 			break;
704 		case ITEM_VALUE_TYPE_STR:
705 			zbx_replace_invalid_utf8(c);
706 			SET_STR_RESULT(result, zbx_strdup(NULL, c));
707 			ret = SUCCEED;
708 			break;
709 		case ITEM_VALUE_TYPE_TEXT:
710 			zbx_replace_invalid_utf8(c);
711 			SET_TEXT_RESULT(result, zbx_strdup(NULL, c));
712 			ret = SUCCEED;
713 			break;
714 		case ITEM_VALUE_TYPE_LOG:
715 			zbx_replace_invalid_utf8(c);
716 			add_log_result(result, c);
717 			ret = SUCCEED;
718 			break;
719 	}
720 
721 	return ret;
722 }
723 
set_result_meta(AGENT_RESULT * result,zbx_uint64_t lastlogsize,int mtime)724 void	set_result_meta(AGENT_RESULT *result, zbx_uint64_t lastlogsize, int mtime)
725 {
726 	result->lastlogsize = lastlogsize;
727 	result->mtime = mtime;
728 	result->type |= AR_META;
729 }
730 
get_result_ui64_value(AGENT_RESULT * result)731 static zbx_uint64_t	*get_result_ui64_value(AGENT_RESULT *result)
732 {
733 	zbx_uint64_t	value;
734 
735 	assert(result);
736 
737 	if (0 != ISSET_UI64(result))
738 	{
739 		/* nothing to do */
740 	}
741 	else if (0 != ISSET_DBL(result))
742 	{
743 		SET_UI64_RESULT(result, result->dbl);
744 	}
745 	else if (0 != ISSET_STR(result))
746 	{
747 		zbx_trim_integer(result->str);
748 		del_zeros(result->str);
749 
750 		if (SUCCEED != is_uint64(result->str, &value))
751 			return NULL;
752 
753 		SET_UI64_RESULT(result, value);
754 	}
755 	else if (0 != ISSET_TEXT(result))
756 	{
757 		zbx_trim_integer(result->text);
758 		del_zeros(result->text);
759 
760 		if (SUCCEED != is_uint64(result->text, &value))
761 			return NULL;
762 
763 		SET_UI64_RESULT(result, value);
764 	}
765 	/* skip AR_MESSAGE - it is information field */
766 
767 	if (0 != ISSET_UI64(result))
768 		return &result->ui64;
769 
770 	return NULL;
771 }
772 
get_result_dbl_value(AGENT_RESULT * result)773 static double	*get_result_dbl_value(AGENT_RESULT *result)
774 {
775 	double	value;
776 
777 	assert(result);
778 
779 	if (0 != ISSET_DBL(result))
780 	{
781 		/* nothing to do */
782 	}
783 	else if (0 != ISSET_UI64(result))
784 	{
785 		SET_DBL_RESULT(result, result->ui64);
786 	}
787 	else if (0 != ISSET_STR(result))
788 	{
789 		zbx_trim_float(result->str);
790 
791 		if (SUCCEED != is_double(result->str, &value))
792 			return NULL;
793 
794 		SET_DBL_RESULT(result, value);
795 	}
796 	else if (0 != ISSET_TEXT(result))
797 	{
798 		zbx_trim_float(result->text);
799 
800 		if (SUCCEED != is_double(result->text, &value))
801 			return NULL;
802 
803 		SET_DBL_RESULT(result, value);
804 	}
805 	/* skip AR_MESSAGE - it is information field */
806 
807 	if (0 != ISSET_DBL(result))
808 		return &result->dbl;
809 
810 	return NULL;
811 }
812 
get_result_str_value(AGENT_RESULT * result)813 static char	**get_result_str_value(AGENT_RESULT *result)
814 {
815 	char	*p, tmp;
816 
817 	assert(result);
818 
819 	if (0 != ISSET_STR(result))
820 	{
821 		/* nothing to do */
822 	}
823 	else if (0 != ISSET_TEXT(result))
824 	{
825 		/* NOTE: copy only line */
826 		for (p = result->text; '\0' != *p && '\r' != *p && '\n' != *p; p++);
827 		tmp = *p; /* remember result->text character */
828 		*p = '\0'; /* replace to NUL */
829 		SET_STR_RESULT(result, zbx_strdup(NULL, result->text)); /* copy line */
830 		*p = tmp; /* restore result->text character */
831 	}
832 	else if (0 != ISSET_UI64(result))
833 	{
834 		SET_STR_RESULT(result, zbx_dsprintf(NULL, ZBX_FS_UI64, result->ui64));
835 	}
836 	else if (0 != ISSET_DBL(result))
837 	{
838 		SET_STR_RESULT(result, zbx_dsprintf(NULL, ZBX_FS_DBL, result->dbl));
839 	}
840 	/* skip AR_MESSAGE - it is information field */
841 
842 	if (0 != ISSET_STR(result))
843 		return &result->str;
844 
845 	return NULL;
846 }
847 
get_result_text_value(AGENT_RESULT * result)848 static char	**get_result_text_value(AGENT_RESULT *result)
849 {
850 	assert(result);
851 
852 	if (0 != ISSET_TEXT(result))
853 	{
854 		/* nothing to do */
855 	}
856 	else if (0 != ISSET_STR(result))
857 	{
858 		SET_TEXT_RESULT(result, zbx_strdup(NULL, result->str));
859 	}
860 	else if (0 != ISSET_UI64(result))
861 	{
862 		SET_TEXT_RESULT(result, zbx_dsprintf(NULL, ZBX_FS_UI64, result->ui64));
863 	}
864 	else if (0 != ISSET_DBL(result))
865 	{
866 		SET_TEXT_RESULT(result, zbx_dsprintf(NULL, ZBX_FS_DBL, result->dbl));
867 	}
868 	/* skip AR_MESSAGE - it is information field */
869 
870 	if (0 != ISSET_TEXT(result))
871 		return &result->text;
872 
873 	return NULL;
874 }
875 
get_result_log_value(AGENT_RESULT * result)876 static zbx_log_t	*get_result_log_value(AGENT_RESULT *result)
877 {
878 	if (0 != ISSET_LOG(result))
879 		return result->log;
880 
881 	if (0 != ISSET_VALUE(result))
882 	{
883 		result->log = (zbx_log_t *)zbx_malloc(result->log, sizeof(zbx_log_t));
884 
885 		zbx_log_init(result->log);
886 
887 		if (0 != ISSET_STR(result))
888 			result->log->value = zbx_strdup(result->log->value, result->str);
889 		else if (0 != ISSET_TEXT(result))
890 			result->log->value = zbx_strdup(result->log->value, result->text);
891 		else if (0 != ISSET_UI64(result))
892 			result->log->value = zbx_dsprintf(result->log->value, ZBX_FS_UI64, result->ui64);
893 		else if (0 != ISSET_DBL(result))
894 			result->log->value = zbx_dsprintf(result->log->value, ZBX_FS_DBL, result->dbl);
895 
896 		result->type |= AR_LOG;
897 
898 		return result->log;
899 	}
900 
901 	return NULL;
902 }
903 
904 /******************************************************************************
905  *                                                                            *
906  * Function: get_result_value_by_type                                         *
907  *                                                                            *
908  * Purpose: return value of result in special type                            *
909  *          if value missing, convert existing value to requested type        *
910  *                                                                            *
911  * Return value:                                                              *
912  *         NULL - if value is missing or can't be converted                   *
913  *                                                                            *
914  * Author: Eugene Grigorjev                                                   *
915  *                                                                            *
916  * Comments:  better use definitions                                          *
917  *                GET_UI64_RESULT                                             *
918  *                GET_DBL_RESULT                                              *
919  *                GET_STR_RESULT                                              *
920  *                GET_TEXT_RESULT                                             *
921  *                GET_LOG_RESULT                                              *
922  *                GET_MSG_RESULT                                              *
923  *                                                                            *
924  *    AR_MESSAGE - skipped in conversion                                      *
925  *                                                                            *
926  ******************************************************************************/
get_result_value_by_type(AGENT_RESULT * result,int require_type)927 void	*get_result_value_by_type(AGENT_RESULT *result, int require_type)
928 {
929 	assert(result);
930 
931 	switch (require_type)
932 	{
933 		case AR_UINT64:
934 			return (void *)get_result_ui64_value(result);
935 		case AR_DOUBLE:
936 			return (void *)get_result_dbl_value(result);
937 		case AR_STRING:
938 			return (void *)get_result_str_value(result);
939 		case AR_TEXT:
940 			return (void *)get_result_text_value(result);
941 		case AR_LOG:
942 			return (void *)get_result_log_value(result);
943 		case AR_MESSAGE:
944 			if (0 != ISSET_MSG(result))
945 				return (void *)(&result->msg);
946 			break;
947 		default:
948 			break;
949 	}
950 
951 	return NULL;
952 }
953 
954 /******************************************************************************
955  *                                                                            *
956  * Function: unquote_key_param                                                *
957  *                                                                            *
958  * Purpose: unquotes special symbols in item key parameter                    *
959  *                                                                            *
960  * Parameters: param - [IN/OUT] item key parameter                            *
961  *                                                                            *
962  * Comments:                                                                  *
963  *   "param"     => param                                                     *
964  *   "\"param\"" => "param"                                                   *
965  *                                                                            *
966  ******************************************************************************/
unquote_key_param(char * param)967 void	unquote_key_param(char *param)
968 {
969 	char	*dst;
970 
971 	if ('"' != *param)
972 		return;
973 
974 	for (dst = param++; '\0' != *param; param++)
975 	{
976 		if ('\\' == *param && '"' == param[1])
977 			continue;
978 
979 		*dst++ = *param;
980 	}
981 	*--dst = '\0';
982 }
983 
984 /******************************************************************************
985  *                                                                            *
986  * Function: quote_key_param                                                  *
987  *                                                                            *
988  * Purpose: quotes special symbols in item key parameter                      *
989  *                                                                            *
990  * Parameters: param   - [IN/OUT] item key parameter                          *
991  *             forced  - [IN] 1 - enclose parameter in " even if it does not  *
992  *                                contain any special characters              *
993  *                            0 - do nothing if the parameter does not        *
994  *                                contain any special characters              *
995  *                                                                            *
996  * Return value: SUCCEED - if parameter was successfully quoted or quoting    *
997  *                         was not necessary                                  *
998  *               FAIL    - if parameter needs to but cannot be quoted due to  *
999  *                         backslash in the end                               *
1000  *                                                                            *
1001  ******************************************************************************/
quote_key_param(char ** param,int forced)1002 int	quote_key_param(char **param, int forced)
1003 {
1004 	size_t	sz_src, sz_dst;
1005 
1006 	if (0 == forced)
1007 	{
1008 		if ('"' != **param && ' ' != **param && '[' != **param && NULL == strchr(*param, ',') &&
1009 				NULL == strchr(*param, ']'))
1010 		{
1011 			return SUCCEED;
1012 		}
1013 	}
1014 
1015 	if (0 != (sz_src = strlen(*param)) && '\\' == (*param)[sz_src - 1])
1016 		return FAIL;
1017 
1018 	sz_dst = zbx_get_escape_string_len(*param, "\"") + 3;
1019 
1020 	*param = (char *)zbx_realloc(*param, sz_dst);
1021 
1022 	(*param)[--sz_dst] = '\0';
1023 	(*param)[--sz_dst] = '"';
1024 
1025 	while (0 < sz_src)
1026 	{
1027 		(*param)[--sz_dst] = (*param)[--sz_src];
1028 		if ('"' == (*param)[sz_src])
1029 			(*param)[--sz_dst] = '\\';
1030 	}
1031 	(*param)[--sz_dst] = '"';
1032 
1033 	return SUCCEED;
1034 }
1035 
1036 #ifdef HAVE_KSTAT_H
get_kstat_numeric_value(const kstat_named_t * kn)1037 zbx_uint64_t	get_kstat_numeric_value(const kstat_named_t *kn)
1038 {
1039 	switch (kn->data_type)
1040 	{
1041 		case KSTAT_DATA_INT32:
1042 			return kn->value.i32;
1043 		case KSTAT_DATA_UINT32:
1044 			return kn->value.ui32;
1045 		case KSTAT_DATA_INT64:
1046 			return kn->value.i64;
1047 		case KSTAT_DATA_UINT64:
1048 			return kn->value.ui64;
1049 		default:
1050 			THIS_SHOULD_NEVER_HAPPEN;
1051 			return 0;
1052 	}
1053 }
1054 #endif
1055 
1056 #ifndef _WINDOWS
1057 /******************************************************************************
1058  *                                                                            *
1059  * Function: serialize_agent_result                                           *
1060  *                                                                            *
1061  * Purpose: serialize agent result to transfer over pipe/socket               *
1062  *                                                                            *
1063  * Parameters: data        - [IN/OUT] the data buffer                         *
1064  *             data_alloc  - [IN/OUT] the data buffer allocated size          *
1065  *             data_offset - [IN/OUT] the data buffer data size               *
1066  *             agent_ret   - [IN] the agent result return code                *
1067  *             result      - [IN] the agent result                            *
1068  *                                                                            *
1069  * Comments: The agent result is serialized as [rc][type][data] where:        *
1070  *             [rc] the agent result return code, 4 bytes                     *
1071  *             [type] the agent result data type, 1 byte                      *
1072  *             [data] the agent result data, null terminated string (optional)*
1073  *                                                                            *
1074  ******************************************************************************/
serialize_agent_result(char ** data,size_t * data_alloc,size_t * data_offset,int agent_ret,AGENT_RESULT * result)1075 static void	serialize_agent_result(char **data, size_t *data_alloc, size_t *data_offset, int agent_ret,
1076 		AGENT_RESULT *result)
1077 {
1078 	char	**pvalue, result_type;
1079 	size_t	value_len;
1080 
1081 	if (SYSINFO_RET_OK == agent_ret)
1082 	{
1083 		if (ISSET_TEXT(result))
1084 			result_type = 't';
1085 		else if (ISSET_STR(result))
1086 			result_type = 's';
1087 		else if (ISSET_UI64(result))
1088 			result_type = 'u';
1089 		else if (ISSET_DBL(result))
1090 			result_type = 'd';
1091 		else if (ISSET_MSG(result))
1092 			result_type = 'm';
1093 		else
1094 			result_type = '-';
1095 	}
1096 	else
1097 		result_type = 'm';
1098 
1099 	switch (result_type)
1100 	{
1101 		case 't':
1102 		case 's':
1103 		case 'u':
1104 		case 'd':
1105 			pvalue = GET_TEXT_RESULT(result);
1106 			break;
1107 		case 'm':
1108 			pvalue = GET_MSG_RESULT(result);
1109 			break;
1110 		default:
1111 			pvalue = NULL;
1112 	}
1113 
1114 	if (NULL != pvalue)
1115 	{
1116 		value_len = strlen(*pvalue) + 1;
1117 	}
1118 	else
1119 	{
1120 		value_len = 0;
1121 		result_type = '-';
1122 	}
1123 
1124 	if (*data_alloc - *data_offset < value_len + 1 + sizeof(int))
1125 	{
1126 		while (*data_alloc - *data_offset < value_len + 1 + sizeof(int))
1127 			*data_alloc *= 1.5;
1128 
1129 		*data = (char *)zbx_realloc(*data, *data_alloc);
1130 	}
1131 
1132 	memcpy(*data + *data_offset, &agent_ret, sizeof(int));
1133 	*data_offset += sizeof(int);
1134 
1135 	(*data)[(*data_offset)++] = result_type;
1136 
1137 	if ('-' != result_type)
1138 	{
1139 		memcpy(*data + *data_offset, *pvalue, value_len);
1140 		*data_offset += value_len;
1141 	}
1142 }
1143 
1144 /******************************************************************************
1145  *                                                                            *
1146  * Function: deserialize_agent_result                                         *
1147  *                                                                            *
1148  * Purpose: deserialize agent result                                          *
1149  *                                                                            *
1150  * Parameters: data        - [IN] the data to deserialize                     *
1151  *             result      - [OUT] the agent result                           *
1152  *                                                                            *
1153  * Return value: the agent result return code (SYSINFO_RET_*)                 *
1154  *                                                                            *
1155  ******************************************************************************/
deserialize_agent_result(char * data,AGENT_RESULT * result)1156 static int	deserialize_agent_result(char *data, AGENT_RESULT *result)
1157 {
1158 	int	ret, agent_ret;
1159 	char	type;
1160 
1161 	memcpy(&agent_ret, data, sizeof(int));
1162 	data += sizeof(int);
1163 
1164 	type = *data++;
1165 
1166 	if ('m' == type || 0 == strcmp(data, ZBX_NOTSUPPORTED))
1167 	{
1168 		SET_MSG_RESULT(result, zbx_strdup(NULL, data));
1169 		return agent_ret;
1170 	}
1171 
1172 	switch (type)
1173 	{
1174 		case 't':
1175 			ret = set_result_type(result, ITEM_VALUE_TYPE_TEXT, data);
1176 			break;
1177 		case 's':
1178 			ret = set_result_type(result, ITEM_VALUE_TYPE_STR, data);
1179 			break;
1180 		case 'u':
1181 			ret = set_result_type(result, ITEM_VALUE_TYPE_UINT64, data);
1182 			break;
1183 		case 'd':
1184 			ret = set_result_type(result, ITEM_VALUE_TYPE_FLOAT, data);
1185 			break;
1186 		default:
1187 			ret = SUCCEED;
1188 	}
1189 
1190 	/* return deserialized return code or SYSINFO_RET_FAIL if setting result data failed */
1191 	return (FAIL == ret ? SYSINFO_RET_FAIL : agent_ret);
1192 }
1193 
1194 /******************************************************************************
1195  *                                                                            *
1196  * Function: write_all                                                        *
1197  *                                                                            *
1198  * Purpose: call write in a loop, iterating until all the data is written.    *
1199  *                                                                            *
1200  * Parameters: fd      - [IN] descriptor                                      *
1201  *             buf     - [IN] buffer to write                                 *
1202  *             n       - [IN] bytes count to write                            *
1203  *                                                                            *
1204  * Return value: SUCCEED - n bytes successfully written                       *
1205  *               FAIL    - less than n bytes are written                      *
1206  *                                                                            *
1207  ******************************************************************************/
write_all(int fd,const char * buf,size_t n)1208 static int	write_all(int fd, const char *buf, size_t n)
1209 {
1210 	ssize_t	ret;
1211 
1212 	while (0 < n)
1213 	{
1214 		if (-1 != (ret = write(fd, buf, n)))
1215 		{
1216 			buf += ret;
1217 			n -= ret;
1218 		}
1219 		else if (EINTR != errno)
1220 			return FAIL;
1221 	}
1222 
1223 	return SUCCEED;
1224 }
1225 
1226 /******************************************************************************
1227  *                                                                            *
1228  * Function: zbx_execute_threaded_metric                                      *
1229  *                                                                            *
1230  * Purpose: execute metric in a separate process/thread so it can be          *
1231  *          killed/terminated when timeout is detected                        *
1232  *                                                                            *
1233  * Parameters: metric_func - [IN] the metric function to execute              *
1234  *             ...                the metric function parameters              *
1235  *                                                                            *
1236  * Return value:                                                              *
1237  *         SYSINFO_RET_OK - the metric was executed successfully              *
1238  *         SYSINFO_RET_FAIL - otherwise                                       *
1239  *                                                                            *
1240  ******************************************************************************/
zbx_execute_threaded_metric(zbx_metric_func_t metric_func,AGENT_REQUEST * request,AGENT_RESULT * result)1241 int	zbx_execute_threaded_metric(zbx_metric_func_t metric_func, AGENT_REQUEST *request, AGENT_RESULT *result)
1242 {
1243 	const char	*__function_name = "zbx_execute_threaded_metric";
1244 
1245 	int		ret = SYSINFO_RET_OK;
1246 	pid_t		pid;
1247 	int		fds[2], n, status;
1248 	char		buffer[MAX_STRING_LEN], *data;
1249 	size_t		data_alloc = MAX_STRING_LEN, data_offset = 0;
1250 
1251 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s'", __function_name, request->key);
1252 
1253 	if (-1 == pipe(fds))
1254 	{
1255 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot create data pipe: %s", strerror_from_system(errno)));
1256 		ret = SYSINFO_RET_FAIL;
1257 		goto out;
1258 	}
1259 
1260 	if (-1 == (pid = zbx_fork()))
1261 	{
1262 		close(fds[0]);
1263 		close(fds[1]);
1264 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot fork data process: %s", strerror_from_system(errno)));
1265 		ret = SYSINFO_RET_FAIL;
1266 		goto out;
1267 	}
1268 
1269 	data = (char *)zbx_malloc(NULL, data_alloc);
1270 
1271 	if (0 == pid)
1272 	{
1273 		zabbix_log(LOG_LEVEL_DEBUG, "executing in data process for key:'%s'", request->key);
1274 
1275 		zbx_set_metric_thread_signal_handler();
1276 
1277 		close(fds[0]);
1278 
1279 		ret = metric_func(request, result);
1280 		serialize_agent_result(&data, &data_alloc, &data_offset, ret, result);
1281 
1282 		ret = write_all(fds[1], data, data_offset);
1283 
1284 		zbx_free(data);
1285 		free_result(result);
1286 
1287 		close(fds[1]);
1288 
1289 		exit(SUCCEED == ret ? EXIT_SUCCESS : EXIT_FAILURE);
1290 	}
1291 
1292 	close(fds[1]);
1293 
1294 	zbx_alarm_on(CONFIG_TIMEOUT);
1295 
1296 	while (0 != (n = read(fds[0], buffer, sizeof(buffer))))
1297 	{
1298 		if (SUCCEED == zbx_alarm_timed_out())
1299 		{
1300 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Timeout while waiting for data."));
1301 			kill(pid, SIGKILL);
1302 			ret = SYSINFO_RET_FAIL;
1303 			break;
1304 		}
1305 
1306 		if (-1 == n)
1307 		{
1308 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Error while reading data: %s", zbx_strerror(errno)));
1309 			kill(pid, SIGKILL);
1310 			ret = SYSINFO_RET_FAIL;
1311 			break;
1312 		}
1313 
1314 		if ((int)(data_alloc - data_offset) < n + 1)
1315 		{
1316 			while ((int)(data_alloc - data_offset) < n + 1)
1317 				data_alloc *= 1.5;
1318 
1319 			data = (char *)zbx_realloc(data, data_alloc);
1320 		}
1321 
1322 		memcpy(data + data_offset, buffer, n);
1323 		data_offset += n;
1324 		data[data_offset] = '\0';
1325 	}
1326 
1327 	zbx_alarm_off();
1328 
1329 	close(fds[0]);
1330 
1331 	while (-1 == waitpid(pid, &status, 0))
1332 	{
1333 		if (EINTR != errno)
1334 		{
1335 			zabbix_log(LOG_LEVEL_ERR, "failed to wait on child processes: %s", zbx_strerror(errno));
1336 			ret = SYSINFO_RET_FAIL;
1337 			break;
1338 		}
1339 	}
1340 
1341 	if (SYSINFO_RET_OK == ret)
1342 	{
1343 		if (0 == WIFEXITED(status))
1344 		{
1345 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Data gathering process terminated unexpectedly with"
1346 					" error %d.", status));
1347 			kill(pid, SIGKILL);
1348 			ret = SYSINFO_RET_FAIL;
1349 		}
1350 		else if (EXIT_SUCCESS != WEXITSTATUS(status))
1351 		{
1352 			SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Data gathering process terminated with error %d.",
1353 					status));
1354 			ret = SYSINFO_RET_FAIL;
1355 		}
1356 		else
1357 			ret = deserialize_agent_result(data, result);
1358 	}
1359 
1360 	zbx_free(data);
1361 out:
1362 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s '%s'", __function_name, zbx_sysinfo_ret_string(ret),
1363 			ISSET_MSG(result) ? result->msg : "");
1364 	return ret;
1365 }
1366 #else
1367 
1368 ZBX_THREAD_LOCAL static zbx_uint32_t	mutex_flag = ZBX_MUTEX_ALL_ALLOW;
1369 
get_thread_global_mutex_flag()1370 zbx_uint32_t get_thread_global_mutex_flag()
1371 {
1372 	return mutex_flag;
1373 }
1374 
1375 typedef struct
1376 {
1377 	zbx_metric_func_t	func;
1378 	AGENT_REQUEST		*request;
1379 	AGENT_RESULT		*result;
1380 	zbx_uint32_t		mutex_flag; /* in regular case should always be = ZBX_MUTEX_ALL_ALLOW */
1381 	HANDLE			timeout_event;
1382 	int			agent_ret;
1383 }
1384 zbx_metric_thread_args_t;
1385 
ZBX_THREAD_ENTRY(agent_metric_thread,data)1386 ZBX_THREAD_ENTRY(agent_metric_thread, data)
1387 {
1388 	zbx_metric_thread_args_t	*args = (zbx_metric_thread_args_t *)((zbx_thread_args_t *)data)->args;
1389 	mutex_flag = args->mutex_flag;
1390 
1391 	zabbix_log(LOG_LEVEL_DEBUG, "executing in data thread for key:'%s'", args->request->key);
1392 
1393 	if (SYSINFO_RET_FAIL == (args->agent_ret = args->func(args->request, args->result, args->timeout_event)))
1394 	{
1395 		if (NULL == GET_MSG_RESULT(args->result))
1396 			SET_MSG_RESULT(args->result, zbx_strdup(NULL, ZBX_NOTSUPPORTED));
1397 	}
1398 
1399 	zbx_thread_exit(0);
1400 }
1401 
1402 /******************************************************************************
1403  *                                                                            *
1404  * Function: zbx_execute_threaded_metric                                      *
1405  *                                                                            *
1406  * Purpose: execute metric in a separate process/thread so it can be          *
1407  *          killed/terminated when timeout is detected                        *
1408  *                                                                            *
1409  * Parameters: metric_func - [IN] the metric function to execute              *
1410  *             ...                the metric function parameters              *
1411  *                                                                            *
1412  * Return value:                                                              *
1413  *         SYSINFO_RET_OK - the metric was executed successfully              *
1414  *         SYSINFO_RET_FAIL - otherwise                                       *
1415  *                                                                            *
1416  ******************************************************************************/
zbx_execute_threaded_metric(zbx_metric_func_t metric_func,AGENT_REQUEST * request,AGENT_RESULT * result)1417 int	zbx_execute_threaded_metric(zbx_metric_func_t metric_func, AGENT_REQUEST *request, AGENT_RESULT *result)
1418 {
1419 	const char			*__function_name = "zbx_execute_threaded_metric";
1420 
1421 	ZBX_THREAD_HANDLE		thread;
1422 	zbx_thread_args_t		thread_args;
1423 	zbx_metric_thread_args_t	metric_args = {metric_func, request, result, ZBX_MUTEX_THREAD_DENIED |
1424 							ZBX_MUTEX_LOGGING_DENIED};
1425 	DWORD				rc;
1426 	BOOL				terminate_thread = FALSE;
1427 
1428 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s'", __function_name, request->key);
1429 
1430 	if (NULL == (metric_args.timeout_event = CreateEvent(NULL, TRUE, FALSE, NULL)))
1431 	{
1432 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot create timeout event for data thread: %s",
1433 				strerror_from_system(GetLastError())));
1434 		return SYSINFO_RET_FAIL;
1435 	}
1436 
1437 	thread_args.args = (void *)&metric_args;
1438 
1439 	zbx_thread_start(agent_metric_thread, &thread_args, &thread);
1440 
1441 	if (ZBX_THREAD_ERROR == thread)
1442 	{
1443 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot start data thread: %s",
1444 				strerror_from_system(GetLastError())));
1445 		CloseHandle(metric_args.timeout_event);
1446 		return SYSINFO_RET_FAIL;
1447 	}
1448 
1449 	/* 1000 is multiplier for converting seconds into milliseconds */
1450 	if (WAIT_FAILED == (rc = WaitForSingleObject(thread, CONFIG_TIMEOUT * 1000)))
1451 	{
1452 		/* unexpected error */
1453 
1454 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot wait for data: %s",
1455 				strerror_from_system(GetLastError())));
1456 		terminate_thread = TRUE;
1457 	}
1458 	else if (WAIT_TIMEOUT == rc)
1459 	{
1460 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Timeout while waiting for data."));
1461 
1462 		/* timeout; notify thread to clean up and exit, if stuck then terminate it */
1463 
1464 		if (FALSE == SetEvent(metric_args.timeout_event))
1465 		{
1466 			zabbix_log(LOG_LEVEL_ERR, "SetEvent() failed: %s", strerror_from_system(GetLastError()));
1467 			terminate_thread = TRUE;
1468 		}
1469 		else
1470 		{
1471 			DWORD	timeout_rc = WaitForSingleObject(thread, 3000);	/* wait up to 3 seconds */
1472 
1473 			if (WAIT_FAILED == timeout_rc)
1474 			{
1475 				zabbix_log(LOG_LEVEL_ERR, "Waiting for data failed: %s",
1476 						strerror_from_system(GetLastError()));
1477 				terminate_thread = TRUE;
1478 			}
1479 			else if (WAIT_TIMEOUT == timeout_rc)
1480 			{
1481 				zabbix_log(LOG_LEVEL_ERR, "Stuck data thread");
1482 				terminate_thread = TRUE;
1483 			}
1484 			/* timeout_rc must be WAIT_OBJECT_0 (signaled) */
1485 		}
1486 	}
1487 
1488 	if (TRUE == terminate_thread)
1489 	{
1490 		if (FALSE != TerminateThread(thread, 0))
1491 		{
1492 			zabbix_log(LOG_LEVEL_ERR, "%s(): TerminateThread() for %s[%s%s] succeeded", __function_name,
1493 					request->key, (0 < request->nparam) ? request->params[0] : "",
1494 					(1 < request->nparam) ? ",..." : "");
1495 		}
1496 		else
1497 		{
1498 			zabbix_log(LOG_LEVEL_ERR, "%s(): TerminateThread() for %s[%s%s] failed: %s", __function_name,
1499 					request->key, (0 < request->nparam) ? request->params[0] : "",
1500 					(1 < request->nparam) ? ",..." : "",
1501 					strerror_from_system(GetLastError()));
1502 		}
1503 	}
1504 
1505 	CloseHandle(thread);
1506 	CloseHandle(metric_args.timeout_event);
1507 
1508 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s '%s'", __function_name,
1509 			zbx_sysinfo_ret_string(metric_args.agent_ret), ISSET_MSG(result) ? result->msg : "");
1510 
1511 	return WAIT_OBJECT_0 == rc ? metric_args.agent_ret : SYSINFO_RET_FAIL;
1512 }
1513 #endif
1514 
1515 /******************************************************************************
1516  *                                                                            *
1517  * Function: zbx_mpoints_free                                                 *
1518  *                                                                            *
1519  * Purpose: frees previously allocated mount-point structure                  *
1520  *                                                                            *
1521  * Parameters: mpoint - [IN] pointer to structure from vector                 *
1522  *                                                                            *
1523  * Return value:                                                              *
1524  *                                                                            *
1525  ******************************************************************************/
zbx_mpoints_free(zbx_mpoint_t * mpoint)1526 void	zbx_mpoints_free(zbx_mpoint_t *mpoint)
1527 {
1528 	zbx_free(mpoint);
1529 }
1530