casaroli opened a new pull request, #21324:
URL: https://github.com/apache/nuttx/pull/21324

   ## Summary
   
   POSIX: `send()`, `sendto()`, `sendmsg()` and `write()` on a stream socket 
that is no longer connected fail with `EPIPE`, and the calling thread also gets 
`SIGPIPE`, unless `MSG_NOSIGNAL` is given. NuttX returned `EPIPE` only. So 
`SIGPIPE` handlers and `CONFIG_SIG_SIGPIPE_ACTION` never saw it, and 
`MSG_NOSIGNAL` had no effect. `Documentation/reference/user/11_network.rst` and 
the comments of `send()`/`sendto()` already describe the signal.
   
   This PR adds `psock_sigpipe()`. It raises `SIGPIPE` in the calling thread 
when a send on a `SOCK_STREAM` or `SOCK_SEQPACKET` socket returned `-EPIPE` and 
`MSG_NOSIGNAL` is not set. The user interfaces call it: `sendto()` (and so 
`send()`), `sendmsg()`, and the `write` method of a socket (`write()`, 
`writev()`). The `psock_send*()` interfaces that the OS uses (telnet driver, 
NFS, 9P, userfs, OP-TEE socket, usrsock server, ...) do not raise it, and 
kernel threads do not get it.
   
   ## Impact
   
   All build modes with `CONFIG_NET`. No change without `CONFIG_NET`, and no 
change with `CONFIG_DISABLE_ALL_SIGNALS`.
   
   This changes the behaviour of the flat build too: `send()`, `sendto()`, 
`sendmsg()`, `write()` and `writev()` on a socket are the same functions for 
all tasks there. A task that gets `EPIPE` from one of these calls on a stream 
socket, without `MSG_NOSIGNAL`, now also gets `SIGPIPE`. With 
`CONFIG_SIG_SIGPIPE_ACTION=y` and no handler, the task now ends; on master it 
continued with `EPIPE`.
   
   A send gets `EPIPE` on master in these cases: a local (Unix) stream socket 
whose peer closed, a TCP or RPMsg socket after its own `shutdown(SHUT_WR)`, and 
a usrsock socket whose remote end closed. A TCP socket whose peer closed gets 
`ENOTCONN`, not `EPIPE`, so it raises no signal (not changed by this PR). In 
these cases a program that does not use `MSG_NOSIGNAL`:
   
   - with a `SIGPIPE` handler: the handler now runs, and the call still fails 
with `EPIPE`;
   - with `SIGPIPE` ignored: no change;
   - without a handler and with `CONFIG_SIG_SIGPIPE_ACTION=y`: the program now 
ends, as the POSIX default action says. No in-tree defconfig sets this option 
(default `n`);
   - without a handler and without `CONFIG_SIG_SIGPIPE_ACTION`: no change (the 
signal has no action and is discarded).
   
   In-tree programs that can get `EPIPE` on a send: `netutils/rexecd` writes to 
the command through `dpopen(O_RDWR)` (a local stream socket pair) and ends with 
`CONFIG_SIG_SIGPIPE_ACTION=y` when the command exits first; programs on a 
usrsock network (TCP over a modem or a host daemon) when the remote end closes. 
`netutils/libwebsockets` and `netutils/paho_mqtt` ignore `SIGPIPE`, 
`netutils/s2opc` uses `MSG_NOSIGNAL`. A program that must not end ignores 
`SIGPIPE` or uses `MSG_NOSIGNAL`.
   
   ## Testing
   
   Base: master 5c441693fa8. Test program `sigpipe.c` (each case: the errno and 
the number of `SIGPIPE` signals that a handler counts), on qemu-armv8a:knsh 
(kernel build) and qemu-armv8a:nsh (flat build, the test as a builtin app), 
QEMU with HVF, with `CONFIG_NET`, `CONFIG_NET_LOCAL`, 
`CONFIG_NET_LOCAL_STREAM`, `CONFIG_NET_TCP`, `CONFIG_NET_IPv4`, 
`CONFIG_NET_LOOPBACK`, `CONFIG_NETDEV_LATEINIT`, `CONFIG_NET_SOCKOPTS`, 
`CONFIG_SCHED_HPWORK`, `CONFIG_SIG_DEFAULT`, `CONFIG_SIG_SIGPIPE_ACTION`, 
`CONFIG_SCHED_WAITPID`, `CONFIG_SCHED_CHILD_STATUS`. The results are the same 
in both builds:
   
   | Case | master | this PR |
   |---|---|---|
   | local stream socket, `send()` after the peer closed | `EPIPE`, no signal: 
FAIL | `EPIPE`, 1 signal: PASS |
   | `send(MSG_NOSIGNAL)` | `EPIPE`, no signal: PASS | `EPIPE`, no signal: PASS 
|
   | `sendto()` | `EPIPE`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `sendmsg()` | `EPIPE`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `sendmsg(MSG_NOSIGNAL)` | `EPIPE`, no signal: PASS | `EPIPE`, no signal: 
PASS |
   | `write()` | `EPIPE`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `writev()` | `EPIPE`, no signal: FAIL | `EPIPE`, 1 signal: PASS |
   | `send()` in a second thread: the handler runs in that thread | no signal: 
FAIL | PASS |
   | `SIGPIPE` blocked: pending after the send | not pending: FAIL | pending: 
PASS |
   | `SIGPIPE` ignored | `EPIPE`: PASS | `EPIPE`: PASS |
   | TCP over loopback, `send()` after `shutdown(SHUT_WR)` | `EPIPE`, no 
signal: FAIL | `EPIPE`, 1 signal: PASS |
   | the same with `MSG_NOSIGNAL` | `EPIPE`, no signal: PASS | `EPIPE`, no 
signal: PASS |
   | TCP over loopback, the peer closed | `ENOTCONN`, no signal | `ENOTCONN`, 
no signal |
   | a child without a handler (default action) | exits with status 0: FAIL | 
ends, wait status 0x100: PASS |
   
   ostest exits with status 0 on qemu-armv8a:knsh and qemu-armv8a:nsh with the 
options above, on master and with this PR.
   
   Build, each OK: qemu-armv8a:nsh (flat, arm64) without `CONFIG_NET`, with the 
network options and `CONFIG_SIG_DEFAULT=n`, with 
`CONFIG_ENABLE_PARTIAL_SIGNALS`; qemu-armv8a:knsh (kernel, arm64) with the 
network options and `CONFIG_SIG_DEFAULT=n`; rv-virt:nsh64 (flat, rv64), 
rv-virt:pnsh64 (protected, rv64) and rv-virt:knsh64 (kernel, rv64) with the 
network options, `CONFIG_SIG_DEFAULT` and `CONFIG_SIG_SIGPIPE_ACTION`.
   
   `tools/checkpatch.sh -c -u -m -g` passes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to