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