⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tall.c

📁 《linux应用开发技术详解》的配套代码
💻 C
📖 第 1 页 / 共 4 页
字号:
      f->errnum = -1;
      error (0, 0, _("`%s' has been replaced with an untailable file;\
 giving up on this name"),
	     pretty_name (f));
      f->ignore = 1;
    }
  else
    {
      f->errnum = 0;
    }

  new_file = 0;
  if (fail)
    {
      close_fd (fd, pretty_name (f));
      close_fd (f->fd, pretty_name (f));
      f->fd = -1;
    }
  else if (prev_errnum && prev_errnum != ENOENT)
    {
      new_file = 1;
      assert (f->fd == -1);
      error (0, 0, _("`%s' has become accessible"), pretty_name (f));
    }
  else if (f->ino != new_stats.st_ino || f->dev != new_stats.st_dev)
    {
      new_file = 1;
      if (f->fd == -1)
	{
	  error (0, 0,
		 _("`%s' has appeared;  following end of new file"),
		 pretty_name (f));
	}
      else
	{
	  /* Close the old one.  */
	  close_fd (f->fd, pretty_name (f));

	  /* File has been replaced (e.g., via log rotation) --
	     tail the new one.  */
	  error (0, 0,
		 _("`%s' has been replaced;  following end of new file"),
		 pretty_name (f));
	}
    }
  else
    {
      if (f->fd == -1)
	{
	  /* This happens when one iteration finds the file missing,
	     then the preceding <dev,inode> pair is reused as the
	     file is recreated.  */
	  new_file = 1;
	}
      else
	{
	  close_fd (fd, pretty_name (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));
    }
}

/* FIXME: describe */

static unsigned int
n_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;
}

/* Tail NFILES files forever, or until killed.
   The pertinent information for each file is stored in an entry of F.
   Loop over each of them, doing an fstat to see if they have changed size,
   and an occasional open/fstat to see if any dev/ino pair has changed.
   If none of them have changed size in one iteration, sleep for a
   while and try again.  Continue until the user interrupts us.  */

static void
tail_forever (struct File_spec *f, int nfiles, double sleep_interval)
{
  int last;
  int writer_is_dead = 0;

  last = nfiles - 1;

  while (1)
    {
      int i;
      int any_changed;

      any_changed = 0;
      for (i = 0; i < nfiles; i++)
	{
	  struct stat stats;

	  if (f[i].ignore)
	    continue;

	  if (f[i].fd < 0)
	    {
	      recheck (&f[i]);
	      continue;
	    }

	  if (fstat (f[i].fd, &stats) < 0)
	    {
	      f[i].fd = -1;
	      f[i].errnum = errno;
	      error (0, errno, "%s", pretty_name (&f[i]));
	      continue;
	    }

	  if (stats.st_size == f[i].size)
	    {
	      f[i].n_consecutive_size_changes = 0;
	      if ((max_n_unchanged_stats_between_opens
		   <= f[i].n_unchanged_stats++)
		  && follow_mode == Follow_name)
		{
		  recheck (&f[i]);
		  f[i].n_unchanged_stats = 0;
		}
	      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;
	    }

	  /* This file has changed size.  Print out what we can, and
	     then keep looping.  */

	  any_changed = 1;

	  /* 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 (i != last)
	    {
	      if (print_headers)
		write_header (pretty_name (&f[i]));
	      last = i;
	    }
	  f[i].size += dump_remainder (pretty_name (&f[i]), f[i].fd,
				       COPY_TO_EOF);
	}

      if (n_live_files (f, nfiles) == 0 && ! reopen_inaccessible_files)
	{
	  error (0, 0, _("no files remaining"));
	  break;
	}

      /* If none of the files changed size, sleep.  */
      if (!any_changed)
	{
	  if (writer_is_dead)
	    break;

	  if (xnanosleep (sleep_interval))
	    error (EXIT_FAILURE, errno, _("cannot read realtime clock"));

	  /* Once the writer is dead, read the files once more to
	     avoid a race condition.  */
	  writer_is_dead = (pid != 0
			    && kill (pid, 0) != 0
			    /* Handle the case in which you cannot send a
			       signal to the writer, so kill fails and sets
			       errno to EPERM.  */
			    && errno != EPERM);
	}
    }
}

/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
  struct stat stats;

  /* We need binary input, since `tail' relies on `lseek' and byte counts,
     while binary output will preserve the style (Unix/DOS) of text file.  */
  SET_BINARY2 (fd, STDOUT_FILENO);

  if (fstat (fd, &stats))
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

  if (from_start)
    {
      if (S_ISREG (stats.st_mode))
	{
	  xlseek (fd, n_bytes, SEEK_CUR, pretty_filename);
	}
      else
	{
	  int t;
	  if ((t = start_bytes (pretty_filename, fd, n_bytes)) < 0)
	    return 0;
	  if (t)
	    return 1;
	}
      dump_remainder (pretty_filename, fd, COPY_TO_EOF);
    }
  else
    {
      if (S_ISREG (stats.st_mode))
	{
	  off_t current_pos, end_pos;
	  off_t bytes_remaining;

	  if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
	      && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
	    {
	      off_t diff;
	      /* Be careful here.  The current position may actually be
		 beyond the end of the file.  */
	      bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
	    }
	  else
	    {
	      error (0, errno, "%s", pretty_filename);
	      return 1;
	    }

	  if (bytes_remaining <= n_bytes)
	    {
	      /* From the current position to end of file, there are no
		 more bytes than have been requested.  So reposition the
		 file pointer to the incoming current position and print
		 everything after that.  */
	      xlseek (fd, current_pos, SEEK_SET, pretty_filename);
	    }
	  else
	    {
	      /* There are more bytes remaining than were requested.
		 Back up.  */
	      xlseek (fd, -n_bytes, SEEK_END, pretty_filename);
	    }
	  dump_remainder (pretty_filename, fd, n_bytes);
	}
      else
	return pipe_bytes (pretty_filename, fd, n_bytes);
    }
  return 0;
}

/* Output the last N_LINES lines of file FILENAME open for reading in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_lines (const char *pretty_filename, int fd, long int n_lines)
{
  struct stat stats;

  /* We need binary input, since `tail' relies on `lseek' and byte counts,
     while binary output will preserve the style (Unix/DOS) of text file.  */
  SET_BINARY2 (fd, STDOUT_FILENO);

  if (fstat (fd, &stats))
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

  if (from_start)
    {
      int t;
      if ((t = start_lines (pretty_filename, fd, n_lines)) < 0)
	return 0;
      if (t)
	return 1;
      dump_remainder (pretty_filename, fd, COPY_TO_EOF);
    }
  else
    {
      off_t length;
      off_t start_pos;

      /* Use file_lines only if FD refers to a regular file for
	 which lseek (... SEEK_END) works.  */
      if (S_ISREG (stats.st_mode)
	  && (start_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
	  && start_pos < (length = lseek (fd, (off_t) 0, SEEK_END)))
	{
	  if (length != 0 && file_lines (pretty_filename, fd, n_lines,
					 start_pos, length))
	    return 1;
	}
      else
	return pipe_lines (pretty_filename, fd, n_lines);
    }
  return 0;
}

/* Display the last N_UNITS units of file FILENAME, open for reading
   in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail (const char *pretty_filename, int fd, off_t n_units)
{
  if (count_lines)
    return tail_lines (pretty_filename, fd, (long) n_units);
  else
    return tail_bytes (pretty_filename, fd, n_units);
}

/* Display the last N_UNITS units of the file described by F.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_file (struct File_spec *f, off_t n_units)
{
  int fd, errors;

  int is_stdin = (STREQ (f->name, "-"));

  if (is_stdin)
    {
      have_read_stdin = 1;
      fd = STDIN_FILENO;
    }
  else
    {
      fd = open (f->name, O_RDONLY);
    }

  f->tailable = !(reopen_inaccessible_files && fd == -1);

  if (fd == -1)
    {
      if (forever)
	{
	  f->fd = -1;
	  f->errnum = errno;
	  f->ignore = 0;
	  f->ino = 0;
	  f->dev = 0;
	}
      error (0, errno, "%s", pretty_name (f));
      errors = 1;
    }
  else
    {
      if (print_headers)
	write_header (pretty_name (f));
      errors = tail (pretty_name (f), fd, n_units);
      if (forever)
	{
	  struct stat stats;

	  f->errnum = 0;
	  if (fstat (fd, &stats) < 0)
	    {
	      errors = 1;
	      f->errnum = errno;
	      error (0, errno, "%s", pretty_name (f));
	    }
	  else if (!IS_TAILABLE_FILE_TYPE (stats.st_mode))
	    {
	      error (0, 0, _("%s: cannot follow end of this type of file;\
 giving up on this name"),
		     pretty_name (f));
	      errors = 1;
	      f->errnum = -1;
	      f->ignore = 1;
	    }

	  if (errors)
	    {
	      close_fd (fd, pretty_name (f));
	      f->fd = -1;
	    }
	  else
	    {
	      f->fd = fd;
	      f->size = stats.st_size;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -