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 "daemon_app.h"
16 #include "decompress.h"
17
18 namespace Hdc {
HdcDaemonApp(HTaskInfo hTaskInfo)19 HdcDaemonApp::HdcDaemonApp(HTaskInfo hTaskInfo)
20 : HdcTransferBase(hTaskInfo)
21 {
22 commandBegin = CMD_APP_BEGIN;
23 commandData = CMD_APP_DATA;
24 funcAppModFinish = nullptr;
25 }
26
~HdcDaemonApp()27 HdcDaemonApp::~HdcDaemonApp()
28 {
29 WRITE_LOG(LOG_INFO, "~HdcDaemonApp channelId:%u", taskInfo->channelId);
30 }
31
ReadyForRelease()32 bool HdcDaemonApp::ReadyForRelease()
33 {
34 if (!HdcTaskBase::ReadyForRelease()) {
35 WRITE_LOG(LOG_WARN, "HdcTaskBase not ready for release channelId:%u", taskInfo->channelId);
36 return false;
37 }
38 if (!asyncCommand.ReadyForRelease()) {
39 WRITE_LOG(LOG_WARN, "asyncCommand not ready for release channelId:%u", taskInfo->channelId);
40 return false;
41 }
42 WRITE_LOG(LOG_INFO, "ReadyForRelease channelId:%u", taskInfo->channelId);
43 return true;
44 }
45
MakeCtxForAppCheck(uint8_t *payload, const int payloadSize)46 void HdcDaemonApp::MakeCtxForAppCheck(uint8_t *payload, const int payloadSize)
47 {
48 string dstPath = "/data/local/tmp/";
49 string bufString(reinterpret_cast<char *>(payload), payloadSize);
50 SerialStruct::ParseFromString(ctxNow.transferConfig, bufString);
51 // update transferconfig to main context
52 ctxNow.master = false;
53 #ifdef HDC_PCDEBUG
54 char tmpPath[256] = "";
55 size_t size = 256;
56 uv_os_tmpdir(tmpPath, &size);
57 dstPath = tmpPath;
58 dstPath += Base::GetPathSep();
59 #endif
60 dstPath += ctxNow.transferConfig.optionalName;
61 ctxNow.localPath = dstPath;
62 ctxNow.transferBegin = Base::GetRuntimeMSec();
63 ctxNow.fileSize = ctxNow.transferConfig.fileSize;
64 return;
65 }
66
CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)67 bool HdcDaemonApp::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
68 {
69 if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
70 WRITE_LOG(LOG_WARN, "HdcTransferBase::CommandDispatch false command:%u channelId:%u",
71 command, taskInfo->channelId);
72 return false;
73 }
74 bool ret = true;
75 switch (command) {
76 case CMD_APP_CHECK: {
77 uv_fs_t *openReq = new uv_fs_t;
78 if (openReq == nullptr) {
79 WRITE_LOG(LOG_FATAL, "HdcDaemonApp::CommandDispatch new uv_fs_t failed");
80 return false;
81 }
82 memset_s(openReq, sizeof(uv_fs_t), 0, sizeof(uv_fs_t));
83 MakeCtxForAppCheck(payload, payloadSize);
84 openReq->data = &ctxNow;
85 ++refCount;
86 uv_fs_open(loopTask, openReq, ctxNow.localPath.c_str(),
87 UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH,
88 OnFileOpen);
89 break;
90 }
91 case CMD_APP_UNINSTALL: {
92 // This maybe has a command implanting risk, since it is a controllable device, it can be ignored
93 string bufString(reinterpret_cast<char *>(payload), payloadSize);
94 string options = "";
95 string packages = "";
96 vector<string> segments;
97 Base::SplitString(bufString, " ", segments);
98 for (auto seg: segments) {
99 if (seg[0] == '-') {
100 options += " " + seg;
101 } else {
102 packages += " " + seg;
103 }
104 }
105 PackageShell(false, options.c_str(), packages);
106 break;
107 }
108 default:
109 break;
110 }
111 return ret;
112 };
113
AsyncInstallFinish(bool finish, int64_t exitStatus, const string result)114 bool HdcDaemonApp::AsyncInstallFinish(bool finish, int64_t exitStatus, const string result)
115 {
116 if (mode == APPMOD_INSTALL) {
117 unlink(ctxNow.localPath.c_str());
118 string::size_type rindex = ctxNow.localPath.rfind(".tar");
119 if (rindex != string::npos) {
120 string dir = ctxNow.localPath.substr(0, rindex);
121 RemovePath(dir);
122 WRITE_LOG(LOG_DEBUG, "RemovePath dir:%s", dir.c_str());
123 }
124 }
125 asyncCommand.DoRelease();
126 string echo = result;
127 echo = Base::ReplaceAll(echo, "\n", " ");
128 vector<uint8_t> vecBuf;
129 vecBuf.push_back(mode);
130 vecBuf.push_back(exitStatus == 0);
131 vecBuf.insert(vecBuf.end(), (uint8_t *)echo.c_str(), (uint8_t *)echo.c_str() + echo.size());
132 SendToAnother(CMD_APP_FINISH, vecBuf.data(), vecBuf.size());
133 --refCount;
134 #ifdef UNIT_TEST
135 Base::WriteBinFile((UT_TMP_PATH + "/appinstall.result").c_str(), (uint8_t *)MESSAGE_SUCCESS.c_str(),
136 MESSAGE_SUCCESS.size(), true);
137 #endif
138 return true;
139 }
140
PackageShell(bool installOrUninstall, const char *options, const string package)141 void HdcDaemonApp::PackageShell(bool installOrUninstall, const char *options, const string package)
142 {
143 ++refCount;
144 // asynccmd Other processes, no RunningProtect protection
145 chmod(package.c_str(), 0755);
146 string doBuf;
147 string opts = string(options);
148 if (installOrUninstall) { // either -p or -s is always required in install
149 if (opts.find("p") == string::npos && opts.find("s") == string::npos) {
150 // basic mode: blank options or both "-s" / "-p" are omitted
151 // eg. hdc install x.hap --> bm install -p x.hap
152 // eg. hdc install -r x.hap --> bm install -r -p x.hap
153 doBuf = Base::StringFormat("bm install %s -p %s", options, package.c_str());
154 } else {
155 // advansed mode for -p/-r/-s and some other options in the future
156 doBuf = Base::StringFormat("bm install %s %s", options, package.c_str());
157 }
158 } else { // -n is always required in uninstall
159 if (opts.find("n") == string::npos) {
160 // basic mode: blank options or "-n" is omitted
161 // eg. hdc uninstall com.xx.xx --> bm uninstall -n com.xx.xx
162 // eg. hdc uninstall -s com.xx.xx --> bm uninstall -s -n com.xx.xx
163 doBuf = Base::StringFormat("bm uninstall %s -n %s", options, package.c_str());
164 } else {
165 // advansed mode for -s/-n and some other options in the future
166 doBuf = Base::StringFormat("bm uninstall %s %s", options, package.c_str());
167 }
168 }
169
170 funcAppModFinish = [this](bool finish, int64_t exitStatus, const string result) -> bool {
171 return this->AsyncInstallFinish(finish, exitStatus, result);
172 };
173 if (installOrUninstall) {
174 mode = APPMOD_INSTALL;
175 } else {
176 mode = APPMOD_UNINSTALL;
177 }
178 asyncCommand.Initial(loopTask, funcAppModFinish, AsyncCmd::OPTION_COMMAND_ONETIME);
179 asyncCommand.ExecuteCommand(doBuf);
180 }
181
Sideload(const char *pathOTA)182 void HdcDaemonApp::Sideload(const char *pathOTA)
183 {
184 mode = APPMOD_SIDELOAD;
185 LogMsg(MSG_OK, "[placeholders] sideload %s", pathOTA);
186 TaskFinish();
187 unlink(pathOTA);
188 }
189
Tar2Dir(const char *path)190 string HdcDaemonApp::Tar2Dir(const char *path)
191 {
192 string dir;
193 string tarpath = path;
194 string::size_type rindex = tarpath.rfind(".tar");
195 if (rindex != string::npos) {
196 dir = tarpath.substr(0, rindex) + Base::GetPathSep();
197 WRITE_LOG(LOG_DEBUG, "path:%s dir:%s", path, dir.c_str());
198 Decompress dc(tarpath);
199 dc.DecompressToLocal(dir);
200 }
201 return dir;
202 }
203
RemoveDir(const string &dir)204 int HdcDaemonApp::RemoveDir(const string &dir)
205 {
206 DIR *pdir = opendir(dir.c_str());
207 if (pdir == nullptr) {
208 WRITE_LOG(LOG_FATAL, "opendir failed dir:%s", dir.c_str());
209 return -1;
210 }
211 struct dirent *ent;
212 struct stat st;
213 while ((ent = readdir(pdir)) != nullptr) {
214 if (ent->d_name[0] == '.') {
215 continue;
216 }
217 std::string subpath = dir + Base::GetPathSep() + ent->d_name;
218 if (lstat(subpath.c_str(), &st) == -1) {
219 WRITE_LOG(LOG_WARN, "lstat failed subpath:%s", subpath.c_str());
220 continue;
221 }
222 if (S_ISDIR(st.st_mode)) {
223 if (RemoveDir(subpath) == -1) {
224 closedir(pdir);
225 return -1;
226 }
227 rmdir(subpath.c_str());
228 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
229 unlink(subpath.c_str());
230 } else {
231 WRITE_LOG(LOG_DEBUG, "lstat st_mode:%07o subpath:%s", st.st_mode, subpath.c_str());
232 }
233 }
234 if (rmdir(dir.c_str()) == -1) {
235 closedir(pdir);
236 return -1;
237 }
238 closedir(pdir);
239 return 0;
240 }
241
RemovePath(const string &path)242 void HdcDaemonApp::RemovePath(const string &path)
243 {
244 struct stat st;
245 if (lstat(path.c_str(), &st) == -1) {
246 WRITE_LOG(LOG_WARN, "lstat failed path:%s", path.c_str());
247 return;
248 }
249 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
250 unlink(path.c_str());
251 } else if (S_ISDIR(st.st_mode)) {
252 if (path == "." || path == "..") {
253 return;
254 }
255 int rc = RemoveDir(path);
256 WRITE_LOG(LOG_INFO, "RemoveDir rc:%d path:%s", rc, path.c_str());
257 }
258 }
259
WhenTransferFinish(CtxFile *context)260 void HdcDaemonApp::WhenTransferFinish(CtxFile *context)
261 {
262 if (context->lastErrno > 0) {
263 constexpr int bufSize = 1024;
264 char buf[bufSize] = { 0 };
265 uv_strerror_r(static_cast<int>(-context->lastErrno), buf, bufSize);
266 WRITE_LOG(LOG_DEBUG, "HdcDaemonApp WhenTransferFinish with errno:%d", context->lastErrno);
267 LogMsg(MSG_FAIL, "Transfer App at:%lld/%lld(Bytes), Reason: %s",
268 context->indexIO, context->fileSize, buf);
269 return;
270 }
271 if (ctxNow.transferConfig.functionName == CMDSTR_APP_SIDELOAD) {
272 Sideload(context->localPath.c_str());
273 } else if (ctxNow.transferConfig.functionName == CMDSTR_APP_INSTALL) {
274 string dir = Tar2Dir(context->localPath.c_str());
275 if (!dir.empty()) {
276 PackageShell(true, context->transferConfig.options.c_str(), dir.c_str());
277 } else {
278 PackageShell(true, context->transferConfig.options.c_str(), context->localPath.c_str());
279 }
280 } else {
281 }
282 };
283 }
284