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 "include/updater/updater.h"
16 #include <cerrno>
17 #include <chrono>
18 #include <cstdio>
19 #include <iomanip>
20 #include <string>
21 #include <sched.h>
22 #include <syscall.h>
23 #include <sys/stat.h>
24 #include <sys/statvfs.h>
25 #include <sys/wait.h>
26 #include <thread>
27 #include <unistd.h>
28 #include <vector>
29 #include "fs_manager/mount.h"
30 #include "log/log.h"
31 #include "package/pkg_manager.h"
32 #include "package/packages_info.h"
33 #include "progress_bar.h"
34 #include "text_label.h"
35 #include "updater/updater_const.h"
36 #include "updater_main.h"
37 #include "updater_ui.h"
38 #include "utils.h"
39
40 namespace updater {
41 using updater::utils::SplitString;
42 using updater::utils::Trim;
43 using namespace hpackage;
44
45 int g_percentage;
46 int g_tmpProgressValue;
47 int g_tmpValue;
48
ExtractUpdaterBinary(PkgManager::PkgManagerPtr manager,const std::string & updaterBinary)49 static int32_t ExtractUpdaterBinary(PkgManager::PkgManagerPtr manager, const std::string &updaterBinary)
50 {
51 PkgManager::StreamPtr outStream = nullptr;
52 int32_t ret = manager->CreatePkgStream(outStream, G_WORK_PATH + updaterBinary, 0, PkgStream::PkgStreamType_Write);
53 UPDATER_ERROR_CHECK(ret == PKG_SUCCESS, "ExtractUpdaterBinary create stream fail", return UPDATE_CORRUPT);
54 ret = manager->ExtractFile(updaterBinary, outStream);
55 manager->ClosePkgStream(outStream);
56 return ret;
57 }
58
GetUpdatePackageInfo(PkgManager::PkgManagerPtr pkgManager,const std::string & path)59 int GetUpdatePackageInfo(PkgManager::PkgManagerPtr pkgManager, const std::string &path)
60 {
61 std::vector<std::string> components;
62 if (pkgManager == nullptr) {
63 LOG(ERROR) << "Fail to GetPackageInstance";
64 return UPDATE_CORRUPT;
65 }
66 int32_t ret = pkgManager->LoadPackage(path, utils::GetCertName(), components);
67 if (ret != PKG_SUCCESS) {
68 LOG(INFO) << "LoadPackage fail ret :"<< ret;
69 return ret;
70 }
71 return PKG_SUCCESS;
72 }
73
UpdatePreProcess(PkgManager::PkgManagerPtr pkgManager,const std::string & path)74 int UpdatePreProcess(PkgManager::PkgManagerPtr pkgManager, const std::string &path)
75 {
76 int ret = -1;
77 if (pkgManager == nullptr) {
78 return PKG_INVALID_VERSION;
79 }
80 PackagesInfoPtr pkginfomanager = PackagesInfo::GetPackagesInfoInstance();
81 UpgradePkgInfo* outPkgInfo = (UpgradePkgInfo*)pkgManager->GetPackageInfo(path);
82 if (pkginfomanager == nullptr || outPkgInfo == nullptr) {
83 LOG(ERROR) << "Fail to GetPackageInstance";
84 return PKG_INVALID_VERSION;
85 }
86 std::vector<std::string> targetVersions = pkginfomanager->GetOTAVersion(pkgManager, "/version_list", G_WORK_PATH);
87 for (size_t i = 0; i < targetVersions.size(); i++) {
88 ret = outPkgInfo->softwareVersion.compare(targetVersions[i]);
89 if (ret == 0) {
90 break;
91 }
92 }
93 // check broad info
94 if (ret != 0) {
95 PackagesInfo::ReleasePackagesInfoInstance(pkginfomanager);
96 return ret;
97 }
98 LOG(WARNING) << "Check version success ";
99 std::string localBoardId = utils::GetLocalBoardId();
100 if (localBoardId.empty()) {
101 PackagesInfo::ReleasePackagesInfoInstance(pkginfomanager);
102 return 0;
103 }
104 std::vector<std::string> boardIdList = pkginfomanager->GetBoardID(pkgManager, "/board_list", "");
105 for (size_t i = 0; i < boardIdList.size(); i++) {
106 ret = localBoardId.compare(boardIdList[i]);
107 if (ret == 0) {
108 LOG(WARNING) << "Check board list success ";
109 break;
110 }
111 }
112 PackagesInfo::ReleasePackagesInfoInstance(pkginfomanager);
113 return ret;
114 }
115
ProgressSmoothHandler()116 static void ProgressSmoothHandler()
117 {
118 while (g_tmpProgressValue < FULL_PERCENT_PROGRESS) {
119 int gap = FULL_PERCENT_PROGRESS - g_tmpProgressValue;
120 int increase = gap / PROGRESS_VALUE_CONST;
121 g_tmpProgressValue += increase;
122 if (g_tmpProgressValue >= FULL_PERCENT_PROGRESS || increase == 0) {
123 break;
124 } else {
125 if (GetProgressBar() != nullptr) {
126 GetProgressBar()->SetProgressValue(g_tmpProgressValue);
127 }
128 std::this_thread::sleep_for(std::chrono::milliseconds(SHOW_FULL_PROGRESS_TIME));
129 }
130 }
131 }
132
DoInstallUpdaterPackage(PkgManager::PkgManagerPtr pkgManager,const std::string & packagePath,int retryCount)133 UpdaterStatus DoInstallUpdaterPackage(PkgManager::PkgManagerPtr pkgManager, const std::string &packagePath,
134 int retryCount)
135 {
136 if (GetProgressBar() != nullptr) {
137 GetProgressBar()->Hide();
138 }
139 ShowUpdateFrame(true);
140 UPDATER_ERROR_CHECK(pkgManager != nullptr, "Fail to GetPackageInstance", return UPDATE_CORRUPT);
141 TextLabel *updateInfoLabel = GetUpdateInfoLabel();
142 UPDATER_ERROR_CHECK(updateInfoLabel != nullptr, "Fail to updateInfoLabel", return UPDATE_CORRUPT);
143
144 LOG(INFO) << "Verify package...";
145 updateInfoLabel->SetText("Verify package...");
146
147 UPDATER_ERROR_CHECK(access(packagePath.c_str(), 0) == 0, "package is not exist",
148 ShowText(GetUpdateInfoLabel(), "package is not exist"); return UPDATE_CORRUPT);
149 if (retryCount > 0) {
150 LOG(INFO) << "Retry for " << retryCount << " time(s)";
151 } else {
152 UpdaterStatus ret = static_cast<UpdaterStatus>(IsSpaceCapacitySufficient(packagePath));
153 // Only handle UPATE_ERROR and UPDATE_SUCCESS here.
154 // If it returns UPDATE_CORRUPT, which means something wrong with package manager.
155 // Let package verify handle this.
156 if (ret == UPDATE_SPACE_NOTENOUGH || ret == UPDATE_ERROR) {
157 return ret;
158 } else if (ret == UPDATE_SUCCESS) {
159 pkgManager = PkgManager::GetPackageInstance();
160 }
161 }
162
163 int32_t verifyret = GetUpdatePackageInfo(pkgManager, packagePath);
164 UPDATER_ERROR_CHECK(verifyret == PKG_SUCCESS, "Verify package Fail...",
165 ShowText(GetUpdateInfoLabel(), "Verify package Fail..."); return UPDATE_CORRUPT);
166 LOG(INFO) << "Package verified. start to install package...";
167 int32_t versionRet = UpdatePreProcess(pkgManager, packagePath);
168 UPDATER_ERROR_CHECK(versionRet == PKG_SUCCESS, "Version Check Fail...", return UPDATE_CORRUPT);
169
170 int maxTemperature;
171 UpdaterStatus updateRet = StartUpdaterProc(pkgManager, packagePath, retryCount, maxTemperature);
172 if (updateRet == UPDATE_SUCCESS) {
173 ProgressSmoothHandler();
174 if (GetProgressBar() != nullptr) {
175 GetProgressBar()->SetProgressValue(FULL_PERCENT_PROGRESS);
176 }
177 updateInfoLabel->SetText("Update success, reboot now");
178 std::this_thread::sleep_for(std::chrono::milliseconds(SHOW_FULL_PROGRESS_TIME));
179 LOG(INFO)<< "update success , do reboot now";
180 } else {
181 updateInfoLabel->SetText("Install failed.");
182 LOG(ERROR) << "Install package failed.";
183 }
184 return updateRet;
185 }
HandleProgressSet(const std::vector<std::string> & output)186 static void HandleProgressSet(const std::vector<std::string> &output)
187 {
188 UPDATER_ERROR_CHECK(output.size() >= DEFAULT_PROCESS_NUM, "check output fail", return);
189 auto outputInfo = Trim(output[1]);
190 float frac = 0.0;
191 frac = std::stof(output[1]);
192 if (frac >= -EPSINON && frac <= EPSINON) {
193 return;
194 } else {
195 g_tmpProgressValue = static_cast<int>(frac * g_percentage);
196 }
197 if (frac >= FULL_EPSINON && g_tmpValue + g_percentage < FULL_PERCENT_PROGRESS) {
198 g_tmpValue += g_percentage;
199 return;
200 }
201 g_tmpProgressValue = g_tmpProgressValue + g_tmpValue;
202 if (g_tmpProgressValue == 0) {
203 return;
204 }
205 if (GetProgressBar() != nullptr) {
206 GetProgressBar()->SetProgressValue(g_tmpProgressValue);
207 }
208 }
209
HandleChildOutput(const std::string & buffer,int32_t bufferLen,bool & retryUpdate)210 static void HandleChildOutput(const std::string &buffer, int32_t bufferLen,
211 bool &retryUpdate)
212 {
213 UPDATER_CHECK_ONLY_RETURN(bufferLen != 0, return);
214 TextLabel *updateInfoLabel = GetUpdateInfoLabel();
215 UPDATER_ERROR_CHECK(updateInfoLabel != nullptr, "Fail to updateInfoLabel", return);
216
217 std::string str = buffer;
218 std::vector<std::string> output = SplitString(str, ":");
219 UPDATER_ERROR_CHECK(output.size() >= 1, "check output fail", return);
220 auto outputHeader = Trim(output[0]);
221 if (outputHeader == "ui_log") {
222 UPDATER_ERROR_CHECK(output.size() >= DEFAULT_PROCESS_NUM, "check output fail", return);
223 auto outputInfo = Trim(output[1]);
224 } else if (outputHeader == "write_log") {
225 UPDATER_ERROR_CHECK(output.size() >= DEFAULT_PROCESS_NUM, "check output fail", return);
226 auto outputInfo = Trim(output[1]);
227 LOG(INFO) << outputInfo;
228 } else if (outputHeader == "retry_update") {
229 retryUpdate = true;
230 } else if (outputHeader == "show_progress") {
231 UPDATER_ERROR_CHECK(output.size() >= DEFAULT_PROCESS_NUM, "check output fail", return);
232 auto outputInfo = Trim(output[1]);
233 float frac;
234 std::vector<std::string> progress = SplitString(outputInfo, ",");
235 if (progress.size() != DEFAULT_PROCESS_NUM) {
236 LOG(ERROR) << "show progress with wrong arguments";
237 } else {
238 if (GetProgressBar() != nullptr) {
239 GetProgressBar()->Show();
240 }
241 updateInfoLabel->SetText("Start to install package.");
242 frac = std::stof(progress[0]);
243 g_percentage = static_cast<int>(frac * FULL_PERCENT_PROGRESS);
244 }
245 } else if (outputHeader == "set_progress") {
246 HandleProgressSet(output);
247 } else {
248 LOG(DEBUG) << "Child process returns unexpected message: " << outputHeader;
249 }
250 }
251
StartUpdaterProc(PkgManager::PkgManagerPtr pkgManager,const std::string & packagePath,int retryCount,int & maxTemperature)252 UpdaterStatus StartUpdaterProc(PkgManager::PkgManagerPtr pkgManager, const std::string &packagePath,
253 int retryCount, int &maxTemperature)
254 {
255 int pfd[DEFAULT_PIPE_NUM]; /* communication between parent and child */
256 UPDATER_FILE_CHECK(pipe(pfd) >= 0, "Create pipe failed: ", return UPDATE_ERROR);
257 UPDATER_ERROR_CHECK(pkgManager != nullptr, "Fail to GetPackageInstance", return UPDATE_CORRUPT);
258 int pipeRead = pfd[0];
259 int pipeWrite = pfd[1];
260 UPDATER_ERROR_CHECK(ExtractUpdaterBinary(pkgManager, UPDATER_BINARY) == 0,
261 "Updater: cannot extract updater binary from update package.", return UPDATE_CORRUPT);
262 g_tmpProgressValue = 0;
263 if (GetProgressBar() != nullptr) {
264 GetProgressBar()->SetProgressValue(0);
265 }
266 pid_t pid = fork();
267 UPDATER_CHECK_ONLY_RETURN(pid >= 0, ERROR_CODE(CODE_FORK_FAIL); return UPDATE_ERROR);
268 if (pid == 0) { // child
269 close(pipeRead); // close read endpoint
270 std::string fullPath = G_WORK_PATH + UPDATER_BINARY;
271 #ifdef UPDATER_UT
272 if (packagePath.find("updater_binary_abnormal") != std::string::npos) {
273 fullPath = "/system/bin/updater_binary_abnormal";
274 } else {
275 fullPath = "/system/bin/test_update_binary";
276 }
277 #endif
278 UPDATER_ERROR_CHECK_NOT_RETURN(chmod(fullPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0,
279 "Failed to change mode");
280
281 // Set process scheduler to normal if current scheduler is
282 // SCHED_FIFO, which may cause bad performance.
283 int policy = syscall(SYS_sched_getscheduler, getpid());
284 if (policy == -1) {
285 LOG(INFO) << "Cannnot get current process scheduler";
286 } else if (policy == SCHED_FIFO) {
287 LOG(DEBUG) << "Current process with scheduler SCHED_FIFO";
288 struct sched_param sp = {
289 .sched_priority = 0,
290 };
291 if (syscall(SYS_sched_setscheduler, getpid(), SCHED_OTHER, &sp) < 0) {
292 LOG(WARNING) << "Cannot set current process schedule with SCHED_OTHER";
293 }
294 }
295 if (retryCount > 0) {
296 execl(fullPath.c_str(), packagePath.c_str(), std::to_string(pipeWrite).c_str(), "retry", nullptr);
297 } else {
298 execl(fullPath.c_str(), packagePath.c_str(), std::to_string(pipeWrite).c_str(), nullptr);
299 }
300 LOG(INFO) << "Execute updater binary failed: " << errno;
301 exit(-1);
302 }
303
304 close(pipeWrite); // close write endpoint
305 char buffer[MAX_BUFFER_SIZE] = {0};
306 bool retryUpdate = false;
307 FILE* fromChild = fdopen(pipeRead, "r");
308 UPDATER_ERROR_CHECK(fromChild != nullptr, "fdopen pipeRead failed", return UPDATE_ERROR);
309 while (fgets(buffer, MAX_BUFFER_SIZE - 1, fromChild) != nullptr) {
310 size_t n = strlen(buffer);
311 if (n > 0 && buffer[n - 1] == '\n') {
312 buffer[n - 1] = '\0';
313 }
314 HandleChildOutput(buffer, MAX_BUFFER_SIZE, retryUpdate);
315 }
316 fclose(fromChild);
317
318 int status;
319 waitpid(pid, &status, 0);
320 UPDATER_CHECK_ONLY_RETURN(!retryUpdate, return UPDATE_RETRY);
321 UPDATER_ERROR_CHECK(!(!WIFEXITED(status) || WEXITSTATUS(status) != 0),
322 "Updater process exit with status: " << WEXITSTATUS(status), return UPDATE_ERROR);
323 LOG(DEBUG) << "Updater process finished.";
324 return UPDATE_SUCCESS;
325 }
326 } // namespace updater
327