From 6840f3fdad01a702edec589d940b24092f19848c Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 26 Jan 2015 13:30:27 -0600 Subject: Networking: Add FIFO management logic needed to support Unix domain sockets --- nuttx/net/local/Make.defs | 2 +- nuttx/net/local/local.h | 67 ++++++- nuttx/net/local/local_accept.c | 134 +++++++++---- nuttx/net/local/local_conn.c | 19 +- nuttx/net/local/local_connect.c | 60 +++++- nuttx/net/local/local_fifo.c | 407 ++++++++++++++++++++++++++++++++++++++++ nuttx/net/local/local_release.c | 2 +- 7 files changed, 642 insertions(+), 49 deletions(-) create mode 100644 nuttx/net/local/local_fifo.c diff --git a/nuttx/net/local/Make.defs b/nuttx/net/local/Make.defs index c72c48a39..670cc9550 100644 --- a/nuttx/net/local/Make.defs +++ b/nuttx/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 local_accept.c +NET_CSRCS += local_listen.c local_accept.c local_fifo.c # Include UDP build support diff --git a/nuttx/net/local/local.h b/nuttx/net/local/local.h index a9ae7dbaa..66ee6f417 100644 --- a/nuttx/net/local/local.h +++ b/nuttx/net/local/local.h @@ -74,14 +74,14 @@ enum local_state_s /* Common states */ LOCAL_STATE_UNBOUND = 0, /* Created by socket, but not bound */ - LOCAL_STATE_BOUND, /* Bound to an pipe */ + LOCAL_STATE_BOUND, /* Bound to an path */ /* SOCK_STREAM only */ LOCAL_STATE_LISTENING, /* Server listening for connections */ LOCAL_STATE_CLOSED, /* Server closed, no longer connected */ LOCAL_STATE_ACCEPT, /* Client waiting for a connection */ - LOCAL_STATE_CONNECTED, /* Client connected */ + LOCAL_STATE_CONNECTED, /* Client (or server) connected */ LOCAL_STATE_DISCONNECTED /* Client disconnected */ }; @@ -102,7 +102,8 @@ struct local_conn_s uint8_t lc_family; /* SOCK_STREAM or SOCK_DGRAM */ uint8_t lc_type; /* See enum local_type_e */ uint8_t lc_state; /* See enum local_state_e */ - int16_t lc_fd; /* File descriptor of underlying pipe */ + int16_t lc_infd; /* File descriptor of read-only FIFO */ + int16_t lc_outfd; /* File descriptor of write-only FIFO */ char lc_path[UNIX_PATH_MAX]; /* Path assigned by bind() */ /* SOCK_STREAM fields common to both client and server */ @@ -364,6 +365,66 @@ ssize_t psock_local_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, int flags, FAR struct sockaddr *from, FAR socklen_t *fromlen); +/**************************************************************************** + * Name: local_create_fifos + * + * Description: + * Create the FIFO pair needed for a connection. + * + ****************************************************************************/ + +int local_create_fifos(FAR struct local_conn_s *client); + +/**************************************************************************** + * Name: local_destroy_fifos + * + * Description: + * Destroy the FIFO pair used for a connection. + * + ****************************************************************************/ + +int local_destroy_fifos(FAR struct local_conn_s *client); + +/**************************************************************************** + * Name: local_open_client_rx + * + * Description: + * Only the client-side Rx FIFO. + * + ****************************************************************************/ + +int local_open_client_rx(FAR struct local_conn_s *client); + +/**************************************************************************** + * Name: local_open_client_tx + * + * Description: + * Only the client-side Tx FIFO. + * + ****************************************************************************/ + +int local_open_client_tx(FAR struct local_conn_s *client); + +/**************************************************************************** + * Name: local_open_server_rx + * + * Description: + * Only the server-side Rx FIFO. + * + ****************************************************************************/ + +int local_open_server_rx(FAR struct local_conn_s *server); + +/**************************************************************************** + * Name: local_open_server_tx + * + * Description: + * Only the server-side Tx FIFO. + * + ****************************************************************************/ + +int local_open_server_tx(FAR struct local_conn_s *server); + #undef EXTERN #ifdef __cplusplus } diff --git a/nuttx/net/local/local_accept.c b/nuttx/net/local/local_accept.c index b7dc623a0..3afe652c7 100644 --- a/nuttx/net/local/local_accept.c +++ b/nuttx/net/local/local_accept.c @@ -44,6 +44,7 @@ #include #include #include +#include #include @@ -83,6 +84,7 @@ int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, { FAR struct local_conn_s *server; FAR struct local_conn_s *client; + FAR struct local_conn_s *conn; int ret; /* Some sanity checks */ @@ -110,68 +112,124 @@ int psock_local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, 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 */ + /* Create a new connection structure for the server side of the + * connection. + */ - if (addr) + conn = local_alloc(); + if (!conn) + { + ndbg("ERROR: Failed to allocate new connection structure\n"); + ret = -ENOMEM; + } + else { - FAR struct sockaddr_un *unaddr; - int totlen; - int pathlen; + /* Initialize the new connection structure */ - /* If an address is provided, then the length must also be - * provided. - */ + conn->lc_crefs = 1; + conn->lc_family = SOCK_STREAM; + conn->lc_type = LOCAL_TYPE_PATHNAME; + conn->lc_state = LOCAL_STATE_CONNECTED; - DEBUGASSERT(addrlen); + strncpy(conn->lc_path, client->lc_path, UNIX_PATH_MAX-1); + conn->lc_path[UNIX_PATH_MAX-1] = '\0'; - /* Get the length of the path (minus the NUL terminator) - * and the length of the whole client address. + /* Open the server-side write-only FIFO. This should not + * block. */ - pathlen = strnlen(client->lc_path, UNIX_PATH_MAX-1); - totlen = sizeof(sa_family_t) + pathlen + 1; + ret = local_open_server_tx(conn); + if (ret < 0) + { + ndbg("ERROR: Failed to open write-only FIFOs for %s: %d\n", + conn->lc_path, ret); + } + } + + if (ret == OK) + { + DEBUGASSERT(conn->lc_outfd >= 0); - /* If the length of the whole client address is larger - * than the buffer provided by the caller, then truncate - * the address to fit. + /* Open the server-side read-only FIFO. This should not + * block because the client side has already opening it + * for writing. */ - if (totlen > *addrlen) + ret = local_open_server_rx(conn); + if (ret < 0) { - pathlen -= (totlen - *addrlen); - totlen = *addrlen; + ndbg("ERROR: Failed to open read-only FIFOs for %s: %d\n", + conn->lc_path, ret); } + } + + if (ret == OK) + { + DEBUGASSERT(conn->lc_infd >= 0); - /* Copy the Unix domain address */ + /* Add the waiting connection to list of clients */ - unaddr = (FAR struct sockaddr_un *)addr; - unaddr->sun_family = AF_LOCAL; - memcpy(unaddr->sun_path, client->lc_path, pathlen); - unaddr->sun_path[pathlen] = '\0'; + dq_addlast(&client->lc_node, &server->u.server.lc_conns); - /* Return the Unix domain address size */ + /* Return the address family */ - *addrlen = totlen; + 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 *)conn; } - /* Return the client connection structure */ + /* Signal the client with the result of the connection */ - *newconn = (FAR void *)client; - return OK; + client->u.client.lc_result = ret; + sem_post(&client->lc_waitsem); + return ret; } /* No.. then there should be no pending connections */ diff --git a/nuttx/net/local/local_conn.c b/nuttx/net/local/local_conn.c index e19a0c12c..171313b29 100644 --- a/nuttx/net/local/local_conn.c +++ b/nuttx/net/local/local_conn.c @@ -88,7 +88,8 @@ FAR struct local_conn_s *local_alloc(void) { /* Initialize non-zero elements the new connection structure */ - conn->lc_fd = -1; + conn->lc_infd = -1; + conn->lc_outfd = -1; sem_init(&conn->lc_waitsem, 0, 0); } @@ -108,13 +109,23 @@ void local_free(FAR struct local_conn_s *conn) { DEBUGASSERT(conn != NULL); - /* Make sure that the pipe is closed */ + /* Make sure that the read-only FIFO is closed */ - if (conn->lc_fd >= 0) + if (conn->lc_infd >= 0) { - close(conn->lc_fd); + close(conn->lc_infd); } + /* Make sure that the write-only FIFO is closed */ + + if (conn->lc_outfd >= 0) + { + close(conn->lc_outfd); + } + + /* Destroy all FIFOs associted with the connection */ + + local_destroy_fifos(conn); sem_destroy(&conn->lc_waitsem); /* And free the connection structure */ diff --git a/nuttx/net/local/local_connect.c b/nuttx/net/local/local_connect.c index c1e4b9fda..fdc0bd74e 100644 --- a/nuttx/net/local/local_connect.c +++ b/nuttx/net/local/local_connect.c @@ -41,6 +41,7 @@ #if defined(CONFIG_NET) && defined(CONFIG_NET_LOCAL) #include +#include #include #include #include @@ -112,12 +113,42 @@ int inline local_stream_connect(FAR struct local_conn_s *client, server->u.server.lc_pending >= server->u.server.lc_backlog) { net_unlock(state); + ndbg("ERROR: Server is not listening: lc_state=%d\n", + server->lc_state); + ndbg(" OR: The backlog limit was reached: %d or %d\n", + server->u.server.lc_pending, server->u.server.lc_backlog); return -ECONNREFUSED; } + /* Increment the number of pending server connection s*/ + server->u.server.lc_pending++; DEBUGASSERT(server->u.server.lc_pending != 0); + /* Create the FIFOs needed for the connection */ + + ret = local_create_fifos(client); + if (ret < 0) + { + ndbg("ERROR: Failed to create FIFOs for %s: %d\n", + client->lc_path, ret); + return ret; + } + + /* Open the client-side write-only FIFO. This should not block and should + * prevent the server-side from blocking as well. + */ + + ret = local_open_client_tx(client); + if (ret < 0) + { + ndbg("ERROR: Failed to open write-only FIFOs for %s: %d\n", + client->lc_path, ret); + goto errout_with_fifos; + } + + DEBUGASSERT(client->lc_outfd >= 0); + /* Add ourself to the list of waiting connections and notify the server. */ dq_addlast(&client->lc_node, &server->u.server.lc_waiters); @@ -135,9 +166,34 @@ int inline local_stream_connect(FAR struct local_conn_s *client, } while (ret == -EBUSY); - /* Was the connection successful? */ + /* Did we successfully connect? */ + + if (ret < 0) + { + ndbg("ERROR: Failed to connect: %d\n", ret); + goto errout_with_outfd; + } + + /* Yes.. open the read-only FIFO */ + + ret = local_open_client_tx(client); + if (ret < 0) + { + ndbg("ERROR: Failed to open write-only FIFOs for %s: %d\n", + client->lc_path, ret); + goto errout_with_outfd; + } + + DEBUGASSERT(client->lc_infd >= 0); + client->lc_state = LOCAL_STATE_CONNECTED; + return OK; + +errout_with_outfd: + (void)close(client->lc_outfd); - client->lc_state = (ret < 0 ? LOCAL_STATE_BOUND : LOCAL_STATE_CONNECTED); +errout_with_fifos: + (void)local_destroy_fifos(client); + client->lc_state = LOCAL_STATE_BOUND; return ret; } diff --git a/nuttx/net/local/local_fifo.c b/nuttx/net/local/local_fifo.c new file mode 100644 index 000000000..15ded2932 --- /dev/null +++ b/nuttx/net/local/local_fifo.c @@ -0,0 +1,407 @@ +/**************************************************************************** + * net/local/local_fifo.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 +#include +#include + +#include "local/local.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#define LOCAL_RX_SUFFIX "RX" +#define LOCAL_TX_SUFFIX "TX" +#define LOCAL_SUFFIX_LEN 2 + +#define LOCAL_FULLPATH_LEN (UNIX_PATH_MAX + LOCAL_SUFFIX_LEN) + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_rx_name + * + * Description: + * Create the name of the RX (client-to-server) FIFO name. + * + ****************************************************************************/ + +static inline void local_rx_name(FAR struct local_conn_s *conn, + FAR char *path) +{ + (void)snprintf(path, LOCAL_FULLPATH_LEN-1, "%s" LOCAL_RX_SUFFIX, + conn->lc_path); + path[LOCAL_FULLPATH_LEN-1] = '\0'; +} + +/**************************************************************************** + * Name: local_tx_name + * + * Description: + * Create the name of the TX (server-to-client) FIFO name. + * + ****************************************************************************/ + +static inline void local_tx_name(FAR struct local_conn_s *conn, + FAR char *path) +{ + (void)snprintf(path, LOCAL_FULLPATH_LEN-1, "%s" LOCAL_TX_SUFFIX, + conn->lc_path); + path[LOCAL_FULLPATH_LEN-1] = '\0'; +} + +/**************************************************************************** + * Name: local_fifo_exists + * + * Description: + * Check if a FIFO exists. + * + ****************************************************************************/ + +static bool local_fifo_exists(FAR const char *path) +{ + struct stat buf; + int ret; + + /* Create the client-to-server FIFO */ + + ret = stat(path, &buf); + if (ret < 0) + { + return false; + } + + /* FIFOs are character devices in NuttX. Return true if what we found + * is a FIFO. What if it is something else? In that case, we will + * return false and mkfifo() will fail. + */ + + return (bool)S_ISCHR(buf.st_mode); +} + +/**************************************************************************** + * Name: local_create_fifo + * + * Description: + * Create the one of FIFOs needed for a connection. + * + ****************************************************************************/ + +static int local_create_fifo(FAR const char *path) +{ + int ret; + + /* Create the client-to-server FIFO if it does not already exist. */ + + if (!local_fifo_exists(path)) + { + ret = mkfifo(path, 0644); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + ndbg("ERROR: Failed to create FIFO %s: %d\n", path, errcode); + return -errcode; + } + } + + /* The FIFO (or some character driver) exists at PATH or we successfully + * created the FIFO at that location. + */ + + return OK; +} + +/**************************************************************************** + * Name: local_destroy_fifo + * + * Description: + * Destroy one of the FIFOs used in a connection. + * + ****************************************************************************/ + +static int local_destroy_fifo(FAR const char *path) +{ + int ret; + + /* Unlink the client-to-server FIFO if it exists. */ + + if (local_fifo_exists(path)) + { + ret = unlink(path); + if (ret < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + ndbg("ERROR: Failed to unlink FIFO %s: %d\n", path, errcode); + return -errcode; + } + } + + /* The FIFO does not exist or we successfully unlinked it. */ + + return OK; +} + +/**************************************************************************** + * Name: local_rx_open + * + * Description: + * Open a FIFO for read-only access. + * + ****************************************************************************/ + +static inline int local_rx_open(FAR struct local_conn_s *conn, + FAR const char *path) +{ + conn->lc_infd = open(path, O_RDONLY); + if (conn->lc_infd < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + ndbg("ERROR: Failed on open %s for reading: %d\n", + path, errcode); + + /* Map the errcode to something consistent with the return + * error codes from connect(): + * + * If errcode is ENOENT, meaning that the FIFO does exist, + * return EFAULT meaning that the socket structure address is + * outside the user's address space. + */ + + return errcode == ENOENT ? -EFAULT : -errcode; + } + + return OK; +} + +/**************************************************************************** + * Name: local_tx_open + * + * Description: + * Open a FIFO for write-only access. + * + ****************************************************************************/ + +static inline int local_tx_open(FAR struct local_conn_s *conn, + FAR const char *path) +{ + conn->lc_infd = open(path, O_WRONLY); + if (conn->lc_infd < 0) + { + int errcode = errno; + DEBUGASSERT(errcode > 0); + + ndbg("ERROR: Failed on open %s for writing: %d\n", + path, errcode); + + /* Map the errcode to something consistent with the return + * error codes from connect(): + * + * If errcode is ENOENT, meaning that the FIFO does exist, + * return EFAULT meaning that the socket structure address is + * outside the user's address space. + */ + + return errcode == ENOENT ? -EFAULT : -errcode; + } + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: local_create_fifos + * + * Description: + * Create the FIFO pair needed for a connection. + * + ****************************************************************************/ + +int local_create_fifos(FAR struct local_conn_s *client) +{ + char path[LOCAL_FULLPATH_LEN]; + int ret; + + /* Create the client-to-server FIFO if it does not already exist. */ + + local_tx_name(client, path); + ret = local_create_fifo(path); + if (ret >= 0) + { + /* Create the server-to-client FIFO if it does not already exist. */ + + local_rx_name(client, path); + ret = local_create_fifo(path); + } + + return ret; +} + +/**************************************************************************** + * Name: local_destroy_fifos + * + * Description: + * Destroy the FIFO pair used for a connection. + * + ****************************************************************************/ + +int local_destroy_fifos(FAR struct local_conn_s *client) +{ + char path[LOCAL_FULLPATH_LEN]; + int ret1; + int ret2; + + /* Destroy the client-to-server FIFO if it exists. */ + + local_tx_name(client, path); + ret1 = local_destroy_fifo(path); + + /* Destroy the server-to-client FIFO if it exists. */ + + local_rx_name(client, path); + ret2 = local_create_fifo(path); + + /* Return a failure if one occurred. */ + + return ret1 < 0 ? ret1 : ret2; +} + +/**************************************************************************** + * Name: local_open_client_rx + * + * Description: + * Only the client-side Rx FIFO. + * + ****************************************************************************/ + +int local_open_client_rx(FAR struct local_conn_s *client) +{ + char path[LOCAL_FULLPATH_LEN]; + + /* Get the server-to-client path name */ + + local_tx_name(client, path); + + /* Then open the file for read-only access */ + + return local_rx_open(client, path); +} + +/**************************************************************************** + * Name: local_open_client_tx + * + * Description: + * Only the client-side Tx FIFO. + * + ****************************************************************************/ + +int local_open_client_tx(FAR struct local_conn_s *client) +{ + char path[LOCAL_FULLPATH_LEN]; + + /* Get the client-to-server path name */ + + local_rx_name(client, path); + + /* Then open the file for write-only access */ + + return local_tx_open(client, path); +} + +/**************************************************************************** + * Name: local_open_server_rx + * + * Description: + * Only the server-side Rx FIFO. + * + ****************************************************************************/ + +int local_open_server_rx(FAR struct local_conn_s *server) +{ + char path[LOCAL_FULLPATH_LEN]; + + /* Get the client-to-server path name */ + + local_rx_name(server, path); + + /* Then open the file for write-only access */ + + return local_rx_open(server, path); +} + +/**************************************************************************** + * Name: local_open_server_tx + * + * Description: + * Only the server-side Tx FIFO. + * + ****************************************************************************/ + +int local_open_server_tx(FAR struct local_conn_s *server) +{ + char path[LOCAL_FULLPATH_LEN]; + + /* Get the server-to-client path name */ + + local_tx_name(server, path); + + /* Then open the file for read-only access */ + + return local_tx_open(server, path); +} +#endif /* CONFIG_NET && CONFIG_NET_LOCAL */ diff --git a/nuttx/net/local/local_release.c b/nuttx/net/local/local_release.c index c2d6092ce..d69a0a3da 100644 --- a/nuttx/net/local/local_release.c +++ b/nuttx/net/local/local_release.c @@ -133,7 +133,7 @@ int local_release(FAR struct local_conn_s *conn) client; client = (FAR struct local_conn_s *)dq_next(&client->lc_node)) { - client->u.client.lc_result = -ENETUNREACH; + client->u.client.lc_result = -ENOTCONN; sem_post(&client->lc_waitsem); conn->lc_state = LOCAL_STATE_CLOSED; } -- cgit v1.2.3