* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "host_app.h"
#include "compress.h"
namespace Hdc {
HdcHostApp::HdcHostApp(HTaskInfo hTaskInfo)
: HdcTransferBase(hTaskInfo)
{
commandBegin = CMD_APP_BEGIN;
commandData = CMD_APP_DATA;
originLocalDir = "";
isStableBuf = hTaskInfo->isStableBuf;
}
HdcHostApp::~HdcHostApp()
{
}
string HdcHostApp::Dir2Tar(const char *dir)
{
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "dir:%s", dir);
} else {
WRITE_LOG(LOG_DEBUG, "dir:%s", Hdc::MaskString(string(dir)).c_str());
}
string tarpath;
uv_fs_t req;
int r = uv_fs_lstat(nullptr, &req, dir, nullptr);
uv_fs_req_cleanup(&req);
if (r == 0 && (req.statbuf.st_mode & S_IFDIR)) {
string sdir = dir;
string tarname = Base::GetRandomString(EXPECTED_LEN) + ".tar";
tarpath = Base::GetTmpDir() + tarname;
WRITE_LOG(LOG_DEBUG, "tarpath:%s", tarpath.c_str());
Compress c;
c.UpdatePrefix(sdir);
c.AddPath(sdir);
c.SaveToFile(tarpath);
}
return tarpath;
}
bool HdcHostApp::BeginInstall(CtxFile *context, const char *command)
{
int argc = 0;
bool ret = false;
string options;
char **argv = Base::SplitCommandToArgs(command, &argc);
if (argc < 1) {
goto Finish;
}
for (int i = 0; i < argc; ++i) {
if (!strcmp(argv[i], CMD_OPTION_CLIENTCWD.c_str())) {
if (i + 1 < argc) {
context->transferConfig.clientCwd = argv[i + 1];
i += 1;
}
} else if (!strncmp(argv[i], "-", 1)) {
if (options.size()) {
options += " ";
}
options += argv[i];
} else {
string path = argv[i];
ExtractRelativePath(context->transferConfig.clientCwd, path);
if (MatchPackageExtendName(path, ".hap") || MatchPackageExtendName(path, ".hsp")
|| MatchPackageExtendName(path, ".app")) {
context->taskQueue.push_back(path);
} else {
string tarpath = Dir2Tar(path.c_str());
if (!tarpath.empty()) {
context->taskQueue.push_back(tarpath);
originLocalDir = path;
}
}
}
}
if (!context->taskQueue.size()) {
LogMsg(MSG_FAIL, "[E006001] Not any installation package was found");
goto Finish;
}
sort(context->taskQueue.begin(), context->taskQueue.end());
context->taskQueue.erase(unique(context->taskQueue.begin(), context->taskQueue.end()), context->taskQueue.end());
context->transferConfig.options = options;
context->transferConfig.functionName = CMDSTR_APP_INSTALL;
ret = RunQueue(context);
Finish:
if (argv) {
delete[](reinterpret_cast<char *>(argv));
}
return ret;
}
bool HdcHostApp::BeginSideload(CtxFile *context, const char *localPath)
{
bool ret = false;
context->transferConfig.functionName = CMDSTR_APP_SIDELOAD;
context->taskQueue.push_back(localPath);
ret = RunQueue(context);
return ret;
}
bool HdcHostApp::RunQueue(CtxFile *context)
{
context->localPath = context->taskQueue.back();
uv_fs_t *openReq = new uv_fs_t;
if (openReq == nullptr) {
LogMsg(MSG_FAIL, "HdcHostApp::RunQueue new uv_fs_t failed");
OnFileOpenFailed(context);
return false;
}
(void)memset_s(openReq, sizeof(uv_fs_t), 0, sizeof(uv_fs_t));
openReq->data = context;
++refCount;
uv_fs_open(loopTask, openReq, context->localPath.c_str(), O_RDONLY, 0, OnFileOpen);
context->master = true;
return true;
}
void HdcHostApp::CheckMaster(CtxFile *context)
{
uv_fs_t fs = {};
uv_fs_fstat(nullptr, &fs, context->openFd, nullptr);
context->transferConfig.fileSize = fs.statbuf.st_size;
uv_fs_req_cleanup(&fs);
context->transferConfig.optionalName
= Base::GetRandomString(EXPECTED_LEN);
if (context->localPath.find(".hap") != static_cast<size_t>(-1)) {
context->transferConfig.optionalName += ".hap";
} else if (context->localPath.find(".hsp") != static_cast<size_t>(-1)) {
context->transferConfig.optionalName += ".hsp";
} else if (context->localPath.find(".tar") != static_cast<size_t>(-1)) {
context->transferConfig.optionalName += ".tar";
} else if (context->localPath.find(".app") != static_cast<size_t>(-1)) {
context->transferConfig.optionalName += ".app";
} else {
context->transferConfig.optionalName += ".bundle";
}
string bufString = SerialStruct::SerializeToString(context->transferConfig);
SendToAnother(CMD_APP_CHECK, const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(bufString.c_str())),
bufString.size());
}
bool HdcHostApp::CheckInstallContinue(AppModType mode, const char *msg)
{
string modeDesc;
switch (mode) {
case APPMOD_INSTALL:
modeDesc = "App install";
break;
case APPMOD_UNINSTALL:
modeDesc = "App uninstall";
break;
case APPMOD_SIDELOAD:
modeDesc = "Side load";
break;
default:
modeDesc = "Unknown";
break;
}
if (ctxNow.taskQueue.size() > 0) {
string path = ctxNow.taskQueue.back();
ctxNow.taskQueue.pop_back();
string::size_type pos = path.rfind(".tar");
if (mode == APPMOD_INSTALL && pos != string::npos) {
if (unlink(path.c_str()) != 0) {
WRITE_LOG(LOG_FATAL, "Failed to unlink file or symlink, error is :%s", strerror(errno));
}
if (Base::GetCaller() == Base::Caller::CLIENT) {
WRITE_LOG(LOG_DEBUG, "unlink path:%s", path.c_str());
} else {
WRITE_LOG(LOG_DEBUG, "unlink path:%s", Hdc::MaskString(path).c_str());
}
}
}
string path = ctxNow.localPath;
if (!originLocalDir.empty()) {
path = originLocalDir;
}
LogMsg(MSG_INFO, "%s path:%s msg:%s", modeDesc.c_str(), path.c_str(), msg + printedMsgLen);
printedMsgLen = strlen(msg);
if (singalStop || !ctxNow.taskQueue.size()) {
LogMsg(MSG_OK, "AppMod finish");
return false;
}
return RunQueue(&ctxNow);
}
bool HdcHostApp::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
{
if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
return false;
}
bool ret = true;
constexpr int cmdOffset = 2;
switch (command) {
case CMD_APP_INIT: {
ret = BeginInstall(&ctxNow, (const char *)payload);
break;
}
case CMD_APP_FINISH: {
AppModType mode = static_cast<AppModType>(payload[0]);
string s(reinterpret_cast<char *>(payload + cmdOffset), payloadSize - cmdOffset);
ret = CheckInstallContinue(mode, s.c_str());
break;
}
case CMD_APP_UNINSTALL: {
SendToAnother(CMD_APP_UNINSTALL, payload, payloadSize);
ctxNow.taskQueue.push_back(reinterpret_cast<char *>(payload));
break;
}
case CMD_APP_SIDELOAD: {
BeginSideload(&ctxNow, (const char *)payload);
break;
}
default:
break;
}
return ret;
};
}