[repost] Patch to make cpdup's stdout unbuffered
Chris Pressey
cpressey at catseye.mine.nu
Tue Jun 8 20:08:51 PDT 2004
On Tue, 8 Jun 2004 16:23:05 -0700 (PDT)
Matthew Dillon <dillon at xxxxxxxxxxxxxxxxxxxx> wrote:
> :Now, we could add a flag for every single program to write unbuffered
> :(linebuffered) output. Or we could do some (non-POSIX?) magic in libc
> :to get most of this just fixed by doing a env
> :LIBC_STDOUT_BUFFERING=line $program or whatever...
> :
> :opinions about this?
> :
> :cheers
> : simon
>
> Say what? No, we are not hacking up libc to turn off buffering in
> some magic way. Buffering is what you want for the vast, vast
> majority of programs that are run from the command line. We
> certainly are not going to add yet another environment variable
> that libc has to check every time a program is started up.
>
> Just add an option to cpdup that calls setvbuf() on stdout.
I'll take that as a green light to commit the previously posted cpdup
patch.
However, I think I just discovered a potential general solution. I
strongly suspected there would be a way to do it with standard BSD
mechanisms, and lo and behold there is a userland utility already set up
to exploit those mechanisms. On FreeBSD 4.9, try popen()'ing:
script -q /dev/null your_favourite_program_with_buffered_output
According to my testing, the result should be unbuffered. This is
thanks to a fairly recent MFC to script(1) which allows its stdin/out
to be something other than a terminal. I've ported the MFC - it's
attached to this e-mail - and I'd like to commit it pending review.
However, there still seem to be some small issues with combining
script(1) and popen(). Namely, when the pipe is forcefully pclose()'ed,
the script(1) process exits, but the cpdup process doesn't :( So until
that's sorted out, I'll stick with cpdup -u in the installer.
-Chris
Index: script.c
===================================================================
RCS file: /home/dcvs/src/usr.bin/script/script.c,v
retrieving revision 1.4
diff -u -r1.4 script.c
--- script.c 28 Mar 2004 01:02:54 -0000 1.4
+++ script.c 31 Mar 2004 21:03:27 -0000
@@ -62,12 +62,17 @@
struct termios tt;
+volatile sig_atomic_t sigtermed = 0;
+volatile struct timeval *tvp;
+const struct timeval tv_zero = { 0, 0 };
+
static void done(int) __dead2;
static void dooutput(void);
static void doshell(char **);
static void fail(void);
static void finish(void);
static void usage(void);
+static void sighndl(int);
int
main(int argc, char **argv)
@@ -76,7 +81,7 @@
struct termios rtt, stt;
struct winsize win;
int aflg, kflg, ch, n;
- struct timeval tv, *tvp;
+ struct timeval tv;
time_t tvec, start;
char obuf[BUFSIZ];
char ibuf[BUFSIZ];
@@ -118,6 +123,15 @@
if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL)
err(1, "%s", fname);
+ if (flushtime > 0)
+ tvp = &tv;
+ else
+ tvp = NULL;
+
+ signal(SIGINT, sighndl);
+ signal(SIGTERM, sighndl);
+ signal(SIGHUP, sighndl);
+
tcgetattr(STDIN_FILENO, &tt);
ioctl(STDIN_FILENO, TIOCGWINSZ, &win);
if (openpty(&master, &slave, NULL, &tt, &win) == -1)
@@ -142,21 +156,16 @@
if (child == 0)
doshell(argv);
- if (flushtime > 0)
- tvp = &tv;
- else
- tvp = NULL;
-
start = time(0);
FD_ZERO(&rfd);
for (;;) {
FD_SET(master, &rfd);
FD_SET(STDIN_FILENO, &rfd);
- if (flushtime > 0) {
- tv.tv_sec = flushtime;
- tv.tv_usec = 0;
- }
- n = select(master + 1, &rfd, 0, 0, tvp);
+ tv.tv_sec = flushtime;
+ tv.tv_usec = 0;
+ n = select(master + 1, &rfd, 0, 0, (struct timeval *)tvp);
+ if (sigtermed)
+ break;
if (n < 0 && errno != EINTR)
break;
if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) {
@@ -262,3 +271,14 @@
close(master);
exit(eno);
}
+
+void sighndl(int signo)
+{
+ sigtermed = signo;
+ /*
+ * The following is to avoid a race condition. In case a
+ * signal arrives just before the select(), force the timeout
+ * to 0 to ensure an immediate exit.
+ */
+ tvp = (volatile struct timeval *)&tv_zero;
+}
More information about the Submit
mailing list