[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