]> granicus.if.org Git - procps-ng/commitdiff
watch: add precision wait time option -p
authorAnthony DeRobertis <asd@suespammers.org>
Tue, 24 Nov 2009 00:00:46 +0000 (11:00 +1100)
committerCraig Small <csmall@enc.com.au>
Mon, 19 Dec 2011 10:57:09 +0000 (21:57 +1100)
A patch from Debian.

Bug-Debian: http://bugs.debian.org/183486
Reviewed-by: Craig Small <csmall@debian.org>
Backported-by: Sami Kerola <kerolasa@iki.fi>
watch.1
watch.c

diff --git a/watch.1 b/watch.1
index cbfa3b328b59014a35f93debb972df7826ff4a40..99ffa6b39668c916d7e8422c4e7cb9951119b307 100644 (file)
--- a/watch.1
+++ b/watch.1
@@ -4,7 +4,7 @@ watch \- execute a program periodically, showing output fullscreen
 .SH SYNOPSIS
 .na
 .B watch
-.RB [ \-bdehvtx ]
+.RB [ \-bdehpvtx ]
 .RB [ \-n
 .IR seconds ]
 .RB [ \-\-beep ]
@@ -15,6 +15,7 @@ watch \- execute a program periodically, showing output fullscreen
 .RB [ \-\-help ]
 .RB [ \-\-interval=\fIseconds\fP]
 .RB [ \-\-no\-title ]
+.RB [ \-\-precise ]
 .RB [ \-\-version ]
 .I command
 .SH DESCRIPTION
@@ -28,7 +29,24 @@ every 2 seconds; use
 .B \-n
 or
 .B \-\-interval
-to specify a different interval.
+to specify a different interval. Normally, this interval is interpreted
+as the amout of time between the completion of one run of
+.I command
+and the beginning of the next run. However, with the
+.I \-p
+or
+.I \-\-precise
+option, you can make
+.BR watch
+attempt to run
+.I command
+every
+.I interval
+seconds. Try it with
+.B ntptime
+and notice how the fractional seconds stays
+(nearly) the same, as opposed to normal mode where they continuously
+increase.
 .PP
 The
 .B \-d
@@ -102,11 +120,21 @@ watch echo '$$'
 .br
 watch echo "'"'$$'"'"
 .PP
+To see the effect of precision time keeping, try adding
+.I \-p
+to
+.IP
+watch \-n 10 sleep 1
+.PP
 You can watch for your administrator to install the latest kernel with
 .IP
 watch uname \-r
 .PP
-(Just kidding.)
+(Note that
+.I \-p
+isn't guaranteed to work across reboots, especially in the face of
+.B ntpdate
+or other bootup time-changing mechanisms)
 .SH BUGS
 Upon terminal resize, the screen will not be correctly repainted until the
 next scheduled update.  All
@@ -115,6 +143,22 @@ highlighting is lost on that update as well.
 .PP
 Non-printing characters are stripped from program output.  Use "cat -v" as
 part of the command pipeline if you want to see them.
+.PP
+.I \-\-precise
+mode doesn't yet have advanced temporal distortion technology to
+compensate for a
+.I command
+that takes more than
+.I interval
+seconds to execute.
+.B watch
+also can get into a state where it rapid-fires as many executions of
+.I command
+as it can to catch up from a previous executions running longer than
+.I interval
+(for example,
+.B netstat
+taking ages on a DNS lookup).
 .SH AUTHORS
 The original
 .B watch
@@ -122,3 +166,7 @@ was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
 corrections by Francois Pinard.  It was reworked and new features added by
 Mike Coleman <mkc@acm.org> in 1999.  The beep, exec, and error handling
 features were added by Morty Abzug <morty@frakir.org> in 2008.
+On a not so dark and stormy morning
+in March of 2003, Anthony DeRobertis <asd@suespammers.org> got sick of
+his watches that should update every minute eventually updating many
+seconds after the minute started, and added microsecond precision.
diff --git a/watch.c b/watch.c
index bfd15e68a986f6bfb5ffcc20cc223326aae583e2..cf407925278d7feb265d0ebe55f6336cbad45441 100644 (file)
--- a/watch.c
+++ b/watch.c
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/time.h>
 #include <time.h>
 #include <unistd.h>
 #include <termios.h>
@@ -39,13 +40,14 @@ static struct option longopts[] = {
        {"beep", no_argument, 0, 'b'},
        {"errexit", no_argument, 0, 'e'},
        {"exec", no_argument, 0, 'x'},
+       {"precise", no_argument, 0, 'p'},
        {"no-title", no_argument, 0, 't'},
        {"version", no_argument, 0, 'v'},
        {0, 0, 0, 0}
 };
 
 static char usage[] =
-    "Usage: %s [-bdhntvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+    "Usage: %s [-bdhnptvx] [--beep] [--color] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
 
 static char *progname;
 
@@ -54,6 +56,7 @@ static int height = 24, width = 80;
 static int screen_size_changed = 0;
 static int first_screen = 1;
 static int show_title = 2;  // number of lines used, 2 or 0
+static int precise_timekeeping = 0;
 
 #define min(x,y) ((x) > (y) ? (y) : (x))
 #define MAX_ANSIBUF 10
@@ -206,6 +209,15 @@ get_terminal_size(void)
        }
 }
 
+/* get current time in usec */
+typedef unsigned long long watch_usec_t;
+#define USECS_PER_SEC (1000000ull)
+watch_usec_t get_time_usec() {
+       struct timeval now;
+       gettimeofday(&now, NULL);
+       return USECS_PER_SEC*now.tv_sec + now.tv_usec;
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -221,6 +233,8 @@ main(int argc, char *argv[])
        char *command;
        char **command_argv;
        int command_length = 0; /* not including final \0 */
+    watch_usec_t next_loop; /* next loop time in us, used for precise time
+                               keeping only */
        int pipefd[2];
        int status;
        pid_t child;
@@ -228,7 +242,7 @@ main(int argc, char *argv[])
        setlocale(LC_ALL, "");
        progname = argv[0];
 
-       while ((optc = getopt_long(argc, argv, "+bced::hn:vtx", longopts, (int *) 0))
+       while ((optc = getopt_long(argc, argv, "+bced::hn:pvtx", longopts, (int *) 0))
               != EOF) {
                switch (optc) {
                case 'b':
@@ -266,6 +280,9 @@ main(int argc, char *argv[])
                                        interval = ~0u/1000000;
                        }
                        break;
+               case 'p':
+                       precise_timekeeping = 1;
+                       break;
                case 'v':
                        option_version = 1;
                        break;
@@ -289,6 +306,7 @@ main(int argc, char *argv[])
                fputs("  -e, --errexit\t\t\t\texit watch if the command has a non-zero exit\n", stderr);
                fputs("  -h, --help\t\t\t\tprint a summary of the options\n", stderr);
                fputs("  -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
+        fputs("  -p, --precise\t\t\t\tprecise timing, ignore command run time\n", stderr);
                fputs("  -v, --version\t\t\t\tprint the version number\n", stderr);
                fputs("  -t, --no-title\t\t\tturns off showing the header\n", stderr);
                fputs("  -x, --exec\t\t\t\tpass command to exec instead of sh\n", stderr);
@@ -336,6 +354,9 @@ main(int argc, char *argv[])
        noecho();
        cbreak();
 
+       if (precise_timekeeping)
+               next_loop = get_time_usec();
+
        for (;;) {
                time_t t = time(NULL);
                char *ts = ctime(&t);
@@ -486,6 +507,12 @@ main(int argc, char *argv[])
 
                first_screen = 0;
                refresh();
+               if (precise_timekeeping) {
+                       watch_usec_t cur_time = get_time_usec();
+                       next_loop += USECS_PER_SEC*interval;
+                       if (cur_time < next_loop)
+                               usleep(next_loop - cur_time);
+               } else
                usleep(interval * 1000000);
        }