On 8/31/23 10:02, Richard W.M. Jones wrote:
> 
> On Wed, Aug 30, 2023 at 05:21:19PM -0500, Eric Blake wrote:
>> I hit another transient failure in libnbd CI when a poorly-written
>> eval script did not consume all of stdin during .pwrite.  As behaving
>> as a data sink can be a somewhat reasonable feature of a
>> quickly-written sh or eval plugin, we should not be so insistent as
>> treating an EPIPE failure as an immediate return of EIO to the client.
> 
> I was thinking about this over night, and came to the conclusion that
> it's always fine to ignore EPIPE errors.

Interesting; I formed the opposite impression!

> For example a script might
> be processing the input data gradually and then encounter an error and
> want to exit immediately.  We also have plenty of plugins that discard
> some or all of the written data.

But that would be associated with a nonzero exit status, right?

And that way the nbd client would see the pwrite operation as failed.

Laszlo

> 
> So my counter-proposal (coming soon) is going to simply turn the EPIPE
> error into a debug message and discard the rest of the write buffer.
> 
> Rich.
> 
> 
>> Signed-off-by: Eric Blake <ebl...@redhat.com>
>> ---
>>
>> I probably need to add unit test coverage of this before committing
>> (although proving that I win the data race on a client process exiting
>> faster than the parent can write enough data to hit EPIPE is hard).
>>
>>  plugins/sh/nbdkit-sh-plugin.pod |  8 +++++++
>>  plugins/sh/call.c               | 38 ++++++++++++++++++++++-----------
>>  2 files changed, 34 insertions(+), 12 deletions(-)
>>
>> diff --git a/plugins/sh/nbdkit-sh-plugin.pod 
>> b/plugins/sh/nbdkit-sh-plugin.pod
>> index b2c946a0..8b83a5b3 100644
>> --- a/plugins/sh/nbdkit-sh-plugin.pod
>> +++ b/plugins/sh/nbdkit-sh-plugin.pod
>> @@ -505,6 +505,14 @@ Unlike in other languages, if you provide a C<pwrite> 
>> method you
>>  B<must> also provide a C<can_write> method which exits with code C<0>
>>  (true).
>>
>> +With nbdkit E<ge> 1.36, this method may return C<0> without consuming
>> +any data from stdin, and without producing any output, in order to
>> +behave as an intentional data sink.  But in older versions, nbdkit
>> +would treat any C<EPIPE> failure in writing to your script as an error
>> +condition even if your script returns success; to avoid unintended
>> +failures, you may want to include C<"cat >/dev/null"> in a script
>> +intending to ignore the client's write requests.
>> +
>>  =item C<flush>
>>
>>   /path/to/script flush <handle>
>> diff --git a/plugins/sh/call.c b/plugins/sh/call.c
>> index 888c6459..79c67a04 100644
>> --- a/plugins/sh/call.c
>> +++ b/plugins/sh/call.c
>> @@ -34,6 +34,7 @@
>>
>>  #include <assert.h>
>>  #include <fcntl.h>
>> +#include <stdbool.h>
>>  #include <stdio.h>
>>  #include <stdlib.h>
>>  #include <inttypes.h>
>> @@ -130,6 +131,7 @@ debug_call (const char **argv)
>>   */
>>  static int
>>  call3 (const char *wbuf, size_t wbuflen, /* sent to stdin (can be NULL) */
>> +       bool *pipe_full,                  /* set if wbuf not fully written */
>>         string *rbuf,                     /* read from stdout */
>>         string *ebuf,                     /* read from stderr */
>>         const char **argv)                /* script + parameters */
>> @@ -275,15 +277,8 @@ call3 (const char *wbuf, size_t wbuflen, /* sent to 
>> stdin (can be NULL) */
>>        r = write (pfds[0].fd, wbuf, wbuflen);
>>        if (r == -1) {
>>          if (errno == EPIPE) {
>> -          /* We tried to write to the script but it didn't consume
>> -           * the data.  Probably the script exited without reading
>> -           * from stdin.  This is an error in the script.
>> -           */
>> -          nbdkit_error ("%s: write to script failed because of a broken 
>> pipe: "
>> -                        "this can happen if the script exits without "
>> -                        "consuming stdin, which usually indicates a bug "
>> -                        "in the script",
>> -                        argv0);
>> +          *pipe_full = true;
>> +          r = wbuflen;
>>          }
>>          else
>>            nbdkit_error ("%s: write: %m", argv0);
>> @@ -555,7 +550,7 @@ call (const char **argv)
>>    CLEANUP_FREE_STRING string rbuf = empty_vector;
>>    CLEANUP_FREE_STRING string ebuf = empty_vector;
>>
>> -  r = call3 (NULL, 0, &rbuf, &ebuf, argv);
>> +  r = call3 (NULL, 0, NULL, &rbuf, &ebuf, argv);
>>    return handle_script_error (argv[0], &ebuf, r);
>>  }
>>
>> @@ -568,7 +563,7 @@ call_read (string *rbuf, const char **argv)
>>    int r;
>>    CLEANUP_FREE_STRING string ebuf = empty_vector;
>>
>> -  r = call3 (NULL, 0, rbuf, &ebuf, argv);
>> +  r = call3 (NULL, 0, NULL, rbuf, &ebuf, argv);
>>    r = handle_script_error (argv[0], &ebuf, r);
>>    if (r == ERROR)
>>      string_reset (rbuf);
>> @@ -584,7 +579,26 @@ call_write (const char *wbuf, size_t wbuflen, const 
>> char **argv)
>>    int r;
>>    CLEANUP_FREE_STRING string rbuf = empty_vector;
>>    CLEANUP_FREE_STRING string ebuf = empty_vector;
>> +  bool pipe_full = false;
>>
>> -  r = call3 (wbuf, wbuflen, &rbuf, &ebuf, argv);
>> +  r = call3 (wbuf, wbuflen, &pipe_full, &rbuf, &ebuf, argv);
>> +  if (pipe_full && r == OK) {
>> +    /* We allow scripts to intentionally ignore data, but they must
>> +     * have no output when doing so.
>> +     */
>> +    if (rbuf.len > 0 || ebuf.len > 0) {
>> +      nbdkit_error ("%s: write to script failed because of a broken pipe: "
>> +                    "this can happen if the script exits without "
>> +                    "consuming stdin, which usually indicates a bug "
>> +                    "in the script",
>> +                    argv[0]);
>> +      r = ERROR;
>> +    }
>> +    else
>> +      nbdkit_debug ("%s: write to script failed because of a broken pipe; "
>> +                    "assuming this was an intentional data sink, although 
>> it "
>> +                    "may indicate a bug in the script",
>> +                    argv[0]);
>> +  }
>>    return handle_script_error (argv[0], &ebuf, r);
>>  }
>> -- 
>> 2.41.0
>>
>> _______________________________________________
>> Libguestfs mailing list
>> Libguestfs@redhat.com
>> https://listman.redhat.com/mailman/listinfo/libguestfs
> 

_______________________________________________
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs

Reply via email to