1 /*
2 * Copyright (C) 2021 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 #include <cstdlib>
16 #include <sys/mount.h>
17 #include <sys/wait.h>
18
19 #include "daemon_unity.h"
20
21 namespace Hdc {
HdcDaemonUnity(HTaskInfo hTaskInfo)22 HdcDaemonUnity::HdcDaemonUnity(HTaskInfo hTaskInfo)
23 : HdcTaskBase(hTaskInfo)
24 {
25 currentDataCommand = CMD_KERNEL_ECHO_RAW; // Default output to shelldata
26 }
27
~HdcDaemonUnity()28 HdcDaemonUnity::~HdcDaemonUnity()
29 {
30 WRITE_LOG(LOG_DEBUG, "~HdcDaemonUnity channelId:%u", taskInfo->channelId);
31 }
32
StopTask()33 void HdcDaemonUnity::StopTask()
34 {
35 // Remove jpid tracker when stopping task
36 RemoveJdwpTracker();
37 asyncCommand.DoRelease();
38 }
39
ReadyForRelease()40 bool HdcDaemonUnity::ReadyForRelease()
41 {
42 if (!HdcTaskBase::ReadyForRelease() || !asyncCommand.ReadyForRelease()) {
43 WRITE_LOG(LOG_DEBUG, "not ready for release channelId:%u", taskInfo->channelId);
44 return false;
45 }
46 return true;
47 }
48
AsyncCmdOut(bool finish,int64_t exitStatus,const string result)49 bool HdcDaemonUnity::AsyncCmdOut(bool finish, int64_t exitStatus, const string result)
50 {
51 #ifdef UNIT_TEST
52 Base::WriteBinFile((UT_TMP_PATH + "/execute.result").c_str(), (uint8_t *)result.c_str(), result.size(),
53 countUt++ == 0);
54 #endif
55 bool ret = false;
56 bool wantFinish = false;
57 do {
58 if (finish) {
59 wantFinish = true;
60 ret = true;
61 --refCount;
62 break;
63 }
64 if (!SendToAnother(currentDataCommand, reinterpret_cast<uint8_t *>(const_cast<char *>(result.c_str())),
65 result.size())) {
66 break;
67 }
68 ret = true;
69 } while (false);
70 if (wantFinish) {
71 TaskFinish();
72 }
73 return ret;
74 }
75
ExecuteShell(const char * shellCommand)76 int HdcDaemonUnity::ExecuteShell(const char *shellCommand)
77 {
78 do {
79 AsyncCmd::CmdResultCallback funcResultOutput;
80 funcResultOutput = [this](bool finish, int64_t exitStatus, const string result) -> bool {
81 return this->AsyncCmdOut(finish, exitStatus, result);
82 };
83 if (!asyncCommand.Initial(loopTask, funcResultOutput)) {
84 break;
85 }
86 asyncCommand.ExecuteCommand(shellCommand);
87 ++refCount;
88 return RET_SUCCESS;
89 } while (false);
90
91 TaskFinish();
92 WRITE_LOG(LOG_DEBUG, "Shell failed finish");
93 return -1;
94 }
95
FindMountDeviceByPath(const char * toQuery,char * dev)96 bool HdcDaemonUnity::FindMountDeviceByPath(const char *toQuery, char *dev)
97 {
98 int ret = false;
99 int len = BUF_SIZE_DEFAULT2;
100 char buf[BUF_SIZE_DEFAULT2];
101
102 FILE *fp = fopen("/proc/mounts", "r");
103 if (fp == nullptr) {
104 WRITE_LOG(LOG_FATAL, "fopen /proc/mounts error:%d", errno);
105 return false;
106 }
107
108 while (fgets(buf, len, fp) != nullptr) {
109 char dir[BUF_SIZE_SMALL] = "";
110 int freq;
111 int passnno;
112 int res = 0;
113 // clang-format off
114 res = sscanf_s(buf, "%255s %255s %*s %*s %d %d\n", dev, BUF_SIZE_SMALL - 1,
115 dir, BUF_SIZE_SMALL - 1, &freq, &passnno);
116 // clang-format on
117 dev[BUF_SIZE_SMALL - 1] = '\0';
118 dir[BUF_SIZE_SMALL - 1] = '\0';
119 if (res == 4 && (strcmp(toQuery, dir) == 0)) { // 4 : The correct number of parameters
120 WRITE_LOG(LOG_DEBUG, "FindMountDeviceByPath dev:%s dir:%s", dev, dir);
121 ret = true;
122 break;
123 }
124 }
125 int rc = fclose(fp);
126 if (rc != 0) {
127 WRITE_LOG(LOG_WARN, "fclose rc:%d error:%d", rc, errno);
128 }
129 if (!ret) {
130 WRITE_LOG(LOG_FATAL, "FindMountDeviceByPath not found %s", toQuery);
131 }
132 return ret;
133 }
134
RemountPartition(const char * dir)135 bool HdcDaemonUnity::RemountPartition(const char *dir)
136 {
137 int fd;
138 int off = 0;
139 char dev[BUF_SIZE_SMALL] = "";
140
141 if (!FindMountDeviceByPath(dir, dev) || strlen(dev) < 4) { // 4 : file count
142 WRITE_LOG(LOG_FATAL, "FindMountDeviceByPath failed %s", dir);
143 return false;
144 }
145
146 if ((fd = open(dev, O_RDONLY | O_CLOEXEC)) < 0) {
147 WRITE_LOG(LOG_FATAL, "Open device:%s failed,error:%d", dev, errno);
148 return false;
149 }
150 ioctl(fd, BLKROSET, &off);
151 Base::CloseFd(fd);
152
153 if (mount(dev, dir, "none", MS_REMOUNT, nullptr) < 0) {
154 WRITE_LOG(LOG_FATAL, "Mount device failed dev:%s dir:%s error:%d", dev, dir, errno);
155 return false;
156 }
157 return true;
158 }
159
CallRemount()160 bool HdcDaemonUnity::CallRemount()
161 {
162 int pipefd[2];
163 pid_t pid;
164 int exitStatus;
165
166 if (pipe(pipefd) == -1) {
167 WRITE_LOG(LOG_FATAL, "Failed to create pipe: %s", strerror(errno));
168 return false;
169 }
170 pid = fork();
171 if (pid < 0) {
172 WRITE_LOG(LOG_FATAL, "Failed to fork: %s", strerror(errno));
173 return false;
174 }
175 if (pid == 0) {
176 close(pipefd[0]);
177 signal(SIGCHLD, SIG_DFL);
178 exitStatus = system("remount");
179 write(pipefd[1], &exitStatus, sizeof(int));
180 close(pipefd[1]);
181 _exit(0);
182 } else {
183 close(pipefd[1]);
184 read(pipefd[0], &exitStatus, sizeof(int));
185 close(pipefd[0]);
186 waitpid(pid, nullptr, 0);
187 if (exitStatus == -1) {
188 WRITE_LOG(LOG_FATAL, "Failed to execute /bin/remount: %s", strerror(errno));
189 return false;
190 } else if (WIFEXITED(exitStatus) && WEXITSTATUS(exitStatus) != 0) {
191 WRITE_LOG(LOG_FATAL, "Remount failed with exit code: %d", WEXITSTATUS(exitStatus));
192 return false;
193 }
194 }
195
196 return true;
197 }
198
RemountDevice()199 bool HdcDaemonUnity::RemountDevice()
200 {
201 if (getuid() != 0) {
202 LogMsg(MSG_FAIL, "Operate need running as root");
203 return false;
204 }
205 struct stat info;
206 if (!lstat("/vendor", &info) && (info.st_mode & S_IFMT) == S_IFDIR) {
207 if (!RemountPartition("/vendor")) {
208 WRITE_LOG(LOG_FATAL, "Mount failed /vendor (via mount)");
209 }
210 }
211 if (!lstat("/system", &info) && (info.st_mode & S_IFMT) == S_IFDIR) {
212 if (!RemountPartition("/")) {
213 WRITE_LOG(LOG_FATAL, "Mount failed /system (via mount)");
214 }
215 }
216
217 if (CallRemount()) {
218 LogMsg(MSG_OK, "Mount finish");
219 return true;
220 } else {
221 LogMsg(MSG_FAIL, "Mount failed");
222 return false;
223 }
224 }
225
RebootDevice(const string & cmd)226 bool HdcDaemonUnity::RebootDevice(const string &cmd)
227 {
228 sync();
229 return SystemDepend::RebootDevice(cmd);
230 }
231
SetDeviceRunMode(const char * cmd)232 bool HdcDaemonUnity::SetDeviceRunMode(const char *cmd)
233 {
234 WRITE_LOG(LOG_INFO, "Set run mode:%s", cmd);
235 string tmp(cmd);
236 char *ptr = tmp.data();
237 char *token = nullptr;
238
239 #ifdef HDC_EMULATOR
240 LogMsg(MSG_FAIL, "[E001300]Not support tmode for Emulator");
241 return true;
242 #endif
243
244 // hdc tmode usb, do nothing
245 if (strcmp(CMDSTR_TMODE_USB.c_str(), cmd) == 0) {
246 LogMsg(MSG_FAIL, "[E001000]For USB debugging, please set it on the device's Settings UI");
247 return true;
248 }
249 // not usb and not tcp
250 if (strncmp("port", cmd, strlen("port")) != 0) {
251 LogMsg(MSG_FAIL, "[E001001]Unknown command");
252 return false;
253 }
254
255 // bypass port
256 token = strtok_r(ptr, " ", &ptr);
257 // get next token
258 token = strtok_r(ptr, " ", &ptr);
259 // hdc tmode port
260 if (token == nullptr) {
261 LogMsg(MSG_OK, "Set device run mode successful.");
262 SystemDepend::SetDevItem("persist.hdc.mode", "tcp");
263 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "enable");
264 } else {
265 /*
266 * hdc tmode port xxxxxx
267 * hdc tmode port close
268 */
269 if (strcmp(token, "close") == 0) {
270 SystemDepend::SetDevItem("persist.hdc.port", "0");
271 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "disable");
272 } else {
273 string tmp(token);
274 if (tmp.find_first_not_of("0123456789") != string::npos) {
275 LogMsg(MSG_FAIL, "[E001100]Invalid port");
276 return false;
277 }
278 LogMsg(MSG_OK, "Set device run mode successful.");
279 SystemDepend::SetDevItem("persist.hdc.mode", "tcp");
280 SystemDepend::SetDevItem("persist.hdc.port", token);
281 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "enable");
282 }
283 }
284 return true;
285 }
286
GetHiLog(const char * cmd)287 inline bool HdcDaemonUnity::GetHiLog(const char *cmd)
288 {
289 string cmdDo = "hilog";
290 if (cmd && !strcmp(const_cast<char *>(cmd), "h")) {
291 cmdDo += " -h";
292 }
293 ExecuteShell(cmdDo.c_str());
294 return true;
295 }
296
ListJdwpProcess(void * daemonIn)297 inline bool HdcDaemonUnity::ListJdwpProcess(void *daemonIn)
298 {
299 HdcDaemon *daemon = (HdcDaemon *)daemonIn;
300 string result = ((HdcJdwp *)daemon->clsJdwp)->GetProcessList();
301 if (!result.size()) {
302 result = EMPTY_ECHO;
303 } else {
304 result.erase(result.end() - 1); // remove tail \n
305 }
306 LogMsg(MSG_OK, result.c_str());
307 return true;
308 }
309
TrackJdwpProcess(void * daemonIn,const string & param)310 inline bool HdcDaemonUnity::TrackJdwpProcess(void *daemonIn, const string& param)
311 {
312 HdcDaemon *daemon = static_cast<HdcDaemon *>(daemonIn);
313 taskInfo->debugRelease = 1;
314 if (param == "p") {
315 taskInfo->debugRelease = 0;
316 } else if (param == "a") {
317 // allApp with display debug or release
318 constexpr uint8_t allAppWithDr = 3;
319 taskInfo->debugRelease = allAppWithDr;
320 }
321 if (!((static_cast<HdcJdwp *>(daemon->clsJdwp))->CreateJdwpTracker(taskInfo))) {
322 string result = MESSAGE_FAIL;
323 LogMsg(MSG_OK, result.c_str());
324 return false;
325 }
326 return true;
327 }
328
RemoveJdwpTracker()329 inline void HdcDaemonUnity::RemoveJdwpTracker()
330 {
331 HdcDaemon *daemon = static_cast<HdcDaemon *>(taskInfo->ownerSessionClass);
332 (static_cast<HdcJdwp *>(daemon->clsJdwp))->RemoveJdwpTracker(taskInfo);
333 }
334
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)335 bool HdcDaemonUnity::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
336 {
337 bool ret = true;
338 HdcDaemon *daemon = (HdcDaemon *)taskInfo->ownerSessionClass;
339 // Both are not executed, do not need to be detected 'childReady'
340 string strPayload = string(reinterpret_cast<char *>(payload), payloadSize);
341 #ifdef HDC_DEBUG
342 WRITE_LOG(LOG_DEBUG, "CommandDispatch command:%d", command);
343 #endif // HDC_DEBUG
344 switch (command) {
345 case CMD_UNITY_EXECUTE: {
346 ExecuteShell(const_cast<char *>(strPayload.c_str()));
347 break;
348 }
349 case CMD_UNITY_REMOUNT: {
350 ret = false;
351 RemountDevice();
352 break;
353 }
354 case CMD_UNITY_REBOOT: {
355 ret = false;
356 RebootDevice(strPayload);
357 break;
358 }
359 case CMD_UNITY_RUNMODE: {
360 ret = false;
361 SetDeviceRunMode(strPayload.c_str());
362 break;
363 }
364 case CMD_UNITY_HILOG: {
365 GetHiLog(strPayload.c_str());
366 break;
367 }
368 case CMD_UNITY_ROOTRUN: {
369 ret = false;
370 string debugMode;
371 // hdcd restart in old pid
372 bool restart = true;
373 SystemDepend::GetDevItem("const.debuggable", debugMode);
374 if (debugMode == "1") {
375 if (payloadSize != 0 && !strcmp(strPayload.c_str(), "r")) {
376 SystemDepend::SetDevItem("persist.hdc.root", "0");
377 } else {
378 // hdcd restart in new pid
379 restart = false;
380 SystemDepend::SetDevItem("persist.hdc.root", "1");
381 }
382 } else {
383 LogMsg(MSG_FAIL, "Cannot set root run mode in undebuggable version.");
384 return false;
385 }
386 daemon->PostStopInstanceMessage(restart);
387 break;
388 }
389 case CMD_UNITY_BUGREPORT_INIT: {
390 currentDataCommand = CMD_UNITY_BUGREPORT_DATA;
391 ExecuteShell("hidumper");
392 break;
393 }
394 case CMD_JDWP_LIST: {
395 ret = false;
396 ListJdwpProcess(daemon);
397 break;
398 }
399 case CMD_JDWP_TRACK: {
400 if (!TrackJdwpProcess(daemon, strPayload)) {
401 ret = false;
402 }
403 break;
404 }
405 default:
406 break;
407 }
408 #ifdef HDC_DEBUG
409 WRITE_LOG(LOG_DEBUG, "CommandDispatch command:%d finish.", command);
410 #endif // HDC_LOCAL_DEBUG
411 return ret;
412 };
413 } // namespace Hdc
414