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
22 #include "dbcache.h"
23 #include "log.h"
24 #include "zbxserver.h"
25 #include "zbxicmpping.h"
26 #include "daemon.h"
27 #include "zbxself.h"
28
29 #include "pinger.h"
30
31 /* defines for `fping' and `fping6' to successfully process pings */
32 #define MIN_COUNT 1
33 #define MAX_COUNT 10000
34 #define MIN_INTERVAL 20
35 #define MIN_SIZE 24
36 #define MAX_SIZE 65507
37 #define MIN_TIMEOUT 50
38
39 extern unsigned char process_type, program_type;
40 extern int server_num, process_num;
41
42 /******************************************************************************
43 * *
44 * Function: process_value *
45 * *
46 * Purpose: process new item value *
47 * *
48 * Parameters: *
49 * *
50 * Return value: *
51 * *
52 * Author: Alexei Vladishev, Alexander Vladishev *
53 * *
54 * Comments: *
55 * *
56 ******************************************************************************/
process_value(zbx_uint64_t itemid,zbx_uint64_t * value_ui64,double * value_dbl,zbx_timespec_t * ts,int ping_result,char * error)57 static void process_value(zbx_uint64_t itemid, zbx_uint64_t *value_ui64, double *value_dbl, zbx_timespec_t *ts,
58 int ping_result, char *error)
59 {
60 const char *__function_name = "process_value";
61 DC_ITEM item;
62 int errcode;
63 AGENT_RESULT value;
64
65 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
66
67 DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
68
69 if (SUCCEED != errcode)
70 goto clean;
71
72 if (ITEM_STATUS_ACTIVE != item.status)
73 goto clean;
74
75 if (HOST_STATUS_MONITORED != item.host.status)
76 goto clean;
77
78 if (NOTSUPPORTED == ping_result)
79 {
80 item.state = ITEM_STATE_NOTSUPPORTED;
81 dc_add_history(item.itemid, item.value_type, item.flags, NULL, ts, item.state, error);
82 }
83 else
84 {
85 init_result(&value);
86
87 if (NULL != value_ui64)
88 SET_UI64_RESULT(&value, *value_ui64);
89 else
90 SET_DBL_RESULT(&value, *value_dbl);
91
92 item.state = ITEM_STATE_NORMAL;
93 dc_add_history(item.itemid, item.value_type, item.flags, &value, ts, item.state, NULL);
94
95 free_result(&value);
96 }
97 clean:
98 DCrequeue_items(&item.itemid, &item.state, &ts->sec, NULL, NULL, &errcode, 1);
99
100 DCconfig_clean_items(&item, &errcode, 1);
101
102 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
103 }
104
105 /******************************************************************************
106 * *
107 * Function: process_values *
108 * *
109 * Purpose: process new item values *
110 * *
111 * Parameters: *
112 * *
113 * Return value: *
114 * *
115 * Author: Alexei Vladishev, Alexander Vladishev *
116 * *
117 * Comments: *
118 * *
119 ******************************************************************************/
process_values(icmpitem_t * items,int first_index,int last_index,ZBX_FPING_HOST * hosts,int hosts_count,zbx_timespec_t * ts,int ping_result,char * error)120 static void process_values(icmpitem_t *items, int first_index, int last_index, ZBX_FPING_HOST *hosts,
121 int hosts_count, zbx_timespec_t *ts, int ping_result, char *error)
122 {
123 const char *__function_name = "process_values";
124 int i, h;
125 zbx_uint64_t value_uint64;
126 double value_dbl;
127
128 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
129
130 for (h = 0; h < hosts_count; h++)
131 {
132 const ZBX_FPING_HOST *host = &hosts[h];
133
134 if (NOTSUPPORTED == ping_result)
135 {
136 zabbix_log(LOG_LEVEL_DEBUG, "host [%s] %s", host->addr, error);
137 }
138 else
139 {
140 zabbix_log(LOG_LEVEL_DEBUG, "host [%s] cnt=%d rcv=%d"
141 " min=" ZBX_FS_DBL " max=" ZBX_FS_DBL " sum=" ZBX_FS_DBL,
142 host->addr, host->cnt, host->rcv, host->min, host->max, host->sum);
143 }
144
145 for (i = first_index; i < last_index; i++)
146 {
147 const icmpitem_t *item = &items[i];
148
149 if (0 != strcmp(item->addr, host->addr))
150 continue;
151
152 if (NOTSUPPORTED == ping_result)
153 {
154 process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED, error);
155 continue;
156 }
157
158 if (0 == host->cnt)
159 {
160 process_value(item->itemid, NULL, NULL, ts, NOTSUPPORTED,
161 "Cannot send ICMP ping packets to this host.");
162 continue;
163 }
164
165 switch (item->icmpping)
166 {
167 case ICMPPING:
168 value_uint64 = (0 != host->rcv ? 1 : 0);
169 process_value(item->itemid, &value_uint64, NULL, ts, SUCCEED, NULL);
170 break;
171 case ICMPPINGSEC:
172 switch (item->type)
173 {
174 case ICMPPINGSEC_MIN:
175 value_dbl = host->min;
176 break;
177 case ICMPPINGSEC_MAX:
178 value_dbl = host->max;
179 break;
180 case ICMPPINGSEC_AVG:
181 value_dbl = (0 != host->rcv ? host->sum / host->rcv : 0);
182 break;
183 }
184
185 if (0 < value_dbl && ZBX_FLOAT_PRECISION > value_dbl)
186 value_dbl = ZBX_FLOAT_PRECISION;
187
188 process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
189 break;
190 case ICMPPINGLOSS:
191 value_dbl = (100 * (host->cnt - host->rcv)) / (double)host->cnt;
192 process_value(item->itemid, NULL, &value_dbl, ts, SUCCEED, NULL);
193 break;
194 }
195 }
196 }
197
198 dc_flush_history();
199
200 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
201 }
202
parse_key_params(const char * key,const char * host_addr,icmpping_t * icmpping,char ** addr,int * count,int * interval,int * size,int * timeout,icmppingsec_type_t * type,char * error,int max_error_len)203 static int parse_key_params(const char *key, const char *host_addr, icmpping_t *icmpping, char **addr, int *count,
204 int *interval, int *size, int *timeout, icmppingsec_type_t *type, char *error, int max_error_len)
205 {
206 const char *tmp;
207 int ret = NOTSUPPORTED;
208 AGENT_REQUEST request;
209
210 init_request(&request);
211
212 if (SUCCEED != parse_item_key(key, &request))
213 {
214 zbx_snprintf(error, max_error_len, "Invalid item key format.");
215 goto out;
216 }
217
218 if (0 == strcmp(get_rkey(&request), SERVER_ICMPPING_KEY))
219 {
220 *icmpping = ICMPPING;
221 }
222 else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGLOSS_KEY))
223 {
224 *icmpping = ICMPPINGLOSS;
225 }
226 else if (0 == strcmp(get_rkey(&request), SERVER_ICMPPINGSEC_KEY))
227 {
228 *icmpping = ICMPPINGSEC;
229 }
230 else
231 {
232 zbx_snprintf(error, max_error_len, "Unsupported pinger key.");
233 goto out;
234 }
235
236 if (6 < get_rparams_num(&request) || (ICMPPINGSEC != *icmpping && 5 < get_rparams_num(&request)))
237 {
238 zbx_snprintf(error, max_error_len, "Too many arguments.");
239 goto out;
240 }
241
242 if (NULL == (tmp = get_rparam(&request, 1)) || '\0' == *tmp)
243 {
244 *count = 3;
245 }
246 else if (FAIL == is_uint31(tmp, count) || MIN_COUNT > *count || *count > MAX_COUNT)
247 {
248 zbx_snprintf(error, max_error_len, "Number of packets \"%s\" is not between %d and %d.",
249 tmp, MIN_COUNT, MAX_COUNT);
250 goto out;
251 }
252
253 if (NULL == (tmp = get_rparam(&request, 2)) || '\0' == *tmp)
254 {
255 *interval = 0;
256 }
257 else if (FAIL == is_uint31(tmp, interval) || MIN_INTERVAL > *interval)
258 {
259 zbx_snprintf(error, max_error_len, "Interval \"%s\" should be at least %d.", tmp, MIN_INTERVAL);
260 goto out;
261 }
262
263 if (NULL == (tmp = get_rparam(&request, 3)) || '\0' == *tmp)
264 {
265 *size = 0;
266 }
267 else if (FAIL == is_uint31(tmp, size) || MIN_SIZE > *size || *size > MAX_SIZE)
268 {
269 zbx_snprintf(error, max_error_len, "Packet size \"%s\" is not between %d and %d.",
270 tmp, MIN_SIZE, MAX_SIZE);
271 goto out;
272 }
273
274 if (NULL == (tmp = get_rparam(&request, 4)) || '\0' == *tmp)
275 {
276 *timeout = 0;
277 }
278 else if (FAIL == is_uint31(tmp, timeout) || MIN_TIMEOUT > *timeout)
279 {
280 zbx_snprintf(error, max_error_len, "Timeout \"%s\" should be at least %d.", tmp, MIN_TIMEOUT);
281 goto out;
282 }
283
284 if (NULL == (tmp = get_rparam(&request, 5)) || '\0' == *tmp)
285 {
286 *type = ICMPPINGSEC_AVG;
287 }
288 else
289 {
290 if (0 == strcmp(tmp, "min"))
291 {
292 *type = ICMPPINGSEC_MIN;
293 }
294 else if (0 == strcmp(tmp, "avg"))
295 {
296 *type = ICMPPINGSEC_AVG;
297 }
298 else if (0 == strcmp(tmp, "max"))
299 {
300 *type = ICMPPINGSEC_MAX;
301 }
302 else
303 {
304 zbx_snprintf(error, max_error_len, "Mode \"%s\" is not supported.", tmp);
305 goto out;
306 }
307 }
308
309 if (NULL == (tmp = get_rparam(&request, 0)) || '\0' == *tmp)
310 *addr = strdup(host_addr);
311 else
312 *addr = strdup(tmp);
313
314 ret = SUCCEED;
315 out:
316 free_request(&request);
317
318 return ret;
319 }
320
get_icmpping_nearestindex(icmpitem_t * items,int items_count,int count,int interval,int size,int timeout)321 static int get_icmpping_nearestindex(icmpitem_t *items, int items_count, int count, int interval, int size, int timeout)
322 {
323 int first_index, last_index, index;
324 icmpitem_t *item;
325
326 if (items_count == 0)
327 return 0;
328
329 first_index = 0;
330 last_index = items_count - 1;
331 while (1)
332 {
333 index = first_index + (last_index - first_index) / 2;
334 item = &items[index];
335
336 if (item->count == count && item->interval == interval && item->size == size && item->timeout == timeout)
337 return index;
338 else if (last_index == first_index)
339 {
340 if (item->count < count ||
341 (item->count == count && item->interval < interval) ||
342 (item->count == count && item->interval == interval && item->size < size) ||
343 (item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
344 index++;
345 return index;
346 }
347 else if (item->count < count ||
348 (item->count == count && item->interval < interval) ||
349 (item->count == count && item->interval == interval && item->size < size) ||
350 (item->count == count && item->interval == interval && item->size == size && item->timeout < timeout))
351 first_index = index + 1;
352 else
353 last_index = index;
354 }
355 }
356
add_icmpping_item(icmpitem_t ** items,int * items_alloc,int * items_count,int count,int interval,int size,int timeout,zbx_uint64_t itemid,char * addr,icmpping_t icmpping,icmppingsec_type_t type)357 static void add_icmpping_item(icmpitem_t **items, int *items_alloc, int *items_count, int count, int interval,
358 int size, int timeout, zbx_uint64_t itemid, char *addr, icmpping_t icmpping, icmppingsec_type_t type)
359 {
360 const char *__function_name = "add_icmpping_item";
361 int index;
362 icmpitem_t *item;
363 size_t sz;
364
365 zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s' count:%d interval:%d size:%d timeout:%d",
366 __function_name, addr, count, interval, size, timeout);
367
368 index = get_icmpping_nearestindex(*items, *items_count, count, interval, size, timeout);
369
370 if (*items_alloc == *items_count)
371 {
372 *items_alloc += 4;
373 sz = *items_alloc * sizeof(icmpitem_t);
374 *items = zbx_realloc(*items, sz);
375 }
376
377 memmove(&(*items)[index + 1], &(*items)[index], sizeof(icmpitem_t) * (*items_count - index));
378
379 item = &(*items)[index];
380 item->count = count;
381 item->interval = interval;
382 item->size = size;
383 item->timeout = timeout;
384 item->itemid = itemid;
385 item->addr = addr;
386 item->icmpping = icmpping;
387 item->type = type;
388
389 (*items_count)++;
390
391 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
392 }
393
394 /******************************************************************************
395 * *
396 * Function: get_pinger_hosts *
397 * *
398 * Purpose: creates buffer which contains list of hosts to ping *
399 * *
400 * Parameters: *
401 * *
402 * Return value: SUCCEED - the file was created successfully *
403 * FAIL - otherwise *
404 * *
405 * Author: Alexei Vladishev, Alexander Vladishev *
406 * *
407 * Comments: *
408 * *
409 ******************************************************************************/
get_pinger_hosts(icmpitem_t ** icmp_items,int * icmp_items_alloc,int * icmp_items_count)410 static void get_pinger_hosts(icmpitem_t **icmp_items, int *icmp_items_alloc, int *icmp_items_count)
411 {
412 const char *__function_name = "get_pinger_hosts";
413 DC_ITEM items[MAX_PINGER_ITEMS];
414 int i, num, count, interval, size, timeout, rc, errcode = SUCCEED;
415 char error[MAX_STRING_LEN], *addr = NULL;
416 icmpping_t icmpping;
417 icmppingsec_type_t type;
418
419 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
420
421 num = DCconfig_get_poller_items(ZBX_POLLER_TYPE_PINGER, items);
422
423 for (i = 0; i < num; i++)
424 {
425 ZBX_STRDUP(items[i].key, items[i].key_orig);
426 rc = substitute_key_macros(&items[i].key, NULL, &items[i], NULL, MACRO_TYPE_ITEM_KEY,
427 error, sizeof(error));
428
429 if (SUCCEED == rc)
430 {
431 rc = parse_key_params(items[i].key, items[i].interface.addr, &icmpping, &addr, &count,
432 &interval, &size, &timeout, &type, error, sizeof(error));
433 }
434
435 if (SUCCEED == rc)
436 {
437 add_icmpping_item(icmp_items, icmp_items_alloc, icmp_items_count, count, interval, size,
438 timeout, items[i].itemid, addr, icmpping, type);
439 }
440 else
441 {
442 zbx_timespec_t ts;
443
444 zbx_timespec(&ts);
445
446 items[i].state = ITEM_STATE_NOTSUPPORTED;
447 dc_add_history(items[i].itemid, items[i].value_type, items[i].flags, NULL, &ts,
448 items[i].state, error);
449
450 DCrequeue_items(&items[i].itemid, &items[i].state, &ts.sec, NULL, NULL, &errcode, 1);
451 }
452
453 zbx_free(items[i].key);
454 }
455
456 DCconfig_clean_items(items, NULL, num);
457
458 dc_flush_history();
459
460 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, *icmp_items_count);
461 }
462
free_hosts(icmpitem_t ** items,int * items_count)463 static void free_hosts(icmpitem_t **items, int *items_count)
464 {
465 int i;
466
467 for (i = 0; i < *items_count; i++)
468 zbx_free((*items)[i].addr);
469
470 *items_count = 0;
471 }
472
add_pinger_host(ZBX_FPING_HOST ** hosts,int * hosts_alloc,int * hosts_count,char * addr)473 static void add_pinger_host(ZBX_FPING_HOST **hosts, int *hosts_alloc, int *hosts_count, char *addr)
474 {
475 const char *__function_name = "add_pinger_host";
476
477 int i;
478 size_t sz;
479 ZBX_FPING_HOST *h;
480
481 zabbix_log(LOG_LEVEL_DEBUG, "In %s() addr:'%s'", __function_name, addr);
482
483 for (i = 0; i < *hosts_count; i++)
484 {
485 if (0 == strcmp(addr, (*hosts)[i].addr))
486 return;
487 }
488
489 (*hosts_count)++;
490
491 if (*hosts_alloc < *hosts_count)
492 {
493 *hosts_alloc += 4;
494 sz = *hosts_alloc * sizeof(ZBX_FPING_HOST);
495 *hosts = zbx_realloc(*hosts, sz);
496 }
497
498 h = &(*hosts)[*hosts_count - 1];
499 memset(h, 0, sizeof(ZBX_FPING_HOST));
500 h->addr = addr;
501
502 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
503 }
504
505 /******************************************************************************
506 * *
507 * Function: process_pinger_hosts *
508 * *
509 * Purpose: *
510 * *
511 * Parameters: *
512 * *
513 * Return value: *
514 * *
515 * Author: Alexander Vladishev *
516 * *
517 * Comments: *
518 * *
519 ******************************************************************************/
process_pinger_hosts(icmpitem_t * items,int items_count)520 static void process_pinger_hosts(icmpitem_t *items, int items_count)
521 {
522 const char *__function_name = "process_pinger_hosts";
523 int i, first_index = 0, ping_result;
524 char error[ITEM_ERROR_LEN_MAX];
525 static ZBX_FPING_HOST *hosts = NULL;
526 static int hosts_alloc = 4;
527 int hosts_count = 0;
528 zbx_timespec_t ts;
529
530 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
531
532 if (NULL == hosts)
533 hosts = zbx_malloc(hosts, sizeof(ZBX_FPING_HOST) * hosts_alloc);
534
535 for (i = 0; i < items_count; i++)
536 {
537 add_pinger_host(&hosts, &hosts_alloc, &hosts_count, items[i].addr);
538
539 if (i == items_count - 1 || items[i].count != items[i + 1].count || items[i].interval != items[i + 1].interval ||
540 items[i].size != items[i + 1].size || items[i].timeout != items[i + 1].timeout)
541 {
542 zbx_setproctitle("%s #%d [pinging hosts]", get_process_type_string(process_type), process_num);
543
544 zbx_timespec(&ts);
545
546 ping_result = do_ping(hosts, hosts_count,
547 items[i].count, items[i].interval, items[i].size, items[i].timeout,
548 error, sizeof(error));
549
550 process_values(items, first_index, i + 1, hosts, hosts_count, &ts, ping_result, error);
551
552 hosts_count = 0;
553 first_index = i + 1;
554 }
555 }
556
557 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
558 }
559
560 /******************************************************************************
561 * *
562 * Function: main_pinger_loop *
563 * *
564 * Purpose: periodically perform ICMP pings *
565 * *
566 * Parameters: *
567 * *
568 * Return value: *
569 * *
570 * Author: Alexei Vladishev *
571 * *
572 * Comments: never returns *
573 * *
574 ******************************************************************************/
ZBX_THREAD_ENTRY(pinger_thread,args)575 ZBX_THREAD_ENTRY(pinger_thread, args)
576 {
577 int nextcheck, sleeptime, items_count = 0, itc;
578 double sec;
579 static icmpitem_t *items = NULL;
580 static int items_alloc = 4;
581
582 process_type = ((zbx_thread_args_t *)args)->process_type;
583 server_num = ((zbx_thread_args_t *)args)->server_num;
584 process_num = ((zbx_thread_args_t *)args)->process_num;
585
586 zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
587 server_num, get_process_type_string(process_type), process_num);
588
589 if (NULL == items)
590 items = zbx_malloc(items, sizeof(icmpitem_t) * items_alloc);
591
592 for (;;)
593 {
594 zbx_handle_log();
595
596 zbx_setproctitle("%s #%d [getting values]", get_process_type_string(process_type), process_num);
597
598 sec = zbx_time();
599 get_pinger_hosts(&items, &items_alloc, &items_count);
600 process_pinger_hosts(items, items_count);
601 sec = zbx_time() - sec;
602 itc = items_count;
603
604 free_hosts(&items, &items_count);
605
606 nextcheck = DCconfig_get_poller_nextcheck(ZBX_POLLER_TYPE_PINGER);
607 sleeptime = calculate_sleeptime(nextcheck, POLLER_DELAY);
608
609 zbx_setproctitle("%s #%d [got %d values in " ZBX_FS_DBL " sec, idle %d sec]",
610 get_process_type_string(process_type), process_num, itc, sec, sleeptime);
611
612 zbx_sleep_loop(sleeptime);
613
614 #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H)
615 zbx_update_resolver_conf(); /* handle /etc/resolv.conf update */
616 #endif
617 }
618 }
619