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 "checks_calculated.h"
21 #include "zbxserver.h"
22 #include "log.h"
23 #include "../../libs/zbxserver/evalfunc.h"
24 
25 typedef struct
26 {
27 	int	functionid;
28 	char	*host;
29 	char	*key;
30 	char	*func;
31 	char	*params;
32 	char	*value;
33 }
34 function_t;
35 
36 typedef struct
37 {
38 	char		*exp;
39 	function_t	*functions;
40 	int		functions_alloc;
41 	int		functions_num;
42 }
43 expression_t;
44 
free_expression(expression_t * exp)45 static void	free_expression(expression_t *exp)
46 {
47 	function_t	*f;
48 	int		i;
49 
50 	for (i = 0; i < exp->functions_num; i++)
51 	{
52 		f = &exp->functions[i];
53 		zbx_free(f->host);
54 		zbx_free(f->key);
55 		zbx_free(f->func);
56 		zbx_free(f->params);
57 		zbx_free(f->value);
58 	}
59 
60 	zbx_free(exp->exp);
61 	zbx_free(exp->functions);
62 	exp->functions_alloc = 0;
63 	exp->functions_num = 0;
64 }
65 
calcitem_add_function(expression_t * exp,char * host,char * key,char * func,char * params)66 static int	calcitem_add_function(expression_t *exp, char *host, char *key, char *func, char *params)
67 {
68 	function_t	*f;
69 
70 	if (exp->functions_alloc == exp->functions_num)
71 	{
72 		exp->functions_alloc += 8;
73 		exp->functions = (function_t *)zbx_realloc(exp->functions, exp->functions_alloc * sizeof(function_t));
74 	}
75 
76 	f = &exp->functions[exp->functions_num++];
77 	f->functionid = exp->functions_num;
78 	f->host = host;
79 	f->key = key;
80 	f->func = func;
81 	f->params = params;
82 	f->value = NULL;
83 
84 	return f->functionid;
85 }
86 
calcitem_parse_expression(DC_ITEM * dc_item,expression_t * exp,char * error,int max_error_len)87 static int	calcitem_parse_expression(DC_ITEM *dc_item, expression_t *exp, char *error, int max_error_len)
88 {
89 	const char	*__function_name = "calcitem_parse_expression";
90 
91 	char		*e, *buf = NULL;
92 	size_t		exp_alloc = 128, exp_offset = 0, f_pos, par_l = 0, par_r = 0;
93 	int		ret = NOTSUPPORTED;
94 
95 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __function_name, dc_item->params);
96 
97 	exp->exp = (char *)zbx_malloc(exp->exp, exp_alloc);
98 
99 	for (e = dc_item->params; SUCCEED == zbx_function_find(e, &f_pos, &par_l, &par_r, error, max_error_len);
100 			e += par_r + 1)
101 	{
102 		char	*func, *params, *host = NULL, *key = NULL;
103 		size_t	param_pos, param_len, sep_pos;
104 		int	functionid, quoted;
105 
106 		/* copy the part of the string preceding function */
107 		zbx_strncpy_alloc(&exp->exp, &exp_alloc, &exp_offset, e, f_pos);
108 
109 		/* extract the first function parameter and <host:>key reference from it */
110 
111 		zbx_function_param_parse(e + par_l + 1, &param_pos, &param_len, &sep_pos);
112 
113 		zbx_free(buf);
114 		buf = zbx_function_param_unquote_dyn(e + par_l + 1 + param_pos, param_len, &quoted);
115 
116 		if (SUCCEED != parse_host_key(buf, &host, &key))
117 		{
118 			zbx_snprintf(error, max_error_len, "Invalid first parameter in function [%.*s].",
119 					(int)(par_r - f_pos + 1), e + f_pos);
120 			goto out;
121 		}
122 		if (NULL == host)
123 			host = zbx_strdup(NULL, dc_item->host.host);
124 
125 		/* extract function name and remaining parameters */
126 
127 		e[par_l] = '\0';
128 		func = zbx_strdup(NULL, e + f_pos);
129 		e[par_l] = '(';
130 
131 		if (')' != e[par_l + 1 + sep_pos]) /* first parameter is not the only one */
132 		{
133 			e[par_r] = '\0';
134 			params = zbx_strdup(NULL, e + par_l + 1 + sep_pos + 1);
135 			e[par_r] = ')';
136 		}
137 		else	/* the only parameter of the function was <host:>key reference */
138 			params = zbx_strdup(NULL, "");
139 
140 		functionid = calcitem_add_function(exp, host, key, func, params);
141 
142 		zabbix_log(LOG_LEVEL_DEBUG, "%s() functionid:%d function:'%s:%s.%s(%s)'",
143 				__function_name, functionid, host, key, func, params);
144 
145 		/* substitute function with id in curly brackets */
146 		zbx_snprintf_alloc(&exp->exp, &exp_alloc, &exp_offset, "{%d}", functionid);
147 	}
148 
149 	if (par_l > par_r)
150 		goto out;
151 
152 	/* copy the remaining part */
153 	zbx_strcpy_alloc(&exp->exp, &exp_alloc, &exp_offset, e);
154 
155 	zabbix_log(LOG_LEVEL_DEBUG, "%s() expression:'%s'", __function_name, exp->exp);
156 
157 	if (SUCCEED == substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &dc_item->host, NULL, NULL, NULL,
158 			&exp->exp, MACRO_TYPE_ITEM_EXPRESSION, error, max_error_len))
159 	{
160 		ret = SUCCEED;
161 	}
162 out:
163 	zbx_free(buf);
164 
165 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
166 
167 	return ret;
168 }
169 
calcitem_evaluate_expression(expression_t * exp,char * error,size_t max_error_len,zbx_vector_ptr_t * unknown_msgs)170 static int	calcitem_evaluate_expression(expression_t *exp, char *error, size_t max_error_len,
171 		zbx_vector_ptr_t *unknown_msgs)
172 {
173 	const char	*__function_name = "calcitem_evaluate_expression";
174 	function_t	*f = NULL;
175 	char		*buf, replace[16], *errstr = NULL;
176 	int		i, ret = SUCCEED;
177 	zbx_host_key_t	*keys = NULL;
178 	DC_ITEM		*items = NULL;
179 	int		*errcodes = NULL;
180 	zbx_timespec_t	ts;
181 
182 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
183 
184 	if (0 == exp->functions_num)
185 		return ret;
186 
187 	keys = (zbx_host_key_t *)zbx_malloc(keys, sizeof(zbx_host_key_t) * (size_t)exp->functions_num);
188 	items = (DC_ITEM *)zbx_malloc(items, sizeof(DC_ITEM) * (size_t)exp->functions_num);
189 	errcodes = (int *)zbx_malloc(errcodes, sizeof(int) * (size_t)exp->functions_num);
190 
191 	for (i = 0; i < exp->functions_num; i++)
192 	{
193 		keys[i].host = exp->functions[i].host;
194 		keys[i].key = exp->functions[i].key;
195 	}
196 
197 	DCconfig_get_items_by_keys(items, keys, errcodes, exp->functions_num);
198 
199 	zbx_timespec(&ts);
200 
201 	for (i = 0; i < exp->functions_num; i++)
202 	{
203 		int	ret_unknown = 0;	/* flag raised if current function evaluates to ZBX_UNKNOWN */
204 		char	*unknown_msg;
205 
206 		f = &exp->functions[i];
207 
208 		if (SUCCEED != errcodes[i])
209 		{
210 			zbx_snprintf(error, max_error_len,
211 					"Cannot evaluate function \"%s(%s)\":"
212 					" item \"%s:%s\" does not exist.",
213 					f->func, f->params, f->host, f->key);
214 			ret = NOTSUPPORTED;
215 			break;
216 		}
217 
218 		/* do not evaluate if the item is disabled or belongs to a disabled host */
219 
220 		if (ITEM_STATUS_ACTIVE != items[i].status)
221 		{
222 			zbx_snprintf(error, max_error_len,
223 					"Cannot evaluate function \"%s(%s)\":"
224 					" item \"%s:%s\" is disabled.",
225 					f->func, f->params, f->host, f->key);
226 			ret = NOTSUPPORTED;
227 			break;
228 		}
229 
230 		if (HOST_STATUS_MONITORED != items[i].host.status)
231 		{
232 			zbx_snprintf(error, max_error_len,
233 					"Cannot evaluate function \"%s(%s)\":"
234 					" item \"%s:%s\" belongs to a disabled host.",
235 					f->func, f->params, f->host, f->key);
236 			ret = NOTSUPPORTED;
237 			break;
238 		}
239 
240 		/* If the item is NOTSUPPORTED then evaluation is allowed for:   */
241 		/*   - functions white-listed in evaluatable_for_notsupported(). */
242 		/*     Their values can be evaluated to regular numbers even for */
243 		/*     NOTSUPPORTED items. */
244 		/*   - other functions. Result of evaluation is ZBX_UNKNOWN.     */
245 
246 		if (ITEM_STATE_NOTSUPPORTED == items[i].state && FAIL == evaluatable_for_notsupported(f->func))
247 		{
248 			/* compose and store 'unknown' message for future use */
249 			unknown_msg = zbx_dsprintf(NULL,
250 					"Cannot evaluate function \"%s(%s)\": item \"%s:%s\" not supported.",
251 					f->func, f->params, f->host, f->key);
252 
253 			zbx_vector_ptr_append(unknown_msgs, unknown_msg);
254 			ret_unknown = 1;
255 		}
256 
257 		f->value = (char *)zbx_malloc(f->value, MAX_BUFFER_LEN);
258 
259 		if (0 == ret_unknown &&
260 				SUCCEED != evaluate_function(f->value, &items[i], f->func, f->params, &ts, &errstr))
261 		{
262 			/* compose and store error message for future use */
263 			if (NULL != errstr)
264 			{
265 				unknown_msg = zbx_dsprintf(NULL, "Cannot evaluate function \"%s(%s)\": %s.",
266 						f->func, f->params, errstr);
267 				zbx_free(errstr);
268 			}
269 			else
270 			{
271 				unknown_msg = zbx_dsprintf(NULL, "Cannot evaluate function \"%s(%s)\".",
272 						f->func, f->params);
273 			}
274 
275 			zbx_vector_ptr_append(unknown_msgs, unknown_msg);
276 			ret_unknown = 1;
277 		}
278 
279 		if (1 == ret_unknown || SUCCEED != is_double_suffix(f->value, ZBX_FLAG_DOUBLE_SUFFIX) || '-' == *f->value)
280 		{
281 			char	*wrapped;
282 
283 			if (0 == ret_unknown)
284 			{
285 				wrapped = zbx_dsprintf(NULL, "(%s)", f->value);
286 			}
287 			else
288 			{
289 				/* write a special token of unknown value with 'unknown' message number, like */
290 				/* ZBX_UNKNOWN0, ZBX_UNKNOWN1 etc. not wrapped in () */
291 				wrapped = zbx_dsprintf(NULL, ZBX_UNKNOWN_STR "%d", unknown_msgs->values_num - 1);
292 			}
293 
294 			zbx_free(f->value);
295 			f->value = wrapped;
296 		}
297 		else
298 			f->value = (char *)zbx_realloc(f->value, strlen(f->value) + 1);
299 
300 		zbx_snprintf(replace, sizeof(replace), "{%d}", f->functionid);
301 		buf = string_replace(exp->exp, replace, f->value);
302 		zbx_free(exp->exp);
303 		exp->exp = buf;
304 	}
305 
306 	DCconfig_clean_items(items, errcodes, exp->functions_num);
307 
308 	zbx_free(errcodes);
309 	zbx_free(items);
310 	zbx_free(keys);
311 
312 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
313 
314 	return ret;
315 }
316 
get_value_calculated(DC_ITEM * dc_item,AGENT_RESULT * result)317 int	get_value_calculated(DC_ITEM *dc_item, AGENT_RESULT *result)
318 {
319 	const char		*__function_name = "get_value_calculated";
320 	expression_t		exp;
321 	int			ret;
322 	char			error[MAX_STRING_LEN];
323 	double			value;
324 	zbx_vector_ptr_t	unknown_msgs;		/* pointers to messages about origins of 'unknown' values */
325 
326 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s' expression:'%s'", __function_name,
327 			dc_item->key_orig, dc_item->params);
328 
329 	memset(&exp, 0, sizeof(exp));
330 
331 	if (SUCCEED != (ret = calcitem_parse_expression(dc_item, &exp, error, sizeof(error))))
332 	{
333 		SET_MSG_RESULT(result, strdup(error));
334 		goto clean1;
335 	}
336 
337 	/* Assumption: most often there will be no NOTSUPPORTED items and function errors. */
338 	/* Therefore initialize error messages vector but do not reserve any space. */
339 	zbx_vector_ptr_create(&unknown_msgs);
340 
341 	if (SUCCEED != (ret = calcitem_evaluate_expression(&exp, error, sizeof(error), &unknown_msgs)))
342 	{
343 		SET_MSG_RESULT(result, strdup(error));
344 		goto clean;
345 	}
346 
347 	if (SUCCEED != evaluate(&value, exp.exp, error, sizeof(error), &unknown_msgs))
348 	{
349 		SET_MSG_RESULT(result, strdup(error));
350 		ret = NOTSUPPORTED;
351 		goto clean;
352 	}
353 
354 	zabbix_log(LOG_LEVEL_DEBUG, "%s() value:" ZBX_FS_DBL, __function_name, value);
355 
356 	if (ITEM_VALUE_TYPE_UINT64 == dc_item->value_type && 0 > value)
357 	{
358 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Received value [" ZBX_FS_DBL "]"
359 				" is not suitable for value type [%s].",
360 				value, zbx_item_value_type_string((zbx_item_value_type_t)dc_item->value_type)));
361 		ret = NOTSUPPORTED;
362 		goto clean;
363 	}
364 
365 	SET_DBL_RESULT(result, value);
366 clean:
367 	zbx_vector_ptr_clear_ext(&unknown_msgs, zbx_ptr_free);
368 	zbx_vector_ptr_destroy(&unknown_msgs);
369 clean1:
370 	free_expression(&exp);
371 
372 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
373 
374 	return ret;
375 }
376