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