1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved.
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 #include "ipc_unix_socket.h"
16 
17 #include <poll.h>
18 #include <sys/socket.h>
19 #include <sys/un.h>
20 #include <unistd.h>
21 
22 #include "hhlog.h"
23 
24 namespace OHOS {
25 namespace Developtools {
26 namespace Hiebpf {
IpcUnixSocketServer()27 IpcUnixSocketServer::IpcUnixSocketServer() {}
28 
~IpcUnixSocketServer()29 IpcUnixSocketServer::~IpcUnixSocketServer()
30 {
31     Stop();
32 }
33 
Start(const std::string &pathname)34 bool IpcUnixSocketServer::Start(const std::string &pathname)
35 {
36     CHECK_TRUE(serverFd_ == -1, false, "Unix Socket Server is running");
37 
38     serverFd_ = socket(AF_UNIX, SOCK_STREAM, 0);
39     CHECK_TRUE(serverFd_ != -1, false, "create Unix Socket Server failed, %d: %s", errno, strerror(errno));
40 
41     unlink(pathname.c_str());
42     struct sockaddr_un addr = {0};
43     addr.sun_family = AF_UNIX;
44     std::copy(pathname.c_str(), pathname.c_str() + pathname.size() + 1, addr.sun_path);
45     if (bind(serverFd_, (struct sockaddr*)&addr, sizeof(sockaddr_un)) != 0) {
46         HHLOGE(true, "bind failed, Unix Socket(%s), %d: %s", pathname.c_str(), errno, strerror(errno));
47         close(serverFd_);
48         return false;
49     }
50     if (listen(serverFd_, UNIX_SOCKET_LISTEN_COUNT) != 0) {
51         HHLOGE(true, "listen failed, Unix Socket(%s), %d: %s", pathname.c_str(), errno, strerror(errno));
52         close(serverFd_);
53         unlink(pathname.c_str());
54         return false;
55     }
56     pathName_ = pathname;
57 
58     isRunning_ = true;
59     handleThread_ = std::thread([this] { this->HandleThreadLoop(); });
60     return true;
61 }
62 
Stop()63 bool IpcUnixSocketServer::Stop()
64 {
65     isRunning_ = false;
66     if (serverFd_ != -1) {
67         close(serverFd_);
68         serverFd_ = -1;
69     }
70     if (clientFd_ != -1) {
71         close(clientFd_);
72         clientFd_ = -1;
73     }
74     if (handleThread_.joinable()) {
75         handleThread_.join();
76     }
77     unlink(pathName_.c_str());
78     return true;
79 }
80 
SendMessage(const void *buf, size_t size)81 bool IpcUnixSocketServer::SendMessage(const void *buf, size_t size)
82 {
83     CHECK_TRUE(clientFd_ != -1, false, "no available Unix Socket");
84 
85     CHECK_TRUE(send(clientFd_, buf, size, 0) != -1, false,
86                "send failed, Unix Socket(%d) %zu bytes, %d: %s", clientFd_, size, errno, strerror(errno));
87     return true;
88 }
89 
HandleThreadLoop()90 void IpcUnixSocketServer::HandleThreadLoop()
91 {
92     while (isRunning_) {
93         struct pollfd pollFd {serverFd_, POLLIN, 0};
94         const int timeout = 1000;
95         int polled = TEMP_FAILURE_RETRY(poll(&pollFd, 1, timeout));
96         if (polled == 0) { // timeout
97             continue;
98         } else if (polled < 0 || !(pollFd.revents & POLLIN)) {
99             HHLOGE(true, "poll failed, Unix Socket(%d), %d: %s", serverFd_, errno, strerror(errno));
100             close(serverFd_);
101             serverFd_ = -1;
102             break;
103         }
104 
105         clientFd_ = accept(serverFd_, nullptr, nullptr);
106         if (clientFd_ == -1) {
107             HHLOGE(true, "accept failed, Unix Socket(%d), %d: %s", serverFd_, errno, strerror(errno));
108             continue;
109         }
110 
111         while (isRunning_ && clientFd_ != -1) {
112             uint8_t buf[UNIX_SOCKET_BUFFER_SIZE] = {0};
113             int recvSize = recv(clientFd_, buf, UNIX_SOCKET_BUFFER_SIZE, 0);
114             if (recvSize > 0) {
115                 if (handleMessageFn_) {
116                     handleMessageFn_(buf, recvSize);
117                 }
118                 continue;
119             } else if (recvSize == 0) {
120                 HHLOGE(true, "recv failed, peer has closed");
121             } else {
122                 HHLOGE(true, "recv failed, Unix Socket(%d), %d: %s", clientFd_, errno, strerror(errno));
123             }
124             close(clientFd_);
125             clientFd_ = -1;
126         }
127     }
128 }
129 
IpcUnixSocketClient()130 IpcUnixSocketClient::IpcUnixSocketClient() {}
131 
~IpcUnixSocketClient()132 IpcUnixSocketClient::~IpcUnixSocketClient()
133 {
134     Disconnect();
135 }
136 
Connect(const std::string &pathname)137 bool IpcUnixSocketClient::Connect(const std::string &pathname)
138 {
139     CHECK_TRUE(sockFd_ == -1, false, "Unix Socket has connected");
140 
141     sockFd_ = socket(AF_UNIX, SOCK_STREAM, 0);
142     CHECK_TRUE(sockFd_ != -1, false, "create Unix Socket Server failed, %d: %s", errno, strerror(errno));
143 
144     struct sockaddr_un addr = {0};
145     addr.sun_family = AF_UNIX;
146     std::copy(pathname.c_str(), pathname.c_str() + pathname.size() + 1, addr.sun_path);
147     if (connect(sockFd_, (struct sockaddr*)&addr, sizeof(sockaddr_un)) == -1) {
148         HHLOGE(true, "connect failed, %d: %s", errno, strerror(errno));
149         sockFd_ = -1;
150         return false;
151     }
152 
153     return true;
154 }
155 
Disconnect()156 void IpcUnixSocketClient::Disconnect()
157 {
158     if (sockFd_ != -1) {
159         close(sockFd_);
160         sockFd_ = -1;
161     }
162 }
163 
SendMessage(const void *buf, size_t size)164 bool IpcUnixSocketClient::SendMessage(const void *buf, size_t size)
165 {
166     CHECK_TRUE(sockFd_ != -1, false, "Unix Socket disconnected");
167 
168     if (send(sockFd_, buf, size, 0) != -1) {
169         return true;
170     }
171     HHLOGE(true, "send failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
172     return false;
173 }
174 
RecvMessage(void *buf, size_t &size, uint32_t timeout)175 bool IpcUnixSocketClient::RecvMessage(void *buf, size_t &size, uint32_t timeout)
176 {
177     CHECK_TRUE(sockFd_ != -1, false, "Unix Socket disconnected");
178 
179     struct pollfd pollFd {sockFd_, POLLIN | POLLERR | POLLHUP, 0};
180     int polled = poll(&pollFd, 1, timeout);
181     if (polled == 0) { // timeout
182         size = 0;
183         return true;
184     } else if (polled < 0 || !(pollFd.revents & POLLIN)) {
185         HHLOGE(true, "poll failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
186         return false;
187     }
188 
189     int recvSize = recv(sockFd_, buf, size, 0);
190     if (recvSize > 0) {
191         size = static_cast<size_t>(recvSize);
192         return true;
193     } else if (recvSize == 0) {
194         HHLOGE(true, "recv failed, peer has closed");
195     } else {
196         HHLOGE(true, "recv failed, Unix Socket(%d), %d: %s", sockFd_, errno, strerror(errno));
197     }
198 
199     return false;
200 }
201 } // namespace Hiebpf
202 } // namespace Developtools
203 } // namespace OHOS
204