Skip to content

Commit 10418bd

Browse files
committed
feat: Add function for sending fd
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
1 parent 330920f commit 10418bd

4 files changed

Lines changed: 170 additions & 0 deletions

File tree

libs/common/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ pfl_add_library(
1616
src/linglong/common/display.h
1717
src/linglong/common/error.h
1818
src/linglong/common/error.cpp
19+
src/linglong/common/socket.cpp
20+
src/linglong/common/socket.h
1921
src/linglong/common/strings.cpp
2022
src/linglong/common/strings.h
2123
src/linglong/common/xdg.cpp
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
#include "linglong/common/socket.h"
6+
7+
#include "linglong/common/error.h"
8+
9+
#include <sys/ioctl.h>
10+
11+
#include <cstring>
12+
13+
#include <sys/socket.h>
14+
#include <unistd.h>
15+
16+
namespace linglong::common::socket {
17+
18+
tl::expected<SocketData, std::string> recvFdWithPayload(int socketFd, std::size_t bufSize)
19+
{
20+
if (socketFd < 0) {
21+
return tl::make_unexpected("Invalid file descriptor");
22+
}
23+
24+
std::string buffer(bufSize, '\0');
25+
struct iovec iov{};
26+
iov.iov_base = buffer.data();
27+
iov.iov_len = buffer.size();
28+
29+
alignas(struct cmsghdr) std::array<char, CMSG_SPACE(sizeof(int))> control_buf{};
30+
31+
struct msghdr msg{};
32+
msg.msg_iov = &iov;
33+
msg.msg_iovlen = 1;
34+
msg.msg_control = control_buf.data();
35+
msg.msg_controllen = sizeof(control_buf);
36+
37+
ssize_t n;
38+
while (true) {
39+
n = recvmsg(socketFd, &msg, 0);
40+
if (n < 0 && errno == EINTR) {
41+
continue;
42+
}
43+
44+
break;
45+
}
46+
47+
if (n < 0) {
48+
return tl::make_unexpected("recvmsg failed: " + error::errorString(errno));
49+
}
50+
51+
if (n == 0) {
52+
return tl::make_unexpected("Connection closed");
53+
}
54+
55+
int received_fd{ -1 };
56+
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
57+
58+
if (cmsg != nullptr && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
59+
std::memcpy(&received_fd, CMSG_DATA(cmsg), sizeof(int));
60+
}
61+
62+
if ((static_cast<std::size_t>(msg.msg_flags) & MSG_CTRUNC) != 0) {
63+
if (received_fd != -1) {
64+
::close(received_fd);
65+
}
66+
67+
return tl::make_unexpected("Control data truncated");
68+
}
69+
70+
if (received_fd == -1) {
71+
return tl::make_unexpected("No file descriptor received");
72+
}
73+
74+
bool isTruncated{ false };
75+
if ((static_cast<std::size_t>(msg.msg_flags) & MSG_TRUNC) != 0) {
76+
isTruncated = true;
77+
} else if (static_cast<std::size_t>(n) == bufSize) {
78+
int bytes_available{ 0 };
79+
if (ioctl(socketFd, FIONREAD, &bytes_available) < 0) {
80+
return tl::make_unexpected("FIONREAD socket failed: " + error::errorString(errno));
81+
}
82+
83+
if (bytes_available > 0) {
84+
isTruncated = true;
85+
}
86+
}
87+
88+
buffer.resize(n);
89+
return SocketData{ isTruncated, received_fd, std::move(buffer) };
90+
}
91+
92+
tl::expected<void, std::string> sendFdWithPayload(int socketFd, int fd, const std::string &payload)
93+
{
94+
if (socketFd < 0) {
95+
return tl::make_unexpected("Invalid socket file descriptor");
96+
}
97+
98+
std::size_t totalSent{ 0 };
99+
bool fdSent{ false };
100+
101+
while (totalSent < payload.size() || !fdSent) {
102+
struct msghdr msg{};
103+
struct iovec iov{};
104+
105+
iov.iov_base = const_cast<char *>(payload.data() + totalSent);
106+
iov.iov_len = payload.size() - totalSent;
107+
msg.msg_iov = &iov;
108+
msg.msg_iovlen = 1;
109+
110+
alignas(struct cmsghdr) std::array<std::byte, CMSG_SPACE(sizeof(int))> control_buf{};
111+
if (!fdSent) {
112+
msg.msg_control = control_buf.data();
113+
msg.msg_controllen = control_buf.size();
114+
115+
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
116+
cmsg->cmsg_level = SOL_SOCKET;
117+
cmsg->cmsg_type = SCM_RIGHTS;
118+
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
119+
std::memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
120+
}
121+
122+
const auto n = sendmsg(socketFd, &msg, 0);
123+
if (n < 0) {
124+
if (errno == EINTR) {
125+
continue;
126+
}
127+
128+
return tl::make_unexpected("sendmsg failed: " + error::errorString(errno));
129+
}
130+
131+
fdSent = true;
132+
totalSent += n;
133+
134+
if (payload.empty()) {
135+
break;
136+
}
137+
}
138+
139+
return {};
140+
}
141+
142+
} // namespace linglong::common::socket
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
2+
//
3+
// SPDX-License-Identifier: LGPL-3.0-or-later
4+
5+
#pragma once
6+
7+
#include <tl/expected.hpp>
8+
9+
#include <string>
10+
11+
namespace linglong::common::socket {
12+
13+
struct SocketData
14+
{
15+
bool more_data{false};
16+
int fd;
17+
std::string payload;
18+
};
19+
20+
tl::expected<SocketData, std::string> recvFdWithPayload(int socketFd, std::size_t bufSize = 4096);
21+
22+
tl::expected<void, std::string> sendFdWithPayload(int socketFd, int fd, const std::string &payload);
23+
24+
} // namespace linglong::common::socket

libs/linglong/tests/ll-tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pfl_add_executable(
1717
src/linglong/builder/source_fetcher_test.cpp
1818
src/linglong/cli/cli_test.cpp
1919
src/linglong/common/strings_test.cpp
20+
src/linglong/common/socket_test.cpp
2021
src/linglong/common/xdg_test.cpp
2122
src/linglong/mocks/layer_packager_mock.h
2223
src/linglong/mocks/linglong_builder_mock.h
@@ -59,6 +60,7 @@ pfl_add_executable(
5960
src/linglong/utils/xdg/directory_test.cpp
6061
src/main.cpp
6162

63+
6264
# ll-driver-detect tests
6365
${PROJECT_SOURCE_DIR}/apps/ll-driver-detect/src/nvidia_driver_detector.cpp
6466
${PROJECT_SOURCE_DIR}/apps/ll-driver-detect/src/driver_detection_config.cpp

0 commit comments

Comments
 (0)