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, ¶m_pos, ¶m_len, &sep_pos);
112
113 zbx_free(buf);
114 buf = zbx_function_param_unquote_dyn(e + par_l + 1 + param_pos, param_len, "ed);
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