1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "db.h"
22 #include "log.h"
23 #include "events.h"
24 #include "discovery.h"
25 
discovery_get_dhost_by_value(zbx_uint64_t dcheckid,const char * value)26 static DB_RESULT	discovery_get_dhost_by_value(zbx_uint64_t dcheckid, const char *value)
27 {
28 	DB_RESULT	result;
29 	char		*value_esc;
30 
31 	value_esc = DBdyn_escape_field("dservices", "value", value);
32 
33 	result = DBselect(
34 			"select dh.dhostid,dh.status,dh.lastup,dh.lastdown"
35 			" from dhosts dh,dservices ds"
36 			" where ds.dhostid=dh.dhostid"
37 				" and ds.dcheckid=" ZBX_FS_UI64
38 				" and ds.value" ZBX_SQL_STRCMP
39 			" order by dh.dhostid",
40 			dcheckid, ZBX_SQL_STRVAL_EQ(value_esc));
41 
42 	zbx_free(value_esc);
43 
44 	return result;
45 }
46 
discovery_get_dhost_by_ip(zbx_uint64_t druleid,const char * ip)47 static DB_RESULT	discovery_get_dhost_by_ip(zbx_uint64_t druleid, const char *ip)
48 {
49 	DB_RESULT	result;
50 	char		*ip_esc;
51 
52 	ip_esc = DBdyn_escape_field("dservices", "ip", ip);
53 
54 	result = DBselect(
55 			"select dh.dhostid,dh.status,dh.lastup,dh.lastdown"
56 			" from dhosts dh,dservices ds"
57 			" where ds.dhostid=dh.dhostid"
58 				" and dh.druleid=" ZBX_FS_UI64
59 				" and ds.ip" ZBX_SQL_STRCMP
60 			" order by dh.dhostid",
61 			druleid, ZBX_SQL_STRVAL_EQ(ip_esc));
62 
63 	zbx_free(ip_esc);
64 
65 	return result;
66 }
67 
68 /******************************************************************************
69  *                                                                            *
70  * Function: discovery_separate_host                                          *
71  *                                                                            *
72  * Purpose: separate multiple-IP hosts                                        *
73  *                                                                            *
74  * Parameters: host ip address                                                *
75  *                                                                            *
76  ******************************************************************************/
discovery_separate_host(DB_DRULE * drule,DB_DHOST * dhost,const char * ip)77 static void	discovery_separate_host(DB_DRULE *drule, DB_DHOST *dhost, const char *ip)
78 {
79 	const char	*__function_name = "discovery_separate_host";
80 
81 	DB_RESULT	result;
82 	DB_ROW		row;
83 	char		*ip_esc, *sql = NULL;
84 	zbx_uint64_t	dhostid;
85 
86 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s'", __function_name, ip);
87 
88 	ip_esc = DBdyn_escape_field("dservices", "ip", ip);
89 
90 	sql = zbx_dsprintf(sql,
91 			"select dserviceid"
92 			" from dservices"
93 			" where dhostid=" ZBX_FS_UI64
94 				" and ip" ZBX_SQL_STRCMP,
95 			dhost->dhostid, ZBX_SQL_STRVAL_NE(ip_esc));
96 
97 	result = DBselectN(sql, 1);
98 
99 	if (NULL != (row = DBfetch(result)))
100 	{
101 		dhostid = DBget_maxid("dhosts");
102 
103 		DBexecute("insert into dhosts (dhostid,druleid)"
104 				" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
105 				dhostid, drule->druleid);
106 
107 		DBexecute("update dservices"
108 				" set dhostid=" ZBX_FS_UI64
109 				" where dhostid=" ZBX_FS_UI64
110 					" and ip" ZBX_SQL_STRCMP,
111 				dhostid, dhost->dhostid, ZBX_SQL_STRVAL_EQ(ip_esc));
112 
113 		dhost->dhostid = dhostid;
114 		dhost->status = DOBJECT_STATUS_DOWN;
115 		dhost->lastup = 0;
116 		dhost->lastdown = 0;
117 	}
118 	DBfree_result(result);
119 
120 	zbx_free(sql);
121 	zbx_free(ip_esc);
122 
123 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
124 }
125 
126 /******************************************************************************
127  *                                                                            *
128  * Function: discovery_register_host                                          *
129  *                                                                            *
130  * Purpose: register host if one does not exist                               *
131  *                                                                            *
132  * Parameters: host ip address                                                *
133  *                                                                            *
134  ******************************************************************************/
discovery_register_host(DB_DRULE * drule,DB_DCHECK * dcheck,DB_DHOST * dhost,const char * ip,int status,const char * value)135 static void	discovery_register_host(DB_DRULE *drule, DB_DCHECK *dcheck, DB_DHOST *dhost,
136 		const char *ip, int status, const char *value)
137 {
138 	const char	*__function_name = "discovery_register_host";
139 
140 	DB_RESULT	result;
141 	DB_ROW		row;
142 
143 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' status:%d value:'%s'",
144 			__function_name, ip, status, value);
145 
146 	if (drule->unique_dcheckid == dcheck->dcheckid)
147 	{
148 		result = discovery_get_dhost_by_value(dcheck->dcheckid, value);
149 
150 		if (NULL == (row = DBfetch(result)))
151 		{
152 			DBfree_result(result);
153 
154 			result = discovery_get_dhost_by_ip(drule->druleid, ip);
155 			row = DBfetch(result);
156 		}
157 	}
158 	else
159 	{
160 		result = discovery_get_dhost_by_ip(drule->druleid, ip);
161 		row = DBfetch(result);
162 	}
163 
164 	if (NULL == row)
165 	{
166 		if (DOBJECT_STATUS_UP == status)	/* add host only if service is up */
167 		{
168 			zabbix_log(LOG_LEVEL_DEBUG, "new host discovered at %s", ip);
169 
170 			dhost->dhostid = DBget_maxid("dhosts");
171 			dhost->status = DOBJECT_STATUS_DOWN;
172 			dhost->lastup = 0;
173 			dhost->lastdown = 0;
174 
175 			DBexecute("insert into dhosts (dhostid,druleid)"
176 					" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
177 					dhost->dhostid, drule->druleid);
178 		}
179 	}
180 	else
181 	{
182 		zabbix_log(LOG_LEVEL_DEBUG, "host at %s is already in database", ip);
183 
184 		ZBX_STR2UINT64(dhost->dhostid, row[0]);
185 		dhost->status = atoi(row[1]);
186 		dhost->lastup = atoi(row[2]);
187 		dhost->lastdown = atoi(row[3]);
188 
189 		if (0 == drule->unique_dcheckid)
190 			discovery_separate_host(drule, dhost, ip);
191 	}
192 	DBfree_result(result);
193 
194 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
195 }
196 
197 /******************************************************************************
198  *                                                                            *
199  * Function: discovery_register_service                                       *
200  *                                                                            *
201  * Purpose: register service if one does not exist                            *
202  *                                                                            *
203  * Parameters: host ip address                                                *
204  *                                                                            *
205  ******************************************************************************/
discovery_register_service(DB_DCHECK * dcheck,DB_DHOST * dhost,DB_DSERVICE * dservice,const char * ip,const char * dns,int port,int status)206 static void	discovery_register_service(DB_DCHECK *dcheck, DB_DHOST *dhost, DB_DSERVICE *dservice, const char *ip,
207 		const char *dns, int port, int status)
208 {
209 	const char	*__function_name = "discovery_register_service";
210 
211 	DB_RESULT	result;
212 	DB_ROW		row;
213 	char		*key_esc, *ip_esc, *dns_esc;
214 
215 	zbx_uint64_t	dhostid;
216 
217 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' port:%d key:'%s'",
218 			__function_name, ip, port, dcheck->key_);
219 
220 	key_esc = DBdyn_escape_field("dservices", "key_", dcheck->key_);
221 	ip_esc = DBdyn_escape_field("dservices", "ip", ip);
222 
223 	result = DBselect(
224 			"select dserviceid,dhostid,status,lastup,lastdown,value,dns"
225 			" from dservices"
226 			" where dcheckid=" ZBX_FS_UI64
227 				" and type=%d"
228 				" and key_" ZBX_SQL_STRCMP
229 				" and ip" ZBX_SQL_STRCMP
230 				" and port=%d",
231 			dcheck->dcheckid,
232 			dcheck->type,
233 			ZBX_SQL_STRVAL_EQ(key_esc),
234 			ZBX_SQL_STRVAL_EQ(ip_esc),
235 			port);
236 
237 	if (NULL == (row = DBfetch(result)))
238 	{
239 		if (DOBJECT_STATUS_UP == status)	/* add host only if service is up */
240 		{
241 			zabbix_log(LOG_LEVEL_DEBUG, "new service discovered on port %d", port);
242 
243 			dservice->dserviceid = DBget_maxid("dservices");
244 			dservice->status = DOBJECT_STATUS_DOWN;
245 			dservice->value = zbx_strdup(dservice->value, "");
246 
247 			dns_esc = DBdyn_escape_field("dservices", "dns", dns);
248 
249 			DBexecute("insert into dservices (dserviceid,dhostid,dcheckid,type,key_,ip,dns,port,status)"
250 					" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ",%d,'%s','%s','%s',%d,%d)",
251 					dservice->dserviceid, dhost->dhostid, dcheck->dcheckid, dcheck->type,
252 					key_esc, ip_esc, dns_esc, port, dservice->status);
253 
254 			zbx_free(dns_esc);
255 		}
256 	}
257 	else
258 	{
259 		zabbix_log(LOG_LEVEL_DEBUG, "service is already in database");
260 
261 		ZBX_STR2UINT64(dservice->dserviceid, row[0]);
262 		ZBX_STR2UINT64(dhostid, row[1]);
263 		dservice->status = atoi(row[2]);
264 		dservice->lastup = atoi(row[3]);
265 		dservice->lastdown = atoi(row[4]);
266 		dservice->value = zbx_strdup(dservice->value, row[5]);
267 
268 		if (dhostid != dhost->dhostid)
269 		{
270 			DBexecute("update dservices"
271 					" set dhostid=" ZBX_FS_UI64
272 					" where dhostid=" ZBX_FS_UI64,
273 					dhost->dhostid, dhostid);
274 
275 			DBexecute("delete from dhosts"
276 					" where dhostid=" ZBX_FS_UI64,
277 					dhostid);
278 		}
279 
280 		if (0 != strcmp(row[6], dns))
281 		{
282 			dns_esc = DBdyn_escape_field("dservices", "dns", dns);
283 
284 			DBexecute("update dservices"
285 					" set dns='%s'"
286 					" where dserviceid=" ZBX_FS_UI64,
287 					dns_esc, dservice->dserviceid);
288 
289 			zbx_free(dns_esc);
290 		}
291 	}
292 	DBfree_result(result);
293 
294 	zbx_free(ip_esc);
295 	zbx_free(key_esc);
296 
297 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
298 }
299 
300 /******************************************************************************
301  *                                                                            *
302  * Function: discovery_update_dservice                                        *
303  *                                                                            *
304  * Purpose: update discovered service details                                 *
305  *                                                                            *
306  ******************************************************************************/
discovery_update_dservice(zbx_uint64_t dserviceid,int status,int lastup,int lastdown,const char * value)307 static void	discovery_update_dservice(zbx_uint64_t dserviceid, int status, int lastup, int lastdown,
308 		const char *value)
309 {
310 	char	*value_esc;
311 
312 	value_esc = DBdyn_escape_field("dservices", "value", value);
313 
314 	DBexecute("update dservices set status=%d,lastup=%d,lastdown=%d,value='%s' where dserviceid=" ZBX_FS_UI64,
315 			status, lastup, lastdown, value_esc, dserviceid);
316 
317 	zbx_free(value_esc);
318 }
319 
320 /******************************************************************************
321  *                                                                            *
322  * Function: discovery_update_dservice_value                                  *
323  *                                                                            *
324  * Purpose: update discovered service details                                 *
325  *                                                                            *
326  ******************************************************************************/
discovery_update_dservice_value(zbx_uint64_t dserviceid,const char * value)327 static void	discovery_update_dservice_value(zbx_uint64_t dserviceid, const char *value)
328 {
329 	char	*value_esc;
330 
331 	value_esc = DBdyn_escape_field("dservices", "value", value);
332 
333 	DBexecute("update dservices set value='%s' where dserviceid=" ZBX_FS_UI64, value_esc, dserviceid);
334 
335 	zbx_free(value_esc);
336 }
337 
338 /******************************************************************************
339  *                                                                            *
340  * Function: discovery_update_dhost                                           *
341  *                                                                            *
342  * Purpose: update discovered host details                                    *
343  *                                                                            *
344  ******************************************************************************/
discovery_update_dhost(const DB_DHOST * dhost)345 static void	discovery_update_dhost(const DB_DHOST *dhost)
346 {
347 	DBexecute("update dhosts set status=%d,lastup=%d,lastdown=%d where dhostid=" ZBX_FS_UI64,
348 			dhost->status, dhost->lastup, dhost->lastdown, dhost->dhostid);
349 }
350 
351 /******************************************************************************
352  *                                                                            *
353  * Function: discovery_update_service_status                                  *
354  *                                                                            *
355  * Purpose: process and update the new service status                         *
356  *                                                                            *
357  ******************************************************************************/
discovery_update_service_status(DB_DHOST * dhost,const DB_DSERVICE * dservice,int service_status,const char * value,int now)358 static void	discovery_update_service_status(DB_DHOST *dhost, const DB_DSERVICE *dservice, int service_status,
359 		const char *value, int now)
360 {
361 	const char	*__function_name = "discovery_update_service_status";
362 
363 	zbx_timespec_t	ts;
364 
365 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
366 
367 	ts.sec = now;
368 	ts.ns = 0;
369 
370 	if (DOBJECT_STATUS_UP == service_status)
371 	{
372 		if (DOBJECT_STATUS_DOWN == dservice->status || 0 == dservice->lastup)
373 		{
374 			discovery_update_dservice(dservice->dserviceid, service_status, now, 0, value);
375 			add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts,
376 					DOBJECT_STATUS_DISCOVER, NULL, NULL, 0, 0);
377 
378 			if (DOBJECT_STATUS_DOWN == dhost->status)
379 			{
380 				/* Service went UP, but host status is DOWN. Update host status. */
381 
382 				dhost->status = DOBJECT_STATUS_UP;
383 				dhost->lastup = now;
384 				dhost->lastdown = 0;
385 
386 				discovery_update_dhost(dhost);
387 				add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
388 						DOBJECT_STATUS_DISCOVER, NULL, NULL, 0, 0);
389 			}
390 		}
391 		else if (0 != strcmp(dservice->value, value))
392 		{
393 			discovery_update_dservice_value(dservice->dserviceid, value);
394 		}
395 	}
396 	else	/* DOBJECT_STATUS_DOWN */
397 	{
398 		if (DOBJECT_STATUS_UP == dservice->status || 0 == dservice->lastdown)
399 		{
400 			discovery_update_dservice(dservice->dserviceid, service_status, 0, now, dservice->value);
401 			add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts,
402 					DOBJECT_STATUS_LOST, NULL, NULL, 0, 0);
403 
404 			/* service went DOWN, no need to update host status here as other services may be UP */
405 		}
406 	}
407 	add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts, service_status,
408 			NULL, NULL, 0, 0);
409 
410 	process_events(NULL);
411 
412 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
413 }
414 
415 /******************************************************************************
416  *                                                                            *
417  * Function: discovery_update_host_status                                     *
418  *                                                                            *
419  * Purpose: update new host status                                            *
420  *                                                                            *
421  ******************************************************************************/
discovery_update_host_status(DB_DHOST * dhost,int status,int now)422 static void	discovery_update_host_status(DB_DHOST *dhost, int status, int now)
423 {
424 	zbx_timespec_t	ts;
425 
426 	ts.sec = now;
427 	ts.ns = 0;
428 
429 	/* update host status */
430 	if (DOBJECT_STATUS_UP == status)
431 	{
432 		if (DOBJECT_STATUS_DOWN == dhost->status || 0 == dhost->lastup)
433 		{
434 			dhost->status = status;
435 			dhost->lastdown = 0;
436 			dhost->lastup = now;
437 
438 			discovery_update_dhost(dhost);
439 			add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
440 					DOBJECT_STATUS_DISCOVER, NULL, NULL, 0, 0);
441 		}
442 	}
443 	else	/* DOBJECT_STATUS_DOWN */
444 	{
445 		if (DOBJECT_STATUS_UP == dhost->status || 0 == dhost->lastdown)
446 		{
447 			dhost->status = status;
448 			dhost->lastdown = now;
449 			dhost->lastup = 0;
450 
451 			discovery_update_dhost(dhost);
452 			add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
453 					DOBJECT_STATUS_LOST, NULL, NULL, 0, 0);
454 		}
455 	}
456 	add_event(0, EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts, status, NULL, NULL, 0, 0);
457 
458 	process_events(NULL);
459 }
460 
461 /******************************************************************************
462  *                                                                            *
463  * Function: discovery_update_host                                            *
464  *                                                                            *
465  * Purpose: process new host status                                           *
466  *                                                                            *
467  * Parameters: host - host info                                               *
468  *                                                                            *
469  ******************************************************************************/
discovery_update_host(DB_DHOST * dhost,int status,int now)470 void	discovery_update_host(DB_DHOST *dhost, int status, int now)
471 {
472 	const char	*__function_name = "discovery_update_host";
473 
474 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
475 
476 	if (0 != dhost->dhostid)
477 		discovery_update_host_status(dhost, status, now);
478 
479 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
480 }
481 
482 /******************************************************************************
483  *                                                                            *
484  * Function: discovery_update_service                                         *
485  *                                                                            *
486  * Purpose: process new service status                                        *
487  *                                                                            *
488  * Parameters: service - service info                                         *
489  *                                                                            *
490  ******************************************************************************/
discovery_update_service(DB_DRULE * drule,DB_DCHECK * dcheck,DB_DHOST * dhost,const char * ip,const char * dns,int port,int status,const char * value,int now)491 void	discovery_update_service(DB_DRULE *drule, DB_DCHECK *dcheck, DB_DHOST *dhost, const char *ip, const char *dns,
492 		int port, int status, const char *value, int now)
493 {
494 	const char	*__function_name = "discovery_update_service";
495 
496 	DB_DSERVICE	dservice;
497 
498 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' dns:'%s' port:%d status:%d value:'%s'",
499 			__function_name, ip, dns, port, status, value);
500 
501 	memset(&dservice, 0, sizeof(dservice));
502 
503 	/* register host if is not registered yet */
504 	if (0 == dhost->dhostid)
505 		discovery_register_host(drule, dcheck, dhost, ip, status, value);
506 
507 	/* register service if is not registered yet */
508 	if (0 != dhost->dhostid)
509 		discovery_register_service(dcheck, dhost, &dservice, ip, dns, port, status);
510 
511 	/* service was not registered because we do not add down service */
512 	if (0 != dservice.dserviceid)
513 		discovery_update_service_status(dhost, &dservice, status, value, now);
514 
515 	zbx_free(dservice.value);
516 
517 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
518 }
519