Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 10 Feb 2012 19:20:12 GMT
From:      Martin Cracauer <cracauer@cons.org>
To:        freebsd-bugs@FreeBSD.org
Subject:   Re: bin/164947: tee looses data when writing to non-blocking file descriptors
Message-ID:  <201202101920.q1AJKCtb081889@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help
The following reply was made to PR bin/164947; it has been noted by GNATS.

From: Martin Cracauer <cracauer@cons.org>
To: Diomidis Spinellis <dds@aueb.gr>
Cc: freebsd-gnats-submit@freebsd.org
Subject: Re: bin/164947: tee looses data when writing to non-blocking file descriptors
Date: Fri, 10 Feb 2012 14:17:36 -0500

 Diomidis Spinellis wrote on Fri, Feb 10, 2012 at 07:04:41AM +0000: 
 > 
 > >Number:         164947
 > >Category:       bin
 > >Synopsis:       tee looses data when writing to non-blocking file descriptors
 > >Confidential:   no
 > >Severity:       serious
 > >Priority:       medium
 > >Responsible:    freebsd-bugs
 > >State:          open
 > >Quarter:        
 > >Keywords:       
 > >Date-Required:
 > >Class:          sw-bug
 > >Submitter-Id:   current-users
 > >Arrival-Date:   Fri Feb 10 07:10:09 UTC 2012
 > >Closed-Date:
 > >Last-Modified:
 > >Originator:     Diomidis Spinellis
 > >Release:        8.1
 > >Organization:
 > AUEB
 > >Environment:
 > FreeBSD istlab.dmst.aueb.gr 8.1-RELEASE-p6 FreeBSD 8.1-RELEASE-p6 #0: Tue Nov  1 15:16:34 EET 2011     dds@istlab.dmst.aueb.gr:/usr/obj/usr/src/sys/ISTLAB  i386
 > You have new mail in /var/mail/dds
 > 
 > >Description:
 > When tee(1) tries to write to a file descriptor that has been set to non-blocking mode the write(2) call may fail with EAGAIN.  Instead of retrying the operation, tee will throw that chunk of data away.
 > >How-To-Repeat:
 > Run the following:
 > #!/usr/local/bin/bash
 > # bash needed for the >(...) functionality
 > # ssh apparently sets O_NONBLOCK
 > # Remove the 2>/dev/null to see tee complaining
 > dd count=100000 if=/dev/zero | 
 > tee >(ssh localhost dd of=/dev/null) 2>/dev/null | 
 > (ssh localhost dd of=/dev/null)
 
 I don't think it is ssh that is causing this. If you use a named pipe
 explicitly and hook ssh up to that the error doesn't appear.  Seems to
 be something that bash is doing there.
 
 That doesn't mean I am opposed to handling EAGAIN.
 
 The way I normally do it is a simple retry loop, not using select.
 I'm aware of the tradeoffs, so far I was always better off not
 investing a second system call into every retry.
 
 Martin
 
 
 > 100000+0 records in
 > 100000+0 records out
 > 51200000 bytes transferred in 9.224390 secs (5550503 bytes/sec)
 > 100000+0 records in
 > 100000+0 records out
 > 51200000 bytes transferred in 9.061471 secs (5650297 bytes/sec)
 > 92080+0 records in
 > 92080+0 records out
 > 47144960 bytes transferred in 9.101738 secs (5179776 bytes/sec)
 > 
 > >Fix:
 > I attach a patch that fixes the problem.
 > 
 > Patch attached with submission follows:
 > 
 > --- tee.c	2012/02/08 14:50:10	1.1
 > +++ tee.c	2012/02/08 14:59:10
 > @@ -46,8 +46,10 @@
 >  #endif /* not lint */
 >  
 >  #include <sys/types.h>
 > +#include <sys/select.h>
 >  #include <sys/stat.h>
 >  #include <err.h>
 > +#include <errno.h>
 >  #include <fcntl.h>
 >  #include <signal.h>
 >  #include <stdio.h>
 > @@ -64,6 +66,7 @@
 >  
 >  void add(int, const char *);
 >  static void usage(void);
 > +static void waitfor(int fd);
 >  
 >  int
 >  main(int argc, char *argv[])
 > @@ -110,9 +113,14 @@
 >  			bp = buf;
 >  			do {
 >  				if ((wval = write(p->fd, bp, n)) == -1) {
 > -					warn("%s", p->name);
 > -					exitval = 1;
 > -					break;
 > +					if (errno == EAGAIN) {
 > +						waitfor(p->fd);
 > +						wval = 0;
 > +					} else {
 > +						warn("%s", p->name);
 > +						exitval = 1;
 > +						break;
 > +					}
 >  				}
 >  				bp += wval;
 >  			} while (n -= wval);
 > @@ -141,3 +149,15 @@
 >  	p->next = head;
 >  	head = p;
 >  }
 > +
 > +/* Wait for the specified fd to be ready for writing */
 > +static void
 > +waitfor(int fd)
 > +{
 > +	fd_set writefds;
 > +
 > +	FD_ZERO(&writefds);
 > +	FD_SET(fd, &writefds);
 > +	if (select(fd + 1, NULL, &writefds, NULL, NULL) == -1)
 > +		err(1, "select");
 > +}
 > 
 > 
 > >Release-Note:
 > >Audit-Trail:
 > >Unformatted:
 > _______________________________________________
 > freebsd-bugs@freebsd.org mailing list
 > http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
 > To unsubscribe, send any mail to "freebsd-bugs-unsubscribe@freebsd.org"
 
 -- 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 Martin Cracauer <cracauer@cons.org>   http://www.cons.org/cracauer/



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