This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

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 <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/socket.h>

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/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]