The attached patch fixes this bug. It provides an option to change the default quoting behavior in watch (i.e. to use exec() instead of system()). Backwards compatibility is preserved because the default remains system(). It also fixes a few other open bugs in watch.
- Morty
diff -Nur procps-3.2.7.orig/watch.1 procps-3.2.7/watch.1 --- procps-3.2.7.orig/watch.1 2003-02-09 02:05:25.000000000 -0500 +++ procps-3.2.7/watch.1 2008-08-14 07:37:41.000000000 -0400 @@ -3,12 +3,13 @@ watch \- execute a program periodically, showing output fullscreen .SH SYNOPSIS .B watch -.I [\-dhvt] [\-n <seconds>] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=<seconds>] [\-\-no\-title] [\-\-version] <command> +.I [\-bedhvt] [\-n <seconds>] [\-\-beep] [\-\-differences[=cumulative]] [\-\-exec] [\-\-help] [\-\-interval=<seconds>] [\-\-no\-title] [\-\-version] <command> .SH DESCRIPTION .BR 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 .I -n @@ -28,7 +29,11 @@ or .I --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 .BR watch will run until interrupted. @@ -37,6 +42,11 @@ .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 -e +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 @@ -84,4 +94,5 @@ .B watch was written by Tony Rems <re...@unisoft.com> in 1991, with mods and corrections by Francois Pinard. It was reworked and new features added by -Mike Coleman <m...@acm.org> in 1999. +Mike Coleman <m...@acm.org> in 1999. The beep, exec, and error handling +features were added by Morty Abzug <mo...@frakir.org> in 2008. diff -Nur procps-3.2.7.orig/watch.c procps-3.2.7/watch.c --- procps-3.2.7.orig/watch.c 2006-06-17 05:18:38.000000000 -0400 +++ procps-3.2.7/watch.c 2008-08-14 07:32:14.000000000 -0400 @@ -8,6 +8,7 @@ * Mike Coleman <m...@acm.org>. * * Changes by Albert Cahalan, 2002-2003. + * stderr handling, exec, and beep option added by Morty Abzug, 2008 */ #define VERSION "0.2.0" @@ -35,13 +36,15 @@ {"differences", optional_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"interval", required_argument, 0, 'n'}, + {"beep", no_argument, 0, 'b'}, + {"exec", no_argument, 0, 'e'}, {"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 [-bedhntv] [--beep] [--differences[=cumulative]] [--exec] [--help] [--interval=<n>] [--no-title] [--version] <command>\n"; static char *progname; @@ -140,22 +143,34 @@ int optc; int option_differences = 0, option_differences_cumulative = 0, + option_exec = 0, + option_beep = 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:vt", 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_exec = 1; + break; case 'h': option_help = 1; break; @@ -191,7 +206,9 @@ 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(" -e, --exec\t\t\t\tpass command to exec instead of sh\n", stderr); fputs("\t\t(cumulative means highlighting is cumulative)\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); @@ -203,6 +220,8 @@ if (optind >= argc) do_usage(); + command_argv=&(argv[optind]); /* save for later */ + command = strdup(argv[optind++]); command_length = strlen(command); for (; optind < argc; optind++) { @@ -261,11 +280,57 @@ 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++) { @@ -313,7 +378,18 @@ 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 (option_beep && (!WIFEXITED(status) || WEXITSTATUS(status))) { + beep(); + } first_screen = 0; refresh();