All of lore.kernel.org
 help / color / mirror / Atom feed
From: "J.H. vd Water" <henri.van.de.water@xs4all.nl>
To: Alejandro Colomar <alx.manpages@gmail.com>,
	linux-man <linux-man@vger.kernel.org>
Cc: Ken Brown <kbrown@cornell.edu>,
	Michael Kerrisk <mtk.manpages@gmail.com>,
	"Michael T. Kerrisk" <mtk@man7.org>
Subject: Re: Fwd: Simple changes to select(2) and pipe(7) - example program
Date: Wed, 9 Nov 2022 16:06:58 +0100	[thread overview]
Message-ID: <2d4d8b7b-5890-cd9f-061d-6d259d8ed6ee@xs4all.nl> (raw)
In-Reply-To: <0fd276c7-3aab-0075-8a54-1371e4fad925@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3618 bytes --]

On 11/9/22 15:15, Alejandro Colomar wrote:
> Hi Henri,

> I've forwarded the emails I got from you to the mailing list, for an
> open discussion.  If I'm missing any, please resend including to the
> list, and preferrably as a reply to this thread.
Oops!

Hi Alex,

Only just now I finished an e-mail, that I intended to send to the list ...

(Sorry, I am that quick anymore)

Regards,
Henri

=====

Subscribe to the list beforehand ...

To: Alejandro Colomar <alx@kernel.org> <==== different e-mail address
To: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: linux-man@vger.kernel.org
Cc: Ken Brown <kbrown@cornell.edu>

Subject Title: [patch] select.2, pipe.7: wfix

L.S.,

As result of a problem report against his implementation of select(2) on Cygwin,
it became clear to Ken Brown (and myself) that both the Linux man pages and LPI,
which is a acronym for the book: Linux Programming Interface by Michael Kerrisk,
are not completely correct in describing the behaviour of select(2).

You can read all about it here:

 - https://cygwin.com/pipermail/cygwin/2022-September/252246.html

   (Re: FIFO issues - response by Ken Brown in which he announces
    the correction of his implementation of select() - in Cygwin)

Basically, select(2) says that the read end of a fifo is "read ready" in case
the write end of the fifo is closed (i.e. select(2) would return in that case,
because read(2) would return in that case).

However, select(2) blocks on the read end of a pipe in case the write end has
never been opened before (different from read(2)) - and only in that case! [1]

[1] select(2) on the read end of a pipe will return in case the write end is
closed, once the write has been opened and closed once.

After studying the Linux man pages (and verification on Fedora 35), I propose
the following modifications:

 1.
man 2 select  ... DESCRIPTION reads:

"select() allows a program to monitor multiple file descriptors, waiting
 until one or more of the file descriptors become "ready" for some class of
 I/O operation (e.g., input possible). A file descriptor is considered
 ready if it is  possible to perform a corresponding I/O operation
 (e.g., read(2), or a sufficiently small write(2)) without blocking."

I suggest to add the following line:

"However, note that select(2) will block on the read end of a pipe/fifo, if
 the write end of the pipe/fifo has never been opened before, unlike read(2)
 (read(2) will always return with zero if the write end of the pipe/fifo is
  closed - see pipe(7) where the text starts with I/O on pipes and fifos).

 2.
man 7 pipe ... where the paragraph start with "I/O on pipes and FIFOs":

"If a process attempts to read from an empty pipe, then read(2) will block
 until data is available."

I suggest to change the above line as follows:

"If a process attempts to read from an empty pipe while the write end is open,
 then read(2) will block (in case the O_NONBLOCK openfile status flag has been
 disabled) until data is available; however, if the write end of the pipe is
 closed and the pipe is empty, then read(2) will return with zero."

-----
Attached to this mail, you will find the files server.c and client.c, which
will hopefully clarify what I am talking about.

Finally, I do realize that this message does not contain an official patch,
but I cannot provide one, as I am not set up to provide one. Several years
ago, I "retired from active duty" (Cygwin, not Linux); git(1) and anything
that is useful (required?) in order to be able to properly "submit a patch
to the list" is currently not present on my system.

Attachments: server.c, client.c

====

[-- Attachment #2: client.c --]
[-- Type: text/x-csrc, Size: 919 bytes --]

// gcc -Wall -Wextra -o client client.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define FIFO_PATH "/tmp/myfifo"

int
main(int argc, char *argv[])
{
    int fd = -1;

    fd = open(FIFO_PATH, O_WRONLY);
    printf("fd = %d\n", fd);

    if (fd < 0) {
        fprintf(stderr, "Could not open fifo %s: %s\n", FIFO_PATH, strerror(errno));
        return -1;
    }

    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
    printf("flags = 0%o\n", flags);

    if (argc > 1) {
        write(fd, argv[1], strlen(argv[1]) );
    } else {
        write(fd, &"What ho!", 9);
    }
    // note: printf "What ho" > /tmp/myfifo from bash, would close the file descriptor.

    printf("Closing ... fd = %d\n", fd);
    close(fd); // EOF

    return 0;
}

//=====

[-- Attachment #3: server.c --]
[-- Type: text/x-csrc, Size: 4449 bytes --]

// gcc -Wall -Wextra -o server server.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>

#define FIFO_PATH "/tmp/myfifo"

int
main()
{
    int fd = -1;
    fd_set readfds;
    int nsel;
    char buf[80] = { 0 };

    if (unlink (FIFO_PATH) < 0  && errno != ENOENT) {
      perror ("unlink");
      exit (1);
    }

    if (mkfifo(FIFO_PATH, 0600) < 0) {
        fprintf(stderr, "Could not create fifo %s: %s\n", FIFO_PATH, strerror(errno));
        return -1;
    }

    fd = open(FIFO_PATH, O_RDONLY | O_NONBLOCK); // open() does not block
    printf("fd = %d\n", fd);

    if (fd < 0) {
        fprintf(stderr, "Could not open fifo %s: %s\n", FIFO_PATH, strerror(errno));
        return -1;
    }

    if (fcntl(fd, F_SETFL, O_RDONLY) == -1) // pragmatic
        fprintf(stderr, "fcntl F_SETFL: %s\n", strerror(errno));
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
    printf("flags = 0%o\n", flags);

    while(1) {
        printf("Calling select() ... ");
        fflush(stdout);
        FD_ZERO (&readfds);
        FD_SET (fd, &readfds);

        /*
        Different from read(2), select(2) will *block* on the read end of a fifo (or
        pipe) if the write end has never been opened before (and only in that case),
        while read(2) will always return with zero (in case of an empty pipe) if the
        write end is closed/ has been closed.

        Therefore, calling select(2) before calling read(2), will enable us to block
        if the pipe has never been openend before (again, and only in that case).
        */

        nsel = select (fd + 1, &readfds, NULL, NULL, NULL);
        // select() blocks until the write end of the pipe has been opened (while the
        // write end has never been opened before) and data has been injected.
        printf ("returned %d\n", nsel);

        printf("Reading ...\n");

        ssize_t status = 0;
        while (1) { // allow me to reset errno and inspect status ...
errno = 0;

            /*
             read(2) will block on the read end of a fifo (or pipe) in case the write end
             of the fifo (or pipe) has been opened and the pipe is empty. [1]
             [1] only if the O_NONBLOCK open file status flag has been disabled.
             */

            status = read(fd, buf, sizeof(buf));
printf("status = %ld, errno = %d\n", status, errno);

            // will arrive here only if the write end of fifo has been opened (before it
            // was closed again)
            if (status == 0) { // the write end of the fifo has been closed (EOF)
                close(fd);
                int fd2;
                if ((fd2 = open (FIFO_PATH, O_RDONLY | O_NONBLOCK)) < 0) {
                    perror ("open");
                    exit (1);
                }

                if (fcntl(fd, F_SETFL, O_RDONLY) == -1) // pragmatic
                    fprintf(stderr, "fcntl F_SETFL: %s\n", strerror(errno));
                int flags = fcntl(fd, F_GETFL);
                if (flags == -1)
                    fprintf(stderr, "fcntl F_GETFL: %s\n", strerror(errno));
                printf("flags = 0%o\n", flags);

                /*
                 assign the new file descriptor to variable fd; otherwise select(2) will
                 not block on the read end in case the write end of the fifo (or pipe) is
                 closed.
                 */
                fd = fd2;

                printf("fd = %d - refreshed\n", fd);
                break;
            }
            if (status > 0) {
                if (write(1, buf, status) < 0) {
                    fprintf(stderr, "Error sending message: '%s': %s\n", buf, strerror(errno));
                }
                if (buf[status - 1] != '\n') {
                    write(1, &"\n", 1);
                }
                if (strncasecmp(buf, "quit", 4) == 0) {
                    close(fd);
                    remove(FIFO_PATH);
                    exit (0);
                }
             }
            if (status < 0) {
                /* An error occurred, bail out */
                close(fd);
                perror("read");
                exit (1);
            }
        } // end while(1) read

    } // end while(!done) select

    close(fd);
    remove(FIFO_PATH);

    return 0;
}

//=====

  reply	other threads:[~2022-11-09 15:08 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <30847211-efc7-12be-6ce9-c5c4ada16805@xs4all.nl>
2022-11-09 13:57 ` Fwd: Simple changes to select(2) and pipe(7) - example program Alejandro Colomar
2022-11-09 14:02   ` Alejandro Colomar
2022-11-09 14:04     ` Alejandro Colomar
2022-11-09 14:04       ` Alejandro Colomar
2022-11-09 14:15   ` Alejandro Colomar
2022-11-09 15:06     ` J.H. vd Water [this message]
2022-11-09 15:32       ` J.H. vd Water
2022-11-22 12:22       ` Simple changes to select(2) and pipe(7) J.H. vd Water
2022-11-22 12:25         ` J.H. vd Water
2022-12-01 14:39           ` J.H. vd Water

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2d4d8b7b-5890-cd9f-061d-6d259d8ed6ee@xs4all.nl \
    --to=henri.van.de.water@xs4all.nl \
    --cc=alx.manpages@gmail.com \
    --cc=kbrown@cornell.edu \
    --cc=linux-man@vger.kernel.org \
    --cc=mtk.manpages@gmail.com \
    --cc=mtk@man7.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.