This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 4f3e8ce0e50156930f9ebe76cd8023a5c61de55d Author: yezhonghui <[email protected]> AuthorDate: Tue Apr 14 13:47:35 2026 +0800 net/local: add poll support for SOCK_DGRAM local sockets Enable poll/epoll on AF_LOCAL SOCK_DGRAM sockets by creating and opening the receiver FIFO persistently at bind time, rather than temporarily at each recvfrom. This allows file_poll on lc_infile to work for DGRAM sockets, removing the -ENOSYS limitation. Changes: - local_bind: create and open receiver FIFO for DGRAM at bind time - local_netpoll: replace -ENOSYS with file_poll for DGRAM sockets Because the FIFO now outlives a single recvfrom, three further cases need handling: - lc_infile is read-only, so it never becomes writable and poll() would never report POLLOUT. A datagram send opens its own FIFO on demand and has no send buffer to wait for, so POLLOUT is reported immediately and file_poll() is used only for POLLIN. An unbound socket has no lc_infile at all, where file_poll() would have returned -ENOSYS and poll() would have reported POLLERR. - bind() opens the FIFO in the blocking mode of the socket, while psock_dgram_recvfrom() only passed MSG_DONTWAIT when it opened the FIFO itself. recv(MSG_DONTWAIT) on an empty bound socket would therefore wait forever; check FIONREAD first and return -EAGAIN, as psock_stream_recvfrom() does. - A second bind() would open a second receiver FIFO over the first, leaking its reader, and a failed bind() left lc_path and lc_type set so that a retry got EADDRINUSE from the socket itself. Reject a bind() of an already bound socket with -EINVAL and restore the unbound state on the error path. Signed-off-by: yezhonghui <[email protected]> --- net/local/local_bind.c | 42 ++++++++++++++++++++++++++++++++++++++++++ net/local/local_netpoll.c | 30 ++++++++++++++++++++++++++++-- net/local/local_recvmsg.c | 20 ++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/net/local/local_bind.c b/net/local/local_bind.c index 593534faddf..f9bb988f36f 100644 --- a/net/local/local_bind.c +++ b/net/local/local_bind.c @@ -54,6 +54,9 @@ int psock_local_bind(FAR struct socket *psock, FAR const struct sockaddr_un *unaddr = (FAR const struct sockaddr_un *)addr; int index; +#ifdef CONFIG_NET_LOCAL_DGRAM + int ret; +#endif DEBUGASSERT(unaddr->sun_family == AF_LOCAL); @@ -64,6 +67,16 @@ int psock_local_bind(FAR struct socket *psock, conn = psock->s_conn; + /* A socket may only be bound once. Rebinding a DGRAM socket would open + * a second receiver FIFO over the first one, leaking its reader. An + * unbound socket has an empty lc_path. + */ + + if (conn->lc_path[0] != '\0') + { + return -EINVAL; + } + /* Check if local address is already in use */ local_lock(); @@ -100,6 +113,35 @@ int psock_local_bind(FAR struct socket *psock, strlcpy(conn->lc_path, &unaddr->sun_path[index], sizeof(conn->lc_path)); +#ifdef CONFIG_NET_LOCAL_DGRAM + if (psock->s_type == SOCK_DGRAM) + { + ret = local_create_halfduplex(conn, conn->lc_path, conn->lc_rcvsize); + if (ret < 0) + { + goto errout; + } + + ret = local_open_receiver(conn, + _SS_ISNONBLOCK(conn->lc_conn.s_flags)); + if (ret < 0) + { + local_release_halfduplex(conn); + goto errout; + } + } +#endif + conn->lc_state = LOCAL_STATE_BOUND; return OK; + +#ifdef CONFIG_NET_LOCAL_DGRAM +errout: + + /* Leave the socket unbound so that it can be bound again. */ + + conn->lc_path[0] = '\0'; + conn->lc_type = LOCAL_TYPE_UNNAMED; + return ret; +#endif } diff --git a/net/local/local_netpoll.c b/net/local/local_netpoll.c index a459d66a8ef..7e04c2c19b1 100644 --- a/net/local/local_netpoll.c +++ b/net/local/local_netpoll.c @@ -161,7 +161,26 @@ int local_pollsetup(FAR struct socket *psock, FAR struct pollfd_s *fds) if (conn->lc_proto == SOCK_DGRAM) { - return -ENOSYS; + /* A datagram send opens its FIFO on demand and has no send buffer to + * wait for, so it is always possible to send. Report POLLOUT + * immediately rather than polling lc_infile, which is read-only and + * would never become writable. + */ + + if ((fds->events & POLLOUT) != 0) + { + fds->revents |= POLLOUT; + poll_notify(&fds, 1, POLLOUT); + } + + /* Only an bound socket has a receiver FIFO to watch for input. */ + + if ((fds->events & POLLIN) != 0 && conn->lc_infile.f_inode != NULL) + { + return file_poll(&conn->lc_infile, fds, true); + } + + return OK; } #ifdef CONFIG_NET_LOCAL_STREAM @@ -310,7 +329,14 @@ int local_pollteardown(FAR struct socket *psock, FAR struct pollfd_s *fds) if (conn->lc_proto == SOCK_DGRAM) { - return -ENOSYS; + /* Only a registered poll (fds->priv set by setup) needs teardown. */ + + if (fds->priv != NULL) + { + return file_poll(&conn->lc_infile, fds, false); + } + + return OK; } #ifdef CONFIG_NET_LOCAL_STREAM diff --git a/net/local/local_recvmsg.c b/net/local/local_recvmsg.c index 6ef5cba6bbc..3ef8bc1478b 100644 --- a/net/local/local_recvmsg.c +++ b/net/local/local_recvmsg.c @@ -515,6 +515,26 @@ psock_dgram_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, goto errout_with_halfduplex; } } + else if ((flags & MSG_DONTWAIT) != 0) + { + /* bind() opened the FIFO in the blocking mode of the socket, so a + * read would wait even though the caller asked not to. Check for + * data first, as psock_stream_recvfrom() does. + */ + + int data_len = 0; + + ret = file_ioctl(&conn->lc_infile, FIONREAD, &data_len); + if (ret < 0) + { + return ret; + } + + if (data_len == 0) + { + return -EAGAIN; + } + } readlen = sizeof(addrlen); ret = psock_fifo_read(psock, &addrlen, offset, &readlen, flags, false);
