1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 
22 #include "db.h"
23 #include "log.h"
24 #include "mutexs.h"
25 
26 #define LOCK_ITSERVICES		zbx_mutex_lock(itservices_lock)
27 #define UNLOCK_ITSERVICES	zbx_mutex_unlock(itservices_lock)
28 
29 static zbx_mutex_t	itservices_lock = ZBX_MUTEX_NULL;
30 
31 /* status update queue items */
32 typedef struct
33 {
34 	/* the update source id */
35 	zbx_uint64_t	sourceid;
36 	/* the new status */
37 	int		status;
38 	/* timestamp */
39 	int		clock;
40 }
41 zbx_status_update_t;
42 
43 /* Service node */
44 typedef struct
45 {
46 	/* service id */
47 	zbx_uint64_t		serviceid;
48 	/* trigger id of leaf nodes */
49 	zbx_uint64_t		triggerid;
50 	/* the initial service status */
51 	int			old_status;
52 	/* the calculated service status */
53 	int			status;
54 	/* the service status calculation algorithm, see SERVICE_ALGORITHM_* defines */
55 	int			algorithm;
56 	/* the parent nodes */
57 	zbx_vector_ptr_t	parents;
58 	/* the child nodes */
59 	zbx_vector_ptr_t	children;
60 }
61 zbx_itservice_t;
62 
63 /* index of services by triggerid */
64 typedef struct
65 {
66 	zbx_uint64_t		triggerid;
67 	zbx_vector_ptr_t	itservices;
68 }
69 zbx_itservice_index_t;
70 
71 /* a set of services used during update session                          */
72 /*                                                                          */
73 /* All services are stored into hashset accessed by serviceid. The services */
74 /* also are indexed by triggerid.                                           */
75 /* The following types of services are loaded during update session:        */
76 /*  1) services directly linked to the triggers with values changed         */
77 /*     during update session.                                               */
78 /*  2) direct or indirect parent services of (1)                            */
79 /*  3) services required to calculate status of (2) and not already loaded  */
80 /*     as (1) or (2).                                                       */
81 /*                                                                          */
82 /* In this schema:                                                          */
83 /*   (1) can't have children services                                       */
84 /*   (2) will have children services                                        */
85 /*   (1) and (2) will have parent services unless it's the root service     */
86 /*   (3) will have neither children or parent services                      */
87 /*                                                                          */
88 typedef struct
89 {
90 	/* loaded services */
91 	zbx_hashset_t	itservices;
92 	/* service index by triggerid */
93 	zbx_hashset_t	index;
94 }
95 zbx_itservices_t;
96 
97 /******************************************************************************
98  *                                                                            *
99  * Function: its_itservices_init                                              *
100  *                                                                            *
101  * Purpose: initializes services data set to store services during update     *
102  *          session                                                           *
103  *                                                                            *
104  * Parameters: set   - [IN] the data set to initialize                        *
105  *                                                                            *
106  ******************************************************************************/
its_itservices_init(zbx_itservices_t * itservices)107 static void	its_itservices_init(zbx_itservices_t *itservices)
108 {
109 	zbx_hashset_create(&itservices->itservices, 512, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
110 	zbx_hashset_create(&itservices->index, 128, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
111 }
112 
113 /******************************************************************************
114  *                                                                            *
115  * Function: its_itservices_clean                                             *
116  *                                                                            *
117  * Purpose: cleans services data set by releasing allocated memory            *
118  *                                                                            *
119  * Parameters: set   - [IN] the data set to clean                             *
120  *                                                                            *
121  ******************************************************************************/
its_itservices_clean(zbx_itservices_t * itservices)122 static void	its_itservices_clean(zbx_itservices_t *itservices)
123 {
124 	zbx_hashset_iter_t	iter;
125 	zbx_itservice_t		*itservice;
126 	zbx_itservice_index_t	*index;
127 
128 	zbx_hashset_iter_reset(&itservices->index, &iter);
129 
130 	while (NULL != (index = (zbx_itservice_index_t *)zbx_hashset_iter_next(&iter)))
131 		zbx_vector_ptr_destroy(&index->itservices);
132 
133 	zbx_hashset_destroy(&itservices->index);
134 
135 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
136 
137 	while (NULL != (itservice = (zbx_itservice_t *)zbx_hashset_iter_next(&iter)))
138 	{
139 		zbx_vector_ptr_destroy(&itservice->children);
140 		zbx_vector_ptr_destroy(&itservice->parents);
141 	}
142 
143 	zbx_hashset_destroy(&itservices->itservices);
144 }
145 
146 /******************************************************************************
147  *                                                                            *
148  * Function: its_itservice_create                                             *
149  *                                                                            *
150  * Purpose: creates a new service node                                        *
151  *                                                                            *
152  * Parameters: itservices  - [IN] the services data                           *
153  *             serviceid   - [IN] the service id                              *
154  *             algorithm   - [IN] the service status calculation mode         *
155  *             triggerid   - [IN] the source trigger id for leaf nodes        *
156  *             status      - [IN] the initial service status                  *
157  *                                                                            *
158  * Return value: the created service node                                     *
159  *                                                                            *
160  ******************************************************************************/
its_itservice_create(zbx_itservices_t * itservices,zbx_uint64_t serviceid,zbx_uint64_t triggerid,int status,int algorithm)161 static zbx_itservice_t	*its_itservice_create(zbx_itservices_t *itservices, zbx_uint64_t serviceid,
162 		zbx_uint64_t triggerid, int status, int algorithm)
163 {
164 	zbx_itservice_t		itservice = {.serviceid = serviceid, .triggerid = triggerid, .old_status = status,
165 				.status = status, .algorithm = algorithm}, *pitservice;
166 	zbx_itservice_index_t	*pindex;
167 
168 	zbx_vector_ptr_create(&itservice.children);
169 	zbx_vector_ptr_create(&itservice.parents);
170 
171 	pitservice = (zbx_itservice_t *)zbx_hashset_insert(&itservices->itservices, &itservice, sizeof(itservice));
172 
173 	if (0 != triggerid)
174 	{
175 		if (NULL == (pindex = (zbx_itservice_index_t *)zbx_hashset_search(&itservices->index, &triggerid)))
176 		{
177 			zbx_itservice_index_t	index = {.triggerid = triggerid};
178 
179 			zbx_vector_ptr_create(&index.itservices);
180 
181 			pindex = (zbx_itservice_index_t *)zbx_hashset_insert(&itservices->index, &index, sizeof(index));
182 		}
183 
184 		zbx_vector_ptr_append(&pindex->itservices, pitservice);
185 	}
186 
187 	return pitservice;
188 }
189 
190 /******************************************************************************
191  *                                                                            *
192  * Function: its_updates_append                                               *
193  *                                                                            *
194  * Purpose: adds an update to the queue                                       *
195  *                                                                            *
196  * Parameters: updates   - [OUT] the update queue                             *
197  *             sourceid  - [IN] the update source id                          *
198  *             status    - [IN] the update status                             *
199  *             clock     - [IN] the update timestamp                          *
200  *                                                                            *
201  ******************************************************************************/
its_updates_append(zbx_vector_ptr_t * updates,zbx_uint64_t sourceid,int status,int clock)202 static void	its_updates_append(zbx_vector_ptr_t *updates, zbx_uint64_t sourceid, int status, int clock)
203 {
204 	zbx_status_update_t	*update;
205 
206 	update = (zbx_status_update_t *)zbx_malloc(NULL, sizeof(zbx_status_update_t));
207 
208 	update->sourceid = sourceid;
209 	update->status = status;
210 	update->clock = clock;
211 
212 	zbx_vector_ptr_append(updates, update);
213 }
214 
zbx_status_update_free(zbx_status_update_t * update)215 static void	zbx_status_update_free(zbx_status_update_t *update)
216 {
217 	zbx_free(update);
218 }
219 
220 /******************************************************************************
221  *                                                                            *
222  * Function: its_itservices_load_children                                     *
223  *                                                                            *
224  * Purpose: loads all missing children of the specified services              *
225  *                                                                            *
226  * Parameters: itservices   - [IN] the services data                          *
227  *                                                                            *
228  ******************************************************************************/
its_itservices_load_children(zbx_itservices_t * itservices)229 static void	its_itservices_load_children(zbx_itservices_t *itservices)
230 {
231 	char			*sql = NULL;
232 	size_t			sql_alloc = 0, sql_offset = 0;
233 	DB_RESULT		result;
234 	DB_ROW			row;
235 	zbx_itservice_t		*itservice, *parent;
236 	zbx_uint64_t		serviceid, parentid;
237 	zbx_vector_uint64_t	serviceids;
238 	zbx_hashset_iter_t	iter;
239 
240 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
241 
242 	zbx_vector_uint64_create(&serviceids);
243 
244 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
245 
246 	while (NULL != (itservice = (zbx_itservice_t *)zbx_hashset_iter_next(&iter)))
247 	{
248 		if (0 == itservice->triggerid)
249 			zbx_vector_uint64_append(&serviceids, itservice->serviceid);
250 	}
251 
252 	/* check for extreme case when there are only leaf nodes */
253 	if (0 == serviceids.values_num)
254 		goto out;
255 
256 	zbx_vector_uint64_sort(&serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
257 
258 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
259 			"select s.serviceid,s.status,s.algorithm,sl.serviceupid"
260 			" from services s,services_links sl"
261 			" where s.serviceid=sl.servicedownid"
262 				" and");
263 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.serviceupid", serviceids.values,
264 			serviceids.values_num);
265 
266 	result = DBselect("%s", sql);
267 
268 	while (NULL != (row = DBfetch(result)))
269 	{
270 		ZBX_STR2UINT64(serviceid, row[0]);
271 		ZBX_STR2UINT64(parentid, row[3]);
272 
273 		if (NULL == (parent = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &parentid)))
274 		{
275 			THIS_SHOULD_NEVER_HAPPEN;
276 			continue;
277 		}
278 
279 		if (NULL == (itservice = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &serviceid)))
280 			itservice = its_itservice_create(itservices, serviceid, 0, atoi(row[1]), atoi(row[2]));
281 
282 		if (FAIL == zbx_vector_ptr_search(&parent->children, itservice, ZBX_DEFAULT_PTR_COMPARE_FUNC))
283 			zbx_vector_ptr_append(&parent->children, itservice);
284 	}
285 	DBfree_result(result);
286 
287 	zbx_free(sql);
288 
289 	zbx_vector_uint64_destroy(&serviceids);
290 out:
291 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
292 }
293 
294 /******************************************************************************
295  *                                                                            *
296  * Function: its_itservices_load_parents                                      *
297  *                                                                            *
298  * Purpose: recursively loads parent nodes of the specified service until the *
299  *          root node                                                         *
300  *                                                                            *
301  * Parameters: itservices   - [IN] the services data                          *
302  *             serviceids   - [IN] a vector containing ids of services to     *
303  *                                 load parents                               *
304  *                                                                            *
305  ******************************************************************************/
its_itservices_load_parents(zbx_itservices_t * itservices,zbx_vector_uint64_t * serviceids)306 static void	its_itservices_load_parents(zbx_itservices_t *itservices, zbx_vector_uint64_t *serviceids)
307 {
308 	DB_RESULT	result;
309 	DB_ROW		row;
310 	char		*sql = NULL;
311 	size_t		sql_alloc = 0, sql_offset = 0;
312 	zbx_itservice_t	*parent, *itservice;
313 	zbx_uint64_t	parentid, serviceid;
314 
315 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
316 
317 	zbx_vector_uint64_sort(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
318 	zbx_vector_uint64_uniq(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
319 
320 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
321 			"select s.serviceid,s.status,s.algorithm,sl.servicedownid"
322 			" from services s,services_links sl"
323 			" where s.serviceid=sl.serviceupid"
324 				" and");
325 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.servicedownid", serviceids->values,
326 			serviceids->values_num);
327 
328 	zbx_vector_uint64_clear(serviceids);
329 
330 	result = DBselect("%s", sql);
331 
332 	while (NULL != (row = DBfetch(result)))
333 	{
334 		ZBX_STR2UINT64(parentid, row[0]);
335 		ZBX_STR2UINT64(serviceid, row[3]);
336 
337 		/* find the service */
338 		if (NULL == (itservice = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &serviceid)))
339 		{
340 			THIS_SHOULD_NEVER_HAPPEN;
341 			continue;
342 		}
343 
344 		/* find/load the parent service */
345 		if (NULL == (parent = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &parentid)))
346 		{
347 			parent = its_itservice_create(itservices, parentid, 0, atoi(row[1]), atoi(row[2]));
348 			zbx_vector_uint64_append(serviceids, parent->serviceid);
349 		}
350 
351 		/* link the service as a parent's child */
352 		if (FAIL == zbx_vector_ptr_search(&itservice->parents, parent, ZBX_DEFAULT_PTR_COMPARE_FUNC))
353 			zbx_vector_ptr_append(&itservice->parents, parent);
354 	}
355 	DBfree_result(result);
356 
357 	zbx_free(sql);
358 
359 	if (0 != serviceids->values_num)
360 		its_itservices_load_parents(itservices, serviceids);
361 
362 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
363 }
364 
365 /******************************************************************************
366  *                                                                            *
367  * Function: its_load_services_by_triggerids                                  *
368  *                                                                            *
369  * Purpose: loads services that might be affected by the specified triggerid  *
370  *          or are required to calculate status of loaded services            *
371  *                                                                            *
372  * Parameters: itservices - [IN] the services data                            *
373  *             triggerids - [IN] the sorted list of trigger ids               *
374  *                                                                            *
375  ******************************************************************************/
its_load_services_by_triggerids(zbx_itservices_t * itservices,const zbx_vector_uint64_t * triggerids)376 static void	its_load_services_by_triggerids(zbx_itservices_t *itservices, const zbx_vector_uint64_t *triggerids)
377 {
378 	DB_RESULT		result;
379 	DB_ROW			row;
380 	zbx_uint64_t		serviceid, triggerid;
381 	zbx_itservice_t		*itservice;
382 	char			*sql = NULL;
383 	size_t			sql_alloc = 0, sql_offset = 0;
384 	zbx_vector_uint64_t	serviceids;
385 
386 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
387 
388 	zbx_vector_uint64_create(&serviceids);
389 
390 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
391 			"select serviceid,triggerid,status,algorithm"
392 			" from services"
393 			" where");
394 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids->values, triggerids->values_num);
395 
396 	result = DBselect("%s", sql);
397 
398 	zbx_free(sql);
399 
400 	while (NULL != (row = DBfetch(result)))
401 	{
402 		ZBX_STR2UINT64(serviceid, row[0]);
403 		ZBX_STR2UINT64(triggerid, row[1]);
404 
405 		itservice = its_itservice_create(itservices, serviceid, triggerid, atoi(row[2]), atoi(row[3]));
406 
407 		zbx_vector_uint64_append(&serviceids, itservice->serviceid);
408 	}
409 	DBfree_result(result);
410 
411 	if (0 != serviceids.values_num)
412 	{
413 		its_itservices_load_parents(itservices, &serviceids);
414 		its_itservices_load_children(itservices);
415 	}
416 
417 	zbx_vector_uint64_destroy(&serviceids);
418 
419 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
420 }
421 
422 /******************************************************************************
423  *                                                                            *
424  * Function: its_itservice_update_status                                      *
425  *                                                                            *
426  * Purpose: updates service and its parents statuses                          *
427  *                                                                            *
428  * Parameters: service    - [IN] the service to update                        *
429  *             clock      - [IN] the update timestamp                         *
430  *             alarms     - [OUT] the alarms update queue                     *
431  *                                                                            *
432  * Comments: This function recalculates service status according to the       *
433  *           algorithm and status of the children services. If the status     *
434  *           has been changed, an alarm is generated and parent services      *
435  *           (up until the root service) are updated too.                     *
436  *                                                                            *
437  ******************************************************************************/
its_itservice_update_status(zbx_itservice_t * itservice,int clock,zbx_vector_ptr_t * alarms)438 static void	its_itservice_update_status(zbx_itservice_t *itservice, int clock, zbx_vector_ptr_t *alarms)
439 {
440 	int	status, i;
441 
442 	switch (itservice->algorithm)
443 	{
444 		case SERVICE_ALGORITHM_MIN:
445 			status = TRIGGER_SEVERITY_COUNT;
446 			for (i = 0; i < itservice->children.values_num; i++)
447 			{
448 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
449 
450 				if (child->status < status)
451 					status = child->status;
452 			}
453 			break;
454 		case SERVICE_ALGORITHM_MAX:
455 			status = 0;
456 			for (i = 0; i < itservice->children.values_num; i++)
457 			{
458 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
459 
460 				if (child->status > status)
461 					status = child->status;
462 			}
463 			break;
464 		case SERVICE_ALGORITHM_NONE:
465 			goto out;
466 		default:
467 			zabbix_log(LOG_LEVEL_ERR, "unknown calculation algorithm of service status [%d]",
468 					itservice->algorithm);
469 			goto out;
470 	}
471 
472 	if (itservice->status != status)
473 	{
474 		itservice->status = status;
475 
476 		its_updates_append(alarms, itservice->serviceid, status, clock);
477 
478 		/* update parent services */
479 		for (i = 0; i < itservice->parents.values_num; i++)
480 			its_itservice_update_status((zbx_itservice_t *)itservice->parents.values[i], clock, alarms);
481 	}
482 out:
483 	;
484 }
485 
486 /******************************************************************************
487  *                                                                            *
488  * Function: its_updates_compare                                              *
489  *                                                                            *
490  * Purpose: used to sort service updates by source id                         *
491  *                                                                            *
492  ******************************************************************************/
its_updates_compare(const zbx_status_update_t ** update1,const zbx_status_update_t ** update2)493 static int	its_updates_compare(const zbx_status_update_t **update1, const zbx_status_update_t **update2)
494 {
495 	ZBX_RETURN_IF_NOT_EQUAL((*update1)->sourceid, (*update2)->sourceid);
496 
497 	return 0;
498 }
499 
500 /******************************************************************************
501  *                                                                            *
502  * Function: its_write_status_and_alarms                                      *
503  *                                                                            *
504  * Purpose: writes service status changes and generated service alarms into   *
505  *          database                                                          *
506  *                                                                            *
507  * Parameters: itservices - [IN] the services data                            *
508  *             alarms     - [IN] the service alarms update queue              *
509  *                                                                            *
510  * Return value: SUCCEED - the data was written successfully                  *
511  *               FAIL    - otherwise                                          *
512  *                                                                            *
513  ******************************************************************************/
its_write_status_and_alarms(zbx_itservices_t * itservices,zbx_vector_ptr_t * alarms)514 static int	its_write_status_and_alarms(zbx_itservices_t *itservices, zbx_vector_ptr_t *alarms)
515 {
516 	int			i, ret = FAIL;
517 	zbx_vector_ptr_t	updates;
518 	char			*sql = NULL;
519 	size_t			sql_alloc = 0, sql_offset = 0;
520 	zbx_uint64_t		alarmid;
521 	zbx_hashset_iter_t	iter;
522 	zbx_itservice_t		*itservice;
523 
524 	/* get a list of service status updates that must be written to database */
525 	zbx_vector_ptr_create(&updates);
526 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
527 
528 	while (NULL != (itservice = (zbx_itservice_t *)zbx_hashset_iter_next(&iter)))
529 	{
530 		if (itservice->old_status != itservice->status)
531 			its_updates_append(&updates, itservice->serviceid, itservice->status, 0);
532 	}
533 
534 	/* write service status changes into database */
535 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
536 
537 	if (0 != updates.values_num)
538 	{
539 		zbx_vector_ptr_sort(&updates, (zbx_compare_func_t)its_updates_compare);
540 		zbx_vector_ptr_uniq(&updates, (zbx_compare_func_t)its_updates_compare);
541 
542 		for (i = 0; i < updates.values_num; i++)
543 		{
544 			zbx_status_update_t	*update = (zbx_status_update_t *)updates.values[i];
545 
546 			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
547 					"update services"
548 					" set status=%d"
549 					" where serviceid=" ZBX_FS_UI64 ";\n",
550 					update->status, update->sourceid);
551 
552 			if (SUCCEED != DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))
553 				goto out;
554 		}
555 	}
556 
557 	DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
558 
559 	if (16 < sql_offset)
560 	{
561 		if (ZBX_DB_OK > DBexecute("%s", sql))
562 			goto out;
563 	}
564 
565 	ret = SUCCEED;
566 
567 	/* write generated service alarms into database */
568 	if (0 != alarms->values_num)
569 	{
570 		zbx_db_insert_t	db_insert;
571 
572 		alarmid = DBget_maxid_num("service_alarms", alarms->values_num);
573 
574 		zbx_db_insert_prepare(&db_insert, "service_alarms", "servicealarmid", "serviceid", "value", "clock",
575 				NULL);
576 
577 		for (i = 0; i < alarms->values_num; i++)
578 		{
579 			zbx_status_update_t	*update = (zbx_status_update_t *)alarms->values[i];
580 
581 			zbx_db_insert_add_values(&db_insert, alarmid++, update->sourceid, update->status,
582 					update->clock);
583 		}
584 
585 		ret = zbx_db_insert_execute(&db_insert);
586 
587 		zbx_db_insert_clean(&db_insert);
588 	}
589 out:
590 	zbx_free(sql);
591 
592 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
593 	zbx_vector_ptr_destroy(&updates);
594 
595 	return ret;
596 }
597 
598 /******************************************************************************
599  *                                                                            *
600  * Function: its_flush_updates                                                *
601  *                                                                            *
602  * Purpose: processes the service update queue                                *
603  *                                                                            *
604  * Return value: SUCCEED - the data was written successfully                  *
605  *               FAIL    - otherwise                                          *
606  *                                                                            *
607  * Comments: The following steps are taken to process the queue:              *
608  *           1) Load all services either directly referenced (with triggerid) *
609  *              by update queue or dependent on those services (directly or   *
610  *              indirectly) or required to calculate status of any loaded     *
611  *              services.                                                     *
612  *           2) Apply updates to the loaded service tree. Queue new service   *
613  *              alarms whenever service status changes.                       *
614  *           3) Write the final service status changes and the generated      *
615  *              service alarm queue into database.                            *
616  *                                                                            *
617  ******************************************************************************/
its_flush_updates(const zbx_vector_ptr_t * updates)618 static int	its_flush_updates(const zbx_vector_ptr_t *updates)
619 {
620 	int				i, j, k, ret = FAIL;
621 	const zbx_status_update_t	*update;
622 	zbx_itservices_t		itservices;
623 	zbx_vector_ptr_t		alarms;
624 	zbx_itservice_index_t		*index;
625 	zbx_vector_uint64_t		triggerids;
626 
627 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
628 
629 	its_itservices_init(&itservices);
630 
631 	zbx_vector_uint64_create(&triggerids);
632 
633 	for (i = 0; i < updates->values_num; i++)
634 	{
635 		update = (zbx_status_update_t *)updates->values[i];
636 
637 		zbx_vector_uint64_append(&triggerids, update->sourceid);
638 	}
639 
640 	zbx_vector_uint64_sort(&triggerids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
641 
642 	/* load all services affected by the trigger status change and      */
643 	/* the services that are required for resulting status calculations */
644 	its_load_services_by_triggerids(&itservices, &triggerids);
645 
646 	zbx_vector_uint64_destroy(&triggerids);
647 
648 	if (0 == itservices.itservices.num_data)
649 	{
650 		ret = SUCCEED;
651 		goto out;
652 	}
653 
654 	zbx_vector_ptr_create(&alarms);
655 
656 	/* apply status updates */
657 	for (i = 0; i < updates->values_num; i++)
658 	{
659 		update = (const zbx_status_update_t *)updates->values[i];
660 
661 		if (NULL == (index = (zbx_itservice_index_t *)zbx_hashset_search(&itservices.index, update)))
662 			continue;
663 
664 		/* change the status of services based on the update */
665 		for (j = 0; j < index->itservices.values_num; j++)
666 		{
667 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
668 
669 			if (SERVICE_ALGORITHM_NONE == itservice->algorithm || itservice->status == update->status)
670 				continue;
671 
672 			its_updates_append(&alarms, itservice->serviceid, update->status, update->clock);
673 			itservice->status = update->status;
674 		}
675 
676 		/* recalculate status of the parent services */
677 		for (j = 0; j < index->itservices.values_num; j++)
678 		{
679 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
680 
681 			/* update parent services */
682 			for (k = 0; k < itservice->parents.values_num; k++)
683 				its_itservice_update_status((zbx_itservice_t *)itservice->parents.values[k], update->clock, &alarms);
684 		}
685 	}
686 
687 	ret = its_write_status_and_alarms(&itservices, &alarms);
688 
689 	zbx_vector_ptr_clear_ext(&alarms, (zbx_clean_func_t)zbx_status_update_free);
690 	zbx_vector_ptr_destroy(&alarms);
691 out:
692 	its_itservices_clean(&itservices);
693 
694 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
695 
696 	return ret;
697 }
698 
699 /*
700  * Public API
701  */
702 
703 /******************************************************************************
704  *                                                                            *
705  * Function: DBupdate_itservices                                              *
706  *                                                                            *
707  * Purpose: updates services by applying event list                           *
708  *                                                                            *
709  * Return value: SUCCEED - the services were updated successfully             *
710  *               FAIL    - otherwise                                          *
711  *                                                                            *
712  ******************************************************************************/
DBupdate_itservices(const zbx_vector_ptr_t * trigger_diff)713 int	DBupdate_itservices(const zbx_vector_ptr_t *trigger_diff)
714 {
715 	int				ret = SUCCEED;
716 	zbx_vector_ptr_t		updates;
717 	int				i;
718 	const zbx_trigger_diff_t	*diff;
719 
720 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
721 
722 	zbx_vector_ptr_create(&updates);
723 
724 	for (i = 0; i < trigger_diff->values_num; i++)
725 	{
726 		diff = (zbx_trigger_diff_t *)trigger_diff->values[i];
727 
728 		if (0 == (diff->flags & ZBX_FLAGS_TRIGGER_DIFF_UPDATE_VALUE))
729 			continue;
730 
731 		its_updates_append(&updates, diff->triggerid, TRIGGER_VALUE_PROBLEM == diff->value ?
732 				diff->priority : 0, diff->lastchange);
733 	}
734 
735 	if (0 != updates.values_num)
736 	{
737 		LOCK_ITSERVICES;
738 
739 		do
740 		{
741 			DBbegin();
742 
743 			ret = its_flush_updates(&updates);
744 		}
745 		while (ZBX_DB_DOWN == DBcommit());
746 
747 		UNLOCK_ITSERVICES;
748 
749 		zbx_vector_ptr_clear_ext(&updates, zbx_ptr_free);
750 	}
751 
752 	zbx_vector_ptr_destroy(&updates);
753 
754 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
755 
756 	return ret;
757 }
758 
759 /******************************************************************************
760  *                                                                            *
761  * Function: DBremove_itservice_triggers                                      *
762  *                                                                            *
763  * Purpose: removes specified trigger ids from dependent services and reset   *
764  *          the status of those services to the default value (0)             *
765  *                                                                            *
766  * Parameters: triggerids     - [IN] an array of trigger ids to remove        *
767  *             triggerids_num - [IN] the number of items in triggerids array  *
768  *                                                                            *
769  * Return value: SUCCEED - the data was written successfully                  *
770  *               FAIL    - otherwise                                          *
771  *                                                                            *
772  ******************************************************************************/
DBremove_triggers_from_itservices(zbx_uint64_t * triggerids,int triggerids_num)773 int	DBremove_triggers_from_itservices(zbx_uint64_t *triggerids, int triggerids_num)
774 {
775 	char			*sql = NULL;
776 	size_t			sql_alloc = 0, sql_offset = 0;
777 	zbx_vector_ptr_t	updates;
778 	int			i, ret = FAIL, now;
779 
780 	if (0 == triggerids_num)
781 		return SUCCEED;
782 
783 	now = time(NULL);
784 
785 	zbx_vector_ptr_create(&updates);
786 
787 	for (i = 0; i < triggerids_num; i++)
788 		its_updates_append(&updates, triggerids[i], 0, now);
789 
790 	LOCK_ITSERVICES;
791 
792 	if (FAIL == its_flush_updates(&updates))
793 		goto out;
794 
795 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update services set triggerid=null,showsla=0 where");
796 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids, triggerids_num);
797 
798 	if (ZBX_DB_OK <= DBexecute("%s", sql))
799 		ret = SUCCEED;
800 
801 	zbx_free(sql);
802 out:
803 	UNLOCK_ITSERVICES;
804 
805 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
806 	zbx_vector_ptr_destroy(&updates);
807 
808 	return ret;
809 }
810 
zbx_create_itservices_lock(char ** error)811 int	zbx_create_itservices_lock(char **error)
812 {
813 	return zbx_mutex_create(&itservices_lock, ZBX_MUTEX_ITSERVICES, error);
814 }
815 
zbx_destroy_itservices_lock(void)816 void	zbx_destroy_itservices_lock(void)
817 {
818 	zbx_mutex_destroy(&itservices_lock);
819 }
820