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 
24 typedef struct
25 {
26 	int	functionid;
27 	char	*host;
28 	char	*key;
29 	char	*func;
30 	char	*params;
31 	char	*value;
32 }
33 function_t;
34 
35 typedef struct
36 {
37 	char		*exp;
38 	function_t	*functions;
39 	int		functions_alloc;
40 	int		functions_num;
41 }
42 expression_t;
43 
free_expression(expression_t * exp)44 static void	free_expression(expression_t *exp)
45 {
46 	function_t	*f;
47 	int		i;
48 
49 	for (i = 0; i < exp->functions_num; i++)
50 	{
51 		f = &exp->functions[i];
52 		zbx_free(f->host);
53 		zbx_free(f->key);
54 		zbx_free(f->func);
55 		zbx_free(f->params);
56 		zbx_free(f->value);
57 	}
58 
59 	zbx_free(exp->exp);
60 	zbx_free(exp->functions);
61 	exp->functions_alloc = 0;
62 	exp->functions_num = 0;
63 }
64 
calcitem_add_function(expression_t * exp,char * host,char * key,char * func,char * params)65 static int	calcitem_add_function(expression_t *exp, char *host, char *key, char *func, char *params)
66 {
67 	function_t	*f;
68 
69 	if (exp->functions_alloc == exp->functions_num)
70 	{
71 		exp->functions_alloc += 8;
72 		exp->functions = zbx_realloc(exp->functions, exp->functions_alloc * sizeof(function_t));
73 	}
74 
75 	f = &exp->functions[exp->functions_num++];
76 	f->functionid = exp->functions_num;
77 	f->host = host;
78 	f->key = key;
79 	f->func = func;
80 	f->params = params;
81 	f->value = NULL;
82 
83 	return f->functionid;
84 }
85 
calcitem_parse_expression(DC_ITEM * dc_item,expression_t * exp,char * error,int max_error_len)86 static int	calcitem_parse_expression(DC_ITEM *dc_item, expression_t *exp, char *error, int max_error_len)
87 {
88 	const char	*__function_name = "calcitem_parse_expression";
89 
90 	char		*e, *buf = NULL;
91 	size_t		exp_alloc = 128, exp_offset = 0, f_pos, par_l, par_r;
92 	int		ret = NOTSUPPORTED;
93 
94 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() expression:'%s'", __function_name, dc_item->params);
95 
96 	exp->exp = zbx_malloc(exp->exp, 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(&exp->exp, &exp_alloc, &exp_offset, e, f_pos);
107 
108 		/* extract the first function parameter and <host:>key reference from it */
109 
110 		e[par_r] = '\0';
111 		zbx_function_param_parse(e + par_l + 1, &param_pos, &param_len, &sep_pos);
112 		e[par_r] = ')';
113 
114 		zbx_free(buf);
115 		buf = zbx_function_param_unquote_dyn(e + par_l + 1 + param_pos, param_len, &quoted);
116 
117 		if (SUCCEED != parse_host_key(buf, &host, &key))
118 		{
119 			zbx_snprintf(error, max_error_len, "Invalid first parameter in function [%.*s].",
120 					par_r - f_pos + 1, e + f_pos);
121 			goto out;
122 		}
123 		if (NULL == host)
124 			host = zbx_strdup(NULL, dc_item->host.host);
125 
126 		/* extract function name and remaining parameters */
127 
128 		e[par_l] = '\0';
129 		func = zbx_strdup(NULL, e + f_pos);
130 		e[par_l] = '(';
131 
132 		if (')' != e[par_l + 1 + sep_pos]) /* first parameter is not the only one */
133 		{
134 			e[par_r] = '\0';
135 			params = zbx_strdup(NULL, e + par_l + 1 + sep_pos + 1);
136 			e[par_r] = ')';
137 		}
138 		else	/* the only parameter of the function was <host:>key reference */
139 			params = zbx_strdup(NULL, "");
140 
141 		functionid = calcitem_add_function(exp, host, key, func, params);
142 
143 		zabbix_log(LOG_LEVEL_DEBUG, "%s() functionid:%d function:'%s:%s.%s(%s)'",
144 				__function_name, functionid, host, key, func, params);
145 
146 		/* substitute function with id in curly brackets */
147 		zbx_snprintf_alloc(&exp->exp, &exp_alloc, &exp_offset, "{%d}", functionid);
148 	}
149 
150 	if (par_l > par_r)
151 		goto out;
152 
153 	/* copy the remaining part */
154 	zbx_strcpy_alloc(&exp->exp, &exp_alloc, &exp_offset, e);
155 
156 	zabbix_log(LOG_LEVEL_DEBUG, "%s() expression:'%s'", __function_name, exp->exp);
157 
158 	if (SUCCEED == substitute_simple_macros(NULL, NULL, NULL, NULL, NULL, &dc_item->host, NULL, NULL, &exp->exp,
159 			MACRO_TYPE_ITEM_EXPRESSION, error, max_error_len))
160 	{
161 		ret = SUCCEED;
162 	}
163 out:
164 	zbx_free(buf);
165 
166 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
167 
168 	return ret;
169 }
170 
calcitem_evaluate_expression(expression_t * exp,char * error,int max_error_len)171 static int	calcitem_evaluate_expression(expression_t *exp, char *error, int max_error_len)
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 	time_t		now;
178 	zbx_host_key_t	*keys = NULL;
179 	DC_ITEM		*items = NULL;
180 	int		*errcodes = NULL;
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_malloc(keys, sizeof(zbx_host_key_t) * exp->functions_num);
188 	items = zbx_malloc(items, sizeof(DC_ITEM) * exp->functions_num);
189 	errcodes = zbx_malloc(errcodes, sizeof(int) * 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 	now = time(NULL);
200 
201 	for (i = 0; i < exp->functions_num; i++)
202 	{
203 		f = &exp->functions[i];
204 
205 		if (SUCCEED != errcodes[i])
206 		{
207 			zbx_snprintf(error, max_error_len,
208 					"Cannot evaluate function \"%s(%s)\":"
209 					" item \"%s:%s\" does not exist.",
210 					f->func, f->params, f->host, f->key);
211 			ret = NOTSUPPORTED;
212 			break;
213 		}
214 
215 		if (ITEM_STATUS_ACTIVE != items[i].status)
216 		{
217 			zbx_snprintf(error, max_error_len,
218 					"Cannot evaluate function \"%s(%s)\":"
219 					" item \"%s:%s\" is disabled.",
220 					f->func, f->params, f->host, f->key);
221 			ret = NOTSUPPORTED;
222 			break;
223 		}
224 
225 		if (HOST_STATUS_MONITORED != items[i].host.status)
226 		{
227 			zbx_snprintf(error, max_error_len,
228 					"Cannot evaluate function \"%s(%s)\":"
229 					" item \"%s:%s\" belongs to a disabled host.",
230 					f->func, f->params, f->host, f->key);
231 			ret = NOTSUPPORTED;
232 			break;
233 		}
234 
235 		if (ITEM_STATE_NOTSUPPORTED == items[i].state)
236 		{
237 			zbx_snprintf(error, max_error_len,
238 					"Cannot evaluate function \"%s(%s)\": item \"%s:%s\" not supported.",
239 					f->func, f->params, f->host, f->key);
240 			ret = NOTSUPPORTED;
241 			break;
242 		}
243 
244 		f->value = zbx_malloc(f->value, MAX_BUFFER_LEN);
245 
246 		if (SUCCEED != evaluate_function(f->value, &items[i], f->func, f->params, now, &errstr))
247 		{
248 			if (NULL != errstr)
249 			{
250 				zbx_snprintf(error, max_error_len, "Cannot evaluate function \"%s(%s)\": %s.",
251 						f->func, f->params, errstr);
252 				zbx_free(errstr);
253 			}
254 			else
255 			{
256 				zbx_snprintf(error, max_error_len, "Cannot evaluate function \"%s(%s)\".",
257 						f->func, f->params);
258 			}
259 
260 			ret = NOTSUPPORTED;
261 			break;
262 		}
263 
264 		if (SUCCEED != is_double_suffix(f->value, ZBX_FLAG_DOUBLE_SUFFIX) || '-' == *f->value)
265 		{
266 			char	*wrapped;
267 
268 			wrapped = zbx_dsprintf(NULL, "(%s)", f->value);
269 
270 			zbx_free(f->value);
271 			f->value = wrapped;
272 		}
273 		else
274 			f->value = zbx_realloc(f->value, strlen(f->value) + 1);
275 
276 		zbx_snprintf(replace, sizeof(replace), "{%d}", f->functionid);
277 		buf = string_replace(exp->exp, replace, f->value);
278 		zbx_free(exp->exp);
279 		exp->exp = buf;
280 	}
281 
282 	DCconfig_clean_items(items, errcodes, exp->functions_num);
283 
284 	zbx_free(errcodes);
285 	zbx_free(items);
286 	zbx_free(keys);
287 
288 	return ret;
289 }
290 
get_value_calculated(DC_ITEM * dc_item,AGENT_RESULT * result)291 int	get_value_calculated(DC_ITEM *dc_item, AGENT_RESULT *result)
292 {
293 	const char	*__function_name = "get_value_calculated";
294 	expression_t	exp;
295 	int		ret;
296 	char		error[MAX_STRING_LEN];
297 	double		value;
298 
299 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s' expression:'%s'", __function_name,
300 			dc_item->key_orig, dc_item->params);
301 
302 	memset(&exp, 0, sizeof(exp));
303 
304 	if (SUCCEED != (ret = calcitem_parse_expression(dc_item, &exp, error, sizeof(error))))
305 	{
306 		SET_MSG_RESULT(result, strdup(error));
307 		goto clean;
308 	}
309 
310 	if (SUCCEED != (ret = calcitem_evaluate_expression(&exp, error, sizeof(error))))
311 	{
312 		SET_MSG_RESULT(result, strdup(error));
313 		goto clean;
314 	}
315 
316 	if (SUCCEED != evaluate(&value, exp.exp, error, sizeof(error)))
317 	{
318 		SET_MSG_RESULT(result, strdup(error));
319 		ret = NOTSUPPORTED;
320 		goto clean;
321 	}
322 
323 	zabbix_log(LOG_LEVEL_DEBUG, "%s() value:" ZBX_FS_DBL, __function_name, value);
324 
325 	if (ITEM_VALUE_TYPE_UINT64 == dc_item->value_type && 0 > value)
326 	{
327 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Received value [" ZBX_FS_DBL "]"
328 				" is not suitable for value type [%s].",
329 				value, zbx_item_value_type_string(dc_item->value_type)));
330 		ret = NOTSUPPORTED;
331 		goto clean;
332 	}
333 
334 	SET_DBL_RESULT(result, value);
335 clean:
336 	free_expression(&exp);
337 
338 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
339 
340 	return ret;
341 }
342