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