Date: Tue, 21 Mar 2006 10:56:49 +0100 (CET) From: Oliver Fromme <olli@secnetix.de> To: FreeBSD-gnats-submit@FreeBSD.org Cc: Oliver Fromme <olli@secnetix.de> Subject: kern/94772: FIFOs (named pipes) + select() == broken Message-ID: <200603210956.k2L9ungW094654@lurza.secnetix.de> Resent-Message-ID: <200603211000.k2LA0U6h086418@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 94772 >Category: kern >Synopsis: FIFOs (named pipes) + select() == broken >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Mar 21 10:00:29 GMT 2006 >Closed-Date: >Last-Modified: >Originator: Oliver Fromme >Release: FreeBSD 6.1-PRERELEASE i386 >Organization: secnetix GmbH & Co. KG http://www.secnetix.de/bsd >Environment: System: FreeBSD epia.fromme.com 6.1-PRERELEASE FreeBSD 6.1-PRERELEASE #0: Tue Mar 21 10:21:23 CET 2006 olli@epia.fromme.com:/usr/src/sys/i386/compile/EPIA i386 I'm using the latest RELENG_6 from today (March 21, 2006). >Description: I recently wondered why several of my scripts that use a named pipe (FIFO) don't work on FreeBSD. After some debugging it turned out that select() seems to be broken when used with FIFOs on FreeBSD 6. Particularly, this is the bug I'm suffering from: When a FIFO had been opened for reading and the writing process closes it, the reading process blocks in select(), even though the descriptor is ready for read(). If the select() call is omitted, read() returns 0 immediately indicating EOF. But as soon as you use select(), it blocks and there is no chance to detect the EOF condition. That's clearly a serious violation of POSIX, SUSv3, Stevens APUE and all other documentations about select() and named pipes that I'm aware of. It needs to be fixed. >How-To-Repeat: Please see the small test program below. Compile it like this: cc -O -o fifotest fifotest.c Then create a named pipe, e.g.: $ mkfifo fifo And run the test program: ./fifotest fifo It will block on the open(), which is to be expected (correct behaviour so far). Then open another shell (e.g. second terminal window) and type: echo foo > fifo You will see from the output of the fifotest program that the open() succeeds, the select() returns 1, and the read() returns 4 bytes ("foo\n"). But then the next call to select() blocks, even though there is an EOF condition! The same test program (with "err()" replaced by a small self-made function) runs without error on all other UNIX systems that I've tried: Linux 2.4.32, Solaris 10, and DEC UNIX 4.0 (predecessor of Tru64). By the way, it's even sufficient to do "cat /dev/null > fifo", i.e. not writing anything at all, but issuing EOF immediately. Under FreeBSD, nothing happens at all in that case. All other UNIX systems recognize EOF (select() returns). The source contains a #define WITH_SELECT. When you undefine it, select() won't be called, only read(). Then the program runs fine and detects the EOF condition correctly. Here's the source code. In case it is mangled somehow by send-pr, I've put a copy on this web page: http://www.secnetix.de/~olli/tmp/fifotest.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/select.h> #include <fcntl.h> #include <err.h> #define WITH_SELECT int main (int argc, char *argv[]) { int fd_in, result; char buffer[4096]; if (argc != 2) errx (1, "Usage: %s <fifo>\n", argv[0]); fprintf (stderr, "Opening FIFO for reading (might block) ...\n"); if ((fd_in = open(argv[1], O_RDONLY, 0666)) < 0) err (1, argv[1]); fprintf (stderr, "FIFO opened successfully.\n"); for (;;) { #ifdef WITH_SELECT fd_set fds_r; FD_ZERO (&fds_r); FD_SET (fd_in, &fds_r); fprintf (stderr, "Calling select(read FD %d) ...\n", fd_in); if ((result = select(fd_in + 1, &fds_r, NULL, NULL, NULL)) < 0) err (1, "select()"); fprintf (stderr, "... return value is %d.\n", result); if (! FD_ISSET(fd_in, &fds_r)) continue; #endif result = read(fd_in, buffer, 4096); fprintf (stderr, "read() returned %d bytes.\n", result); if (result < 1) { if (result < 0) err (1, "read()"); break; } } fprintf (stderr, "Got EOF!\n"); close (fd_in); return 0; } /* END OF SOURCE */ >Fix: None, known, unfortunately. :-( >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200603210956.k2L9ungW094654>