-
Notifications
You must be signed in to change notification settings - Fork 65
feat: Add function for sending fd #1561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. | ||
| // | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
|
|
||
| #include "linglong/common/error.h" | ||
|
|
||
| #include <system_error> | ||
|
|
||
| namespace linglong::common::error { | ||
|
|
||
| std::string errorString(int err) | ||
| { | ||
| return std::system_category().message(err); | ||
| } | ||
|
|
||
| } // namespace linglong::common::error |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. | ||
| // | ||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <string> | ||
|
|
||
| namespace linglong::common::error { | ||
|
|
||
| std::string errorString(int err); | ||
|
|
||
| } // namespace linglong::common::error |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,142 @@ | ||||||||||||
| // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. | ||||||||||||
| // | ||||||||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||||||||||
|
|
||||||||||||
| #include "linglong/common/socket.h" | ||||||||||||
|
|
||||||||||||
| #include "linglong/common/error.h" | ||||||||||||
|
|
||||||||||||
| #include <sys/ioctl.h> | ||||||||||||
|
|
||||||||||||
| #include <cstring> | ||||||||||||
|
|
||||||||||||
| #include <sys/socket.h> | ||||||||||||
| #include <unistd.h> | ||||||||||||
|
|
||||||||||||
| namespace linglong::common::socket { | ||||||||||||
|
|
||||||||||||
| tl::expected<SocketData, std::string> recvFdWithPayload(int socketFd, std::size_t bufSize) | ||||||||||||
| { | ||||||||||||
| if (socketFd < 0) { | ||||||||||||
| return tl::make_unexpected("Invalid file descriptor"); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| std::string buffer(bufSize, '\0'); | ||||||||||||
| struct iovec iov{}; | ||||||||||||
| iov.iov_base = buffer.data(); | ||||||||||||
| iov.iov_len = buffer.size(); | ||||||||||||
|
|
||||||||||||
| alignas(struct cmsghdr) std::array<char, CMSG_SPACE(sizeof(int))> control_buf{}; | ||||||||||||
|
|
||||||||||||
| struct msghdr msg{}; | ||||||||||||
| msg.msg_iov = &iov; | ||||||||||||
| msg.msg_iovlen = 1; | ||||||||||||
| msg.msg_control = control_buf.data(); | ||||||||||||
| msg.msg_controllen = sizeof(control_buf); | ||||||||||||
|
|
||||||||||||
| ssize_t n; | ||||||||||||
| while (true) { | ||||||||||||
| n = recvmsg(socketFd, &msg, 0); | ||||||||||||
| if (n < 0 && errno == EINTR) { | ||||||||||||
| continue; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (n < 0) { | ||||||||||||
| return tl::make_unexpected("recvmsg failed: " + error::errorString(errno)); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (n == 0) { | ||||||||||||
| return tl::make_unexpected("Connection closed"); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| int received_fd{ -1 }; | ||||||||||||
| struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); | ||||||||||||
|
|
||||||||||||
| if (cmsg != nullptr && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { | ||||||||||||
| std::memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int)); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if ((static_cast<std::size_t>(msg.msg_flags) & MSG_CTRUNC) != 0) { | ||||||||||||
| if (received_fd != -1) { | ||||||||||||
| ::close(received_fd); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return tl::make_unexpected("Control data truncated"); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (received_fd == -1) { | ||||||||||||
| return tl::make_unexpected("No file descriptor received"); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| bool isTruncated{ false }; | ||||||||||||
| if ((static_cast<std::size_t>(msg.msg_flags) & MSG_TRUNC) != 0) { | ||||||||||||
| isTruncated = true; | ||||||||||||
| } else if (static_cast<std::size_t>(n) == bufSize) { | ||||||||||||
| int bytes_available{ 0 }; | ||||||||||||
| if (ioctl(socketFd, FIONREAD, &bytes_available) < 0) { | ||||||||||||
| return tl::make_unexpected("FIONREAD socket failed: " + error::errorString(errno)); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (bytes_available > 0) { | ||||||||||||
| isTruncated = true; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| buffer.resize(n); | ||||||||||||
| return SocketData{ isTruncated, received_fd, std::move(buffer) }; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| tl::expected<void, std::string> sendFdWithPayload(int socketFd, int fd, const std::string &payload) | ||||||||||||
| { | ||||||||||||
|
||||||||||||
| { | |
| { | |
| if (socketFd < 0) { | |
| return tl::make_unexpected("Invalid socket file descriptor"); | |
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,24 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // SPDX-License-Identifier: LGPL-3.0-or-later | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #pragma once | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <tl/expected.hpp> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <string> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| namespace linglong::common::socket { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct SocketData | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool more_data{ false }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int fd; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::string payload; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tl::expected<SocketData, std::string> recvFdWithPayload(int socketFd, std::size_t bufSize = 4096); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
+21
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tl::expected<SocketData, std::string> recvFdWithPayload(int fd, std::size_t bufSize = 4096); | |
| /** | |
| * @brief Receives a file descriptor and an optional payload from a socket. | |
| * | |
| * This function expects the peer to send one file descriptor along with an | |
| * associated payload (typically a small control message) over a socket | |
| * that supports passing file descriptors (e.g. a UNIX domain socket). | |
| * | |
| * @param fd | |
| * A valid, connected socket file descriptor to receive from. | |
| * @param bufSize | |
| * Maximum number of bytes to read for the payload. If the incoming | |
| * payload is larger than this value, the behavior is implementation | |
| * defined (e.g. it may be truncated or treated as an error). The | |
| * default buffer size is 4096 bytes. | |
| * | |
| * @return tl::expected<SocketData, std::string> | |
| * - On success, contains a SocketData instance with the received file | |
| * descriptor and payload string. The caller is responsible for | |
| * managing (and eventually closing) the received file descriptor. | |
| * - On failure, contains a human-readable error message describing the | |
| * problem (for example, an invalid socket, protocol error, or system | |
| * call failure). | |
| */ | |
| tl::expected<SocketData, std::string> recvFdWithPayload(int fd, std::size_t bufSize = 4096); | |
| /** | |
| * @brief Sends a file descriptor and an associated payload over a socket. | |
| * | |
| * This function sends the given file descriptor together with the provided | |
| * payload (typically a small control message) over a socket that supports | |
| * passing file descriptors (e.g. a UNIX domain socket). | |
| * | |
| * @param socketFd | |
| * A valid, connected socket file descriptor to send on. | |
| * @param fd | |
| * The file descriptor to send to the peer. Ownership of this file | |
| * descriptor is not transferred by this function; the caller remains | |
| * responsible for closing it unless otherwise specified by the | |
| * surrounding protocol. | |
| * @param payload | |
| * A string payload to send alongside the file descriptor. Very large | |
| * payloads may fail depending on the underlying implementation or OS | |
| * limits. | |
| * | |
| * @return tl::expected<void, std::string> | |
| * - On success, contains no value (i.e. an engaged expected<void>). | |
| * - On failure, contains a human-readable error message describing the | |
| * problem (for example, an invalid argument, protocol error, or | |
| * system call failure). | |
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If cmsg is nullptr, the function returns an error but the received_fd is not closed. However, in this specific case, if there's no control message header, no file descriptor was received, so received_fd remains -1 and doesn't need to be closed. This is correct but could benefit from a code comment explaining this for maintainability.