1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rs_profiler_socket.h"
17
18 #include <fcntl.h>
19 #include <netinet/in.h>
20 #include <netinet/tcp.h>
21 #include <securec.h>
22 #include <sys/select.h>
23 #include <sys/ioctl.h>
24 #include <sys/un.h>
25 #include <unistd.h>
26 #include <poll.h>
27
28 #include "rs_profiler_log.h"
29 #include "rs_profiler_utils.h"
30
31 namespace OHOS::Rosen {
32
GetTimeoutDesc(uint32_t milliseconds)33 static timeval GetTimeoutDesc(uint32_t milliseconds)
34 {
35 const uint32_t millisecondsInSecond = 1000u;
36
37 timeval timeout = {};
38 timeout.tv_sec = milliseconds / millisecondsInSecond;
39 timeout.tv_usec = (milliseconds % millisecondsInSecond) * millisecondsInSecond;
40 return timeout;
41 }
42
GetTimeout(int32_t socket)43 static timeval GetTimeout(int32_t socket)
44 {
45 timeval timeout = {};
46 socklen_t size = sizeof(timeout);
47 getsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<char*>(&timeout), &size);
48 return timeout;
49 }
50
SetTimeout(int32_t socket,const timeval & timeout)51 static void SetTimeout(int32_t socket, const timeval& timeout)
52 {
53 setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&timeout), sizeof(timeout));
54 }
55
SetTimeout(int32_t socket,uint32_t milliseconds)56 static void SetTimeout(int32_t socket, uint32_t milliseconds)
57 {
58 SetTimeout(socket, GetTimeoutDesc(milliseconds));
59 }
60
ToggleFlag(uint32_t flags,uint32_t flag,bool enable)61 static int32_t ToggleFlag(uint32_t flags, uint32_t flag, bool enable)
62 {
63 return enable ? (flags | flag) : (flags & ~flag);
64 }
65
SetBlocking(int32_t socket,bool enable)66 static void SetBlocking(int32_t socket, bool enable)
67 {
68 fcntl(socket, F_SETFL, ToggleFlag(fcntl(socket, F_GETFL, 0), O_NONBLOCK, !enable));
69 }
70
SetCloseOnExec(int32_t socket,bool enable)71 static void SetCloseOnExec(int32_t socket, bool enable)
72 {
73 fcntl(socket, F_SETFD, ToggleFlag(fcntl(socket, F_GETFD, 0), FD_CLOEXEC, enable));
74 }
75
~Socket()76 Socket::~Socket()
77 {
78 Shutdown();
79 }
80
Connected() const81 bool Socket::Connected() const
82 {
83 return (socket_ != -1) && (client_ != -1) && (state_ == SocketState::CONNECTED);
84 }
85
GetState() const86 SocketState Socket::GetState() const
87 {
88 return state_;
89 }
90
Shutdown()91 void Socket::Shutdown()
92 {
93 shutdown(socket_, SHUT_RDWR);
94 close(socket_);
95 socket_ = -1;
96
97 shutdown(client_, SHUT_RDWR);
98 close(client_);
99 client_ = -1;
100
101 state_ = SocketState::SHUTDOWN;
102 }
103
Open(uint16_t port)104 void Socket::Open(uint16_t port)
105 {
106 socket_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
107 if (socket_ == -1) {
108 Shutdown();
109 return;
110 }
111
112 const std::string socketName = "render_service_" + std::to_string(port);
113 sockaddr_un address {};
114 address.sun_family = AF_UNIX;
115 address.sun_path[0] = 0;
116 ::memmove_s(address.sun_path + 1, sizeof(address.sun_path) - 1, socketName.data(), socketName.size());
117
118 const size_t addressSize = offsetof(sockaddr_un, sun_path) + socketName.size() + 1;
119 if (bind(socket_, reinterpret_cast<sockaddr*>(&address), addressSize) == -1) {
120 Shutdown();
121 return;
122 }
123
124 const int32_t maxConnections = 5;
125 if (listen(socket_, maxConnections) != 0) {
126 Shutdown();
127 return;
128 }
129
130 SetBlocking(socket_, false);
131 SetCloseOnExec(socket_, true);
132
133 state_ = SocketState::CREATE;
134 }
135
AcceptClient()136 void Socket::AcceptClient()
137 {
138 client_ = accept4(socket_, nullptr, nullptr, SOCK_CLOEXEC);
139 if (client_ == -1) {
140 if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EINTR)) {
141 Shutdown();
142 }
143 return;
144 }
145
146 SetBlocking(client_, false);
147 SetCloseOnExec(client_, true);
148
149 int32_t nodelay = 1;
150 setsockopt(client_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&nodelay), sizeof(nodelay));
151
152 state_ = SocketState::CONNECTED;
153 }
154
Available()155 size_t Socket::Available()
156 {
157 int32_t size = 0;
158 const auto result = ioctl(client_, FIONREAD, &size);
159 if (result == -1) {
160 HRPE("Socket: Available failed: %d", errno);
161 return 0u;
162 }
163 return static_cast<size_t>(size);
164 }
165
SendWhenReady(const void * data,size_t size)166 bool Socket::SendWhenReady(const void* data, size_t size)
167 {
168 if (!data || (size == 0)) {
169 return true;
170 }
171
172 SetBlocking(client_, true);
173
174 const timeval previousTimeout = GetTimeout(client_);
175
176 const uint32_t timeoutMilliseconds = 40;
177 SetTimeout(client_, timeoutMilliseconds);
178
179 const char* bytes = reinterpret_cast<const char*>(data);
180 size_t sent = 0;
181 while (sent < size) {
182 if (PollSend(1) == 0) {
183 // wait for 1ms in worst case to have socket ready for sending
184 continue;
185 }
186 const ssize_t sentBytes = send(client_, bytes, size - sent, 0);
187 if ((sentBytes <= 0) && (errno != EINTR)) {
188 HRPE("Socket: SendWhenReady: Invoke shutdown: %d", errno);
189 Shutdown();
190 return false;
191 }
192 auto actualSentBytes = static_cast<size_t>(sentBytes);
193 sent += actualSentBytes;
194 bytes += actualSentBytes;
195 }
196
197 SetTimeout(client_, previousTimeout);
198 SetBlocking(client_, false);
199 return true;
200 }
201
Receive(void * data,size_t & size)202 bool Socket::Receive(void* data, size_t& size)
203 {
204 if (!data || (size == 0)) {
205 return true;
206 }
207
208 SetBlocking(client_, false);
209
210 const ssize_t receivedBytes = recv(client_, static_cast<char*>(data), size, 0);
211 if (receivedBytes > 0) {
212 size = static_cast<size_t>(receivedBytes);
213 } else {
214 size = 0;
215 if ((errno == EWOULDBLOCK) || (errno == EAGAIN) || (errno == EINTR)) {
216 return true;
217 }
218 HRPE("Socket: Receive: Invoke shutdown: %d", errno);
219 Shutdown();
220 return false;
221 }
222 return true;
223 }
224
ReceiveWhenReady(void * data,size_t size)225 bool Socket::ReceiveWhenReady(void* data, size_t size)
226 {
227 if (!data || (size == 0)) {
228 return true;
229 }
230
231 const timeval previousTimeout = GetTimeout(client_);
232 const uint32_t bandwitdth = 10000; // KB/ms
233 const uint32_t timeoutPad = 100;
234 const uint32_t timeout = size / bandwitdth + timeoutPad;
235
236 SetBlocking(client_, true);
237 SetTimeout(client_, timeout);
238
239 size_t received = 0;
240 char* bytes = static_cast<char*>(data);
241 while (received < size) {
242 // receivedBytes can only be -1 or [0, size - received] (from recv man)
243 const ssize_t receivedBytes = recv(client_, bytes, size - received, 0);
244 if ((receivedBytes == -1) && (errno != EINTR)) {
245 HRPE("Socket: ReceiveWhenReady: Invoke shutdown: %d", errno);
246 Shutdown();
247 return false;
248 }
249
250 // so receivedBytes here always [0, size - received]
251 // then received can't be > `size` and it can't be overflowed
252 auto actualReceivedBytes = static_cast<size_t>(receivedBytes);
253 received += actualReceivedBytes;
254 bytes += actualReceivedBytes;
255 }
256
257 SetTimeout(client_, previousTimeout);
258 SetBlocking(client_, false);
259 return true;
260 }
261
PollReceive(int timeout)262 int Socket::PollReceive(int timeout)
263 {
264 struct pollfd pollFd = {0};
265 pollFd.fd = client_;
266 pollFd.events = POLLIN;
267 return poll(&pollFd, 1, timeout);
268 }
269
PollSend(int timeout)270 int Socket::PollSend(int timeout)
271 {
272 struct pollfd pollFd = {0};
273 pollFd.fd = client_;
274 pollFd.events = POLLOUT;
275 return poll(&pollFd, 1, timeout);
276 }
277
278 } // namespace OHOS::Rosen