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)

Reply via email to