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