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