1 /*
2 * Copyright (C) 2021-2022 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 "hdc_jdwp.h"
17
18 #include <unistd.h>
19
20 namespace Hdc {
21
HdcJdwpSimulator(const std::string processName,const std::string pkgName,bool isDebug,Callback cb)22 HdcJdwpSimulator::HdcJdwpSimulator(const std::string processName, const std::string pkgName, bool isDebug, Callback cb)
23 {
24 processName_ = processName;
25 pkgName_ = pkgName;
26 isDebug_ = isDebug;
27 cb_ = cb;
28 cfd_ = -1;
29 ctxPoint_ = static_cast<HCtxJdwpSimulator>(MallocContext());
30 disconnectFlag_ = false;
31 startOnce_ = true;
32 }
33
Disconnect()34 void HdcJdwpSimulator::Disconnect()
35 {
36 disconnectFlag_ = true;
37 if (ctxPoint_ != nullptr && ctxPoint_->cfd > -1) {
38 shutdown(ctxPoint_->cfd, SHUT_RDWR);
39 close(ctxPoint_->cfd);
40 ctxPoint_->cfd = -1;
41 }
42 if (readThread_.joinable()) {
43 readThread_.join();
44 }
45 }
46
~HdcJdwpSimulator()47 HdcJdwpSimulator::~HdcJdwpSimulator()
48 {
49 disconnectFlag_ = true;
50 if (ctxPoint_ != nullptr && ctxPoint_->cfd > -1) {
51 shutdown(ctxPoint_->cfd, SHUT_RDWR);
52 close(ctxPoint_->cfd);
53 ctxPoint_->cfd = -1;
54 }
55 if (readThread_.joinable()) {
56 readThread_.join();
57 }
58 if (ctxPoint_ != nullptr) {
59 delete ctxPoint_;
60 ctxPoint_ = nullptr;
61 }
62 }
63
SendToJpid(int fd,const uint8_t * buf,const int bufLen)64 bool HdcJdwpSimulator::SendToJpid(int fd, const uint8_t *buf, const int bufLen)
65 {
66 ssize_t rc = write(fd, buf, bufLen);
67 if (rc < 0) {
68 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "SendToJpid failed errno:%{public}d", errno);
69 return false;
70 }
71 return true;
72 }
73
ConnectJpid(HdcJdwpSimulator * param)74 bool HdcJdwpSimulator::ConnectJpid(HdcJdwpSimulator *param)
75 {
76 uint32_t pidCurr = static_cast<uint32_t>(getpid());
77 HdcJdwpSimulator *thisClass = param;
78 #ifdef JS_JDWP_CONNECT
79 std::string processName = thisClass->processName_;
80 std::string pkgName = thisClass->pkgName_;
81 bool isDebug = thisClass->isDebug_;
82 std::string pp = pkgName;
83 if (!processName.empty()) {
84 pp += "/" + processName;
85 }
86 uint32_t ppSize = pp.size() + sizeof(JsMsgHeader);
87 uint8_t* info = new (std::nothrow) uint8_t[ppSize]();
88 if (info == nullptr) {
89 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "ConnectJpid new info fail.");
90 return false;
91 }
92 if (memset_s(info, ppSize, 0, ppSize) != EOK) {
93 delete[] info;
94 info = nullptr;
95 return false;
96 }
97 JsMsgHeader *jsMsg = reinterpret_cast<JsMsgHeader *>(info);
98 jsMsg->msgLen = ppSize;
99 jsMsg->pid = pidCurr;
100 jsMsg->isDebug = isDebug;
101 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL,
102 "ConnectJpid send pid:%{public}d, pp:%{public}s, isDebug:%{public}d, msglen:%{public}d",
103 jsMsg->pid, pp.c_str(), isDebug, jsMsg->msgLen);
104 bool ret = true;
105 if (memcpy_s(info + sizeof(JsMsgHeader), pp.size(), &pp[0], pp.size()) != EOK) {
106 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "ConnectJpid memcpy_s fail :%{public}s.", pp.c_str());
107 ret = false;
108 } else {
109 ret = SendToJpid(thisClass->ctxPoint_->cfd, static_cast<uint8_t*>(info), ppSize);
110 }
111 delete[] info;
112 return ret;
113 #endif
114 return false;
115 }
116
MallocContext()117 void *HdcJdwpSimulator::MallocContext()
118 {
119 HCtxJdwpSimulator ctx = nullptr;
120 if ((ctx = new (std::nothrow) ContextJdwpSimulator()) == nullptr) {
121 return nullptr;
122 }
123 ctx->thisClass = this;
124 ctx->cfd = -1;
125 return ctx;
126 }
127
Connect()128 bool HdcJdwpSimulator::Connect()
129 {
130 const char jdwp[] = { '\0', 'o', 'h', 'j', 'p', 'i', 'd', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', 0 };
131 if (ctxPoint_ == nullptr) {
132 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "MallocContext failed");
133 return false;
134 }
135 struct sockaddr_un caddr;
136 if (memset_s(&caddr, sizeof(caddr), 0, sizeof(caddr)) != EOK) {
137 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "memset_s failed");
138 return false;
139 }
140 caddr.sun_family = AF_UNIX;
141 for (size_t i = 0; i < sizeof(jdwp); i++) {
142 caddr.sun_path[i] = jdwp[i];
143 }
144 cfd_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
145 if (cfd_ < 0) {
146 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "socket failed errno:%{public}d", errno);
147 return false;
148 }
149 ctxPoint_->cfd = cfd_;
150
151 struct timeval timeout;
152 timeout.tv_sec = 1;
153 timeout.tv_usec = 0;
154 setsockopt(cfd_, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
155 size_t caddrLen = sizeof(caddr.sun_family) + sizeof(jdwp) - 1;
156 int rc = connect(cfd_, reinterpret_cast<struct sockaddr *>(&caddr), caddrLen);
157 if (rc != 0) {
158 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "connect failed errno:%{public}d", errno);
159 close(cfd_);
160 cfd_ = -1;
161 return false;
162 }
163 if (ConnectJpid(this)) {
164 if (startOnce_) {
165 startOnce_ = false;
166 ReadStart();
167 }
168 }
169 return true;
170 }
171
ReadStart()172 void HdcJdwpSimulator::ReadStart()
173 {
174 readThread_ = std::thread(ReadWork, this);
175 }
176
ReadWork(HdcJdwpSimulator * param)177 void HdcJdwpSimulator::ReadWork(HdcJdwpSimulator *param)
178 {
179 HdcJdwpSimulator *jdwp = param;
180 jdwp->Read();
181 }
182
Read()183 void HdcJdwpSimulator::Read()
184 {
185 constexpr size_t size = 256;
186 constexpr long sec = 5;
187 uint8_t buf[size] = { 0 };
188 while (!disconnectFlag_ && cfd_ > -1) {
189 ssize_t cnt = 0;
190 ssize_t minlen = sizeof(int32_t);
191 fd_set rset;
192 struct timeval timeout;
193 timeout.tv_sec = sec;
194 timeout.tv_usec = 0;
195 FD_ZERO(&rset);
196 FD_SET(cfd_, &rset);
197 int rc = select(cfd_ + 1, &rset, nullptr, nullptr, &timeout);
198 if (rc < 0) {
199 if (errno == EINTR) {
200 continue;
201 }
202 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "Read select fd:%{public}d error:%{public}d", cfd_, errno);
203 break;
204 } else if (rc == 0) {
205 continue;
206 }
207 if (memset_s(buf, size, 0, size) != EOK) {
208 continue;
209 }
210 struct iovec iov;
211 iov.iov_base = buf;
212 iov.iov_len = size - 1;
213 struct msghdr msg;
214 msg.msg_iov = &iov;
215 msg.msg_iovlen = 1;
216 int len = CMSG_SPACE(static_cast<unsigned int>(sizeof(int)));
217 char ctlBuf[len];
218 msg.msg_controllen = sizeof(ctlBuf);
219 msg.msg_control = ctlBuf;
220 cnt = recvmsg(cfd_, &msg, 0);
221 if (cnt < 0) {
222 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "Read recvmsg cfd:%{public}d errno:%{public}d", cfd_, errno);
223 break;
224 } else if (cnt == 0) {
225 OHOS::HiviewDFX::HiLog::Warn(LOG_LABEL, "Read recvmsg socket peer closed cfd:%{public}d", cfd_);
226 close(cfd_);
227 cfd_ = -1;
228 Reconnect();
229 continue;
230 } else if (cnt < minlen) {
231 OHOS::HiviewDFX::HiLog::Warn(LOG_LABEL, "Read recvmsg cnt:%{public}zd cfd:%{public}d", cnt, cfd_);
232 continue;
233 }
234 int32_t fd = *reinterpret_cast<int32_t *>(buf);
235 std::string str(reinterpret_cast<char *>(buf + sizeof(int32_t)), cnt - sizeof(int32_t));
236 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "Read fd:%{public}d str:%{public}s", fd, str.c_str());
237 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
238 if (cmsg == nullptr) {
239 OHOS::HiviewDFX::HiLog::Fatal(LOG_LABEL, "Read cmsg is nullptr");
240 continue;
241 }
242 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS ||
243 cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
244 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "Read level:%{public}d type:%{public}d len:%{public}d",
245 cmsg->cmsg_level, cmsg->cmsg_type, cmsg->cmsg_len);
246 continue;
247 }
248 int newfd = *(reinterpret_cast<int *>(CMSG_DATA(cmsg)));
249 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "Read fd:%{public}d newfd:%{public}d str:%{public}s",
250 fd, newfd, str.c_str());
251 if (cb_) {
252 cb_(newfd, str);
253 }
254 }
255 }
256
Reconnect()257 void HdcJdwpSimulator::Reconnect()
258 {
259 constexpr int timeout = 3;
260 int retry = 5;
261 // wait for hdcd restart
262 sleep(timeout);
263 while (!disconnectFlag_ && retry > 0) {
264 bool c = Connect();
265 if (c) {
266 OHOS::HiviewDFX::HiLog::Info(LOG_LABEL, "Reconnect success cfd:%{public}d", cfd_);
267 break;
268 }
269 OHOS::HiviewDFX::HiLog::Warn(LOG_LABEL, "Reconnect cfd:%{public}d retry:%{public}d", cfd_, retry--);
270 sleep(timeout);
271 }
272 }
273 } // namespace Hdc
274