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