coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] tee: Add --pipe-check to allow instantly detecting closed ou


From: Carl Edquist
Subject: Re: [PATCH] tee: Add --pipe-check to allow instantly detecting closed outputs
Date: Fri, 9 Dec 2022 08:26:03 -0600 (CST)

On Fri, 9 Dec 2022, Arsen Arsenović wrote:

Similar to the situation here, i was seeing things annoyingly look like they are still 'alive' longer than they ought to be when providing input from the terminal.

Huh, I never tried that, honestly.

Here is a simple example:

    exec 3<> /dev/tcp/example.com/80

    echo_crlf () { printf "%s\r\n" "$@"; }

    { echo_crlf "GET / HTTP/1.1"
      echo_crlf "Host: example.com"
      echo_crlf "Connection: close"
      echo_crlf ""
    } >&3

    cat <&3


In this example, i'm sending the input to the socket (on fd 3) directly using the 'printf' builtin and shell redirection, and i request the server close its side of the connection after this request.

But if i want to type the request by hand, i might do:

    sed -u 's/$/\r/' >&3

to read from the terminal, convert line terminators to CRLF, and write to the socket. Only, similar to the situation with tee and pipes, sed here will not detect when the remote read end of the socket has closed, so it hangs waiting for more input.

Did polling help your use-case?

Mostly no, as i kind of got into later last time. It seems the semantics are different for sockets than pipes; for some reason it seems the socket doesn't behave like a broken pipe until after the remote read end shuts down and then another write is made to the local end. Only after these does poll() seem to detect the broken-pipe -like state.

Oh interesting. That wasn't on my radar at all. I guess this means that when cross-compiling, the configure script is run on the cross-compiling host, rather than on the target platform; so any test programs in configure.ac with AC_RUN_IFELSE don't necessarily check the target platform functionality (?)

Or worse, is unable to run at all (and always fails), if the binary is for a different kernel or architecture.

Ok, so i guess for the sake of cross-compiling we are limited to compile/link checks.

Originally i had imagined (or hoped) that this broken-pipe detection could also be used for sockets (that was how the issue came up for me), but it seems the semantics for sockets are different than for pipes.

This might require POLLPRI or POLLRDHUP or such. Can you try with those to the set of events in pollfd?

Oh interesting! I had assumed these wouldn't help - POLLPRI is for OOB data, and, I had assumed POLLRDHUP was only for polling the read side of a socket (thus POLL*RD*HUP), to determine if the remote write end was shutdown. But you are right, poll() returns with POLLRDHUP in revents as soon as the remote end is closed. Thanks for the tip!

I assume the reason this works is, even though i have in mind that i am monitoring the socket as an output, the socket serves as an input also (it's open for RW). So, if my interpretation is correct, POLLRDHUP is actually monitoring the local read side of the socket. And so, poll() is actually detecting the remote write side getting shutdown.

This is not technically the same thing as monitoring the local output side, but if the remote end of the socket closes entirely (shutting down the remote read and write ends together), then that tells us about the local output side as well.

This definitely seems to work for the case i was playing with, though i'm not sure if it would behave as intended if the remote side only shut down its read or write end (but not both).

Also, my bits/poll.h seems to suggest this is a Linux extension:

#ifdef __USE_GNU
/* These are extensions for Linux.  */
# define POLLMSG    0x400
# define POLLREMOVE 0x1000
# define POLLRDHUP  0x2000
#endif


Anyway, this is all Good To Know. I don't know what the semantics with poll() for sockets are supposed to be in the general case (beyond Linux), so i don't feel i'm in a good position to advocate for it in coreutils. But maybe someone who knows better can comment on this topic.

A quick note, this check only needs to be done a total of once per output, it shouldn't be done inside iopoll(), which would result in an additional redundant fstat() per read().

Could this be handled by get_next_out?

Sure, either in that function or immediately after it gets called. But also once for stdout before the while (n_outputs) loop. Alternatively, allocate a file type array and populate it in the for loop that does all the fopen() calls.

Either way, the idea would be to then replace

        if (pipe_check)

with something like

        if (pipe_check && first_out_is_pipe)

or (with a file type array)

        if (pipe_check && S_ISFIFO (output_types[first_out]))


... Also, i suspect that the pipe_check option can be disabled if the _input_ is a regular file (or block device), since (i think) these always show up as ready for reading. (This check would only need to be done once for fd 0 at program start.)

Yes, there's no point poll-driving those, since it'll be always readable, up to EOF, and never hesitate to bring more data. It might just end up being a no-op if used in current form (but I haven't tried).

Well currently if the input is a regular file, poll() immediately returns POLLIN, along with any potential errors for the output. But yes the net effective behavior is the same as if the poll() call had been skipped.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]