Mailing-List: contact cygwin-help AT sourceware DOT cygnus DOT com; run by ezmlm List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT sources DOT redhat DOT com Delivered-To: mailing list cygwin AT sources DOT redhat DOT com Date: 13 Jul 2001 14:14:35 -0400 Message-ID: <20010713181435.3695.qmail@lizard.curl.com> From: Jonathan Kamens To: cygwin AT cygwin DOT com Subject: data in socketpair() channel lost if writer closes or exits without shutting down The test program below creates a socketpair and then forks. The child writes some data to the socketpair and then exits, and the parent tries to read that data. Instead of printing "PARENT: foo (exiting)" as it should, the parent prints "PARENT: read: Connection reset by peer". This is also true if you compile the program with "-DUSE_CLOSE" to tell the child to close the write end of the socketpair before exiting. However, it works properly if you compile the program with "-DUSE_SHUTDOWN" to tell the child to shutdown its end of the socketpair before exiting. There are two different bugs here: 1) close() on an end of a socketpair should behave the same as shutdown(..., 2), in terms of flushing data to the other end of the socketpair. 2) Data sent by the writer should be flushed to the reader, not lost, when the writer exits. This problem causes recent versions of rsync, when they are compiled to use socketpairs (which is the default), to report a bogus "read error: connection reset by peer" at the end of every rsync, even when it in fact copied everything successfully. I'm going to submit a patch to the rsync maintainers suggesting that they work around this problem by calling shutdown() before exiting. jik ************************* #include #include #include #include #include main() { int pipefds[2]; int readfd, writefd; char inbuf[4]; char outbuf[4] = "foo"; int pid; fd_set readfds, exceptfds; socketpair(AF_UNIX, SOCK_STREAM, 0, pipefds); /* pipe(pipefds); */ readfd = pipefds[0]; writefd = pipefds[1]; if (pid = fork()) { fprintf(stderr, "PARENT: close(writefd)\n"); close(writefd); FD_ZERO(&readfds); FD_SET(readfd, &readfds); exceptfds = readfds; fprintf(stderr, "PARENT: selecting\n"); if (select(readfd + 1, &readfds, NULL, &exceptfds, NULL) <= 0) { perror("PARENT: select"); exit(1); } if (FD_ISSET(readfd, &exceptfds)) { fprintf(stderr, "PARENT: exception is set\n"); } if (FD_ISSET(readfd, &readfds)) { fprintf(stderr, "PARENT: read is set\n"); } fprintf(stderr, "PARENT: reading\n"); if (read(readfd, inbuf, sizeof(inbuf)) != sizeof(inbuf)) { perror("PARENT: read"); exit(1); } printf("PARENT: %s (exiting)\n", inbuf); exit(0); } else { fprintf(stderr, "CHILD: close(readfd)\n"); close(readfd); fprintf(stderr, "CHILD: writing\n"); if (write(writefd, outbuf, sizeof(outbuf)) != sizeof(outbuf)) { perror("CHILD: write"); exit(1); } #ifdef USE_SHUTDOWN shutdown(writefd, 2); #endif #ifdef USE_CLOSE close(writefd); #endif fprintf(stderr, "CHILD: exiting\n"); exit(0); } } -- Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple Bug reporting: http://cygwin.com/bugs.html Documentation: http://cygwin.com/docs.html FAQ: http://cygwin.com/faq/