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 "zbxicmpping.h"
21 #include "threads.h"
22 #include "comms.h"
23 #include "zbxexec.h"
24 #include "log.h"
25 
26 extern char	*CONFIG_SOURCE_IP;
27 extern char	*CONFIG_FPING_LOCATION;
28 #ifdef HAVE_IPV6
29 extern char	*CONFIG_FPING6_LOCATION;
30 #endif
31 extern char	*CONFIG_TMPDIR;
32 
33 /* old official fping (2.4b2_to_ipv6) did not support source IP address */
34 /* old patched versions (2.4b2_to_ipv6) provided either -I or -S options */
35 /* since fping 3.x it provides -I option for binding to an interface and -S option for source IP address */
36 
37 static unsigned char	source_ip_checked;
38 static const char	*source_ip_option;
39 #ifdef HAVE_IPV6
40 static unsigned char	source_ip6_checked;
41 static const char	*source_ip6_option;
42 #endif
43 
44 #define FPING_UNINITIALIZED_VALUE	-2
45 static int		packet_interval;
46 #ifdef HAVE_IPV6
47 static int		packet_interval6;
48 static int		fping_ipv6_supported;
49 #endif
50 
51 #define FPING_CHECK_EXPIRED	3600	/* seconds, expire detected fping options every hour */
52 static time_t	fping_check_reset_at;	/* time of the last fping options expiration */
53 
get_source_ip_option(const char * fping,const char ** option,unsigned char * checked)54 static void	get_source_ip_option(const char *fping, const char **option, unsigned char *checked)
55 {
56 	FILE	*f;
57 	char	*p, tmp[MAX_STRING_LEN];
58 
59 	zbx_snprintf(tmp, sizeof(tmp), "%s -h 2>&1", fping);
60 
61 	if (NULL == (f = popen(tmp, "r")))
62 		return;
63 
64 	while (NULL != fgets(tmp, sizeof(tmp), f))
65 	{
66 		for (p = tmp; isspace(*p); p++)
67 			;
68 
69 		if ('-' == p[0] && 'I' == p[1] && (isspace(p[2]) || ',' == p[2]))
70 		{
71 			*option = "-I";
72 			continue;
73 		}
74 
75 		if ('-' == p[0] && 'S' == p[1] && (isspace(p[2]) || ',' == p[2]))
76 		{
77 			*option = "-S";
78 			break;
79 		}
80 	}
81 
82 	pclose(f);
83 
84 	*checked = 1;
85 }
86 
87 /******************************************************************************
88  *                                                                            *
89  * Function: get_interval_option                                              *
90  *                                                                            *
91  * Purpose: detect minimal possible fping packet interval                     *
92  *                                                                            *
93  * Parameters: fping         - [IN] the the location of fping program         *
94  *             dst           - [IN] the the ip address for test               *
95  *             value         - [OUT] interval between sending ping packets    *
96  *                                   (in millisec)                            *
97  *             error         - [OUT] error string if function fails           *
98  *             max_error_len - [IN] length of error buffer                    *
99  *                                                                            *
100  * Return value: SUCCEED if processed successfully or FAIL otherwise          *
101  *                                                                            *
102  * Comments: supported minimum interval (in milliseconds) in different fping  *
103  *           versions:                                                        *
104  *           +------------------+--------------------------+---------+        *
105  *           | version X        | as root/non-root/without | Default |        *
106  *           |                  | "safe limits"            |         |        *
107  *           +------------------+--------------------------+---------+        *
108  *           |         X < 3.14 | 1 / 10 / -               | 25      |        *
109  *           | 3.14 <= X <  4.0 | 0 /  1 / -               | 25      |        *
110  *           | 4.0  <= X        | 0 /  0 / 1               | 10      |        *
111  *           +------------------+--------------------------+---------+        *
112  *           Note! "Safe limits" is compile-time option introduced in         *
113  *           fping 4.0. Distribution packages ship fping binary without       *
114  *           "safe limits".                                                   *
115  *                                                                            *
116  ******************************************************************************/
get_interval_option(const char * fping,const char * dst,int * value,char * error,size_t max_error_len)117 static int	get_interval_option(const char *fping, const char *dst, int *value, char *error, size_t max_error_len)
118 {
119 	char		*out = NULL;
120 	unsigned int	intervals[] = {0, 1, 10};
121 	size_t		i;
122 	int		ret = FAIL;
123 
124 	for (i = 0; i < ARRSIZE(intervals); i++)
125 	{
126 		int		ret_exec;
127 		char		tmp[MAX_STRING_LEN], err[255];
128 		const char	*p;
129 
130 		zabbix_log(LOG_LEVEL_DEBUG, "testing fping interval %u ms", intervals[i]);
131 
132 		zbx_snprintf(tmp, sizeof(tmp), "%s -c1 -t50 -i%u %s", fping, intervals[i], dst);
133 
134 		zbx_free(out);
135 
136 		/* call fping, ignore its exit code but mind execution failures */
137 		if (TIMEOUT_ERROR == (ret_exec = zbx_execute(tmp, &out, err, sizeof(err), 1,
138 				ZBX_EXIT_CODE_CHECKS_DISABLED)))
139 		{
140 			zbx_snprintf(error, max_error_len, "Timeout while executing \"%s\"", tmp);
141 			goto out;
142 		}
143 
144 		if (FAIL == ret_exec)
145 		{
146 			zbx_snprintf(error, max_error_len, "Cannot execute \"%s\": %s", tmp, err);
147 			goto out;
148 		}
149 
150 		/* First, check the output for suggested interval option, e. g.:          */
151 		/*                                                                        */
152 		/* /usr/sbin/fping: these options are too risky for mere mortals.         */
153 		/* /usr/sbin/fping: You need i >= 1, p >= 20, r < 20, and t >= 50         */
154 
155 #define FPING_YOU_NEED_PREFIX	"You need i >= "
156 
157 		if (NULL != (p = strstr(out, FPING_YOU_NEED_PREFIX)))
158 		{
159 			p += ZBX_CONST_STRLEN(FPING_YOU_NEED_PREFIX);
160 
161 			*value = atoi(p);
162 			ret = SUCCEED;
163 
164 			goto out;
165 		}
166 
167 #undef FPING_YOU_NEED_PREFIX
168 
169 		/* in fping 3.16 they changed "You need i >=" to "You need -i >=" */
170 
171 #define FPING_YOU_NEED_PREFIX	"You need -i >= "
172 
173 		if (NULL != (p = strstr(out, FPING_YOU_NEED_PREFIX)))
174 		{
175 			p += ZBX_CONST_STRLEN(FPING_YOU_NEED_PREFIX);
176 
177 			*value = atoi(p);
178 			ret = SUCCEED;
179 
180 			goto out;
181 		}
182 
183 #undef FPING_YOU_NEED_PREFIX
184 
185 		/* if we get dst in the beginning of the output, the used interval is allowed, */
186 		/* unless we hit the help message which is always bigger than 1 Kb             */
187 		if (ZBX_KIBIBYTE > strlen(out))
188 		{
189 			/* skip white spaces */
190 			for (p = out; '\0' != *p && isspace(*p); p++)
191 				;
192 
193 			if (strlen(p) >= strlen(dst) && 0 == strncmp(p, dst, strlen(dst)))
194 			{
195 				*value = intervals[i];
196 				ret = SUCCEED;
197 
198 				goto out;
199 			}
200 
201 			/* check if we hit the error message */
202 			if (NULL != (p = strstr(out, " as root")))
203 			{
204 				zbx_rtrim(out, "\n");
205 				zbx_strlcpy(error, out, max_error_len);
206 				goto out;
207 			}
208 		}
209 	}
210 
211 	/* if we are here we have probably hit the usage or error message, let's collect it if it's error message */
212 
213 	if (ZBX_KIBIBYTE > strlen(out) && 0 != strlen(out))
214 	{
215 		zbx_rtrim(out, "\n");
216 		zbx_strlcpy(error, out, max_error_len);
217 	}
218 	else
219 		zbx_snprintf(error, max_error_len, "Cannot detect the minimum interval of %s", fping);
220 out:
221 	zbx_free(out);
222 
223 	return ret;
224 }
225 
226 #ifdef HAVE_IPV6
227 /******************************************************************************
228  *                                                                            *
229  * Function: get_ipv6_support                                                 *
230  *                                                                            *
231  * Purpose: check fping supports IPv6                                         *
232  *                                                                            *
233  * Parameters: fping - [IN] the the location of fping program                 *
234  *             dst   - [IN] the the ip address for test                       *
235  *                                                                            *
236  * Return value: SUCCEED - IPv6 is supported                                  *
237  *               FAIL    - IPv6 is not supported                              *
238  *                                                                            *
239  ******************************************************************************/
get_ipv6_support(const char * fping,const char * dst)240 static int	get_ipv6_support(const char * fping, const char *dst)
241 {
242 	int	ret;
243 	char	tmp[MAX_STRING_LEN], error[255], *out = NULL;
244 
245 	zbx_snprintf(tmp, sizeof(tmp), "%s -6 -c1 -t50 %s", fping, dst);
246 
247 	if ((SUCCEED == (ret = zbx_execute(tmp, &out, error, sizeof(error), 1, ZBX_EXIT_CODE_CHECKS_DISABLED)) &&
248 				ZBX_KIBIBYTE > strlen(out) && NULL != strstr(out, dst)) || TIMEOUT_ERROR == ret)
249 	{
250 		ret = SUCCEED;
251 	}
252 	else
253 	{
254 		ret = FAIL;
255 	}
256 
257 	zbx_free(out);
258 
259 	return ret;
260 
261 }
262 #endif	/* HAVE_IPV6 */
263 
process_ping(ZBX_FPING_HOST * hosts,int hosts_count,int count,int interval,int size,int timeout,char * error,size_t max_error_len)264 static int	process_ping(ZBX_FPING_HOST *hosts, int hosts_count, int count, int interval, int size, int timeout,
265 		char *error, size_t max_error_len)
266 {
267 	const char	*__function_name = "process_ping";
268 	const int	response_time_chars_max = 20;
269 
270 	FILE		*f;
271 	char		params[70];
272 	char		filename[MAX_STRING_LEN];
273 	char		*tmp = NULL;
274 	size_t		tmp_size;
275 	size_t		offset;
276 	double		sec;
277 	int 		i, ret = NOTSUPPORTED, index;
278 
279 #ifdef HAVE_IPV6
280 	int		family;
281 	char		params6[70];
282 	size_t		offset6;
283 	char		fping_existence = 0;
284 #define	FPING_EXISTS	0x1
285 #define	FPING6_EXISTS	0x2
286 
287 #endif	/* HAVE_IPV6 */
288 
289 	assert(hosts);
290 
291 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() hosts_count:%d", __function_name, hosts_count);
292 
293 	/* expire detected options once in a while */
294 	if ((time(NULL) - fping_check_reset_at) > FPING_CHECK_EXPIRED)
295 	{
296 		fping_check_reset_at = time(NULL);
297 
298 		source_ip_checked = 0;
299 		packet_interval = FPING_UNINITIALIZED_VALUE;
300 #ifdef HAVE_IPV6
301 		source_ip6_checked = 0;
302 		packet_interval6 = FPING_UNINITIALIZED_VALUE;
303 		fping_ipv6_supported = FPING_UNINITIALIZED_VALUE;
304 #endif
305 	}
306 
307 	tmp_size = (size_t)(MAX_STRING_LEN + count * response_time_chars_max);
308 	tmp = zbx_malloc(tmp, tmp_size);
309 
310 	if (-1 == access(CONFIG_FPING_LOCATION, X_OK))
311 	{
312 #if !defined(HAVE_IPV6)
313 		zbx_snprintf(error, max_error_len, "%s: %s", CONFIG_FPING_LOCATION, zbx_strerror(errno));
314 		goto out;
315 #endif
316 	}
317 	else
318 	{
319 #ifdef HAVE_IPV6
320 		fping_existence |= FPING_EXISTS;
321 #else
322 		if (NULL != CONFIG_SOURCE_IP)
323 		{
324 			if (FAIL == is_ip4(CONFIG_SOURCE_IP)) /* we do not have IPv4 family address in CONFIG_SOURCE_IP */
325 			{
326 				zbx_snprintf(error, max_error_len,
327 					"You should enable IPv6 support to use IPv6 family address for SourceIP '%s'.", CONFIG_SOURCE_IP);
328 				goto out;
329 			}
330 		}
331 #endif
332 	}
333 
334 #ifdef HAVE_IPV6
335 	if (-1 == access(CONFIG_FPING6_LOCATION, X_OK))
336 	{
337 		if (0 == (fping_existence & FPING_EXISTS))
338 		{
339 			zbx_snprintf(error, max_error_len, "At least one of '%s', '%s' must exist. Both are missing in the system.",
340 					CONFIG_FPING_LOCATION,
341 					CONFIG_FPING6_LOCATION);
342 			goto out;
343 		}
344 	}
345 	else
346 		fping_existence |= FPING6_EXISTS;
347 #endif	/* HAVE_IPV6 */
348 
349 	offset = zbx_snprintf(params, sizeof(params), "-C%d", count);
350 	if (0 != interval)
351 		offset += zbx_snprintf(params + offset, sizeof(params) - offset, " -p%d", interval);
352 	if (0 != size)
353 		offset += zbx_snprintf(params + offset, sizeof(params) - offset, " -b%d", size);
354 	if (0 != timeout)
355 		offset += zbx_snprintf(params + offset, sizeof(params) - offset, " -t%d", timeout);
356 
357 #ifdef HAVE_IPV6
358 	strscpy(params6, params);
359 	offset6 = offset;
360 
361 	if (0 != (fping_existence & FPING_EXISTS) && 0 != hosts_count)
362 	{
363 		if (FPING_UNINITIALIZED_VALUE == packet_interval)
364 		{
365 			if (SUCCEED != get_interval_option(CONFIG_FPING_LOCATION, hosts[0].addr, &packet_interval,
366 					error, max_error_len))
367 			{
368 				goto out;
369 			}
370 
371 			zabbix_log(LOG_LEVEL_DEBUG, "detected minimum supported fping interval (-i): %d",
372 					packet_interval);
373 		}
374 
375 		offset += zbx_snprintf(params + offset, sizeof(params) - offset, " -i%d", packet_interval);
376 	}
377 
378 	if (0 != (fping_existence & FPING6_EXISTS) && 0 != hosts_count)
379 	{
380 		if (FPING_UNINITIALIZED_VALUE == packet_interval6)
381 		{
382 			if (SUCCEED != get_interval_option(CONFIG_FPING6_LOCATION, hosts[0].addr, &packet_interval6,
383 					error, max_error_len))
384 			{
385 				goto out;
386 			}
387 
388 			zabbix_log(LOG_LEVEL_DEBUG, "detected minimum supported fping6 interval (-i): %d",
389 					packet_interval6);
390 		}
391 
392 		offset6 += zbx_snprintf(params6 + offset6, sizeof(params6) - offset6, " -i%d", packet_interval6);
393 	}
394 #else
395 	if (0 != hosts_count)
396 	{
397 		if (FPING_UNINITIALIZED_VALUE == packet_interval)
398 		{
399 			if (SUCCEED != get_interval_option(CONFIG_FPING_LOCATION, hosts[0].addr, &packet_interval,
400 					error, max_error_len))
401 			{
402 				goto out;
403 			}
404 
405 			zabbix_log(LOG_LEVEL_DEBUG, "detected minimum supported fping interval (-i): %d",
406 					packet_interval);
407 		}
408 
409 		offset += zbx_snprintf(params + offset, sizeof(params) - offset, " -i%d", packet_interval);
410 	}
411 #endif	/* HAVE_IPV6 */
412 
413 	if (NULL != CONFIG_SOURCE_IP)
414 	{
415 #ifdef HAVE_IPV6
416 		if (0 != (fping_existence & FPING_EXISTS))
417 		{
418 			if (0 == source_ip_checked)
419 			{
420 				get_source_ip_option(CONFIG_FPING_LOCATION, &source_ip_option, &source_ip_checked);
421 
422 				zabbix_log(LOG_LEVEL_DEBUG, "detected fping source IP option: \"%s\"",
423 						ZBX_NULL2EMPTY_STR(source_ip_option));
424 			}
425 
426 			if (NULL != source_ip_option)
427 				zbx_snprintf(params + offset, sizeof(params) - offset,
428 						" %s%s", source_ip_option, CONFIG_SOURCE_IP);
429 		}
430 
431 		if (0 != (fping_existence & FPING6_EXISTS))
432 		{
433 			if (0 == source_ip6_checked)
434 			{
435 				get_source_ip_option(CONFIG_FPING6_LOCATION, &source_ip6_option, &source_ip6_checked);
436 
437 				zabbix_log(LOG_LEVEL_DEBUG, "detected fping6 source IP option: \"%s\"",
438 						ZBX_NULL2EMPTY_STR(source_ip6_option));
439 			}
440 
441 			if (NULL != source_ip6_option)
442 				zbx_snprintf(params6 + offset6, sizeof(params6) - offset6,
443 						" %s%s", source_ip6_option, CONFIG_SOURCE_IP);
444 		}
445 #else
446 		if (0 == source_ip_checked)
447 		{
448 			get_source_ip_option(CONFIG_FPING_LOCATION, &source_ip_option, &source_ip_checked);
449 
450 			zabbix_log(LOG_LEVEL_DEBUG, "detected fping source IP option: \"%s\"",
451 					ZBX_NULL2EMPTY_STR(source_ip_option));
452 		}
453 
454 		if (NULL != source_ip_option)
455 			zbx_snprintf(params + offset, sizeof(params) - offset,
456 					" %s%s", source_ip_option, CONFIG_SOURCE_IP);
457 #endif	/* HAVE_IPV6 */
458 	}
459 
460 	zbx_snprintf(filename, sizeof(filename), "%s/%s_%li.pinger", CONFIG_TMPDIR, progname, zbx_get_thread_id());
461 
462 #ifdef HAVE_IPV6
463 	if (NULL != CONFIG_SOURCE_IP)
464 	{
465 		if (SUCCEED != get_address_family(CONFIG_SOURCE_IP, &family, error, (int)max_error_len))
466 			goto out;
467 
468 		if (family == PF_INET)
469 		{
470 			if (0 == (fping_existence & FPING_EXISTS))
471 			{
472 				zbx_snprintf(error, max_error_len, "File '%s' cannot be found in the system.",
473 						CONFIG_FPING_LOCATION);
474 				goto out;
475 			}
476 
477 			zbx_snprintf(tmp, tmp_size, "%s %s 2>&1 <%s", CONFIG_FPING_LOCATION, params, filename);
478 		}
479 		else
480 		{
481 			if (0 == (fping_existence & FPING6_EXISTS))
482 			{
483 				zbx_snprintf(error, max_error_len, "File '%s' cannot be found in the system.",
484 						CONFIG_FPING6_LOCATION);
485 				goto out;
486 			}
487 
488 			zbx_snprintf(tmp, tmp_size, "%s %s 2>&1 <%s", CONFIG_FPING6_LOCATION, params6, filename);
489 		}
490 	}
491 	else
492 	{
493 		offset = 0;
494 
495 		if (0 != (fping_existence & FPING_EXISTS))
496 		{
497 			if (FPING_UNINITIALIZED_VALUE == fping_ipv6_supported)
498 			{
499 				fping_ipv6_supported = get_ipv6_support(CONFIG_FPING_LOCATION,hosts[0].addr);
500 
501 				zabbix_log(LOG_LEVEL_DEBUG, "detected fping IPv6 support: \"%s\"",
502 						SUCCEED == fping_ipv6_supported ? "yes" : "no");
503 			}
504 
505 			offset += zbx_snprintf(tmp + offset, tmp_size - offset,
506 					"%s %s 2>&1 <%s;", CONFIG_FPING_LOCATION, params, filename);
507 		}
508 
509 		if (0 != (fping_existence & FPING6_EXISTS) && SUCCEED != fping_ipv6_supported)
510 		{
511 			zbx_snprintf(tmp + offset, tmp_size - offset,
512 					"%s %s 2>&1 <%s;", CONFIG_FPING6_LOCATION, params6, filename);
513 		}
514 	}
515 #else
516 	zbx_snprintf(tmp, tmp_size, "%s %s 2>&1 <%s", CONFIG_FPING_LOCATION, params, filename);
517 #endif	/* HAVE_IPV6 */
518 
519 	if (NULL == (f = fopen(filename, "w")))
520 	{
521 		zbx_snprintf(error, max_error_len, "%s: %s", filename, zbx_strerror(errno));
522 		goto out;
523 	}
524 
525 	zabbix_log(LOG_LEVEL_DEBUG, "%s", filename);
526 
527 	for (i = 0; i < hosts_count; i++)
528 	{
529 		zabbix_log(LOG_LEVEL_DEBUG, "    %s", hosts[i].addr);
530 		fprintf(f, "%s\n", hosts[i].addr);
531 	}
532 
533 	fclose(f);
534 
535 	zabbix_log(LOG_LEVEL_DEBUG, "%s", tmp);
536 
537 	if (NULL == (f = popen(tmp, "r")))
538 	{
539 		zbx_snprintf(error, max_error_len, "%s: %s", tmp, zbx_strerror(errno));
540 
541 		unlink(filename);
542 
543 		goto out;
544 	}
545 
546 	if (NULL == fgets(tmp, (int)tmp_size, f))
547 	{
548 		zbx_snprintf(tmp, tmp_size, "no output");
549 	}
550 	else
551 	{
552 		for (i = 0; i < hosts_count; i++)
553 		{
554 			hosts[i].status = (char *)zbx_malloc(NULL, (size_t)count);
555 			memset(hosts[i].status, 0, (size_t)count);
556 		}
557 
558 		do
559 		{
560 			ZBX_FPING_HOST	*host = NULL;
561 			char		*c;
562 
563 			zbx_rtrim(tmp, "\n");
564 			zabbix_log(LOG_LEVEL_DEBUG, "read line [%s]", tmp);
565 
566 			if (NULL != (c = strchr(tmp, ' ')))
567 			{
568 				*c = '\0';
569 
570 				for (i = 0; i < hosts_count; i++)
571 				{
572 					if (0 == strcmp(tmp, hosts[i].addr))
573 					{
574 						host = &hosts[i];
575 						break;
576 					}
577 				}
578 
579 				*c = ' ';
580 			}
581 
582 			if (NULL == host)
583 				continue;
584 
585 			if (NULL == (c = strstr(tmp, " : ")))
586 				continue;
587 
588 			/* when NIC bonding is used, there are also lines like */
589 			/* 192.168.1.2 : duplicate for [0], 96 bytes, 0.19 ms */
590 
591 			if (NULL != strstr(tmp, "duplicate for"))
592 				continue;
593 
594 			c += 3;
595 
596 			/* There were two issues with processing only the fping's final status line: */
597 			/*   1) pinging broadcast addresses could have resulted in responses from    */
598 			/*      different hosts, which were counted as the target host responses;    */
599 			/*   2) there is a bug in fping (v3.8 at least) where pinging broadcast      */
600 			/*      address will result in no individual responses, but the final        */
601 			/*      status line might contain a bogus value.                             */
602 			/* Because of the above issues we must monitor the individual responses      */
603 			/* and mark the valid ones.                                                  */
604 			if ('[' == *c)
605 			{
606 				/* Fping appends response source address in format '[<- 10.3.0.10]' */
607 				/* if it does not match the target address. Ignore such responses.  */
608 				if (NULL != strstr(c + 1, "[<-"))
609 					continue;
610 
611 				/* get the index of individual ping response */
612 				index = atoi(c + 1);
613 
614 				if (0 > index || index >= count)
615 					continue;
616 
617 				/* since 5.0 Fping outputs individual failed packages in additional to successful: */
618 				/*                                                                                 */
619 				/*   fping -C3 -i0 7.7.7.7 8.8.8.8                                                 */
620 				/*   8.8.8.8 : [0], 64 bytes, 9.37 ms (9.37 avg, 0% loss)                          */
621 				/*   7.7.7.7 : [0], timed out (NaN avg, 100% loss)                                 */
622 				/*   8.8.8.8 : [1], 64 bytes, 8.72 ms (9.05 avg, 0% loss)                          */
623 				/*   7.7.7.7 : [1], timed out (NaN avg, 100% loss)                                 */
624 				/*   8.8.8.8 : [2], 64 bytes, 7.28 ms (8.46 avg, 0% loss)                          */
625 				/*   7.7.7.7 : [2], timed out (NaN avg, 100% loss)                                 */
626 				/*                                                                                 */
627 				/*   7.7.7.7 : - - -                                                               */
628 				/*   8.8.8.8 : 9.37 8.72 7.28                                                      */
629 				/*                                                                                 */
630 				/* Judging by Fping source code we can disregard lines reporting "timed out".      */
631 
632 				if (NULL != strstr(c + 2, " timed out "))
633 					continue;
634 
635 				host->status[index] = 1;
636 
637 				continue;
638 			}
639 
640 			/* process status line for a host */
641 			index = 0;
642 			do
643 			{
644 				if (1 == host->status[index])
645 				{
646 					sec = atof(c) / 1000; /* convert ms to seconds */
647 
648 					if (0 == host->rcv || host->min > sec)
649 						host->min = sec;
650 					if (0 == host->rcv || host->max < sec)
651 						host->max = sec;
652 					host->sum += sec;
653 					host->rcv++;
654 				}
655 			}
656 			while (++index < count && NULL != (c = strchr(c + 1, ' ')));
657 
658 			host->cnt += count;
659 #ifdef HAVE_IPV6
660 			if (host->cnt == count && NULL == CONFIG_SOURCE_IP &&
661 					0 != (fping_existence & FPING_EXISTS) &&
662 					0 != (fping_existence & FPING6_EXISTS))
663 			{
664 				memset(host->status, 0, (size_t)count);	/* reset response statuses for IPv6 */
665 			}
666 #endif
667 			ret = SUCCEED;
668 		}
669 		while (NULL != fgets(tmp, (int)tmp_size, f));
670 
671 		for (i = 0; i < hosts_count; i++)
672 			zbx_free(hosts[i].status);
673 	}
674 	pclose(f);
675 
676 	unlink(filename);
677 
678 	if (NOTSUPPORTED == ret)
679 		zbx_snprintf(error, max_error_len, "fping failed: %s", tmp);
680 
681 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
682 out:
683 	zbx_free(tmp);
684 
685 	return ret;
686 }
687 
688 /******************************************************************************
689  *                                                                            *
690  * Function: zbx_ping                                                         *
691  *                                                                            *
692  * Purpose: ping hosts listed in the host files                               *
693  *                                                                            *
694  *             hosts_count   - [IN]  number of target hosts                   *
695  *             count         - [IN]  number of pings to send to each target   *
696  *                                   (fping option -C)                        *
697  *             period        - [IN]  interval between ping packets to one     *
698  *                                   target, in milliseconds                  *
699  *                                   (fping option -p)                        *
700  *             size          - [IN]  amount of ping data to send, in bytes    *
701  *                                   (fping option -b)                        *
702  *             timeout       - [IN]  individual target initial timeout except *
703  *                                   when count > 1, where it's the -p period *
704  *                                   (fping option -t)                        *
705  *             error         - [OUT] error string if function fails           *
706  *             max_error_len - [IN]  length of error buffer                   *
707  *                                                                            *
708  * Return value: SUCCEED - successfully processed hosts                       *
709  *               NOTSUPPORTED - otherwise                                     *
710  *                                                                            *
711  * Author: Alexei Vladishev                                                   *
712  *                                                                            *
713  * Comments: use external binary 'fping' to avoid superuser privileges        *
714  *                                                                            *
715  ******************************************************************************/
zbx_ping(ZBX_FPING_HOST * hosts,int hosts_count,int count,int period,int size,int timeout,char * error,size_t max_error_len)716 int	zbx_ping(ZBX_FPING_HOST *hosts, int hosts_count, int count, int period, int size, int timeout,
717 		char *error, size_t max_error_len)
718 {
719 	const char	*__function_name = "zbx_ping";
720 
721 	int	ret;
722 
723 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() hosts_count:%d", __function_name, hosts_count);
724 
725 	if (NOTSUPPORTED == (ret = process_ping(hosts, hosts_count, count, period, size, timeout, error, max_error_len)))
726 		zabbix_log(LOG_LEVEL_ERR, "%s", error);
727 
728 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
729 
730 	return ret;
731 }
732