Skip site navigation (1)Skip section navigation (2)
Date:      Thu,  5 Apr 2012 07:24:58 +0000 (UTC)
From:      Jeremie Le Hen <jeremie@le-hen.org>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/166660: [patch] New util/shlib to change per-fd default stdio buffering mode
Message-ID:  <20120405072458.6E55DD95F@felucia.tataz.chchile.org>
Resent-Message-ID: <201204050730.q357U29P038825@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>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:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20120405072458.6E55DD95F>