Files
zephyr/drivers/can/can_native_linux_adapt.c
Alberto Escolar Piedras 01118e2657 drivers can_native_linux: Set socket to be closed on exec
If the process does an exec() (or fork, or..) all descriptors are kept
open by default, unless O_CLOEXEC is set for them.
(This default behaviour is usefull for stdin/out/err so that new process
is kept connected to them, but it is very rare for it to be usefull for
any other descriptor)

In general this leads to descriptors being kept open unnecessarily,
which either will block other process from using them (for example
if the child survives the parent but it does something else while keeping
a port open).
Or for a "leak" which unnecessarily uses descriptors and memory in the
child process.

Let's ensure we do not leak it for this component as we do not need it.

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
2025-10-02 22:00:02 +02:00

154 lines
3.1 KiB
C

/*
* Copyright (c) 2019 Intel Corporation
* Copyright (c) 2022 Martin Jäger <martin@libre.solar>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Routines setting up the host system. Those are placed in separate file
* because there is naming conflicts between host and zephyr network stacks.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
/* Linux host include files. */
#ifdef __linux
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#else
#error "This driver can only be built on Linux systems"
#endif
#include "can_native_linux_adapt.h"
#ifndef CANFD_FDF
/* Linux kernels before v5.14 do not define CANFD_FDF */
#define CANFD_FDF 0x04
#endif /* CANFD_FDF */
int linux_socketcan_iface_open(const char *if_name)
{
struct sockaddr_can addr;
struct ifreq ifr;
int fd, opt, ret = -EINVAL;
fd = socket(PF_CAN, SOCK_RAW | SOCK_CLOEXEC, CAN_RAW);
if (fd < 0) {
return -errno;
}
(void)memset(&ifr, 0, sizeof(ifr));
(void)memset(&addr, 0, sizeof(addr));
strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
ret = ioctl(fd, SIOCGIFINDEX, (void *)&ifr);
if (ret < 0) {
close(fd);
return -errno;
}
addr.can_ifindex = ifr.ifr_ifindex;
addr.can_family = PF_CAN;
ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
close(fd);
return -errno;
}
/* this option must always be enabled in order to receive TX confirmations */
opt = 1;
ret = setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &opt, sizeof(opt));
if (ret < 0) {
close(fd);
return -errno;
}
return fd;
}
int linux_socketcan_iface_close(int fd)
{
return close(fd);
}
int linux_socketcan_poll_data(int fd)
{
struct timeval timeout;
fd_set rset;
int ret;
FD_ZERO(&rset);
FD_SET(fd, &rset);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
ret = select(fd + 1, &rset, NULL, NULL, &timeout);
if (ret < 0 && errno != EINTR) {
return -errno;
} else if (ret > 0) {
if (FD_ISSET(fd, &rset)) {
return 0;
}
}
return -EAGAIN;
}
int linux_socketcan_read_data(int fd, void *buf, size_t buf_len, bool *msg_confirm)
{
struct canfd_frame *frame = (struct canfd_frame *)buf;
struct msghdr msg = {0};
struct iovec iov = {
.iov_base = buf,
.iov_len = buf_len,
};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
int ret = (int)recvmsg(fd, &msg, MSG_WAITALL);
if (msg_confirm != NULL) {
*msg_confirm = (msg.msg_flags & MSG_CONFIRM) != 0;
}
/* Make sure to set the flags for all frames received via the Linux API.
*
* Zephyr relies on defined flags field of the SocketCAN data for both FD and classical CAN
* frames. In Linux the flags field is undefined for legacy frames.
*/
if (ret == CANFD_MTU) {
frame->flags |= CANFD_FDF;
} else if (ret == CAN_MTU) {
frame->flags = 0;
}
return ret;
}
int linux_socketcan_set_mode_fd(int fd, bool mode_fd)
{
int opt = mode_fd ? 1 : 0;
return setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &opt, sizeof(opt));
}