bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Improved tail patch


From: Paul Eggert
Subject: Re: Improved tail patch
Date: Fri, 23 Jul 2004 16:14:44 -0700
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

I installed the following patch to fix the rest of the problem that
you noted with "tail -f" and fifos.  It turns out that using "select"
helps only in the case where there are two or more input files, at
least one of which is a fifo.  Optimizing this case would complicate
the code (and it's already too complicated, as you may be able to tell
by reading the patch :-) so I left out all uses of 'select' for now.

2004-07-23  Paul Eggert  <address@hidden>

        Fix bug with "tail -f" reported by Rob Holland in
        <http://lists.gnu.org/archive/html/bug-coreutils/2004-07/msg00054.html>.
        Also, remove the undocumented and unsupported-since-2000
        --max-consecutive-size-changes options.  Fix another related bug:
        "tail" got confused if stdin, stdout, or stderr were closed.
        Also, use output buffering even with "tail -f".
        
        * NEWS: Document this, plus yesterday's patch.
        * doc/coreutils.texi (tail invocation): "size has remained the same"
        -> "file has not changed", which is more accurate for fifos.
        * lib/Makefile.am (libfetish_a_SOURCES): Add fcntl-safer.h,
        open-safer.c.
        * src/tail.c: Include fcntl-safer.h.
        (COPY_TO_EOF): Set to UINTMAX_MAX, not OFF_T_MAX (which was wrong).
        (COPY_A_BUFFER): New macro.
        (struct File_spec): New members mtime, mode, blocking.
        Remove member n_consecutive_size_changes.
        (DEFAULT_MAX_N_CONSECUTIVE_SIZE_CHANGES,
        max_n_consecutive_size_changes_between_opens,
        MAX_CONSECUTIVE_SIZE_CHANGES_OPTION): Remove.
        (long_options, tail_forever, parse_options):
        Remove (non-)support for --max-consecutive-size-changes.
        (record_open_fd): New function.
        (recheck, tail_file): Use it.  Don't assume that stdin is open.
        (dump_remainder): Add support for new COPY_A_BUFFER special value.
        Treat errno==EAGAIN like EOF, since it might be a nonblocking read.
        (recheck): New arg BLOCKING, specifying whether to use blocking reads.
        All uses changed.
        (n_live_files): Remove, replacing with...
        (any_live_files): New function.  All uses changed.
        (tail_forever): Use nonblocking I/O unless we know that blocking I/O
        is safe; this avoids some hangs when reading from a fifo.
        Avoid invoking fstat or sleep when using blocking I/O.
        Do not check for changes to size if the file is not a regular file,
        as the size is undefined in that case.
        Check for changes to mtime or mode, too; this works for non-regular
        files.
        (tail_forever, main): Redo fflush strategy to work even when input
        is nonblocking.  Don't use unbuffered output; just flush when needed.
        * lib/fcntl-safer.h, open-safer.c: New files.

        * m4/prereq.m4 (gl_PREREQ): Add gl_FCNTL_SAFER.
        * m4/fcntl-safer.m4: New file.

Index: NEWS
===================================================================
RCS file: /home/eggert/coreutils/cu/NEWS,v
retrieving revision 1.224
retrieving revision 1.225
diff -p -u -r1.224 -r1.225
--- NEWS        8 Jul 2004 14:03:45 -0000       1.224
+++ NEWS        23 Jul 2004 22:11:15 -0000      1.225
@@ -38,6 +38,9 @@ GNU coreutils NEWS                      
 
   rm no longer requires read access to the current directory.
 
+  tail -f no longer mishandles pipes and fifos.  With no operands,
+  tail now ignores -f if standard input is a pipe, as POSIX requires.
+
   For some types of errors (e.g., read-only file system, I/O error)
   when first encountering a directory, `rm -r' would mistakenly fail
   to remove files under that directory.
@@ -156,6 +159,10 @@ GNU coreutils NEWS                      
 
   The stat option --filesystem has been renamed to --file-system, for
   consistency with POSIX "file system" and with cp and du --one-file-system.
+
+** Removed features
+
+  tail's undocumented --max-consecutive-size-changes option has been removed.
 
 * Major changes in release 5.2.1 (2004-03-12) [stable]
 
Index: doc/coreutils.texi
===================================================================
RCS file: /home/eggert/coreutils/cu/doc/coreutils.texi,v
retrieving revision 1.196
retrieving revision 1.197
diff -p -u -r1.196 -r1.197
--- doc/coreutils.texi  22 Jul 2004 20:54:33 -0000      1.196
+++ doc/coreutils.texi  23 Jul 2004 22:11:49 -0000      1.197
@@ -2509,7 +2509,7 @@ will print a warning if this is the case
 @opindex --max-unchanged-stats
 When tailing a file by name, if there have been @var{n} (default
 address@hidden) consecutive
-iterations for which the size has remained the same, then
+iterations for which the file has not changed, then
 @code{open}/@code{fstat} the file to determine if that file name is
 still associated with the same device/inode-number pair as before.
 When following a log file that is rotated, this is approximately the
Index: lib/Makefile.am
===================================================================
RCS file: /home/eggert/coreutils/cu/lib/Makefile.am,v
retrieving revision 1.189
retrieving revision 1.190
diff -p -u -r1.189 -r1.190
--- lib/Makefile.am     12 Jul 2004 06:29:59 -0000      1.189
+++ lib/Makefile.am     23 Jul 2004 22:33:23 -0000      1.190
@@ -57,6 +57,7 @@ libfetish_a_SOURCES = \
   exclude.c exclude.h \
   exit.h \
   exitfail.c exitfail.h \
+  fcntl-safer.h \
   filemode.c filemode.h \
   file-type.c file-type.h \
   fopen-safer.c \
@@ -87,6 +88,7 @@ libfetish_a_SOURCES = \
   modechange.c modechange.h \
   mountlist.h \
   offtostr.c \
+  open-safer.c \
   path-concat.c path-concat.h \
   pathmax.h \
   physmem.c physmem.h \
Index: m4/prereq.m4
===================================================================
RCS file: /home/eggert/coreutils/cu/m4/prereq.m4,v
retrieving revision 1.91
retrieving revision 1.92
diff -p -u -r1.91 -r1.92
--- m4/prereq.m4        12 Jul 2004 06:36:02 -0000      1.91
+++ m4/prereq.m4        23 Jul 2004 22:35:07 -0000      1.92
@@ -1,4 +1,4 @@
-#serial 41
+#serial 42
 
 dnl We use gl_ for non Autoconf macros.
 m4_pattern_forbid([^gl_[ABCDEFGHIJKLMNOPQRSTUVXYZ]])dnl
@@ -25,6 +25,7 @@ AC_DEFUN([gl_PREREQ],
   AC_REQUIRE([gl_ERROR])
   AC_REQUIRE([gl_EXCLUDE])
   AC_REQUIRE([gl_EXITFAIL])
+  AC_REQUIRE([gl_FCNTL_SAFER])
   AC_REQUIRE([gl_FILEBLOCKS])
   AC_REQUIRE([gl_FILEMODE])
   AC_REQUIRE([gl_FILE_TYPE])
Index: src/tail.c
===================================================================
RCS file: /home/eggert/coreutils/cu/src/tail.c,v
retrieving revision 1.224
retrieving revision 1.225
diff -p -u -r1.224 -r1.225
--- src/tail.c  22 Jul 2004 20:54:04 -0000      1.224
+++ src/tail.c  23 Jul 2004 22:33:51 -0000      1.225
@@ -35,6 +35,7 @@
 #include "argmatch.h"
 #include "c-strtod.h"
 #include "error.h"
+#include "fcntl-safer.h"
 #include "inttostr.h"
 #include "posixver.h"
 #include "quote.h"
@@ -58,8 +59,9 @@
 /* Number of items to tail.  */
 #define DEFAULT_N_LINES 10
 
-/* A special value for dump_remainder's N_BYTES parameter.  */
-#define COPY_TO_EOF OFF_T_MAX
+/* Special values for dump_remainder's N_BYTES parameter.  */
+#define COPY_TO_EOF UINTMAX_MAX
+#define COPY_A_BUFFER (UINTMAX_MAX - 1)
 
 /* FIXME: make Follow_name the default?  */
 #define DEFAULT_FOLLOW_MODE Follow_descriptor
@@ -99,12 +101,15 @@ struct File_spec
   /* File descriptor on which the file is open; -1 if it's not open.  */
   int fd;
 
-  /* The size of the file the last time we checked.  */
+  /* Attributes of the file the last time we checked.  */
   off_t size;
-
-  /* The device and inode of the file the last time we checked.  */
+  struct timespec mtime;
   dev_t dev;
   ino_t ino;
+  mode_t mode;
+
+  /* 1 if O_NONBLOCK is clear, 0 if set, -1 if not known.  */
+  int blocking;
 
   /* The specified name initially referred to a directory or some other
      type for which tail isn't meaningful.  Unlike for a permission problem
@@ -114,9 +119,6 @@ struct File_spec
   /* See description of DEFAULT_MAX_N_... below.  */
   unsigned int n_unchanged_stats;
 
-  /* See description of DEFAULT_MAX_N_... below.  */
-  unsigned int n_consecutive_size_changes;
-
   /* A file is tailable if it exists, is readable, and is of type
      IS_TAILABLE_FILE_TYPE.  */
   int tailable;
@@ -154,7 +156,7 @@ enum header_mode
 };
 
 /* When tailing a file by name, if there have been this many consecutive
-   iterations for which the size has remained the same, then open/fstat
+   iterations for which the file has not changed, then open/fstat
    the file to determine if that file name is still associated with the
    same device/inode-number pair as before.  This option is meaningful only
    when following by name.  --max-unchanged-stats=N  */
@@ -162,16 +164,6 @@ enum header_mode
 static unsigned long max_n_unchanged_stats_between_opens =
   DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS;
 
-/* This variable is used to ensure that a file that is unlinked or moved
-   aside, yet always growing will be recognized as having been renamed.
-   After detecting this many consecutive size changes for a file, open/fstat
-   the file to determine if that file name is still associated with the
-   same device/inode-number pair as before.  This option is meaningful only
-   when following by name.  --max-consecutive-size-changes=N  */
-#define DEFAULT_MAX_N_CONSECUTIVE_SIZE_CHANGES 200
-static unsigned long max_n_consecutive_size_changes_between_opens =
-  DEFAULT_MAX_N_CONSECUTIVE_SIZE_CHANGES;
-
 /* The name this program was run with.  */
 char *program_name;
 
@@ -194,11 +186,6 @@ enum
   RETRY_OPTION = CHAR_MAX + 1,
   ALLOW_MISSING_OPTION,   /* deprecated, FIXME: remove in late 2004 */
   MAX_UNCHANGED_STATS_OPTION,
-
-  /* FIXME: remove this in 2001, unless someone can show a good
-     reason to keep it.  */
-  MAX_CONSECUTIVE_SIZE_CHANGES_OPTION,
-
   PID_OPTION,
   PRESUME_INPUT_PIPE_OPTION,
   LONG_FOLLOW_OPTION
@@ -212,8 +199,6 @@ static struct option const long_options[
   {"follow", optional_argument, NULL, LONG_FOLLOW_OPTION},
   {"lines", required_argument, NULL, 'n'},
   {"max-unchanged-stats", required_argument, NULL, MAX_UNCHANGED_STATS_OPTION},
-  {"max-consecutive-size-changes", required_argument, NULL,
-   MAX_CONSECUTIVE_SIZE_CHANGES_OPTION},
   {"pid", required_argument, NULL, PID_OPTION},
   {"presume-input-pipe", no_argument, NULL,
    PRESUME_INPUT_PIPE_OPTION}, /* do not document */
@@ -327,6 +312,28 @@ xwrite (int fd, char const *buffer, size
     error (EXIT_FAILURE, errno, _("write error"));
 }
 
+/* Record a file F with descriptor FD, size SIZE, status ST, and
+   blocking status BLOCKING.  */
+
+static void
+record_open_fd (struct File_spec *f, int fd,
+               off_t size, struct stat const *st,
+               int blocking)
+{
+  f->fd = fd;
+  f->size = size;
+  f->mtime.tv_sec = st->st_mtime;
+  f->mtime.tv_nsec = TIMESPEC_NS (st->st_mtim);
+  f->dev = st->st_dev;
+  f->ino = st->st_ino;
+  f->mode = st->st_mode;
+  f->blocking = blocking;
+  f->n_unchanged_stats = 0;
+  f->ignore = 0;
+}
+
+/* Close the file with descriptor FD and name FILENAME.  */
+
 static void
 close_fd (int fd, const char *filename)
 {
@@ -347,6 +354,7 @@ write_header (const char *pretty_filenam
 
 /* Read and output N_BYTES of file PRETTY_FILENAME starting at the current
    position in FD.  If N_BYTES is COPY_TO_EOF, then copy until end of file.
+   If N_BYTES is COPY_A_BUFFER, then copy at most one buffer's worth.
    Return the number of bytes read from the file.  */
 
 static uintmax_t
@@ -362,13 +370,22 @@ dump_remainder (const char *pretty_filen
       size_t n = MIN (n_remaining, BUFSIZ);
       size_t bytes_read = safe_read (fd, buffer, n);
       if (bytes_read == SAFE_READ_ERROR)
-       error (EXIT_FAILURE, errno, _("error reading %s"),
-              quote (pretty_filename));
+       {
+         if (errno != EAGAIN)
+           error (EXIT_FAILURE, errno, _("error reading %s"),
+                  quote (pretty_filename));
+         break;
+       }
       if (bytes_read == 0)
        break;
       xwrite (STDOUT_FILENO, buffer, bytes_read);
-      n_remaining -= bytes_read;
       n_written += bytes_read;
+      if (n_bytes != COPY_TO_EOF)
+       {
+         n_remaining -= bytes_read;
+         if (n_remaining == 0 || n_bytes == COPY_A_BUFFER)
+           break;
+       }
     }
 
   return n_written;
@@ -822,21 +839,21 @@ start_lines (const char *pretty_filename
 /* FIXME: describe */
 
 static void
-recheck (struct File_spec *f)
+recheck (struct File_spec *f, bool blocking)
 {
   /* open/fstat the file and announce if dev/ino have changed */
   struct stat new_stats;
-  int fd;
   int fail = 0;
   int is_stdin = (STREQ (f->name, "-"));
   int was_tailable = f->tailable;
   int prev_errnum = f->errnum;
   int new_file;
+  int fd = (is_stdin
+           ? STDIN_FILENO
+           : open_safer (f->name, O_RDONLY | (blocking ? 0 : O_NONBLOCK)));
 
   assert (valid_file_spec (f));
 
-  fd = (is_stdin ? STDIN_FILENO : open (f->name, O_RDONLY));
-
   /* If the open fails because the file doesn't exist,
      then mark the file as not tailable.  */
   f->tailable = !(reopen_inaccessible_files && fd == -1);
@@ -931,32 +948,24 @@ recheck (struct File_spec *f)
 
   if (new_file)
     {
-      /* Record new file info in f.  */
-      f->fd = fd;
-      f->size = 0; /* Start at the beginning of the file...  */
-      f->dev = new_stats.st_dev;
-      f->ino = new_stats.st_ino;
-      f->n_unchanged_stats = 0;
-      f->n_consecutive_size_changes = 0;
-      f->ignore = 0;
-      xlseek (f->fd, f->size, SEEK_SET, pretty_name (f));
+      /* Start at the beginning of the file.  */
+      record_open_fd (f, fd, 0, &new_stats, (is_stdin ? -1 : blocking));
+      xlseek (fd, 0, SEEK_SET, pretty_name (f));
     }
 }
 
-/* FIXME: describe */
+/* Return true if any of the N_FILES files in F are live, i.e., have
+   open file descriptors.  */
 
-static unsigned int
-n_live_files (const struct File_spec *f, int n_files)
+static bool
+any_live_files (const struct File_spec *f, int n_files)
 {
   int i;
-  unsigned int n_live = 0;
 
   for (i = 0; i < n_files; i++)
-    {
-      if (f[i].fd >= 0)
-       ++n_live;
-    }
-  return n_live;
+    if (0 <= f[i].fd)
+      return true;
+  return false;
 }
 
 /* Tail NFILES files forever, or until killed.
@@ -969,6 +978,9 @@ n_live_files (const struct File_spec *f,
 static void
 tail_forever (struct File_spec *f, int nfiles, double sleep_interval)
 {
+  /* Use blocking I/O as an optimization, when it's easy.  */
+  bool blocking = (pid == 0 && follow_mode == Follow_descriptor
+                  && nfiles == 1 && ! S_ISREG (f[0].mode));
   int last;
   int writer_is_dead = 0;
 
@@ -977,90 +989,110 @@ tail_forever (struct File_spec *f, int n
   while (1)
     {
       int i;
-      int any_changed;
+      bool any_input = false;
 
-      any_changed = 0;
       for (i = 0; i < nfiles; i++)
        {
+         int fd;
+         char const *name;
+         mode_t mode;
          struct stat stats;
+         uintmax_t bytes_read;
 
          if (f[i].ignore)
            continue;
 
          if (f[i].fd < 0)
            {
-             recheck (&f[i]);
+             recheck (&f[i], blocking);
              continue;
            }
 
-         if (fstat (f[i].fd, &stats) < 0)
+         fd = f[i].fd;
+         name = pretty_name (&f[i]);
+         mode = f[i].mode;
+
+         if (f[i].blocking != blocking)
            {
-             f[i].fd = -1;
-             f[i].errnum = errno;
-             error (0, errno, "%s", pretty_name (&f[i]));
-             continue;
+             int old_flags = fcntl (fd, F_GETFL);
+             int new_flags = old_flags | (blocking ? 0 : O_NONBLOCK);
+             if (old_flags < 0
+                 || (new_flags != old_flags
+                     && fcntl (fd, F_SETFL, new_flags) == -1))
+               error (EXIT_FAILURE, errno,
+                      _("%s: cannot change nonblocking mode"), name);
+             f[i].blocking = blocking;
            }
 
-         if (stats.st_size == f[i].size)
+         if (!blocking)
            {
-             f[i].n_consecutive_size_changes = 0;
-             if ((max_n_unchanged_stats_between_opens
-                  <= f[i].n_unchanged_stats++)
-                 && follow_mode == Follow_name)
+             if (fstat (fd, &stats) != 0)
                {
-                 recheck (&f[i]);
-                 f[i].n_unchanged_stats = 0;
+                 f[i].fd = -1;
+                 f[i].errnum = errno;
+                 error (0, errno, "%s", name);
+                 continue;
                }
-             continue;
-           }
 
-         /* Ensure that a file that's unlinked or moved aside, yet always
-            growing will be recognized as having been renamed.  */
-         if ((max_n_consecutive_size_changes_between_opens
-              <= f[i].n_consecutive_size_changes++)
-             && follow_mode == Follow_name)
-           {
-             f[i].n_consecutive_size_changes = 0;
-             recheck (&f[i]);
-             continue;
-           }
+             if ((! S_ISREG (stats.st_mode) || f[i].size == stats.st_size)
+                 && f[i].mtime.tv_sec == stats.st_mtime
+                 && f[i].mtime.tv_nsec == TIMESPEC_NS (stats.st_mtim)
+                 && f[i].mode == stats.st_mode)
+               {
+                 if ((max_n_unchanged_stats_between_opens
+                      <= f[i].n_unchanged_stats++)
+                     && follow_mode == Follow_name)
+                   {
+                     recheck (&f[i], blocking);
+                     f[i].n_unchanged_stats = 0;
+                   }
+                 continue;
+               }
 
-         /* This file has changed size.  Print out what we can, and
-            then keep looping.  */
+             /* This file has changed.  Print out what we can, and
+                then keep looping.  */
 
-         any_changed = 1;
+             f[i].mtime.tv_sec = stats.st_mtime;
+             f[i].mtime.tv_nsec = TIMESPEC_NS (stats.st_mtim);
+             f[i].mode = stats.st_mode;
 
-         /* reset counter */
-         f[i].n_unchanged_stats = 0;
+             /* reset counter */
+             f[i].n_unchanged_stats = 0;
 
-         if (stats.st_size < f[i].size)
-           {
-             error (0, 0, _("%s: file truncated"), pretty_name (&f[i]));
-             last = i;
-             xlseek (f[i].fd, (off_t) stats.st_size, SEEK_SET,
-                     pretty_name (&f[i]));
-             f[i].size = stats.st_size;
-             continue;
-           }
+             if (S_ISREG (mode) && stats.st_size < f[i].size)
+               {
+                 error (0, 0, _("%s: file truncated"), name);
+                 last = i;
+                 xlseek (fd, stats.st_size, SEEK_SET, name);
+                 f[i].size = stats.st_size;
+                 continue;
+               }
 
-         if (i != last)
-           {
-             if (print_headers)
-               write_header (pretty_name (&f[i]));
-             last = i;
+             if (i != last)
+               {
+                 if (print_headers)
+                   write_header (name);
+                 last = i;
+               }
            }
-         f[i].size += dump_remainder (pretty_name (&f[i]), f[i].fd,
-                                      COPY_TO_EOF);
+
+         bytes_read = dump_remainder (name, fd,
+                                      blocking ? COPY_A_BUFFER : COPY_TO_EOF);
+         any_input |= (bytes_read != 0);
+         f[i].size += bytes_read;
        }
 
-      if (n_live_files (f, nfiles) == 0 && ! reopen_inaccessible_files)
+      if (! any_live_files (f, nfiles) && ! reopen_inaccessible_files)
        {
          error (0, 0, _("no files remaining"));
          break;
        }
 
-      /* If none of the files changed size, sleep.  */
-      if (!any_changed)
+      if ((!any_input | blocking) && fflush (stdout) != 0)
+       error (EXIT_FAILURE, errno, _("write error"));
+
+      /* If nothing was read, sleep and/or check for dead writers.  */
+      if (!any_input)
        {
          if (writer_is_dead)
            break;
@@ -1189,8 +1221,8 @@ tail_lines (const char *pretty_filename,
         which lseek (... SEEK_END) works.  */
       if ( ! presume_input_pipe
           && S_ISREG (stats.st_mode)
-          && (start_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
-          && start_pos < (end_pos = lseek (fd, (off_t) 0, SEEK_END)))
+          && (start_pos = lseek (fd, 0, SEEK_CUR)) != -1
+          && start_pos < (end_pos = lseek (fd, 0, SEEK_END)))
        {
          *read_pos = end_pos;
          if (end_pos != 0 && file_lines (pretty_filename, fd, n_lines,
@@ -1250,7 +1282,7 @@ tail_file (struct File_spec *f, uintmax_
     }
   else
     {
-      fd = open (f->name, O_RDONLY);
+      fd = open_safer (f->name, O_RDONLY);
     }
 
   f->tailable = !(reopen_inaccessible_files && fd == -1);
@@ -1310,17 +1342,10 @@ tail_file (struct File_spec *f, uintmax_
            }
          else
            {
-             f->fd = fd;
-
              /* Note: we must use read_pos here, not stats.st_size,
                 to avoid a race condition described by Ken Raeburn:
        http://mail.gnu.org/archive/html/bug-textutils/2003-05/msg00007.html */
-             f->size = read_pos;
-             f->dev = stats.st_dev;
-             f->ino = stats.st_ino;
-             f->n_unchanged_stats = 0;
-             f->n_consecutive_size_changes = 0;
-             f->ignore = 0;
+             record_open_fd (f, fd, read_pos, &stats, (is_stdin ? -1 : 1));
            }
        }
       else
@@ -1553,18 +1578,6 @@ parse_options (int argc, char **argv,
            }
          break;
 
-       case MAX_CONSECUTIVE_SIZE_CHANGES_OPTION:
-         /* --max-consecutive-size-changes=N */
-         if (xstrtoul (optarg, NULL, 10,
-                       &max_n_consecutive_size_changes_between_opens, "")
-             != LONGINT_OK)
-           {
-             error (EXIT_FAILURE, 0,
-                  _("%s: invalid maximum number of consecutive size changes"),
-                    optarg);
-           }
-         break;
-
        case PID_OPTION:
          {
            strtol_error s_err;
@@ -1729,14 +1742,7 @@ main (int argc, char **argv)
     exit_status |= tail_file (&F[i], n_units);
 
   if (forever)
-    {
-      /* This fflush appears to be required only on Solaris 5.7.  */
-      if (fflush (stdout) < 0)
-       error (EXIT_FAILURE, errno, _("write error"));
-
-      SETVBUF (stdout, NULL, _IONBF, 0);
-      tail_forever (F, n_files, sleep_interval);
-    }
+    tail_forever (F, n_files, sleep_interval);
 
   if (have_read_stdin && close (STDIN_FILENO) < 0)
     error (EXIT_FAILURE, errno, "-");
--- /dev/null   2003-03-18 13:55:57 -0800
+++ lib/fcntl-safer.h   2004-07-22 21:29:12 -0700
@@ -0,0 +1,21 @@
+/* Invoke fcntl functions, but avoid some glitches.
+
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Written by Paul Eggert.  */
+
+int open_safer (char const *, int, ...);
--- /dev/null   2003-03-18 13:55:57 -0800
+++ lib/open-safer.c    2004-07-23 15:29:43 -0700
@@ -0,0 +1,75 @@
+/* Invoke open, but avoid some glitches.
+
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* Written by Paul Eggert.  */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <fcntl-safer.h>
+
+#include <unistd-safer.h>
+
+#include <errno.h>
+#include <stdarg.h>
+
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifndef STDERR_FILENO
+# define STDERR_FILENO 2
+#endif
+
+/* Like open, but do not return STDIN_FILENO, STDOUT_FILENO, or
+   STDERR_FILENO.  */
+
+int
+open_safer (char const *file, int oflag, ...)
+{
+  int fd;
+  mode_t mode = 0;
+
+  if (oflag & O_CREAT)
+    {
+      va_list args;
+      va_start (args, oflag);
+      if (sizeof (int) <= sizeof (mode_t))
+       mode = va_arg (args, mode_t);
+      else
+       mode = va_arg (args, int);
+      va_end (args);
+    }
+
+  fd = open (file, oflag, mode);
+
+  if (0 <= fd && fd <= STDERR_FILENO)
+    {
+      int f = dup_safer (fd);
+      int e = errno;
+      close (fd);
+      errno = e;
+      fd = f;
+    }
+
+  return fd;
+}
--- /dev/null   2003-03-18 13:55:57 -0800
+++ m4/fcntl-safer.m4   2004-07-23 15:17:50 -0700
@@ -0,0 +1,29 @@
+# fcntl-safer.m4 serial 1
+
+# Copyright (C) 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+# Written by Paul Eggert.
+
+AC_DEFUN([gl_FCNTL_SAFER],
+[
+  gl_PREREQ_OPEN_SAFER
+])
+
+# Prerequisites of lib/open-safer.c.
+AC_DEFUN([gl_PREREQ_OPEN_SAFER], [
+  AC_CHECK_HEADERS_ONCE(fcntl.h unistd.h)
+])




reply via email to

[Prev in Thread] Current Thread [Next in Thread]