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
CheckbundlePath(const string & bundleName,string & mountPath)76 bool HdcDaemonUnity::CheckbundlePath(const string &bundleName, string &mountPath)
77 {
78 if (access(DEBUG_BUNDLE_PATH.c_str(), F_OK) != 0 || !Base::CheckBundleName(bundleName)) {
79 WRITE_LOG(LOG_FATAL, "debug path %s not found", DEBUG_BUNDLE_PATH.c_str());
80 LogMsg(MSG_FAIL, "[E003001] Invalid bundle name: %s", bundleName.c_str());
81 return false;
82 }
83 string targetPath = "";
84 targetPath += DEBUG_BUNDLE_PATH;
85 targetPath += bundleName;
86 if ((access(targetPath.c_str(), F_OK) != 0)) {
87 WRITE_LOG(LOG_FATAL, "bundle mount path %s not found", targetPath.c_str());
88 LogMsg(MSG_FAIL, "[E003001] Invalid bundle name: %s", bundleName.c_str());
89 return false;
90 }
91 mountPath = targetPath;
92 return true;
93 }
94
ExecuteOptionShell(const string & shellCommand,const string & bundleName)95 int HdcDaemonUnity::ExecuteOptionShell(const string &shellCommand, const string &bundleName)
96 {
97 string mountPath = "";
98 if (!CheckbundlePath(bundleName, mountPath)) {
99 return -1;
100 }
101 return ExecuteShell(shellCommand, mountPath);
102 }
103
ExecuteShell(const string & shellCommand,string optionPath)104 int HdcDaemonUnity::ExecuteShell(const string &shellCommand, string optionPath)
105 {
106 do {
107 AsyncCmd::CmdResultCallback funcResultOutput;
108 funcResultOutput = [this](bool finish, int64_t exitStatus, const string result) -> bool {
109 return this->AsyncCmdOut(finish, exitStatus, result);
110 };
111 if (!asyncCommand.Initial(loopTask, funcResultOutput)) {
112 break;
113 }
114 asyncCommand.ExecuteCommand(shellCommand, optionPath);
115 ++refCount;
116 return RET_SUCCESS;
117 } while (false);
118
119 TaskFinish();
120 WRITE_LOG(LOG_DEBUG, "Shell failed finish");
121 return -1;
122 }
123
ExecuteShellExtend(const uint8_t * payload,const int payloadSize)124 int HdcDaemonUnity::ExecuteShellExtend(const uint8_t *payload, const int payloadSize)
125 {
126 string bundleName = "";
127 string command = "";
128 string errMsg = "";
129 TlvBuf tlvbuf(const_cast<uint8_t *>(payload), payloadSize, Base::REGISTERD_TAG_SET);
130 tlvbuf.Display();
131 if (tlvbuf.ContainInvalidTag()) {
132 LogMsg(MSG_FAIL, "[E003004] Device does not support this shell option");
133 return -1;
134 } else {
135 if (!tlvbuf.FindTlv(TAG_SHELL_BUNDLE, bundleName)) {
136 WRITE_LOG(LOG_FATAL, "ExecuteShellExtend bundleName is empty");
137 }
138 if (!tlvbuf.FindTlv(TAG_SHELL_CMD, command)) {
139 WRITE_LOG(LOG_FATAL, "ExecuteShellExtend command is empty");
140 }
141 }
142 WRITE_LOG(LOG_DEBUG, "ExecuteShellExtend command: %s, bundleName: %s", command.c_str(), bundleName.c_str());
143 return ExecuteOptionShell(command, bundleName);
144 }
145
FindMountDeviceByPath(const char * toQuery,char * dev)146 bool HdcDaemonUnity::FindMountDeviceByPath(const char *toQuery, char *dev)
147 {
148 int ret = false;
149 int len = BUF_SIZE_DEFAULT2;
150 char buf[BUF_SIZE_DEFAULT2];
151
152 FILE *fp = fopen("/proc/mounts", "r");
153 if (fp == nullptr) {
154 WRITE_LOG(LOG_FATAL, "fopen /proc/mounts error:%d", errno);
155 return false;
156 }
157
158 while (fgets(buf, len, fp) != nullptr) {
159 char dir[BUF_SIZE_SMALL] = "";
160 int freq;
161 int passnno;
162 int res = 0;
163 // clang-format off
164 res = sscanf_s(buf, "%255s %255s %*s %*s %d %d\n", dev, BUF_SIZE_SMALL - 1,
165 dir, BUF_SIZE_SMALL - 1, &freq, &passnno);
166 // clang-format on
167 dev[BUF_SIZE_SMALL - 1] = '\0';
168 dir[BUF_SIZE_SMALL - 1] = '\0';
169 if (res == 4 && (strcmp(toQuery, dir) == 0)) { // 4 : The correct number of parameters
170 WRITE_LOG(LOG_DEBUG, "FindMountDeviceByPath dev:%s dir:%s", dev, dir);
171 ret = true;
172 break;
173 }
174 }
175 int rc = fclose(fp);
176 if (rc != 0) {
177 WRITE_LOG(LOG_WARN, "fclose rc:%d error:%d", rc, errno);
178 }
179 if (!ret) {
180 WRITE_LOG(LOG_FATAL, "FindMountDeviceByPath not found %s", toQuery);
181 }
182 return ret;
183 }
184
RemountPartition(const char * dir)185 bool HdcDaemonUnity::RemountPartition(const char *dir)
186 {
187 int fd;
188 int off = 0;
189 char dev[BUF_SIZE_SMALL] = "";
190
191 if (!FindMountDeviceByPath(dir, dev) || strlen(dev) < 4) { // 4 : file count
192 WRITE_LOG(LOG_FATAL, "FindMountDeviceByPath failed %s", dir);
193 return false;
194 }
195
196 if ((fd = open(dev, O_RDONLY | O_CLOEXEC)) < 0) {
197 WRITE_LOG(LOG_FATAL, "Open device:%s failed,error:%d", dev, errno);
198 return false;
199 }
200 ioctl(fd, BLKROSET, &off);
201 Base::CloseFd(fd);
202
203 if (mount(dev, dir, "none", MS_REMOUNT, nullptr) < 0) {
204 WRITE_LOG(LOG_FATAL, "Mount device failed dev:%s dir:%s error:%d", dev, dir, errno);
205 return false;
206 }
207 return true;
208 }
209
CallRemount()210 bool HdcDaemonUnity::CallRemount()
211 {
212 int pipefd[2];
213 pid_t pid;
214 int exitStatus;
215
216 if (pipe(pipefd) == -1) {
217 WRITE_LOG(LOG_FATAL, "Failed to create pipe: %s", strerror(errno));
218 return false;
219 }
220 pid = fork();
221 if (pid < 0) {
222 close(pipefd[0]);
223 close(pipefd[1]);
224 WRITE_LOG(LOG_FATAL, "Failed to fork: %s", strerror(errno));
225 return false;
226 }
227 if (pid == 0) {
228 close(pipefd[0]);
229 signal(SIGCHLD, SIG_DFL);
230 exitStatus = system("remount");
231 write(pipefd[1], &exitStatus, sizeof(int));
232 close(pipefd[1]);
233 _exit(0);
234 } else {
235 close(pipefd[1]);
236 read(pipefd[0], &exitStatus, sizeof(int));
237 close(pipefd[0]);
238 waitpid(pid, nullptr, 0);
239 if (exitStatus == -1) {
240 WRITE_LOG(LOG_FATAL, "Failed to execute /bin/remount: %s", strerror(errno));
241 return false;
242 } else if (WIFEXITED(exitStatus) && WEXITSTATUS(exitStatus) != 0) {
243 WRITE_LOG(LOG_FATAL, "Remount failed with exit code: %d", WEXITSTATUS(exitStatus));
244 return false;
245 }
246 }
247
248 return true;
249 }
250
RemountDevice()251 bool HdcDaemonUnity::RemountDevice()
252 {
253 string debugMode;
254 SystemDepend::GetDevItem("const.debuggable", debugMode);
255 if (debugMode != "1") {
256 LogMsg(MSG_FAIL, "[E007100] Operate need running under debug mode");
257 return false;
258 }
259
260 if (getuid() != 0) {
261 LogMsg(MSG_FAIL, "Operate need running as root");
262 return false;
263 }
264 struct stat info;
265 if (!lstat("/vendor", &info) && (info.st_mode & S_IFMT) == S_IFDIR) {
266 if (!RemountPartition("/vendor")) {
267 WRITE_LOG(LOG_FATAL, "Mount failed /vendor (via mount)");
268 }
269 }
270 if (!lstat("/system", &info) && (info.st_mode & S_IFMT) == S_IFDIR) {
271 if (!RemountPartition("/")) {
272 WRITE_LOG(LOG_FATAL, "Mount failed /system (via mount)");
273 }
274 }
275 if (CallRemount()) {
276 LogMsg(MSG_OK, "Mount finish");
277 return true;
278 } else {
279 LogMsg(MSG_FAIL, "Mount failed");
280 return false;
281 }
282 }
283
RebootDevice(const string & cmd)284 bool HdcDaemonUnity::RebootDevice(const string &cmd)
285 {
286 sync();
287 return SystemDepend::RebootDevice(cmd);
288 }
289
SetDeviceRunMode(const char * cmd)290 bool HdcDaemonUnity::SetDeviceRunMode(const char *cmd)
291 {
292 WRITE_LOG(LOG_INFO, "Set run mode:%s", cmd);
293 string tmp(cmd);
294 char *ptr = tmp.data();
295 char *token = nullptr;
296
297 #ifdef HDC_EMULATOR
298 LogMsg(MSG_FAIL, "[E001300]Not support tmode for Emulator");
299 return true;
300 #endif
301
302 // hdc tmode usb, do nothing
303 if (strcmp(CMDSTR_TMODE_USB.c_str(), cmd) == 0) {
304 LogMsg(MSG_FAIL, "[E001000]For USB debugging, please set it on the device's Settings UI");
305 return true;
306 }
307 // not usb and not tcp
308 if (strncmp("port", cmd, strlen("port")) != 0) {
309 LogMsg(MSG_FAIL, "[E001001]Unknown command");
310 return false;
311 }
312
313 // bypass port
314 token = strtok_r(ptr, " ", &ptr);
315 // get next token
316 token = strtok_r(ptr, " ", &ptr);
317 // hdc tmode port
318 if (token == nullptr) {
319 LogMsg(MSG_OK, "Set device run mode successful.");
320 SystemDepend::SetDevItem("persist.hdc.mode", "tcp");
321 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "enable");
322 } else {
323 /*
324 * hdc tmode port xxxxxx
325 * hdc tmode port close
326 */
327 if (strcmp(token, "close") == 0) {
328 SystemDepend::SetDevItem("persist.hdc.port", "0");
329 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "disable");
330 } else {
331 string tmp(token);
332 if (tmp.find_first_not_of("0123456789") != string::npos) {
333 LogMsg(MSG_FAIL, "[E001100]Invalid port");
334 return false;
335 }
336 LogMsg(MSG_OK, "Set device run mode successful.");
337 SystemDepend::SetDevItem("persist.hdc.mode", "tcp");
338 SystemDepend::SetDevItem("persist.hdc.port", token);
339 SystemDepend::SetDevItem("persist.hdc.mode.tcp", "enable");
340 }
341 }
342 return true;
343 }
344
GetHiLog(const char * cmd)345 inline bool HdcDaemonUnity::GetHiLog(const char *cmd)
346 {
347 string cmdDo = "hilog";
348 if (cmd && !strcmp(const_cast<char *>(cmd), "h")) {
349 cmdDo += " -h";
350 }
351 ExecuteShell(cmdDo.c_str());
352 return true;
353 }
354
ListJdwpProcess(void * daemonIn)355 inline bool HdcDaemonUnity::ListJdwpProcess(void *daemonIn)
356 {
357 HdcDaemon *daemon = (HdcDaemon *)daemonIn;
358 string result = ((HdcJdwp *)daemon->clsJdwp)->GetProcessList();
359 if (!result.size()) {
360 result = EMPTY_ECHO;
361 } else {
362 result.erase(result.end() - 1); // remove tail \n
363 }
364 LogMsg(MSG_OK, result.c_str());
365 return true;
366 }
367
TrackJdwpProcess(void * daemonIn,const string & param)368 inline bool HdcDaemonUnity::TrackJdwpProcess(void *daemonIn, const string& param)
369 {
370 HdcDaemon *daemon = static_cast<HdcDaemon *>(daemonIn);
371 taskInfo->debugRelease = 1;
372 if (param == "p") {
373 taskInfo->debugRelease = 0;
374 } else if (param == "a") {
375 // allApp with display debug or release
376 constexpr uint8_t allAppWithDr = 3;
377 taskInfo->debugRelease = allAppWithDr;
378 }
379 if (!((static_cast<HdcJdwp *>(daemon->clsJdwp))->CreateJdwpTracker(taskInfo))) {
380 string result = MESSAGE_FAIL;
381 LogMsg(MSG_OK, result.c_str());
382 return false;
383 }
384 return true;
385 }
386
RemoveJdwpTracker()387 inline void HdcDaemonUnity::RemoveJdwpTracker()
388 {
389 HdcDaemon *daemon = static_cast<HdcDaemon *>(taskInfo->ownerSessionClass);
390 (static_cast<HdcJdwp *>(daemon->clsJdwp))->RemoveJdwpTracker(taskInfo);
391 }
392
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)393 bool HdcDaemonUnity::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
394 {
395 bool ret = true;
396 HdcDaemon *daemon = (HdcDaemon *)taskInfo->ownerSessionClass;
397 // Both are not executed, do not need to be detected 'childReady'
398 string strPayload = string(reinterpret_cast<char *>(payload), payloadSize);
399 #ifdef HDC_DEBUG
400 WRITE_LOG(LOG_DEBUG, "CommandDispatch command:%d", command);
401 #endif // HDC_DEBUG
402 switch (command) {
403 case CMD_UNITY_EXECUTE: {
404 ExecuteShell(const_cast<char *>(strPayload.c_str()));
405 break;
406 }
407 case CMD_UNITY_EXECUTE_EX: {
408 if (ExecuteShellExtend(payload, payloadSize) != 0) {
409 ret = false;
410 }
411 break;
412 }
413 case CMD_UNITY_REMOUNT: {
414 ret = false;
415 RemountDevice();
416 break;
417 }
418 case CMD_UNITY_REBOOT: {
419 ret = false;
420 RebootDevice(strPayload);
421 break;
422 }
423 case CMD_UNITY_RUNMODE: {
424 ret = false;
425 SetDeviceRunMode(strPayload.c_str());
426 break;
427 }
428 case CMD_UNITY_HILOG: {
429 GetHiLog(strPayload.c_str());
430 break;
431 }
432 case CMD_UNITY_ROOTRUN: {
433 ret = false;
434 string debugMode;
435 // hdcd restart in old pid
436 bool restart = true;
437 SystemDepend::GetDevItem("const.debuggable", debugMode);
438 if (debugMode == "1") {
439 if (payloadSize != 0 && !strcmp(strPayload.c_str(), "r")) {
440 SystemDepend::SetDevItem("persist.hdc.root", "0");
441 } else {
442 // hdcd restart in new pid
443 restart = false;
444 SystemDepend::SetDevItem("persist.hdc.root", "1");
445 }
446 } else {
447 LogMsg(MSG_FAIL, "Cannot set root run mode in undebuggable version.");
448 return false;
449 }
450 daemon->PostStopInstanceMessage(restart);
451 break;
452 }
453 case CMD_UNITY_BUGREPORT_INIT: {
454 currentDataCommand = CMD_UNITY_BUGREPORT_DATA;
455 ExecuteShell("hidumper");
456 break;
457 }
458 case CMD_JDWP_LIST: {
459 ret = false;
460 ListJdwpProcess(daemon);
461 break;
462 }
463 case CMD_JDWP_TRACK: {
464 if (!TrackJdwpProcess(daemon, strPayload)) {
465 ret = false;
466 }
467 break;
468 }
469 default:
470 break;
471 }
472 #ifdef HDC_DEBUG
473 WRITE_LOG(LOG_DEBUG, "CommandDispatch command:%d finish.", command);
474 #endif // HDC_LOCAL_DEBUG
475 return ret;
476 };
477 } // namespace Hdc
478