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