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 NULL);
364
365 if (DOBJECT_STATUS_DOWN == dhost->status)
366 {
367 /* Service went UP, but host status is DOWN. Update host status. */
368
369 dhost->status = DOBJECT_STATUS_UP;
370 dhost->lastup = now;
371 dhost->lastdown = 0;
372
373 discovery_update_dhost(dhost);
374 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
375 DOBJECT_STATUS_DISCOVER, NULL, NULL, NULL, 0, 0, NULL,
376 0, NULL, 0, NULL, NULL, NULL);
377 }
378 }
379 else if (0 != strcmp(dservice->value, value))
380 {
381 discovery_update_dservice_value(dservice->dserviceid, value);
382 }
383 }
384 else /* DOBJECT_STATUS_DOWN */
385 {
386 if (DOBJECT_STATUS_UP == dservice->status || 0 == dservice->lastdown)
387 {
388 discovery_update_dservice(dservice->dserviceid, service_status, 0, now, dservice->value);
389 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts,
390 DOBJECT_STATUS_LOST, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL,
391 NULL);
392
393 /* service went DOWN, no need to update host status here as other services may be UP */
394 }
395 }
396 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DSERVICE, dservice->dserviceid, &ts, service_status,
397 NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL, NULL);
398
399 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
400 }
401
402 /******************************************************************************
403 * *
404 * Function: discovery_update_host_status *
405 * *
406 * Purpose: update new host status *
407 * *
408 ******************************************************************************/
discovery_update_host_status(DB_DHOST * dhost,int status,int now)409 static void discovery_update_host_status(DB_DHOST *dhost, int status, int now)
410 {
411 zbx_timespec_t ts;
412
413 ts.sec = now;
414 ts.ns = 0;
415
416 /* update host status */
417 if (DOBJECT_STATUS_UP == status)
418 {
419 if (DOBJECT_STATUS_DOWN == dhost->status || 0 == dhost->lastup)
420 {
421 dhost->status = status;
422 dhost->lastdown = 0;
423 dhost->lastup = now;
424
425 discovery_update_dhost(dhost);
426 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
427 DOBJECT_STATUS_DISCOVER, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL,
428 NULL);
429 }
430 }
431 else /* DOBJECT_STATUS_DOWN */
432 {
433 if (DOBJECT_STATUS_UP == dhost->status || 0 == dhost->lastdown)
434 {
435 dhost->status = status;
436 dhost->lastdown = now;
437 dhost->lastup = 0;
438
439 discovery_update_dhost(dhost);
440 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts,
441 DOBJECT_STATUS_LOST, NULL, NULL, NULL, 0, 0, NULL, 0, NULL, 0, NULL, NULL,
442 NULL);
443 }
444 }
445 zbx_add_event(EVENT_SOURCE_DISCOVERY, EVENT_OBJECT_DHOST, dhost->dhostid, &ts, status, NULL, NULL, NULL, 0, 0,
446 NULL, 0, NULL, 0, NULL, NULL, NULL);
447 }
448
449 /******************************************************************************
450 * *
451 * Function: discovery_update_host *
452 * *
453 * Purpose: process new host status *
454 * *
455 * Parameters: host - host info *
456 * *
457 ******************************************************************************/
discovery_update_host(DB_DHOST * dhost,int status,int now)458 void discovery_update_host(DB_DHOST *dhost, int status, int now)
459 {
460 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
461
462 if (0 != dhost->dhostid)
463 discovery_update_host_status(dhost, status, now);
464
465 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
466 }
467
468 /******************************************************************************
469 * *
470 * Function: discovery_update_service *
471 * *
472 * Purpose: process new service status *
473 * *
474 * Parameters: service - service info *
475 * *
476 ******************************************************************************/
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)477 void discovery_update_service(const DB_DRULE *drule, zbx_uint64_t dcheckid, DB_DHOST *dhost, const char *ip,
478 const char *dns, int port, int status, const char *value, int now)
479 {
480 DB_DSERVICE dservice;
481
482 zabbix_log(LOG_LEVEL_DEBUG, "In %s() ip:'%s' dns:'%s' port:%d status:%d value:'%s'",
483 __func__, ip, dns, port, status, value);
484
485 memset(&dservice, 0, sizeof(dservice));
486
487 /* register host if is not registered yet */
488 if (0 == dhost->dhostid)
489 discovery_register_host(drule, dcheckid, dhost, ip, port, status, value);
490
491 /* register service if is not registered yet */
492 if (0 != dhost->dhostid)
493 discovery_register_service(dcheckid, dhost, &dservice, ip, dns, port, status);
494
495 /* service was not registered because we do not add down service */
496 if (0 != dservice.dserviceid)
497 discovery_update_service_status(dhost, &dservice, status, value, now);
498
499 zbx_free(dservice.value);
500
501 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
502 }
503