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_port(zbx_uint64_t druleid,const char * ip,int port)47 static DB_RESULT	discovery_get_dhost_by_ip_port(zbx_uint64_t druleid, const char *ip, int port)
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 				" and ds.port=%d"
61 			" order by dh.dhostid",
62 			druleid, ZBX_SQL_STRVAL_EQ(ip_esc), port);
63 
64 	zbx_free(ip_esc);
65 
66 	return result;
67 }
68 
69 /******************************************************************************
70  *                                                                            *
71  * Function: discovery_separate_host                                          *
72  *                                                                            *
73  * Purpose: separate multiple-IP hosts                                        *
74  *                                                                            *
75  * Parameters: host ip address                                                *
76  *                                                                            *
77  ******************************************************************************/
discovery_separate_host(DB_DRULE * drule,DB_DHOST * dhost,const char * ip)78 static void	discovery_separate_host(DB_DRULE *drule, DB_DHOST *dhost, const char *ip)
79 {
80 	const char	*__function_name = "discovery_separate_host";
81 
82 	DB_RESULT	result;
83 	DB_ROW		row;
84 	char		*ip_esc, *sql = NULL;
85 	zbx_uint64_t	dhostid;
86 
87 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s'", __function_name, ip);
88 
89 	ip_esc = DBdyn_escape_field("dservices", "ip", ip);
90 
91 	sql = zbx_dsprintf(sql,
92 			"select dserviceid"
93 			" from dservices"
94 			" where dhostid=" ZBX_FS_UI64
95 				" and ip" ZBX_SQL_STRCMP,
96 			dhost->dhostid, ZBX_SQL_STRVAL_NE(ip_esc));
97 
98 	result = DBselectN(sql, 1);
99 
100 	if (NULL != (row = DBfetch(result)))
101 	{
102 		dhostid = DBget_maxid("dhosts");
103 
104 		DBexecute("insert into dhosts (dhostid,druleid)"
105 				" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
106 				dhostid, drule->druleid);
107 
108 		DBexecute("update dservices"
109 				" set dhostid=" ZBX_FS_UI64
110 				" where dhostid=" ZBX_FS_UI64
111 					" and ip" ZBX_SQL_STRCMP,
112 				dhostid, dhost->dhostid, ZBX_SQL_STRVAL_EQ(ip_esc));
113 
114 		dhost->dhostid = dhostid;
115 		dhost->status = DOBJECT_STATUS_DOWN;
116 		dhost->lastup = 0;
117 		dhost->lastdown = 0;
118 	}
119 	DBfree_result(result);
120 
121 	zbx_free(sql);
122 	zbx_free(ip_esc);
123 
124 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
125 }
126 
127 /******************************************************************************
128  *                                                                            *
129  * Function: discovery_register_host                                          *
130  *                                                                            *
131  * Purpose: register host if one does not exist                               *
132  *                                                                            *
133  * Parameters: host ip address                                                *
134  *                                                                            *
135  ******************************************************************************/
discovery_register_host(DB_DRULE * drule,zbx_uint64_t dcheckid,DB_DHOST * dhost,const char * ip,int port,int status,const char * value)136 static void	discovery_register_host(DB_DRULE *drule, zbx_uint64_t dcheckid, DB_DHOST *dhost,
137 		const char *ip, int port, int status, const char *value)
138 {
139 	const char	*__function_name = "discovery_register_host";
140 
141 	DB_RESULT	result;
142 	DB_ROW		row;
143 	int		match_value = 0;
144 
145 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' status:%d value:'%s'",
146 			__function_name, ip, status, value);
147 
148 	if (drule->unique_dcheckid == dcheckid)
149 	{
150 		result = discovery_get_dhost_by_value(dcheckid, value);
151 
152 		if (NULL == (row = DBfetch(result)))
153 		{
154 			DBfree_result(result);
155 
156 			result = discovery_get_dhost_by_ip_port(drule->druleid, ip, port);
157 			row = DBfetch(result);
158 		}
159 		else
160 			match_value = 1;
161 
162 	}
163 	else
164 	{
165 		result = discovery_get_dhost_by_ip_port(drule->druleid, ip, port);
166 		row = DBfetch(result);
167 	}
168 
169 	if (NULL == row)
170 	{
171 		if (DOBJECT_STATUS_UP == status)	/* add host only if service is up */
172 		{
173 			zabbix_log(LOG_LEVEL_DEBUG, "new host discovered at %s", ip);
174 
175 			dhost->dhostid = DBget_maxid("dhosts");
176 			dhost->status = DOBJECT_STATUS_DOWN;
177 			dhost->lastup = 0;
178 			dhost->lastdown = 0;
179 
180 			DBexecute("insert into dhosts (dhostid,druleid)"
181 					" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 ")",
182 					dhost->dhostid, drule->druleid);
183 		}
184 	}
185 	else
186 	{
187 		zabbix_log(LOG_LEVEL_DEBUG, "host at %s is already in database", ip);
188 
189 		ZBX_STR2UINT64(dhost->dhostid, row[0]);
190 		dhost->status = atoi(row[1]);
191 		dhost->lastup = atoi(row[2]);
192 		dhost->lastdown = atoi(row[3]);
193 
194 		if (0 == match_value)
195 			discovery_separate_host(drule, dhost, ip);
196 	}
197 	DBfree_result(result);
198 
199 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
200 }
201 
202 /******************************************************************************
203  *                                                                            *
204  * Function: discovery_register_service                                       *
205  *                                                                            *
206  * Purpose: register service if one does not exist                            *
207  *                                                                            *
208  * Parameters: host ip address                                                *
209  *                                                                            *
210  ******************************************************************************/
discovery_register_service(zbx_uint64_t dcheckid,DB_DHOST * dhost,DB_DSERVICE * dservice,const char * ip,const char * dns,int port,int status)211 static void	discovery_register_service(zbx_uint64_t dcheckid, DB_DHOST *dhost, DB_DSERVICE *dservice,
212 		const char *ip, const char *dns, int port, int status)
213 {
214 	const char	*__function_name = "discovery_register_service";
215 
216 	DB_RESULT	result;
217 	DB_ROW		row;
218 	char		*ip_esc, *dns_esc;
219 
220 	zbx_uint64_t	dhostid;
221 
222 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' port:%d", __function_name, ip, port);
223 
224 	ip_esc = DBdyn_escape_field("dservices", "ip", ip);
225 
226 	result = DBselect(
227 			"select dserviceid,dhostid,status,lastup,lastdown,value,dns"
228 			" from dservices"
229 			" where dcheckid=" ZBX_FS_UI64
230 				" and ip" ZBX_SQL_STRCMP
231 				" and port=%d",
232 			dcheckid, ZBX_SQL_STRVAL_EQ(ip_esc), port);
233 
234 	if (NULL == (row = DBfetch(result)))
235 	{
236 		if (DOBJECT_STATUS_UP == status)	/* add host only if service is up */
237 		{
238 			zabbix_log(LOG_LEVEL_DEBUG, "new service discovered on port %d", port);
239 
240 			dservice->dserviceid = DBget_maxid("dservices");
241 			dservice->status = DOBJECT_STATUS_DOWN;
242 			dservice->value = zbx_strdup(dservice->value, "");
243 
244 			dns_esc = DBdyn_escape_field("dservices", "dns", dns);
245 
246 			DBexecute("insert into dservices (dserviceid,dhostid,dcheckid,ip,dns,port,status)"
247 					" values (" ZBX_FS_UI64 "," ZBX_FS_UI64 "," ZBX_FS_UI64 ",'%s','%s',%d,%d)",
248 					dservice->dserviceid, dhost->dhostid, dcheckid, ip_esc, dns_esc, port,
249 					dservice->status);
250 
251 			zbx_free(dns_esc);
252 		}
253 	}
254 	else
255 	{
256 		zabbix_log(LOG_LEVEL_DEBUG, "service is already in database");
257 
258 		ZBX_STR2UINT64(dservice->dserviceid, row[0]);
259 		ZBX_STR2UINT64(dhostid, row[1]);
260 		dservice->status = atoi(row[2]);
261 		dservice->lastup = atoi(row[3]);
262 		dservice->lastdown = atoi(row[4]);
263 		dservice->value = zbx_strdup(dservice->value, row[5]);
264 
265 		if (dhostid != dhost->dhostid)
266 		{
267 			DBexecute("update dservices"
268 					" set dhostid=" ZBX_FS_UI64
269 					" where dhostid=" ZBX_FS_UI64,
270 					dhost->dhostid, dhostid);
271 
272 			DBexecute("delete from dhosts"
273 					" where dhostid=" ZBX_FS_UI64,
274 					dhostid);
275 		}
276 
277 		if (0 != strcmp(row[6], dns))
278 		{
279 			dns_esc = DBdyn_escape_field("dservices", "dns", dns);
280 
281 			DBexecute("update dservices"
282 					" set dns='%s'"
283 					" where dserviceid=" ZBX_FS_UI64,
284 					dns_esc, dservice->dserviceid);
285 
286 			zbx_free(dns_esc);
287 		}
288 	}
289 	DBfree_result(result);
290 
291 	zbx_free(ip_esc);
292 
293 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
294 }
295 
296 /******************************************************************************
297  *                                                                            *
298  * Function: discovery_update_dservice                                        *
299  *                                                                            *
300  * Purpose: update discovered service details                                 *
301  *                                                                            *
302  ******************************************************************************/
discovery_update_dservice(zbx_uint64_t dserviceid,int status,int lastup,int lastdown,const char * value)303 static void	discovery_update_dservice(zbx_uint64_t dserviceid, int status, int lastup, int lastdown,
304 		const char *value)
305 {
306 	char	*value_esc;
307 
308 	value_esc = DBdyn_escape_field("dservices", "value", value);
309 
310 	DBexecute("update dservices set status=%d,lastup=%d,lastdown=%d,value='%s' where dserviceid=" ZBX_FS_UI64,
311 			status, lastup, lastdown, value_esc, dserviceid);
312 
313 	zbx_free(value_esc);
314 }
315 
316 /******************************************************************************
317  *                                                                            *
318  * Function: discovery_update_dservice_value                                  *
319  *                                                                            *
320  * Purpose: update discovered service details                                 *
321  *                                                                            *
322  ******************************************************************************/
discovery_update_dservice_value(zbx_uint64_t dserviceid,const char * value)323 static void	discovery_update_dservice_value(zbx_uint64_t dserviceid, const char *value)
324 {
325 	char	*value_esc;
326 
327 	value_esc = DBdyn_escape_field("dservices", "value", value);
328 
329 	DBexecute("update dservices set value='%s' where dserviceid=" ZBX_FS_UI64, value_esc, dserviceid);
330 
331 	zbx_free(value_esc);
332 }
333 
334 /******************************************************************************
335  *                                                                            *
336  * Function: discovery_update_dhost                                           *
337  *                                                                            *
338  * Purpose: update discovered host details                                    *
339  *                                                                            *
340  ******************************************************************************/
discovery_update_dhost(const DB_DHOST * dhost)341 static void	discovery_update_dhost(const DB_DHOST *dhost)
342 {
343 	DBexecute("update dhosts set status=%d,lastup=%d,lastdown=%d where dhostid=" ZBX_FS_UI64,
344 			dhost->status, dhost->lastup, dhost->lastdown, dhost->dhostid);
345 }
346 
347 /******************************************************************************
348  *                                                                            *
349  * Function: discovery_update_service_status                                  *
350  *                                                                            *
351  * Purpose: process and update the new service status                         *
352  *                                                                            *
353  ******************************************************************************/
discovery_update_service_status(DB_DHOST * dhost,const DB_DSERVICE * dservice,int service_status,const char * value,int now)354 static void	discovery_update_service_status(DB_DHOST *dhost, const DB_DSERVICE *dservice, int service_status,
355 		const char *value, int now)
356 {
357 	const char	*__function_name = "discovery_update_service_status";
358 
359 	zbx_timespec_t	ts;
360 
361 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
362 
363 	ts.sec = now;
364 	ts.ns = 0;
365 
366 	if (DOBJECT_STATUS_UP == service_status)
367 	{
368 		if (DOBJECT_STATUS_DOWN == dservice->status || 0 == dservice->lastup)
369 		{
370 			discovery_update_dservice(dservice->dserviceid, service_status, now, 0, value);
371 			zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts,
372 					DOBJECT_STATUS_DISCOVER, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL);
373 
374 			if (DOBJECT_STATUS_DOWN == dhost->status)
375 			{
376 				/* Service went UP, but host status is DOWN. Update host status. */
377 
378 				dhost->status = DOBJECT_STATUS_UP;
379 				dhost->lastup = now;
380 				dhost->lastdown = 0;
381 
382 				discovery_update_dhost(dhost);
383 				zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
384 						DOBJECT_STATUS_DISCOVER, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0,
385 						NULL);
386 			}
387 		}
388 		else if (0 != strcmp(dservice->value, value))
389 		{
390 			discovery_update_dservice_value(dservice->dserviceid, value);
391 		}
392 	}
393 	else	/* DOBJECT_STATUS_DOWN */
394 	{
395 		if (DOBJECT_STATUS_UP == dservice->status || 0 == dservice->lastdown)
396 		{
397 			discovery_update_dservice(dservice->dserviceid, service_status, 0, now, dservice->value);
398 			zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts,
399 					DOBJECT_STATUS_LOST, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL);
400 
401 			/* service went DOWN, no need to update host status here as other services may be UP */
402 		}
403 	}
404 	zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts, service_status,
405 			NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL);
406 
407 	zbx_process_events(NULL, NULL);
408 	zbx_clean_events();
409 
410 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
411 }
412 
413 /******************************************************************************
414  *                                                                            *
415  * Function: discovery_update_host_status                                     *
416  *                                                                            *
417  * Purpose: update new host status                                            *
418  *                                                                            *
419  ******************************************************************************/
discovery_update_host_status(DB_DHOST * dhost,int status,int now)420 static void	discovery_update_host_status(DB_DHOST *dhost, int status, int now)
421 {
422 	zbx_timespec_t	ts;
423 
424 	ts.sec = now;
425 	ts.ns = 0;
426 
427 	/* update host status */
428 	if (DOBJECT_STATUS_UP == status)
429 	{
430 		if (DOBJECT_STATUS_DOWN == dhost->status || 0 == dhost->lastup)
431 		{
432 			dhost->status = status;
433 			dhost->lastdown = 0;
434 			dhost->lastup = now;
435 
436 			discovery_update_dhost(dhost);
437 			zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
438 					DOBJECT_STATUS_DISCOVER, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL);
439 		}
440 	}
441 	else	/* DOBJECT_STATUS_DOWN */
442 	{
443 		if (DOBJECT_STATUS_UP == dhost->status || 0 == dhost->lastdown)
444 		{
445 			dhost->status = status;
446 			dhost->lastdown = now;
447 			dhost->lastup = 0;
448 
449 			discovery_update_dhost(dhost);
450 			zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
451 					DOBJECT_STATUS_LOST, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL);
452 		}
453 	}
454 	zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts, status, NULL, NULL, NULL, 0, 0,
455 			NULL, 0, NULL, 0, NULL);
456 
457 	zbx_process_events(NULL, NULL);
458 	zbx_clean_events();
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,zbx_uint64_t dcheckid,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, zbx_uint64_t dcheckid, DB_DHOST *dhost, const char *ip,
492 		const char *dns, 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, dcheckid, dhost, ip, port, status, value);
506 
507 	/* register service if is not registered yet */
508 	if (0 != dhost->dhostid)
509 		discovery_register_service(dcheckid, 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