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