diff --git a/drivers/net/telnet.c b/drivers/net/telnet.c index 7fded5c2263..241ef778f6d 100644 --- a/drivers/net/telnet.c +++ b/drivers/net/telnet.c @@ -1226,7 +1226,7 @@ static int telnet_poll(FAR struct file *filep, FAR struct pollfd *fds, */ psock = &priv->td_psock; - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { return -EBADF; } diff --git a/fs/Makefile b/fs/Makefile index a5725e1c0bd..815088b8113 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -50,7 +50,6 @@ include mmap/Make.defs include semaphore/Make.defs include mqueue/Make.defs include shm/Make.defs -include socket/Make.defs # Additional files required is mount-able file systems are supported diff --git a/fs/procfs/fs_procfsproc.c b/fs/procfs/fs_procfsproc.c index 2a12d3a0b34..bec2fc33b7f 100644 --- a/fs/procfs/fs_procfsproc.c +++ b/fs/procfs/fs_procfsproc.c @@ -1075,6 +1075,9 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile, { FAR struct task_group_s *group = tcb->group; FAR struct file *file; +#ifdef CONFIG_NET + FAR struct socket *socket; +#endif size_t remaining; size_t linesize; size_t copysize; @@ -1108,7 +1111,7 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile, { /* Is there an inode associated with the file descriptor? */ - if (file->f_inode && !INODE_IS_SOCKET(file->f_inode)) + if (file->f_inode) { linesize = snprintf(procfile->line, STATUS_LINELEN, "%3d %8ld %04x\n", i, (long)file->f_pos, @@ -1145,33 +1148,29 @@ static ssize_t proc_groupfd(FAR struct proc_file_s *procfile, /* Examine each open socket descriptor */ - for (i = 0, file = group->tg_filelist.fl_files; - i < CONFIG_NFILE_DESCRIPTORS; - i++, file++) + for (i = 0, socket = group->tg_socketlist.sl_sockets; + i < CONFIG_NSOCKET_DESCRIPTORS; + i++, socket++) { /* Is there an connection associated with the socket descriptor? */ - if (file->f_inode && INODE_IS_SOCKET(file->f_inode)) + if (socket->s_conn) { - FAR struct socket *socket = file->f_inode->i_private; - if (socket->s_conn) + linesize = snprintf(procfile->line, STATUS_LINELEN, + "%3d %2d %3d %02x", + i + CONFIG_NFILE_DESCRIPTORS, + socket->s_crefs, socket->s_type, + socket->s_flags); + copysize = procfs_memcpy(procfile->line, linesize, buffer, + remaining, &offset); + + totalsize += copysize; + buffer += copysize; + remaining -= copysize; + + if (totalsize >= buflen) { - linesize = snprintf(procfile->line, STATUS_LINELEN, - "%3d %3d %02x", - i + CONFIG_NFILE_DESCRIPTORS, - socket->s_type, - socket->s_flags); - copysize = procfs_memcpy(procfile->line, linesize, buffer, - remaining, &offset); - - totalsize += copysize; - buffer += copysize; - remaining -= copysize; - - if (totalsize >= buflen) - { - return totalsize; - } + return totalsize; } } } diff --git a/fs/socket/Make.defs b/fs/socket/Make.defs deleted file mode 100644 index f605f18450c..00000000000 --- a/fs/socket/Make.defs +++ /dev/null @@ -1,31 +0,0 @@ -############################################################################ -# fs/socket/Make.defs -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. The -# ASF licenses this file to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance with the -# License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -############################################################################ - -# Include socket support - -ifeq ($(CONFIG_NET),y) - -CSRCS += socket.c - -# Include socket build support - -DEPPATH += --dep-path socket -VPATH += :socket -endif diff --git a/fs/socket/socket.c b/fs/socket/socket.c deleted file mode 100644 index 8cfb61fa52a..00000000000 --- a/fs/socket/socket.c +++ /dev/null @@ -1,298 +0,0 @@ -/**************************************************************************** - * fs/socket/socket.c - * - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. The - * ASF licenses this file to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "inode/inode.h" - -/**************************************************************************** - * Private Functions Prototypes - ****************************************************************************/ - -static int sock_file_open(FAR struct file *filep); -static int sock_file_close(FAR struct file *filep); -static ssize_t sock_file_read(FAR struct file *filep, FAR char *buffer, - size_t buflen); -static ssize_t sock_file_write(FAR struct file *filep, - FAR const char *buffer, size_t buflen); -static int sock_file_ioctl(FAR struct file *filep, int cmd, - unsigned long arg); -static int sock_file_poll(FAR struct file *filep, struct pollfd *fds, - bool setup); - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -static const struct file_operations g_sock_fileops = -{ - sock_file_open, /* open */ - sock_file_close, /* close */ - sock_file_read, /* read */ - sock_file_write, /* write */ - NULL, /* seek */ - sock_file_ioctl, /* ioctl */ - sock_file_poll, /* poll */ -#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS - NULL, /* unlink */ -#endif -}; - -static struct inode g_sock_inode = -{ - .i_crefs = 1, - .i_flags = FSNODEFLAG_TYPE_SOCKET, - .u = - { - .i_ops = &g_sock_fileops, - }, -}; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -static int sock_file_open(FAR struct file *filep) -{ - FAR struct socket *psock; - int ret; - - psock = kmm_zalloc(sizeof(*psock)); - if (psock == NULL) - { - return -ENOMEM; - } - - ret = psock_dup2(filep->f_priv, psock); - if (ret >= 0) - { - filep->f_priv = psock; - } - else - { - kmm_free(psock); - } - - return ret; -} - -static int sock_file_close(FAR struct file *filep) -{ - psock_close(filep->f_priv); - kmm_free(filep->f_priv); - return 0; -} - -static ssize_t sock_file_read(FAR struct file *filep, FAR char *buffer, - size_t buflen) -{ - return psock_recv(filep->f_priv, buffer, buflen, 0); -} - -static ssize_t sock_file_write(FAR struct file *filep, - FAR const char *buffer, size_t buflen) -{ - return psock_send(filep->f_priv, buffer, buflen, 0); -} - -static int sock_file_ioctl(FAR struct file *filep, int cmd, - unsigned long arg) -{ - return psock_ioctl(filep->f_priv, cmd, arg); -} - -static int sock_file_poll(FAR struct file *filep, FAR struct pollfd *fds, - bool setup) -{ - return psock_poll(filep->f_priv, fds, setup); -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: sockfd_allocate - * - * Description: - * Allocate a socket descriptor - * - * Input Parameters: - * psock A double pointer to socket structure to be allocated. - * - * Returned Value: - * Allocate a struct files instance and associate it with an socket - * instance. Returns the file descriptor == index into the files array. - * - ****************************************************************************/ - -int sockfd_allocate(FAR struct socket **psock) -{ - int sockfd; - - *psock = kmm_zalloc(sizeof(**psock)); - if (*psock == NULL) - { - return -ENOMEM; - } - - sockfd = files_allocate(&g_sock_inode, O_RDWR, 0, *psock, 0); - if (sockfd < 0) - { - kmm_free(*psock); - } - - inode_addref(&g_sock_inode); - - return sockfd; -} - -/**************************************************************************** - * Name: sockfd_socket - * - * Description: - * Given a socket descriptor, return the underlying socket structure. - * - * Input Parameters: - * sockfd - The socket descriptor index to use. - * - * Returned Value: - * On success, a reference to the socket structure associated with the - * the socket descriptor is returned. NULL is returned on any failure. - * - ****************************************************************************/ - -FAR struct socket *sockfd_socket(int sockfd) -{ - FAR struct file *filep; - - if (fs_getfilep(sockfd, &filep) < 0) - { - return NULL; - } - - if (INODE_IS_SOCKET(filep->f_inode)) - { - return filep->f_priv; - } - - return NULL; -} - -/**************************************************************************** - * Name: socket - * - * Description: - * socket() creates an endpoint for communication and returns a descriptor. - * - * Input Parameters: - * domain (see sys/socket.h) - * type (see sys/socket.h) - * protocol (see sys/socket.h) - * - * Returned Value: - * A non-negative socket descriptor on success; -1 on error with errno set - * appropriately. - * - * EACCES - * Permission to create a socket of the specified type and/or protocol - * is denied. - * EAFNOSUPPORT - * The implementation does not support the specified address family. - * EINVAL - * Unknown protocol, or protocol family not available. - * EMFILE - * Process file table overflow. - * ENFILE - * The system limit on the total number of open files has been reached. - * ENOBUFS or ENOMEM - * Insufficient memory is available. The socket cannot be created until - * sufficient resources are freed. - * EPROTONOSUPPORT - * The protocol type or the specified protocol is not supported within - * this domain. - * - * Assumptions: - * - ****************************************************************************/ - -int socket(int domain, int type, int protocol) -{ - FAR struct socket *psock; - FAR struct file *filep; - int errcode; - int sockfd; - int ret; - - /* Allocate a socket descriptor */ - - sockfd = sockfd_allocate(&psock); - if (sockfd < 0) - { - nerr("ERROR: Failed to allocate a socket descriptor\n"); - errcode = -sockfd; - goto errout; - } - - ret = fs_getfilep(sockfd, &filep); - if (ret < 0) - { - goto errout_with_sockfd; - } - - if (type & SOCK_CLOEXEC) - { - filep->f_oflags |= O_CLOEXEC; - } - - /* Initialize the socket structure */ - - ret = psock_socket(domain, type, protocol, psock); - if (ret < 0) - { - nerr("ERROR: psock_socket() failed: %d\n", ret); - errcode = -ret; - goto errout_with_sockfd; - } - - return sockfd; - -errout_with_sockfd: - close(sockfd); - -errout: - set_errno(errcode); - return ERROR; -} diff --git a/fs/vfs/fs_close.c b/fs/vfs/fs_close.c index 9df6a71a53e..4f7eec8fe52 100644 --- a/fs/vfs/fs_close.c +++ b/fs/vfs/fs_close.c @@ -46,6 +46,10 @@ #include #include +#ifdef CONFIG_NET +# include +#endif + #include "inode/inode.h" /**************************************************************************** @@ -126,7 +130,18 @@ int nx_close(int fd) if (fd >= CONFIG_NFILE_DESCRIPTORS) { - return -EBADF; + /* Close a socket descriptor */ + +#ifdef CONFIG_NET + if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + return net_close(fd); + } + else +#endif + { + return -EBADF; + } } /* Close the driver or mountpoint. NOTES: (1) there is no diff --git a/fs/vfs/fs_dup.c b/fs/vfs/fs_dup.c index 2792682a505..2dd3d556e34 100644 --- a/fs/vfs/fs_dup.c +++ b/fs/vfs/fs_dup.c @@ -134,7 +134,24 @@ int nx_dup(int fd) } else { - return -EBADF; + /* Not a valid file descriptor. + * Did we get a valid socket descriptor? + */ + +#ifdef CONFIG_NET + if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + /* Yes.. dup the socket descriptor. */ + + return net_dup(fd, CONFIG_NFILE_DESCRIPTORS); + } + else +#endif + { + /* No.. then it is a bad descriptor number */ + + return -EBADF; + } } } diff --git a/fs/vfs/fs_dup2.c b/fs/vfs/fs_dup2.c index 5054e0c80d9..c59b04a7b76 100644 --- a/fs/vfs/fs_dup2.c +++ b/fs/vfs/fs_dup2.c @@ -117,7 +117,6 @@ int file_dup2(FAR struct file *filep1, FAR struct file *filep2) { /* (Re-)open the pseudo file or device driver */ - temp.f_priv = filep1->f_priv; ret = inode->u.i_ops->open(&temp); } @@ -167,7 +166,24 @@ int nx_dup2(int fd1, int fd2) if (fd1 >= CONFIG_NFILE_DESCRIPTORS) { - return -EBADF; + /* Not a valid file descriptor. + * Did we get a valid socket descriptor? + */ + +#ifdef CONFIG_NET + if (fd1 < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + /* Yes.. dup the socket descriptor. */ + + return net_dup2(fd1, fd2); + } + else +#endif + { + /* No.. then it is a bad descriptor number */ + + return -EBADF; + } } else { diff --git a/fs/vfs/fs_fcntl.c b/fs/vfs/fs_fcntl.c index 8f3a42dabc0..28c92fd4d2d 100644 --- a/fs/vfs/fs_fcntl.c +++ b/fs/vfs/fs_fcntl.c @@ -236,6 +236,7 @@ static int file_vfcntl(FAR struct file *filep, int cmd, va_list ap) static int nx_vfcntl(int fd, int cmd, va_list ap) { FAR struct file *filep; + int ret; /* Did we get a valid file descriptor? */ @@ -243,34 +244,41 @@ static int nx_vfcntl(int fd, int cmd, va_list ap) { /* Get the file structure corresponding to the file descriptor. */ - if (fs_getfilep(fd, &filep) >= 0) + ret = fs_getfilep(fd, &filep); + if (ret >= 0) { DEBUGASSERT(filep != NULL); - /* check for operations on a socket descriptor */ - -#ifdef CONFIG_NET - if (INODE_IS_SOCKET(filep->f_inode) && - cmd != F_DUPFD && cmd != F_GETFD && cmd != F_SETFD) - { - /* Yes.. defer socket descriptor operations to psock_vfcntl(). The - * errno is not set on failures. - */ - - return psock_vfcntl(sockfd_socket(fd), cmd, ap); - } -#endif /* Let file_vfcntl() do the real work. The errno is not set on * failures. */ - return file_vfcntl(filep, cmd, ap); + ret = file_vfcntl(filep, cmd, ap); + } + } + else + { + /* No... check for operations on a socket descriptor */ + +#ifdef CONFIG_NET + if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + /* Yes.. defer socket descriptor operations to net_vfcntl(). The + * errno is not set on failures. + */ + + ret = net_vfcntl(fd, cmd, ap); + } + else +#endif + { + /* No.. this descriptor number is out of range */ + + ret = -EBADF; } } - /* No.. this descriptor number is out of range */ - - return -EBADF; + return ret; } /**************************************************************************** diff --git a/fs/vfs/fs_fdopen.c b/fs/vfs/fs_fdopen.c index e5de24a0695..cace0ca49de 100644 --- a/fs/vfs/fs_fdopen.c +++ b/fs/vfs/fs_fdopen.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "inode/inode.h" @@ -145,8 +146,18 @@ int fs_fdopen(int fd, int oflags, FAR struct tcb_s *tcb, if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) { + /* No.. If networking is enabled then this might be a socket + * descriptor. + */ + +#ifdef CONFIG_NET + ret = net_checksd(fd, oflags); +#else + /* No networking... it is just a bad descriptor */ + ret = -EBADF; goto errout; +#endif } /* The descriptor is in a valid range to file descriptor... perform some diff --git a/fs/vfs/fs_fstat.c b/fs/vfs/fs_fstat.c index 448b27a97de..2612ba86343 100644 --- a/fs/vfs/fs_fstat.c +++ b/fs/vfs/fs_fstat.c @@ -237,8 +237,22 @@ int fstat(int fd, FAR struct stat *buf) if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) { +#ifdef CONFIG_NET + /* Let the networking logic handle the fstat() */ + + ret = net_fstat(fd, buf); + if (ret < 0) + { + goto errout; + } + + return OK; +#else + /* No networking... it is just a bad descriptor */ + ret = -EBADF; goto errout; +#endif } /* The descriptor is in a valid range for a file descriptor... do the @@ -252,21 +266,6 @@ int fstat(int fd, FAR struct stat *buf) goto errout; } -#ifdef CONFIG_NET - if (INODE_IS_SOCKET(filep->f_inode)) - { - /* Let the networking logic handle the fstat() */ - - ret = psock_fstat(sockfd_socket(fd), buf); - if (ret < 0) - { - goto errout; - } - - return OK; - } -#endif - /* Perform the fstat operation */ ret = file_fstat(filep, buf); diff --git a/fs/vfs/fs_ioctl.c b/fs/vfs/fs_ioctl.c index 19be1e4bd78..0e8b15db5d4 100644 --- a/fs/vfs/fs_ioctl.c +++ b/fs/vfs/fs_ioctl.c @@ -46,6 +46,12 @@ #include #include +#include + +#ifdef CONFIG_NET +# include +#endif + #include "inode/inode.h" /**************************************************************************** @@ -96,7 +102,18 @@ static int nx_vioctl(int fd, int req, va_list ap) if (fd >= CONFIG_NFILE_DESCRIPTORS) { - return -EBADF; + /* Perform the socket ioctl */ + +#ifdef CONFIG_NET + if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + ret = netdev_vioctl(fd, req, ap); + } + else +#endif + { + return -EBADF; + } } else { diff --git a/fs/vfs/fs_poll.c b/fs/vfs/fs_poll.c index ab9aeb309bd..a3e8d1d42fd 100644 --- a/fs/vfs/fs_poll.c +++ b/fs/vfs/fs_poll.c @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -89,7 +90,18 @@ static int poll_fdsetup(int fd, FAR struct pollfd *fds, bool setup) if (fd >= CONFIG_NFILE_DESCRIPTORS) { - return -EBADF; + /* Perform the socket ioctl */ + +#ifdef CONFIG_NET + if (fd < (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS)) + { + return net_poll(fd, fds, setup); + } + else +#endif + { + return -EBADF; + } } return fs_poll(fd, fds, setup); @@ -152,6 +164,15 @@ static inline int poll_setup(FAR struct pollfd *fds, nfds_t nfds, } break; +#ifdef CONFIG_NET + case POLLSOCK: + if (fds[i].ptr != NULL) + { + ret = psock_poll(fds[i].ptr, &fds[i], true); + } + break; +#endif + default: ret = -EINVAL; break; @@ -177,6 +198,12 @@ static inline int poll_setup(FAR struct pollfd *fds, nfds_t nfds, file_poll(fds[j].ptr, &fds[j], false); break; +#ifdef CONFIG_NET + case POLLSOCK: + psock_poll(fds[j].ptr, &fds[j], false); + break; +#endif + default: break; } @@ -228,6 +255,15 @@ static inline int poll_teardown(FAR struct pollfd *fds, nfds_t nfds, } break; +#ifdef CONFIG_NET + case POLLSOCK: + if (fds[i].ptr != NULL) + { + status = psock_poll(fds[i].ptr, &fds[i], false); + } + break; +#endif + default: status = -EINVAL; break; @@ -290,8 +326,7 @@ int file_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) * If not, return -ENOSYS */ - if ((INODE_IS_DRIVER(inode) || INODE_IS_MQUEUE(inode) || - INODE_IS_SOCKET(inode)) && + if ((INODE_IS_DRIVER(inode) || INODE_IS_MQUEUE(inode)) && inode->u.i_ops != NULL && inode->u.i_ops->poll != NULL) { /* Yes, it does... Setup the poll */ diff --git a/fs/vfs/fs_read.c b/fs/vfs/fs_read.c index 65ad98647d9..994c6139aa3 100644 --- a/fs/vfs/fs_read.c +++ b/fs/vfs/fs_read.c @@ -41,12 +41,14 @@ #include #include +#include #include #include #include #include #include +#include #include "inode/inode.h" @@ -63,6 +65,7 @@ * * - It does not modify the errno variable, * - It is not a cancellation point, + * - It does not handle socket descriptors, and * - It accepts a file structure instance instead of file descriptor. * * Input Parameters: @@ -139,7 +142,17 @@ ssize_t nx_read(int fd, FAR void *buf, size_t nbytes) if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) { +#ifdef CONFIG_NET + /* No.. If networking is enabled, read() is the same as recv() with + * the flags parameter set to zero. + */ + + return nx_recv(fd, buf, nbytes, 0); +#else + /* No networking... it is a bad descriptor in any event */ + return -EBADF; +#endif } else { diff --git a/fs/vfs/fs_sendfile.c b/fs/vfs/fs_sendfile.c index ec16d165bcd..6ecf6f78740 100644 --- a/fs/vfs/fs_sendfile.c +++ b/fs/vfs/fs_sendfile.c @@ -108,35 +108,35 @@ ssize_t sendfile(int outfd, int infd, off_t *offset, size_t count) * descriptor? Check the source file: Is it a normal file? */ - FAR struct socket *psock; - - psock = sockfd_socket(outfd); - if (psock != NULL) + if ((unsigned int)outfd >= CONFIG_NFILE_DESCRIPTORS && + (unsigned int)infd < CONFIG_NFILE_DESCRIPTORS) { + FAR struct file *filep; + int ret; + /* This appears to be a file-to-socket transfer. Get the file * structure. */ - FAR struct file *infilep; - int ret = fs_getfilep(infd, &infilep); + ret = fs_getfilep(infd, &filep); if (ret < 0) { set_errno(-ret); return ERROR; } - DEBUGASSERT(infilep != NULL); + DEBUGASSERT(filep != NULL); - /* Then let psock_sendfile do the work. */ + /* Then let net_sendfile do the work. */ - ret = psock_sendfile(psock, infilep, offset, count); + ret = net_sendfile(outfd, filep, offset, count); if (ret >= 0 || get_errno() != ENOSYS) { return ret; } /* Fall back to the slow path if errno equals ENOSYS, - * because psock_sendfile fail to optimize this transfer. + * because net_sendfile fail to optimize this transfer. */ } #endif diff --git a/fs/vfs/fs_write.c b/fs/vfs/fs_write.c index 3a0fb8ca06a..d1b7f035b31 100644 --- a/fs/vfs/fs_write.c +++ b/fs/vfs/fs_write.c @@ -47,7 +47,12 @@ #include #include +#ifdef CONFIG_NET_TCP +# include +#endif + #include +#include #include "inode/inode.h" @@ -66,6 +71,7 @@ * * - It does not modify the errno variable, * - It is not a cancellation point, and + * - It does not handle socket descriptors. * * Input Parameters: * filep - Instance of struct file to use with the write @@ -118,7 +124,7 @@ ssize_t file_write(FAR struct file *filep, FAR const void *buf, * - It is not a cancellation point. * * Input Parameters: - * fd - file descriptor to write to + * fd - file descriptor (or socket descriptor) to write to * buf - Data to write * nbytes - Length of data to write * @@ -144,7 +150,15 @@ ssize_t nx_write(int fd, FAR const void *buf, size_t nbytes) if ((unsigned int)fd >= CONFIG_NFILE_DESCRIPTORS) { +#if defined(CONFIG_NET_TCP) || defined(CONFIG_NET_CAN) + /* Write to a socket descriptor is equivalent to + * send with flags == 0. + */ + + ret = nx_send(fd, buf, nbytes, 0); +#else ret = -EBADF; +#endif } else { @@ -175,7 +189,7 @@ ssize_t nx_write(int fd, FAR const void *buf, size_t nbytes) * descriptor fd from the buffer starting at buf. * * Input Parameters: - * fd - file descriptor to write to + * fd - file descriptor (or socket descriptor) to write to * buf - Data to write * nbytes - Length of data to write * diff --git a/include/aio.h b/include/aio.h index a740073ab82..fd9379c4b20 100644 --- a/include/aio.h +++ b/include/aio.h @@ -128,7 +128,7 @@ struct aiocb FAR volatile void *aio_buf; /* Location of buffer */ off_t aio_offset; /* File offset */ size_t aio_nbytes; /* Length of transfer */ -#if CONFIG_NFILE_DESCRIPTORS > 127 +#if (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS) > 127 int16_t aio_fildes; /* File descriptor (should be int) */ #else int8_t aio_fildes; /* File descriptor (should be int) */ diff --git a/include/nuttx/fs/fs.h b/include/nuttx/fs/fs.h index a26f4915b8d..4bba51ad243 100644 --- a/include/nuttx/fs/fs.h +++ b/include/nuttx/fs/fs.h @@ -114,7 +114,6 @@ #define FSNODEFLAG_TYPE_SHM 0x00000006 /* Shared memory region */ #define FSNODEFLAG_TYPE_MTD 0x00000007 /* Named MTD driver */ #define FSNODEFLAG_TYPE_SOFTLINK 0x00000008 /* Soft link */ -#define FSNODEFLAG_TYPE_SOCKET 0x00000009 /* Socket */ #define FSNODEFLAG_DELETED 0x00000010 /* Unlinked */ #define INODE_IS_TYPE(i,t) \ @@ -129,7 +128,6 @@ #define INODE_IS_SHM(i) INODE_IS_TYPE(i,FSNODEFLAG_TYPE_SHM) #define INODE_IS_MTD(i) INODE_IS_TYPE(i,FSNODEFLAG_TYPE_MTD) #define INODE_IS_SOFTLINK(i) INODE_IS_TYPE(i,FSNODEFLAG_TYPE_SOFTLINK) -#define INODE_IS_SOCKET(i) INODE_IS_TYPE(i,FSNODEFLAG_TYPE_SOCKET) #define INODE_GET_TYPE(i) ((i)->i_flags & FSNODEFLAG_TYPE_MASK) #define INODE_SET_TYPE(i,t) \ @@ -147,7 +145,6 @@ #define INODE_SET_SHM(i) INODE_SET_TYPE(i,FSNODEFLAG_TYPE_SHM) #define INODE_SET_MTD(i) INODE_SET_TYPE(i,FSNODEFLAG_TYPE_MTD) #define INODE_SET_SOFTLINK(i) INODE_SET_TYPE(i,FSNODEFLAG_TYPE_SOFTLINK) -#define INODE_SET_SOCKET(i) INODE_SET_TYPE(i,FSNODEFLAG_TYPE_SOCKET) /* Mountpoint fd_flags values */ diff --git a/include/nuttx/net/net.h b/include/nuttx/net/net.h index f3f952b27ea..33fe2edea15 100644 --- a/include/nuttx/net/net.h +++ b/include/nuttx/net/net.h @@ -96,6 +96,7 @@ /* Definitions of 8-bit socket flags */ #define _SF_INITD 0x01 /* Bit 0: Socket structure is initialized */ +#define _SF_CLOEXEC 0x04 /* Bit 2: Close on execute */ #define _SF_NONBLOCK 0x08 /* Bit 3: Don't block if no data (TCP/READ only) */ #define _SF_LISTENING 0x10 /* Bit 4: SOCK_STREAM is listening */ #define _SF_BOUND 0x20 /* Bit 5: SOCK_STREAM is bound to an address */ @@ -114,6 +115,7 @@ /* Macro to manage the socket state and flags */ #define _SS_INITD(s) (((s) & _SF_INITD) != 0) +#define _SS_ISCLOEXEC(s) (((s) & _SF_CLOEXEC) != 0) #define _SS_ISNONBLOCK(s) (((s) & _SF_NONBLOCK) != 0) #define _SS_ISLISTENING(s) (((s) & _SF_LISTENING) != 0) #define _SS_ISBOUND(s) (((s) & _SF_BOUND) != 0) @@ -123,7 +125,7 @@ /* Determine if a socket is valid. Valid means both (1) allocated and (2) * successfully initialized: * - * Allocated: psock->s_conn != NULL + * Allocated: psock->s_crefs > 0 * Initialized: _SF_INITD bit set in psock->s_flags * * This logic is used within the OS to pick the sockets to be cloned when a @@ -132,7 +134,7 @@ * pthread. */ -#define _PS_ALLOCD(psock) ((psock)->s_conn != NULL) +#define _PS_ALLOCD(psock) ((psock)->s_crefs > 0) #define _PS_INITD(psock) (_SS_INITD((psock)->s_flags)) #define _PS_VALID(psock) (_PS_ALLOCD(psock) && _PS_INITD(psock)) @@ -260,6 +262,7 @@ struct devif_callback_s; /* Forward reference */ struct socket { + int16_t s_crefs; /* Reference count on the socket */ uint8_t s_domain; /* IP domain */ uint8_t s_type; /* Protocol type */ uint8_t s_proto; /* Socket Protocol */ @@ -294,6 +297,16 @@ struct socket #endif }; +/* This defines a list of sockets indexed by the socket descriptor */ + +#ifdef CONFIG_NET +struct socketlist +{ + sem_t sl_sem; /* Manage access to the socket list */ + struct socket sl_sockets[CONFIG_NSOCKET_DESCRIPTORS]; +}; +#endif + /**************************************************************************** * Public Data ****************************************************************************/ @@ -511,21 +524,51 @@ FAR struct iob_s *net_ioballoc(bool throttled, enum iob_user_e consumerid); #endif /**************************************************************************** - * Name: sockfd_allocate + * Name: net_checksd * * Description: - * Allocate a socket descriptor - * - * Input Parameters: - * psock A double pointer to socket structure to be allocated. - * - * Returned Value: - * Allocate a struct files instance and associate it with an socket - * instance. Returns the file descriptor == index into the files array. + * Check if the socket descriptor is valid for the provided TCB and if it + * supports the requested access. This trivial operation is part of the + * fdopen() operation when the fdopen() is performed on a socket + * descriptor. It simply performs some sanity checking before permitting + * the socket descriptor to be wrapped as a C FILE stream. * ****************************************************************************/ -int sockfd_allocate(FAR struct socket **psock); +int net_checksd(int fd, int oflags); + +/**************************************************************************** + * Name: net_initlist + * + * Description: + * Initialize a list of sockets for a new task + * + * Input Parameters: + * list -- A reference to the pre-alloated socket list to be initialized. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void net_initlist(FAR struct socketlist *list); + +/**************************************************************************** + * Name: net_releaselist + * + * Description: + * Release resources held by the socket list + * + * Input Parameters: + * list -- A reference to the pre-allocated socket list to be un- + * initialized. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void net_releaselist(FAR struct socketlist *list); /**************************************************************************** * Name: sockfd_socket @@ -587,6 +630,25 @@ FAR struct socket *sockfd_socket(int sockfd); int psock_socket(int domain, int type, int protocol, FAR struct socket *psock); +/**************************************************************************** + * Name: net_close + * + * Description: + * Performs the close operation on socket descriptors + * + * Input Parameters: + * sockfd Socket descriptor of socket + * + * Returned Value: + * Returns zero (OK) on success. On failure, it returns a negated errno + * value to indicate the nature of the error. + * + * Assumptions: + * + ****************************************************************************/ + +int net_close(int sockfd); + /**************************************************************************** * Name: psock_close * @@ -1229,6 +1291,39 @@ int psock_getpeername(FAR struct socket *psock, FAR struct sockaddr *addr, int psock_vioctl(FAR struct socket *psock, int cmd, va_list ap); int psock_ioctl(FAR struct socket *psock, int cmd, ...); +/**************************************************************************** + * Name: netdev_vioctl + * + * Description: + * Perform network device specific operations. + * + * Input Parameters: + * sockfd Socket descriptor of device + * cmd The ioctl command + * ap The argument of the ioctl cmd + * + * Returned Value: + * A non-negative value is returned on success; a negated errno value is + * returned on any failure to indicate the nature of the failure: + * + * EBADF + * 'sockfd' is not a valid socket descriptor. + * EFAULT + * 'arg' references an inaccessible memory area. + * ENOTTY + * 'cmd' not valid. + * EINVAL + * 'arg' is not valid. + * ENOTTY + * 'sockfd' is not associated with a network device. + * ENOTTY + * The specified request does not apply to the kind of object that the + * descriptor 'sockfd' references. + * + ****************************************************************************/ + +int netdev_vioctl(int sockfd, int cmd, va_list ap); + /**************************************************************************** * Name: psock_poll * @@ -1252,10 +1347,32 @@ struct pollfd; /* Forward reference -- see poll.h */ int psock_poll(FAR struct socket *psock, struct pollfd *fds, bool setup); /**************************************************************************** - * Name: psock_dup2 + * Name: net_poll * * Description: - * Clone a socket instance to an new instance. + * The standard poll() operation redirects operations on socket descriptors + * to this function. + * + * Input Parameters: + * fd - The socket descriptor of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +struct pollfd; /* Forward reference -- see poll.h */ + +int net_poll(int sockfd, struct pollfd *fds, bool setup); + +/**************************************************************************** + * Name: psock_dup + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. * * Returned Value: * On success, returns the number of new socket. On any error, @@ -1263,16 +1380,54 @@ int psock_poll(FAR struct socket *psock, struct pollfd *fds, bool setup); * ****************************************************************************/ +int psock_dup(FAR struct socket *psock, int minsd); + +/**************************************************************************** + * Name: net_dup + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. + * + * Returned Value: + * On success, returns the number of new socket. On any error, + * a negated errno value is returned. + * + ****************************************************************************/ + +int net_dup(int sockfd, int minsd); + +/**************************************************************************** + * Name: psock_dup2 + * + * Description: + * Performs the low level, common portion of net_dup() and net_dup2() + * + ****************************************************************************/ + int psock_dup2(FAR struct socket *psock1, FAR struct socket *psock2); /**************************************************************************** - * Name: psock_fstat + * Name: net_dup2 + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int net_dup2(int sockfd1, int sockfd2); + +/**************************************************************************** + * Name: net_fstat * * Description: * Performs fstat operations on socket * * Input Parameters: - * psock - An instance of the internal socket structure. + * sockfd - Socket descriptor of the socket to operate on * buf - Caller-provided location in which to return the fstat data * * Returned Value: @@ -1283,10 +1438,10 @@ int psock_dup2(FAR struct socket *psock1, FAR struct socket *psock2); struct stat; /* Forward reference. See sys/stat.h */ -int psock_fstat(FAR struct socket *psock, FAR struct stat *buf); +int net_fstat(int sockfd, FAR struct stat *buf); /**************************************************************************** - * Name: psock_sendfile + * Name: net_sendfile * * Description: * The send() call may be used only when the socket is in a connected state @@ -1349,8 +1504,8 @@ int psock_fstat(FAR struct socket *psock, FAR struct stat *buf); #ifdef CONFIG_NET_SENDFILE struct file; -ssize_t psock_sendfile(FAR struct socket *psock, FAR struct file *infile, - FAR off_t *offset, size_t count); +ssize_t net_sendfile(int outfd, struct file *infile, off_t *offset, + size_t count); #endif /**************************************************************************** @@ -1393,6 +1548,25 @@ int psock_vfcntl(FAR struct socket *psock, int cmd, va_list ap); int psock_fcntl(FAR struct socket *psock, int cmd, ...); +/**************************************************************************** + * Name: net_vfcntl + * + * Description: + * Performs fcntl operations on socket + * + * Input Parameters: + * sockfd - Socket descriptor of the socket to operate on + * cmd - The fcntl command. + * ap - Command-specific arguments + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +int net_vfcntl(int sockfd, int cmd, va_list ap); + /**************************************************************************** * Name: netdev_register * diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h index c4376d491fd..0d1a1b06063 100644 --- a/include/nuttx/sched.h +++ b/include/nuttx/sched.h @@ -594,6 +594,12 @@ struct task_group_s #endif #endif +#ifdef CONFIG_NET + /* Sockets ********************************************************************/ + + struct socketlist tg_socketlist; /* Maps socket descriptor to socket */ +#endif + #ifdef CONFIG_ARCH_ADDRENV /* Address Environment ********************************************************/ @@ -904,6 +910,10 @@ FAR struct filelist *nxsched_get_files(void); FAR struct streamlist *nxsched_get_streams(void); #endif /* CONFIG_FILE_STREAM */ +#ifdef CONFIG_NET +FAR struct socketlist *nxsched_get_sockets(void); +#endif + /******************************************************************************** * Name: nxtask_init * diff --git a/include/sys/poll.h b/include/sys/poll.h index 21800852741..341d40d44e3 100644 --- a/include/sys/poll.h +++ b/include/sys/poll.h @@ -93,7 +93,8 @@ #define POLLFD (0x00) #define POLLFILE (0x40) -#define POLLMASK (0x40) +#define POLLSOCK (0x80) +#define POLLMASK (0xC0) /**************************************************************************** * Public Type Definitions diff --git a/include/sys/select.h b/include/sys/select.h index 7b136d44927..9a357ac7209 100644 --- a/include/sys/select.h +++ b/include/sys/select.h @@ -52,7 +52,11 @@ /* Get the total number of descriptors that we will have to support */ -#define FD_SETSIZE CONFIG_NFILE_DESCRIPTORS +#ifdef CONFIG_NSOCKET_DESCRIPTORS +# define FD_SETSIZE (CONFIG_NFILE_DESCRIPTORS + CONFIG_NSOCKET_DESCRIPTORS) +#else +# define FD_SETSIZE CONFIG_NFILE_DESCRIPTORS +#endif /* We will use a 32-bit bitsets to represent the set of descriptors. How * many uint32_t's do we need to span all descriptors? diff --git a/net/bluetooth/bluetooth_sendto.c b/net/bluetooth/bluetooth_sendto.c index 3d6dd2bd7bf..b4a930d9c4a 100644 --- a/net/bluetooth/bluetooth_sendto.c +++ b/net/bluetooth/bluetooth_sendto.c @@ -243,12 +243,13 @@ ssize_t psock_bluetooth_sendto(FAR struct socket *psock, FAR const void *buf, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } conn = (FAR struct bluetooth_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); /* Verify that the address is large enough to be a valid PF_BLUETOOTH * address. diff --git a/net/can/can_send.c b/net/can/can_send.c index ab3f31c6a04..3c01684ab99 100644 --- a/net/can/can_send.c +++ b/net/can/can_send.c @@ -184,15 +184,15 @@ ssize_t psock_can_send(FAR struct socket *psock, FAR const void *buf, struct send_s state; int ret = OK; + conn = (FAR struct can_conn_s *)psock->s_conn; + /* Verify that the sockfd corresponds to valid, allocated socket */ - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { return -EBADF; } - conn = (FAR struct can_conn_s *)psock->s_conn; - /* Get the device driver that will service this transfer */ dev = conn->dev; @@ -316,15 +316,15 @@ ssize_t psock_can_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg) struct send_s state; int ret = OK; + conn = (FAR struct can_conn_s *)psock->s_conn; + /* Verify that the sockfd corresponds to valid, allocated socket */ - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { return -EBADF; } - conn = (FAR struct can_conn_s *)psock->s_conn; - /* Get the device driver that will service this transfer */ dev = conn->dev; diff --git a/net/ieee802154/ieee802154_sendto.c b/net/ieee802154/ieee802154_sendto.c index ab7c2d15e75..391e4c63d72 100644 --- a/net/ieee802154/ieee802154_sendto.c +++ b/net/ieee802154/ieee802154_sendto.c @@ -431,12 +431,13 @@ ssize_t psock_ieee802154_sendto(FAR struct socket *psock, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } conn = (FAR struct ieee802154_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); /* Verify that the address is large enough to be a valid PF_IEEE802154 * address. diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c index 856d90e50c2..488f2d1bfc0 100644 --- a/net/inet/inet_sockif.c +++ b/net/inet/inet_sockif.c @@ -874,6 +874,7 @@ static int inet_accept(FAR struct socket *psock, FAR struct sockaddr *addr, /* Initialize the socket structure. */ + newsock->s_crefs = 1; newsock->s_domain = psock->s_domain; newsock->s_type = SOCK_STREAM; newsock->s_sockif = psock->s_sockif; diff --git a/net/local/local_accept.c b/net/local/local_accept.c index 3a8e548efb5..8f03700f380 100644 --- a/net/local/local_accept.c +++ b/net/local/local_accept.c @@ -233,6 +233,7 @@ int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr, { /* Setup the client socket structure */ + newsock->s_crefs = 1; newsock->s_domain = psock->s_domain; newsock->s_type = SOCK_STREAM; newsock->s_sockif = psock->s_sockif; diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c index e387af7a076..5b1984a886b 100644 --- a/net/netdev/netdev_ioctl.c +++ b/net/netdev/netdev_ioctl.c @@ -1662,7 +1662,7 @@ int psock_vioctl(FAR struct socket *psock, int cmd, va_list ap) /* Verify that the psock corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } @@ -1772,6 +1772,44 @@ int psock_ioctl(FAR struct socket *psock, int cmd, ...) return ret; } +/**************************************************************************** + * Name: netdev_vioctl + * + * Description: + * Perform network device specific operations. + * + * Input Parameters: + * sockfd Socket descriptor of device + * cmd The ioctl command + * ap The argument of the ioctl cmd + * + * Returned Value: + * A non-negative value is returned on success; a negated errno value is + * returned on any failure to indicate the nature of the failure: + * + * EBADF + * 'sockfd' is not a valid socket descriptor. + * EFAULT + * 'arg' references an inaccessible memory area. + * ENOTTY + * 'cmd' not valid. + * EINVAL + * 'arg' is not valid. + * ENOTTY + * 'sockfd' is not associated with a network device. + * ENOTTY + * The specified request does not apply to the kind of object that the + * descriptor 'sockfd' references. + * + ****************************************************************************/ + +int netdev_vioctl(int sockfd, int cmd, va_list ap) +{ + FAR struct socket *psock = sockfd_socket(sockfd); + + return psock_vioctl(psock, cmd, ap); +} + /**************************************************************************** * Name: netdev_ifup / netdev_ifdown * diff --git a/net/pkt/pkt_send.c b/net/pkt/pkt_send.c index 4121a026e6c..2e66ecc03b5 100644 --- a/net/pkt/pkt_send.c +++ b/net/pkt/pkt_send.c @@ -177,7 +177,7 @@ ssize_t psock_pkt_send(FAR struct socket *psock, FAR const void *buf, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/rpmsg/rpmsg_sockif.c b/net/rpmsg/rpmsg_sockif.c index 98b8713274c..673dfe5163e 100644 --- a/net/rpmsg/rpmsg_sockif.c +++ b/net/rpmsg/rpmsg_sockif.c @@ -732,6 +732,7 @@ static int rpmsg_socket_accept(FAR struct socket *psock, newsock->s_sockif = psock->s_sockif; newsock->s_type = SOCK_STREAM; newsock->s_conn = conn; + newsock->s_crefs = 1; rpmsg_socket_getaddr(conn, addr, addrlen); diff --git a/net/sixlowpan/sixlowpan_tcpsend.c b/net/sixlowpan/sixlowpan_tcpsend.c index a9b3b133023..0019f3a5c7c 100644 --- a/net/sixlowpan/sixlowpan_tcpsend.c +++ b/net/sixlowpan/sixlowpan_tcpsend.c @@ -716,12 +716,12 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, ninfo("buflen %lu\n", (unsigned long)buflen); sixlowpan_dumpbuffer("Outgoing TCP payload", buf, buflen); - DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0); DEBUGASSERT(psock->s_type == SOCK_STREAM); /* Make sure that this is a valid socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); return (ssize_t)-EBADF; @@ -738,6 +738,7 @@ ssize_t psock_6lowpan_tcp_send(FAR struct socket *psock, FAR const void *buf, /* Get the underlying TCP connection structure */ conn = (FAR struct tcp_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); #ifdef CONFIG_NET_IPv4 /* Ignore if not IPv6 domain */ diff --git a/net/sixlowpan/sixlowpan_udpsend.c b/net/sixlowpan/sixlowpan_udpsend.c index 1ceeb322a6d..309504df795 100644 --- a/net/sixlowpan/sixlowpan_udpsend.c +++ b/net/sixlowpan/sixlowpan_udpsend.c @@ -157,7 +157,7 @@ ssize_t psock_6lowpan_udp_sendto(FAR struct socket *psock, ninfo("buflen %lu\n", (unsigned long)buflen); - DEBUGASSERT(psock != NULL && psock->s_conn != NULL && to != NULL); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && to != NULL); DEBUGASSERT(psock->s_type == SOCK_DGRAM); sixlowpan_dumpbuffer("Outgoing UDP payload", buf, buflen); @@ -169,7 +169,7 @@ ssize_t psock_6lowpan_udp_sendto(FAR struct socket *psock, /* Make sure that this is a datagram valid socket */ - if (psock->s_conn == NULL || psock->s_type != SOCK_DGRAM) + if (psock->s_crefs <= 0 || psock->s_type != SOCK_DGRAM) { nerr("ERROR: Invalid socket\n"); return (ssize_t)-EBADF; @@ -334,14 +334,14 @@ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, ninfo("buflen %lu\n", (unsigned long)buflen); - DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0); DEBUGASSERT(psock->s_type == SOCK_DGRAM); sixlowpan_dumpbuffer("Outgoing UDP payload", buf, buflen); /* Make sure that this is a valid socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock != NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); return (ssize_t)-EBADF; @@ -359,6 +359,7 @@ ssize_t psock_6lowpan_udp_send(FAR struct socket *psock, FAR const void *buf, /* Get the underlying UDP "connection" structure */ conn = (FAR struct udp_conn_s *)psock->s_conn; + DEBUGASSERT(conn != NULL); /* Ignore if not IPv6 domain */ diff --git a/net/socket/Make.defs b/net/socket/Make.defs index c2b09e7e7fe..565f2a9ae77 100644 --- a/net/socket/Make.defs +++ b/net/socket/Make.defs @@ -37,7 +37,7 @@ SOCK_CSRCS += bind.c connect.c getsockname.c getpeername.c SOCK_CSRCS += recv.c recvfrom.c send.c sendto.c -SOCK_CSRCS += socket.c net_close.c +SOCK_CSRCS += socket.c net_sockets.c net_close.c net_dup.c SOCK_CSRCS += net_dup2.c net_sockif.c net_poll.c net_vfcntl.c SOCK_CSRCS += net_fstat.c @@ -58,6 +58,12 @@ ifeq ($(CONFIG_NET_SOCKOPTS),y) SOCK_CSRCS += setsockopt.c getsockopt.c net_timeo.c endif +# Support for network access using streams + +ifeq ($(CONFIG_FILE_STREAM),y) +SOCK_CSRCS += net_checksd.c +endif + # Support for sendfile() ifeq ($(CONFIG_NET_SENDFILE),y) diff --git a/net/socket/accept.c b/net/socket/accept.c index fa93f85c08a..d7a78b33a06 100644 --- a/net/socket/accept.c +++ b/net/socket/accept.c @@ -42,7 +42,6 @@ #include #include -#include #include #include #include @@ -250,7 +249,7 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { /* It is not a valid socket description. Distinguish between the cases * where sockfd is a just valid and when it is a valid file descriptor used @@ -273,13 +272,20 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) * cannot fail later) */ - newfd = sockfd_allocate(&newsock); + newfd = sockfd_allocate(0); if (newfd < 0) { errcode = ENFILE; goto errout; } + newsock = sockfd_socket(newfd); + if (newsock == NULL) + { + errcode = ENFILE; + goto errout_with_socket; + } + ret = psock_accept(psock, addr, addrlen, newsock); if (ret < 0) { @@ -291,7 +297,7 @@ int accept(int sockfd, FAR struct sockaddr *addr, FAR socklen_t *addrlen) return newfd; errout_with_socket: - close(newfd); + sockfd_release(newfd); errout: leave_cancellation_point(); diff --git a/net/socket/bind.c b/net/socket/bind.c index 6f0ce05f307..572622d9238 100644 --- a/net/socket/bind.c +++ b/net/socket/bind.c @@ -95,7 +95,7 @@ int psock_bind(FAR struct socket *psock, const struct sockaddr *addr, /* Verify that the psock corresponds to valid, allocated socket */ - if (!psock || psock->s_conn == NULL) + if (!psock || psock->s_crefs <= 0) { return -ENOTSOCK; } diff --git a/net/socket/connect.c b/net/socket/connect.c index f11a3809afb..ed8b78aa97a 100644 --- a/net/socket/connect.c +++ b/net/socket/connect.c @@ -135,7 +135,7 @@ int psock_connect(FAR struct socket *psock, FAR const struct sockaddr *addr, /* Verify that the psock corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/getpeername.c b/net/socket/getpeername.c index 2b082c6f2be..6444d17bbd8 100644 --- a/net/socket/getpeername.c +++ b/net/socket/getpeername.c @@ -81,7 +81,7 @@ int psock_getpeername(FAR struct socket *psock, FAR struct sockaddr *addr, { /* Verify that the psock corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/getsockname.c b/net/socket/getsockname.c index 8527539da9b..613e0f67df5 100644 --- a/net/socket/getsockname.c +++ b/net/socket/getsockname.c @@ -96,7 +96,7 @@ int psock_getsockname(FAR struct socket *psock, FAR struct sockaddr *addr, { /* Verify that the psock corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/getsockopt.c b/net/socket/getsockopt.c index a9e059bc5dd..f6c5862b23d 100644 --- a/net/socket/getsockopt.c +++ b/net/socket/getsockopt.c @@ -339,7 +339,7 @@ int psock_getsockopt(FAR struct socket *psock, int level, int option, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/listen.c b/net/socket/listen.c index 82ab9084df3..6f1b8951e3d 100644 --- a/net/socket/listen.c +++ b/net/socket/listen.c @@ -148,7 +148,7 @@ int listen(int sockfd, int backlog) /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { /* It is not a valid socket description. Distinguish between the * cases where sockfd is a just invalid and when it is a valid file diff --git a/net/socket/net_checksd.c b/net/socket/net_checksd.c new file mode 100644 index 00000000000..e7069a37b80 --- /dev/null +++ b/net/socket/net_checksd.c @@ -0,0 +1,70 @@ +/**************************************************************************** + * net/socket/net_checksd.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include +#include + +#include "socket/socket.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: net_checksd + * + * Description: + * Check if the socket descriptor is valid for the provided TCB and if it + * supports the requested access. This trivial operation is part of the + * fdopen() operation when the fdopen() is performed on a socket + * descriptor. It simply performs some sanity checking before permitting + * the socket descriptor to be wrapped as a C FILE stream. + * + ****************************************************************************/ + +int net_checksd(int sd, int oflags) +{ + FAR struct socket *psock = sockfd_socket(sd); + + /* Verify that the sockfd corresponds to valid, allocated socket */ + + if (!psock || psock->s_crefs <= 0) + { + ninfo("No valid socket for sd: %d\n", sd); + return -EBADF; + } + + /* NOTE: We permit the socket FD to be "wrapped" in a stream as + * soon as the socket descriptor is created by socket(). Therefore + * (1) we don't care if the socket is connected yet, and (2) there + * are no access restrictions that can be enforced yet. + */ + + return OK; +} diff --git a/net/socket/net_close.c b/net/socket/net_close.c index ed29be5be50..8d00fceac0b 100644 --- a/net/socket/net_close.c +++ b/net/socket/net_close.c @@ -65,7 +65,7 @@ int psock_close(FAR struct socket *psock) /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } @@ -78,7 +78,7 @@ int psock_close(FAR struct socket *psock) * waiting in accept. */ - if (psock->s_conn != NULL) + if (psock->s_crefs <= 1 && psock->s_conn != NULL) { /* Assume that the socket close operation will be successful. Save * the current flags and mark the socket uninitialized. This avoids @@ -109,11 +109,34 @@ int psock_close(FAR struct socket *psock) } } - /* The socket will not persist... reset it */ - - memset(psock, 0, sizeof(*psock)); + /* Then release our reference on the socket structure containing the + * connection. + */ + psock_release(psock); return OK; } +/**************************************************************************** + * Name: net_close + * + * Description: + * Performs the close operation on socket descriptors + * + * Input Parameters: + * sockfd Socket descriptor of socket + * + * Returned Value: + * Returns zero (OK) on success. On failure, it returns a negated errno + * value to indicate the nature of the error. + * + * Assumptions: + * + ****************************************************************************/ + +int net_close(int sockfd) +{ + return psock_close(sockfd_socket(sockfd)); +} + #endif /* CONFIG_NET */ diff --git a/net/socket/net_dup.c b/net/socket/net_dup.c new file mode 100644 index 00000000000..e8a38804de1 --- /dev/null +++ b/net/socket/net_dup.c @@ -0,0 +1,149 @@ +/**************************************************************************** + * net/socket/net_dup.c + * + * Copyright (C) 2009, 2017 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 + +#include +#include +#include +#include + +#include "socket/socket.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: psock_dup + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. + * + * Returned Value: + * On success, returns the number of new socket. On any error, + * a negated errno value is returned. + * + ****************************************************************************/ + +int psock_dup(FAR struct socket *psock, int minsd) +{ + FAR struct socket *psock2; + int sockfd2; + int ret; + + /* Make sure that the minimum socket descriptor is within the legal range. + * The minimum value we receive is relative to file descriptor 0; we need + * map it relative of the first socket descriptor. + */ + + if (minsd >= CONFIG_NFILE_DESCRIPTORS) + { + minsd -= CONFIG_NFILE_DESCRIPTORS; + } + else + { + minsd = 0; + } + + /* Lock the scheduler throughout the following */ + + sched_lock(); + + /* Verify that the sockfd corresponds to valid, allocated socket */ + + if (!psock || psock->s_crefs <= 0) + { + ret = -EBADF; + goto errout; + } + + /* Allocate a new socket descriptor */ + + sockfd2 = sockfd_allocate(minsd); + if (sockfd2 < 0) + { + ret = -ENFILE; + goto errout; + } + + /* Get the socket structure underlying the new descriptor */ + + psock2 = sockfd_socket(sockfd2); + if (!psock2) + { + ret = -ENOSYS; /* Should not happen */ + goto errout_with_sockfd; + } + + /* Duplicate the socket state */ + + ret = psock_dup2(psock, psock2); + if (ret < 0) + { + goto errout_with_sockfd; + } + + sched_unlock(); + return sockfd2; + +errout_with_sockfd: + sockfd_release(sockfd2); + +errout: + sched_unlock(); + return ret; +} + +/**************************************************************************** + * Name: net_dup + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. + * + * Returned Value: + * On success, returns the number of new socket. On any error, + * a negated errno value is returned. + * + ****************************************************************************/ + +int net_dup(int sockfd, int minsd) +{ + return psock_dup(sockfd_socket(sockfd), minsd); +} diff --git a/net/socket/net_dup2.c b/net/socket/net_dup2.c index 4ff03637e5d..1e9f4d25d83 100644 --- a/net/socket/net_dup2.c +++ b/net/socket/net_dup2.c @@ -59,7 +59,7 @@ * Name: psock_dup2 * * Description: - * Performs the low level, common portion of dup + * Performs the low level, common portion of net_dup() and net_dup2() * * Input Parameters: * psock1 - The existing socket that is being cloned. @@ -98,6 +98,10 @@ int psock_dup2(FAR struct socket *psock1, FAR struct socket *psock2) #endif psock2->s_conn = psock1->s_conn; /* UDP or TCP connection structure */ + /* Increment the reference count on the socket */ + + psock2->s_crefs = 1; /* One reference on the new socket itself */ + /* Increment the reference count on the underlying connection structure * for this address family type. */ @@ -136,9 +140,11 @@ int psock_dup2(FAR struct socket *psock1, FAR struct socket *psock2) inet_close(psock2); - /* The socket will not persist... reset it */ + /* Then release our reference on the socket structure containing + * the connection. + */ - memset(psock2, 0, sizeof(*psock2)); + psock_release(psock2); } } #endif @@ -146,3 +152,58 @@ int psock_dup2(FAR struct socket *psock1, FAR struct socket *psock2) net_unlock(); return ret; } + +/**************************************************************************** + * Name: net_dup2 + * + * Description: + * Clone a socket descriptor to an arbitrary descriptor number. + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int net_dup2(int sockfd1, int sockfd2) +{ + FAR struct socket *psock1; + FAR struct socket *psock2; + int ret; + + /* Lock the scheduler throughout the following */ + + sched_lock(); + + /* Get the socket structures underly both descriptors */ + + psock1 = sockfd_socket(sockfd1); + psock2 = sockfd_socket(sockfd2); + + /* Verify that the sockfd1 and sockfd2 both refer to valid socket + * descriptors and that sockfd2 corresponds to an allocated socket + */ + + if (psock1 == NULL || psock2 == NULL || psock1->s_crefs <= 0) + { + ret = -EBADF; + goto errout; + } + + /* If sockfd2 also valid, allocated socket, then we will have to + * close it! + */ + + if (psock2->s_crefs > 0) + { + net_close(sockfd2); + } + + /* Duplicate the socket state */ + + ret = psock_dup2(psock1, psock2); + +errout: + sched_unlock(); + return ret; +} diff --git a/net/socket/net_fstat.c b/net/socket/net_fstat.c index c9ce6cfe61a..46b6788bded 100644 --- a/net/socket/net_fstat.c +++ b/net/socket/net_fstat.c @@ -56,13 +56,13 @@ ****************************************************************************/ /**************************************************************************** - * Name: psock_fstat + * Name: net_fstat * * Description: * Performs fstat operations on socket * * Input Parameters: - * psock - The pointer of the socket to operate on + * sockfd - Socket descriptor of the socket to operate on * buf - Caller-provided location in which to return the fstat data * * Returned Value: @@ -71,11 +71,15 @@ * ****************************************************************************/ -int psock_fstat(FAR struct socket *psock, FAR struct stat *buf) +int net_fstat(int sockfd, FAR struct stat *buf) { + FAR struct socket *psock; int ret = OK; - if (psock == NULL || psock->s_conn == NULL) + /* Get the underlying socket structure */ + + psock = sockfd_socket(sockfd); + if (psock == NULL) { /* sockfd does not refer to a valid, open socket */ diff --git a/net/socket/net_poll.c b/net/socket/net_poll.c index 45747df89f5..11fa7c41ff9 100644 --- a/net/socket/net_poll.c +++ b/net/socket/net_poll.c @@ -77,3 +77,42 @@ int psock_poll(FAR struct socket *psock, FAR struct pollfd *fds, bool setup) DEBUGASSERT(psock->s_sockif != NULL && psock->s_sockif->si_poll != NULL); return psock->s_sockif->si_poll(psock, fds, setup); } + +/**************************************************************************** + * Name: net_poll + * + * Description: + * The standard poll() operation redirects operations on socket descriptors + * to this function. + * + * Input Parameters: + * fd - The socket descriptor of interest + * fds - The structure describing the events to be monitored, OR NULL if + * this is a request to stop monitoring events. + * setup - true: Setup up the poll; false: Teardown the poll + * + * Returned Value: + * 0: Success; Negated errno on failure + * + ****************************************************************************/ + +int net_poll(int sockfd, struct pollfd *fds, bool setup) +{ + FAR struct socket *psock; + + DEBUGASSERT(fds != NULL); + + /* Get the underlying socket structure and verify that the sockfd + * corresponds to valid, allocated socket + */ + + psock = sockfd_socket(sockfd); + if (!psock || psock->s_crefs <= 0) + { + return -EBADF; + } + + /* Then let psock_poll() do the heavy lifting */ + + return psock_poll(psock, fds, setup); +} diff --git a/net/socket/net_sendfile.c b/net/socket/net_sendfile.c index c9407f73095..a499504b126 100644 --- a/net/socket/net_sendfile.c +++ b/net/socket/net_sendfile.c @@ -60,10 +60,10 @@ ****************************************************************************/ /**************************************************************************** - * Name: psock_sendfile + * Name: net_sendfile * * Description: - * The psock_sendfile() call may be used only when the socket is in a + * The net_sendfile() call may be used only when the socket is in a * connected state (so that the intended recipient is known). * * Input Parameters: @@ -118,16 +118,17 @@ * ****************************************************************************/ -ssize_t psock_sendfile(FAR struct socket *psock, FAR struct file *infile, - FAR off_t *offset, size_t count) +ssize_t net_sendfile(int outfd, FAR struct file *infile, FAR off_t *offset, + size_t count) { + FAR struct socket *psock = sockfd_socket(outfd); ssize_t ret = -ENOSYS; DEBUGASSERT(psock != NULL && infile != NULL); /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock != NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); _SO_SETERRNO(psock, EBADF); diff --git a/net/socket/net_sockets.c b/net/socket/net_sockets.c new file mode 100644 index 00000000000..547e283dce3 --- /dev/null +++ b/net/socket/net_sockets.c @@ -0,0 +1,264 @@ +/**************************************************************************** + * net/socket/net_sockets.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "socket/socket.h" + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static void _net_semtake(FAR struct socketlist *list) +{ + net_lockedwait_uninterruptible(&list->sl_sem); +} + +#define _net_semgive(list) nxsem_post(&list->sl_sem) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: net_initlist + * + * Description: + * Initialize a list of sockets for a new task + * + * Input Parameters: + * list -- A reference to the pre-allocated socket list to be initialized. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void net_initlist(FAR struct socketlist *list) +{ + /* Initialize the list access mutex */ + + nxsem_init(&list->sl_sem, 0, 1); +} + +/**************************************************************************** + * Name: net_releaselist + * + * Description: + * Release resources held by the socket list + * + * Input Parameters: + * list - A reference to the pre-allocated socket list to be un- + * initialized. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void net_releaselist(FAR struct socketlist *list) +{ + int ndx; + + DEBUGASSERT(list); + + /* Close each open socket in the list. */ + + for (ndx = 0; ndx < CONFIG_NSOCKET_DESCRIPTORS; ndx++) + { + FAR struct socket *psock = &list->sl_sockets[ndx]; + if (psock->s_crefs > 0) + { + psock_close(psock); + } + } + + /* Destroy the semaphore */ + + nxsem_destroy(&list->sl_sem); +} + +/**************************************************************************** + * Name: sockfd_allocate + * + * Description: + * Allocate a socket descriptor + * + * Input Parameters: + * Lowest socket descriptor index to be used. + * + * Returned Value: + * On success, a socket descriptor >= minsd is returned. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +int sockfd_allocate(int minsd) +{ + FAR struct socketlist *list; + int i; + + /* Get the socket list for this task/thread */ + + list = nxsched_get_sockets(); + if (list) + { + /* Search for a socket structure with no references */ + + _net_semtake(list); + for (i = minsd; i < CONFIG_NSOCKET_DESCRIPTORS; i++) + { + /* Are there references on this socket? */ + + if (!list->sl_sockets[i].s_crefs) + { + /* No take the reference and return the index + an offset + * as the socket descriptor. + */ + + memset(&list->sl_sockets[i], 0, sizeof(struct socket)); + list->sl_sockets[i].s_crefs = 1; + _net_semgive(list); + return i + __SOCKFD_OFFSET; + } + } + + _net_semgive(list); + } + + return ERROR; +} + +/**************************************************************************** + * Name: psock_release + * + * Description: + * Free a socket. + * + * Input Parameters: + * psock - A reference to the socket instance to be freed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void psock_release(FAR struct socket *psock) +{ + if (psock != NULL) + { + /* Decrement the count if there the socket will persist + * after this. + */ + + if (psock->s_crefs > 1) + { + psock->s_crefs--; + } + else + { + /* The socket will not persist... reset it */ + + memset(psock, 0, sizeof(struct socket)); + } + } +} + +/**************************************************************************** + * Name: sockfd_release + * + * Description: + * Free the socket by its socket descriptor. + * + * Input Parameters: + * sockfd - Socket descriptor identifies the socket to be released. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sockfd_release(int sockfd) +{ + /* Get the socket structure for this sockfd */ + + FAR struct socket *psock = sockfd_socket(sockfd); + + if (psock) + { + /* Take the list semaphore so that there will be no accesses + * to this socket structure. + */ + + FAR struct socketlist *list = nxsched_get_sockets(); + if (list) + { + _net_semtake(list); + psock_release(psock); + _net_semgive(list); + } + } +} + +/**************************************************************************** + * Name: sockfd_socket + * + * Description: + * Given a socket descriptor, return the underlying socket structure. + * + * Input Parameters: + * sockfd - The socket descriptor index to use. + * + * Returned Value: + * On success, a reference to the socket structure associated with the + * the socket descriptor is returned. NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct socket *sockfd_socket(int sockfd) +{ + FAR struct socketlist *list; + int ndx = sockfd - __SOCKFD_OFFSET; + + if (ndx >= 0 && ndx < CONFIG_NSOCKET_DESCRIPTORS) + { + list = nxsched_get_sockets(); + if (list) + { + return &list->sl_sockets[ndx]; + } + } + + return NULL; +} diff --git a/net/socket/net_vfcntl.c b/net/socket/net_vfcntl.c index 3a048595ea4..a065e4f5fc0 100644 --- a/net/socket/net_vfcntl.c +++ b/net/socket/net_vfcntl.c @@ -81,7 +81,7 @@ int psock_vfcntl(FAR struct socket *psock, int cmd, va_list ap) /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } @@ -93,6 +93,66 @@ int psock_vfcntl(FAR struct socket *psock, int cmd, va_list ap) net_lock(); switch (cmd) { + case F_DUPFD: + /* Return a new file descriptor which shall be the lowest numbered + * available (that is, not already open) file descriptor greater than + * or equal to the third argument, arg, taken as an integer of type + * int. The new file descriptor shall refer to the same open file + * description as the original file descriptor, and shall share any + * locks. The FD_CLOEXEC flag associated with the new file + * descriptor shall be cleared to keep the file open across calls + * to one of the exec functions. + */ + + { + /* Does not set the errno value on failure */ + + ret = psock_dup(psock, va_arg(ap, int)); + } + break; + + case F_GETFD: + /* Get the file descriptor flags defined in that are + * associated with the file descriptor fd. File descriptor flags + * are associated with a single file descriptor and do not affect + * other file descriptors that refer to the same file. + */ + + { + ret = _SS_ISCLOEXEC(psock->s_flags) ? FD_CLOEXEC : 0; + } + break; + + case F_SETFD: + /* Set the file descriptor flags defined in , that are + * associated with fd, to the third argument, arg, taken as type int. + * If the FD_CLOEXEC flag in the third argument is 0, the file shall + * remain open across the exec functions; otherwise, the file shall + * be closed upon successful execution of one of the exec functions. + */ + + { + int oflags = va_arg(ap, int); + + if (oflags & ~FD_CLOEXEC) + { + ret = -ENOSYS; + break; + } + + if (oflags & FD_CLOEXEC) + { + psock->s_flags |= _SF_CLOEXEC; + } + else + { + psock->s_flags &= ~_SF_CLOEXEC; + } + + ret = OK; + } + break; + case F_GETFL: /* Get the file status flags and file access modes, defined in * , for the file description associated with fd. The file @@ -266,3 +326,25 @@ int psock_fcntl(FAR struct socket *psock, int cmd, ...) va_end(ap); return ret; } + +/**************************************************************************** + * Name: net_vfcntl + * + * Description: + * Performs fcntl operations on socket + * + * Input Parameters: + * sockfd - Socket descriptor of the socket to operate on + * cmd - The fcntl command. + * ap - Command-specific arguments + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure to indicate the nature of the failure. + * + ****************************************************************************/ + +int net_vfcntl(int sockfd, int cmd, va_list ap) +{ + return psock_vfcntl(sockfd_socket(sockfd), cmd, ap); +} diff --git a/net/socket/recvfrom.c b/net/socket/recvfrom.c index 2cfa8fe093d..6a01782c0da 100644 --- a/net/socket/recvfrom.c +++ b/net/socket/recvfrom.c @@ -103,7 +103,7 @@ ssize_t psock_recvfrom(FAR struct socket *psock, FAR void *buf, size_t len, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/recvmsg.c b/net/socket/recvmsg.c index a2354f01bbd..9ba5de30cea 100644 --- a/net/socket/recvmsg.c +++ b/net/socket/recvmsg.c @@ -100,7 +100,7 @@ ssize_t psock_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/send.c b/net/socket/send.c index f50aafe33ba..92b152c7757 100644 --- a/net/socket/send.c +++ b/net/socket/send.c @@ -98,7 +98,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/sendmsg.c b/net/socket/sendmsg.c index 723553602b2..c6513edb5d6 100644 --- a/net/socket/sendmsg.c +++ b/net/socket/sendmsg.c @@ -100,7 +100,7 @@ ssize_t psock_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/sendto.c b/net/socket/sendto.c index 72e4796c6f9..9a70cda821d 100644 --- a/net/socket/sendto.c +++ b/net/socket/sendto.c @@ -143,7 +143,7 @@ ssize_t psock_sendto(FAR struct socket *psock, FAR const void *buf, /* Verify that the psock corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); return -EBADF; diff --git a/net/socket/setsockopt.c b/net/socket/setsockopt.c index 39cb5b6b0f0..74e62a422ab 100644 --- a/net/socket/setsockopt.c +++ b/net/socket/setsockopt.c @@ -361,7 +361,7 @@ int psock_setsockopt(FAR struct socket *psock, int level, int option, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { return -EBADF; } diff --git a/net/socket/socket.c b/net/socket/socket.c index 5041ab4a869..b0ea41f95e1 100644 --- a/net/socket/socket.c +++ b/net/socket/socket.c @@ -84,6 +84,7 @@ int psock_socket(int domain, int type, int protocol, /* Initialize the socket structure */ + psock->s_crefs = 1; psock->s_domain = domain; psock->s_proto = protocol; psock->s_conn = NULL; @@ -91,6 +92,11 @@ int psock_socket(int domain, int type, int protocol, psock->s_sndcb = NULL; #endif + if (type & SOCK_CLOEXEC) + { + psock->s_flags |= _SF_CLOEXEC; + } + if (type & SOCK_NONBLOCK) { psock->s_flags |= _SF_NONBLOCK; @@ -108,38 +114,120 @@ int psock_socket(int domain, int type, int protocol, ret = g_usrsock_sockif.si_setup(psock, protocol); psock->s_sockif = &g_usrsock_sockif; + return ret; } - else #endif /* CONFIG_NET_USRSOCK */ + + /* Get the socket interface */ + + sockif = net_sockif(domain, type, protocol); + if (sockif == NULL) { - /* Get the socket interface */ + nerr("ERROR: socket address family unsupported: %d\n", domain); + return -EAFNOSUPPORT; + } - sockif = net_sockif(domain, type, protocol); - if (sockif == NULL) - { - nerr("ERROR: socket address family unsupported: %d\n", domain); - return -EAFNOSUPPORT; - } + /* The remaining of the socket initialization depends on the address + * family. + */ - /* The remaining of the socket initialization depends on the address - * family. - */ + DEBUGASSERT(sockif->si_setup != NULL); + psock->s_sockif = sockif; - DEBUGASSERT(sockif->si_setup != NULL); - psock->s_sockif = sockif; + ret = sockif->si_setup(psock, protocol); + if (ret < 0) + { + nerr("ERROR: socket si_setup() failed: %d\n", ret); + return ret; + } - ret = sockif->si_setup(psock, protocol); - if (ret < 0) - { - nerr("ERROR: socket si_setup() failed: %d\n", ret); - return ret; - } + return OK; +} + +/**************************************************************************** + * Name: socket + * + * Description: + * socket() creates an endpoint for communication and returns a descriptor. + * + * Input Parameters: + * domain (see sys/socket.h) + * type (see sys/socket.h) + * protocol (see sys/socket.h) + * + * Returned Value: + * A non-negative socket descriptor on success; -1 on error with errno set + * appropriately. + * + * EACCES + * Permission to create a socket of the specified type and/or protocol + * is denied. + * EAFNOSUPPORT + * The implementation does not support the specified address family. + * EINVAL + * Unknown protocol, or protocol family not available. + * EMFILE + * Process file table overflow. + * ENFILE + * The system limit on the total number of open files has been reached. + * ENOBUFS or ENOMEM + * Insufficient memory is available. The socket cannot be created until + * sufficient resources are freed. + * EPROTONOSUPPORT + * The protocol type or the specified protocol is not supported within + * this domain. + * + * Assumptions: + * + ****************************************************************************/ + +int socket(int domain, int type, int protocol) +{ + FAR struct socket *psock; + int errcode; + int sockfd; + int ret; + + /* Allocate a socket descriptor */ + + sockfd = sockfd_allocate(0); + if (sockfd < 0) + { + nerr("ERROR: Failed to allocate a socket descriptor\n"); + errcode = ENFILE; + goto errout; + } + + /* Get the underlying socket structure */ + + psock = sockfd_socket(sockfd); + if (!psock) + { + errcode = ENOSYS; /* should not happen */ + goto errout_with_sockfd; + } + + /* Initialize the socket structure */ + + ret = psock_socket(domain, type, protocol, psock); + if (ret < 0) + { + nerr("ERROR: psock_socket() failed: %d\n", ret); + errcode = -ret; + goto errout_with_sockfd; } /* The socket has been successfully initialized */ psock->s_flags |= _SF_INITD; - return ret; + return sockfd; + +errout_with_sockfd: + sockfd_release(sockfd); + +errout: + set_errno(errcode); + return ERROR; } #endif /* CONFIG_NET */ diff --git a/net/socket/socket.h b/net/socket/socket.h index 841637209da..bd2a0ea792e 100644 --- a/net/socket/socket.h +++ b/net/socket/socket.h @@ -139,6 +139,55 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: sockfd_allocate + * + * Description: + * Allocate a socket descriptor + * + * Input Parameters: + * Lowest socket descriptor index to be used. + * + * Returned Value: + * On success, a socket descriptor >= minsd is returned. A negated errno + * value is returned on failure. + * + ****************************************************************************/ + +int sockfd_allocate(int minsd); + +/**************************************************************************** + * Name: psock_release + * + * Description: + * Free a socket. + * + * Input Parameters: + * psock - A reference to the socket instance to be freed. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void psock_release(FAR struct socket *psock); + +/**************************************************************************** + * Name: sockfd_release + * + * Description: + * Free the socket by its socket descriptor. + * + * Input Parameters: + * sockfd - Socket descriptor identifies the socket to be released. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void sockfd_release(int sockfd); + /**************************************************************************** * Name: net_sockif * @@ -181,10 +230,5 @@ net_sockif(sa_family_t family, int type, int protocol); int net_timeo(clock_t start_time, socktimeo_t timeo); #endif -#undef EXTERN -#if defined(__cplusplus) -} -#endif - #endif /* CONFIG_NET */ #endif /* _NET_SOCKET_SOCKET_H */ diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c index 5cd5be37653..754d42d0894 100644 --- a/net/tcp/tcp_send_buffered.c +++ b/net/tcp/tcp_send_buffered.c @@ -983,7 +983,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, bool nonblock; int ret = OK; - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); ret = -EBADF; @@ -1000,6 +1000,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, FAR const void *buf, /* Make sure that we have the IP address mapping */ conn = (FAR struct tcp_conn_s *)psock->s_conn; + DEBUGASSERT(conn); #if defined(CONFIG_NET_ARP_SEND) || defined(CONFIG_NET_ICMPv6_NEIGHBOR) #ifdef CONFIG_NET_ARP_SEND @@ -1223,7 +1224,7 @@ int psock_tcp_cansend(FAR struct socket *psock) { /* Verify that we received a valid socket */ - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); return -EBADF; diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c index 981ea7c686f..387f3c78d08 100644 --- a/net/tcp/tcp_send_unbuffered.c +++ b/net/tcp/tcp_send_unbuffered.c @@ -593,7 +593,7 @@ ssize_t psock_tcp_send(FAR struct socket *psock, /* Verify that the sockfd corresponds to valid, allocated socket */ - if (psock == NULL || psock->s_conn == NULL) + if (psock == NULL || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); ret = -EBADF; diff --git a/net/tcp/tcp_txdrain.c b/net/tcp/tcp_txdrain.c index 76bb21a4d2d..4993df828fe 100644 --- a/net/tcp/tcp_txdrain.c +++ b/net/tcp/tcp_txdrain.c @@ -105,7 +105,7 @@ int tcp_txdrain(FAR struct socket *psock, unsigned int timeout) sem_t waitsem; int ret; - DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL); DEBUGASSERT(psock->s_type == SOCK_STREAM); conn = (FAR struct tcp_conn_s *)psock->s_conn; diff --git a/net/udp/udp_sendto_buffered.c b/net/udp/udp_sendto_buffered.c index 594df5abd1d..a2511924f14 100644 --- a/net/udp/udp_sendto_buffered.c +++ b/net/udp/udp_sendto_buffered.c @@ -821,7 +821,7 @@ int psock_udp_cansend(FAR struct socket *psock) { /* Verify that we received a valid socket */ - if (!psock || !psock->s_conn) + if (!psock || psock->s_crefs <= 0) { nerr("ERROR: Invalid socket\n"); return -EBADF; diff --git a/net/udp/udp_txdrain.c b/net/udp/udp_txdrain.c index 4a977fea7cd..01b12bb0174 100644 --- a/net/udp/udp_txdrain.c +++ b/net/udp/udp_txdrain.c @@ -105,7 +105,7 @@ int udp_txdrain(FAR struct socket *psock, unsigned int timeout) sem_t waitsem; int ret; - DEBUGASSERT(psock != NULL && psock->s_conn != NULL); + DEBUGASSERT(psock != NULL && psock->s_crefs > 0 && psock->s_conn != NULL); DEBUGASSERT(psock->s_type == SOCK_DGRAM); conn = (FAR struct udp_conn_s *)psock->s_conn; diff --git a/sched/group/group_leave.c b/sched/group/group_leave.c index a3396e001c0..58344748893 100644 --- a/sched/group/group_leave.c +++ b/sched/group/group_leave.c @@ -157,6 +157,12 @@ static inline void group_release(FAR struct task_group_s *group) lib_stream_release(group); #endif /* CONFIG_FILE_STREAM */ +#ifdef CONFIG_NET + /* Free resource held by the socket list */ + + net_releaselist(&group->tg_socketlist); +#endif + #ifndef CONFIG_DISABLE_ENVIRON /* Release all shared environment variables */ diff --git a/sched/group/group_setupidlefiles.c b/sched/group/group_setupidlefiles.c index b990c5ed06e..ff8e8c54ba5 100644 --- a/sched/group/group_setupidlefiles.c +++ b/sched/group/group_setupidlefiles.c @@ -84,6 +84,12 @@ int group_setupidlefiles(FAR struct task_tcb_s *tcb) files_initlist(&group->tg_filelist); +#ifdef CONFIG_NET + /* Allocate socket descriptors for the TCB */ + + net_initlist(&group->tg_socketlist); +#endif + /* Open stdin, dup to get stdout and stderr. This should always * be the first file opened and, hence, should always get file * descriptor 0. diff --git a/sched/group/group_setuptaskfiles.c b/sched/group/group_setuptaskfiles.c index 6a1742d4939..5dff08e6086 100644 --- a/sched/group/group_setuptaskfiles.c +++ b/sched/group/group_setuptaskfiles.c @@ -113,6 +113,65 @@ static inline void sched_dupfiles(FAR struct task_tcb_s *tcb) # define sched_dupfiles(tcb) #endif /* !CONFIG_FDCLONE_DISABLE */ +/**************************************************************************** + * Name: sched_dupsockets + * + * Description: + * Duplicate the parent task's socket descriptors. + * + * Input Parameters: + * tcb - tcb of the new task. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_NET) && !defined(CONFIG_SDCLONE_DISABLE) +static inline void sched_dupsockets(FAR struct task_tcb_s *tcb) +{ + /* The parent task is the one at the head of the ready-to-run list */ + + FAR struct tcb_s *rtcb = this_task(); + FAR struct socket *parent; + FAR struct socket *child; + int i; + + /* Duplicate the socket descriptors of all sockets opened by the parent + * task. + */ + + DEBUGASSERT(tcb && tcb->cmn.group && rtcb->group); + + /* Get pointers to the parent and child task socket lists */ + + parent = rtcb->group->tg_socketlist.sl_sockets; + child = tcb->cmn.group->tg_socketlist.sl_sockets; + + /* Check each socket in the parent socket list */ + + for (i = 0; i < CONFIG_NSOCKET_DESCRIPTORS; i++) + { + /* Check if this parent socket is valid. Valid means both (1) + * allocated and (2) successfully initialized. A complexity in SMP + * mode is that a socket my be allocated, but not yet initialized when + * the socket is cloned by another pthread. + * + * Sockets with the close-on-exec flag set should not be cloned either. + */ + + if (_PS_VALID(&parent[i]) && !_SS_ISCLOEXEC(parent[i].s_flags)) + { + /* Yes... duplicate it for the child */ + + psock_dup2(&parent[i], &child[i]); + } + } +} +#else /* CONFIG_NET && !CONFIG_SDCLONE_DISABLE */ +# define sched_dupsockets(tcb) +#endif /* CONFIG_NET && !CONFIG_SDCLONE_DISABLE */ + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -149,10 +208,20 @@ int group_setuptaskfiles(FAR struct task_tcb_s *tcb) files_initlist(&group->tg_filelist); +#ifdef CONFIG_NET + /* Allocate socket descriptors for the TCB */ + + net_initlist(&group->tg_socketlist); +#endif + /* Duplicate the parent task's file descriptors */ sched_dupfiles(tcb); + /* Duplicate the parent task's socket descriptors */ + + sched_dupsockets(tcb); + /* Allocate file/socket streams for the new TCB */ #ifdef CONFIG_FILE_STREAM diff --git a/sched/sched/Make.defs b/sched/sched/Make.defs index d42d37985e8..f46c39db7ae 100644 --- a/sched/sched/Make.defs +++ b/sched/sched/Make.defs @@ -23,7 +23,7 @@ CSRCS += sched_addreadytorun.c sched_removereadytorun.c CSRCS += sched_addprioritized.c sched_mergeprioritized.c sched_mergepending.c CSRCS += sched_addblocked.c sched_removeblocked.c CSRCS += sched_gettcb.c sched_verifytcb.c sched_releasetcb.c -CSRCS += sched_getstreams.c +CSRCS += sched_getsockets.c sched_getstreams.c CSRCS += sched_setparam.c sched_setpriority.c sched_getparam.c CSRCS += sched_setscheduler.c sched_getscheduler.c CSRCS += sched_yield.c sched_rrgetinterval.c sched_foreach.c diff --git a/sched/sched/sched_getsockets.c b/sched/sched/sched_getsockets.c new file mode 100644 index 00000000000..b29421d9c49 --- /dev/null +++ b/sched/sched/sched_getsockets.c @@ -0,0 +1,59 @@ +/**************************************************************************** + * sched/sched/sched_getsockets.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include "sched/sched.h" + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxsched_get_sockets + * + * Description: + * Return a pointer to the socket list for this thread + * + * Input Parameters: + * None + * + * Returned Value: + * A pointer to the errno. + * + * Assumptions: + * + ****************************************************************************/ + +#ifdef CONFIG_NET +FAR struct socketlist *nxsched_get_sockets(void) +{ + FAR struct tcb_s *rtcb = this_task(); + FAR struct task_group_s *group = rtcb->group; + + DEBUGASSERT(group); + return &group->tg_socketlist; +} +#endif