coreutils
[Top][All Lists]
Advanced

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

Re: [PATCH v3] tee: handle EAGAIN returned from fwrite/fclose()


From: Kamil Dudka
Subject: Re: [PATCH v3] tee: handle EAGAIN returned from fwrite/fclose()
Date: Thu, 01 Nov 2018 15:39:10 +0100

On Tuesday, September 11, 2018 3:05:20 PM CET Kamil Dudka wrote:
> 'tee' expected the output file descriptors to operate in blocking mode
> but this assumption might be invalidated by other programs connected to
> the same terminal, as in the following example:
> 
> $ telnet ... | tee log_file
> 
> telnet calls ioctl(stdin, FIONBIO, [1]), which causes the O_NONBLOCK
> flag to be set on tee's output, which is connected to the same terminal.
> 
> This patch has zero impact unless EAGAIN returns from fwrite/fclose().
> In that case 'tee' waits for the underlying file descriptor to become
> writable again and then it writes the remaining data.

Are there still any concerns about the last version of this patch?

Is there anything I can do to move this forward?

Thanks in advance!

Kamil

> ---
>  src/tee.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 73 insertions(+), 2 deletions(-)
> 
> diff --git a/src/tee.c b/src/tee.c
> index dae0f1e50..264560e81 100644
> --- a/src/tee.c
> +++ b/src/tee.c
> @@ -17,6 +17,8 @@
>  /* Mike Parker, Richard M. Stallman, and David MacKenzie */
> 
>  #include <config.h>
> +#include <assert.h>
> +#include <poll.h>
>  #include <sys/types.h>
>  #include <signal.h>
>  #include <getopt.h>
> @@ -176,6 +178,75 @@ main (int argc, char **argv)
>    return ok ? EXIT_SUCCESS : EXIT_FAILURE;
>  }
> 
> +/* On EAGAIN, wait for the underlying file descriptor to become writable.
> +   Return true, if EAGAIN has been successfully handled. */
> +static bool
> +handle_eagain_on_write (FILE *f)
> +{
> +  struct pollfd pfd;
> +  if (errno != EAGAIN)
> +    /* non-recoverable write error */
> +    return false;
> +
> +  /* obtain the file descriptor we are writing to */
> +  pfd.fd = fileno (f);
> +  if (pfd.fd == -1)
> +    goto fail;
> +
> +  /* wait for the file descriptor to become writable */
> +  pfd.events = POLLOUT;
> +  pfd.revents = 0;
> +  if (poll (&pfd, 1, -1) != 1)
> +    goto fail;
> +  if (!(pfd.revents & POLLOUT))
> +    goto fail;
> +
> +  /* successfully waited for the descriptor to become writable */
> +  clearerr(f);
> +  return true;
> +
> +fail:
> +  errno = EAGAIN;
> +  return false;
> +}
> +
> +/* wrapper of fwrite() that handles EAGAIN properly in case the O_NONBLOCK
> +   flag was set on the output file descriptor */
> +static bool
> +write_buf (const char *buf, ssize_t size, FILE *f)
> +{
> +  for (;;)
> +    {
> +      const size_t written = fwrite (buf, 1, size, f);
> +      size -= written;
> +      assert (size >= 0);
> +      if (size <= 0)
> +        /* everything written */
> +        return true;
> +
> +      if (!handle_eagain_on_write (f))
> +        return false;
> +
> +      /* update position in the write buffer */
> +      buf += written;
> +    }
> +}
> +
> +/* wrapper of fclose() that handles EAGAIN properly in case the O_NONBLOCK
> +   flag was set on the output file descriptor */
> +static bool
> +close_file (FILE *f)
> +{
> +  for (;;)
> +    {
> +      if (fclose (f) == 0)
> +        return true;
> +
> +      if (!handle_eagain_on_write (f))
> +        return false;
> +    }
> +}
> +
>  /* Copy the standard input into each of the NFILES files in FILES
>     and into the standard output.  As a side effect, modify FILES[-1].
>     Return true if successful.  */
> @@ -238,7 +309,7 @@ tee_files (int nfiles, char **files)
>           Standard output is the first one.  */
>        for (i = 0; i <= nfiles; i++)
>          if (descriptors[i]
> -            && fwrite (buffer, bytes_read, 1, descriptors[i]) != 1)
> +            && !write_buf (buffer, bytes_read, descriptors[i]))
>            {
>              int w_errno = errno;
>              bool fail = errno != EPIPE || (output_error ==
> output_error_exit @@ -266,7 +337,7 @@ tee_files (int nfiles, char **files)
> 
>    /* Close the files, but not standard output.  */
>    for (i = 1; i <= nfiles; i++)
> -    if (descriptors[i] && fclose (descriptors[i]) != 0)
> +    if (descriptors[i] && !close_file (descriptors[i]))
>        {
>          error (0, errno, "%s", quotef (files[i]));
>          ok = false;





reply via email to

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