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 	const char		*__function_name = "its_itservices_load_children";
232 
233 	char			*sql = NULL;
234 	size_t			sql_alloc = 0, sql_offset = 0;
235 	DB_RESULT		result;
236 	DB_ROW			row;
237 	zbx_itservice_t		*itservice, *parent;
238 	zbx_uint64_t		serviceid, parentid;
239 	zbx_vector_uint64_t	serviceids;
240 	zbx_hashset_iter_t	iter;
241 
242 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
243 
244 	zbx_vector_uint64_create(&serviceids);
245 
246 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
247 
248 	while (NULL != (itservice = (zbx_itservice_t *)zbx_hashset_iter_next(&iter)))
249 	{
250 		if (0 == itservice->triggerid)
251 			zbx_vector_uint64_append(&serviceids, itservice->serviceid);
252 	}
253 
254 	/* check for extreme case when there are only leaf nodes */
255 	if (0 == serviceids.values_num)
256 		goto out;
257 
258 	zbx_vector_uint64_sort(&serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
259 
260 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
261 			"select s.serviceid,s.status,s.algorithm,sl.serviceupid"
262 			" from services s,services_links sl"
263 			" where s.serviceid=sl.servicedownid"
264 				" and");
265 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.serviceupid", serviceids.values,
266 			serviceids.values_num);
267 
268 	result = DBselect("%s", sql);
269 
270 	while (NULL != (row = DBfetch(result)))
271 	{
272 		ZBX_STR2UINT64(serviceid, row[0]);
273 		ZBX_STR2UINT64(parentid, row[3]);
274 
275 		if (NULL == (parent = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &parentid)))
276 		{
277 			THIS_SHOULD_NEVER_HAPPEN;
278 			continue;
279 		}
280 
281 		if (NULL == (itservice = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &serviceid)))
282 			itservice = its_itservice_create(itservices, serviceid, 0, atoi(row[1]), atoi(row[2]));
283 
284 		if (FAIL == zbx_vector_ptr_search(&parent->children, itservice, ZBX_DEFAULT_PTR_COMPARE_FUNC))
285 			zbx_vector_ptr_append(&parent->children, itservice);
286 	}
287 	DBfree_result(result);
288 
289 	zbx_free(sql);
290 
291 	zbx_vector_uint64_destroy(&serviceids);
292 out:
293 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
294 }
295 
296 /******************************************************************************
297  *                                                                            *
298  * Function: its_itservices_load_parents                                      *
299  *                                                                            *
300  * Purpose: recursively loads parent nodes of the specified service until the *
301  *          root node                                                         *
302  *                                                                            *
303  * Parameters: itservices   - [IN] the services data                          *
304  *             serviceids   - [IN] a vector containing ids of services to     *
305  *                                 load parents                               *
306  *                                                                            *
307  ******************************************************************************/
its_itservices_load_parents(zbx_itservices_t * itservices,zbx_vector_uint64_t * serviceids)308 static void	its_itservices_load_parents(zbx_itservices_t *itservices, zbx_vector_uint64_t *serviceids)
309 {
310 	const char	*__function_name = "its_itservices_load_parents";
311 
312 	DB_RESULT	result;
313 	DB_ROW		row;
314 	char		*sql = NULL;
315 	size_t		sql_alloc = 0, sql_offset = 0;
316 	zbx_itservice_t	*parent, *itservice;
317 	zbx_uint64_t	parentid, serviceid;
318 
319 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
320 
321 	zbx_vector_uint64_sort(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
322 	zbx_vector_uint64_uniq(serviceids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
323 
324 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
325 			"select s.serviceid,s.status,s.algorithm,sl.servicedownid"
326 			" from services s,services_links sl"
327 			" where s.serviceid=sl.serviceupid"
328 				" and");
329 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "sl.servicedownid", serviceids->values,
330 			serviceids->values_num);
331 
332 	zbx_vector_uint64_clear(serviceids);
333 
334 	result = DBselect("%s", sql);
335 
336 	while (NULL != (row = DBfetch(result)))
337 	{
338 		ZBX_STR2UINT64(parentid, row[0]);
339 		ZBX_STR2UINT64(serviceid, row[3]);
340 
341 		/* find the service */
342 		if (NULL == (itservice = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &serviceid)))
343 		{
344 			THIS_SHOULD_NEVER_HAPPEN;
345 			continue;
346 		}
347 
348 		/* find/load the parent service */
349 		if (NULL == (parent = (zbx_itservice_t *)zbx_hashset_search(&itservices->itservices, &parentid)))
350 		{
351 			parent = its_itservice_create(itservices, parentid, 0, atoi(row[1]), atoi(row[2]));
352 			zbx_vector_uint64_append(serviceids, parent->serviceid);
353 		}
354 
355 		/* link the service as a parent's child */
356 		if (FAIL == zbx_vector_ptr_search(&itservice->parents, parent, ZBX_DEFAULT_PTR_COMPARE_FUNC))
357 			zbx_vector_ptr_append(&itservice->parents, parent);
358 	}
359 	DBfree_result(result);
360 
361 	zbx_free(sql);
362 
363 	if (0 != serviceids->values_num)
364 		its_itservices_load_parents(itservices, serviceids);
365 
366 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
367 }
368 
369 /******************************************************************************
370  *                                                                            *
371  * Function: its_load_services_by_triggerids                                  *
372  *                                                                            *
373  * Purpose: loads services that might be affected by the specified triggerid  *
374  *          or are required to calculate status of loaded services            *
375  *                                                                            *
376  * Parameters: itservices - [IN] the services data                            *
377  *             triggerids - [IN] the sorted list of trigger ids               *
378  *                                                                            *
379  ******************************************************************************/
its_load_services_by_triggerids(zbx_itservices_t * itservices,const zbx_vector_uint64_t * triggerids)380 static void	its_load_services_by_triggerids(zbx_itservices_t *itservices, const zbx_vector_uint64_t *triggerids)
381 {
382 	const char		*__function_name = "its_load_services_by_triggerids";
383 
384 	DB_RESULT		result;
385 	DB_ROW			row;
386 	zbx_uint64_t		serviceid, triggerid;
387 	zbx_itservice_t		*itservice;
388 	char			*sql = NULL;
389 	size_t			sql_alloc = 0, sql_offset = 0;
390 	zbx_vector_uint64_t	serviceids;
391 
392 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
393 
394 	zbx_vector_uint64_create(&serviceids);
395 
396 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset,
397 			"select serviceid,triggerid,status,algorithm"
398 			" from services"
399 			" where");
400 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids->values, triggerids->values_num);
401 
402 	result = DBselect("%s", sql);
403 
404 	zbx_free(sql);
405 
406 	while (NULL != (row = DBfetch(result)))
407 	{
408 		ZBX_STR2UINT64(serviceid, row[0]);
409 		ZBX_STR2UINT64(triggerid, row[1]);
410 
411 		itservice = its_itservice_create(itservices, serviceid, triggerid, atoi(row[2]), atoi(row[3]));
412 
413 		zbx_vector_uint64_append(&serviceids, itservice->serviceid);
414 	}
415 	DBfree_result(result);
416 
417 	if (0 != serviceids.values_num)
418 	{
419 		its_itservices_load_parents(itservices, &serviceids);
420 		its_itservices_load_children(itservices);
421 	}
422 
423 	zbx_vector_uint64_destroy(&serviceids);
424 
425 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
426 }
427 
428 /******************************************************************************
429  *                                                                            *
430  * Function: its_itservice_update_status                                      *
431  *                                                                            *
432  * Purpose: updates service and its parents statuses                          *
433  *                                                                            *
434  * Parameters: service    - [IN] the service to update                        *
435  *             clock      - [IN] the update timestamp                         *
436  *             alarms     - [OUT] the alarms update queue                     *
437  *                                                                            *
438  * Comments: This function recalculates service status according to the       *
439  *           algorithm and status of the children services. If the status     *
440  *           has been changed, an alarm is generated and parent services      *
441  *           (up until the root service) are updated too.                     *
442  *                                                                            *
443  ******************************************************************************/
its_itservice_update_status(zbx_itservice_t * itservice,int clock,zbx_vector_ptr_t * alarms)444 static void	its_itservice_update_status(zbx_itservice_t *itservice, int clock, zbx_vector_ptr_t *alarms)
445 {
446 	int	status, i;
447 
448 	switch (itservice->algorithm)
449 	{
450 		case SERVICE_ALGORITHM_MIN:
451 			status = TRIGGER_SEVERITY_COUNT;
452 			for (i = 0; i < itservice->children.values_num; i++)
453 			{
454 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
455 
456 				if (child->status < status)
457 					status = child->status;
458 			}
459 			break;
460 		case SERVICE_ALGORITHM_MAX:
461 			status = 0;
462 			for (i = 0; i < itservice->children.values_num; i++)
463 			{
464 				zbx_itservice_t	*child = (zbx_itservice_t *)itservice->children.values[i];
465 
466 				if (child->status > status)
467 					status = child->status;
468 			}
469 			break;
470 		case SERVICE_ALGORITHM_NONE:
471 			goto out;
472 		default:
473 			zabbix_log(LOG_LEVEL_ERR, "unknown calculation algorithm of service status [%d]",
474 					itservice->algorithm);
475 			goto out;
476 	}
477 
478 	if (itservice->status != status)
479 	{
480 		itservice->status = status;
481 
482 		its_updates_append(alarms, itservice->serviceid, status, clock);
483 
484 		/* update parent services */
485 		for (i = 0; i < itservice->parents.values_num; i++)
486 			its_itservice_update_status((zbx_itservice_t *)itservice->parents.values[i], clock, alarms);
487 	}
488 out:
489 	;
490 }
491 
492 /******************************************************************************
493  *                                                                            *
494  * Function: its_updates_compare                                              *
495  *                                                                            *
496  * Purpose: used to sort service updates by source id                         *
497  *                                                                            *
498  ******************************************************************************/
its_updates_compare(const zbx_status_update_t ** update1,const zbx_status_update_t ** update2)499 static int	its_updates_compare(const zbx_status_update_t **update1, const zbx_status_update_t **update2)
500 {
501 	ZBX_RETURN_IF_NOT_EQUAL((*update1)->sourceid, (*update2)->sourceid);
502 
503 	return 0;
504 }
505 
506 /******************************************************************************
507  *                                                                            *
508  * Function: its_write_status_and_alarms                                      *
509  *                                                                            *
510  * Purpose: writes service status changes and generated service alarms into   *
511  *          database                                                          *
512  *                                                                            *
513  * Parameters: itservices - [IN] the services data                            *
514  *             alarms     - [IN] the service alarms update queue              *
515  *                                                                            *
516  * Return value: SUCCEED - the data was written successfully                  *
517  *               FAIL    - otherwise                                          *
518  *                                                                            *
519  ******************************************************************************/
its_write_status_and_alarms(zbx_itservices_t * itservices,zbx_vector_ptr_t * alarms)520 static int	its_write_status_and_alarms(zbx_itservices_t *itservices, zbx_vector_ptr_t *alarms)
521 {
522 	int			i, ret = FAIL;
523 	zbx_vector_ptr_t	updates;
524 	char			*sql = NULL;
525 	size_t			sql_alloc = 0, sql_offset = 0;
526 	zbx_uint64_t		alarmid;
527 	zbx_hashset_iter_t	iter;
528 	zbx_itservice_t		*itservice;
529 
530 	/* get a list of service status updates that must be written to database */
531 	zbx_vector_ptr_create(&updates);
532 	zbx_hashset_iter_reset(&itservices->itservices, &iter);
533 
534 	while (NULL != (itservice = (zbx_itservice_t *)zbx_hashset_iter_next(&iter)))
535 	{
536 		if (itservice->old_status != itservice->status)
537 			its_updates_append(&updates, itservice->serviceid, itservice->status, 0);
538 	}
539 
540 	/* write service status changes into database */
541 	DBbegin_multiple_update(&sql, &sql_alloc, &sql_offset);
542 
543 	if (0 != updates.values_num)
544 	{
545 		zbx_vector_ptr_sort(&updates, (zbx_compare_func_t)its_updates_compare);
546 		zbx_vector_ptr_uniq(&updates, (zbx_compare_func_t)its_updates_compare);
547 
548 		for (i = 0; i < updates.values_num; i++)
549 		{
550 			zbx_status_update_t	*update = (zbx_status_update_t *)updates.values[i];
551 
552 			zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
553 					"update services"
554 					" set status=%d"
555 					" where serviceid=" ZBX_FS_UI64 ";\n",
556 					update->status, update->sourceid);
557 
558 			if (SUCCEED != DBexecute_overflowed_sql(&sql, &sql_alloc, &sql_offset))
559 				goto out;
560 		}
561 	}
562 
563 	DBend_multiple_update(&sql, &sql_alloc, &sql_offset);
564 
565 	if (16 < sql_offset)
566 	{
567 		if (ZBX_DB_OK > DBexecute("%s", sql))
568 			goto out;
569 	}
570 
571 	ret = SUCCEED;
572 
573 	/* write generated service alarms into database */
574 	if (0 != alarms->values_num)
575 	{
576 		zbx_db_insert_t	db_insert;
577 
578 		alarmid = DBget_maxid_num("service_alarms", alarms->values_num);
579 
580 		zbx_db_insert_prepare(&db_insert, "service_alarms", "servicealarmid", "serviceid", "value", "clock",
581 				NULL);
582 
583 		for (i = 0; i < alarms->values_num; i++)
584 		{
585 			zbx_status_update_t	*update = (zbx_status_update_t *)alarms->values[i];
586 
587 			zbx_db_insert_add_values(&db_insert, alarmid++, update->sourceid, update->status,
588 					update->clock);
589 		}
590 
591 		ret = zbx_db_insert_execute(&db_insert);
592 
593 		zbx_db_insert_clean(&db_insert);
594 	}
595 out:
596 	zbx_free(sql);
597 
598 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
599 	zbx_vector_ptr_destroy(&updates);
600 
601 	return ret;
602 }
603 
604 /******************************************************************************
605  *                                                                            *
606  * Function: its_flush_updates                                                *
607  *                                                                            *
608  * Purpose: processes the service update queue                                *
609  *                                                                            *
610  * Return value: SUCCEED - the data was written successfully                  *
611  *               FAIL    - otherwise                                          *
612  *                                                                            *
613  * Comments: The following steps are taken to process the queue:              *
614  *           1) Load all services either directly referenced (with triggerid) *
615  *              by update queue or dependent on those services (directly or   *
616  *              indirectly) or required to calculate status of any loaded     *
617  *              services.                                                     *
618  *           2) Apply updates to the loaded service tree. Queue new service   *
619  *              alarms whenever service status changes.                       *
620  *           3) Write the final service status changes and the generated      *
621  *              service alarm queue into database.                            *
622  *                                                                            *
623  ******************************************************************************/
its_flush_updates(const zbx_vector_ptr_t * updates)624 static int	its_flush_updates(const zbx_vector_ptr_t *updates)
625 {
626 	const char			*__function_name = "its_flush_updates";
627 
628 	int				i, j, k, ret = FAIL;
629 	const zbx_status_update_t	*update;
630 	zbx_itservices_t		itservices;
631 	zbx_vector_ptr_t		alarms;
632 	zbx_itservice_index_t		*index;
633 	zbx_vector_uint64_t		triggerids;
634 
635 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
636 
637 	its_itservices_init(&itservices);
638 
639 	zbx_vector_uint64_create(&triggerids);
640 
641 	for (i = 0; i < updates->values_num; i++)
642 	{
643 		update = (zbx_status_update_t *)updates->values[i];
644 
645 		zbx_vector_uint64_append(&triggerids, update->sourceid);
646 	}
647 
648 	zbx_vector_uint64_sort(&triggerids, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
649 
650 	/* load all services affected by the trigger status change and      */
651 	/* the services that are required for resulting status calculations */
652 	its_load_services_by_triggerids(&itservices, &triggerids);
653 
654 	zbx_vector_uint64_destroy(&triggerids);
655 
656 	if (0 == itservices.itservices.num_data)
657 	{
658 		ret = SUCCEED;
659 		goto out;
660 	}
661 
662 	zbx_vector_ptr_create(&alarms);
663 
664 	/* apply status updates */
665 	for (i = 0; i < updates->values_num; i++)
666 	{
667 		update = (const zbx_status_update_t *)updates->values[i];
668 
669 		if (NULL == (index = (zbx_itservice_index_t *)zbx_hashset_search(&itservices.index, update)))
670 			continue;
671 
672 		/* change the status of services based on the update */
673 		for (j = 0; j < index->itservices.values_num; j++)
674 		{
675 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
676 
677 			if (SERVICE_ALGORITHM_NONE == itservice->algorithm || itservice->status == update->status)
678 				continue;
679 
680 			its_updates_append(&alarms, itservice->serviceid, update->status, update->clock);
681 			itservice->status = update->status;
682 		}
683 
684 		/* recalculate status of the parent services */
685 		for (j = 0; j < index->itservices.values_num; j++)
686 		{
687 			zbx_itservice_t	*itservice = (zbx_itservice_t *)index->itservices.values[j];
688 
689 			/* update parent services */
690 			for (k = 0; k < itservice->parents.values_num; k++)
691 				its_itservice_update_status((zbx_itservice_t *)itservice->parents.values[k], update->clock, &alarms);
692 		}
693 	}
694 
695 	ret = its_write_status_and_alarms(&itservices, &alarms);
696 
697 	zbx_vector_ptr_clear_ext(&alarms, (zbx_clean_func_t)zbx_status_update_free);
698 	zbx_vector_ptr_destroy(&alarms);
699 out:
700 	its_itservices_clean(&itservices);
701 
702 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
703 
704 	return ret;
705 }
706 
707 /*
708  * Public API
709  */
710 
711 /******************************************************************************
712  *                                                                            *
713  * Function: DBupdate_itservices                                              *
714  *                                                                            *
715  * Purpose: updates services by applying event list                           *
716  *                                                                            *
717  * Return value: SUCCEED - the services were updated successfully             *
718  *               FAIL    - otherwise                                          *
719  *                                                                            *
720  ******************************************************************************/
DBupdate_itservices(const zbx_vector_ptr_t * trigger_diff)721 int	DBupdate_itservices(const zbx_vector_ptr_t *trigger_diff)
722 {
723 	const char			*__function_name = "DBupdate_itservices";
724 
725 	int				ret = SUCCEED;
726 	zbx_vector_ptr_t		updates;
727 	int				i;
728 	const zbx_trigger_diff_t	*diff;
729 
730 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
731 
732 	zbx_vector_ptr_create(&updates);
733 
734 	for (i = 0; i < trigger_diff->values_num; i++)
735 	{
736 		diff = (zbx_trigger_diff_t *)trigger_diff->values[i];
737 
738 		if (0 == (diff->flags & ZBX_FLAGS_TRIGGER_DIFF_UPDATE_VALUE))
739 			continue;
740 
741 		its_updates_append(&updates, diff->triggerid, TRIGGER_VALUE_PROBLEM == diff->value ?
742 				diff->priority : 0, diff->lastchange);
743 	}
744 
745 	if (0 != updates.values_num)
746 	{
747 		LOCK_ITSERVICES;
748 
749 		do
750 		{
751 			DBbegin();
752 
753 			ret = its_flush_updates(&updates);
754 		}
755 		while (ZBX_DB_DOWN == DBcommit());
756 
757 		UNLOCK_ITSERVICES;
758 
759 		zbx_vector_ptr_clear_ext(&updates, zbx_ptr_free);
760 	}
761 
762 	zbx_vector_ptr_destroy(&updates);
763 
764 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
765 
766 	return ret;
767 }
768 
769 /******************************************************************************
770  *                                                                            *
771  * Function: DBremove_itservice_triggers                                      *
772  *                                                                            *
773  * Purpose: removes specified trigger ids from dependent services and reset   *
774  *          the status of those services to the default value (0)             *
775  *                                                                            *
776  * Parameters: triggerids     - [IN] an array of trigger ids to remove        *
777  *             triggerids_num - [IN] the number of items in triggerids array  *
778  *                                                                            *
779  * Return value: SUCCEED - the data was written successfully                  *
780  *               FAIL    - otherwise                                          *
781  *                                                                            *
782  ******************************************************************************/
DBremove_triggers_from_itservices(zbx_uint64_t * triggerids,int triggerids_num)783 int	DBremove_triggers_from_itservices(zbx_uint64_t *triggerids, int triggerids_num)
784 {
785 	char			*sql = NULL;
786 	size_t			sql_alloc = 0, sql_offset = 0;
787 	zbx_vector_ptr_t	updates;
788 	int			i, ret = FAIL, now;
789 
790 	if (0 == triggerids_num)
791 		return SUCCEED;
792 
793 	now = time(NULL);
794 
795 	zbx_vector_ptr_create(&updates);
796 
797 	for (i = 0; i < triggerids_num; i++)
798 		its_updates_append(&updates, triggerids[i], 0, now);
799 
800 	LOCK_ITSERVICES;
801 
802 	if (FAIL == its_flush_updates(&updates))
803 		goto out;
804 
805 	zbx_strcpy_alloc(&sql, &sql_alloc, &sql_offset, "update services set triggerid=null,showsla=0 where");
806 	DBadd_condition_alloc(&sql, &sql_alloc, &sql_offset, "triggerid", triggerids, triggerids_num);
807 
808 	if (ZBX_DB_OK <= DBexecute("%s", sql))
809 		ret = SUCCEED;
810 
811 	zbx_free(sql);
812 out:
813 	UNLOCK_ITSERVICES;
814 
815 	zbx_vector_ptr_clear_ext(&updates, (zbx_clean_func_t)zbx_status_update_free);
816 	zbx_vector_ptr_destroy(&updates);
817 
818 	return ret;
819 }
820 
zbx_create_itservices_lock(char ** error)821 int	zbx_create_itservices_lock(char **error)
822 {
823 	return zbx_mutex_create(&itservices_lock, ZBX_MUTEX_ITSERVICES, error);
824 }
825 
zbx_destroy_itservices_lock(void)826 void	zbx_destroy_itservices_lock(void)
827 {
828 	zbx_mutex_destroy(&itservices_lock);
829 }
830