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