bin/164947: tee looses data when writing to non-blocking file
descriptors
Diomidis Spinellis
dds at aueb.gr
Fri Feb 10 07:10:09 UTC 2012
>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 at 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)
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:
More information about the freebsd-bugs
mailing list