• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             WRITE_LOG(LOG_INFO, "CMD_APP_CHECK cid:%u sid:%u uv_fs_open local:%s remote:%s",
87                 taskInfo->channelId, taskInfo->sessionId, Hdc::MaskString(ctxNow.localPath).c_str(),
88                 Hdc::MaskString(ctxNow.remotePath).c_str());
89             uv_fs_open(loopTask, openReq, ctxNow.localPath.c_str(),
90                        UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH,
91                        OnFileOpen);
92             break;
93         }
94         case CMD_APP_UNINSTALL: {
95             // This maybe has a command implanting risk, since it is a controllable device, it can be ignored
96             string bufString(reinterpret_cast<char *>(payload), payloadSize);
97             string options = "";
98             string packages = "";
99             vector<string> segments;
100             Base::SplitString(bufString, " ", segments);
101             for (auto seg: segments) {
102                 if (seg[0] == '-') {
103                     options += " " + seg;
104                 } else {
105                     packages += " " + seg;
106                 }
107             }
108             PackageShell(false, options.c_str(), packages);
109             break;
110         }
111         default:
112             break;
113     }
114     return ret;
115 };
116 
AsyncInstallFinish(bool finish,int64_t exitStatus,const string result)117 bool HdcDaemonApp::AsyncInstallFinish(bool finish, int64_t exitStatus, const string result)
118 {
119     if (mode == APPMOD_INSTALL) {
120         unlink(ctxNow.localPath.c_str());
121         string::size_type rindex = ctxNow.localPath.rfind(".tar");
122         if (rindex != string::npos) {
123             string dir = ctxNow.localPath.substr(0, rindex);
124             RemovePath(dir);
125             WRITE_LOG(LOG_DEBUG, "RemovePath dir:%s", dir.c_str());
126         }
127     }
128     asyncCommand.DoRelease();
129     string echo = result;
130     echo = Base::ReplaceAll(echo, "\n", " ");
131     vector<uint8_t> vecBuf;
132     vecBuf.push_back(mode);
133     vecBuf.push_back(exitStatus == 0);
134     vecBuf.insert(vecBuf.end(), (uint8_t *)echo.c_str(), (uint8_t *)echo.c_str() + echo.size());
135     SendToAnother(CMD_APP_FINISH, vecBuf.data(), vecBuf.size());
136     --refCount;
137 #ifdef UNIT_TEST
138     Base::WriteBinFile((UT_TMP_PATH + "/appinstall.result").c_str(), (uint8_t *)MESSAGE_SUCCESS.c_str(),
139                        MESSAGE_SUCCESS.size(), true);
140 #endif
141     return true;
142 }
143 
PackageShell(bool installOrUninstall,const char * options,const string package)144 void HdcDaemonApp::PackageShell(bool installOrUninstall, const char *options, const string package)
145 {
146     ++refCount;
147     // asynccmd Other processes, no RunningProtect protection
148     chmod(package.c_str(), 0755);
149     string doBuf;
150     string opts = string(options);
151     if (installOrUninstall) { // either -p or -s is always required in install
152         if (opts.find("p") == string::npos && opts.find("s") == string::npos) {
153             // basic mode: blank options or both "-s" / "-p" are omitted
154             // eg. hdc install x.hap --> bm install -p x.hap
155             // eg. hdc install -r x.hap --> bm install -r -p x.hap
156             doBuf = Base::StringFormat("bm install %s -p %s", options, package.c_str());
157         } else {
158             // advansed mode for -p/-r/-s and some other options in the future
159             doBuf = Base::StringFormat("bm install %s %s", options, package.c_str());
160         }
161     } else {  // -n is always required in uninstall
162         if (opts.find("n") == string::npos) {
163             // basic mode: blank options or "-n" is omitted
164             // eg. hdc uninstall com.xx.xx --> bm uninstall -n com.xx.xx
165             // eg. hdc uninstall -s com.xx.xx --> bm uninstall -s -n com.xx.xx
166             doBuf = Base::StringFormat("bm uninstall %s -n %s", options, package.c_str());
167         } else {
168             // advansed mode for -s/-n and some other options in the future
169             doBuf = Base::StringFormat("bm uninstall %s %s", options, package.c_str());
170         }
171     }
172 
173     funcAppModFinish = [this](bool finish, int64_t exitStatus, const string result) -> bool {
174         return this->AsyncInstallFinish(finish, exitStatus, result);
175     };
176     if (installOrUninstall) {
177         mode = APPMOD_INSTALL;
178     } else {
179         mode = APPMOD_UNINSTALL;
180     }
181     asyncCommand.Initial(loopTask, funcAppModFinish, AsyncCmd::OPTION_COMMAND_ONETIME);
182     asyncCommand.ExecuteCommand(doBuf);
183 }
184 
Sideload(const char * pathOTA)185 void HdcDaemonApp::Sideload(const char *pathOTA)
186 {
187     mode = APPMOD_SIDELOAD;
188     LogMsg(MSG_OK, "[placeholders] sideload %s", pathOTA);
189     TaskFinish();
190     unlink(pathOTA);
191 }
192 
Tar2Dir(const char * path)193 string HdcDaemonApp::Tar2Dir(const char *path)
194 {
195     string dir;
196     string tarpath = path;
197     string::size_type rindex = tarpath.rfind(".tar");
198     if (rindex != string::npos) {
199         dir = tarpath.substr(0, rindex) + Base::GetPathSep();
200         WRITE_LOG(LOG_DEBUG, "path:%s dir:%s", path, dir.c_str());
201         Decompress dc(tarpath);
202         dc.DecompressToLocal(dir);
203     }
204     return dir;
205 }
206 
RemoveDir(const string & dir)207 int HdcDaemonApp::RemoveDir(const string &dir)
208 {
209     DIR *pdir = opendir(dir.c_str());
210     if (pdir == nullptr) {
211         WRITE_LOG(LOG_FATAL, "opendir failed dir:%s", dir.c_str());
212         return -1;
213     }
214     struct dirent *ent;
215     struct stat st;
216     while ((ent = readdir(pdir)) != nullptr) {
217         if (ent->d_name[0] == '.') {
218             continue;
219         }
220         std::string subpath = dir + Base::GetPathSep() + ent->d_name;
221         if (lstat(subpath.c_str(), &st) == -1) {
222             WRITE_LOG(LOG_WARN, "lstat failed subpath:%s", subpath.c_str());
223             continue;
224         }
225         if (S_ISDIR(st.st_mode)) {
226             if (RemoveDir(subpath) == -1) {
227                 closedir(pdir);
228                 return -1;
229             }
230             rmdir(subpath.c_str());
231         } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
232             unlink(subpath.c_str());
233         } else {
234             WRITE_LOG(LOG_DEBUG, "lstat st_mode:%07o subpath:%s", st.st_mode, subpath.c_str());
235         }
236     }
237     if (rmdir(dir.c_str()) == -1) {
238         closedir(pdir);
239         return -1;
240     }
241     closedir(pdir);
242     return 0;
243 }
244 
RemovePath(const string & path)245 void HdcDaemonApp::RemovePath(const string &path)
246 {
247     struct stat st;
248     if (lstat(path.c_str(), &st) == -1) {
249         WRITE_LOG(LOG_WARN, "lstat failed path:%s", path.c_str());
250         return;
251     }
252     if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
253         unlink(path.c_str());
254     } else if (S_ISDIR(st.st_mode)) {
255         if (path == "." || path == "..") {
256             return;
257         }
258         int rc = RemoveDir(path);
259         WRITE_LOG(LOG_INFO, "RemoveDir rc:%d path:%s", rc, path.c_str());
260     }
261 }
262 
WhenTransferFinish(CtxFile * context)263 void HdcDaemonApp::WhenTransferFinish(CtxFile *context)
264 {
265     if (context->lastErrno > 0) {
266         constexpr int bufSize = 1024;
267         char buf[bufSize] = { 0 };
268         uv_strerror_r(static_cast<int>(-context->lastErrno), buf, bufSize);
269         WRITE_LOG(LOG_DEBUG, "HdcDaemonApp WhenTransferFinish with errno:%d", context->lastErrno);
270         LogMsg(MSG_FAIL, "Transfer App at:%lld/%lld(Bytes), Reason: %s",
271                context->indexIO, context->fileSize, buf);
272         return;
273     }
274     if (ctxNow.transferConfig.functionName == CMDSTR_APP_SIDELOAD) {
275         Sideload(context->localPath.c_str());
276     } else if (ctxNow.transferConfig.functionName == CMDSTR_APP_INSTALL) {
277         string dir = Tar2Dir(context->localPath.c_str());
278         if (!dir.empty()) {
279             PackageShell(true, context->transferConfig.options.c_str(), dir.c_str());
280         } else {
281             PackageShell(true, context->transferConfig.options.c_str(), context->localPath.c_str());
282         }
283     } else {
284     }
285 };
286 }
287