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);

Reply via email to