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 "appspawn_modulemgr.h"
17
18#include "appspawn_hook.h"
19#include "appspawn_manager.h"
20#include "appspawn_utils.h"
21#include "hookmgr.h"
22#include "modulemgr.h"
23
24typedef struct {
25    const AppSpawnContent *content;
26    const AppSpawnedProcessInfo *appInfo;
27} AppSpawnAppArg;
28
29static struct {
30    MODULE_MGR *moduleMgr;
31    AppSpawnModuleType type;
32    const char *moduleName;
33} g_moduleMgr[MODULE_MAX] = {
34    {NULL, MODULE_DEFAULT, "appspawn"},
35    {NULL, MODULE_APPSPAWN, "appspawn/appspawn"},
36    {NULL, MODULE_NWEBSPAWN, "appspawn/nwebspawn"},
37    {NULL, MODULE_COMMON, "appspawn/common"},
38    {NULL, MODULE_NATIVESPAWN, "appspawn/nativespawn"},
39};
40static HOOK_MGR *g_appspawnHookMgr = NULL;
41
42int AppSpawnModuleMgrInstall(const char *moduleName)
43{
44    if (moduleName == NULL) {
45        return -1;
46    }
47    int type = MODULE_DEFAULT;
48    if (g_moduleMgr[type].moduleMgr == NULL) {
49        g_moduleMgr[type].moduleMgr = ModuleMgrCreate(g_moduleMgr[type].moduleName);
50    }
51    if (g_moduleMgr[type].moduleMgr == NULL) {
52        return -1;
53    }
54#ifndef APPSPAWN_TEST
55    return ModuleMgrInstall(g_moduleMgr[type].moduleMgr, moduleName, 0, NULL);
56#else
57    return 0;
58#endif
59}
60
61void AppSpawnModuleMgrUnInstall(int type)
62{
63    if ((type < 0) || (type >= MODULE_MAX)) {
64        return;
65    }
66    if (g_moduleMgr[type].moduleMgr == NULL) {
67        return;
68    }
69    ModuleMgrDestroy(g_moduleMgr[type].moduleMgr);
70    g_moduleMgr[type].moduleMgr = NULL;
71}
72
73int AppSpawnLoadAutoRunModules(int type)
74{
75    if ((type < 0) || (type >= MODULE_MAX)) {
76        return -1;
77    }
78    if (g_moduleMgr[type].moduleMgr != NULL) {
79        return 0;
80    }
81    APPSPAWN_LOGI("AppSpawnLoadAutoRunModules: %{public}d moduleName: %{public}s", type, g_moduleMgr[type].moduleName);
82#ifndef APPSPAWN_TEST
83    g_moduleMgr[type].moduleMgr = ModuleMgrScan(g_moduleMgr[type].moduleName);
84    return g_moduleMgr[type].moduleMgr == NULL ? -1 : 0;
85#else
86    return 0;
87#endif
88}
89
90HOOK_MGR *GetAppSpawnHookMgr(void)
91{
92    if (g_appspawnHookMgr != NULL) {
93        return g_appspawnHookMgr;
94    }
95    g_appspawnHookMgr = HookMgrCreate("appspawn");
96    return g_appspawnHookMgr;
97}
98
99void DeleteAppSpawnHookMgr(void)
100{
101    HookMgrDestroy(g_appspawnHookMgr);
102    g_appspawnHookMgr = NULL;
103}
104
105static void UpdateAppSpawnTime(int used)
106{
107    if (used < GetAppSpawnMgr()->spawnTime.minAppspawnTime) {
108        GetAppSpawnMgr()->spawnTime.minAppspawnTime = used;
109        APPSPAWN_LOGI("spawn min time: %{public}d", GetAppSpawnMgr()->spawnTime.minAppspawnTime);
110    }
111    if (used > GetAppSpawnMgr()->spawnTime.maxAppspawnTime) {
112        GetAppSpawnMgr()->spawnTime.maxAppspawnTime = used;
113        APPSPAWN_LOGI("spawn max time: %{public}d", GetAppSpawnMgr()->spawnTime.maxAppspawnTime);
114    }
115}
116
117static int ServerStageHookRun(const HOOK_INFO *hookInfo, void *executionContext)
118{
119    AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
120    ServerStageHook realHook = (ServerStageHook)hookInfo->hookCookie;
121    return realHook((void *)arg->content);
122}
123
124static void PreHookExec(const HOOK_INFO *hookInfo, void *executionContext)
125{
126    AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
127    AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
128    clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadStart);
129    APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
130}
131
132static void PostHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
133{
134    AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
135    AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
136    clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadEnd);
137    uint64_t diff = DiffTime(&spawnMgr->perLoadStart, &spawnMgr->perLoadEnd);
138    APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
139        hookInfo->stage, hookInfo->prio, diff, executionRetVal);
140    UpdateAppSpawnTime(diff);
141}
142
143int ServerStageHookExecute(AppSpawnHookStage stage, AppSpawnContent *content)
144{
145    APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID, "Invalid content");
146    APPSPAWN_CHECK((stage >= STAGE_SERVER_PRELOAD) && (stage <= STAGE_SERVER_EXIT),
147        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
148    AppSpawnHookArg arg;
149    arg.content = content;
150    arg.client = NULL;
151    HOOK_EXEC_OPTIONS options;
152    options.flags = TRAVERSE_STOP_WHEN_ERROR;
153    options.preHook = PreHookExec;
154    options.postHook = PostHookExec;
155    int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&arg), &options);
156    APPSPAWN_LOGV("Execute hook [%{public}d] result %{public}d", stage, ret);
157    return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
158}
159
160int AddServerStageHook(AppSpawnHookStage stage, int prio, ServerStageHook hook)
161{
162    APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
163    APPSPAWN_CHECK((stage >= STAGE_SERVER_PRELOAD) && (stage <= STAGE_SERVER_EXIT),
164        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
165    HOOK_INFO info;
166    info.stage = stage;
167    info.prio = prio;
168    info.hook = ServerStageHookRun;
169    info.hookCookie = (void *)hook;
170    APPSPAWN_LOGI("AddServerStageHook prio: %{public}d", prio);
171    return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
172}
173
174static int AppSpawnHookRun(const HOOK_INFO *hookInfo, void *executionContext)
175{
176    AppSpawnForkArg *arg = (AppSpawnForkArg *)executionContext;
177    AppSpawnHook realHook = (AppSpawnHook)hookInfo->hookCookie;
178    return realHook((AppSpawnMgr *)arg->content, (AppSpawningCtx *)arg->client);
179}
180
181static void PreAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext)
182{
183    AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
184    clock_gettime(CLOCK_MONOTONIC, &arg->tmStart);
185    APPSPAWN_LOGV("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
186}
187
188static void PostAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
189{
190    AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
191    clock_gettime(CLOCK_MONOTONIC, &arg->tmEnd);
192    uint64_t diff = DiffTime(&arg->tmStart, &arg->tmEnd);
193    APPSPAWN_LOGV("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
194        hookInfo->stage, hookInfo->prio, diff, executionRetVal);
195}
196
197int AppSpawnHookExecute(AppSpawnHookStage stage, uint32_t flags, AppSpawnContent *content, AppSpawnClient *client)
198{
199    APPSPAWN_CHECK(content != NULL && client != NULL, return APPSPAWN_ARG_INVALID, "Invalid arg");
200    APPSPAWN_LOGV("Execute hook [%{public}d] for app: %{public}s", stage, GetProcessName((AppSpawningCtx *)client));
201    APPSPAWN_CHECK((stage >= STAGE_PARENT_PRE_FORK) && (stage <= STAGE_CHILD_PRE_RUN),
202        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
203    AppSpawnHookArg forkArg;
204    forkArg.client = client;
205    forkArg.content = content;
206    HOOK_EXEC_OPTIONS options;
207    options.flags = (int)flags;  // TRAVERSE_STOP_WHEN_ERROR : 0;
208    options.preHook = PreAppSpawnHookExec;
209    options.postHook = PostAppSpawnHookExec;
210    int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&forkArg), &options);
211    ret = (ret == ERR_NO_HOOK_STAGE) ? 0 : ret;
212    if (ret != 0) {
213        APPSPAWN_LOGE("Execute hook [%{public}d] result %{public}d", stage, ret);
214    }
215    return ret;
216}
217
218int AppSpawnExecuteClearEnvHook(AppSpawnContent *content, AppSpawnClient *client)
219{
220    return AppSpawnHookExecute(STAGE_CHILD_PRE_COLDBOOT, HOOK_STOP_WHEN_ERROR, content, client);
221}
222
223int AppSpawnExecuteSpawningHook(AppSpawnContent *content, AppSpawnClient *client)
224{
225    return AppSpawnHookExecute(STAGE_CHILD_EXECUTE, HOOK_STOP_WHEN_ERROR, content, client);
226}
227
228int AppSpawnExecutePostReplyHook(AppSpawnContent *content, AppSpawnClient *client)
229{
230    return AppSpawnHookExecute(STAGE_CHILD_POST_RELY, HOOK_STOP_WHEN_ERROR, content, client);
231}
232
233int AppSpawnExecutePreReplyHook(AppSpawnContent *content, AppSpawnClient *client)
234{
235    return AppSpawnHookExecute(STAGE_CHILD_PRE_RELY, HOOK_STOP_WHEN_ERROR, content, client);
236}
237
238void AppSpawnEnvClear(AppSpawnContent *content, AppSpawnClient *client)
239{
240    (void)AppSpawnHookExecute(STAGE_CHILD_PRE_RUN, 0, content, client);
241}
242
243int AddAppSpawnHook(AppSpawnHookStage stage, int prio, AppSpawnHook hook)
244{
245    APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
246    APPSPAWN_CHECK((stage >= STAGE_PARENT_PRE_FORK) && (stage <= STAGE_CHILD_PRE_RUN),
247        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
248    HOOK_INFO info;
249    info.stage = stage;
250    info.prio = prio;
251    info.hook = AppSpawnHookRun;
252    info.hookCookie = (void *)hook;
253    APPSPAWN_LOGI("AddAppSpawnHook stage: %{public}d prio: %{public}d", stage, prio);
254    return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
255}
256
257int ProcessMgrHookExecute(AppSpawnHookStage stage, const AppSpawnContent *content,
258    const AppSpawnedProcessInfo *appInfo)
259{
260    APPSPAWN_CHECK(content != NULL && appInfo != NULL,
261        return APPSPAWN_ARG_INVALID, "Invalid hook");
262    APPSPAWN_CHECK((stage >= STAGE_SERVER_APP_ADD) && (stage <= STAGE_SERVER_APP_DIED),
263        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
264
265    AppSpawnAppArg arg;
266    arg.appInfo = appInfo;
267    arg.content = content;
268    int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&arg), NULL);
269    return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
270}
271
272static int ProcessMgrHookRun(const HOOK_INFO *hookInfo, void *executionContext)
273{
274    AppSpawnAppArg *arg = (AppSpawnAppArg *)executionContext;
275    ProcessChangeHook realHook = (ProcessChangeHook)hookInfo->hookCookie;
276    return realHook((AppSpawnMgr *)arg->content, arg->appInfo);
277}
278
279int AddProcessMgrHook(AppSpawnHookStage stage, int prio, ProcessChangeHook hook)
280{
281    APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
282    APPSPAWN_CHECK((stage >= STAGE_SERVER_APP_ADD) && (stage <= STAGE_SERVER_APP_DIED),
283        return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
284    HOOK_INFO info;
285    info.stage = stage;
286    info.prio = prio;
287    info.hook = ProcessMgrHookRun;
288    info.hookCookie = hook;
289    return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
290}
291
292void RegChildLooper(struct AppSpawnContent *content, ChildLoop loop)
293{
294    APPSPAWN_CHECK(content != NULL && loop != NULL, return, "Invalid content for RegChildLooper");
295    content->runChildProcessor = loop;
296}
297