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