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