nuttx/net/local/local_sendmsg.c

451 lines
12 KiB
C
Raw Normal View History

/****************************************************************************
* net/local/local_sendmsg.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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 <nuttx/config.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/net/net.h>
#include "socket/socket.h"
#include "local/local.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: local_sendctl
*
* Description:
* Handle the socket message conntrol field
*
* Input Parameters:
* conn Local connection instance
* msg Message to send
*
* Returned Value:
* On any failure, a negated errno value is returned
*
****************************************************************************/
#ifdef CONFIG_NET_LOCAL_SCM
static void local_freectl(FAR struct local_conn_s *conn, int count)
{
FAR struct local_conn_s *peer = conn->lc_peer;
while (count-- > 0)
{
net/local: simplify file descriptor passing using shared filep references This commit removes unnecessary file duplication and memory allocation when passing file descriptors through Unix domain sockets, leveraging the new filep reference counting framework. Background: With the new filep framework, file structures (filep) can be safely shared across processes using reference counting. When passing file descriptors through SCM_RIGHTS control messages on local sockets, the previous implementation unnecessarily duplicated the entire file structure. Changes: 1. In local_sendctl() (sender side): - Removed allocation of filep2 structure (kmm_zalloc) - Removed file_dup2() call that copied the file structure - Now directly stores the original filep with its reference count - Eliminates memory allocation overhead and potential failure points 2. In local_recvctl() (receiver side): - Changed from file_close() + kmm_free() to file_put() - file_put() properly decrements reference count and handles cleanup - Consistent with the new filep reference counting model 3. In local_freectl() (cleanup on error): - Changed from file_close() + kmm_free() to file_put() - Ensures proper reference count management during error paths Technical Details: The new filep framework ensures that: - Multiple file descriptors across different processes can reference the same underlying filep structure safely - Reference counting (via file_get/file_put) manages lifetime correctly - The underlying file object is only released when all references are gone This change is safe because file_dup() in the receiver already calls file_get() internally to increment the reference count for the new fd, so the filep remains valid after the sender calls file_put(). Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
2025-11-06 15:56:59 +08:00
file_put(peer->lc_cfps[--peer->lc_cfpcount]);
peer->lc_cfps[peer->lc_cfpcount] = NULL;
}
}
static int local_sendctl(FAR struct local_conn_s *conn,
FAR struct msghdr *msg)
{
FAR struct local_conn_s *peer;
FAR struct file *filep;
FAR struct cmsghdr *cmsg;
int count = 0;
FAR int *fds;
int ret;
int i = 0;
local_lock();
peer = conn->lc_peer;
if (peer == NULL)
{
peer = conn;
}
for_each_cmsghdr(cmsg, msg)
{
if (!CMSG_OK(msg, cmsg) ||
cmsg->cmsg_level != SOL_SOCKET ||
cmsg->cmsg_type != SCM_RIGHTS)
{
ret = -EOPNOTSUPP;
goto fail;
}
fds = (FAR int *)CMSG_DATA(cmsg);
count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
if (count + peer->lc_cfpcount >= LOCAL_NCONTROLFDS)
{
ret = -EMFILE;
goto fail;
}
for (i = 0; i < count; i++)
{
ret = file_get(fds[i], &filep);
if (ret < 0)
{
goto fail;
}
net/local: simplify file descriptor passing using shared filep references This commit removes unnecessary file duplication and memory allocation when passing file descriptors through Unix domain sockets, leveraging the new filep reference counting framework. Background: With the new filep framework, file structures (filep) can be safely shared across processes using reference counting. When passing file descriptors through SCM_RIGHTS control messages on local sockets, the previous implementation unnecessarily duplicated the entire file structure. Changes: 1. In local_sendctl() (sender side): - Removed allocation of filep2 structure (kmm_zalloc) - Removed file_dup2() call that copied the file structure - Now directly stores the original filep with its reference count - Eliminates memory allocation overhead and potential failure points 2. In local_recvctl() (receiver side): - Changed from file_close() + kmm_free() to file_put() - file_put() properly decrements reference count and handles cleanup - Consistent with the new filep reference counting model 3. In local_freectl() (cleanup on error): - Changed from file_close() + kmm_free() to file_put() - Ensures proper reference count management during error paths Technical Details: The new filep framework ensures that: - Multiple file descriptors across different processes can reference the same underlying filep structure safely - Reference counting (via file_get/file_put) manages lifetime correctly - The underlying file object is only released when all references are gone This change is safe because file_dup() in the receiver already calls file_get() internally to increment the reference count for the new fd, so the filep remains valid after the sender calls file_put(). Signed-off-by: dongjiuzhu1 <dongjiuzhu1@xiaomi.com>
2025-11-06 15:56:59 +08:00
peer->lc_cfps[peer->lc_cfpcount++] = filep;
}
}
local_unlock();
return count;
fail:
local_freectl(conn, i);
local_unlock();
return ret;
}
#endif /* CONFIG_NET_LOCAL_SCM */
/****************************************************************************
* Name: local_send
*
* Description:
* Send a local packet as a stream.
*
* Input Parameters:
* psock An instance of the internal socket structure.
* buf Data to send
* len Length of data to send
* flags Send flags (ignored for now)
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* -1 is returned, and errno is set appropriately (see send() for the
* list of errno numbers).
*
****************************************************************************/
static ssize_t local_send(FAR struct socket *psock,
FAR const struct iovec *buf,
size_t len, int flags)
{
ssize_t ret;
switch (psock->s_type)
{
#ifdef CONFIG_NET_LOCAL_STREAM
case SOCK_STREAM:
#endif /* CONFIG_NET_LOCAL_STREAM */
#ifdef CONFIG_NET_LOCAL_DGRAM
case SOCK_DGRAM:
#endif /* CONFIG_NET_LOCAL_DGRAM */
{
FAR struct local_conn_s *conn = psock->s_conn;
/* Local TCP packet send */
DEBUGASSERT(buf);
/* Verify that this is a connected peer socket and that it has
* opened the outgoing FIFO for write-only access.
*/
if (conn->lc_state != LOCAL_STATE_CONNECTED)
{
nerr("ERROR: not connected\n");
return -ENOTCONN;
}
/* Check shutdown state */
if (conn->lc_outfile.f_inode == NULL)
{
return -EPIPE;
}
/* Send the packet */
ret = nxmutex_lock(&conn->lc_sendlock);
if (ret < 0)
{
/* May fail because the task was canceled. */
return ret;
}
ret = local_send_packet(&conn->lc_outfile, buf, len);
nxmutex_unlock(&conn->lc_sendlock);
}
break;
default:
{
/* EDESTADDRREQ. Signifies that the socket is not connection-mode
* and no peer address is set.
*/
ret = -EDESTADDRREQ;
}
break;
}
return ret;
}
/****************************************************************************
* Name: local_sendto
*
* Description:
* This function implements the Unix domain-specific logic of the
* standard sendto() socket operation.
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* buf Data to send
* len Length of data to send
* flags Send flags
* to Address of recipient
* tolen The length of the address structure
*
* NOTE: All input parameters were verified by sendto() before this
* function was called.
*
* Returned Value:
* On success, returns the number of characters sent. On error,
* a negated errno value is returned. See the description in
* net/socket/sendto.c for the list of appropriate return value.
*
****************************************************************************/
static ssize_t local_sendto(FAR struct socket *psock,
FAR const struct iovec *buf,
size_t len, int flags,
FAR const struct sockaddr *to,
socklen_t tolen)
{
#ifdef CONFIG_NET_LOCAL_DGRAM
FAR struct local_conn_s *conn = psock->s_conn;
FAR struct local_conn_s *server;
FAR const struct sockaddr_un *unaddr = (FAR const struct sockaddr_un *)to;
ssize_t ret;
/* Verify that a valid address has been provided */
if (to->sa_family != AF_LOCAL || tolen < sizeof(sa_family_t))
{
nerr("ERROR: Unrecognized address family: %d\n",
to->sa_family);
return -EAFNOSUPPORT;
}
/* At present, only standard pathname type address are support */
if (tolen < sizeof(sa_family_t) + 2)
{
/* EFAULT
* - An invalid user space address was specified for a parameter
*/
return -EFAULT;
}
/* If this is a connected socket, then return EISCONN */
if (psock->s_type != SOCK_DGRAM)
{
nerr("ERROR: Connected socket\n");
return -EISCONN;
}
/* Verify that this is not a connected peer socket. It need not be
* bound, however. If unbound, recvfrom will see this as a nameless
* connection.
*/
if (conn->lc_state != LOCAL_STATE_UNBOUND &&
conn->lc_state != LOCAL_STATE_BOUND)
{
/* Either not bound to address or it is connected */
nerr("ERROR: Connected state\n");
return -EISCONN;
}
local_lock();
server = local_findconn(conn, unaddr);
if (server == NULL)
{
local_unlock();
nerr("ERROR: No such file or directory\n");
return -ENOENT;
}
local_unlock();
/* Make sure that dgram is sent safely */
ret = nxmutex_lock(&conn->lc_sendlock);
if (ret < 0)
{
/* May fail because the task was canceled. */
nerr("ERROR: Failed to get localsocket sendlock: %zd\n", ret);
return ret;
}
/* The outgoing FIFO should not be open */
DEBUGASSERT(conn->lc_outfile.f_inode == NULL);
/* Make sure that half duplex FIFO has been created.
* REVISIT: Or should be just make sure that it already exists?
*/
ret = local_create_halfduplex(conn, unaddr->sun_path, server->lc_rcvsize);
if (ret < 0)
{
nerr("ERROR: Failed to create FIFO for %s: %zd\n",
unaddr->sun_path, ret);
goto errout_with_lock;
}
/* Open the sending side of the transfer */
ret = local_open_sender(conn, unaddr->sun_path,
_SS_ISNONBLOCK(conn->lc_conn.s_flags) ||
(flags & MSG_DONTWAIT) != 0);
if (ret < 0)
{
nerr("ERROR: Failed to open FIFO for %s: %zd\n",
unaddr->sun_path, ret);
goto errout_with_halfduplex;
}
/* Send the preamble */
ret = local_send_preamble(conn, &conn->lc_outfile, buf, len,
server->lc_rcvsize);
if (ret < 0)
{
nerr("ERROR: Failed to send the preamble: %zd\n", ret);
goto errout_with_sender;
}
/* Send the packet */
ret = local_send_packet(&conn->lc_outfile, buf, len);
if (ret < 0)
{
nerr("ERROR: Failed to send the packet: %zd\n", ret);
goto errout_with_sender;
}
errout_with_sender:
/* Now we can close the write-only socket descriptor */
file_close(&conn->lc_outfile);
conn->lc_outfile.f_inode = NULL;
errout_with_halfduplex:
2019-10-25 11:31:42 -06:00
/* Release our reference to the half duplex FIFO */
local_release_halfduplex(conn);
errout_with_lock:
/* Release localsocket sendlock */
nxmutex_unlock(&conn->lc_sendlock);
return ret;
#else
return -EISCONN;
#endif /* CONFIG_NET_LOCAL_DGRAM */
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: local_sendmsg
*
* Description:
* Implements the sendmsg() operation for the case of the local Unix socket
*
* Input Parameters:
* psock A pointer to a NuttX-specific, internal socket structure
* msg msg to send
* flags Send flags
*
* Returned Value:
* On success, returns the number of characters sent. On error, a negated
* errno value is returned (see sendmsg() for the list of appropriate error
* values.
*
****************************************************************************/
ssize_t local_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
int flags)
{
FAR const struct sockaddr *to = msg->msg_name;
FAR const struct iovec *buf = msg->msg_iov;
socklen_t tolen = msg->msg_namelen;
size_t len = msg->msg_iovlen;
#ifdef CONFIG_NET_LOCAL_SCM
FAR struct local_conn_s *conn = psock->s_conn;
int count = 0;
if (msg->msg_control &&
msg->msg_controllen > sizeof(struct cmsghdr))
{
count = local_sendctl(conn, msg);
if (count < 0)
{
return count;
}
}
len = to ? local_sendto(psock, buf, len, flags, to, tolen) :
local_send(psock, buf, len, flags);
if (len < 0 && count > 0)
{
local_lock();
local_freectl(conn, count);
local_unlock();
}
#else
len = to ? local_sendto(psock, buf, len, flags, to, tolen) :
local_send(psock, buf, len, flags);
#endif
return len;
}