tech-userlevel archive

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

Re: stdio buffering extension



On 14 July 2015 at 21:21, Christos Zoulas <christos%zoulas.com@localhost> wrote:
>
> Hi,
>
> After discussing importing stdbuf(1) from FreeBSD with various
> people, they objected about it using LD_PRELOAD and suggested to
> use the environment directly to alter the default buffering policy.
>
> Here's an implementation of that...
>
> Suggestions/Comments?
>
> christos
>
> Index: fopen.3
> ===================================================================
> RCS file: /cvsroot/src/lib/libc/stdio/fopen.3,v
> retrieving revision 1.30
> diff -u -u -r1.30 fopen.3
> --- fopen.3     11 Feb 2015 15:19:05 -0000      1.30
> +++ fopen.3     14 Jul 2015 20:16:45 -0000
> @@ -196,6 +196,13 @@
>  .Em stdin ,
>  or
>  .Em stdout ) .
> +.Pp
> +Input and output against the opened stream will be fully buffered, unless
> +it refers to an interactive terminal device, or a different kind of buffering
> +is specified in the environment.
> +See
> +.Xr setvbuf 3
> +for additional details.
>  .Sh RETURN VALUES
>  Upon successful completion
>  .Fn fopen ,
> Index: makebuf.c
> ===================================================================
> RCS file: /cvsroot/src/lib/libc/stdio/makebuf.c,v
> retrieving revision 1.17
> diff -u -u -r1.17 makebuf.c
> --- makebuf.c   15 Mar 2012 18:22:30 -0000      1.17
> +++ makebuf.c   14 Jul 2015 20:16:45 -0000
> @@ -49,10 +49,64 @@
>  #include <stdio.h>
>  #include <stdlib.h>
>  #include <unistd.h>
> +#include <inttypes.h>
> +#include <ctype.h>
>  #include "reentrant.h"
>  #include "local.h"
>
>  /*
> + * Override the file buffering based on the environment setting STDBUF%d
> + * (for the specific file descriptor) and STDBUF (for all descriptors).
> + * the setting is ULB<num> standing for "Unbuffered", "Linebuffered",
> + * and Fullybuffered", and <num> is a value from 0 to 1M
> + */
> +static int
> +__senvbuf(FILE *fp, size_t *size, int *couldbetty)
> +{
> +       char evb[64], *evp;
> +       int flags, e;
> +       intmax_t s;
> +
> +       flags = 0;
> +       if (snprintf(evb, sizeof(evb), "STDBUF%d", fp->_file) < 0)
> +               return flags;
> +
> +       if ((evp = getenv(evb)) == NULL && (evp = getenv("STDBUF")) == NULL)
> +               return flags;
> +
> +       switch (*evp) {
> +       case 'u':
> +       case 'U':
> +               evp++;
> +               flags |= __SNBF;
> +               break;
> +       case 'l':
> +       case 'L':
> +               evp++;
> +               flags |= __SLBF;
> +               break;
> +       case 'f':
> +       case 'F':
> +               evp++;
> +               *couldbetty = 0;
> +               break;
> +       }
> +
> +       if (!isdigit((unsigned char)*evp))
> +               return flags;
> +
> +       s = strtoi(evp, NULL, 0, 0, 1024 * 1024, &e);
> +       if (e != 0)
> +               return flags;
> +
> +       *size = (size_t)s;
> +       if (*size == 0)
> +               return __SNBF;
> +
> +       return flags;
> +}
> +
> +/*
>   * Allocate a file buffer, or switch to unbuffered I/O.
>   * Per the ANSI C standard, ALL tty devices default to line buffered.
>   *
> @@ -69,18 +123,21 @@
>
>         _DIAGASSERT(fp != NULL);
>
> -       if (fp->_flags & __SNBF) {
> -               fp->_bf._base = fp->_p = fp->_nbuf;
> -               fp->_bf._size = 1;
> -               return;
> -       }
> +       if (fp->_flags & __SNBF)
> +               goto unbuf;
> +
>         flags = __swhatbuf(fp, &size, &couldbetty);
> -       if ((p = malloc(size)) == NULL) {
> -               fp->_flags |= __SNBF;
> -               fp->_bf._base = fp->_p = fp->_nbuf;
> -               fp->_bf._size = 1;
> -               return;
> +
> +       if ((fp->_flags & (__SLBF|__SNBF|__SMBF)) == 0
> +           && fp->_cookie == fp && fp->_file >= 0) {
> +               flags |= __senvbuf(fp, &size, &couldbetty);
> +               if (flags & __SNBF)
> +                       goto unbuf;
>         }
> +
> +       if ((p = malloc(size)) == NULL)
> +               goto unbuf;
> +
>         __cleanup = _cleanup;
>         flags |= __SMBF;
>         fp->_bf._base = fp->_p = p;
> @@ -89,6 +146,11 @@
>         if (couldbetty && isatty(__sfileno(fp)))
>                 flags |= __SLBF;
>         fp->_flags |= flags;
> +       return;
> +unbuf:
> +       fp->_flags |= __SNBF;
> +       fp->_bf._base = fp->_p = fp->_nbuf;
> +       fp->_bf._size = 1;
>  }
>
>  /*
> Index: setbuf.3
> ===================================================================
> RCS file: /cvsroot/src/lib/libc/stdio/setbuf.3,v
> retrieving revision 1.13
> diff -u -u -r1.13 setbuf.3
> --- setbuf.3    7 Aug 2003 16:43:31 -0000       1.13
> +++ setbuf.3    14 Jul 2015 20:16:45 -0000
> @@ -63,6 +63,27 @@
>  when it is line buffered characters are saved up until a newline is
>  output or input is read from any stream attached to a terminal device
>  (typically stdin).
> +.Pp
> +The default buffer settings can be overwritten per descriptor
> +.Dv ( STDBUFn )
> +where
> +.Dv n
> +is the numeric value of the file descriptor represented by the stream, or
> +for all descriptors
> +.Dv ( STDBUF ) .
> +The environment variable value is a letter followed by an optional numeric
> +value indicating the size of the buffer.
> +Valid sizes range from 0B to 1MB.
> +Valid letters are:
> +.Bl -tag -width X -indent
> +.It Dv Li U
> +Unbuffered.
> +.It Dv Li L
> +Line-buffered.
> +.It Dv Li F
> +Fully-buffered.
> +.El
> +.Pp
>  The function
>  .Xr fflush 3
>  may be used to force the block out early.

I'd love to see this in (and used in rc :)

Some possible thoughts:

Do we want to disallow this for setuid programs (probably not an
issue, but just in case). Would the 1M limit be good to take from a
sysctl value to allow tuning safe limits (thinking of a resource DOS
on a small system, though someone needs to be able to set arbitrary
environment variables anyway)


Home | Main Index | Thread Index | Old Index