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 "common.h"
21 #include "db.h"
22 #include "zbxtasks.h"
23 #include "log.h"
24 #include "zbxserver.h"
25 #include "postinit.h"
26 #include "valuecache.h"
27
28 #define ZBX_HIST_MACRO_NONE (-1)
29 #define ZBX_HIST_MACRO_ITEM_VALUE 0
30 #define ZBX_HIST_MACRO_ITEM_LASTVALUE 1
31
32 /******************************************************************************
33 * *
34 * Function: get_trigger_count *
35 * *
36 * Purpose: gets the total number of triggers on system *
37 * *
38 * Parameters: *
39 * *
40 * Return value: The total number of triggers on system. *
41 * *
42 ******************************************************************************/
get_trigger_count(void)43 static int get_trigger_count(void)
44 {
45 DB_RESULT result;
46 DB_ROW row;
47 int triggers_num;
48
49 result = DBselect("select count(*) from triggers");
50 if (NULL != (row = DBfetch(result)))
51 triggers_num = atoi(row[0]);
52 else
53 triggers_num = 0;
54 DBfree_result(result);
55
56 return triggers_num;
57 }
58
59 /******************************************************************************
60 * *
61 * Function: is_historical_macro *
62 * *
63 * Purpose: checks if this is historical macro that cannot be expanded for *
64 * bulk event name update *
65 * *
66 * Parameters: macro - [IN] the macro name *
67 * *
68 * Return value: ZBX_HIST_MACRO_* defines *
69 * *
70 ******************************************************************************/
is_historical_macro(const char * macro)71 static int is_historical_macro(const char *macro)
72 {
73 if (0 == strncmp(macro, "ITEM.VALUE", ZBX_CONST_STRLEN("ITEM.VALUE")))
74 return ZBX_HIST_MACRO_ITEM_VALUE;
75
76 if (0 == strncmp(macro, "ITEM.LASTVALUE", ZBX_CONST_STRLEN("ITEM.LASTVALUE")))
77 return ZBX_HIST_MACRO_ITEM_LASTVALUE;
78
79 return ZBX_HIST_MACRO_NONE;
80 }
81
82 /******************************************************************************
83 * *
84 * Function: convert_historical_macro *
85 * *
86 * Purpose: translates historical macro to lld macro format *
87 * *
88 * Parameters: macro - [IN] the macro type (see ZBX_HIST_MACRO_* defines) *
89 * *
90 * Return value: the macro *
91 * *
92 * Comments: Some of the macros can be converted to different name. *
93 * *
94 ******************************************************************************/
convert_historical_macro(int macro)95 static const char *convert_historical_macro(int macro)
96 {
97 /* When expanding macros for old events ITEM.LASTVALUE macro would */
98 /* always expand to one (latest) value. Expanding it as ITEM.VALUE */
99 /* makes more sense in this case. */
100 const char *macros[] = {"#ITEM.VALUE", "#ITEM.VALUE"};
101
102 return macros[macro];
103 }
104
105 /******************************************************************************
106 * *
107 * Function: preprocess_trigger_name *
108 * *
109 * Purpose: pre-process trigger name(description) by expanding non historical *
110 * macros *
111 * *
112 * Parameters: trigger - [IN] the trigger *
113 * historical - [OUT] 1 - trigger name contains historical macros *
114 * 0 - otherwise *
115 * *
116 * Comments: Some historical macros might be replaced with other macros to *
117 * better match the trigger name at event creation time. *
118 * *
119 ******************************************************************************/
preprocess_trigger_name(DB_TRIGGER * trigger,int * historical)120 static void preprocess_trigger_name(DB_TRIGGER *trigger, int *historical)
121 {
122 int pos = 0, macro_len, macro_type;
123 zbx_token_t token;
124 size_t name_alloc, name_len, replace_alloc = 64, replace_offset, r, l;
125 char *replace;
126 const char *macro;
127 DB_EVENT event;
128
129 *historical = FAIL;
130
131 replace = (char *)zbx_malloc(NULL, replace_alloc);
132
133 name_alloc = name_len = strlen(trigger->description) + 1;
134
135 while (SUCCEED == zbx_token_find(trigger->description, pos, &token, ZBX_TOKEN_SEARCH_BASIC))
136 {
137 if (ZBX_TOKEN_MACRO == token.type || ZBX_TOKEN_FUNC_MACRO == token.type)
138 {
139 /* the macro excluding the opening and closing brackets {}, for example: ITEM.VALUE */
140 if (ZBX_TOKEN_MACRO == token.type)
141 {
142 l = token.data.macro.name.l;
143 r = token.data.macro.name.r;
144 }
145 else
146 {
147 l = token.data.func_macro.macro.l + 1;
148 r = token.data.func_macro.macro.r - 1;
149 }
150
151 macro = trigger->description + l;
152
153 if (ZBX_HIST_MACRO_NONE != (macro_type = is_historical_macro(macro)))
154 {
155 if (0 != isdigit(*(trigger->description + r)))
156 macro_len = r - l;
157 else
158 macro_len = r - l + 1;
159
160 macro = convert_historical_macro(macro_type);
161
162 token.loc.r += zbx_replace_mem_dyn(&trigger->description, &name_alloc, &name_len, l,
163 macro_len, macro, strlen(macro));
164 *historical = SUCCEED;
165 }
166 }
167 pos = token.loc.r;
168 }
169
170 memset(&event, 0, sizeof(DB_EVENT));
171 event.object = EVENT_OBJECT_TRIGGER;
172 event.objectid = trigger->triggerid;
173 event.trigger = *trigger;
174
175 substitute_simple_macros(NULL, &event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &trigger->description,
176 MACRO_TYPE_TRIGGER_DESCRIPTION, NULL, 0);
177
178 if (SUCCEED == *historical)
179 {
180 pos = 0;
181 name_alloc = name_len = strlen(trigger->description) + 1;
182
183 while (SUCCEED == zbx_token_find(trigger->description, pos, &token, ZBX_TOKEN_SEARCH_BASIC))
184 {
185 if (ZBX_TOKEN_LLD_MACRO == token.type || ZBX_TOKEN_LLD_FUNC_MACRO == token.type)
186 {
187 if (ZBX_TOKEN_LLD_MACRO == token.type)
188 {
189 l = token.data.lld_macro.name.l;
190 r = token.data.lld_macro.name.r;
191 }
192 else
193 {
194 l = token.data.lld_func_macro.macro.l + 2;
195 r = token.data.lld_func_macro.macro.r - 1;
196 }
197
198 macro = trigger->description + l;
199
200 if (ZBX_HIST_MACRO_NONE != is_historical_macro(macro))
201 {
202 macro_len = r - l + 1;
203 replace_offset = 0;
204 zbx_strncpy_alloc(&replace, &replace_alloc, &replace_offset, macro, macro_len);
205
206 token.loc.r += zbx_replace_mem_dyn(&trigger->description, &name_alloc,
207 &name_len, l - 1, macro_len + 1, replace, replace_offset);
208 }
209 }
210 pos = token.loc.r;
211 }
212 }
213
214 zbx_free(replace);
215 }
216
217 /******************************************************************************
218 * *
219 * Function: process_event_bulk_update *
220 * *
221 * Purpose: update event/problem names for a trigger with bulk request *
222 * *
223 * Parameters: trigger - [IN] the trigger *
224 * sql - [IN/OUT] the sql query *
225 * sql_alloc - [IN/OUT] the sql query size *
226 * sql_offset - [IN/OUT] the sql query length *
227 * *
228 * Return value: SUCCEED - the update was successful *
229 * FAIL - otherwise *
230 * *
231 * Comments: Event names for triggers without historical macros will be the *
232 * same and can be updated with a single sql query. *
233 * *
234 ******************************************************************************/
process_event_bulk_update(const DB_TRIGGER * trigger,char ** sql,size_t * sql_alloc,size_t * sql_offset)235 static int process_event_bulk_update(const DB_TRIGGER *trigger, char **sql, size_t *sql_alloc, size_t *sql_offset)
236 {
237 char *name_esc;
238 int ret;
239
240 name_esc = DBdyn_escape_string_len(trigger->description, EVENT_NAME_LEN);
241
242 zbx_snprintf_alloc(sql, sql_alloc, sql_offset,
243 "update events"
244 " set name='%s'"
245 " where source=%d"
246 " and object=%d"
247 " and objectid=" ZBX_FS_UI64 ";\n",
248 name_esc, EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, trigger->triggerid);
249
250 if (SUCCEED == (ret = DBexecute_overflowed_sql(sql, sql_alloc, sql_offset)))
251 {
252 zbx_snprintf_alloc(sql, sql_alloc, sql_offset,
253 "update problem"
254 " set name='%s'"
255 " where source=%d"
256 " and object=%d"
257 " and objectid=" ZBX_FS_UI64 ";\n",
258 name_esc, EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, trigger->triggerid);
259
260 ret = DBexecute_overflowed_sql(sql, sql_alloc, sql_offset);
261 }
262
263 zbx_free(name_esc);
264
265 return ret;
266 }
267
268 /******************************************************************************
269 * *
270 * Function: process_event_update *
271 * *
272 * Purpose: update event/problem names for a trigger with separate requests *
273 * for each event *
274 * *
275 * Parameters: trigger - [IN] the trigger *
276 * sql - [IN/OUT] the sql query *
277 * sql_alloc - [IN/OUT] the sql query size *
278 * sql_offset - [IN/OUT] the sql query length *
279 * *
280 * Return value: SUCCEED - the update was successful *
281 * FAIL - otherwise *
282 * *
283 * Comments: Event names for triggers with historical macros might differ and *
284 * historical macros in trigger name must be expanded for each *
285 * event. *
286 * *
287 ******************************************************************************/
process_event_update(const DB_TRIGGER * trigger,char ** sql,size_t * sql_alloc,size_t * sql_offset)288 static int process_event_update(const DB_TRIGGER *trigger, char **sql, size_t *sql_alloc, size_t *sql_offset)
289 {
290 DB_RESULT result;
291 DB_ROW row;
292 DB_EVENT event;
293 char *name, *name_esc;
294 int ret = SUCCEED;
295
296 memset(&event, 0, sizeof(DB_EVENT));
297
298 result = DBselect("select eventid,source,object,objectid,clock,value,acknowledged,ns,name"
299 " from events"
300 " where source=%d"
301 " and object=%d"
302 " and objectid=" ZBX_FS_UI64
303 " order by eventid",
304 EVENT_SOURCE_TRIGGERS, EVENT_OBJECT_TRIGGER, trigger->triggerid);
305
306 while (SUCCEED == ret && NULL != (row = DBfetch(result)))
307 {
308 ZBX_STR2UINT64(event.eventid, row[0]);
309 event.source = atoi(row[1]);
310 event.object = atoi(row[2]);
311 ZBX_STR2UINT64(event.objectid, row[3]);
312 event.clock = atoi(row[4]);
313 event.value = atoi(row[5]);
314 event.acknowledged = atoi(row[6]);
315 event.ns = atoi(row[7]);
316 event.name = row[8];
317
318 event.trigger = *trigger;
319
320 name = zbx_strdup(NULL, trigger->description);
321
322 substitute_simple_macros(NULL, &event, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &name,
323 MACRO_TYPE_TRIGGER_DESCRIPTION, NULL, 0);
324
325 name_esc = DBdyn_escape_string_len(name, EVENT_NAME_LEN);
326
327 zbx_snprintf_alloc(sql, sql_alloc, sql_offset,
328 "update events"
329 " set name='%s'"
330 " where eventid=" ZBX_FS_UI64 ";\n",
331 name_esc, event.eventid);
332
333 if (SUCCEED == (ret = DBexecute_overflowed_sql(sql, sql_alloc, sql_offset)))
334 {
335 zbx_snprintf_alloc(sql, sql_alloc, sql_offset,
336 "update problem"
337 " set name='%s'"
338 " where eventid=" ZBX_FS_UI64 ";\n",
339 name_esc, event.eventid);
340
341 ret = DBexecute_overflowed_sql(sql, sql_alloc, sql_offset);
342 }
343
344 zbx_free(name_esc);
345 zbx_free(name);
346 }
347
348 DBfree_result(result);
349
350 return ret;
351 }
352
353 /******************************************************************************
354 * *
355 * Function: update_event_names *
356 * *
357 * Purpose: update event names in events and problem tables *
358 * *
359 * Return value: SUCCEED - the update was successful *
360 * FAIL - otherwise *
361 * *
362 ******************************************************************************/
update_event_names(void)363 static int update_event_names(void)
364 {
365 DB_RESULT result;
366 DB_ROW row;
367 DB_TRIGGER trigger;
368 int ret = SUCCEED, historical, triggers_num, processed_num = 0, completed, last_completed = 0;
369 char *sql;
370 size_t sql_alloc = 4096, sql_offset = 0;
371
372 zabbix_log(LOG_LEVEL_WARNING, "starting event name update forced by database upgrade");
373
374 if (0 == (triggers_num = get_trigger_count()))
375 goto out;
376
377 memset(&trigger, 0, sizeof(DB_TRIGGER));
378
379 sql = (char *)zbx_malloc(NULL, sql_alloc);
380 DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
381
382 result = DBselect(
383 "select triggerid,description,expression,priority,comments,url,recovery_expression,"
384 "recovery_mode,value"
385 " from triggers"
386 " order by triggerid");
387
388 while (SUCCEED == ret && NULL != (row = DBfetch(result)))
389 {
390 ZBX_STR2UINT64(trigger.triggerid, row[0]);
391 trigger.description = zbx_strdup(NULL, row[1]);
392 trigger.expression = zbx_strdup(NULL, row[2]);
393 ZBX_STR2UCHAR(trigger.priority, row[3]);
394 trigger.comments = zbx_strdup(NULL, row[4]);
395 trigger.url = zbx_strdup(NULL, row[5]);
396 trigger.recovery_expression = zbx_strdup(NULL, row[6]);
397 ZBX_STR2UCHAR(trigger.recovery_mode, row[7]);
398 ZBX_STR2UCHAR(trigger.value, row[8]);
399
400 preprocess_trigger_name(&trigger, &historical);
401
402 if (FAIL == historical)
403 ret = process_event_bulk_update(&trigger, &sql, &sql_alloc, &sql_offset);
404 else
405 ret = process_event_update(&trigger, &sql, &sql_alloc, &sql_offset);
406
407 zbx_db_trigger_clean(&trigger);
408
409 processed_num++;
410
411 if (last_completed != (completed = 100.0 * processed_num / triggers_num))
412 {
413 zabbix_log(LOG_LEVEL_WARNING, "completed %d%% of event name update", completed);
414 last_completed = completed;
415 }
416 }
417
418 DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
419
420 if (SUCCEED == ret && 16 < sql_offset) /* in ORACLE always present begin..end; */
421 {
422 if (ZBX_DB_OK > DBexecute("%s", sql))
423 ret = FAIL;
424 }
425
426 DBfree_result(result);
427
428 zbx_free(sql);
429 out:
430 if (SUCCEED == ret)
431 zabbix_log(LOG_LEVEL_WARNING, "event name update completed");
432 else
433 zabbix_log(LOG_LEVEL_WARNING, "event name update failed");
434
435 return ret;
436 }
437
438 /******************************************************************************
439 * *
440 * Function: zbx_check_postinit_tasks *
441 * *
442 * Purpose: process post initialization tasks *
443 * *
444 * Return value: SUCCEED - the update was successful *
445 * FAIL - otherwise *
446 * *
447 ******************************************************************************/
zbx_check_postinit_tasks(char ** error)448 int zbx_check_postinit_tasks(char **error)
449 {
450 DB_RESULT result;
451 DB_ROW row;
452 int ret = SUCCEED;
453
454 result = DBselect("select taskid from task where type=%d and status=%d", ZBX_TM_TASK_UPDATE_EVENTNAMES,
455 ZBX_TM_STATUS_NEW);
456
457 if (NULL != (row = DBfetch(result)))
458 {
459 DBbegin();
460
461 if (SUCCEED == (ret = update_event_names()))
462 {
463 DBexecute("delete from task where taskid=%s", row[0]);
464 DBcommit();
465 }
466 else
467 DBrollback();
468 }
469
470 DBfree_result(result);
471
472 if (SUCCEED != ret)
473 *error = zbx_strdup(*error, "cannot update event names");
474
475 return ret;
476 }
477