1 /*
2 * Copyright (C) 2022 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
16 #include "host_updater.h"
17
18 #include <algorithm>
19 #include <unordered_map>
20
21 #include "common.h"
22 #include "define.h"
23 #include "serial_struct.h"
24
25 namespace Hdc {
26 namespace {
27 constexpr uint8_t PERCENT_FINISH = 100;
28 constexpr uint8_t PERCENT_CLEAR = UINT8_MAX;
29 constexpr int MAX_RETRY_COUNT = 3;
30 constexpr size_t FLASH_PARAM_MIN_COUNT = 2;
31 constexpr size_t FLASH_FILE_INDEX = 1;
32 constexpr size_t UPDATE_PARAM_MIN_COUNT = 1;
33 constexpr size_t UPDATE_FILE_INDEX = 0;
34 constexpr size_t FORMAT_PARAM_MIN_COUNT = 2;
35 constexpr size_t ERASE_PARAM_MIN_COUNT = 2;
36
37 const std::string CMD_STR_UPDATE = "update ";
38 const std::string CMD_STR_FLASH = "flash ";
39 const std::string CMD_STR_ERASE = "erase ";
40 const std::string CMD_STR_FORMAT = "format ";
41
42 const std::unordered_map<std::string, uint16_t> FLASHD_CMD = {
43 {CMD_STR_UPDATE, CMD_FLASHD_UPDATE_INIT},
44 {CMD_STR_FLASH, CMD_FLASHD_FLASH_INIT},
45 {CMD_STR_ERASE, CMD_FLASHD_ERASE},
46 {CMD_STR_FORMAT, CMD_FLASHD_FORMAT},
47 };
48
Split(const std::string & src,const std::vector<std::string> & filter)49 std::vector<std::string> Split(const std::string &src, const std::vector<std::string> &filter)
50 {
51 std::vector<std::string> result;
52 if (src.empty()) {
53 return result;
54 }
55 const auto len = src.size() + 1;
56 auto buffer = std::vector<char>(len, 0);
57 buffer.assign(src.begin(), src.end());
58 const char delimit[] = "\t\r\n ";
59 char *nextToken = nullptr;
60 char *token = strtok_s(buffer.data(), delimit, &nextToken);
61 while (token != nullptr) {
62 if (std::find(filter.cbegin(), filter.cend(), token) == filter.cend()) {
63 result.push_back(token);
64 }
65 token = strtok_s(nullptr, delimit, &nextToken);
66 }
67 return result;
68 }
69 }
70
HostUpdater(HTaskInfo hTaskInfo)71 HostUpdater::HostUpdater(HTaskInfo hTaskInfo) : HdcTransferBase(hTaskInfo)
72 {
73 commandBegin = CMD_FLASHD_BEGIN;
74 commandData = CMD_FLASHD_DATA;
75 }
76
~HostUpdater()77 HostUpdater::~HostUpdater() {}
78
RunQueue(CtxFile & context)79 void HostUpdater::RunQueue(CtxFile &context)
80 {
81 refCount++;
82 context.localPath = context.taskQueue.back();
83 uv_fs_open(loopTask, &context.fsOpenReq, context.localPath.c_str(), O_RDONLY, 0, OnFileOpen);
84 context.master = true;
85 }
86
BeginTransfer(const std::string & function,const uint8_t * payload,int payloadSize,size_t minParam,size_t fileIndex)87 bool HostUpdater::BeginTransfer(const std::string &function, const uint8_t *payload, int payloadSize, size_t minParam,
88 size_t fileIndex)
89 {
90 if (payload[payloadSize - 1] != '\0') {
91 WRITE_LOG(LOG_FATAL, "payload is invalid");
92 return false;
93 }
94 std::string cmdParam(reinterpret_cast<const char *>(payload));
95 auto params = Split(cmdParam, {});
96 auto count = minParam;
97 auto index = fileIndex;
98 if (std::find(params.cbegin(), params.cend(), "-f") != params.cend()) {
99 count++;
100 index++;
101 }
102 if (params.size() != count || params.size() <= index) {
103 WRITE_LOG(LOG_FATAL, "param count is invalid");
104 return false;
105 }
106
107 std::string localPath = params[index];
108 if (!Base::CheckDirectoryOrPath(localPath.c_str(), true, true)) {
109 WRITE_LOG(LOG_FATAL, "localPath is invalid");
110 return false;
111 }
112
113 if (MatchPackageExtendName(localPath, ".img") || MatchPackageExtendName(localPath, ".bin") ||
114 MatchPackageExtendName(localPath, ".fd") || MatchPackageExtendName(localPath, ".cpio")) {
115 ctxNow.transferConfig.compressType = COMPRESS_NONE;
116 } else if (MatchPackageExtendName(localPath, ".zip")) {
117 WRITE_LOG(LOG_INFO, "file type is zip");
118 } else {
119 WRITE_LOG(LOG_FATAL, "file type is invalid");
120 return false;
121 }
122 ctxNow.transferConfig.functionName = function;
123 ctxNow.transferConfig.options = cmdParam;
124 ctxNow.localPath = localPath;
125 ctxNow.taskQueue.push_back(localPath);
126 RunQueue(ctxNow);
127 return true;
128 }
129
CheckMaster(CtxFile * context)130 void HostUpdater::CheckMaster(CtxFile *context)
131 {
132 uv_fs_t fs;
133 Base::ZeroStruct(fs.statbuf);
134 uv_fs_fstat(nullptr, &fs, context->fsOpenReq.result, nullptr);
135 context->transferConfig.fileSize = fs.statbuf.st_size;
136 uv_fs_req_cleanup(&fs);
137 context->transferConfig.optionalName = Base::GetFileNameAny(context->localPath);
138 std::string bufString = SerialStruct::SerializeToString(context->transferConfig);
139
140 WRITE_LOG(LOG_DEBUG, "functionName = %s, fileSize = %llu", context->transferConfig.functionName.c_str(),
141 context->transferConfig.fileSize);
142
143 std::vector<uint8_t> buffer(sizeof(uint64_t) / sizeof(uint8_t), 0);
144 buffer.insert(buffer.end(), bufString.begin(), bufString.end());
145 SendToAnother(CMD_FLASHD_CHECK, (uint8_t *)buffer.data(), buffer.size());
146 }
147
CheckCmd(HdcCommand command,uint8_t * payload,int payloadSize,size_t paramCount)148 bool HostUpdater::CheckCmd(HdcCommand command, uint8_t *payload, int payloadSize, size_t paramCount)
149 {
150 if (payloadSize < 1 || payload[payloadSize - 1] != '\0') {
151 WRITE_LOG(LOG_FATAL, "payload is invalid");
152 return false;
153 }
154 std::string cmdParam(reinterpret_cast<char *>(payload));
155 WRITE_LOG(LOG_INFO, "cmdParam = %s, paramCount = %u", cmdParam.c_str(), paramCount);
156
157 auto result = Split(cmdParam, {});
158 auto iter = std::find(result.cbegin(), result.cend(), "-f");
159 bool ret = (iter != result.cend()) ? (result.size() == (paramCount + 1)) : (result.size() == paramCount);
160 if (!ret) {
161 WRITE_LOG(LOG_FATAL, "CheckCmd failed");
162 return false;
163 }
164
165 SendToAnother(command, payload, payloadSize);
166 ctxNow.taskQueue.push_back(cmdParam);
167 return true;
168 }
169
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)170 bool HostUpdater::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
171 {
172 if (command == CMD_FLASHD_BEGIN) {
173 if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
174 return false;
175 }
176 std::string tip("Processing: 0%");
177 sendProgress_ = true;
178 SendRawData(tip);
179 return true;
180 }
181
182 if (payload == nullptr || payloadSize <= 0) {
183 WRITE_LOG(LOG_FATAL, "payload or payloadSize is invalid");
184 return false;
185 }
186 if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
187 return false;
188 }
189 bool ret = true;
190 switch (command) {
191 case CMD_FLASHD_UPDATE_INIT:
192 ret = BeginTransfer(CMDSTR_FLASHD_UPDATE, payload, payloadSize, UPDATE_PARAM_MIN_COUNT, UPDATE_FILE_INDEX);
193 break;
194 case CMD_FLASHD_FLASH_INIT:
195 ret = BeginTransfer(CMDSTR_FLASHD_FLASH, payload, payloadSize, FLASH_PARAM_MIN_COUNT, FLASH_FILE_INDEX);
196 break;
197 case CMD_FLASHD_FINISH:
198 ret = CheckUpdateContinue(command, payload, payloadSize);
199 break;
200 case CMD_FLASHD_ERASE:
201 ret = CheckCmd(CMD_FLASHD_ERASE, payload, payloadSize, ERASE_PARAM_MIN_COUNT);
202 break;
203 case CMD_FLASHD_FORMAT:
204 ret = CheckCmd(CMD_FLASHD_FORMAT, payload, payloadSize, FORMAT_PARAM_MIN_COUNT);
205 break;
206 case CMD_FLASHD_PROGRESS:
207 ProcessProgress(*payload);
208 break;
209 default:
210 break;
211 }
212 return ret;
213 }
214
ProcessProgress(uint8_t percentage)215 void HostUpdater::ProcessProgress(uint8_t percentage)
216 {
217 if (!sendProgress_) {
218 return;
219 }
220 if (percentage == PERCENT_CLEAR) {
221 SendRawData("\n");
222 sendProgress_ = false;
223 return;
224 }
225 std::string plrogress = "\rProcessing: " + std::to_string(percentage) + "%";
226 SendRawData(plrogress);
227 if (percentage == PERCENT_FINISH) {
228 SendRawData("\n");
229 sendProgress_ = false;
230 }
231 }
232
CheckUpdateContinue(const uint16_t command,const uint8_t * payload,int payloadSize)233 bool HostUpdater::CheckUpdateContinue(const uint16_t command, const uint8_t *payload, int payloadSize)
234 {
235 if (static_cast<size_t>(payloadSize) < sizeof(uint16_t)) {
236 return false;
237 }
238
239 MessageLevel level = static_cast<MessageLevel>(payload[1]);
240 if ((level == MSG_OK) && sendProgress_) {
241 ProcessProgress(PERCENT_FINISH);
242 }
243 std::string info(reinterpret_cast<char *>(const_cast<uint8_t *>(payload + sizeof(uint16_t))),
244 payloadSize - sizeof(uint16_t));
245 if (!info.empty()) {
246 LogMsg(level, "%s", info.c_str());
247 }
248 WRITE_LOG(LOG_DEBUG, "CheckUpdateContinue payloadSize %d %d %s", payloadSize, level, info.c_str());
249 if (ctxNow.taskQueue.size() != 0) {
250 ctxNow.taskQueue.pop_back();
251 }
252 if (singalStop || !ctxNow.taskQueue.size()) {
253 return false;
254 }
255 RunQueue(ctxNow);
256 return true;
257 }
258
CheckMatchUpdate(const std::string & input,TranslateCommand::FormatCommand & outCmd)259 bool HostUpdater::CheckMatchUpdate(const std::string &input, TranslateCommand::FormatCommand &outCmd)
260 {
261 WRITE_LOG(LOG_DEBUG, "CheckMatchUpdate command:%s", input.c_str());
262 for (const auto &iter : FLASHD_CMD) {
263 if ((input.find(iter.first) == 0) && (input.size() > iter.first.size())) {
264 outCmd.cmdFlag = iter.second;
265 return true;
266 }
267 }
268 return false;
269 }
270
ConfirmCommand(const string & commandIn,bool & closeInput)271 bool HostUpdater::ConfirmCommand(const string &commandIn, bool &closeInput)
272 {
273 std::string tip = "";
274 if (!strncmp(commandIn.c_str(), CMD_STR_UPDATE.c_str(), CMD_STR_UPDATE.size())) {
275 closeInput = true;
276 } else if (!strncmp(commandIn.c_str(), CMD_STR_FLASH.c_str(), CMD_STR_FLASH.size())) {
277 tip = "Confirm flash partition";
278 closeInput = true;
279 } else if (!strncmp(commandIn.c_str(), CMD_STR_ERASE.c_str(), CMD_STR_ERASE.size())) {
280 tip = "Confirm erase partition";
281 } else if (!strncmp(commandIn.c_str(), CMD_STR_FORMAT.c_str(), CMD_STR_FORMAT.size())) {
282 tip = "Confirm format partition";
283 }
284 if (tip.empty() || strstr(commandIn.c_str(), " -f") != nullptr) {
285 return true;
286 }
287 const size_t minLen = strlen("yes");
288 int retryCount = 0;
289 do {
290 printf("%s ? (Yes/No) ", tip.c_str());
291 fflush(stdin);
292 std::string info = {};
293 size_t i = 0;
294 while (1) {
295 char c = getchar();
296 if (c == '\r' || c == '\n') {
297 break;
298 }
299 if (c == ' ') {
300 continue;
301 }
302 if (i < minLen && isprint(c)) {
303 info.append(1, std::tolower(c));
304 i++;
305 }
306 }
307 if (info == "n" || info == "no") {
308 return false;
309 }
310 if (info == "y" || info == "yes") {
311 return true;
312 }
313 retryCount++;
314 } while (retryCount < MAX_RETRY_COUNT);
315 return (retryCount >= MAX_RETRY_COUNT) ? false : true;
316 }
317
SendRawData(std::string rawData) const318 void HostUpdater::SendRawData(std::string rawData) const
319 {
320 HdcSessionBase *sessionBase = (HdcSessionBase *)clsSession;
321 if (sessionBase == nullptr) {
322 WRITE_LOG(LOG_FATAL, "sessionBase is null");
323 return;
324 }
325 sessionBase->ServerCommand(taskInfo->sessionId, taskInfo->channelId, CMD_KERNEL_ECHO_RAW,
326 reinterpret_cast<uint8_t *>(rawData.data()), rawData.size());
327 }
328 } // namespace Hdc