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_instructionhelper.h"
16#include <dlfcn.h>
17#include <set>
18#include "dump.h"
19#include "scope_guard.h"
20#include "script_basicinstruction.h"
21#include "script_loadscript.h"
22#include "script_manager_impl.h"
23#include "script_registercmd.h"
24#include "script_updateprocesser.h"
25#include "script_utils.h"
26
27using namespace BasicInstruction;
28using namespace Updater;
29
30namespace Uscript {
31static std::set<std::string> g_reservedInstructions = {
32    "LoadScript", "RegisterCmd", "abort", "assert", "concat",
33    "is_substring", "stdout", "sleep", "set_progress", "ui_print",
34    "show_progress", "set_proportion"
35    };
36
37static ScriptInstructionHelper* g_instructionHelper = nullptr;
38
39ScriptInstructionHelper* ScriptInstructionHelper::GetBasicInstructionHelper(ScriptManagerImpl *impl)
40{
41    if (g_instructionHelper == nullptr) {
42        if (impl == nullptr) {
43            return nullptr;
44        }
45        g_instructionHelper = new ScriptInstructionHelper(impl);
46    }
47    return g_instructionHelper;
48}
49
50void ScriptInstructionHelper::ReleaseBasicInstructionHelper()
51{
52    if (g_instructionHelper != nullptr) {
53        delete g_instructionHelper;
54    }
55    g_instructionHelper = nullptr;
56}
57
58ScriptInstructionHelper::~ScriptInstructionHelper()
59{
60    if (instrLib_ != nullptr) {
61        dlclose(instrLib_);
62    }
63    instrLib_ = nullptr;
64}
65
66int32_t ScriptInstructionHelper::RegisterInstructions() const
67{
68    scriptManager_->AddInstruction("RegisterCmder", new ScriptRegisterCmd());
69    scriptManager_->AddInstruction("LoadScript", new ScriptLoadScript());
70    scriptManager_->AddInstruction("Stdout", new UScriptInstructionStdout());
71    scriptManager_->AddInstruction("Abort", new UScriptInstructionAbort());
72    scriptManager_->AddInstruction("Assert", new UScriptInstructionAssert());
73    scriptManager_->AddInstruction("Sleep", new UScriptInstructionSleep());
74    scriptManager_->AddInstruction("Concat", new UScriptInstructionConcat());
75    scriptManager_->AddInstruction("IsSubString", new UScriptInstructionIsSubString());
76    scriptManager_->AddInstruction("set_progress", new UScriptInstructionSetProcess());
77    scriptManager_->AddInstruction("show_progress", new UScriptInstructionShowProcess());
78    scriptManager_->AddInstruction("ui_print", new UScriptInstructionUiPrint());
79    scriptManager_->AddInstruction("DeleteFile", new UScriptInstructionDeleteFile());
80    scriptManager_->AddInstruction("DeleteDir", new UScriptInstructionDeleteDir());
81    scriptManager_->AddInstruction("set_proportion", new UScriptInstructionSetProportion());
82    return USCRIPT_SUCCESS;
83}
84
85bool ScriptInstructionHelper::IsReservedInstruction(const std::string &scriptName) const
86{
87    if (g_reservedInstructions.find(scriptName) != g_reservedInstructions.end()) {
88        return true;
89    }
90    return false;
91}
92
93int32_t ScriptInstructionHelper::AddScript(const std::string &scriptName, int32_t priority) const
94{
95    return scriptManager_->AddScript(scriptName, priority);
96}
97
98int32_t ScriptInstructionHelper::AddInstruction(const std::string &instrName, const UScriptInstructionPtr instr)
99{
100    if (IsReservedInstruction(instrName)) {
101        USCRIPT_LOGE(" %s reserved", instrName.c_str());
102        return USCRIPT_ERROR_REVERED;
103    }
104    return scriptManager_->AddInstruction(instrName, instr);
105}
106
107int32_t ScriptInstructionHelper::RegisterAddInstruction(const Uscript::UScriptInstructionFactoryPtr factory,
108    const std::string &instrName)
109{
110    UPDATER_INIT_RECORD;
111    // Create instruction and register it
112    UScriptInstructionPtr instr = nullptr;
113    int32_t ret = factory->CreateInstructionInstance(instr, instrName);
114    if (ret != USCRIPT_SUCCESS || instr == nullptr) {
115        USCRIPT_LOGE("Fail to create instruction for %s", instrName.c_str());
116        UPDATER_LAST_WORD(ret);
117        return ret == USCRIPT_SUCCESS ? USCRIPT_ERROR_CREATE_OBJ : USCRIPT_NOTEXIST_INSTRUCTION;
118    }
119
120    ret = AddInstruction(instrName, instr);
121    if (ret != USCRIPT_SUCCESS) {
122        USCRIPT_LOGE("Fail to add instruction for %s", instrName.c_str());
123        UPDATER_LAST_WORD(ret);
124        // ret is USCRIPT_ERROR_REVERED, instr register failed, can be deleted
125        delete instr;
126        instr = nullptr;
127    }
128    // ScriptManagerImpl::AddInstruction has saved instr, don't delete it here!!!
129    return ret;
130}
131
132int32_t ScriptInstructionHelper::RegisterUserInstruction(const std::string& libName,
133    const std::string &instrName)
134{
135    // first get realpath of libName, then compare with realLibName
136    UPDATER_INIT_RECORD;
137    char *realPath = realpath(libName.c_str(), nullptr);
138    if (realPath == nullptr) {
139        USCRIPT_LOGE("realPath is NULL %s", libName.c_str());
140        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
141        return USCRIPT_INVALID_PARAM;
142    }
143    std::string realLibName = realPath;
144    free(realPath);
145    if (!userInstrLibName_.empty() && userInstrLibName_.compare(realLibName) != 0) {
146        USCRIPT_LOGE("Lib name must be equal %s ", realLibName.c_str());
147        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
148        return USCRIPT_INVALID_PARAM;
149    }
150
151    userInstrLibName_.assign(realLibName);
152    Uscript::UScriptInstructionFactoryPtr factory = nullptr;
153    if (instrLib_ == nullptr) {
154        instrLib_ = dlopen(realLibName.c_str(), RTLD_LAZY | RTLD_LOCAL);
155    }
156    if (instrLib_ == nullptr) {
157        USCRIPT_LOGE("Fail to dlopen %s , dlerror: %s", libName.c_str(), dlerror());
158        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
159        return USCRIPT_INVALID_PARAM;
160    }
161    auto pGetInstructionFactory =
162        (Uscript::UScriptInstructionFactoryPtr(*)())dlsym(instrLib_, "GetInstructionFactory");
163    auto pReleaseInstructionFactory =
164        (void(*)(Uscript::UScriptInstructionFactoryPtr))dlsym(instrLib_, "ReleaseInstructionFactory");
165    if (pReleaseInstructionFactory == nullptr || pGetInstructionFactory == nullptr) {
166        USCRIPT_LOGE("Fail to get sym %s", libName.c_str());
167        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
168        return USCRIPT_INVALID_PARAM;
169    }
170    factory = pGetInstructionFactory();
171    if (factory == nullptr) {
172        USCRIPT_LOGE("Fail to create instruction factory for %s", instrName.c_str());
173        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
174        return USCRIPT_INVALID_PARAM;
175    }
176    ON_SCOPE_EXIT(freeFactory) {
177        pReleaseInstructionFactory(factory);
178    };
179
180    return RegisterAddInstruction(factory, instrName);
181}
182
183int32_t ScriptInstructionHelper::RegisterUserInstruction(const std::string &instrName,
184    Uscript::UScriptInstructionFactory *factory)
185{
186    if (factory == nullptr) {
187        USCRIPT_LOGE("%s factory is null", instrName.c_str());
188        return USCRIPT_INVALID_PARAM;
189    }
190
191    // Create instruction and register it
192    UScriptInstructionPtr instr = nullptr;
193    int32_t ret = factory->CreateInstructionInstance(instr, instrName);
194    if (ret != USCRIPT_SUCCESS || instr == nullptr) {
195        USCRIPT_LOGE("Fail to create instruction for %s", instrName.c_str());
196        // when instr == nullptr && ret == USCRIPT_SUCCESS, shouldn't return USCRIPT_SUCCESS
197        return ret == USCRIPT_SUCCESS ? USCRIPT_ERROR_CREATE_OBJ : USCRIPT_NOTEXIST_INSTRUCTION;
198    }
199
200    ret = AddInstruction(instrName, instr);
201    if (ret != USCRIPT_SUCCESS) {
202        USCRIPT_LOGE("Fail to add instruction for %s", instrName.c_str());
203        delete instr;
204        instr = nullptr;
205        return ret;
206    }
207
208    USCRIPT_LOGD("RegisterUserInstruction %s successfull", instrName.c_str());
209    return ret;
210}
211} // namespace Uscript
212