1/*
2 * Copyright (c) 2024 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
16#include <cerrno>
17#include <cstring>
18#include <dlfcn.h>
19#include <set>
20#include <string>
21#include <unistd.h>
22#include <utility>
23#include <vector>
24
25#include "appspawn_hook.h"
26#include "appspawn_server.h"
27#include "appspawn_service.h"
28#include "appspawn_manager.h"
29#include "appspawn_utils.h"
30#include "command_lexer.h"
31#include "config_policy_utils.h"
32#include "hitrace_meter.h"
33#include "js_runtime.h"
34#include "json_utils.h"
35#include "parameters.h"
36#include "resource_manager.h"
37#ifndef APPSPAWN_TEST
38#include "ace_forward_compatibility.h"
39#include "main_thread.h"
40#include "runtime.h"
41#endif
42
43using namespace OHOS::AppSpawn;
44using namespace OHOS::Global;
45
46#ifdef ASAN_DETECTOR
47static const bool DEFAULT_PRELOAD_VALUE = false;
48#else
49static const bool DEFAULT_PRELOAD_VALUE = true;
50#endif
51static const std::string PRELOAD_JSON_CONFIG("/appspawn_preload.json");
52
53typedef struct TagParseJsonContext {
54    std::set<std::string> modules;
55} ParseJsonContext;
56
57static void GetModules(const cJSON *root, std::set<std::string> &modules)
58{
59    // no config
60    cJSON *modulesJson = cJSON_GetObjectItemCaseSensitive(root, "napi");
61    if (modulesJson == nullptr) {
62        return;
63    }
64
65    uint32_t moduleCount = (uint32_t)cJSON_GetArraySize(modulesJson);
66    for (uint32_t i = 0; i < moduleCount; ++i) {
67        const char *moduleName = cJSON_GetStringValue(cJSON_GetArrayItem(modulesJson, i));
68        if (moduleName == nullptr) {
69            continue;
70        }
71        APPSPAWN_LOGV("moduleName %{public}s", moduleName);
72        if (!modules.count(moduleName)) {
73            modules.insert(moduleName);
74        }
75    }
76}
77
78static int GetModuleSet(const cJSON *root, ParseJsonContext *context)
79{
80    GetModules(root, context->modules);
81    return 0;
82}
83
84static void PreloadModule(void)
85{
86    OHOS::AbilityRuntime::Runtime::Options options;
87    options.lang = OHOS::AbilityRuntime::Runtime::Language::JS;
88    options.loadAce = true;
89    options.preload = true;
90
91    auto runtime = OHOS::AbilityRuntime::Runtime::Create(options);
92    if (!runtime) {
93        APPSPAWN_LOGE("LoadExtendLib: Failed to create runtime");
94        return;
95    }
96
97    ParseJsonContext context = {};
98    (void)ParseJsonConfig("etc/appspawn", PRELOAD_JSON_CONFIG.c_str(), GetModuleSet, &context);
99    for (std::string moduleName : context.modules) {
100        APPSPAWN_LOGI("moduleName %{public}s", moduleName.c_str());
101        runtime->PreloadSystemModule(moduleName);
102    }
103    // Save preloaded runtime
104    OHOS::AbilityRuntime::Runtime::SavePreloaded(std::move(runtime));
105}
106
107static void LoadExtendLib(void)
108{
109    const char *acelibdir = OHOS::Ace::AceForwardCompatibility::GetAceLibName();
110    APPSPAWN_LOGI("LoadExtendLib: Start calling dlopen acelibdir.");
111    void *aceAbilityLib = dlopen(acelibdir, RTLD_NOW | RTLD_LOCAL);
112    APPSPAWN_CHECK(aceAbilityLib != nullptr, return, "Fail to dlopen %{public}s, [%{public}s]", acelibdir, dlerror());
113    APPSPAWN_LOGI("LoadExtendLib: Success to dlopen %{public}s", acelibdir);
114
115    OHOS::AppExecFwk::MainThread::PreloadExtensionPlugin();
116    bool preload = OHOS::system::GetBoolParameter("persist.appspawn.preload", DEFAULT_PRELOAD_VALUE);
117    if (!preload) {
118        APPSPAWN_LOGI("LoadExtendLib: Do not preload JS VM");
119        return;
120    }
121
122    APPSPAWN_LOGI("LoadExtendLib: Start preload JS VM");
123    SetTraceDisabled(true);
124    PreloadModule();
125    SetTraceDisabled(false);
126
127    OHOS::Ace::AceForwardCompatibility::ReclaimFileCache(getpid());
128    Resource::ResourceManager *systemResMgr = Resource::GetSystemResourceManagerNoSandBox();
129    APPSPAWN_CHECK(systemResMgr != nullptr, return, "Fail to get system resource manager");
130    APPSPAWN_LOGI("LoadExtendLib: End preload JS VM");
131}
132
133APPSPAWN_STATIC void LoadExtendCJLib(void)
134{
135    const char *acelibdir = OHOS::Ace::AceForwardCompatibility::GetAceLibName();
136    APPSPAWN_LOGI("LoadExtendLib: Start calling dlopen acelibdir.");
137    void *aceAbilityLib = dlopen(acelibdir, RTLD_NOW | RTLD_LOCAL);
138    APPSPAWN_CHECK(aceAbilityLib != nullptr, return, "Fail to dlopen %{public}s, [%{public}s]", acelibdir, dlerror());
139    APPSPAWN_LOGI("LoadExtendLib: Success to dlopen %{public}s", acelibdir);
140}
141
142APPSPAWN_STATIC int BuildFdInfoMap(const AppSpawnMsgNode *message, std::map<std::string, int> &fdMap, int isColdRun)
143{
144    APPSPAWN_CHECK_ONLY_EXPER(message != NULL && message->buffer != NULL, return -1);
145    APPSPAWN_CHECK_ONLY_EXPER(message->tlvOffset != NULL, return -1);
146    int findFdIndex = 0;
147    AppSpawnMsgReceiverCtx recvCtx;
148    if (!isColdRun) {
149        APPSPAWN_CHECK_ONLY_EXPER(message->connection != NULL, return -1);
150        recvCtx = message->connection->receiverCtx;
151        if (recvCtx.fdCount <= 0) {
152            APPSPAWN_LOGI("no need to build fd info %{public}d, %{public}d", recvCtx.fds != NULL, recvCtx.fdCount);
153            return 0;
154        }
155    }
156    for (uint32_t index = TLV_MAX; index < (TLV_MAX + message->tlvCount); index++) {
157        if (message->tlvOffset[index] == INVALID_OFFSET) {
158            return -1;
159        }
160        uint8_t *data = message->buffer + message->tlvOffset[index];
161        if (((AppSpawnTlv *)data)->tlvType != TLV_MAX) {
162            continue;
163        }
164        AppSpawnTlvExt *tlv = (AppSpawnTlvExt *)data;
165        if (strcmp(tlv->tlvName, MSG_EXT_NAME_APP_FD) != 0) {
166            continue;
167        }
168        std::string key((char *)data + sizeof(AppSpawnTlvExt));
169        if (isColdRun) {
170            std::string envKey = std::string(APP_FDENV_PREFIX) + key;
171            char *fdChar = getenv(envKey.c_str());
172            APPSPAWN_CHECK(fdChar != NULL, continue, "getfd from env failed %{public}s", envKey.c_str());
173            int fd = atoi(fdChar);
174            APPSPAWN_CHECK(fd > 0, continue, "getfd from env atoi errno %{public}s,%{public}d", envKey.c_str(), fd);
175            fdMap[key] = fd;
176        } else {
177            APPSPAWN_CHECK(findFdIndex < recvCtx.fdCount && recvCtx.fds[findFdIndex] > 0,
178                return -1, "invalid fd info  %{public}d %{public}d", findFdIndex, recvCtx.fds[findFdIndex]);
179            fdMap[key] = recvCtx.fds[findFdIndex++];
180            if (findFdIndex >= recvCtx.fdCount) {
181                break;
182            }
183        }
184    }
185    return 0;
186}
187
188static int RunChildThread(const AppSpawnMgr *content, const AppSpawningCtx *property)
189{
190    std::string checkExit;
191    if (OHOS::system::GetBoolParameter("persist.init.debug.checkexit", true)) {
192        checkExit = std::to_string(getpid());
193    }
194    setenv(APPSPAWN_CHECK_EXIT, checkExit.c_str(), true);
195    if (CheckAppMsgFlagsSet(property, APP_FLAGS_CHILDPROCESS)) {
196        std::map<std::string, int> fdMap;
197        BuildFdInfoMap(property->message, fdMap, IsColdRunMode(content));
198        AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
199        OHOS::AppExecFwk::MainThread::StartChild(fdMap);
200    } else {
201        AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
202        OHOS::AppExecFwk::MainThread::Start();
203    }
204    unsetenv(APPSPAWN_CHECK_EXIT);
205    return 0;
206}
207
208APPSPAWN_STATIC int RunChildByRenderCmd(const AppSpawnMgr *content, const AppSpawningCtx *property)
209{
210    uint32_t len = 0;
211    char *renderCmd = reinterpret_cast<char *>(GetAppPropertyExt(property, MSG_EXT_NAME_RENDER_CMD, &len));
212    if (renderCmd == NULL || !IsDeveloperModeOn(property)) {
213        APPSPAWN_LOGE("Denied launching a native process: not in developer mode");
214        return -1;
215    }
216    APPSPAWN_LOGI("renderCmd %{public}s", renderCmd);
217    std::vector<std::string> args;
218    std::string command(renderCmd);
219    CommandLexer lexer(command);
220    if (!lexer.GetAllArguments(args)) {
221        return -1;
222    }
223    if (args.empty()) {
224        APPSPAWN_LOGE("Failed to run a native process: empty command %{public}s", renderCmd);
225        return -1;
226    }
227    std::vector<char *> options;
228    for (const auto &arg : args) {
229        options.push_back(const_cast<char *>(arg.c_str()));
230    }
231    options.push_back(nullptr);
232    // clear appspawn env, do not user any content and property
233    AppSpawnEnvClear((AppSpawnContent *)&content->content, (AppSpawnClient *)&property->client);
234    execvp(args[0].c_str(), options.data());
235    // If it succeeds calling execvp, it never returns.
236    int err = errno;
237    APPSPAWN_LOGE("Failed to launch a native process with execvp: %{public}s", strerror(err));
238    return 0;
239}
240
241static int RunChildProcessor(AppSpawnContent *content, AppSpawnClient *client)
242{
243    EnableCache();
244    APPSPAWN_CHECK(client != NULL && content != NULL, return -1, "Invalid client");
245    AppSpawningCtx *property = reinterpret_cast<AppSpawningCtx *>(client);
246    int ret = 0;
247    if (GetAppSpawnMsgType(property) == MSG_SPAWN_NATIVE_PROCESS) {
248        ret = RunChildByRenderCmd(reinterpret_cast<AppSpawnMgr *>(content), property);
249    } else {
250        ret = RunChildThread(reinterpret_cast<AppSpawnMgr *>(content), property);
251    }
252    return ret;
253}
254
255APPSPAWN_STATIC int PreLoadAppSpawn(AppSpawnMgr *content)
256{
257    if (IsNWebSpawnMode(content)) {
258        return 0;
259    }
260    // register
261    RegChildLooper(&content->content, RunChildProcessor);
262    if (strcmp(content->content.longProcName, CJAPPSPAWN_SERVER_NAME) == 0) {
263        LoadExtendCJLib();
264        return 0;
265    }
266    LoadExtendLib();
267    return 0;
268}
269
270MODULE_CONSTRUCTOR(void)
271{
272    APPSPAWN_LOGV("Load ace module ...");
273    AddPreloadHook(HOOK_PRIO_HIGHEST, PreLoadAppSpawn);
274}
275