]> granicus.if.org Git - procps-ng/commitdiff
watch: add -exec and -beep flags and has better quoting
authorMordechai T. Abzug <morty@frakir.org>
Tue, 24 Nov 2009 00:00:43 +0000 (11:00 +1100)
committerCraig Small <csmall@enc.com.au>
Sun, 18 Dec 2011 11:59:15 +0000 (22:59 +1100)
Additionally add -errexit flag (#183346).

A patch from Debian.

Bug-Debian: http://bugs.debian.org/410967
Bug-Debian: http://bugs.debian.org/183346
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 16167942ddaed93430bc3b60645015b4a6c94891..802ab8412dd9baaa0ca6436b547ede52b7f4c9d2 100644 (file)
--- a/watch.1
+++ b/watch.1
@@ -4,10 +4,13 @@ watch \- execute a program periodically, showing output fullscreen
 .SH SYNOPSIS
 .na
 .B watch
-.RB [ \-dhvt ]
+.RB [ \-bdehvtx ]
 .RB [ \-n
 .IR seconds ]
+.RB [ \-\-beep ]
 .RB [ \-\-differences[=\fIcumulative\fP]]
+.RB [ \-\-errexit ]
+.RB [ \-\-exec ]
 .RB [ \-\-help ]
 .RB [ \-\-interval=\fIseconds\fP]
 .RB [ \-\-no\-title ]
@@ -17,7 +20,8 @@ watch \- execute a program periodically, showing output fullscreen
 .B watch
 runs
 .I command
-repeatedly, displaying its output (the first screenfull).  This allows you to
+repeatedly, displaying its output and errors (the first screenfull).  This
+allows you to
 watch the program output change over time.  By default, the program is run
 every 2 seconds; use 
 .B \-n
@@ -37,15 +41,33 @@ positions that have ever changed.  The
 or
 .B \-\-no\-title
 option turns off the header showing the interval, command, and current
-time at the top of the display, as well as the following blank line.
+time at the top of the display, as well as the following blank line.  The
+.I \-b
+or
+.I \-\-beep
+option causes the command to beep if it has a non-zero exit.
 .PP
 .B watch
-will run until interrupted.
+will normally run until interrupted. If you want
+.B watch
+to exit on an error from the program running use the
+.I \-e
+or
+.I \-\-errexit
+options, which will cause
+.B watch
+to exit if the return value from the program is non-zero.
+
 .SH NOTE
 Note that
 .I command
 is given to "sh \-c"
 which means that you may need to use extra quoting to get the desired effect.
+You can disable this with the
+.I -x
+or
+.I --exec
+option, which passes the command to exec(2) instead.
 .PP
 Note that POSIX option processing is used (i.e., option processing stops at
 the first non\-option argument).  This means that flags after
@@ -93,4 +115,5 @@ The original
 .B watch
 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.
+Mike Coleman <mkc@acm.org> in 1999.  The beep, exec, and error handling
+features were added by Morty Abzug <morty@frakir.org> in 2008.
diff --git a/watch.c b/watch.c
index af7d2620039f0dbd487fc8176054efa5eef05e63..25f8964f225d80485162cce525c397c339bbc9d6 100644 (file)
--- a/watch.c
+++ b/watch.c
@@ -8,6 +8,7 @@
  * Mike Coleman <mkc@acm.org>.
  *
  * Changes by Albert Cahalan, 2002-2003.
+ * stderr handling, exec, and beep option added by Morty Abzug, 2008
  */
 
 #include <ctype.h>
@@ -34,13 +35,16 @@ static struct option longopts[] = {
        {"differences", optional_argument, 0, 'd'},
        {"help", no_argument, 0, 'h'},
        {"interval", required_argument, 0, 'n'},
+       {"beep", no_argument, 0, 'b'},
+       {"errexit", no_argument, 0, 'e'},
+       {"exec", no_argument, 0, 'x'},
        {"no-title", no_argument, 0, 't'},
        {"version", no_argument, 0, 'v'},
        {0, 0, 0, 0}
 };
 
 static char usage[] =
-    "Usage: %s [-dhntv] [--differences[=cumulative]] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
+    "Usage: %s [-bdhntvx] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n";
 
 static char *progname;
 
@@ -139,28 +143,44 @@ main(int argc, char *argv[])
        int optc;
        int option_differences = 0,
            option_differences_cumulative = 0,
+                       option_exec = 0,
+                       option_beep = 0,
+        option_errexit = 0,
            option_help = 0, option_version = 0;
        double interval = 2;
        char *command;
+       char **command_argv;
        int command_length = 0; /* not including final \0 */
+       int pipefd[2];
+       int status;
+       pid_t child;
 
        setlocale(LC_ALL, "");
        progname = argv[0];
 
-       while ((optc = getopt_long(argc, argv, "+d::hn:vt", longopts, (int *) 0))
+       while ((optc = getopt_long(argc, argv, "+bed::hn:vtx", longopts, (int *) 0))
               != EOF) {
                switch (optc) {
+               case 'b':
+                       option_beep = 1;
+                       break;
                case 'd':
                        option_differences = 1;
                        if (optarg)
                                option_differences_cumulative = 1;
                        break;
+        case 'e':
+            option_errexit = 1;
+            break;
                case 'h':
                        option_help = 1;
                        break;
                case 't':
                        show_title = 0;
                        break;
+               case 'x':
+                 option_exec = 1;
+                       break;
                case 'n':
                        {
                                char *str;
@@ -190,18 +210,23 @@ main(int argc, char *argv[])
 
        if (option_help) {
                fprintf(stderr, usage, progname);
+               fputs("  -b, --beep\t\t\t\tbeep if the command has a non-zero exit\n", stderr);
                fputs("  -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
                fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
+               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("  -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);
                exit(0);
        }
 
        if (optind >= argc)
                do_usage();
 
+       command_argv=&(argv[optind]); /* save for later */
+
        command = strdup(argv[optind++]);
        command_length = strlen(command);
        for (; optind < argc; optind++) {
@@ -260,11 +285,57 @@ main(int argc, char *argv[])
                        free(header);
                }
 
-               if (!(p = popen(command, "r"))) {
-                       perror("popen");
+               /* allocate pipes */
+               if (pipe(pipefd)<0) {
+                 perror("pipe");
+                       do_exit(7);
+         }
+
+               /* flush stdout and stderr, since we're about to do fd stuff */
+               fflush(stdout);
+               fflush(stderr);
+
+               /* fork to prepare to run command */
+               child=fork();
+
+               if (child<0) { /* fork error */
+                 perror("fork");
                        do_exit(2);
+               } else if (child==0) { /* in child */
+                       close (pipefd[0]); /* child doesn't need read side of pipe */
+                       close (1); /* prepare to replace stdout with pipe */
+                       if (dup2 (pipefd[1], 1)<0) { /* replace stdout with write side of pipe */
+                         perror("dup2");
+                               exit(3);
+                       }
+                       dup2(1, 2); /* stderr should default to stdout */
+
+                       if (option_exec) { /* pass command to exec instead of system */
+                         if (execvp(command_argv[0], command_argv)==-1) {
+                                 perror("exec");
+                                 exit(4);
+                               }
+                       } else {
+                   status=system(command); /* watch manpage promises sh quoting */
+
+                         /* propagate command exit status as child exit status */
+                         if (!WIFEXITED(status)) { /* child exits nonzero if command does */
+                           exit(1);
+                         } else {
+                           exit(WEXITSTATUS(status));
+                   }
+                       }
+
+               }
+
+               /* otherwise, we're in parent */
+               close(pipefd[1]); /* close write side of pipe */
+               if ((p=fdopen(pipefd[0], "r"))==NULL) {
+                 perror("fdopen");
+                       do_exit(5);
                }
 
+
                for (y = show_title; y < height; y++) {
                        int eolseen = 0, tabpending = 0;
                        for (x = 0; x < width; x++) {
@@ -312,7 +383,19 @@ main(int argc, char *argv[])
                        oldeolseen = eolseen;
                }
 
-               pclose(p);
+               fclose(p);
+
+               /* harvest child process and get status, propagated from command */
+               if (waitpid(child, &status, 0)<0) {
+                 perror("waitpid");
+                       do_exit(8);
+               };
+
+               /* if child process exited in error, beep if option_beep is set */
+               if ((!WIFEXITED(status) || WEXITSTATUS(status))) {
+          if (option_beep) beep();
+          if (option_errexit) do_exit(8);
+               }
 
                first_screen = 0;
                refresh();