From 129bc6d941ca68f19315535e1b46a14893782bc7 Mon Sep 17 00:00:00 2001
From: Rakesh Pandit <rakesh@fedoraproject.org>
Date: Tue, 6 Oct 2009 19:03:58 +0530
Subject: [PATCH] Added new option --timeout to ping/ping6 for setting timeout
 before ping exits.

---
 ChangeLog          |   21 +++++++++++++++++++++
 NEWS               |    6 +++++-
 doc/inetutils.texi |    6 ++++++
 ping/libping.c     |    1 +
 ping/ping.c        |   37 +++++++++++++++++++++++++------------
 ping/ping6.c       |   38 ++++++++++++++++++++++++++------------
 ping/ping_common.c |   13 +++++++++++++
 ping/ping_common.h |    2 ++
 8 files changed, 99 insertions(+), 25 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a7ec4c7..087165a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2009-10-06  Rakesh Pandit  <rakesh@fedoraproject.org>
+
+	* ping/ping_common.h (ping_data): New member `ping_start_time'.
+	(ping_timeout_p): New prototype.
+	* ping/ping_common.c (ping_timeout_p): New function.
+
+	* ping/ping6.c: Rename `timeout' to `resp_time', and use `timeout'
+	for --timeout.
+	(argp_options, parse_opt): New option --timeout.
+	(ping_run): Check if we should abort due to timeout.
+	(ping_init): Initialise P->ping_start_time.
+
+	* ping/ping.c: Rename `timeout' to `resp_time', and use `timeout'
+	for --timeout.
+	(argp_options, parse_opt): New option --timeout.
+	(ping_run): Check if we should abort due to timeout.
+	* ping/libping.c (ping_init): Initialise P->ping_start_time.
+
+	* doc/inetutils.texi (ping invocation): Updated.
+	* NEWS: Updated.
+
 2009-10-01  Rakesh Pandit <rakesh@fedoraproject.org>
 
 	* talkd/talkd.c [HAVE_CONFIG_H]: Include config.h.
diff --git a/NEWS b/NEWS
index f256ee3..30beda0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU inetutils NEWS -- history of user-visible changes. 2009-06-28
+GNU inetutils NEWS -- history of user-visible changes. 2009-10-06
 
 Copyright (C) 2002, 2004, 2006, 2007, 2008, 2009 
 	  Free Software Foundation, Inc.
@@ -10,6 +10,10 @@ Please send inetutils bug reports to <bug-inetutils@gnu.org>.
 Unreleased
 Version 1.7:
 
+* ping
+
+New option --timeout=N, stop sending packets after N seconds.
+
 * New logger implementation
 
 The `logger' utility has been rewritten from scratch.  The new
diff --git a/doc/inetutils.texi b/doc/inetutils.texi
index ec011ee..aba0c28 100644
--- a/doc/inetutils.texi
+++ b/doc/inetutils.texi
@@ -468,6 +468,12 @@ routes.  Many hosts ignore or discard this option.
 Specifies the number of data bytes to be sent.  The default is 56,
 which translates into 64 @acronym{ICMP} data bytes when combined with
 the 8 bytes of ICMP header data.
+
+@item -w @var{n}
+@itemx --timeout=@var{n}
+@opindex -w
+@opindex --timeout
+Stop after @var{n} seconds.
 @end table
 
 @section Using ping for network fault isolation
diff --git a/ping/libping.c b/ping/libping.c
index c0d1d65..7e56d3d 100644
--- a/ping/libping.c
+++ b/ping/libping.c
@@ -89,6 +89,7 @@ ping_init (int type, int ident)
   /* Make sure we use only 16 bits in this field, id for icmp is a u_short.  */
   p->ping_ident = ident & 0xFFFF;
   p->ping_cktab_size = PING_CKTABSIZE;
+  gettimeofday (&p->ping_start_time, NULL);
   return p;
 }
 
diff --git a/ping/ping.c b/ping/ping.c
index b83332f..e3be687 100644
--- a/ping/ping.c
+++ b/ping/ping.c
@@ -67,6 +67,7 @@ size_t interval;
 size_t data_length = PING_DATALEN;
 unsigned options;
 unsigned long preload = 0;
+int timeout = -1;
 int (*ping_type) (char *hostname) = ping_echo;
 
 int (*decode_type (const char *arg)) (char *hostname);
@@ -113,6 +114,7 @@ static struct argp_option argp_options[] = {
   {"ignore-routing", 'r', NULL, 0, "send directly to a host on an attached "
    "network", GRP+1},
   {"verbose", 'v', NULL, 0, "verbose output", GRP+1},
+  {"timeout", 'w', "N", 0, "stop after N seconds", GRP+1},
 #undef GRP
 #define GRP 20
   {NULL, 0, NULL, 0, "Options valid for --echo requests:", GRP},
@@ -176,6 +178,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       options |= OPT_QUIET;
       break;
 
+    case 'w':
+      timeout = ping_cvt_number (arg, INT_MAX, 0);
+      break;
+
     case 'R':
       options |= OPT_RROUTE;
       break;
@@ -310,7 +316,7 @@ ping_run (PING * ping, int (*finish) ())
 {
   fd_set fdset;
   int fdmax;
-  struct timeval timeout;
+  struct timeval resp_time;
   struct timeval last, intvl, now;
   struct timeval *t = NULL;
   int finishing = 0;
@@ -342,24 +348,24 @@ ping_run (PING * ping, int (*finish) ())
       FD_ZERO (&fdset);
       FD_SET (ping->ping_fd, &fdset);
       gettimeofday (&now, NULL);
-      timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
-      timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
+      resp_time.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
+      resp_time.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
 
-      while (timeout.tv_usec < 0)
+      while (resp_time.tv_usec < 0)
 	{
-	  timeout.tv_usec += 1000000;
-	  timeout.tv_sec--;
+	  resp_time.tv_usec += 1000000;
+	  resp_time.tv_sec--;
 	}
-      while (timeout.tv_usec >= 1000000)
+      while (resp_time.tv_usec >= 1000000)
 	{
-	  timeout.tv_usec -= 1000000;
-	  timeout.tv_sec++;
+	  resp_time.tv_usec -= 1000000;
+	  resp_time.tv_sec++;
 	}
 
-      if (timeout.tv_sec < 0)
-	timeout.tv_sec = timeout.tv_usec = 0;
+      if (resp_time.tv_sec < 0)
+	resp_time.tv_sec = resp_time.tv_usec = 0;
 
-      n = select (fdmax, &fdset, NULL, NULL, &timeout);
+      n = select (fdmax, &fdset, NULL, NULL, &resp_time);
       if (n < 0)
 	{
 	  if (errno != EINTR)
@@ -375,6 +381,10 @@ ping_run (PING * ping, int (*finish) ())
 	      gettimeofday (&now, NULL);
 	      t = &now;
 	    }
+
+	  if (ping_timeout_p (&ping->ping_start_time, timeout))
+	    break;
+
 	  if (ping->ping_count && nresp >= ping->ping_count)
 	    break;
 	}
@@ -385,6 +395,9 @@ ping_run (PING * ping, int (*finish) ())
 	      send_echo (ping);
 	      if (!(options & OPT_QUIET) && options & OPT_FLOOD)
 		putchar ('.');
+
+	      if (ping_timeout_p (&ping->ping_start_time, timeout))
+		break;
 	    }
 	  else if (finishing)
 	    break;
diff --git a/ping/ping6.c b/ping/ping6.c
index 6a7fbff..bf89352 100644
--- a/ping/ping6.c
+++ b/ping/ping6.c
@@ -54,6 +54,7 @@ size_t data_length = PING_DATALEN;
 size_t count = DEFAULT_PING_COUNT;
 size_t interval;
 int socket_type;
+int timeout = -1;
 static unsigned int options;
 static unsigned long preload = 0;
 
@@ -80,6 +81,7 @@ static struct argp_option argp_options[] = {
   {"numeric", 'n', NULL, 0, "so not resolve host addresses", GRP+1},
   {"ignore-routing", 'r', NULL, 0, "send directly to a host on an attached "
    "network", GRP+1},
+  {"timeout", 'w', "N", 0, "stop after N seconds", GRP+1},
 #undef GRP
 #define GRP 10
   {NULL, 0, NULL, 0, "Options valid for --echo requests:", GRP},
@@ -151,6 +153,10 @@ parse_opt (int key, char *arg, struct argp_state *state)
       socket_type = SO_DEBUG;
       break;
 
+    case 'w':
+      timeout = ping_cvt_number (arg, INT_MAX, 0);
+      break;
+
     case 's':
       data_length = ping_cvt_number (arg, PING_MAX_DATALEN, 1);
       break;
@@ -266,7 +272,7 @@ ping_run (PING * ping, int (*finish) ())
 {
   fd_set fdset;
   int fdmax;
-  struct timeval timeout;
+  struct timeval resp_time;
   struct timeval last, intvl, now;
   struct timeval *t = NULL;
   int finishing = 0;
@@ -298,24 +304,24 @@ ping_run (PING * ping, int (*finish) ())
       FD_ZERO (&fdset);
       FD_SET (ping->ping_fd, &fdset);
       gettimeofday (&now, NULL);
-      timeout.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
-      timeout.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
+      resp_time.tv_sec = last.tv_sec + intvl.tv_sec - now.tv_sec;
+      resp_time.tv_usec = last.tv_usec + intvl.tv_usec - now.tv_usec;
 
-      while (timeout.tv_usec < 0)
+      while (resp_time.tv_usec < 0)
 	{
-	  timeout.tv_usec += 1000000;
-	  timeout.tv_sec--;
+	  resp_time.tv_usec += 1000000;
+	  resp_time.tv_sec--;
 	}
-      while (timeout.tv_usec >= 1000000)
+      while (resp_time.tv_usec >= 1000000)
 	{
-	  timeout.tv_usec -= 1000000;
-	  timeout.tv_sec++;
+	  resp_time.tv_usec -= 1000000;
+	  resp_time.tv_sec++;
 	}
 
-      if (timeout.tv_sec < 0)
-	timeout.tv_sec = timeout.tv_usec = 0;
+      if (resp_time.tv_sec < 0)
+	resp_time.tv_sec = resp_time.tv_usec = 0;
 
-      n = select (fdmax, &fdset, NULL, NULL, &timeout);
+      n = select (fdmax, &fdset, NULL, NULL, &resp_time);
       if (n < 0)
 	{
 	  if (errno != EINTR)
@@ -331,6 +337,10 @@ ping_run (PING * ping, int (*finish) ())
 	      gettimeofday (&now, NULL);
 	      t = &now;
 	    }
+
+	  if (ping_timeout_p (&ping->ping_start_time, timeout))
+	    break;
+
 	  if (ping->ping_count && nresp >= ping->ping_count)
 	    break;
 	}
@@ -343,6 +353,9 @@ ping_run (PING * ping, int (*finish) ())
 		{
 		  putchar ('.');
 		}
+
+	      if (ping_timeout_p (&ping->ping_start_time, timeout))
+		break;
 	    }
 	  else if (finishing)
 	    break;
@@ -741,6 +754,7 @@ ping_init (int type, int ident)
   /* Make sure we use only 16 bits in this field, id for icmp is a u_short.  */
   p->ping_ident = ident & 0xFFFF;
   p->ping_cktab_size = PING_CKTABSIZE;
+  gettimeofday (&p->ping_start_time, NULL);
   return p;
 }
 
diff --git a/ping/ping_common.c b/ping/ping_common.c
index 67c4cad..9abb002 100644
--- a/ping/ping_common.c
+++ b/ping/ping_common.c
@@ -216,3 +216,16 @@ ping_unset_data (PING * p)
   _ping_freebuf (p);
 }
 
+int
+ping_timeout_p (struct timeval *start_time, int timeout)
+{
+  struct timeval now;
+  gettimeofday (&now, NULL);
+  if (timeout != -1)
+    {
+      tvsub (&now, start_time);
+      if (now.tv_sec >= timeout)
+        return 1;
+    }
+  return 0;
+}
diff --git a/ping/ping_common.h b/ping/ping_common.h
index 62cc77b..ac1b6a5 100644
--- a/ping/ping_common.h
+++ b/ping/ping_common.h
@@ -97,6 +97,7 @@ struct ping_data
   int ping_fd;                 /* Raw socket descriptor */
   int ping_type;               /* Type of packets to send */
   size_t ping_count;           /* Number of packets to send */
+  struct timeval ping_start_time; /* Start time */
   size_t ping_interval;        /* Number of seconds to wait between sending pkts */
   union ping_address ping_dest;/* whom to ping */
   char *ping_hostname;         /* Printable hostname */
@@ -156,4 +157,5 @@ void ping_set_count (PING * ping, size_t count);
 void ping_set_sockopt (PING * ping, int opt, void *val, int valsize);
 void ping_set_interval (PING * ping, size_t interval);
 void ping_unset_data (PING * p);
+int ping_timeout_p (struct timeval *start_time, int timeout);
 
-- 
1.6.4.4

