casaroli opened a new pull request, #3959: URL: https://github.com/apache/nuttx-apps/pull/3959
## Summary Since apache/nuttx#21324, `send()`, `sendto()`, `sendmsg()` and `write()` on a stream socket raise `SIGPIPE` when they fail with `EPIPE`. With `CONFIG_SIG_SIGPIPE_ACTION`, the default action of `SIGPIPE` ends the task. A server that writes to a client that closed the connection then stops for all clients. Today NuttX TCP returns `ENOTCONN`, not `EPIPE`, for a send after the peer closed or reset the connection, so TCP servers do not get the signal yet. POSIX and Linux return `EPIPE` in this case, and a NuttX change to do the same is planned. This PR prepares the TCP servers in apps for it. The servers get `EPIPE` as an error and end only the connection with that client. The fix per program: - `send()` with `MSG_NOSIGNAL` instead of `send(..., 0)` or `write()`: netutils/ftpd, netutils/webserver, system/telnet (telnet_chatd), system/fastboot (TCP transport), netutils/xmlrpc, examples/tcpecho, examples/xmlrpc, examples/tcp_ipc_server, examples/poll, examples/netloop. - `sendmsg()` with `MSG_NOSIGNAL` instead of `writev()`: industry/nxmodbus (TCP transport). - `signal(SIGPIPE, SIG_IGN)` at the start, as upstream dropbear does in svr-main.c: netutils/dropbear. Its writes are in the upstream sources. `task_spawn()` starts the NSH sessions with the default action. `MSG_NOSIGNAL` changes only the one call, so the commands that fastbootd (`oem shell`) starts keep the default action. Two programs also stopped on `EPIPE` without the signal: telnet_chatd called `exit()` for each send error other than `EINTR` and `ECONNRESET`, and a failed send in nxmodbus server mode ended the poll loop of examples/nxmbserver. telnet_chatd now handles `EPIPE` as `ECONNRESET`, and nxmodbus closes only that client and returns `-EAGAIN`, as it does after a failed read. The first commit fixes older nxstyle errors in the changed files, because the CI check runs nxstyle on each changed file as a whole. It changes only the layout (`git diff -w` shows only added braces, one moved declaration, two wrapped lines and three spelling fixes in comments). netutils/rexecd has the same problem with a local socket pair, which raises `SIGPIPE` already today. It has its own PR: apache/nuttx-apps#3957. ## Impact Only the programs above. Without `CONFIG_SIG_SIGPIPE_ACTION` there is no change in behaviour: a `SIGPIPE` without a handler is discarded, and the send already returned the error. All the changed calls write to sockets, so `send()` and `sendmsg()` with `MSG_NOSIGNAL` are equal to `write()` and `writev()` there. No documentation change: the pages of these programs in Documentation/applications do not describe this behaviour. ## Testing qemu-armv8a:nsh (flat build, QEMU with HVF) on nuttx master 4a98cd7b44f, with `CONFIG_NET_TCP`, `NET_LOOPBACK`, `NET_TCP_WRITE_BUFFERS`, `SIG_DEFAULT`, `SIG_SIGPIPE_ACTION`, small socket buffers (`NET_RECV_BUFSIZE` and `NET_SEND_BUFSIZE` 2048), examples/ftpd, fastbootd (TCP, with `oem shell`) and examples/tcpecho. A test program (builtin app) runs one case per server over 127.0.0.1. A first client makes the server block in a send and then resets the connection: it closes the socket with unread data. ftpd: `RETR` of a 1 MB file in passive mode, then the reset of the data connection. fastbootd: `oem filedump` and `upload` of the same file. tcpecho: 6 KiB of data without reading the echo. Then a second client checks that the server still serves: the FTP greeting, `getvar:version`, and an echo. `ps` shows which servers still run. To get `EPIPE` from TCP as POSIX gives it, the test applies a test-only patch to nuttx (not part of any PR): the TCP send returns `EPIPE` instead of `ENOTCONN` after a reset of the connection. | Case | apps master 54374b3b2, TCP `EPIPE` | this PR, TCP `EPIPE` | apps master, nuttx master | this PR, nuttx master | |---|---|---|---|---| | ftpd: second client | no greeting, FTP daemon ended: FAIL | PASS | PASS | PASS | | fastbootd: second client | `ECONNREFUSED`, fastbootd ended: FAIL | PASS | PASS | PASS | | tcpecho: second client | `ECONNREFUSED`, tcpecho ended: FAIL | PASS | PASS | PASS | | ostest | 0 | 0 | 0 | 0 | The last two columns show that nuttx master does not give the signal over TCP today (`ENOTCONN`), and that the PR does not change that behaviour. Builds with `EXTRAFLAGS="-Wno-cpp -Werror"`, no compiler warnings: qemu-armv8a:nsh (flat, arm64) and rv-virt:nsh64 (flat, rv64) and rv-virt:pnsh64 (protected, rv64) with every changed program enabled (webserver with sendfile, directory list and chunked encoding; fastbootd with TCP, serial and shell; nxmodbus TCP server); the defconfigs lm3s6965-ek:tcpecho, stm3240g-eval:xmlrpc, stm3240g-eval:webserver, qemu-armv8a:fastboot (with TCP), ntosd-dm320:poll, spresense:rndis (ftpd, webserver), stm32f746g-disco:dropbear; esp32-devkitc:wifi with examples/tcp_ipc_server (it needs `CONFIG_ESP32_UART1`). Not run: webserver, telnet_chatd, nxmodbus, dropbear, xmlrpc, tcp_ipc_server, poll and netloop (build only; the change in each is the same `MSG_NOSIGNAL` flag, or `signal()` for dropbear). `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]
