Skip to content

Commit 4e1c938

Browse files
ComixHedengbo11
authored andcommitted
feat: Add function for sending fd
Signed-off-by: ComixHe <ComixHe1895@outlook.com>
1 parent 876e7d7 commit 4e1c938

4 files changed

Lines changed: 177 additions & 19 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

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

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

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

libs/linglong/tests/ll-tests/src/linglong/common/socket_test.cpp

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,33 @@ TEST_F(SocketFdTest, InvalidFileDescriptor)
7474

7575
TEST_F(SocketFdTest, EmptyAncillaryData)
7676
{
77-
if (fork() == 0) {
77+
auto child = fork();
78+
ASSERT_NE(child, -1);
79+
if (child == 0) {
7880
close(sv[1]);
7981
std::string data = "just-text-no-fd";
8082
ASSERT_NE(write(sv[0], data.data(), data.size()), -1);
8183
close(sv[0]);
82-
_exit(0);
84+
_exit(EXIT_SUCCESS);
8385
}
8486

8587
close(sv[0]);
8688
auto res = recvFdWithPayload(sv[1], 1024);
8789
EXPECT_FALSE(res.has_value());
88-
wait(nullptr);
90+
waitpid(child, nullptr, 0);
8991
}
9092

9193
TEST_F(SocketFdTest, PayloadBufferTruncation)
9294
{
9395
const std::string long_payload(200, 'Z');
94-
if (fork() == 0) {
96+
auto child = fork();
97+
ASSERT_NE(child, -1);
98+
if (child == 0) {
9599
close(sv[1]);
96100
auto ret = sendFdWithPayload(sv[0], STDOUT_FILENO, long_payload);
97101
EXPECT_TRUE(ret) << ret.error();
98102
close(sv[0]);
99-
_exit(0);
103+
_exit(EXIT_SUCCESS);
100104
}
101105
close(sv[0]);
102106

@@ -109,23 +113,28 @@ TEST_F(SocketFdTest, PayloadBufferTruncation)
109113
close(res->fd);
110114
}
111115

112-
wait(nullptr);
116+
waitpid(child, nullptr, 0);
113117
}
114118

115119
TEST_F(SocketFdTest, SignalInterruptionRecovery)
116120
{
117-
auto handler = signal(SIGUSR1, [](int) { });
118-
ASSERT_NE(handler, SIG_ERR);
119-
120-
if (fork() == 0) {
121+
struct sigaction sa{};
122+
sa.sa_handler = []([[maybe_unused]] int sig) -> void { };
123+
sigemptyset(&sa.sa_mask);
124+
struct sigaction oldsa{};
125+
ASSERT_NE(-1, sigaction(SIGUSR1, &sa, &oldsa));
126+
127+
auto child = fork();
128+
ASSERT_NE(child, -1);
129+
if (child == 0) {
121130
close(sv[1]);
122131
usleep(50000);
123132
kill(getppid(), SIGUSR1);
124133
usleep(50000);
125134
auto ret = sendFdWithPayload(sv[0], STDERR_FILENO, "recovered");
126135
EXPECT_TRUE(ret) << ret.error();
127136
close(sv[0]);
128-
_exit(0);
137+
_exit(EXIT_SUCCESS);
129138
}
130139

131140
close(sv[0]);
@@ -137,22 +146,24 @@ TEST_F(SocketFdTest, SignalInterruptionRecovery)
137146
close(res->fd);
138147
}
139148

140-
wait(nullptr);
149+
waitpid(child, nullptr, 0);
141150

142-
ASSERT_NE(signal(SIGUSR1, handler), SIG_ERR);
151+
ASSERT_NE(-1, sigaction(SIGUSR1, &oldsa, nullptr));
143152
}
144153

145154
TEST_F(SocketFdTest, PeerClosedBeforeSending)
146155
{
147-
if (fork() == 0) {
156+
auto child = fork();
157+
ASSERT_NE(child, -1);
158+
if (child == 0) {
148159
close(sv[1]);
149160
close(sv[0]);
150-
_exit(0);
161+
_exit(EXIT_SUCCESS);
151162
}
152163
close(sv[0]);
153164
auto res = recvFdWithPayload(sv[1], 1024);
154165
EXPECT_FALSE(res.has_value());
155-
wait(nullptr);
166+
waitpid(child, nullptr, 0);
156167
}
157168

158169
TEST_F(SocketFdTest, SendInvalidFdHandle)
@@ -164,11 +175,12 @@ TEST_F(SocketFdTest, SendInvalidFdHandle)
164175
TEST_F(SocketFdTest, LargePayloadHandling)
165176
{
166177
const std::string large_payload(65536, 'B');
167-
if (fork() == 0) {
178+
auto child = fork();
179+
if (child == 0) {
168180
close(sv[1]);
169181
auto ret = sendFdWithPayload(sv[0], STDIN_FILENO, large_payload);
170182
close(sv[0]);
171-
_exit(0);
183+
_exit(EXIT_SUCCESS);
172184
}
173185
close(sv[0]);
174186
auto res = recvFdWithPayload(sv[1], 70000);
@@ -179,5 +191,5 @@ TEST_F(SocketFdTest, LargePayloadHandling)
179191
close(res->fd);
180192
}
181193

182-
wait(nullptr);
194+
waitpid(child, nullptr, 0);
183195
}

0 commit comments

Comments
 (0)