diff --git a/net/local/Make.defs b/net/local/Make.defs index 4ddea967d4e..c72c48a39b9 100644 --- a/net/local/Make.defs +++ b/net/local/Make.defs @@ -38,7 +38,7 @@ ifeq ($(CONFIG_NET_LOCAL),y) NET_CSRCS += local_conn.c local_connect.c local_release.c local_bind.c -NET_CSRCS += local_listen.c +NET_CSRCS += local_listen.c local_accept.c # Include UDP build support diff --git a/net/local/local.h b/net/local/local.h index 9324d6df636..a9ae7dbaa9a 100644 --- a/net/local/local.h +++ b/net/local/local.h @@ -223,6 +223,20 @@ int local_bind(FAR struct local_conn_s *conn, int local_connect(FAR struct local_conn_s *client, FAR const struct sockaddr *addr); +/**************************************************************************** + * Name: local_release + * + * Description: + * If the local, Unix domain socket is in the connected state, then + * disconnect it. Release the local connection structure in any event + * + * Input Parameters: + * conn - A reference to local connection structure + * + ****************************************************************************/ + +int local_release(FAR struct local_conn_s *conn); + /**************************************************************************** * Name: local_listen * @@ -238,24 +252,13 @@ int local_connect(FAR struct local_conn_s *client, * Returned Value: * Zero (OK) on success; a negated errno value on failure. * + * Assumptions: + * The network is NOT locked + * ****************************************************************************/ int local_listen(FAR struct local_conn_s *server, int backlog); -/**************************************************************************** - * Name: local_release - * - * Description: - * If the local, Unix domain socket is in the connected state, then - * disconnect it. Release the local connection structure in any event - * - * Input Parameters: - * conn - A reference to local connection structure - * - ****************************************************************************/ - -int local_release(FAR struct local_conn_s *conn); - /**************************************************************************** * Function: psock_local_accept * diff --git a/net/local/local_accept.c b/net/local/local_accept.c new file mode 100644 index 00000000000..e1406168aff --- /dev/null +++ b/net/local/local_accept.c @@ -0,0 +1,202 @@ +/**************************************************************************** + * net/local/local_accept.c + * + * Copyright (C) 2015 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) + +#include +#include +#include +#include + +#include + +#include "socket/socket.h" +#include "local/local.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: psock_local_accept + * + * Description: + * This function implements accept() for Unix domain sockets. See the + * description of accept() for further information. + * + * Parameters: + * psock The listening Unix domain socket structure + * addr Receives the address of the connecting client + * addrlen Input: allocated size of 'addr', Return: returned size of 'addr' + * newconn The new, accepted Unix domain connection structure + * + * Returned Value: + * Returns zero (OK) on success or a negated errno value on failure. + * See the description of accept of the possible errno values in the + * description of accept(). + * + * Assumptions: + * Network is NOT locked. + * + ****************************************************************************/ + +int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, + FAR socklen_t *addrlen, FAR void **newconn) + +{ + FAR struct local_conn_s *server; + FAR struct local_conn_s *client; + + /* Some sanity checks */ + + DEBUGASSERT(psock && psock->s_conn); + server = (FAR struct local_conn_s *)psock->s_conn; + + if (server->lc_family != SOCK_STREAM || + server->lc_state != LOCAL_STATE_LISTENING || + server->lc_type != LOCAL_TYPE_PATHNAME) + { + return -EOPNOTSUPP; + } + + /* Are there pending connections. Remove the client from the + * head of the waiting list. + */ + +try_again: + + client = (FAR struct local_conn_s *) + dq_remfirst(&server->u.server.lc_waiters); + + if (client) + { + /* Add the waiting connection to list of clients */ + + dq_addlast(&client->lc_node, &server->u.server.lc_conns); + + /* Decrement the number of pending clients */ + + DEBUGASSERT(server->u.server.lc_pending > 0); + server->u.server.lc_pending--; + + /* And signal the client that the connection was successful */ + + client->u.client.lc_result = OK; + sem_post(&client->lc_waitsem); + + /* Return the address family */ + + if (addr) + { + FAR struct sockaddr_un *unaddr; + int totlen; + int pathlen; + + /* If an address is provided, then the length must also be + * provided. + */ + + DEBUGASSERT(addrlen); + + /* Get the length of the path (minus the NUL terminator) + * and the length of the whole client address. + */ + + pathlen = strnlen(client->lc_path, UNIX_PATH_MAX-1); + totlen = sizeof(sa_family_t) + pathlen + 1; + + /* If the length of the whole client address is larger + * than the buffer provided by the caller, then truncate + * the address to fit. + */ + + if (totlen > *addrlen) + { + pathlen -= (totlen - *addrlen); + totlen = *addrlen; + } + + /* Copy the Unix domain address */ + + unaddr = (FAR struct sockaddr_un *)addr; + unaddr->sun_family = AF_LOCAL; + memcpy(unaddr->sun_path, client->lc_path, pathlen); + unaddr->sun_path[pathlen] = '\0'; + + /* Return the Unix domain address size */ + + *addrlen = totlen; + } + + /* Return the client connection structure */ + + *newconn = (FAR void *)client; + return OK; + } + else + { + int ret; + + /* No.. then there should be no pending connections */ + + DEBUGASSERT(server->u.server.lc_pending == 0); + + /* Was the socket opened non-blocking? */ + + if (_SS_ISNONBLOCK(psock->s_flags)) + { + /* Yes.. return EAGAIN */ + + return -EAGAIN; + } + + /* Otherwise, listen for a connection and try again. */ + + ret = local_listen(server, server->u.server.lc_backlog); + if (ret < 0) + { + return ret; + } + + goto try_again; + } +} + +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/net/local/local_listen.c b/net/local/local_listen.c index 0f72b77ac04..5459088208b 100644 --- a/net/local/local_listen.c +++ b/net/local/local_listen.c @@ -75,6 +75,9 @@ dq_queue_t g_local_listeners; * Returned Value: * Zero (OK) on success; a negated errno value on failure. * + * Assumptions: + * The network is NOT locked + * ****************************************************************************/ int local_listen(FAR struct local_conn_s *server, int backlog) @@ -123,7 +126,7 @@ int local_listen(FAR struct local_conn_s *server, int backlog) server->lc_state = LOCAL_STATE_LISTENING; } - /* Loop until a connection requested or we receive a signal */ + /* Loop until a connection is requested or we receive a signal */ while (dq_empty(&server->u.server.lc_waiters)) { diff --git a/net/socket/accept.c b/net/socket/accept.c index 3d42160d67f..5cddaa264f7 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -133,7 +133,6 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) { FAR struct socket *psock = sockfd_socket(sockfd); FAR struct socket *pnewsock; - net_lock_t save; int newfd; int err; int ret; @@ -266,6 +265,8 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) if (psock->s_domain == PF_LOCAL) #endif { + /* Perform the local accept operation (with the network unlocked) */ + ret = psock_local_accept(psock, addr, addrlen, &pnewsock->s_conn); if (ret < 0) { @@ -280,11 +281,15 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) else #endif { - save = net_lock(); - ret = psock_tcp_accept(psock, addr, addrlen, &pnewsock->s_conn); + net_lock_t state; + + /* Perform the local accept operation (with the network locked) */ + + state = net_lock(); + ret = psock_tcp_accept(psock, addr, addrlen, &pnewsock->s_conn); if (ret < 0) { - net_unlock(save); + net_unlock(state); err = -ret; goto errout_with_socket; } @@ -294,7 +299,7 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) */ net_startmonitor(pnewsock); - net_unlock(save); + net_unlock(state); } #endif /* CONFIG_NET_TCP */ diff --git a/net/tcp/tcp_accept.c b/net/tcp/tcp_accept.c index d5eda4560ca..86e4e8bc012 100644 --- a/net/tcp/tcp_accept.c +++ b/net/tcp/tcp_accept.c @@ -107,7 +107,7 @@ static inline void accept_tcpsender(FAR struct socket *psock, { if (addr) { - /* If an address is provided, then the lenght must also be provided. */ + /* If an address is provided, then the length must also be provided. */ DEBUGASSERT(addrlen); @@ -158,8 +158,8 @@ static inline void accept_tcpsender(FAR struct socket *psock, * Receive interrupt level callbacks when connections occur * * Parameters: - * listener The conection stucture of the listener - * conn The connection stucture that was just accepted + * listener The connection structure of the listener + * conn The connection structure that was just accepted * * Returned Value: * None