author | Florian Forster <ff@octo.it> | |
Fri, 5 May 2017 06:43:52 +0000 (08:43 +0200) | ||
committer | Florian Forster <ff@octo.it> | |
Fri, 5 May 2017 08:46:44 +0000 (10:46 +0200) |
25 files changed:
diff --git a/.gitignore b/.gitignore
index 53ca1f5ea9130e789c8af95001c1a21efc0e4a9d..b9cb926d92052e09d7c8324f2ff732ce284399fa 100644 (file)
--- a/.gitignore
+++ b/.gitignore
install-sh
missing
stamp-h1
+/m4/
# libtool stuff
libltdl
ltmain.sh
# build output
-.libs
+.libs/
bindings/perl/Oping.c
bindings/perl/blib
bindings/perl/pm_to_blib
src/mans/*.3
src/mans/*.8
src/oping
+src/noping
+src/liboping.pc
*.bs
*.la
*.lo
*.o
*.tar.gz
*.tar.bz2
+bindings/perl/MYMETA.yml
diff --git a/ChangeLog b/ChangeLog
index e895bbee83df2522b0073fd161afbd895ef6cbb6..7d85b14f8820ce09ee8c7dee637b6b74b64c0a46 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+2016-06-27, Version 1.9.0:
+ * liboping: The new "PING_OPT_MARK" option allows to mark packets,
+ which can be used for filtering and routing such packets on Linux.
+ * oping, noping: The new "-m" command line option allows to set a mark
+ on packets sent by the tool.
+ * oping, noping: The new "-O" command line option allows to write
+ measurements to an CSV file.
+ * oping, noping: The new "-w" command line option allows to specify
+ the timeout after which a packet/reply is considered "dropped".
+
+2014-11-20, Version 1.8.0:
+ * oping, noping: Average and standard deviation have been removed from
+ the status output, which show median and 95th percentile instead.
+ The percentile can be chosen with the "-P" option.
+ * noping: The additional graph types "histogram" and "boxplot" have
+ been added, which can be selected with the "-g" option.
+
+2014-09-25, Version 1.7.0:
+ * oping, noping: The new -Z option allows the exit status to indicate
+ the number of failing hosts. Thanks to Barak Pearlmutter for the
+ patch.
+ * noping: The ability to print a "prettyping" style graph has been
+ added. Thanks to Antoine Beaupré for his work!
+ * src/liboping.c: Build issues on Solaris have been fixed. Thanks
+ Scott Severtson for the fix!
+ * Build system: Creation and installation of a pkg-config file has
+ been added. Thanks to Barak Pearlmutter for the patch.
+
+2012-01-31, Version 1.6.2:
+ * Build system: Setting capabilities and the set-UID bit has been made
+ more fault-tolerant, so that it will work with Debian's fakeroot(1)
+ utility.
+ * src/liboping.c: Fixed a compiler warning about an non-static format
+ string. Thanks to Brian Edwards for pointing this out.
+ * src/liboping.c: Fixed compilation under Mac OS X and Solaris. Thanks
+ to Clayton O'Neill for his patch.
+
2011-03-06, Version 1.6.1:
* Build system: If "make install" is executed as root, the CAP_NET_RAW
capability is added to the binary (on Linux) or the set-UID bit is
diff --git a/Makefile.am b/Makefile.am
index af22243598002a2585f1d8437352f6b6cc50c636..6993205c24033afaebfa4ff0f6506134074df8b2 100644 (file)
--- a/Makefile.am
+++ b/Makefile.am
SUBDIRS = src bindings
-dist-hook:
- find $(distdir) -type d -name '.svn' | xargs rm -rf
+ACLOCAL_AMFLAGS = -I m4
index 5488fb4c66ea3c498c2222d535c5cc8cba817813..8611d7c65f748db7aa37be32b4126e8811cc8c02 100644 (file)
--- a/README
+++ b/README
liboping – Library to ping IPv4 and IPv6 hosts in parallel
════════════════════════════════════════════════════════════
-http://verplant.org/liboping/
+http://noping.cc/
About
━━━━━
(which is strongly suggested), you won't be able to use the binaries as a
normal user, because you won't have the permission to open raw sockets.
+ The “install” target will automatically try fix this, if it is run with UID~0
+ (as user root). When on Linux, the capabilities described below will be
+ added. On other UNIXes the traditional Set-UID method (also described below)
+ is used instead. The build system will not abort if this fails, because there
+ are file systems which do not support either method. Also, the Debian
+ packaging system and possibly other scenarios only act as if they were
+ running as root.
+
Linux
━━━━━
On Linux, the preferred method is to assign the required “capability” to the
diff --git a/autogen.sh b/autogen.sh
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -e
+
+autoreconf --warnings=all --install
+echo "autoconfiguration done, to build: ./configure ; make"
index 5525dd0172c75e8bc452312901d3a7c663cbf9ea..98f93d838cdc4344ef4665c20d84a08b1be9f2c2 100644 (file)
PREREQ_PM => {},
($] >= 5.005
? (ABSTRACT_FROM => 'lib/Net/Oping.pm',
- AUTHOR => 'Florian Forster <octo@verplant.org>')
+ AUTHOR => 'Florian Forster <ff@octo.it>')
: ()),
($OPING_DEPEND ? (depend => $OPING_DEPEND) : ()),
LIBS => [$OPING_LIBS],
diff --git a/bindings/perl/Oping.xs b/bindings/perl/Oping.xs
index 5731d3d8088f28d4df8eb826dba4ab232b1b1b98..c88e3dca981710649212fac8a21ff97ad68c92bd 100644 (file)
--- a/bindings/perl/Oping.xs
+++ b/bindings/perl/Oping.xs
*
* Authors:
* Olivier Fredj <ofredj at proxad.net>
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <ff at octo.it>
*/
#include "EXTERN.h"
#include "perl.h"
diff --git a/bindings/perl/README b/bindings/perl/README
index 4d51a969cce4e0e8c4d7f601ebffd25cec2b2a49..040a38a42287c289da59c3eaa02c482af29d000b 100644 (file)
--- a/bindings/perl/README
+++ b/bindings/perl/README
DEPENDENCIES
This module requires the "oping" library to be installed. The library is
- available at <http://verplant.org/liboping/>.
+ available at <http://noping.cc/>.
COPYRIGHT AND LICENSE
Copyright (C) 2007 by Olivier Fredj <ofredj at proxad.net>
- Copyright (C) 2008,2009 by Florian Forster <octo at verplant.org>
+ Copyright (C) 2008,2009 by Florian Forster <ff at octo.it>
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself, either Perl version 5.8.7 or, at your
index bc109dc6cdd7609c75d7482f4e36e62c46545934..ba67f739afffcb606a3cc1594b528fb7b9ef7a8d 100644 (file)
#
# Authors:
# Olivier Fredj <ofredj at proxad.net>
-# Florian octo Forster <octo at verplant.org>
+# Florian octo Forster <ff at octo.it>
#
package Net::Oping;
=head1 DESCRIPTION
This Perl module is a high-level interface to the
-L<oping library|http://verplant.org/liboping/>. Its purpose it to send
-C<ICMP ECHO_REQUEST> packets (also known as "ping") to a host and measure the
-time that elapses until the reception of an C<ICMP ECHO_REPLY> packet (also
-known as "pong"). If no such packet is received after a certain timeout the
-host is considered to be unreachable.
+L<oping library|http://noping.cc/>. Its purpose it to send C<ICMP ECHO_REQUEST>
+packets (also known as "ping") to a host and measure the time that elapses
+until the reception of an C<ICMP ECHO_REPLY> packet (also known as "pong"). If
+no such packet is received after a certain timeout the host is considered to be
+unreachable.
The used I<oping> library supports "ping"ing multiple hosts in parallel and
works with IPv4 and IPv6 transparently. Other advanced features that are
L<liboping(3)>
-The I<liboping> homepage may be found at L<http://verplant.org/liboping/>.
+The I<liboping> homepage may be found at L<http://noping.cc/>.
Information about its mailing list may be found at
L<http://mailman.verplant.org/listinfo/liboping>.
Copyright (C) 2007 by Olivier Fredj E<lt>ofredjE<nbsp>atE<nbsp>proxad.netE<gt>
-Copyright (C) 2008,2009 by Florian Forster
-E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>
+Copyright (C) 2008,2009 by Florian Forster E<lt>ffE<nbsp>atE<nbsp>octo.itE<gt>
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.7 or,
diff --git a/configure.ac b/configure.ac
index a4eb2898f31a437634cdcee20f65285d66ed97dd..60138fc5cd0e4f9582c3da00f5e4508908b3f5ca 100644 (file)
--- a/configure.ac
+++ b/configure.ac
-AC_INIT(liboping, 1.6.1)
-AC_CONFIG_SRCDIR(src/liboping.c)
-AC_CONFIG_HEADERS(src/config.h)
-AM_INIT_AUTOMAKE(dist-bzip2)
-AC_LANG(C)
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.65])
+AC_INIT([liboping],
+ [1.9.0],
+ [liboping@verplant.org],
+ [],
+ [http://noping.cc/])
+AC_CONFIG_SRCDIR([src/liboping.c])
+AC_CONFIG_HEADERS([src/config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([dist-bzip2])
+AC_LANG([C])
AC_PREFIX_DEFAULT("/opt/oping")
# ABI version
LIBOPING_CURRENT=2
-LIBOPING_REVISION=7
+LIBOPING_REVISION=11
LIBOPING_AGE=2
AC_SUBST(LIBOPING_CURRENT)
AC_SUBST(LIBOPING_REVISION)
fi
AC_ARG_VAR(PERL, [Perl interpreter command])
-#
# configure libtool
-#
-AC_LIBTOOL_DLOPEN
-AC_PROG_LIBTOOL
-#AC_PROG_RANLIB
+LT_INIT([dlopen])
+
+# pkg-config interface
+PKG_INSTALLDIR
+
+AC_ARG_WITH(pkgconfigdir,
+ AC_HELP_STRING([--with-pkgconfigdir], [Use the specified pkgconfig dir (default is libdir/pkgconfig)]),
+ [pkgconfigdir="${withval}"],
+ [pkgconfigdir='${libdir}/pkgconfig'])
+AC_SUBST([pkgconfigdir])
-#
# Checks for header files.
-#
AC_HEADER_STDC
-AC_CHECK_HEADERS(unistd.h)
-AC_CHECK_HEADERS(math.h)
-AC_CHECK_HEADERS(fcntl.h)
-AC_CHECK_HEADERS(sys/types.h)
-AC_CHECK_HEADERS(sys/stat.h)
AC_HEADER_TIME
-AC_CHECK_HEADERS(sys/socket.h)
-AC_CHECK_HEADERS(netdb.h)
-AC_CHECK_HEADERS(signal.h)
+AC_CHECK_HEADERS([math.h signal.h fcntl.h inttypes.h netdb.h stdint.h stdlib.h string.h sys/socket.h sys/time.h unistd.h locale.h langinfo.h])
# This sucks, but what can I do..?
AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
#endif
])
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_UID_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT8_T
+
+LIBOPING_PC_LIBS_PRIVATE=''
+
socket_needs_socket="no"
AC_CHECK_FUNCS(socket, [],
AC_CHECK_LIB(socket, socket,
[socket_needs_socket="yes"],
AC_MSG_ERROR(cannot find socket)))
-AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
+if test "x$socket_needs_socket" = "xyes"; then
+ LIBOPING_PC_LIBS_PRIVATE="${LIBOPING_PC_LIBS_PRIVATE} -lsocket"
+fi
# Under Solaris, the `xnet' library provides `recvmsg' which complies with the
# X/Open CAE Specification.
then
CPPFLAGS="$CPPFLAGS -D_XPG4_2 -D__EXTENSIONS__"
fi
-AM_CONDITIONAL(BUILD_WITH_LIBXNET, test "x$with_libxnet" = "xyes")
-
-nanosleep_needs_rt="no"
-AC_CHECK_FUNCS(nanosleep, [],
- AC_CHECK_LIB(rt, nanosleep,
- [nanosleep_needs_rt="yes"],
- AC_MSG_ERROR(cannot find nanosleep)))
-AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
-
-with_ncurses="yes"
-AC_CHECK_HEADERS(ncurses.h, [with_ncurses="yes"], [with_ncurses="no"])
-if test "x$with_ncurses" = "xyes"
-then
- AC_CHECK_LIB(ncurses, mvwprintw, [with_ncurses="yes"], [with_ncurses="no"])
+if test "x$with_libxnet" = "xyes"; then
+ LIBOPING_PC_LIBS_PRIVATE="${LIBOPING_PC_LIBS_PRIVATE} -lxnet"
fi
-AM_CONDITIONAL(BUILD_WITH_LIBNCURSES, test "x$with_ncurses" = "xyes")
+
+AC_SUBST(LIBOPING_PC_LIBS_PRIVATE)
+
+AC_SEARCH_LIBS([nanosleep],[rt],[],
+ [AC_MSG_ERROR([cannot find nanosleep])])
+
+AC_ARG_WITH(ncurses, AS_HELP_STRING([--with-ncurses], [Build oping CLI tool with ncurses support]))
+AS_IF([test "x$with_ncurses" != "xno"], [
+ can_build_with_ncurses="no"
+ PKG_CHECK_MODULES([NCURSES], [ncursesw], [can_build_with_ncurses=yes], [
+ PKG_CHECK_MODULES([NCURSES], [ncurses], [can_build_with_ncurses=yes], [
+ AC_CHECK_LIB(ncursesw, mvwprintw, [NCURSES_LIBS="-lncursesw"; can_build_with_ncurses=yes], [
+ AC_CHECK_LIB(ncurses, mvwprintw, [NCURSES_LIBS="-lncurses"; can_build_with_ncurses=yes])
+ ])
+ ])
+ ])
+
+ AS_IF([test "x$can_build_with_ncurses" = "xyes"], [
+ AC_CHECK_HEADERS([ncursesw/curses.h ncursesw.h ncurses/curses.h ncurses.h], [can_build_with_ncurses=yes; break;], [can_build_with_ncurses=no])
+ ])
+
+ AS_IF([test "x$can_build_with_ncurses" = "xno" && test "x$with_ncurses" = "xyes"], [
+ AC_MSG_ERROR([ncurses not found but explicit enabled])
+ ],
+ [test "x$can_build_with_ncurses" = "xno"], [
+ AC_MSG_WARN([Will not build oping with ncurses support -- no suiteable ncurses installation found])
+ ])
+])
+
+AM_CONDITIONAL(BUILD_WITH_LIBNCURSES, test "x$with_ncurses" != "xno" && test "x$can_build_with_ncurses" = "xyes")
AC_FUNC_STRERROR_R
AC_SUBST(BINDINGS)
-AC_OUTPUT(Makefile src/Makefile src/mans/Makefile bindings/Makefile)
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_STRERROR_R
+AC_CHECK_FUNCS([gettimeofday memset modf select socket sqrt strcasecmp strdup strerror strncasecmp strtoul])
+
+AC_CONFIG_FILES([Makefile src/Makefile src/liboping.pc src/mans/Makefile bindings/Makefile])
+AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index 13267836f714d3e4686d01bcac44830cf9daea12..b8571aace90044dce062294747e29e68cd0fe086 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
liboping_la_CPPFLAGS = $(AM_CPPFLAGS)
liboping_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIBOPING_CURRENT@:@LIBOPING_REVISION@:@LIBOPING_AGE@
-liboping_la_LIBADD =
-if BUILD_WITH_LIBSOCKET
-liboping_la_LIBADD += -lsocket
-endif
-if BUILD_WITH_LIBXNET
-liboping_la_LIBADD += -lxnet
-endif
+liboping_la_LIBADD = $(LIBOPING_PC_LIBS_PRIVATE)
+
+pkgconfig_DATA = liboping.pc
+
+MOSTLYCLEANFILES = $(pkgconfig_DATA)
bin_PROGRAMS = oping
oping_SOURCES = oping.c
oping_LDADD = liboping.la -lm
-if BUILD_WITH_LIBRT
-oping_LDADD += -lrt
-endif
if BUILD_WITH_LIBNCURSES
bin_PROGRAMS += noping
noping_SOURCES = oping.c
-noping_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_NCURSES=1
-noping_LDADD = liboping.la -lm -lncurses
-if BUILD_WITH_LIBRT
-noping_LDADD += -lrt
-endif
+noping_CPPFLAGS = $(AM_CPPFLAGS) -DUSE_NCURSES=1 $(NCURSES_CFLAGS)
+noping_LDADD = liboping.la -lm $(NCURSES_LIBS)
endif # BUILD_WITH_LIBNCURSES
install-exec-hook:
diff --git a/src/liboping.c b/src/liboping.c
index 776f24f09e2af9f6b25ee248f759a76514ce3867..31b88135cdc090827627325ddb52545572f66905 100644 (file)
--- a/src/liboping.c
+++ b/src/liboping.c
/**
* Object oriented C module to send ICMP and ICMPv6 `echo's.
- * Copyright (C) 2006-2011 Florian octo Forster <ff at octo.it>
+ * Copyright (C) 2006-2016 Florian octo Forster <ff at octo.it>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#ifdef __APPLE__
+#define __APPLE_USE_RFC_3542
+#endif
+
#if HAVE_CONFIG_H
# include <config.h>
#endif
char *device;
+ char set_mark;
+ int mark;
+
char errmsg[PING_ERRMSG_LEN];
pinghost_t *head;
- pinghost_t *table[PING_TABLE_LEN];
+ pinghost_t *table[PING_TABLE_LEN];
};
/*
buffer += ip_hdr_len;
buffer_len -= ip_hdr_len;
- if (buffer_len < sizeof (struct icmp))
+ if (buffer_len < ICMP_MINLEN)
return (NULL);
icmp_hdr = (struct icmp *) buffer;
- buffer += sizeof (struct icmp);
- buffer_len -= sizeof (struct icmp);
-
if (icmp_hdr->icmp_type != ICMP_ECHOREPLY)
{
- dprintf ("Unexpected ICMP type: %i\n", icmp_hdr->icmp_type);
+ dprintf ("Unexpected ICMP type: %"PRIu8"\n", icmp_hdr->icmp_type);
return (NULL);
}
recv_checksum = icmp_hdr->icmp_cksum;
+ /* This writes to buffer. */
icmp_hdr->icmp_cksum = 0;
- calc_checksum = ping_icmp4_checksum ((char *) icmp_hdr,
- sizeof (struct icmp) + buffer_len);
+ calc_checksum = ping_icmp4_checksum (buffer, buffer_len);
if (recv_checksum != calc_checksum)
{
pinghost_t *ptr;
- if (buffer_len < sizeof (struct icmp6_hdr))
+ if (buffer_len < ICMP_MINLEN)
return (NULL);
icmp_hdr = (struct icmp6_hdr *) buffer;
- buffer += sizeof (struct icmp);
- buffer_len -= sizeof (struct icmp);
+ buffer += ICMP_MINLEN;
+ buffer_len -= ICMP_MINLEN;
if (icmp_hdr->icmp6_type != ICMP6_ECHO_REPLY)
{
sizeof (recv_qos));
dprintf ("TOSv6 = 0x%02"PRIx8";\n", recv_qos);
} else
+#ifdef IPV6_HOPLIMIT
if (cmsg->cmsg_type == IPV6_HOPLIMIT)
{
memcpy (&recv_ttl, CMSG_DATA (cmsg),
dprintf ("TTLv6 = %i;\n", recv_ttl);
}
else
+#endif
+#ifdef IPV6_UNICAST_HOPS
+ if (cmsg->cmsg_type == IPV6_UNICAST_HOPS)
+ {
+ memcpy (&recv_ttl, CMSG_DATA (cmsg),
+ sizeof (recv_ttl));
+ dprintf ("TTLv6 = %i;\n", recv_ttl);
+ }
+ else
+#endif
+#ifdef IPV6_MULTICAST_HOPS
+ if (cmsg->cmsg_type == IPV6_MULTICAST_HOPS)
+ {
+ memcpy (&recv_ttl, CMSG_DATA (cmsg),
+ sizeof (recv_ttl));
+ dprintf ("TTLv6 = %i;\n", recv_ttl);
+ }
+ else
+#endif
{
dprintf ("Not handling option %i.\n",
cmsg->cmsg_type);
struct icmp *icmp4;
int status;
- char buf[4096];
- int buflen;
+ char buf[4096] = {0};
+ size_t buflen;
char *data;
- int datalen;
+ size_t datalen;
dprintf ("ph->hostname = %s\n", ph->hostname);
- memset (buf, '\0', sizeof (buf));
icmp4 = (struct icmp *) buf;
- data = (char *) (icmp4 + 1);
-
- icmp4->icmp_type = ICMP_ECHO;
- icmp4->icmp_code = 0;
- icmp4->icmp_cksum = 0;
- icmp4->icmp_id = htons (ph->ident);
- icmp4->icmp_seq = htons (ph->sequence);
+ *icmp4 = (struct icmp) {
+ .icmp_type = ICMP_ECHO,
+ .icmp_id = htons (ph->ident),
+ .icmp_seq = htons (ph->sequence),
+ };
- buflen = 4096 - sizeof (struct icmp);
- strncpy (data, ph->data, buflen);
- datalen = strlen (data);
+ datalen = strlen (ph->data);
+ buflen = ICMP_MINLEN + datalen;
+ if (sizeof (buf) < buflen)
+ return (EINVAL);
- buflen = datalen + sizeof (struct icmp);
+ data = buf + ICMP_MINLEN;
+ memcpy (data, ph->data, datalen);
icmp4->icmp_cksum = ping_icmp4_checksum (buf, buflen);
struct icmp6_hdr *icmp6;
int status;
- char buf[4096];
+ char buf[4096] = {0};
int buflen;
char *data;
dprintf ("ph->hostname = %s\n", ph->hostname);
- memset (buf, '\0', sizeof (buf));
icmp6 = (struct icmp6_hdr *) buf;
- data = (char *) (icmp6 + 1);
+ *icmp6 = (struct icmp6_hdr) {
+ .icmp6_type = ICMP6_ECHO_REQUEST,
+ .icmp6_id = htons (ph->ident),
+ .icmp6_seq = htons (ph->sequence),
+ };
- icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
- icmp6->icmp6_code = 0;
- /* The checksum will be calculated by the TCP/IP stack. */
- /* FIXME */
- icmp6->icmp6_cksum = 0;
- icmp6->icmp6_id = htons (ph->ident);
- icmp6->icmp6_seq = htons (ph->sequence);
+ datalen = strlen (ph->data);
+ buflen = sizeof (*icmp6) + datalen;
+ if (sizeof (buf) < buflen)
+ return (EINVAL);
- buflen = 4096 - sizeof (struct icmp6_hdr);
- strncpy (data, ph->data, buflen);
- datalen = strlen (data);
+ data = buf + ICMP_MINLEN;
+ memcpy (data, ph->data, datalen);
- buflen = datalen + sizeof (struct icmp6_hdr);
+ /* The checksum will be calculated by the TCP/IP stack. */
dprintf ("Sending ICMPv6 package with ID 0x%04x\n", ph->ident);
ping_set_errno (obj, errno);
return -1;
}
+ else if (fd >= FD_SETSIZE)
+ {
+ dprintf ("socket(2) returned file descriptor %d, which is above the file "
+ "descriptor limit for select(2) (FD_SETSIZE = %d)\n",
+ fd, FD_SETSIZE);
+ close (fd);
+ ping_set_errno (obj, EMFILE);
+ return -1;
+ }
if (obj->srcaddr != NULL)
{
}
}
#endif /* SO_BINDTODEVICE */
+#ifdef SO_MARK
+ if (obj->set_mark)
+ {
+ if (setsockopt(fd, SOL_SOCKET, SO_MARK,
+ &obj->mark, sizeof(obj->mark)) != 0)
+ {
+ ping_set_errno (obj, errno);
+#if WITH_DEBUG
+ char errbuf[PING_ERRMSG_LEN];
+ dprintf ("setsockopt (SO_MARK): %s\n",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+#endif
+ close (fd);
+ return -1;
+ }
+ }
+#endif
#ifdef SO_TIMESTAMP
if (1) /* {{{ */
{
{
int opt;
+#ifdef IP_RECVTOS
/* Enable receiving the TOS field */
opt = 1;
setsockopt (fd, IPPROTO_IP, IP_RECVTOS,
&opt, sizeof (opt));
+#endif /* IP_RECVTOS */
/* Enable receiving the TTL field */
opt = 1;
} /* case PING_OPT_DEVICE */
break;
+ case PING_OPT_MARK:
+ {
+#ifdef SO_MARK
+ obj->mark = *(int*)(value);
+ obj->set_mark = 1;
+#else /* SO_MARK */
+ ping_set_errno (obj, ENOTSUP);
+ ret = -1;
+#endif /* !SO_MARK */
+
+ } /* case PING_OPT_MARK */
+ break;
+
default:
ret = -2;
} /* switch (option) */
if (fd4 != -1) num_fds++;
if (fd6 != -1) num_fds++;
max_fd = fd4 > fd6 ? fd4 : fd6;
+ assert (max_fd < FD_SETSIZE);
while (pings > 0 || ptr != NULL)
{
if ((status == -1) && (errno == EINTR))
{
dprintf ("select was interrupted by signal..\n");
- continue;
+ ping_set_errno (obj, EINTR);
+ return (-1);
}
else if (status < 0)
{
if (!ping_receive_one(obj, &nowtime, AF_INET))
--pings;
}
- else if (ptr != NULL && ptr->addrfamily == AF_INET &&
+ else if (ptr != NULL && ptr->addrfamily == AF_INET &&
FD_ISSET (fd4, &write_fds))
{
if (!ping_send_one(obj, ptr, fd4))
diff --git a/src/liboping.pc.in b/src/liboping.pc.in
--- /dev/null
+++ b/src/liboping.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: @PACKAGE_NAME@
+Description: C/C++ library to generate ICMP ECHO_REQUESTs
+URL: @PACKAGE_URL@
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -loping
+Libs.private: @LIBOPING_PC_LIBS_PRIVATE@
diff --git a/src/mans/liboping.pod b/src/mans/liboping.pod
index d2c42f9ea6cd77e39946f57726bab9d470daf466..c36a89c0e256122ccd2fe37b763a5e640605a2bf 100644 (file)
--- a/src/mans/liboping.pod
+++ b/src/mans/liboping.pod
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
diff --git a/src/mans/oping.pod b/src/mans/oping.pod
index e076299daa342f95863caf9cafd8946ccef3754b..f1e08071d1119311caf0f791507475e3bddb272e 100644 (file)
--- a/src/mans/oping.pod
+++ b/src/mans/oping.pod
=item B<-4>
-Force the use of IPv4.
+Force the use of IPv4.
=item B<-6>
Send one ICMP packet (per host) each I<interval> seconds. This can be a
floating-point number to specify sub-second precision.
+=item B<-w> I<timeout>
+
+Specifies the time to wait for an C<ECHO REPLY> packet before giving up, in
+seconds. This can be a floating point number for sub-second precision. Defaults
+to B<1.0> seconds.
+
=item B<-t> I<ttl>
Set the IP Time to Live to I<ttl>. This must be a number between (and
returned by L<geteuid(2)>) differ, the only argument allowed for this option is
"-" (i.e. standard input).
+=item B<-O> I<filename>
+
+Write measurements in I<Comma Separated Values> (CSV) format to I<filename>.
+This option writes three columns per row: wall clock time in (fractional)
+seconds since epoch, hostname and the round trip time in milliseconds.
+
=item B<-Q> I<qos>
Specify the I<Quality of Service> (QoS) for outgoing packets. This is a
I<Type of Service> (ToS) aliases were used to specify the bits of outgoing
packets.
+=item B<-m> I<mark>
+
+I<Linux only> Sets the I<mark> (an integer number) on outgoing packets. This
+can be used by L<iptables(8)> and other networking infrastructure for filtering
+and routing.
+
+=item B<-u>|B<-U>
+
+I<noping only> B<-u> forces UTF-8 output, B<-U> disables UTF-8 output. If
+neither is given, the codeset is automatically determined from the locale.
+
+=item B<-g> B<none>|B<prettyping>|B<boxplot>|B<histogram>
+
+I<noping only> Selects the graph to display.
+
+=over 4
+
+=item B<none>
+
+Do not show a graph.
+
+=item B<prettyping>
+
+Show a graph with time on the x-axis, the y-axis shows the round-trip time.
+This is the default graph.
+
+If your terminal supports unicode and colors, they are used to improve
+the precision of the data shown: a green box is drawn for round-trip times up
+to one third of the configured timeout, the height representing the RTT. Longer
+RTTs will start to fill the box yellow (with a green background) and then red
+(with a yellow background). Lost packages are drawn as a bold red explamation
+mark.
+
+=item B<boxplot>
+
+Show a I<box plot> where the x-axis, i.e. the width of the window, is the
+round-trip time. The entire width of the window it the ping interval, set with
+the B<-i> option.
+
+The box is sized so it contains 50% of the replies. The vertical line shows the
+median. The whiskers are sized to contain 95% of the replies -- 2.5% below the
+whiskers and 2.5% above.
+
+ |----------[#####|##########]--------------------------------------------|
+ ^ ^ ^ ^ ^
+ 2.5% 25% 50% 75% 97.5%
+
+=item B<histogram>
+
+Show a I<histrogram> of the round-trip times. The width of the window is taken
+as round-trip time from 0ms on the left to the I<interval> (the B<-i> option,
+default 1000ms) on the right.
+
+The height of the graph is scaled so that the most-used buckets vertically fills
+the line. The buckets are colored green up to and including the 80th
+percentile, yellow up to and including the 95th percentile and red for the
+remainder.
+
+=back
+
+=item B<-b>
+
+Audible bell. Print a ASCII BEL character (\a or 0x07) when a packet
+is received before the timeout occurs. This can be useful in order to
+monitory hosts' connectivity without looking physically at the
+console, for example to trace network cables (start audible beep,
+disconnect cable N: if beep stops, the cable was in use) or to tell
+when a host returns from a reboot.
+
+This relies on the terminal bell to be functional. To enable the
+terminal bell, use the following instructions.
+
+=over 4
+
+=item
+
+the visual bell is disabled in your terminal emulator, with the +vb
+commandline flag or the following in your .Xresources:
+
+ XTerm*visualBell: false
+
+=item
+
+the PC speaker module is loaded in your kernel:
+
+ modprobe pcspkr
+
+=item
+
+X11 has the terminal bell enabled:
+
+ xset b on; xset b 100
+
+=item
+
+and finally, if you are using PulseAudio, that the module-x11-bell
+module is loaded with a pre-loaded sample defined in your pulseaudio
+configuration:
+
+ load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/complete.oga
+ load-module module-x11-bell sample=x11-bell
+
+=back
+
+=item B<-P> I<percent>
+
+Configures the latency percentile to report. I<percent> must be a number
+between zero and 100, exclusively in both cases. In general, defaults to B<95>.
+If B<-c> is given and a number less than 20, this would be the same as the
+maximum. In this case the default is chosen so that it excludes the maximum,
+e.g. if B<-cE<nbsp>5> is given, the default is I<80>. The calculated percentile
+is based on the last 900 packets (15 minutes with the default interval).
+
+=item B<-Z> I<percent>
+
+If any hosts have a drop rate higher than I<percent>, where I<percent> is a
+number between zero and 100 inclusively, exit with a non-zero exit status.
+Since it is not possible to have a higher drop rate than 100%, passing this
+limit will effectively disable the feature (the default). Setting the option to
+zero means that the exit status will only be zero if I<all> replies for I<all>
+hosts have been received.
+
+The exit status will indicate the number of hosts with more than I<percent>
+packets lost, up to a number of 255 failing hosts.
+
=back
=head1 COLORS
the "expected" range, yellow marks moderately unusual times and times that
differ a lot from the expected value are printed in red.
-The information used to categorize round-trip times is the I<average>
-round-trip time and the I<standard deviation>. RTTs that differ from the
-average by less than the standard deviation are considered to be "normal" and
-are printed in green. Times that differ from the average more than the standard
-deviation but less than twice the standard deviation are considered "moderately
-unusual" and are printed in yellow. Times differing more than twice the
-standard deviation from the average are considered to be "unusual" and are
+The information used to categorize round-trip times is the I<percentile>. RTTs
+in the 80th percentile are considered to be "normal" and are printed in green.
+RTTs within the 95th percentile are considered "moderately unusual" and are
+printed in yellow. RTTs above that are considered to be "unusual" and are
printed in red.
=head1 SEE ALSO
-L<ping(8)>, L<http://www.fping.com/>, L<liboping(3)>
+L<ping(8)>, L<http://fping.org/>, L<liboping(3)>
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>ff at octo.itE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2010 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index da082c8a61dc27007caf3ed3600d9b7558fce0bd..019538c305a94c50c79d91ce5d0e533c6248e890 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index df9a015a54b632c84434f4eaf6dc809064dcdbc7..23ab2a7f6a12eda04767978bfecb7b133dac895f 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index 254ac3578736ed9c77fb20e14a3ecc89d04469a3..aaa18d3d3b6ccb2e83fca547064fb8f955bb321a 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index 860f448c2b0aca8514e7a9634d4b8dc5fb7dedb0..74d28586fdd3724fed43f052410c9b2122e2c02d 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index f0585abdba82fa82b000b06b0fb1a56e9a640cf7..82bc0ab5a6ca5a15aba1c3a640626c7954a539f8 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index 744a552f887b6a683d77633ba1806c0c12dbd741..849e156f25bdb8c60241b0b23d70f4c79fa9aa58 100644 (file)
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
diff --git a/src/mans/ping_send.pod b/src/mans/ping_send.pod
index 8237daed1e96b41c0c45c73646631ebe28d39e7c..8bf9bd8987f9c6d0d70357c75b91fdd173d3863b 100644 (file)
--- a/src/mans/ping_send.pod
+++ b/src/mans/ping_send.pod
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2009 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
index 582b3559b4231a734c8a0f0330103ba7810925a5..f8c4ff66ed4b6a1770f3c702aefcb3394662f974 100644 (file)
--- a/src/mans/ping_setopt.pod
+++ b/src/mans/ping_setopt.pod
responsibility to chose a valid bit combination. For details, read the L<ip(7)>
and L<ipv6(7)> manual pages, as well as I<RFCE<nbsp>2474>.
+=item B<PING_OPT_MARK>
+
+Mark (as in netfilter) outgoing packets using the SO_MARK socket option. Takes
+an int* pointer as a value. Setting this requires CAP_NET_ADMIN under Linux.
+Fails with C<operation not supported> on platforms which don't have SO_MARK.
+
=back
The I<val> argument is a pointer to the new value. It must not be NULL. It is
=head1 AUTHOR
-liboping is written by Florian octo Forster E<lt>octo at verplant.orgE<gt>.
-Its homepage can be found at L<http://verplant.org/liboping/>.
+liboping is written by Florian "octo" Forster E<lt>ff at octo.itE<gt>.
+Its homepage can be found at L<http://noping.cc/>.
-(c) 2005-2010 by Florian octo Forster.
+Copyright (c) 2005-2016 by Florian "octo" Forster.
diff --git a/src/oping.c b/src/oping.c
index 79d7569db279b8a1d04eeb8b2ced4e3669179188..528c90d1e25930077472719056ed6a8112ab3ad3 100644 (file)
--- a/src/oping.c
+++ b/src/oping.c
/**
* Object oriented C module to send ICMP and ICMPv6 `echo's.
- * Copyright (C) 2006-2011 Florian octo Forster <ff at octo.it>
+ * Copyright (C) 2006-2016 Florian octo Forster <ff at octo.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <sys/types.h>
#endif
+#include <locale.h>
+#include <langinfo.h>
+
#if USE_NCURSES
# define NCURSES_OPAQUE 1
-# include <ncurses.h>
+/* http://newsgroups.derkeiler.com/Archive/Rec/rec.games.roguelike.development/2010-09/msg00050.html */
+# define _X_OPEN_SOURCE_EXTENDED
+
+#if defined HAVE_NCURSESW_CURSES_H
+# include <ncursesw/curses.h>
+#elif defined HAVE_NCURSESW_H
+# include <ncursesw.h>
+#elif defined HAVE_NCURSES_CURSES_H
+# include <ncurses/curses.h>
+#elif defined HAVE_NCURSES_H
+# include <ncurses.h>
+#else
+# error "SysV or X/Open-compatible Curses header file required"
+#endif
# define OPING_GREEN 1
# define OPING_YELLOW 2
# define OPING_RED 3
+# define OPING_GREEN_HIST 4
+# define OPING_YELLOW_HIST 5
+# define OPING_RED_HIST 6
+
+double const threshold_green = 0.8;
+double const threshold_yellow = 0.95;
+
+static char const * const hist_symbols_utf8[] = {
+ "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
+static size_t const hist_symbols_utf8_num = sizeof (hist_symbols_utf8)
+ / sizeof (hist_symbols_utf8[0]);
+
+/* scancodes for 6 levels of horizontal bars, ncurses-specific */
+/* those are not the usual constants because those are not constant */
+static int const hist_symbols_acs[] = {
+ 115, /* ACS_S9 "⎽" */
+ 114, /* ACS_S7 "⎼" */
+ 113, /* ACS_S5 "─" */
+ 112, /* ACS_S3 "⎻" */
+ 111 /* ACS_S1 "⎺" */
+};
+static size_t const hist_symbols_acs_num = sizeof (hist_symbols_acs)
+ / sizeof (hist_symbols_acs[0]);
+
+/* use different colors without a background for scancodes */
+static int const hist_colors_utf8[] = {
+ OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
+static int const hist_colors_acs[] = {
+ OPING_GREEN, OPING_YELLOW, OPING_RED };
+/* assuming that both arrays are the same size */
+static size_t const hist_colors_num = sizeof (hist_colors_utf8)
+ / sizeof (hist_colors_utf8[0]);
#endif
+/* "─" */
+#define BOXPLOT_WHISKER_BAR (113 | A_ALTCHARSET)
+/* "├" */
+#define BOXPLOT_WHISKER_LEFT_END (116 | A_ALTCHARSET)
+/* "┤" */
+#define BOXPLOT_WHISKER_RIGHT_END (117 | A_ALTCHARSET)
+/* Inverted */
+#define BOXPLOT_BOX ' '
+/* "│", inverted */
+#define BOXPLOT_MEDIAN (120 | A_ALTCHARSET)
+
#include "oping.h"
#ifndef _POSIX_SAVED_IDS
# define _POSIX_SAVED_IDS 0
#endif
+#ifndef IPTOS_MINCOST
+# define IPTOS_MINCOST 0x02
+#endif
+
/* Remove GNU specific __attribute__ settings when using another compiler */
#if !__GNUC__
# define __attribute__(x) /**/
int req_sent;
int req_rcvd;
- double latency_min;
- double latency_max;
double latency_total;
- double latency_total_square;
+
+#ifndef HISTORY_SIZE_MAX
+# define HISTORY_SIZE_MAX 900
+#endif
+ /* The last n RTTs in the order they were sent. */
+ double history_by_time[HISTORY_SIZE_MAX];
+
+ /* Current number of entries in the history. This is a value between 0
+ * and HISTORY_SIZE_MAX. */
+ size_t history_size;
+
+ /* Total number of reponses received. */
+ size_t history_received;
+
+ /* Index of the next RTT to be written to history_by_time. This wraps
+ * around to 0 once the histroty has grown to HISTORY_SIZE_MAX. */
+ size_t history_index;
+
+ /* The last history_size RTTs sorted by value. timed out packets (NAN
+ * entries) are sorted to the back. */
+ double history_by_value[HISTORY_SIZE_MAX];
+
+ /* If set to true, history_by_value has to be re-calculated. */
+ _Bool history_dirty;
#if USE_NCURSES
WINDOW *window;
} ping_context_t;
static double opt_interval = 1.0;
+static double opt_timeout = PING_DEF_TIMEOUT;
static int opt_addrfamily = PING_DEF_AF;
static char *opt_srcaddr = NULL;
static char *opt_device = NULL;
+static char *opt_mark = NULL;
static char *opt_filename = NULL;
static int opt_count = -1;
static int opt_send_ttl = 64;
static uint8_t opt_send_qos = 0;
+#define OPING_DEFAULT_PERCENTILE 95.0
+static double opt_percentile = -1.0;
+static double opt_exit_status_threshold = 1.0;
+#if USE_NCURSES
+static int opt_show_graph = 1;
+static int opt_utf8 = 0;
+#endif
+static char *opt_outfile = NULL;
+static int opt_bell = 0;
-static int host_num = 0;
+static int host_num = 0;
+static FILE *outfile = NULL;
#if USE_NCURSES
static WINDOW *main_win = NULL;
opt_count = 0;
} /* }}} void sigint_handler */
-static ping_context_t *context_create (void) /* {{{ */
+static ping_context_t *context_create () /* {{{ */
{
- ping_context_t *ret;
-
- if ((ret = malloc (sizeof (ping_context_t))) == NULL)
+ ping_context_t *ctx = calloc (1, sizeof (*ctx));
+ if (ctx == NULL)
return (NULL);
- memset (ret, '\0', sizeof (ping_context_t));
-
- ret->latency_min = -1.0;
- ret->latency_max = -1.0;
- ret->latency_total = 0.0;
- ret->latency_total_square = 0.0;
-
#if USE_NCURSES
- ret->window = NULL;
+ ctx->window = NULL;
#endif
- return (ret);
+ return (ctx);
} /* }}} ping_context_t *context_create */
static void context_destroy (ping_context_t *context) /* {{{ */
free (context);
} /* }}} void context_destroy */
-static double context_get_average (ping_context_t *ctx) /* {{{ */
+static int compare_double (void const *arg0, void const *arg1) /* {{{ */
{
- double num_total;
+ double dbl0 = *((double *) arg0);
+ double dbl1 = *((double *) arg1);
- if (ctx == NULL)
- return (-1.0);
+ if (isnan (dbl0))
+ {
+ if (isnan (dbl1))
+ return 0;
+ else
+ return 1;
+ }
+ else if (isnan (dbl1))
+ return -1;
+ else if (dbl0 < dbl1)
+ return -1;
+ else if (dbl0 > dbl1)
+ return 1;
+ else
+ return 0;
+} /* }}} int compare_double */
+
+static void clean_history (ping_context_t *ctx) /* {{{ */
+{
+ size_t i;
+
+ if (!ctx->history_dirty)
+ return;
+
+ /* Copy all values from by_time to by_value. */
+ memcpy (ctx->history_by_value, ctx->history_by_time,
+ sizeof (ctx->history_by_time));
- if (ctx->req_rcvd < 1)
- return (-0.0);
+ /* Sort all RTTs. */
+ qsort (ctx->history_by_value, ctx->history_size, sizeof
+ (ctx->history_by_value[0]), compare_double);
- num_total = (double) ctx->req_rcvd;
- return (ctx->latency_total / num_total);
-} /* }}} double context_get_average */
+ /* Update the number of received RTTs. */
+ ctx->history_received = 0;
+ for (i = 0; i < ctx->history_size; i++)
+ if (!isnan (ctx->history_by_value[i]))
+ ctx->history_received++;
-static double context_get_stddev (ping_context_t *ctx) /* {{{ */
+ /* Mark as clean. */
+ ctx->history_dirty = 0;
+} /* }}} void clean_history */
+
+static double percentile_to_latency (ping_context_t *ctx, /* {{{ */
+ double percentile)
{
- double num_total;
+ size_t index;
- if (ctx == NULL)
- return (-1.0);
+ clean_history (ctx);
- if (ctx->req_rcvd < 1)
- return (-0.0);
- else if (ctx->req_rcvd < 2)
- return (0.0);
+ /* Not a single packet was received successfully. */
+ if (ctx->history_received == 0)
+ return NAN;
+
+ if (percentile <= 0.0)
+ index = 0;
+ else if (percentile >= 100.0)
+ index = ctx->history_received - 1;
+ else
+ {
+ index = (size_t) ceil ((percentile / 100.0) * ((double) ctx->history_received));
+ assert (index > 0);
+ index--;
+ }
- num_total = (double) ctx->req_rcvd;
- return (sqrt (((num_total * ctx->latency_total_square)
- - (ctx->latency_total * ctx->latency_total))
- / (num_total * (num_total - 1.0))));
-} /* }}} double context_get_stddev */
+ return (ctx->history_by_value[index]);
+} /* }}} double percentile_to_latency */
+
+#if USE_NCURSES
+static double latency_to_ratio (ping_context_t *ctx, /* {{{ */
+ double latency)
+{
+ size_t low;
+ size_t high;
+ size_t index;
+
+ clean_history (ctx);
+
+ /* Not a single packet was received successfully. */
+ if (ctx->history_received == 0)
+ return NAN;
+
+ low = 0;
+ high = ctx->history_received - 1;
+
+ if (latency < ctx->history_by_value[low])
+ return 0.0;
+ else if (latency >= ctx->history_by_value[high])
+ return 100.0;
+
+ /* Do a binary search for the latency. This will work even when the
+ * exact latency is not in the array. If the latency is in the array
+ * multiple times, "low" will be set to the index of the last
+ * occurrence. The value at index "high" will be larger than the
+ * searched for latency (assured by the above "if" block. */
+ while ((high - low) > 1)
+ {
+ index = (high + low) / 2;
+
+ if (ctx->history_by_value[index] > latency)
+ high = index;
+ else
+ low = index;
+ }
+
+ assert (ctx->history_by_value[high] > latency);
+ assert (ctx->history_by_value[low] <= latency);
+
+ if (ctx->history_by_value[low] == latency)
+ index = low;
+ else
+ index = high;
+
+ return (((double) (index + 1)) / ((double) ctx->history_received));
+} /* }}} double latency_to_ratio */
+#endif
static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
{
" -4|-6 force the use of IPv4 or IPv6\n"
" -c count number of ICMP packets to send\n"
" -i interval interval with which to send ICMP packets\n"
+ " -w timeout time to wait for replies, in seconds\n"
" -t ttl time to live for each ICMP packet\n"
" -Q qos Quality of Service (QoS) of outgoing packets\n"
" Use \"-Q help\" for a list of valid options.\n"
" -I srcaddr source address\n"
" -D device outgoing interface name\n"
- " -f filename filename to read hosts from\n"
+ " -m mark mark to set on outgoing packets\n"
+ " -f filename read hosts from <filename>\n"
+ " -O filename write RTT measurements to <filename>\n"
+#if USE_NCURSES
+ " -u / -U force / disable UTF-8 output\n"
+ " -g graph graph type to draw\n"
+#endif
+ " -P percent Report the n'th percentile of latency\n"
+ " -Z percent Exit with non-zero exit status if more than this percentage of\n"
+ " probes timed out. (default: never)\n"
- "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
- "by Florian octo Forster <octo@verplant.org>\n"
+ "\noping "PACKAGE_VERSION", http://noping.cc/\n"
+ "by Florian octo Forster <ff@octo.it>\n"
"for contributions see `AUTHORS'\n",
name);
exit (status);
while (1)
{
- optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:");
+ optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:Z:O:P:m:w:b"
+#if USE_NCURSES
+ "uUg:"
+#endif
+ );
if (optchar == -1)
break;
int new_count;
new_count = atoi (optarg);
if (new_count > 0)
+ {
opt_count = new_count;
+
+ if ((opt_percentile < 0.0) && (opt_count < 20))
+ opt_percentile = 100.0 * (opt_count - 1) / opt_count;
+ }
else
fprintf(stderr, "Ignoring invalid count: %s\n",
optarg);
opt_interval = new_interval;
}
break;
+
+ case 'w':
+ {
+ char *endp = NULL;
+ double t = strtod (optarg, &endp);
+ if ((optarg[0] != 0) && (endp != NULL) && (*endp == 0))
+ opt_timeout = t;
+ else
+ fprintf (stderr, "Ignoring invalid timeout: %s\n",
+ optarg);
+ }
+ break;
+
case 'I':
{
if (opt_srcaddr != NULL)
opt_device = optarg;
break;
+ case 'm':
+ opt_mark = optarg;
+ break;
+
case 't':
{
int new_send_ttl;
set_opt_send_qos (optarg);
break;
+ case 'O':
+ {
+ free (opt_outfile);
+ opt_outfile = strdup (optarg);
+ }
+ break;
+
+ case 'P':
+ {
+ double new_percentile;
+ new_percentile = atof (optarg);
+ if (isnan (new_percentile)
+ || (new_percentile < 0.1)
+ || (new_percentile > 100.0))
+ fprintf (stderr, "Ignoring invalid percentile: %s\n",
+ optarg);
+ else
+ opt_percentile = new_percentile;
+ }
+ break;
+
+#if USE_NCURSES
+ case 'g':
+ if (strcasecmp ("none", optarg) == 0)
+ opt_show_graph = 0;
+ else if (strcasecmp ("prettyping", optarg) == 0)
+ opt_show_graph = 1;
+ else if (strcasecmp ("histogram", optarg) == 0)
+ opt_show_graph = 2;
+ else if (strcasecmp ("boxplot", optarg) == 0)
+ opt_show_graph = 3;
+ else
+ fprintf (stderr, "Unknown graph option: %s\n", optarg);
+ break;
+
+ case 'u':
+ opt_utf8 = 2;
+ break;
+ case 'U':
+ opt_utf8 = 1;
+ break;
+#endif
+ case 'b':
+ opt_bell = 1;
+ break;
+
+ case 'Z':
+ {
+ char *endptr = NULL;
+ double tmp;
+
+ errno = 0;
+ tmp = strtod (optarg, &endptr);
+ if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (tmp < 0.0) || (tmp > 100.0))
+ {
+ fprintf (stderr, "Ignoring invalid -Z argument: %s\n", optarg);
+ fprintf (stderr, "The \"-Z\" option requires a numeric argument between 0 and 100.\n");
+ }
+ else
+ opt_exit_status_threshold = tmp / 100.0;
+
+ break;
+ }
+
case 'h':
usage_exit (argv[0], 0);
break;
+
default:
usage_exit (argv[0], 1);
}
}
+ if (opt_percentile <= 0.0)
+ opt_percentile = OPING_DEFAULT_PERCENTILE;
+
return (optind);
} /* }}} read_options */
} /* }}} void time_calc */
#if USE_NCURSES
-static int update_stats_from_context (ping_context_t *ctx) /* {{{ */
+static _Bool has_utf8() /* {{{ */
{
+# if HAVE_NCURSESW_NCURSES_H
+ if (!opt_utf8)
+ {
+ /* Automatically determine */
+ if (strcasecmp ("UTF-8", nl_langinfo (CODESET)) == 0)
+ opt_utf8 = 2;
+ else
+ opt_utf8 = 1;
+ }
+ return ((_Bool) (opt_utf8 - 1));
+# else
+ return (0);
+# endif
+} /* }}} _Bool has_utf8 */
+
+static int update_graph_boxplot (ping_context_t *ctx) /* {{{ */
+{
+ uint32_t *counters;
+ double *ratios;
+ size_t i;
+ size_t x_max;
+ size_t x;
+
+ clean_history (ctx);
+
+ if (ctx->history_received == 0)
+ return (ENOENT);
+
+ x_max = (size_t) getmaxx (ctx->window);
+ if (x_max <= 8)
+ return (EINVAL);
+ x_max -= 4;
+
+ counters = calloc (x_max, sizeof (*counters));
+ ratios = calloc (x_max, sizeof (*ratios));
+
+ /* Bucketize */
+ for (i = 0; i < ctx->history_received; i++)
+ {
+ double latency = ctx->history_by_value[i] / 1000.0;
+ size_t index = (size_t) (((double) x_max) * latency / opt_interval);
+
+ if (index >= x_max)
+ index = x_max - 1;
+
+ counters[index]++;
+ }
+
+ /* Sum and calc ratios */
+ ratios[0] = ((double) counters[0]) / ((double) ctx->history_received);
+ for (x = 1; x < x_max; x++)
+ {
+ counters[x] += counters[x - 1];
+ ratios[x] = ((double) counters[x]) / ((double) ctx->history_received);
+ }
+
+ for (x = 0; x < x_max; x++)
+ {
+ int symbol = ' ';
+ _Bool reverse = 0;
+
+ if (x == 0)
+ {
+ if (ratios[x] >= 0.5)
+ {
+ symbol = BOXPLOT_MEDIAN;
+ reverse = 1;
+ }
+ else if (ratios[x] > 0.25)
+ {
+ symbol = BOXPLOT_BOX;
+ reverse = 1;
+ }
+ else if (ratios[x] > 0.025)
+ symbol = BOXPLOT_WHISKER_BAR;
+ else
+ symbol = ' '; /* NOP */
+ }
+ else /* (x != 0) */
+ {
+ if ((ratios[x - 1] < 0.5) && (ratios[x] >= 0.5))
+ {
+ symbol = BOXPLOT_MEDIAN;
+ reverse = 1;
+ }
+ else if (((ratios[x] >= 0.25) && (ratios[x] <= 0.75))
+ || ((ratios[x - 1] < 0.75) && (ratios[x] > 0.75)))
+ {
+ symbol = BOXPLOT_BOX;
+ reverse = 1;
+ }
+ else if ((ratios[x] < 0.5) && (ratios[x] >= 0.025))
+ {
+ if (ratios[x - 1] < 0.025)
+ symbol = BOXPLOT_WHISKER_LEFT_END;
+ else
+ symbol = BOXPLOT_WHISKER_BAR;
+ }
+ else if ((ratios[x] > .5) && (ratios[x] < 0.975))
+ {
+ symbol = BOXPLOT_WHISKER_BAR;
+ }
+ else if ((ratios[x] >= 0.975) && (ratios[x - 1] < 0.975))
+ symbol = BOXPLOT_WHISKER_RIGHT_END;
+ }
+
+ if (reverse)
+ wattron (ctx->window, A_REVERSE);
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
+ // mvwprintw (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
+ if (reverse)
+ wattroff (ctx->window, A_REVERSE);
+ }
+
+ free (counters);
+ free (ratios);
+ return (0);
+} /* }}} int update_graph_boxplot */
+
+static int update_graph_prettyping (ping_context_t *ctx, /* {{{ */
+ double latency, unsigned int sequence)
+{
+ size_t x;
+ size_t x_max;
+ size_t history_offset;
+
+ x_max = (size_t) getmaxx (ctx->window);
+ if (x_max <= 4)
+ return (EINVAL);
+ x_max -= 4;
+
+ /* Determine the first index in the history we need to draw
+ * the graph. */
+ history_offset = 0;
+ if (((size_t) x_max) < ctx->history_size) /* window is smaller than history */
+ {
+ if (ctx->history_index > x_max)
+ history_offset = ctx->history_index - x_max;
+ else /* wrap around */
+ history_offset = ctx->history_index + ctx->history_size - x_max;
+ }
+ else /* window is larger than history */
+ {
+ if (ctx->history_index != ctx->history_size) /* no longer growing. */
+ history_offset = ctx->history_index;
+ else /* start-up */
+ history_offset = 0;
+ }
+
+ for (x = 0; x < x_max; x++)
+ {
+ size_t index;
+ double latency;
+
+ int color = OPING_RED;
+ char const *symbol = "!";
+ int symbolc = '!';
+
+ if (x >= ctx->history_size)
+ {
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
+ continue;
+ }
+
+ index = (history_offset + x) % ctx->history_size;
+ latency = ctx->history_by_time[index];
+
+ if (latency >= 0.0)
+ {
+ double ratio;
+
+ size_t symbols_num = hist_symbols_acs_num;
+ size_t colors_num = 1;
+
+ size_t index_symbols;
+ size_t index_colors;
+ size_t intensity;
+
+ /* latency is in milliseconds, opt_interval is in seconds. */
+ ratio = (latency * 0.001) / opt_interval;
+ if (ratio > 1) {
+ ratio = 1.0;
+ }
+
+ if (has_utf8 ())
+ symbols_num = hist_symbols_utf8_num;
+
+ if (has_colors () == TRUE)
+ colors_num = hist_colors_num;
+
+ intensity = (size_t) (ratio * ((double) (symbols_num * colors_num)));
+ if (intensity >= (symbols_num * colors_num))
+ intensity = (symbols_num * colors_num) - 1;
+
+ index_symbols = intensity % symbols_num;
+ assert (index_symbols < symbols_num);
+
+ index_colors = intensity / symbols_num;
+ assert (index_colors < colors_num);
+
+ if (has_utf8())
+ {
+ color = hist_colors_utf8[index_colors];
+ symbol = hist_symbols_utf8[index_symbols];
+ }
+ else
+ {
+ color = hist_colors_acs[index_colors];
+ symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
+ }
+ }
+ else /* if (!(latency >= 0.0)) */
+ wattron (ctx->window, A_BOLD);
+
+ if (has_colors () == TRUE)
+ wattron (ctx->window, COLOR_PAIR(color));
+
+ if (has_utf8())
+ mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2, symbol);
+ else
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, symbolc);
+
+ if (has_colors () == TRUE)
+ wattroff (ctx->window, COLOR_PAIR(color));
+
+ /* Use negation here to handle NaN correctly. */
+ if (!(latency >= 0.0))
+ wattroff (ctx->window, A_BOLD);
+ } /* for (x) */
+
+ return (0);
+} /* }}} int update_graph_prettyping */
+
+static int update_graph_histogram (ping_context_t *ctx) /* {{{ */
+{
+ uint32_t *counters;
+ uint32_t *accumulated;
+ uint32_t max;
+ size_t i;
+ size_t x_max;
+ size_t x;
+
+ size_t symbols_num = hist_symbols_acs_num;
+
+ clean_history (ctx);
+
+ if (ctx->history_received == 0)
+ return (ENOENT);
+
+ if (has_utf8 ())
+ symbols_num = hist_symbols_utf8_num;
+
+ x_max = (size_t) getmaxx (ctx->window);
+ if (x_max <= 4)
+ return (EINVAL);
+ x_max -= 4;
+
+ counters = calloc (x_max, sizeof (*counters));
+ accumulated = calloc (x_max, sizeof (*accumulated));
+
+ /* Bucketize */
+ max = 0;
+ for (i = 0; i < ctx->history_received; i++)
+ {
+ double latency = ctx->history_by_value[i] / 1000.0;
+ size_t index = (size_t) (((double) x_max) * latency / opt_interval);
+
+ if (index >= x_max)
+ index = x_max - 1;
+
+ counters[index]++;
+ if (max < counters[index])
+ max = counters[index];
+ }
+
+ /* Sum */
+ accumulated[0] = counters[0];
+ for (x = 1; x < x_max; x++)
+ accumulated[x] = counters[x] + accumulated[x - 1];
+
+ /* Calculate ratios */
+ for (x = 0; x < x_max; x++)
+ {
+ double height = ((double) counters[x]) / ((double) max);
+ double ratio_this = ((double) accumulated[x]) / ((double) ctx->history_received);
+ double ratio_prev = 0.0;
+ size_t index;
+ int color = 0;
+
+ index = (size_t) (height * ((double) symbols_num));
+ if (index >= symbols_num)
+ index = symbols_num - 1;
+
+ if (x > 0)
+ ratio_prev = ((double) accumulated[x - 1]) / ((double) ctx->history_received);
+
+ if (has_colors () == TRUE)
+ {
+ if ((ratio_this <= threshold_green)
+ || ((ratio_prev < threshold_green)
+ && (ratio_this > threshold_green)))
+ color = OPING_GREEN;
+ else if ((ratio_this <= threshold_yellow)
+ || ((ratio_prev < threshold_yellow)
+ && (ratio_this > threshold_yellow)))
+ color = OPING_YELLOW;
+ else
+ color = OPING_RED;
+
+ wattron (ctx->window, COLOR_PAIR(color));
+ }
+
+ if (counters[x] == 0)
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
+ else if (has_utf8 ())
+ mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2,
+ hist_symbols_utf8[index]);
+ else
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2,
+ hist_symbols_acs[index] | A_ALTCHARSET);
+
+ if (has_colors () == TRUE)
+ wattroff (ctx->window, COLOR_PAIR(color));
+
+ }
+
+ free (accumulated);
+ return (0);
+} /* }}} int update_graph_histogram */
+
+static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
+{
+ double latency = -1.0;
+ size_t buffer_len = sizeof (latency);
+
+ ping_iterator_get_info (iter, PING_INFO_LATENCY,
+ &latency, &buffer_len);
+
+ unsigned int sequence = 0;
+ buffer_len = sizeof (sequence);
+ ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
+ &sequence, &buffer_len);
+
+
if ((ctx == NULL) || (ctx->window == NULL))
return (EINVAL);
- werase (ctx->window);
+ /* werase (ctx->window); */
box (ctx->window, 0, 0);
wattron (ctx->window, A_BOLD);
ctx->latency_total);
if (ctx->req_rcvd != 0)
{
- double average;
- double deviation;
+ double min;
+ double median;
+ double max;
+ double percentile;
+
+ min = percentile_to_latency (ctx, 0.0);
+ median = percentile_to_latency (ctx, 50.0);
+ max = percentile_to_latency (ctx, 100.0);
+ percentile = percentile_to_latency (ctx, opt_percentile);
- average = context_get_average (ctx);
- deviation = context_get_stddev (ctx);
-
mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
- "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
- ctx->latency_min,
- average,
- ctx->latency_max,
- deviation);
+ "RTT[ms]: min = %.0f, median = %.0f, p(%.0f) = %.0f, max = %.0f ",
+ min, median, opt_percentile, percentile, max);
}
+ if (opt_show_graph == 1)
+ update_graph_prettyping (ctx, latency, sequence);
+ else if (opt_show_graph == 2)
+ update_graph_histogram (ctx);
+ else if (opt_show_graph == 3)
+ update_graph_boxplot (ctx);
+
wrefresh (ctx->window);
return (0);
int width = 0;
int height = 0;
int main_win_height;
+ int box_height = (opt_show_graph == 0) ? 4 : 5;
getmaxyx (stdscr, height, width);
if ((height < 1) || (width < 1))
return (EINVAL);
- main_win_height = height - (4 * host_num);
+ main_win_height = height - (box_height * host_num);
wresize (main_win, main_win_height, /* width = */ width);
/* Allow scrolling */
scrollok (main_win, TRUE);
delwin (context->window);
context->window = NULL;
}
- context->window = newwin (/* height = */ 4,
+ context->window = newwin (/* height = */ box_height,
/* width = */ width,
- /* y = */ main_win_height + (4 * context->index),
+ /* y = */ main_win_height + (box_height * context->index),
/* x = */ 0);
}
break;
else if (key == KEY_RESIZE)
need_resize = 1;
+ else if (key == 'g')
+ {
+ if (opt_show_graph == 3)
+ opt_show_graph = 1;
+ else if (opt_show_graph > 0)
+ opt_show_graph++;
+ }
}
if (need_resize)
int width = 0;
int height = 0;
int main_win_height;
+ int box_height = (opt_show_graph == 0) ? 4 : 5;
initscr ();
cbreak ();
if (has_colors () == TRUE)
{
start_color ();
- init_pair (OPING_GREEN, COLOR_GREEN, /* default = */ 0);
- init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
- init_pair (OPING_RED, COLOR_RED, /* default = */ 0);
+ use_default_colors ();
+ init_pair (OPING_GREEN, COLOR_GREEN, /* default = */ -1);
+ init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ -1);
+ init_pair (OPING_RED, COLOR_RED, /* default = */ -1);
+ init_pair (OPING_GREEN_HIST, COLOR_GREEN, -1);
+ init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
+ init_pair (OPING_RED_HIST, COLOR_RED, COLOR_YELLOW);
}
- main_win_height = height - (4 * host_num);
+ main_win_height = height - (box_height * host_num);
main_win = newwin (/* height = */ main_win_height,
/* width = */ width,
/* y = */ 0, /* x = */ 0);
delwin (context->window);
context->window = NULL;
}
- context->window = newwin (/* height = */ 4,
+ context->window = newwin (/* height = */ box_height,
/* width = */ width,
- /* y = */ main_win_height + (4 * context->index),
+ /* y = */ main_win_height + (box_height * context->index),
/* x = */ 0);
}
@@ -819,6 +1465,30 @@ static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
} /* }}} int post_sleep_hook */
#endif
+static void update_context (ping_context_t *ctx, double latency) /* {{{ */
+{
+ ctx->req_sent++;
+
+ if (latency > 0.0)
+ {
+ ctx->req_rcvd++;
+ ctx->latency_total += latency;
+ }
+ else
+ {
+ latency = NAN;
+ }
+
+ ctx->history_by_time[ctx->history_index] = latency;
+
+ ctx->history_dirty = 1;
+
+ /* Update index and size. */
+ ctx->history_index = (ctx->history_index + 1) % HISTORY_SIZE_MAX;
+ if (ctx->history_size < HISTORY_SIZE_MAX)
+ ctx->history_size++;
+} /* }}} void update_context */
+
static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
__attribute__((unused)) int index)
{
# define HOST_PRINTF(...) printf(__VA_ARGS__)
#endif
- context->req_sent++;
+ update_context (context, latency);
+
if (latency > 0.0)
{
- context->req_rcvd++;
- context->latency_total += latency;
- context->latency_total_square += (latency * latency);
-
- if ((context->latency_max < 0.0) || (context->latency_max < latency))
- context->latency_max = latency;
- if ((context->latency_min < 0.0) || (context->latency_min > latency))
- context->latency_min = latency;
-
#if USE_NCURSES
if (has_colors () == TRUE)
{
+ double ratio;
int color = OPING_GREEN;
- double average = context_get_average (context);
- double stddev = context_get_stddev (context);
- if ((latency < (average - (2 * stddev)))
- || (latency > (average + (2 * stddev))))
- color = OPING_RED;
- else if ((latency < (average - stddev))
- || (latency > (average + stddev)))
+ ratio = latency_to_ratio (context, latency);
+ if (ratio < threshold_green)
+ color = OPING_GREEN;
+ else if (ratio < threshold_yellow)
color = OPING_YELLOW;
+ else
+ color = OPING_RED;
HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
data_len, context->host, context->addr,
#if USE_NCURSES
}
#endif
+ if (opt_bell) {
+#if USE_NCURSES
+ beep();
+#else
+ HOST_PRINTF ("\a");
+#endif
+ }
}
- else
+ else /* if (!(latency > 0.0)) */
{
#if USE_NCURSES
if (has_colors () == TRUE)
#endif
}
+ if (outfile != NULL)
+ {
+ struct timeval tv = {0};
+ if (gettimeofday (&tv, NULL) == 0)
+ {
+ double t = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0);
+
+ if ((sequence % 32) == 0)
+ fprintf (outfile, "#time,host,latency[ms]\n");
+
+ fprintf (outfile, "%.3f,\"%s\",%.2f\n", t, context->host, latency);
+ }
+ }
+
#if USE_NCURSES
- update_stats_from_context (context);
+ update_stats_from_context (context, iter);
wrefresh (main_win);
#endif
} /* }}} void update_host_hook */
+/* Prints statistics for each host, cleans up the contexts and returns the
+ * number of hosts which failed to return more than the fraction
+ * opt_exit_status_threshold of pings. */
static int post_loop_hook (pingobj_t *ping) /* {{{ */
{
pingobj_iter_t *iter;
+ int failure_count = 0;
#if USE_NCURSES
endwin ();
context_get_packet_loss (context),
context->latency_total);
- if (context->req_rcvd != 0)
{
- double average;
- double deviation;
-
- average = context_get_average (context);
- deviation = context_get_stddev (context);
+ double pct_failed = 1.0 - (((double) context->req_rcvd)
+ / ((double) context->req_sent));
+ if (pct_failed > opt_exit_status_threshold)
+ failure_count++;
+ }
- printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
- context->latency_min,
- average,
- context->latency_max,
- deviation);
+ if (context->req_rcvd != 0)
+ {
+ double min;
+ double median;
+ double max;
+ double percentile;
+
+ min = percentile_to_latency (context, 0.0);
+ median = percentile_to_latency (context, 50.0);
+ max = percentile_to_latency (context, 100.0);
+ percentile = percentile_to_latency (context, opt_percentile);
+
+ printf ("RTT[ms]: min = %.0f, median = %.0f, p(%.0f) = %.0f, max = %.0f\n",
+ min, median, opt_percentile, percentile, max);
}
ping_iterator_set_context (iter, NULL);
context_destroy (context);
}
- return (0);
+ return (failure_count);
} /* }}} int post_loop_hook */
int main (int argc, char **argv) /* {{{ */
}
#endif
+ setlocale(LC_ALL, "");
optind = read_options (argc, argv);
#if !_POSIX_SAVED_IDS
/* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
}
+ if (ping_setopt (ping, PING_OPT_TIMEOUT, (void*)(&opt_timeout)) != 0)
+ {
+ fprintf (stderr, "Setting timeout failed: %s\n",
+ ping_get_error (ping));
+ }
+
if (opt_addrfamily != PING_DEF_AF)
ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
}
}
+ if (opt_mark != NULL)
+ {
+ char *endp = NULL;
+ int mark = (int) strtol (opt_mark, &endp, /* base = */ 0);
+ if ((opt_mark[0] != 0) && (endp != NULL) && (*endp == 0))
+ {
+ if (ping_setopt(ping, PING_OPT_MARK, (void*)(&mark)) != 0)
+ {
+ fprintf (stderr, "Setting mark failed: %s\n",
+ ping_get_error (ping));
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Ignoring invalid mark: %s\n", optarg);
+ }
+ }
+
if (opt_filename != NULL)
{
FILE *infile;
exit (EXIT_FAILURE);
}
+ if (host_num == 0)
+ exit (EXIT_FAILURE);
+
#if _POSIX_SAVED_IDS
saved_set_uid = (uid_t) -1;
#endif
+ if (opt_outfile != NULL)
+ {
+ outfile = fopen (opt_outfile, "a");
+ if (outfile == NULL)
+ {
+ fprintf (stderr, "opening \"%s\" failed: %s\n",
+ opt_outfile, strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+ }
+
ping_initialize_contexts (ping);
if (i == 0)
return (1);
}
- if (ping_send (ping) < 0)
+ status = ping_send (ping);
+ if (status == -EINTR)
+ {
+ continue;
+ }
+ else if (status < 0)
{
fprintf (stderr, "ping_send failed: %s\n",
ping_get_error (ping));
/* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
{
- if (errno != EINTR)
+ if (errno == EINTR)
{
- perror ("nanosleep");
- break;
+ continue;
}
- else if (opt_count == 0)
+ else
{
- /* sigint */
+ perror ("nanosleep");
break;
}
}
opt_count--;
} /* while (opt_count != 0) */
- post_loop_hook (ping);
+ /* Returns the number of failed hosts according to -Z. */
+ status = post_loop_hook (ping);
ping_destroy (ping);
- return (0);
+ if (outfile != NULL)
+ {
+ fclose (outfile);
+ outfile = NULL;
+ }
+
+ if (status == 0)
+ exit (EXIT_SUCCESS);
+ else
+ {
+ if (status > 255)
+ status = 255;
+ exit (status);
+ }
} /* }}} int main */
/* vim: set fdm=marker : */
diff --git a/src/oping.h b/src/oping.h
index e53d1397b30ac1886eb769eb65e52abb3e6acc13..1970ef273f40e634c689cf8cfacf7b654c31d55c 100644 (file)
--- a/src/oping.h
+++ b/src/oping.h
/**
* Object oriented C module to send ICMP and ICMPv6 `echo's.
- * Copyright (C) 2006-2011 Florian octo Forster <ff at octo.it>
+ * Copyright (C) 2006-2016 Florian octo Forster <ff at octo.it>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
extern "C" {
#endif
-#define OPING_VERSION 1006001
+#define OPING_VERSION 1009000
/*
* Type definitions
#define PING_OPT_SOURCE 0x10
#define PING_OPT_DEVICE 0x20
#define PING_OPT_QOS 0x40
+#define PING_OPT_MARK 0x80
#define PING_DEF_TIMEOUT 1.0
#define PING_DEF_TTL 255