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 #include "common.h"
20 #include "log.h"
21 #include "zbxalgo.h"
22 #include "dbcache.h"
23 #include "mutexs.h"
24 
25 #define ZBX_DBCONFIG_IMPL
26 #include "dbconfig.h"
27 
28 #include "dbsync.h"
29 #include "zbxserver.h"
30 
31 extern int		CONFIG_TIMER_FORKS;
32 
33 typedef struct
34 {
35 	zbx_uint64_t			hostid;
36 	const zbx_dc_maintenance_t	*maintenance;
37 }
38 zbx_host_maintenance_t;
39 
40 typedef struct
41 {
42 	zbx_uint64_t		hostid;
43 	zbx_vector_ptr_t	maintenances;
44 }
45 zbx_host_event_maintenance_t;
46 
47 /******************************************************************************
48  *                                                                            *
49  * Function: DCsync_maintenances                                              *
50  *                                                                            *
51  * Purpose: Updates maintenances in configuration cache                       *
52  *                                                                            *
53  * Parameters: sync - [IN] the db synchronization data                        *
54  *                                                                            *
55  * Comments: The result contains the following fields:                        *
56  *           0 - maintenanceid                                                *
57  *           1 - maintenance_type                                             *
58  *           2 - active_since                                                 *
59  *           3 - active_till                                                  *
60  *           4 - tags_evaltype                                                *
61  *                                                                            *
62  ******************************************************************************/
DCsync_maintenances(zbx_dbsync_t * sync)63 void	DCsync_maintenances(zbx_dbsync_t *sync)
64 {
65 	char			**row;
66 	zbx_uint64_t		rowid;
67 	unsigned char		tag;
68 	zbx_uint64_t		maintenanceid;
69 	zbx_dc_maintenance_t	*maintenance;
70 	int			found, ret;
71 
72 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
73 
74 	while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
75 	{
76 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_TRUE;
77 
78 		/* removed rows will be always added at the end */
79 		if (ZBX_DBSYNC_ROW_REMOVE == tag)
80 			break;
81 
82 		ZBX_STR2UINT64(maintenanceid, row[0]);
83 
84 		maintenance = (zbx_dc_maintenance_t *)DCfind_id(&config->maintenances, maintenanceid,
85 				sizeof(zbx_dc_maintenance_t), &found);
86 
87 		if (0 == found)
88 		{
89 			maintenance->state = ZBX_MAINTENANCE_IDLE;
90 			maintenance->running_since = 0;
91 			maintenance->running_until = 0;
92 
93 			zbx_vector_uint64_create_ext(&maintenance->groupids, config->maintenances.mem_malloc_func,
94 					config->maintenances.mem_realloc_func, config->maintenances.mem_free_func);
95 			zbx_vector_uint64_create_ext(&maintenance->hostids, config->maintenances.mem_malloc_func,
96 					config->maintenances.mem_realloc_func, config->maintenances.mem_free_func);
97 			zbx_vector_ptr_create_ext(&maintenance->tags, config->maintenances.mem_malloc_func,
98 					config->maintenances.mem_realloc_func, config->maintenances.mem_free_func);
99 			zbx_vector_ptr_create_ext(&maintenance->periods, config->maintenances.mem_malloc_func,
100 					config->maintenances.mem_realloc_func, config->maintenances.mem_free_func);
101 		}
102 
103 		ZBX_STR2UCHAR(maintenance->type, row[1]);
104 		ZBX_STR2UCHAR(maintenance->tags_evaltype, row[4]);
105 		maintenance->active_since = atoi(row[2]);
106 		maintenance->active_until = atoi(row[3]);
107 	}
108 
109 	/* remove deleted maintenances */
110 
111 	for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
112 	{
113 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances, &rowid)))
114 			continue;
115 
116 		zbx_vector_uint64_destroy(&maintenance->groupids);
117 		zbx_vector_uint64_destroy(&maintenance->hostids);
118 		zbx_vector_ptr_destroy(&maintenance->tags);
119 		zbx_vector_ptr_destroy(&maintenance->periods);
120 
121 		zbx_hashset_remove_direct(&config->maintenances, maintenance);
122 	}
123 
124 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
125 }
126 
127 /******************************************************************************
128  *                                                                            *
129  * Function: dc_compare_maintenance_tags                                      *
130  *                                                                            *
131  * Purpose: compare maintenance tags by tag name for sorting                  *
132  *                                                                            *
133  ******************************************************************************/
dc_compare_maintenance_tags(const void * d1,const void * d2)134 static int	dc_compare_maintenance_tags(const void *d1, const void *d2)
135 {
136 	const zbx_dc_maintenance_tag_t	*tag1 = *(const zbx_dc_maintenance_tag_t **)d1;
137 	const zbx_dc_maintenance_tag_t	*tag2 = *(const zbx_dc_maintenance_tag_t **)d2;
138 
139 	return strcmp(tag1->tag, tag2->tag);
140 }
141 
142 /******************************************************************************
143  *                                                                            *
144  * Function: DCsync_maintenance_tags                                          *
145  *                                                                            *
146  * Purpose: Updates maintenance tags in configuration cache                   *
147  *                                                                            *
148  * Parameters: sync - [IN] the db synchronization data                        *
149  *                                                                            *
150  * Comments: The result contains the following fields:                        *
151  *           0 - maintenancetagid                                             *
152  *           1 - maintenanceid                                                *
153  *           2 - operator                                                     *
154  *           3 - tag                                                          *
155  *           4 - value                                                        *
156  *                                                                            *
157  ******************************************************************************/
DCsync_maintenance_tags(zbx_dbsync_t * sync)158 void	DCsync_maintenance_tags(zbx_dbsync_t *sync)
159 {
160 	char				**row;
161 	zbx_uint64_t			rowid;
162 	unsigned char			tag;
163 	zbx_uint64_t			maintenancetagid, maintenanceid;
164 	zbx_dc_maintenance_tag_t	*maintenance_tag;
165 	zbx_dc_maintenance_t		*maintenance;
166 	zbx_vector_ptr_t		maintenances;
167 	int				found, ret, index, i;
168 
169 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
170 
171 	zbx_vector_ptr_create(&maintenances);
172 
173 	while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
174 	{
175 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_TRUE;
176 
177 		/* removed rows will be always added at the end */
178 		if (ZBX_DBSYNC_ROW_REMOVE == tag)
179 			break;
180 
181 		ZBX_STR2UINT64(maintenanceid, row[1]);
182 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
183 				&maintenanceid)))
184 		{
185 			continue;
186 		}
187 
188 		ZBX_STR2UINT64(maintenancetagid, row[0]);
189 		maintenance_tag = (zbx_dc_maintenance_tag_t *)DCfind_id(&config->maintenance_tags, maintenancetagid,
190 				sizeof(zbx_dc_maintenance_tag_t), &found);
191 
192 		maintenance_tag->maintenanceid = maintenanceid;
193 		ZBX_STR2UCHAR(maintenance_tag->op, row[2]);
194 		DCstrpool_replace(found, &maintenance_tag->tag, row[3]);
195 		DCstrpool_replace(found, &maintenance_tag->value, row[4]);
196 
197 		if (0 == found)
198 			zbx_vector_ptr_append(&maintenance->tags, maintenance_tag);
199 
200 		zbx_vector_ptr_append(&maintenances, maintenance);
201 	}
202 
203 	/* remove deleted maintenance tags */
204 
205 	for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
206 	{
207 		if (NULL == (maintenance_tag = (zbx_dc_maintenance_tag_t *)zbx_hashset_search(&config->maintenance_tags,
208 				&rowid)))
209 		{
210 			continue;
211 		}
212 
213 		if (NULL != (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
214 				&maintenance_tag->maintenanceid)))
215 		{
216 			index = zbx_vector_ptr_search(&maintenance->tags, &maintenance_tag->maintenancetagid,
217 					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
218 
219 			if (FAIL != index)
220 				zbx_vector_ptr_remove_noorder(&maintenance->tags, index);
221 
222 			zbx_vector_ptr_append(&maintenances, maintenance);
223 		}
224 
225 		zbx_strpool_release(maintenance_tag->tag);
226 		zbx_strpool_release(maintenance_tag->value);
227 
228 		zbx_hashset_remove_direct(&config->maintenance_tags, maintenance_tag);
229 	}
230 
231 	/* sort maintenance tags */
232 
233 	zbx_vector_ptr_sort(&maintenances, ZBX_DEFAULT_PTR_COMPARE_FUNC);
234 	zbx_vector_ptr_uniq(&maintenances, ZBX_DEFAULT_PTR_COMPARE_FUNC);
235 
236 	for (i = 0; i < maintenances.values_num; i++)
237 	{
238 		maintenance = (zbx_dc_maintenance_t *)maintenances.values[i];
239 		zbx_vector_ptr_sort(&maintenance->tags, dc_compare_maintenance_tags);
240 	}
241 
242 	zbx_vector_ptr_destroy(&maintenances);
243 
244 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
245 }
246 
247 /******************************************************************************
248  *                                                                            *
249  * Function: DCsync_maintenance_periods                                       *
250  *                                                                            *
251  * Purpose: Updates maintenance period in configuration cache                 *
252  *                                                                            *
253  * Parameters: sync - [IN] the db synchronization data                        *
254  *                                                                            *
255  * Comments: The result contains the following fields:                        *
256  *           0 - timeperiodid                                                 *
257  *           1 - timeperiod_type                                              *
258  *           2 - every                                                        *
259  *           3 - month                                                        *
260  *           4 - dayofweek                                                    *
261  *           5 - day                                                          *
262  *           6 - start_time                                                   *
263  *           7 - period                                                       *
264  *           8 - start_date                                                   *
265  *           9 - maintenanceid                                                *
266  *                                                                            *
267  ******************************************************************************/
DCsync_maintenance_periods(zbx_dbsync_t * sync)268 void	DCsync_maintenance_periods(zbx_dbsync_t *sync)
269 {
270 	char				**row;
271 	zbx_uint64_t			rowid;
272 	unsigned char			tag;
273 	zbx_uint64_t			periodid, maintenanceid;
274 	zbx_dc_maintenance_period_t	*period;
275 	zbx_dc_maintenance_t		*maintenance;
276 	int				found, ret, index;
277 
278 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
279 
280 	while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
281 	{
282 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_TRUE;
283 
284 		/* removed rows will be always added at the end */
285 		if (ZBX_DBSYNC_ROW_REMOVE == tag)
286 			break;
287 
288 		ZBX_STR2UINT64(maintenanceid, row[9]);
289 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
290 				&maintenanceid)))
291 		{
292 			continue;
293 		}
294 
295 		ZBX_STR2UINT64(periodid, row[0]);
296 		period = (zbx_dc_maintenance_period_t *)DCfind_id(&config->maintenance_periods, periodid,
297 				sizeof(zbx_dc_maintenance_period_t), &found);
298 
299 		period->maintenanceid = maintenanceid;
300 		ZBX_STR2UCHAR(period->type, row[1]);
301 		period->every = atoi(row[2]);
302 		period->month = atoi(row[3]);
303 		period->dayofweek = atoi(row[4]);
304 		period->day = atoi(row[5]);
305 		period->start_time = atoi(row[6]);
306 		period->period = atoi(row[7]);
307 		period->start_date = atoi(row[8]);
308 
309 		if (0 == found)
310 			zbx_vector_ptr_append(&maintenance->periods, period);
311 	}
312 
313 	/* remove deleted maintenance tags */
314 
315 	for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
316 	{
317 		if (NULL == (period = (zbx_dc_maintenance_period_t *)zbx_hashset_search(&config->maintenance_periods,
318 				&rowid)))
319 		{
320 			continue;
321 		}
322 
323 		if (NULL != (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
324 				&period->maintenanceid)))
325 		{
326 			index = zbx_vector_ptr_search(&maintenance->periods, &period->timeperiodid,
327 					ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
328 
329 			if (FAIL != index)
330 				zbx_vector_ptr_remove_noorder(&maintenance->periods, index);
331 		}
332 
333 		zbx_hashset_remove_direct(&config->maintenance_periods, period);
334 	}
335 
336 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
337 }
338 
339 /******************************************************************************
340  *                                                                            *
341  * Function: DCsync_maintenance_groups                                        *
342  *                                                                            *
343  * Purpose: Updates maintenance groups in configuration cache                 *
344  *                                                                            *
345  * Parameters: sync - [IN] the db synchronization data                        *
346  *                                                                            *
347  * Comments: The result contains the following fields:                        *
348  *           0 - maintenanceid                                                *
349  *           1 - groupid                                                      *
350  *                                                                            *
351  ******************************************************************************/
DCsync_maintenance_groups(zbx_dbsync_t * sync)352 void	DCsync_maintenance_groups(zbx_dbsync_t *sync)
353 {
354 	char			**row;
355 	zbx_uint64_t		rowid;
356 	unsigned char		tag;
357 	zbx_dc_maintenance_t	*maintenance = NULL;
358 	int			index, ret;
359 	zbx_uint64_t		last_maintenanceid = 0, maintenanceid, groupid;
360 
361 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
362 
363 	while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
364 	{
365 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_TRUE;
366 
367 		/* removed rows will be always added at the end */
368 		if (ZBX_DBSYNC_ROW_REMOVE == tag)
369 			break;
370 
371 		ZBX_STR2UINT64(maintenanceid, row[0]);
372 
373 		if (last_maintenanceid != maintenanceid || 0 == last_maintenanceid)
374 		{
375 			if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
376 					&maintenanceid)))
377 			{
378 				continue;
379 			}
380 			last_maintenanceid = maintenanceid;
381 		}
382 
383 		ZBX_STR2UINT64(groupid, row[1]);
384 
385 		zbx_vector_uint64_append(&maintenance->groupids, groupid);
386 	}
387 
388 	/* remove deleted maintenance groupids from cache */
389 	for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
390 	{
391 		ZBX_STR2UINT64(maintenanceid, row[0]);
392 
393 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
394 				&maintenanceid)))
395 		{
396 			continue;
397 		}
398 		ZBX_STR2UINT64(groupid, row[1]);
399 
400 		if (FAIL == (index = zbx_vector_uint64_search(&maintenance->groupids, groupid,
401 				ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
402 		{
403 			continue;
404 		}
405 
406 		zbx_vector_uint64_remove_noorder(&maintenance->groupids, index);
407 	}
408 
409 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
410 }
411 
412 /******************************************************************************
413  *                                                                            *
414  * Function: DCsync_maintenance_hosts                                         *
415  *                                                                            *
416  * Purpose: Updates maintenance hosts in configuration cache                  *
417  *                                                                            *
418  * Parameters: sync - [IN] the db synchronization data                        *
419  *                                                                            *
420  * Comments: The result contains the following fields:                        *
421  *           0 - maintenanceid                                                *
422  *           1 - hostid                                                       *
423  *                                                                            *
424  ******************************************************************************/
DCsync_maintenance_hosts(zbx_dbsync_t * sync)425 void	DCsync_maintenance_hosts(zbx_dbsync_t *sync)
426 {
427 	char			**row;
428 	zbx_uint64_t		rowid;
429 	unsigned char		tag;
430 	zbx_vector_ptr_t	maintenances;
431 	zbx_dc_maintenance_t	*maintenance = NULL;
432 	int			index, ret, i;
433 	zbx_uint64_t		last_maintenanceid, maintenanceid, hostid;
434 
435 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
436 
437 	zbx_vector_ptr_create(&maintenances);
438 
439 	while (SUCCEED == (ret = zbx_dbsync_next(sync, &rowid, &row, &tag)))
440 	{
441 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_TRUE;
442 
443 		/* removed rows will be always added at the end */
444 		if (ZBX_DBSYNC_ROW_REMOVE == tag)
445 			break;
446 
447 		ZBX_STR2UINT64(maintenanceid, row[0]);
448 
449 		if (NULL == maintenance || last_maintenanceid != maintenanceid)
450 		{
451 			if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
452 					&maintenanceid)))
453 			{
454 				continue;
455 			}
456 			last_maintenanceid = maintenanceid;
457 		}
458 
459 		ZBX_STR2UINT64(hostid, row[1]);
460 
461 		zbx_vector_uint64_append(&maintenance->hostids, hostid);
462 		zbx_vector_ptr_append(&maintenances, maintenance);
463 	}
464 
465 	/* remove deleted maintenance hostids from cache */
466 	for (; SUCCEED == ret; ret = zbx_dbsync_next(sync, &rowid, &row, &tag))
467 	{
468 		ZBX_STR2UINT64(maintenanceid, row[0]);
469 
470 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
471 				&maintenanceid)))
472 		{
473 			continue;
474 		}
475 		ZBX_STR2UINT64(hostid, row[1]);
476 
477 		if (FAIL == (index = zbx_vector_uint64_search(&maintenance->hostids, hostid,
478 				ZBX_DEFAULT_UINT64_COMPARE_FUNC)))
479 		{
480 			continue;
481 		}
482 
483 		zbx_vector_uint64_remove_noorder(&maintenance->hostids, index);
484 		zbx_vector_ptr_append(&maintenances, maintenance);
485 	}
486 
487 	zbx_vector_ptr_sort(&maintenances, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
488 	zbx_vector_ptr_uniq(&maintenances, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
489 
490 	for (i = 0; i < maintenances.values_num; i++)
491 	{
492 		maintenance = (zbx_dc_maintenance_t *)maintenances.values[i];
493 		zbx_vector_uint64_sort(&maintenance->hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
494 	}
495 
496 	zbx_vector_ptr_destroy(&maintenances);
497 
498 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
499 }
500 
501 /******************************************************************************
502  *                                                                            *
503  * Function: dc_substract_time                                                *
504  *                                                                            *
505  * Purpose: substract two local times with DST correction                     *
506  *                                                                            *
507  * Parameter: minuend       - [IN] the minuend time                           *
508  *            subtrahend    - [IN] the subtrahend time (may be negative)      *
509  *            tm            - [OUT] the struct tm                             *
510  *                                                                            *
511  * Return value: the resulting time difference in seconds                     *
512  *                                                                            *
513  ******************************************************************************/
dc_substract_time(time_t minuend,int subtrahend,struct tm * tm)514 static time_t dc_substract_time(time_t minuend, int subtrahend, struct tm *tm)
515 {
516 	time_t	diff, offset_min, offset_diff;
517 
518 	offset_min = zbx_get_timezone_offset(minuend, tm);
519 	diff = minuend - subtrahend;
520 	offset_diff = zbx_get_timezone_offset(diff, tm);
521 	diff -= offset_diff - offset_min;
522 
523 	return diff;
524 }
525 
526 /******************************************************************************
527  *                                                                            *
528  * Function: dc_calculate_maintenance_period                                  *
529  *                                                                            *
530  * Purpose: calculate start time for the specified maintenance period         *
531  *                                                                            *
532  * Parameter: maintenance   - [IN] the maintenance                            *
533  *            period        - [IN] the maintenance period                     *
534  *            start_date    - [IN] the period starting timestamp based on     *
535  *                                 current time                               *
536  *            running_since - [OUT] the actual period starting timestamp      *
537  *            running_until - [OUT] the actual period ending timestamp        *
538  *                                                                            *
539  * Return value: SUCCEED - a valid period was found                           *
540  *               FAIL    - period started before maintenance activation time  *
541  *                                                                            *
542  ******************************************************************************/
dc_calculate_maintenance_period(const zbx_dc_maintenance_t * maintenance,const zbx_dc_maintenance_period_t * period,time_t start_date,time_t * running_since,time_t * running_until)543 static int	dc_calculate_maintenance_period(const zbx_dc_maintenance_t *maintenance,
544 		const zbx_dc_maintenance_period_t *period, time_t start_date, time_t *running_since,
545 		time_t *running_until)
546 {
547 	int		day, wday, week;
548 	struct tm	tm;
549 	time_t		active_since = maintenance->active_since;
550 
551 	if (TIMEPERIOD_TYPE_ONETIME == period->type)
552 	{
553 		*running_since = (period->start_date < active_since ? active_since : period->start_date);
554 		*running_until = period->start_date + period->period;
555 		if (maintenance->active_until < *running_until)
556 			*running_until = maintenance->active_until;
557 
558 		return SUCCEED;
559 	}
560 
561 	switch (period->type)
562 	{
563 		case TIMEPERIOD_TYPE_DAILY:
564 			if (start_date < active_since)
565 				return FAIL;
566 
567 			tm = *localtime(&active_since);
568 			active_since = dc_substract_time(active_since,
569 					tm.tm_hour * SEC_PER_HOUR + tm.tm_min * SEC_PER_MIN + tm.tm_sec, &tm);
570 
571 			day = (start_date - active_since) / SEC_PER_DAY;
572 			start_date = dc_substract_time(start_date, SEC_PER_DAY * (day % period->every), &tm);
573 			break;
574 		case TIMEPERIOD_TYPE_WEEKLY:
575 			if (start_date < active_since)
576 				return FAIL;
577 
578 			tm = *localtime(&active_since);
579 			wday = (0 == tm.tm_wday ? 7 : tm.tm_wday) - 1;
580 			active_since = dc_substract_time(active_since, wday * SEC_PER_DAY +
581 					tm.tm_hour * SEC_PER_HOUR + tm.tm_min * SEC_PER_MIN + tm.tm_sec, &tm);
582 
583 			for (; start_date >= active_since; start_date = dc_substract_time(start_date, SEC_PER_DAY, &tm))
584 			{
585 				/* check for every x week(s) */
586 				week = (start_date - active_since) / SEC_PER_WEEK;
587 				if (0 != week % period->every)
588 					continue;
589 
590 				/* check for day of the week */
591 				tm = *localtime(&start_date);
592 				wday = (0 == tm.tm_wday ? 7 : tm.tm_wday) - 1;
593 				if (0 == (period->dayofweek & (1 << wday)))
594 					continue;
595 
596 				break;
597 			}
598 			break;
599 		case TIMEPERIOD_TYPE_MONTHLY:
600 			for (; start_date >= active_since; start_date = dc_substract_time(start_date, SEC_PER_DAY, &tm))
601 			{
602 				/* check for month */
603 				tm = *localtime(&start_date);
604 				if (0 == (period->month & (1 << tm.tm_mon)))
605 					continue;
606 
607 				if (0 != period->day)
608 				{
609 					/* check for day of the month */
610 					if (period->day != tm.tm_mday)
611 						continue;
612 				}
613 				else
614 				{
615 					/* check for day of the week */
616 					wday = (0 == tm.tm_wday ? 7 : tm.tm_wday) - 1;
617 					if (0 == (period->dayofweek & (1 << wday)))
618 						continue;
619 
620 					/* check for number of day (first, second, third, fourth or last) */
621 					day = (tm.tm_mday - 1) / 7 + 1;
622 					if (5 == period->every && 4 == day)
623 					{
624 						if (tm.tm_mday + 7 <= zbx_day_in_month(1900 + tm.tm_year,
625 								tm.tm_mon + 1))
626 						{
627 							continue;
628 						}
629 					}
630 					else if (period->every != day)
631 						continue;
632 				}
633 
634 				if (start_date < active_since)
635 					return FAIL;
636 
637 				break;
638 			}
639 			break;
640 		default:
641 			return FAIL;
642 	}
643 
644 	*running_since = start_date;
645 	*running_until = start_date + period->period;
646 	if (maintenance->active_until < *running_until)
647 		*running_until = maintenance->active_until;
648 
649 	return SUCCEED;
650 }
651 
652 /******************************************************************************
653  *                                                                            *
654  * Function: dc_check_maintenance_period                                      *
655  *                                                                            *
656  * Purpose: calculates start time for the specified maintenance period and    *
657  *          checks if we are inside the maintenance period                    *
658  *                                                                            *
659  * Parameter: maintenance   - [IN] the maintenance                            *
660  *            period        - [IN] the maintenance period                     *
661  *            now           - [IN] current time                               *
662  *            running_since - [OUT] the actual period starting timestamp      *
663  *            running_until - [OUT] the actual period ending timestamp        *
664  *                                                                            *
665  * Return value: SUCCEED - current time is inside valid maintenance period    *
666  *               FAIL    - current time is outside valid maintenance period   *
667  *                                                                            *
668  ******************************************************************************/
dc_check_maintenance_period(const zbx_dc_maintenance_t * maintenance,const zbx_dc_maintenance_period_t * period,time_t now,time_t * running_since,time_t * running_until)669 static int	dc_check_maintenance_period(const zbx_dc_maintenance_t *maintenance,
670 		const zbx_dc_maintenance_period_t *period, time_t now, time_t *running_since, time_t *running_until)
671 {
672 	struct tm	tm;
673 	int		seconds, rc, ret = FAIL;
674 	time_t		period_start, period_end;
675 
676 	tm = *localtime(&now);
677 	seconds = tm.tm_hour * SEC_PER_HOUR + tm.tm_min * SEC_PER_MIN + tm.tm_sec;
678 	period_start = dc_substract_time(now, seconds, &tm);
679 	period_start = dc_substract_time(period_start, -period->start_time, &tm);
680 
681 	tm = *localtime(&period_start);
682 
683 	/* skip maintenance if the time does not exist due to DST */
684 	if (period->start_time != (tm.tm_hour * SEC_PER_HOUR + tm.tm_min * SEC_PER_MIN + tm.tm_sec))
685 	{
686 		goto out;
687 	}
688 
689 	if (now < period_start)
690 		period_start = dc_substract_time(period_start, SEC_PER_DAY, &tm);
691 
692 	rc = dc_calculate_maintenance_period(maintenance, period, period_start, &period_start, &period_end);
693 
694 	if (SUCCEED == rc && period_start <= now && now < period_end)
695 	{
696 		*running_since = period_start;
697 		*running_until = period_end;
698 		ret = SUCCEED;
699 	}
700 out:
701 	return ret;
702 }
703 
704 /******************************************************************************
705  *                                                                            *
706  * Function: zbx_dc_maintenance_set_update_flags                              *
707  *                                                                            *
708  * Purpose: sets maintenance update flags for all timers                      *
709  *                                                                            *
710  ******************************************************************************/
zbx_dc_maintenance_set_update_flags(void)711 void	zbx_dc_maintenance_set_update_flags(void)
712 {
713 	int	slots_num = ZBX_MAINTENANCE_UPDATE_FLAGS_NUM(), timers_left;
714 
715 	WRLOCK_CACHE;
716 
717 	memset(config->maintenance_update_flags, 0xff, sizeof(zbx_uint64_t) * slots_num);
718 
719 	if (0 != (timers_left = (CONFIG_TIMER_FORKS % (sizeof(uint64_t) * 8))))
720 		config->maintenance_update_flags[slots_num - 1] >>= (sizeof(zbx_uint64_t) * 8 - timers_left);
721 
722 	UNLOCK_CACHE;
723 }
724 
725 /******************************************************************************
726  *                                                                            *
727  * Function: zbx_dc_maintenance_reset_update_flag                             *
728  *                                                                            *
729  * Purpose: resets maintenance update flags for the specified timer           *
730  *                                                                            *
731  * Parameters: timer - [IN] the timer process number                          *
732  *                                                                            *
733  ******************************************************************************/
zbx_dc_maintenance_reset_update_flag(int timer)734 void	zbx_dc_maintenance_reset_update_flag(int timer)
735 {
736 	int		slot, bit;
737 	zbx_uint64_t	mask;
738 
739 	timer--;
740 	slot = timer / (sizeof(uint64_t) * 8);
741 	bit = timer % (sizeof(uint64_t) * 8);
742 
743 	mask = ~(__UINT64_C(1) << bit);
744 
745 	WRLOCK_CACHE;
746 
747 	config->maintenance_update_flags[slot] &= mask;
748 
749 	UNLOCK_CACHE;
750 }
751 
752 /******************************************************************************
753  *                                                                            *
754  * Function: zbx_dc_maintenance_check_update_flag                             *
755  *                                                                            *
756  * Purpose: checks if the maintenance update flag is set for the specified    *
757  *          timer                                                             *
758  *                                                                            *
759  * Parameters: timer - [IN] the timer process number                          *
760  *                                                                            *
761  * Return value: SUCCEED - maintenance update flag is set                     *
762  *               FAIL    - otherwise                                          *
763  ******************************************************************************/
zbx_dc_maintenance_check_update_flag(int timer)764 int	zbx_dc_maintenance_check_update_flag(int timer)
765 {
766 	int		slot, bit, ret;
767 	zbx_uint64_t	mask;
768 
769 	timer--;
770 	slot = timer / (sizeof(uint64_t) * 8);
771 	bit = timer % (sizeof(uint64_t) * 8);
772 
773 	mask = __UINT64_C(1) << bit;
774 
775 	RDLOCK_CACHE;
776 
777 	ret = (0 == (config->maintenance_update_flags[slot] & mask) ? FAIL : SUCCEED);
778 
779 	UNLOCK_CACHE;
780 
781 	return ret;
782 }
783 
784 /******************************************************************************
785  *                                                                            *
786  * Function: zbx_dc_maintenance_check_update_flags                            *
787  *                                                                            *
788  * Purpose: checks if at least one maintenance update flag is set             *
789  *                                                                            *
790  * Return value: SUCCEED - a maintenance update flag is set                   *
791  *               FAIL    - otherwise                                          *
792  *                                                                            *
793  ******************************************************************************/
zbx_dc_maintenance_check_update_flags(void)794 int	zbx_dc_maintenance_check_update_flags(void)
795 {
796 	int	slots_num = ZBX_MAINTENANCE_UPDATE_FLAGS_NUM(), ret = SUCCEED;
797 
798 	RDLOCK_CACHE;
799 
800 	if (0 != config->maintenance_update_flags[0])
801 		goto out;
802 
803 	if (1 != slots_num)
804 	{
805 		if (0 != memcmp(config->maintenance_update_flags, config->maintenance_update_flags + 1, slots_num - 1))
806 			goto out;
807 	}
808 
809 	ret = FAIL;
810 out:
811 	UNLOCK_CACHE;
812 
813 	return ret;
814 }
815 
816 /******************************************************************************
817  *                                                                            *
818  * Function: zbx_dc_update_maintenances                                       *
819  *                                                                            *
820  * Purpose: update maintenance state depending on maintenance periods         *
821  *                                                                            *
822  * Return value: SUCCEED - maintenance status was changed, host/event update  *
823  *                         must be performed                                  *
824  *               FAIL    - otherwise                                          *
825  *                                                                            *
826  * Comments: This function calculates if any maintenance period is running    *
827  *           and based on that sets current maintenance state - running/idle  *
828  *           and period start/end time.                                       *
829  *                                                                            *
830  ******************************************************************************/
zbx_dc_update_maintenances(void)831 int	zbx_dc_update_maintenances(void)
832 {
833 	zbx_dc_maintenance_t		*maintenance;
834 	zbx_dc_maintenance_period_t	*period;
835 	zbx_hashset_iter_t		iter;
836 	int				i, running_num = 0, started_num = 0, stopped_num = 0, ret = FAIL;
837 	unsigned char			state;
838 	time_t				now, period_start, period_end, running_since, running_until;
839 
840 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
841 
842 	now = time(NULL);
843 
844 	WRLOCK_CACHE;
845 
846 	if (ZBX_MAINTENANCE_UPDATE_TRUE == config->maintenance_update)
847 	{
848 		ret = SUCCEED;
849 		config->maintenance_update = ZBX_MAINTENANCE_UPDATE_FALSE;
850 	}
851 
852 	zbx_hashset_iter_reset(&config->maintenances, &iter);
853 	while (NULL != (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_iter_next(&iter)))
854 	{
855 		state = ZBX_MAINTENANCE_IDLE;
856 		running_since = 0;
857 		running_until = 0;
858 
859 		if (now >= maintenance->active_since && now < maintenance->active_until)
860 		{
861 			/* find the longest running maintenance period */
862 			for (i = 0; i < maintenance->periods.values_num; i++)
863 			{
864 				period = (zbx_dc_maintenance_period_t *)maintenance->periods.values[i];
865 
866 				if (SUCCEED == dc_check_maintenance_period(maintenance, period, now, &period_start,
867 						&period_end))
868 				{
869 					state = ZBX_MAINTENANCE_RUNNING;
870 					if (period_end > running_until)
871 					{
872 						running_since = period_start;
873 						running_until = period_end;
874 					}
875 				}
876 			}
877 		}
878 
879 		if (state == ZBX_MAINTENANCE_RUNNING)
880 		{
881 			if (ZBX_MAINTENANCE_IDLE == maintenance->state)
882 			{
883 				maintenance->running_since = running_since;
884 				maintenance->state = ZBX_MAINTENANCE_RUNNING;
885 				started_num++;
886 
887 				/* Precache nested host groups for started maintenances.   */
888 				/* Nested host groups for running maintenances are already */
889 				/* precached during configuration cache synchronization.   */
890 				for (i = 0; i < maintenance->groupids.values_num; i++)
891 				{
892 					zbx_dc_hostgroup_t	*group;
893 
894 					if (NULL != (group = (zbx_dc_hostgroup_t *)zbx_hashset_search(
895 							&config->hostgroups, &maintenance->groupids.values[i])))
896 					{
897 						dc_hostgroup_cache_nested_groupids(group);
898 					}
899 				}
900 				ret = SUCCEED;
901 			}
902 
903 			if (maintenance->running_until != running_until)
904 			{
905 				maintenance->running_until = running_until;
906 				ret = SUCCEED;
907 			}
908 			running_num++;
909 		}
910 		else
911 		{
912 			if (ZBX_MAINTENANCE_RUNNING == maintenance->state)
913 			{
914 				maintenance->running_since = 0;
915 				maintenance->running_until = 0;
916 				maintenance->state = ZBX_MAINTENANCE_IDLE;
917 				stopped_num++;
918 				ret = SUCCEED;
919 			}
920 		}
921 	}
922 
923 	UNLOCK_CACHE;
924 
925 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() started:%d stopped:%d running:%d", __func__,
926 			started_num, stopped_num, running_num);
927 
928 	return ret;
929 }
930 
931 /******************************************************************************
932  *                                                                            *
933  * Function: dc_assign_maintenance_to_host                                    *
934  *                                                                            *
935  * Purpose: assign maintenance to a host, host can only be in one maintenance *
936  *                                                                            *
937  * Parameters: host_maintenances - [OUT] host with maintenance                *
938  *             maintenance       - [IN] maintenance that host is in           *
939  *             hostid            - [IN] ID of the host                        *
940  *                                                                            *
941  ******************************************************************************/
dc_assign_maintenance_to_host(zbx_hashset_t * host_maintenances,zbx_dc_maintenance_t * maintenance,zbx_uint64_t hostid)942 static void	dc_assign_maintenance_to_host(zbx_hashset_t *host_maintenances, zbx_dc_maintenance_t *maintenance,
943 		zbx_uint64_t hostid)
944 {
945 	zbx_host_maintenance_t	*host_maintenance, host_maintenance_local;
946 
947 	if (NULL == (host_maintenance = (zbx_host_maintenance_t *)zbx_hashset_search(host_maintenances, &hostid)))
948 	{
949 		host_maintenance_local.hostid = hostid;
950 		host_maintenance_local.maintenance = maintenance;
951 
952 		zbx_hashset_insert(host_maintenances, &host_maintenance_local, sizeof(host_maintenance_local));
953 	}
954 	else if (MAINTENANCE_TYPE_NORMAL == host_maintenance->maintenance->type &&
955 			MAINTENANCE_TYPE_NODATA == maintenance->type)
956 	{
957 		host_maintenance->maintenance = maintenance;
958 	}
959 }
960 
961 /******************************************************************************
962  *                                                                            *
963  * Function: dc_assign_event_maintenance_to_host                              *
964  *                                                                            *
965  * Purpose: assign maintenance to a host that event belongs to, events can be *
966  *          in multiple maintenances at a time                                *
967  *                                                                            *
968  * Parameters: host_event_maintenances - [OUT] host with maintenances         *
969  *             maintenance             - [IN] maintenance that host is in     *
970  *             hostid                  - [IN] ID of the host                  *
971  *                                                                            *
972  ******************************************************************************/
dc_assign_event_maintenance_to_host(zbx_hashset_t * host_event_maintenances,zbx_dc_maintenance_t * maintenance,zbx_uint64_t hostid)973 static void	dc_assign_event_maintenance_to_host(zbx_hashset_t *host_event_maintenances,
974 		zbx_dc_maintenance_t *maintenance, zbx_uint64_t hostid)
975 {
976 	zbx_host_event_maintenance_t	*host_event_maintenance, host_event_maintenance_local;
977 
978 	if (NULL == (host_event_maintenance = (zbx_host_event_maintenance_t *)zbx_hashset_search(
979 			host_event_maintenances, &hostid)))
980 	{
981 		host_event_maintenance_local.hostid = hostid;
982 		zbx_vector_ptr_create(&host_event_maintenance_local.maintenances);
983 		zbx_vector_ptr_append(&host_event_maintenance_local.maintenances, maintenance);
984 
985 		zbx_hashset_insert(host_event_maintenances, &host_event_maintenance_local,
986 				sizeof(host_event_maintenance_local));
987 		return;
988 	}
989 
990 	zbx_vector_ptr_append(&host_event_maintenance->maintenances, maintenance);
991 }
992 
993 typedef void	(*assign_maintenance_to_host_f)(zbx_hashset_t *host_maintenances,
994 		zbx_dc_maintenance_t *maintenance, zbx_uint64_t hostid);
995 
996 /******************************************************************************
997  *                                                                            *
998  * Function: dc_get_host_maintenances_by_ids                                  *
999  *                                                                            *
1000  * Purpose: get hosts and their maintenances                                  *
1001  *                                                                            *
1002  * Parameters: maintenanceids    - [IN] the maintenance ids                   *
1003  *             host_maintenances - [OUT] the maintenances running on hosts    *
1004  *             cb                - [IN] callback function                     *
1005  *                                                                            *
1006  ******************************************************************************/
dc_get_host_maintenances_by_ids(const zbx_vector_uint64_t * maintenanceids,zbx_hashset_t * host_maintenances,assign_maintenance_to_host_f cb)1007 static void	dc_get_host_maintenances_by_ids(const zbx_vector_uint64_t *maintenanceids,
1008 		zbx_hashset_t *host_maintenances, assign_maintenance_to_host_f cb)
1009 {
1010 	zbx_dc_maintenance_t	*maintenance;
1011 	int			i, j;
1012 	zbx_vector_uint64_t	groupids;
1013 
1014 	zbx_vector_uint64_create(&groupids);
1015 
1016 	for (i = 0; i < maintenanceids->values_num; i++)
1017 	{
1018 		if (NULL == (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_search(&config->maintenances,
1019 				&maintenanceids->values[i])))
1020 		{
1021 			continue;
1022 		}
1023 
1024 		for (j = 0; j < maintenance->hostids.values_num; j++)
1025 			cb(host_maintenances, maintenance, maintenance->hostids.values[j]);
1026 
1027 		if (0 != maintenance->groupids.values_num)	/* hosts groups */
1028 		{
1029 			zbx_dc_hostgroup_t	*group;
1030 
1031 			for (j = 0; j < maintenance->groupids.values_num; j++)
1032 				dc_get_nested_hostgroupids(maintenance->groupids.values[j], &groupids);
1033 
1034 			zbx_vector_uint64_sort(&groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1035 			zbx_vector_uint64_uniq(&groupids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1036 
1037 			for (j = 0; j < groupids.values_num; j++)
1038 			{
1039 				zbx_hashset_iter_t	iter;
1040 				zbx_uint64_t		*phostid;
1041 
1042 				if (NULL == (group = (zbx_dc_hostgroup_t *)zbx_hashset_search(&config->hostgroups,
1043 						&groupids.values[j])))
1044 				{
1045 					continue;
1046 				}
1047 
1048 				zbx_hashset_iter_reset(&group->hostids, &iter);
1049 
1050 				while (NULL != (phostid = (zbx_uint64_t *)zbx_hashset_iter_next(&iter)))
1051 					cb(host_maintenances, maintenance, *phostid);
1052 			}
1053 
1054 			zbx_vector_uint64_clear(&groupids);
1055 		}
1056 	}
1057 
1058 	zbx_vector_uint64_destroy(&groupids);
1059 }
1060 
1061 /******************************************************************************
1062  *                                                                            *
1063  * Function: dc_get_host_maintenance_updates                                  *
1064  *                                                                            *
1065  * Purpose: gets maintenance updates for all hosts                            *
1066  *                                                                            *
1067  * Parameters: host_maintenances - [IN] the maintenances running on hosts     *
1068  *             updates           - [OUT] updates to be applied                *
1069  *                                                                            *
1070  ******************************************************************************/
dc_get_host_maintenance_updates(zbx_hashset_t * host_maintenances,zbx_vector_ptr_t * updates)1071 static void	dc_get_host_maintenance_updates(zbx_hashset_t *host_maintenances, zbx_vector_ptr_t *updates)
1072 {
1073 	zbx_hashset_iter_t		iter;
1074 	ZBX_DC_HOST			*host;
1075 	int				maintenance_from;
1076 	unsigned char			maintenance_status, maintenance_type;
1077 	zbx_uint64_t			maintenanceid;
1078 	zbx_host_maintenance_diff_t	*diff;
1079 	unsigned int			flags;
1080 	const zbx_host_maintenance_t	*host_maintenance;
1081 
1082 	zbx_hashset_iter_reset(&config->hosts, &iter);
1083 	while (NULL != (host = (ZBX_DC_HOST *)zbx_hashset_iter_next(&iter)))
1084 	{
1085 		if (HOST_STATUS_PROXY_ACTIVE == host->status || HOST_STATUS_PROXY_PASSIVE == host->status)
1086 			continue;
1087 
1088 		if (NULL != (host_maintenance = zbx_hashset_search(host_maintenances, &host->hostid)))
1089 		{
1090 			maintenance_status = HOST_MAINTENANCE_STATUS_ON;
1091 			maintenance_type = host_maintenance->maintenance->type;
1092 			maintenanceid = host_maintenance->maintenance->maintenanceid;
1093 			maintenance_from = host_maintenance->maintenance->running_since;
1094 		}
1095 		else
1096 		{
1097 			maintenance_status = HOST_MAINTENANCE_STATUS_OFF;
1098 			maintenance_type = MAINTENANCE_TYPE_NORMAL;
1099 			maintenanceid = 0;
1100 			maintenance_from = 0;
1101 		}
1102 
1103 		flags = 0;
1104 
1105 		if (maintenanceid != host->maintenanceid)
1106 			flags |= ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCEID;
1107 
1108 		if (maintenance_status != host->maintenance_status)
1109 			flags |= ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_STATUS;
1110 
1111 		if (maintenance_from != host->maintenance_from)
1112 			flags |= ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_FROM;
1113 
1114 		if (maintenance_type != host->maintenance_type)
1115 			flags |= ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_TYPE;
1116 
1117 		if (0 != flags)
1118 		{
1119 			diff = (zbx_host_maintenance_diff_t *)zbx_malloc(0, sizeof(zbx_host_maintenance_diff_t));
1120 			diff->flags = flags;
1121 			diff->hostid = host->hostid;
1122 			diff->maintenanceid = maintenanceid;
1123 			diff->maintenance_status = maintenance_status;
1124 			diff->maintenance_from = maintenance_from;
1125 			diff->maintenance_type = maintenance_type;
1126 			zbx_vector_ptr_append(updates, diff);
1127 		}
1128 	}
1129 }
1130 
1131 /******************************************************************************
1132  *                                                                            *
1133  * Function: zbx_dc_flush_host_maintenance_updates                            *
1134  *                                                                            *
1135  * Purpose: flush host maintenance updates to configuration cache             *
1136  *                                                                            *
1137  * Parameters: updates - [IN] the updates to flush                            *
1138  *                                                                            *
1139  ******************************************************************************/
zbx_dc_flush_host_maintenance_updates(const zbx_vector_ptr_t * updates)1140 void	zbx_dc_flush_host_maintenance_updates(const zbx_vector_ptr_t *updates)
1141 {
1142 	int					i;
1143 	const zbx_host_maintenance_diff_t	*diff;
1144 	ZBX_DC_HOST				*host;
1145 	int					now;
1146 
1147 	now = time(NULL);
1148 
1149 	WRLOCK_CACHE;
1150 
1151 	for (i = 0; i < updates->values_num; i++)
1152 	{
1153 		int	maintenance_without_data = 0;
1154 
1155 		diff = (zbx_host_maintenance_diff_t *)updates->values[i];
1156 
1157 		if (NULL == (host = (ZBX_DC_HOST *)zbx_hashset_search(&config->hosts, &diff->hostid)))
1158 			continue;
1159 
1160 		if (HOST_MAINTENANCE_STATUS_ON == host->maintenance_status &&
1161 				MAINTENANCE_TYPE_NODATA == host->maintenance_type)
1162 		{
1163 			maintenance_without_data = 1;
1164 		}
1165 
1166 		if (0 != (diff->flags & ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCEID))
1167 			host->maintenanceid = diff->maintenanceid;
1168 
1169 		if (0 != (diff->flags & ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_TYPE))
1170 			host->maintenance_type = diff->maintenance_type;
1171 
1172 		if (0 != (diff->flags & ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_STATUS))
1173 			host->maintenance_status = diff->maintenance_status;
1174 
1175 		if (0 != (diff->flags & ZBX_FLAG_HOST_MAINTENANCE_UPDATE_MAINTENANCE_FROM))
1176 			host->maintenance_from = diff->maintenance_from;
1177 
1178 		if (1 == maintenance_without_data && (HOST_MAINTENANCE_STATUS_ON != host->maintenance_status ||
1179 				MAINTENANCE_TYPE_NODATA != host->maintenance_type))
1180 		{
1181 			/* Store time at which no-data maintenance ended for the host (either */
1182 			/* because no-data maintenance ended or because maintenance type was  */
1183 			/* changed to normal), this is needed for nodata() trigger function.  */
1184 			host->data_expected_from = now;
1185 		}
1186 	}
1187 
1188 	UNLOCK_CACHE;
1189 }
1190 
1191 /******************************************************************************
1192  *                                                                            *
1193  * Function: zbx_dc_get_host_maintenance_updates                              *
1194  *                                                                            *
1195  * Purpose: calculates required host maintenance updates based on specified   *
1196  *          maintenances                                                      *
1197  *                                                                            *
1198  * Parameters: maintenanceids   - [IN] identifiers of the maintenances to     *
1199  *                                process                                     *
1200  *             updates          - [OUT] pending updates                       *
1201  *                                                                            *
1202  * Comments: This function must be called after zbx_dc_update_maintenances()  *
1203  *           function has updated maintenance state in configuration cache.   *
1204  *           To be able to work with lazy nested group caching and read locks *
1205  *           all nested groups used in maintenances must be already precached *
1206  *           before calling this function.                                    *
1207  *                                                                            *
1208  ******************************************************************************/
zbx_dc_get_host_maintenance_updates(const zbx_vector_uint64_t * maintenanceids,zbx_vector_ptr_t * updates)1209 void	zbx_dc_get_host_maintenance_updates(const zbx_vector_uint64_t *maintenanceids, zbx_vector_ptr_t *updates)
1210 {
1211 	zbx_hashset_t	host_maintenances;
1212 
1213 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1214 
1215 	zbx_hashset_create(&host_maintenances, maintenanceids->values_num, ZBX_DEFAULT_UINT64_HASH_FUNC,
1216 			ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1217 
1218 	RDLOCK_CACHE;
1219 
1220 	dc_get_host_maintenances_by_ids(maintenanceids, &host_maintenances, dc_assign_maintenance_to_host);
1221 
1222 	/* host maintenance update must be performed even without running maintenances */
1223 	/* to reset host maintenances status for stopped maintenances                  */
1224 	dc_get_host_maintenance_updates(&host_maintenances, updates);
1225 
1226 	UNLOCK_CACHE;
1227 
1228 	zbx_hashset_destroy(&host_maintenances);
1229 
1230 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s() updates:%d", __func__, updates->values_num);
1231 }
1232 
1233 /******************************************************************************
1234  *                                                                            *
1235  * Function: dc_maintenance_tag_match                                         *
1236  *                                                                            *
1237  * Purpose: perform maintenance tag comparison using maintenance tag operator *
1238  *                                                                            *
1239  ******************************************************************************/
dc_maintenance_tag_value_match(const zbx_dc_maintenance_tag_t * mt,const zbx_tag_t * tag)1240 static int	dc_maintenance_tag_value_match(const zbx_dc_maintenance_tag_t *mt, const zbx_tag_t *tag)
1241 {
1242 	switch (mt->op)
1243 	{
1244 		case ZBX_MAINTENANCE_TAG_OPERATOR_LIKE:
1245 			return (NULL != strstr(tag->value, mt->value) ? SUCCEED : FAIL);
1246 		case ZBX_MAINTENANCE_TAG_OPERATOR_EQUAL:
1247 			return (0 == strcmp(tag->value, mt->value) ? SUCCEED : FAIL);
1248 		default:
1249 			THIS_SHOULD_NEVER_HAPPEN;
1250 			return FAIL;
1251 	}
1252 }
1253 
1254 /******************************************************************************
1255  *                                                                            *
1256  * Function: dc_maintenance_match_tag_range                                   *
1257  *                                                                            *
1258  * Purpose: matches tags with [*mt_pos] maintenance tag name                  *
1259  *                                                                            *
1260  * Parameters: mtags    - [IN] the maintenance tags, sorted by tag names      *
1261  *             etags    - [IN] the event tags, sorted by tag names            *
1262  *             mt_pos   - [IN/OUT] the next maintenance tag index             *
1263  *             et_pos   - [IN/OUT] the next event tag index                   *
1264  *                                                                            *
1265  * Return value: SUCCEED - found matching tag                                 *
1266  *               FAIL    - no matching tags found                             *
1267  *                                                                            *
1268  ******************************************************************************/
dc_maintenance_match_tag_range(const zbx_vector_ptr_t * mtags,const zbx_vector_ptr_t * etags,int * mt_pos,int * et_pos)1269 static int	dc_maintenance_match_tag_range(const zbx_vector_ptr_t *mtags, const zbx_vector_ptr_t *etags,
1270 		int *mt_pos, int *et_pos)
1271 {
1272 	const zbx_dc_maintenance_tag_t	*mtag;
1273 	const zbx_tag_t			*etag;
1274 	const char			*name;
1275 	int				i, j, ret, mt_start, mt_end, et_start, et_end;
1276 
1277 	/* get the maintenance tag name */
1278 	mtag = (const zbx_dc_maintenance_tag_t *)mtags->values[*mt_pos];
1279 	name = mtag->tag;
1280 
1281 	/* find maintenance and event tag ranges matching the first maintenance tag name */
1282 	/* (maintenance tag range [mt_start,mt_end], event tag range [et_start,et_end])  */
1283 
1284 	mt_start = *mt_pos;
1285 	et_start = *et_pos;
1286 
1287 	/* find last maintenance tag with the required name */
1288 
1289 	for (i = mt_start + 1; i < mtags->values_num; i++)
1290 	{
1291 		mtag = (const zbx_dc_maintenance_tag_t *)mtags->values[i];
1292 		if (0 != strcmp(mtag->tag, name))
1293 			break;
1294 	}
1295 	mt_end = i - 1;
1296 	*mt_pos = i;
1297 
1298 	/* find first event tag with the required name */
1299 
1300 	for (i = et_start; i < etags->values_num; i++)
1301 	{
1302 		etag = (const zbx_tag_t *)etags->values[i];
1303 		if (0 < (ret = strcmp(etag->tag, name)))
1304 		{
1305 			*et_pos = i;
1306 			return FAIL;
1307 		}
1308 
1309 		if (0 == ret)
1310 			break;
1311 	}
1312 
1313 	if (i == etags->values_num)
1314 	{
1315 		*et_pos = i;
1316 		return FAIL;
1317 	}
1318 
1319 	et_start = i++;
1320 
1321 	/* find last event tag with the required name */
1322 
1323 	for (; i < etags->values_num; i++)
1324 	{
1325 		etag = (const zbx_tag_t *)etags->values[i];
1326 		if (0 != strcmp(etag->tag, name))
1327 			break;
1328 	}
1329 
1330 	et_end = i - 1;
1331 	*et_pos = i;
1332 
1333 	/* cross-compare maintenance and event tags within the found ranges */
1334 
1335 	for (i = mt_start; i <= mt_end; i++)
1336 	{
1337 		mtag = (const zbx_dc_maintenance_tag_t *)mtags->values[i];
1338 
1339 		for (j = et_start; j <= et_end; j++)
1340 		{
1341 			etag = (const zbx_tag_t *)etags->values[j];
1342 			if (SUCCEED == dc_maintenance_tag_value_match(mtag, etag))
1343 				return SUCCEED;
1344 		}
1345 	}
1346 
1347 	return FAIL;
1348 }
1349 
1350 /******************************************************************************
1351  *                                                                            *
1352  * Function: dc_maintenance_match_tags_or                                     *
1353  *                                                                            *
1354  * Purpose: matches maintenance and event tags using OR eval type             *
1355  *                                                                            *
1356  * Parameters: mtags    - [IN] the maintenance tags, sorted by tag names      *
1357  *             etags    - [IN] the event tags, sorted by tag names            *
1358  *                                                                            *
1359  * Return value: SUCCEED - event tags matches maintenance                     *
1360  *               FAIL    - otherwise                                          *
1361  *                                                                            *
1362  ******************************************************************************/
dc_maintenance_match_tags_or(const zbx_dc_maintenance_t * maintenance,const zbx_vector_ptr_t * tags)1363 static int	dc_maintenance_match_tags_or(const zbx_dc_maintenance_t *maintenance, const zbx_vector_ptr_t *tags)
1364 {
1365 	int	mt_pos = 0, et_pos = 0;
1366 
1367 	while (mt_pos < maintenance->tags.values_num && et_pos < tags->values_num)
1368 	{
1369 		if (SUCCEED == dc_maintenance_match_tag_range(&maintenance->tags, tags, &mt_pos, &et_pos))
1370 			return SUCCEED;
1371 	}
1372 
1373 	return FAIL;
1374 }
1375 
1376 /******************************************************************************
1377  *                                                                            *
1378  * Function: dc_maintenance_match_tags_andor                                  *
1379  *                                                                            *
1380  * Purpose: matches maintenance and event tags using AND/OR eval type         *
1381  *                                                                            *
1382  * Parameters: mtags    - [IN] the maintenance tags, sorted by tag names      *
1383  *             etags    - [IN] the event tags, sorted by tag names            *
1384  *                                                                            *
1385  * Return value: SUCCEED - event tags matches maintenance                     *
1386  *               FAIL    - otherwise                                          *
1387  *                                                                            *
1388  ******************************************************************************/
dc_maintenance_match_tags_andor(const zbx_dc_maintenance_t * maintenance,const zbx_vector_ptr_t * tags)1389 static int	dc_maintenance_match_tags_andor(const zbx_dc_maintenance_t *maintenance, const zbx_vector_ptr_t *tags)
1390 {
1391 	int	mt_pos = 0, et_pos = 0;
1392 
1393 	while (mt_pos < maintenance->tags.values_num && et_pos < tags->values_num)
1394 	{
1395 		if (FAIL == dc_maintenance_match_tag_range(&maintenance->tags, tags, &mt_pos, &et_pos))
1396 			return FAIL;
1397 	}
1398 
1399 	if (mt_pos != maintenance->tags.values_num)
1400 		return FAIL;
1401 
1402 	return SUCCEED;
1403 }
1404 
1405 /******************************************************************************
1406  *                                                                            *
1407  * Function: dc_maintenance_match_tags                                        *
1408  *                                                                            *
1409  * Purpose: check if the tags must be processed by the specified maintenance  *
1410  *                                                                            *
1411  * Parameters: maintenance - [IN] the maintenance                             *
1412  *             tags        - [IN] the tags to check                           *
1413  *                                                                            *
1414  * Return value: SUCCEED - the tags must be processed by the maintenance      *
1415  *               FAIL    - otherwise                                          *
1416  *                                                                            *
1417  ******************************************************************************/
dc_maintenance_match_tags(const zbx_dc_maintenance_t * maintenance,const zbx_vector_ptr_t * tags)1418 static int	dc_maintenance_match_tags(const zbx_dc_maintenance_t *maintenance, const zbx_vector_ptr_t *tags)
1419 {
1420 	switch (maintenance->tags_evaltype)
1421 	{
1422 		case MAINTENANCE_TAG_EVAL_TYPE_AND_OR:
1423 			/* break; is not missing here */
1424 		case MAINTENANCE_TAG_EVAL_TYPE_OR:
1425 			if (0 == maintenance->tags.values_num)
1426 				return SUCCEED;
1427 
1428 			if (0 == tags->values_num)
1429 				return FAIL;
1430 			break;
1431 		default:
1432 			THIS_SHOULD_NEVER_HAPPEN;
1433 			return FAIL;
1434 	}
1435 
1436 	if (MAINTENANCE_TAG_EVAL_TYPE_AND_OR == maintenance->tags_evaltype)
1437 		return dc_maintenance_match_tags_andor(maintenance, tags);
1438 	else
1439 		return dc_maintenance_match_tags_or(maintenance, tags);
1440 }
1441 
1442 /******************************************************************************
1443  *                                                                            *
1444  * Function: dc_compare_tags                                                  *
1445  *                                                                            *
1446  * Purpose: compare maintenance tags by tag name for sorting                  *
1447  *                                                                            *
1448  ******************************************************************************/
dc_compare_tags(const void * d1,const void * d2)1449 static int	dc_compare_tags(const void *d1, const void *d2)
1450 {
1451 	const zbx_tag_t	*tag1 = *(const zbx_tag_t **)d1;
1452 	const zbx_tag_t	*tag2 = *(const zbx_tag_t **)d2;
1453 
1454 	return strcmp(tag1->tag, tag2->tag);
1455 }
1456 
host_event_maintenance_clean(zbx_host_event_maintenance_t * host_event_maintenance)1457 static void	host_event_maintenance_clean(zbx_host_event_maintenance_t *host_event_maintenance)
1458 {
1459 	zbx_vector_ptr_destroy(&host_event_maintenance->maintenances);
1460 }
1461 
1462 /******************************************************************************
1463  *                                                                            *
1464  * Function: zbx_dc_get_event_maintenances                                    *
1465  *                                                                            *
1466  * Purpose: get maintenance data for events                                   *
1467  *                                                                            *
1468  * Parameters: event_queries -  [IN/OUT] in - event data                      *
1469  *                                       out - running maintenances for each  *
1470  *                                            event                           *
1471  *             maintenanceids - [IN] the maintenances to process              *
1472  *                                                                            *
1473  * Return value: SUCCEED - at least one matching maintenance was found        *
1474  *                                                                            *
1475  ******************************************************************************/
zbx_dc_get_event_maintenances(zbx_vector_ptr_t * event_queries,const zbx_vector_uint64_t * maintenanceids)1476 int	zbx_dc_get_event_maintenances(zbx_vector_ptr_t *event_queries, const zbx_vector_uint64_t *maintenanceids)
1477 {
1478 	zbx_hashset_t			host_event_maintenances;
1479 	int				i, j, k, ret = FAIL;
1480 	zbx_event_suppress_query_t	*query;
1481 	ZBX_DC_ITEM			*item;
1482 	ZBX_DC_FUNCTION			*function;
1483 	zbx_vector_uint64_t		hostids;
1484 	zbx_hashset_iter_t		iter;
1485 	zbx_host_event_maintenance_t	*host_event_maintenance;
1486 
1487 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
1488 
1489 	zbx_vector_uint64_create(&hostids);
1490 
1491 	zbx_hashset_create_ext(&host_event_maintenances, maintenanceids->values_num, ZBX_DEFAULT_UINT64_HASH_FUNC,
1492 			ZBX_DEFAULT_UINT64_COMPARE_FUNC, (zbx_clean_func_t)host_event_maintenance_clean,
1493 			ZBX_DEFAULT_MEM_MALLOC_FUNC, ZBX_DEFAULT_MEM_REALLOC_FUNC, ZBX_DEFAULT_MEM_FREE_FUNC);
1494 	/* event tags must be sorted by name to perform maintenance tag matching */
1495 
1496 	for (i = 0; i < event_queries->values_num; i++)
1497 	{
1498 		query = (zbx_event_suppress_query_t *)event_queries->values[i];
1499 		if (0 != query->tags.values_num)
1500 			zbx_vector_ptr_sort(&query->tags, dc_compare_tags);
1501 	}
1502 
1503 	RDLOCK_CACHE;
1504 
1505 	dc_get_host_maintenances_by_ids(maintenanceids, &host_event_maintenances, dc_assign_event_maintenance_to_host);
1506 
1507 	if (0 == host_event_maintenances.num_data)
1508 		goto unlock;
1509 
1510 	zbx_hashset_iter_reset(&host_event_maintenances, &iter);
1511 
1512 	while (NULL != (host_event_maintenance = (zbx_host_event_maintenance_t *)zbx_hashset_iter_next(&iter)))
1513 	{
1514 		zbx_vector_ptr_sort(&host_event_maintenance->maintenances, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
1515 		zbx_vector_ptr_uniq(&host_event_maintenance->maintenances, ZBX_DEFAULT_UINT64_PTR_COMPARE_FUNC);
1516 	}
1517 
1518 	for (i = 0; i < event_queries->values_num; i++)
1519 	{
1520 		query = (zbx_event_suppress_query_t *)event_queries->values[i];
1521 
1522 		/* find hostids of items used in event trigger expressions */
1523 
1524 		/* Some processes do not have trigger data at hand and create event queries */
1525 		/* without filling query functionids. Do it here if necessary.              */
1526 		if (0 == query->functionids.values_num)
1527 		{
1528 			ZBX_DC_TRIGGER	*trigger;
1529 
1530 			if (NULL == (trigger = (ZBX_DC_TRIGGER *)zbx_hashset_search(&config->triggers,
1531 					&query->triggerid)))
1532 			{
1533 				continue;
1534 			}
1535 
1536 			zbx_get_serialized_expression_functionids(trigger->expression, trigger->expression_bin,
1537 					&query->functionids);
1538 
1539 			if (TRIGGER_RECOVERY_MODE_RECOVERY_EXPRESSION == trigger->recovery_mode)
1540 			{
1541 				zbx_get_serialized_expression_functionids(trigger->recovery_expression,
1542 						trigger->recovery_expression_bin, &query->functionids);
1543 			}
1544 		}
1545 
1546 		for (j = 0; j < query->functionids.values_num; j++)
1547 		{
1548 			ZBX_DC_HOST	*dc_host;
1549 
1550 			if (NULL == (function = (ZBX_DC_FUNCTION *)zbx_hashset_search(&config->functions,
1551 					&query->functionids.values[j])))
1552 			{
1553 				continue;
1554 			}
1555 
1556 			if (NULL == (item = (ZBX_DC_ITEM *)zbx_hashset_search(&config->items, &function->itemid)))
1557 				continue;
1558 
1559 			if (NULL == (dc_host = (ZBX_DC_HOST *)zbx_hashset_search(&config->hosts, &item->hostid)))
1560 				continue;
1561 
1562 			if (HOST_MAINTENANCE_STATUS_OFF == dc_host->maintenance_status)
1563 				goto skip;
1564 
1565 			zbx_vector_uint64_append(&hostids, item->hostid);
1566 		}
1567 
1568 		zbx_vector_uint64_sort(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1569 		zbx_vector_uint64_uniq(&hostids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
1570 
1571 		/* find matching maintenances */
1572 		for (j = 0; j < hostids.values_num; j++)
1573 		{
1574 			const zbx_dc_maintenance_t	*maintenance;
1575 
1576 			if (NULL == (host_event_maintenance = zbx_hashset_search(&host_event_maintenances,
1577 					&hostids.values[j])))
1578 			{
1579 				continue;
1580 			}
1581 
1582 			for (k = 0; k < host_event_maintenance->maintenances.values_num; k++)
1583 			{
1584 				zbx_uint64_pair_t	pair;
1585 
1586 				maintenance = (zbx_dc_maintenance_t *)host_event_maintenance->maintenances.values[k];
1587 
1588 				if (ZBX_MAINTENANCE_RUNNING != maintenance->state)
1589 					continue;
1590 
1591 				pair.first = maintenance->maintenanceid;
1592 
1593 				if (FAIL != zbx_vector_uint64_pair_search(&query->maintenances, pair,
1594 						ZBX_DEFAULT_UINT64_COMPARE_FUNC))
1595 				{
1596 					continue;
1597 				}
1598 
1599 				if (SUCCEED != dc_maintenance_match_tags(maintenance, &query->tags))
1600 					continue;
1601 
1602 				pair.second = maintenance->running_until;
1603 				zbx_vector_uint64_pair_append(&query->maintenances, pair);
1604 				ret = SUCCEED;
1605 			}
1606 		}
1607 skip:
1608 		zbx_vector_uint64_clear(&hostids);
1609 	}
1610 unlock:
1611 	UNLOCK_CACHE;
1612 
1613 	zbx_vector_uint64_destroy(&hostids);
1614 	zbx_hashset_destroy(&host_event_maintenances);
1615 
1616 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
1617 
1618 	return ret;
1619 }
1620 
1621 /******************************************************************************
1622  *                                                                            *
1623  * Function: zbx_event_suppress_query_free                                    *
1624  *                                                                            *
1625  * Purpose: free event suppress query structure                               *
1626  *                                                                            *
1627  ******************************************************************************/
zbx_event_suppress_query_free(zbx_event_suppress_query_t * query)1628 void	zbx_event_suppress_query_free(zbx_event_suppress_query_t *query)
1629 {
1630 	zbx_vector_uint64_destroy(&query->functionids);
1631 	zbx_vector_uint64_pair_destroy(&query->maintenances);
1632 	zbx_vector_ptr_clear_ext(&query->tags, (zbx_clean_func_t)zbx_free_tag);
1633 	zbx_vector_ptr_destroy(&query->tags);
1634 	zbx_free(query);
1635 }
1636 
1637 /******************************************************************************
1638  *                                                                            *
1639  * Function: zbx_dc_get_running_maintenanceids                                *
1640  *                                                                            *
1641  * Purpose: get identifiers of the running maintenances                       *
1642  *                                                                            *
1643  * Return value: SUCCEED - at least one running maintenance was found         *
1644  *               FAIL    - no running maintenances were found                 *
1645  *                                                                            *
1646  ******************************************************************************/
zbx_dc_get_running_maintenanceids(zbx_vector_uint64_t * maintenanceids)1647 int	zbx_dc_get_running_maintenanceids(zbx_vector_uint64_t *maintenanceids)
1648 {
1649 	zbx_dc_maintenance_t	*maintenance;
1650 	zbx_hashset_iter_t	iter;
1651 
1652 	RDLOCK_CACHE;
1653 
1654 	zbx_hashset_iter_reset(&config->maintenances, &iter);
1655 	while (NULL != (maintenance = (zbx_dc_maintenance_t *)zbx_hashset_iter_next(&iter)))
1656 	{
1657 		if (ZBX_MAINTENANCE_RUNNING == maintenance->state)
1658 			zbx_vector_uint64_append(maintenanceids, maintenance->maintenanceid);
1659 	}
1660 
1661 	UNLOCK_CACHE;
1662 
1663 	return (0 != maintenanceids->values_num ? SUCCEED : FAIL);
1664 }
1665 
1666 #ifdef HAVE_TESTS
1667 #	include "../../../tests/libs/zbxdbcache/dbconfig_maintenance_test.c"
1668 #endif
1669