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 "script_manager_impl.h"
16 #include <cstring>
17 #include <dlfcn.h>
18 #include "dump.h"
19 #include "pkg_manager.h"
20 #include "script_instructionhelper.h"
21 #include "script_interpreter.h"
22 #include "script_utils.h"
23 #include "thread_pool.h"
24 #include "updater_const.h"
25 #include "utils.h"
26
27 using namespace Hpackage;
28
29 namespace Uscript {
30 constexpr const char *LOAD_SCRIPT_NAME = "loadScript.us";
31 constexpr const char *REGISTER_CMD_SCRIPT_NAME = "registerCmd.us";
32
33 static ScriptManagerImpl* g_scriptManager = nullptr;
GetScriptManager(UScriptEnv * env,const Hpackage::HashDataVerifier * verifier)34 ScriptManager* ScriptManager::GetScriptManager(UScriptEnv *env, const Hpackage::HashDataVerifier *verifier)
35 {
36 if (env == nullptr || verifier == nullptr) {
37 USCRIPT_LOGE("Env or verifier is null");
38 return nullptr;
39 }
40 if (g_scriptManager != nullptr) {
41 return g_scriptManager;
42 }
43 g_scriptManager = new (std::nothrow) ScriptManagerImpl(env, verifier);
44 if (g_scriptManager == nullptr) {
45 USCRIPT_LOGE("Create g_scriptManager failed");
46 return nullptr;
47 }
48 if (g_scriptManager->Init() != USCRIPT_SUCCESS) {
49 USCRIPT_LOGE("g_scriptManager init failed");
50 return nullptr;
51 }
52 return g_scriptManager;
53 }
54
ReleaseScriptManager()55 void ScriptManager::ReleaseScriptManager()
56 {
57 if (g_scriptManager != nullptr) {
58 delete g_scriptManager;
59 }
60 g_scriptManager = nullptr;
61 }
62
~ScriptManagerImpl()63 ScriptManagerImpl::~ScriptManagerImpl()
64 {
65 if (threadPool_) {
66 ThreadPool::Destroy();
67 threadPool_ = nullptr;
68 }
69 for (int i = 0; i < MAX_PRIORITY; i++) {
70 scriptFiles_[i].clear();
71 }
72 auto iter1 = scriptInstructions_.begin();
73 while (iter1 != scriptInstructions_.end()) {
74 UScriptInstructionPtr inst = (*iter1).second;
75 delete inst;
76 iter1 = scriptInstructions_.erase(iter1);
77 }
78 scriptInstructions_.clear();
79 ScriptInstructionHelper::ReleaseBasicInstructionHelper();
80 }
81
Init()82 int32_t ScriptManagerImpl::Init()
83 {
84 if (scriptEnv_ == nullptr) {
85 USCRIPT_LOGE("Env null");
86 return USCRIPT_INVALID_PARAM;
87 }
88
89 threadPool_ = ThreadPool::CreateThreadPool(MAX_PRIORITY);
90 if (threadPool_ == nullptr) {
91 USCRIPT_LOGE("Failed to create thread pool");
92 return USCRIPT_INVALID_PARAM;
93 }
94
95 // Register system reserved instructions
96 ScriptInstructionHelper* helper = ScriptInstructionHelper::GetBasicInstructionHelper(this);
97 if (helper == nullptr) {
98 USCRIPT_LOGE("Failed to get helper");
99 return USCRIPT_INVALID_PARAM;
100 }
101 helper->RegisterInstructions();
102
103 // Register customized instructions
104 RegisterInstruction(*helper);
105
106 PkgManager::PkgManagerPtr manager = scriptEnv_->GetPkgManager();
107 if (manager == nullptr) {
108 USCRIPT_LOGE("Failed to get pkg manager");
109 return USCRIPT_INVALID_PARAM;
110 }
111
112 // Register other instructions from scripts
113 int32_t ret = USCRIPT_SUCCESS;
114 const FileInfo *info = manager->GetFileInfo(REGISTER_CMD_SCRIPT_NAME);
115 if (info != nullptr) {
116 ret = ExtractAndExecuteScript(manager, REGISTER_CMD_SCRIPT_NAME);
117 }
118 if (ret != USCRIPT_SUCCESS) {
119 USCRIPT_LOGE("Failed to extract and execute script ");
120 return ret;
121 }
122
123 // Collect scripts
124 ret = ExtractAndExecuteScript(manager, LOAD_SCRIPT_NAME);
125 if (ret != USCRIPT_SUCCESS) {
126 USCRIPT_LOGE("Failed to extract and execute script ");
127 return ret;
128 }
129 return USCRIPT_SUCCESS;
130 }
131
RegisterInstruction(ScriptInstructionHelper & helper)132 int32_t ScriptManagerImpl::RegisterInstruction(ScriptInstructionHelper &helper)
133 {
134 Uscript::UScriptInstructionFactoryPtr factory = scriptEnv_->GetInstructionFactory();
135 if (factory == nullptr) {
136 USCRIPT_LOGE("None factory");
137 return USCRIPT_SUCCESS;
138 }
139
140 for (auto instrName : scriptEnv_->GetInstructionNames()) {
141 // Create instructions and register it.
142 UScriptInstructionPtr instr = nullptr;
143 int32_t ret = factory->CreateInstructionInstance(instr, instrName);
144 if (ret != USCRIPT_SUCCESS) {
145 USCRIPT_LOGE("Failed to create instruction for %s", instrName.c_str());
146 return ret;
147 }
148 helper.AddInstruction(instrName, instr);
149 if (ret != USCRIPT_SUCCESS) {
150 USCRIPT_LOGE("Failed to add instruction for %s", instrName.c_str());
151 return ret;
152 }
153 }
154 return USCRIPT_SUCCESS;
155 }
156
ExtractAndExecuteScript(PkgManager::PkgManagerPtr manager,const std::string & scriptName)157 int32_t ScriptManagerImpl::ExtractAndExecuteScript(PkgManager::PkgManagerPtr manager,
158 const std::string &scriptName)
159 {
160 Updater::UPDATER_INIT_RECORD;
161 PkgManager::StreamPtr outStream = nullptr;
162 const std::string path = Updater::Utils::IsUpdaterMode() ? "/tmp" : Updater::UPDATER_PATH;
163 const FileInfo *info = manager->GetFileInfo(scriptName);
164 if (info == nullptr) {
165 USCRIPT_LOGE("Error to get file info");
166 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
167 return USCRIPT_INVALID_PARAM;
168 }
169 int32_t ret = manager->CreatePkgStream(outStream, path + "/" + scriptName,
170 info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
171 if (ret != USCRIPT_SUCCESS) {
172 USCRIPT_LOGE("Failed to create script stream %s", scriptName.c_str());
173 UPDATER_LAST_WORD(ret);
174 return ret;
175 }
176 ret = manager->ExtractFile(scriptName, outStream);
177 if (ret != USCRIPT_SUCCESS) {
178 manager->ClosePkgStream(outStream);
179 USCRIPT_LOGE("Failed to extract script stream %s", scriptName.c_str());
180 UPDATER_LAST_WORD(ret);
181 return ret;
182 }
183 if (scriptVerifier_ == nullptr || !scriptVerifier_->VerifyHashData(scriptName, outStream)) {
184 manager->ClosePkgStream(outStream);
185 USCRIPT_LOGE("verify script %s by hash signed data failed", scriptName.c_str());
186 UPDATER_LAST_WORD(ret);
187 return USCRIPT_INVALID_SCRIPT;
188 }
189 ret = ScriptInterpreter::ExecuteScript(this, outStream);
190 manager->ClosePkgStream(outStream);
191 if (ret != USCRIPT_SUCCESS) {
192 USCRIPT_LOGE("Failed to ExecuteScript %s", scriptName.c_str());
193 return ret;
194 }
195 return ret;
196 }
197
ExecuteScript(int32_t priority)198 int32_t ScriptManagerImpl::ExecuteScript(int32_t priority)
199 {
200 if (priority >= MAX_PRIORITY || priority < 0) {
201 USCRIPT_LOGE("ExecuteScript priority not support %d", priority);
202 UPDATER_LAST_WORD(USCRIPT_INVALID_PRIORITY, priority);
203 return USCRIPT_INVALID_PRIORITY;
204 }
205 PkgManager::PkgManagerPtr manager = scriptEnv_->GetPkgManager();
206 if (manager == nullptr) {
207 USCRIPT_LOGE("Failed to get pkg manager");
208 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
209 return USCRIPT_INVALID_PARAM;
210 }
211 if (scriptFiles_[priority].size() == 0) {
212 return USCRIPT_SUCCESS;
213 }
214
215 // Execute scripts
216 int32_t threadNumber = threadPool_->GetThreadNumber();
217 Task task;
218 int32_t ret = USCRIPT_SUCCESS;
219 int32_t retCode = USCRIPT_SUCCESS;
220 task.workSize = threadNumber;
221 task.processor = [&](int iter) {
222 for (size_t i = static_cast<size_t>(iter); i < scriptFiles_[priority].size();
223 i += static_cast<size_t>(threadNumber)) {
224 ret = ExtractAndExecuteScript(manager, scriptFiles_[priority][i]);
225 if (ret != USCRIPT_SUCCESS) {
226 USCRIPT_LOGE("Failed to execute script %s", scriptFiles_[priority][i].c_str());
227 retCode = ret;
228 }
229 }
230 };
231 ThreadPool::AddTask(std::move(task));
232 return retCode;
233 }
234
AddInstruction(const std::string & instrName,const UScriptInstructionPtr instruction)235 int32_t ScriptManagerImpl::AddInstruction(const std::string &instrName, const UScriptInstructionPtr instruction)
236 {
237 USCRIPT_LOGI("AddInstruction instrName: %s ", instrName.c_str());
238 if (scriptInstructions_.find(instrName) != scriptInstructions_.end()) {
239 USCRIPT_LOGW("Instruction: %s exist", instrName.c_str());
240 // New instruction has the same name
241 // with already registered instruction,
242 // just override it.
243 delete scriptInstructions_[instrName];
244 }
245 scriptInstructions_[instrName] = instruction;
246 return USCRIPT_SUCCESS;
247 }
248
AddScript(const std::string & scriptName,int32_t priority)249 int32_t ScriptManagerImpl::AddScript(const std::string &scriptName, int32_t priority)
250 {
251 Updater::UPDATER_INIT_RECORD;
252 if (priority < 0 || priority >= MAX_PRIORITY) {
253 USCRIPT_LOGE("Invalid priority %d", priority);
254 UPDATER_LAST_WORD(USCRIPT_INVALID_PRIORITY);
255 return USCRIPT_INVALID_PRIORITY;
256 }
257
258 PkgManager::PkgManagerPtr manager = scriptEnv_->GetPkgManager();
259 if (manager == nullptr) {
260 USCRIPT_LOGE("Failed to get pkg manager");
261 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
262 return USCRIPT_INVALID_PARAM;
263 }
264
265 if (manager->GetFileInfo(scriptName) == nullptr) {
266 USCRIPT_LOGE("Failed to access script %s", scriptName.c_str());
267 UPDATER_LAST_WORD(USCRIPT_INVALID_SCRIPT);
268 return USCRIPT_INVALID_SCRIPT;
269 }
270 scriptFiles_[priority].push_back(scriptName);
271 return USCRIPT_SUCCESS;
272 }
273
FindInstruction(const std::string & instrName)274 UScriptInstruction* ScriptManagerImpl::FindInstruction(const std::string &instrName)
275 {
276 if (scriptInstructions_.find(instrName) == scriptInstructions_.end()) {
277 return nullptr;
278 }
279 return scriptInstructions_[instrName];
280 }
281
GetScriptEnv(const std::string & instrName) const282 UScriptEnv* ScriptManagerImpl::GetScriptEnv(const std::string &instrName) const
283 {
284 return scriptEnv_;
285 }
286 } // namespace Uscript
287