One of the standard problems with running diagnostic utilities under batch schedulers is that they produce so MUCH junk, and you only want the last few lines. Unfortunately, using tail rarely works, as it gets killed when the job times out together with the failing program. This is particularly relevant for 'stuck' jobs that are killed by an elapsed time counter. I have added an option to textutils 2.0 to trap the standard signals and close down. I append the diffs - use them as you will. It should be pretty portable, but I can't test it much, as textutils 2.0 is VERY dependent on C99 extensions, and most systems I manage have C90 environments. Several files include code like the following: #if HAVE_INTTYPES_H # include <inttypes.h> #endif and then use uintmax_t or equivalent. This will rarely (if ever) work. Either the #if is redundant, or the code should be: #if HAVE_INTTYPES_H # include <inttypes.h> #else typedef unsigned long uintmax_t; #endif Also, for reasons that I haven't looked into, textutils 2.0 configure gets things even more wrong than usual. It defines HAVE_ALLOC_H and HAVE_INTTYPES_H on systems that don't have them, and probably gets other things wrong, too. I gave up after fixing the first half-dozen problems on those systems; textutils 1.22d works well enough. Regards, Nick Maclaren, University of Cambridge Computing Service, New Museums Site, Pembroke Street, Cambridge CB2 3QG, England. Email: [EMAIL PROTECTED] Tel.: +44 1223 334761 Fax: +44 1223 334679 *** ./doc/textutils.texi.org Sat Jul 31 10:03:30 1999 --- ./doc/textutils.texi Wed Aug 30 15:05:34 2000 *************** *** 1566,1571 **** --- 1566,1581 ---- See the output of @code{tail --help} for the default value. This option is meaningful only when following by name. + @itemx --trap-signals + @opindex --trap-signals + When running under a batch scheduler using tail to select just the + last few lines of diagnostic output, it is common for the tail process + to be killed as well as the failing process. This option requests that + tail traps all of the standard trappable, fatal signals that are + likely to be sent by the scheduler, writes any output and stops. It + will not help if the scheduler uses untrappable signals (like SIGKILL) + or non-standard signals. + @itemx -n @var{n} @itemx --lines=@var{n} @opindex -n *** ./man/tail.1.org Fri Aug 6 20:24:10 1999 --- ./man/tail.1 Wed Aug 30 16:06:52 2000 *************** *** 38,43 **** --- 38,46 ---- .IP (the default is 200) .TP + \fB\-\-trap\-signals\fR + trap standard fatal signals, write output and stop + .TP \fB\-\-pid\fR=\fIPID\fR with \fB\-f\fR, terminate after process ID, PID dies .TP *** ./src/tail.c.org Thu Aug 5 15:38:02 1999 --- ./src/tail.c Wed Aug 30 15:14:35 2000 *************** *** 180,185 **** --- 180,188 ---- /* Nonzero if we have ever read standard input. */ static int have_read_stdin; + /* Initially NULL, otherwise the signal name we are terminating with. */ + static const char *signal_name = NULL; + static struct option const long_options[] = { /* --allow-missing is deprecated; use --retry instead *************** *** 191,196 **** --- 194,200 ---- {"max-unchanged-stats", required_argument, NULL, CHAR_MAX + 2}, {"max-consecutive-size-changes", required_argument, NULL, CHAR_MAX + 3}, {"pid", required_argument, NULL, CHAR_MAX + 4}, + {"trap-signals", no_argument, NULL, CHAR_MAX + 5}, {"quiet", no_argument, NULL, 'q'}, {"retry", no_argument, NULL, CHAR_MAX + 1}, {"silent", no_argument, NULL, 'q'}, *************** *** 231,236 **** --- 235,241 ---- --max-consecutive-size-changes=N see the texinfo documentation\n\ (the default is %d)\n\ --pid=PID with -f, terminate after process ID, PID dies\n\ + --trap-signals trap POSIX fatal signals, write output and stop\n\ -q, --quiet, --silent never output headers giving file names\n\ -s, --sleep-interval=S with -f, sleep S seconds between iterations\n\ -v, --verbose always output headers giving file names\n\ *************** *** 294,299 **** --- 299,337 ---- } } + static ssize_t + SAFE_READ (int desc, void *ptr, size_t len) + { + if (signal_name != NULL) return 0; + return safe_read(desc,ptr,len); + } + + static void + signal_handler (int sig) + { + switch (sig) { + case SIGABRT: signal_name = "SIGABRT"; break; + case SIGALRM: signal_name = "SIGALRM"; break; + case SIGHUP: signal_name = "SIGHUP"; break; + case SIGINT: signal_name = "SIGINT"; break; + case SIGPIPE: signal_name = "SIGPIPE"; break; + case SIGQUIT: signal_name = "SIGQUIT"; break; + case SIGTERM: signal_name = "SIGTERM"; break; + case SIGUSR1: signal_name = "SIGUSR1"; break; + case SIGUSR2: signal_name = "SIGUSR2"; break; + #ifdef _XOPEN_UNIX + case SIGPOLL: signal_name = "SIGPOLL"; break; + case SIGPROF: signal_name = "SIGPROF"; break; + #ifdef SIGVTALARM + case SIGVTALARM: signal_name = "SIGVTALARM"; break; + #endif + case SIGXCPU: signal_name = "SIGXCPU"; break; + case SIGXFSZ: signal_name = "SIGXFSZ"; break; + #endif + default: signal_name = "<unknown>"; break; + } + } + static void write_header (const char *pretty_filename, const char *comment) { *************** *** 321,327 **** while (1) { long n = MIN (n_remaining, (off_t) BUFSIZ); ! bytes_read = safe_read (fd, buffer, n); if (bytes_read <= 0) break; xwrite (STDOUT_FILENO, buffer, bytes_read); --- 359,365 ---- while (1) { long n = MIN (n_remaining, (off_t) BUFSIZ); ! bytes_read = SAFE_READ (fd, buffer, n); if (bytes_read <= 0) break; xwrite (STDOUT_FILENO, buffer, bytes_read); *************** *** 364,370 **** pos -= bytes_read; /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); ! bytes_read = safe_read (fd, buffer, bytes_read); if (bytes_read == -1) { error (0, errno, "%s", pretty_filename); --- 402,408 ---- pos -= bytes_read; /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); ! bytes_read = SAFE_READ (fd, buffer, bytes_read); if (bytes_read == -1) { error (0, errno, "%s", pretty_filename); *************** *** 405,411 **** /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); } ! while ((bytes_read = safe_read (fd, buffer, BUFSIZ)) > 0); if (bytes_read == -1) { --- 443,449 ---- /* FIXME: check lseek return value */ lseek (fd, pos, SEEK_SET); } ! while ((bytes_read = SAFE_READ (fd, buffer, BUFSIZ)) > 0); if (bytes_read == -1) { *************** *** 442,448 **** tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); /* Input is always read into a fresh buffer. */ ! while ((tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0) { tmp->nlines = 0; tmp->next = NULL; --- 480,486 ---- tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER)); /* Input is always read into a fresh buffer. */ ! while ((tmp->nbytes = SAFE_READ (fd, tmp->buffer, BUFSIZ)) > 0) { tmp->nlines = 0; tmp->next = NULL; *************** *** 561,567 **** tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); /* Input is always read into a fresh buffer. */ ! while ((tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0) { tmp->next = NULL; --- 599,605 ---- tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER)); /* Input is always read into a fresh buffer. */ ! while ((tmp->nbytes = SAFE_READ (fd, tmp->buffer, BUFSIZ)) > 0) { tmp->next = NULL; *************** *** 640,646 **** char buffer[BUFSIZ]; int bytes_read = 0; ! while (n_bytes > 0 && (bytes_read = safe_read (fd, buffer, BUFSIZ)) > 0) n_bytes -= bytes_read; if (bytes_read == -1) { --- 678,684 ---- char buffer[BUFSIZ]; int bytes_read = 0; ! while (n_bytes > 0 && (bytes_read = SAFE_READ (fd, buffer, BUFSIZ)) > 0) n_bytes -= bytes_read; if (bytes_read == -1) { *************** *** 663,669 **** int bytes_read = 0; int bytes_to_skip = 0; ! while (n_lines && (bytes_read = safe_read (fd, buffer, BUFSIZ)) > 0) { bytes_to_skip = 0; while (bytes_to_skip < bytes_read) --- 701,707 ---- int bytes_read = 0; int bytes_to_skip = 0; ! while (n_lines && (bytes_read = SAFE_READ (fd, buffer, BUFSIZ)) > 0) { bytes_to_skip = 0; while (bytes_to_skip < bytes_read) *************** *** 916,922 **** /* If none of the files changed size, sleep. */ if (!any_changed) { ! if (writer_is_dead) break; sleep (sleep_interval); --- 954,960 ---- /* If none of the files changed size, sleep. */ if (!any_changed) { ! if (writer_is_dead || signal_name != NULL) break; sleep (sleep_interval); *************** *** 1311,1317 **** count_lines = 1; forever = from_start = print_headers = 0; ! while ((c = getopt_long (argc, argv, "c:n:f::qs:v", long_options, NULL)) != -1) { switch (c) --- 1349,1355 ---- count_lines = 1; forever = from_start = print_headers = 0; ! while ((c = getopt_long (argc, argv, "c:n:f:::qs:v", long_options, NULL)) != -1) { switch (c) *************** *** 1398,1403 **** --- 1436,1462 ---- } break; + case CHAR_MAX + 5: + signal(SIGABRT,signal_handler); + signal(SIGALRM,signal_handler); + signal(SIGHUP,signal_handler); + signal(SIGINT,signal_handler); + signal(SIGPIPE,signal_handler); + signal(SIGQUIT,signal_handler); + signal(SIGTERM,signal_handler); + signal(SIGUSR1,signal_handler); + signal(SIGUSR2,signal_handler); + #ifdef _XOPEN_UNIX + signal(SIGPOLL,signal_handler); + signal(SIGPROF,signal_handler); + #ifdef SIGVTALARM + signal(SIGVTALARM,signal_handler); + #endif + signal(SIGXCPU,signal_handler); + signal(SIGXFSZ,signal_handler); + #endif + break; + case 'q': *header_mode = never; break; *************** *** 1512,1517 **** --- 1571,1578 ---- tail_forever (F, n_files); } + if (signal_name != NULL) + printf("==> tail terminated by signal %s <==\n",signal_name); if (have_read_stdin && close (0) < 0) error (EXIT_FAILURE, errno, "-"); if (fclose (stdout) == EOF)