• 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 "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