bin/166660: [patch] New util/shlib to change per-fd default stdio
buffering mode
Jeremie Le Hen
jeremie at le-hen.org
Thu Apr 5 07:30:02 UTC 2012
>Number: 166660
>Category: bin
>Synopsis: [patch] New util/shlib to change per-fd default stdio buffering mode
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: change-request
>Submitter-Id: current-users
>Arrival-Date: Thu Apr 05 07:30:02 UTC 2012
>Closed-Date:
>Last-Modified:
>Originator: Jeremie Le Hen
>Release: FreeBSD 9.0-STABLE
>Organization:
>Environment:
System: FreeBSD 9.0-STABLE
>Description:
When stdin and stdout are not connected to a terminal, the stdio
subsystem switches automatically stdout buffering mode from
line-buffered to fully-buffered.
This is a problem when trying to watch the real-time output of a
command such as vmstat(8) or iostat(8) through multiple filters.
For instance: iostat -x 1 | cat -n | grep -v "extended"
In this case, cat(1)'s stdout is fully buffered and iostat(8)
produces output too slowly, so you have to wait multiple seconds
before the buffer is eventually flushed as a block to the last
command.
>How-To-Repeat:
Just run the command above. On my system with 4 drives, I have
to wait more than 20 seconds to see something.
>Fix:
The patch below adds a new shared library "libstdbuf.so" which,
when LD_PRELOAD'ed, can be configured through environment
variables to override the default buffering mode of either
stdin, stdout or stderr.
In order to avoid the manual setting of LD_PRELOAD and configuration
variables, an utility is provided: stdbuf(1). It is named after a
similar functionality found on Linux. Of course, given the
command-line interface is sane enough to comply with BSD standards,
I kept it fully compatible.
Here is an example of how the above problem can be solved with this
new utility:
iostat -x 1 | stdbuf -o L cat -n | grep -v "extended"
--- stdbuf.diff begins here ---
diff -urNp src.HEAD_20111506/lib/libc/stdio/setbuf.3 src/lib/libc/stdio/setbuf.3
--- src.HEAD_20111506/lib/libc/stdio/setbuf.3 2007-01-09 01:28:07.000000000 +0100
+++ src/lib/libc/stdio/setbuf.3 2011-08-04 19:00:49.000000000 +0200
@@ -83,6 +83,9 @@ normally does) it is line buffered.
The standard error stream
.Dv stderr
is always unbuffered.
+Note that these defaults maybe be altered using the
+.Xr stdbuf 1
+utility.
.Pp
The
.Fn setvbuf
@@ -177,6 +180,7 @@ function returns what the equivalent
.Fn setvbuf
would have returned.
.Sh SEE ALSO
+.Xr stdbuf 1 ,
.Xr fclose 3 ,
.Xr fopen 3 ,
.Xr fread 3 ,
diff -urNp src.HEAD_20111506/lib/libstdbuf/Makefile src/lib/libstdbuf/Makefile
--- src.HEAD_20111506/lib/libstdbuf/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ src/lib/libstdbuf/Makefile 2011-08-04 18:49:59.000000000 +0200
@@ -0,0 +1,15 @@
+# $FreeBSD: src/lib/libftpio/Makefile,v 1.17.2.1 2009/08/03 08:13:06 kensmith Exp $
+
+.include <bsd.own.mk>
+
+LIB= stdbuf
+SRCS= stdbuf.c
+SHLIB_MAJOR= 1
+MAN= libstdbuf.3
+
+CFLAGS+= -I${.CURDIR}
+LDADD= -lutil
+
+WARNS?= 6
+
+.include <bsd.lib.mk>
diff -urNp src.HEAD_20111506/lib/libstdbuf/libstdbuf.3 src/lib/libstdbuf/libstdbuf.3
--- src.HEAD_20111506/lib/libstdbuf/libstdbuf.3 1970-01-01 01:00:00.000000000 +0100
+++ src/lib/libstdbuf/libstdbuf.3 2011-08-04 18:47:04.000000000 +0200
@@ -0,0 +1,111 @@
+.\" Copyright (c) 2011 Jeremie Le Hen
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 4, 2011
+.Dt LIBSTDBUF 3
+.Os
+.Sh NAME
+.Nm libstdbuf
+.Nd preloaded library to change standard streams initial buffering
+.Sh DESCRIPTION
+The
+.Nm
+library is meant to be preloaded with the
+.Ev LD_PRELOAD
+environment variable to as to change the initial buffering
+of standard input, standard output and standard error streams.
+.Pp
+Although you may load and configure this library manually,
+an utility,
+.Xr stdbuf 1 ,
+can be used to run a command with the appropriate environment variables.
+.Sh ENVIRONMENT
+Each stream can be configured indepentently through the following
+environment variables (values are defined below):
+.Bl -tag -width size -offset indent
+.It Ev STDBUF_0
+Initial buffering definition for the standard input stream
+.It Ev STDBUF_1
+Initial buffering definition for the standard output stream
+.It Ev STDBUF_2
+Initial buffering definition for the standard error stream
+.It Ev _STDBUF_I
+GNU-compatible variable for
+.Ev STDBUF_0
+.It Ev _STDBUF_O
+GNU-compatible variable equivalent to
+.Ev STDBUF_1
+.It Ev _STDBUF_E
+GNU-compatible variable equivalent to
+.Ev STDBUF_2
+.El
+.Pp
+Each variable may take one of the following values:
+.Bl -tag -width size -offset indent
+.It Qq 0
+unbuffered
+.It Qq L
+line buffered
+.It Qq B
+fully buffered with the default buffer size
+.It Ar size
+fully buffered with a buffer of
+.Ar size
+bytes
+.El
+.Sh EXAMPLE
+In the following example, the stdout stream of the
+.Xr awk 1
+command
+will be fully buffered by default because it does not refer
+to a terminal.
+.Nm
+is used to force it to be line-buffered so
+.Xr vmstat 8 Ns 's
+output will not stall until the full buffer fills.
+.Bd -literal -offset indent
+# vmstat 1 | LD_PRELOAD=/usr/lib/libstdbuf.so \\
+ STDBUF_1=L awk '$2 > 1 || $3 > 1' | cat -n
+.Ed
+.Pp
+See also the manpage of
+.Xr stdbuf 1
+for a simpler way to do this.
+.Sh HISTORY
+The
+.Nm
+library first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+.An -nosplit
+The original idea of the
+.Nm
+command comes from
+.An Padraig Brady
+who implemented it in the GNU coreutils.
+.An Jeremie Le Hen
+implemented it on
+.Fx .
diff -urNp src.HEAD_20111506/lib/libstdbuf/stdbuf.c src/lib/libstdbuf/stdbuf.c
--- src.HEAD_20111506/lib/libstdbuf/stdbuf.c 1970-01-01 01:00:00.000000000 +0100
+++ src/lib/libstdbuf/stdbuf.c 2011-08-04 18:17:07.000000000 +0200
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 2011 Jeremie Le Hen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libutil.h>
+
+static const char *
+stream_name(FILE *s)
+{
+ if (s == stdin)
+ return "stdin";
+ if (s == stdout)
+ return "stdout";
+ if (s == stderr)
+ return "stderr";
+ /* This should not happen. */
+ abort();
+}
+
+static void
+change_buf(FILE *s, const char *bufmode)
+{
+ size_t bufsiz;
+ uint64_t sizearg;
+ int mode;
+
+ bufsiz = 0;
+ if (bufmode[0] == '0' && bufmode[1] == '\0')
+ mode = _IONBF;
+ else if (bufmode[0] == 'L' && bufmode[1] == '\0')
+ mode = _IOLBF;
+ else if (bufmode[0] == 'B' && bufmode[1] == '\0') {
+ mode = _IOFBF;
+ bufsiz = 0;
+ } else {
+ errno = 0;
+ if (expand_number(bufmode, &sizearg) == -1) {
+ warn("Wrong buffer mode '%s' for %s", bufmode,
+ stream_name(s));
+ return;
+ }
+ if (sizearg > SIZE_T_MAX) {
+ warn("Buffer size too big for %s", stream_name(s));
+ return;
+ }
+ mode = _IOFBF;
+ bufsiz = (size_t)sizearg;
+ }
+ if (setvbuf(s, NULL, mode, bufsiz) != 0)
+ warn("Cannot set buffer mode '%s' for %s", bufmode,
+ stream_name(s));
+}
+
+__attribute__ ((constructor)) static void
+stdbuf(void)
+{
+ char *i_mode, *o_mode, *e_mode;
+
+ i_mode = getenv("STDBUF_0");
+ if (i_mode == NULL)
+ i_mode = getenv("_STDBUF_I");
+ o_mode = getenv("STDBUF_1");
+ if (o_mode == NULL)
+ o_mode = getenv("_STDBUF_O");
+ e_mode = getenv("STDBUF_2");
+ if (e_mode == NULL)
+ e_mode = getenv("_STDBUF_E");
+
+ if (e_mode)
+ change_buf(stderr, e_mode);
+ if (i_mode)
+ change_buf(stdin, i_mode);
+ if (o_mode)
+ change_buf(stdout, o_mode);
+}
diff -urNp src.HEAD_20111506/usr.bin/stdbuf/Makefile src/usr.bin/stdbuf/Makefile
--- src.HEAD_20111506/usr.bin/stdbuf/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ src/usr.bin/stdbuf/Makefile 2011-08-02 19:16:30.000000000 +0200
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= stdbuf
+SRCS= stdbuf.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
Files src.HEAD_20111506/usr.bin/stdbuf/sh.core and src/usr.bin/stdbuf/sh.core differ
diff -urNp src.HEAD_20111506/usr.bin/stdbuf/stdbuf.1 src/usr.bin/stdbuf/stdbuf.1
--- src.HEAD_20111506/usr.bin/stdbuf/stdbuf.1 1970-01-01 01:00:00.000000000 +0100
+++ src/usr.bin/stdbuf/stdbuf.1 2011-08-04 18:46:35.000000000 +0200
@@ -0,0 +1,124 @@
+.\" Copyright (C) 2011 Jeremie Le Hen
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code and documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed or owned by Caldera
+.\" International, Inc.
+.\" 4. Neither the name of Caldera International, Inc. nor the names of other
+.\" contributors may be used to endorse or promote products derived from
+.\" this software without specific prior written permission.
+.\"
+.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
+.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/usr.bin/dc/dc.1,v 1.2 2010/01/22 23:50:46 delphij Exp $
+.\"
+.Dd August 4, 2011
+.Dt STDBUF 1
+.Os
+.Sh NAME
+.Nm stdbuf
+.Nd change standard streams initial buffering
+.Sh SYNOPSIS
+.Nm
+.Op Fl e Ar bufdef
+.Op Fl i Ar bufdef
+.Op Fl o Ar bufdef
+.Op Ar command Op ...
+.Sh DESCRIPTION
+.Nm
+is used to change the initial buffering of standard input,
+standard output and/or standard error streams for
+.Ar command .
+It relies on
+.Xr libstdbuf 3
+which is loaded and configured by
+.Nm
+through environment variables.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl e Ar bufdef
+Set initial buffering of the standard error stream for
+.Ar command
+as defined by
+.Ar bufdef
+.Pq see Sx BUFFER DEFINITION .
+.It Fl i Ar bufdef
+Set initial buffering of the standard input stream for
+.Ar command
+as defined by
+.Ar bufdef
+.Pq see Sx BUFFER DEFINITION .
+.It Fl o Ar bufdef
+Set initial buffering of the standard output stream for
+.Ar command
+as defined by
+.Ar bufdef
+.Pq see Sx BUFFER DEFINITION .
+.El
+.Sh BUFFER DEFINITION
+Buffer definition is the same as in
+.Xr libstdbuf 3 :
+.Bl -tag -width size -offset indent
+.It Qq 0
+unbuffered
+.It Qq L
+line buffered
+.It Qq B
+fully buffered with the default buffer size
+.It Ar size
+fully buffered with a buffer of
+.Ar size
+bytes
+.El
+.Sh EXAMPLES
+In the following example, the stdout stream of the
+.Xr awk 1
+command
+will be fully buffered by default because it does not refer
+to a terminal.
+.Nm
+is used to force it to be line-buffered so
+.Xr vmstat 8 Ns 's
+output will not stall until the full buffer fills.
+.Bd -literal -offset indent
+# vmstat 1 | stdbuf -o L awk '$2 > 1 || $3 > 1' | cat -n
+.Ed
+.Sh SEE ALSO
+.Xr libstdbuf 3 ,
+.Xr setvbuf 3
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 9.0 .
+.Sh AUTHORS
+.An -nosplit
+The original idea of the
+.Nm
+command comes from
+.An Padraig Brady
+who implemented it in the GNU coreutils.
+.An Jeremie Le Hen
+implemented it on
+.Fx .
diff -urNp src.HEAD_20111506/usr.bin/stdbuf/stdbuf.c src/usr.bin/stdbuf/stdbuf.c
--- src.HEAD_20111506/usr.bin/stdbuf/stdbuf.c 1970-01-01 01:00:00.000000000 +0100
+++ src/usr.bin/stdbuf/stdbuf.c 2011-08-04 18:17:50.000000000 +0200
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2011 Jeremie Le Hen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define LIBSTDBUF "/usr/lib/libstdbuf.so"
+
+extern char *__progname;
+
+static void usage(int);
+
+static void
+usage(int s)
+{
+
+ fprintf(stderr, "Usage: %s [-e bufdef] [-i bufdef] [-o bufdef] "
+ "<command> [args ...]\n", __progname);
+ fprintf(stderr, " ``bufdef'' being:\n");
+ fprintf(stderr, " - \"0\" to disable buffering;\n");
+ fprintf(stderr, " - \"L\" to set line-buffering;\n");
+ fprintf(stderr, " - <size> to set full-buffering.\n");
+ exit(s);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ char *ibuf = NULL, *obuf = NULL, *ebuf = NULL;
+ char *preload0, *preload1;
+
+ while ((i = getopt(argc, argv, ":e:i:o:")) != -1) {
+ switch (i) {
+ case 'e':
+ ebuf = optarg;
+ break;
+ case 'i':
+ ibuf = optarg;
+ break;
+ case 'o':
+ obuf = optarg;
+ break;
+ case ':':
+ warnx("Missing argument for option -%c", optopt);
+ usage(1);
+ break;
+ case '?':
+ default:
+ warnx("Unknown option -%c", optopt);
+ usage(1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (ibuf != NULL && setenv("STDBUF_0", ibuf, 1) == -1)
+ warn("Failed to set environment variable: %s=%s",
+ "STDBUF_0", ibuf);
+ if (obuf != NULL && setenv("STDBUF_1", obuf, 1) == -1)
+ warn("Failed to set environment variable: %s=%s",
+ "STDBUF_1", obuf);
+ if (ebuf != NULL && setenv("STDBUF_2", ebuf, 1) == -1)
+ warn("Failed to set environment variable: %s=%s",
+ "STDBUF_2", ebuf);
+
+ preload0 = getenv("LD_PRELOAD");
+ if (preload0 == NULL)
+ i = asprintf(&preload1, "LD_PRELOAD=" LIBSTDBUF);
+ else
+ i = asprintf(&preload1, "LD_PRELOAD=%s:%s", preload0,
+ LIBSTDBUF);
+
+ if (i < 0 || putenv(preload1) == -1)
+ warn("Failed to set environment variable: %s", preload1);
+
+ execvp(argv[0], argv);
+ err(2, "%s", argv[0]);
+}
--- stdbuf.diff ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-bugs
mailing list