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