* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "appspawn_modulemgr.h"
#include "appspawn_hook.h"
#include "appspawn_manager.h"
#include "appspawn_utils.h"
#include "hookmgr.h"
#include "modulemgr.h"
typedef struct {
const AppSpawnContent *content;
const AppSpawnedProcessInfo *appInfo;
} AppSpawnAppArg;
static struct {
MODULE_MGR *moduleMgr;
AppSpawnModuleType type;
const char *moduleName;
} g_moduleMgr[MODULE_MAX] = {
{NULL, MODULE_DEFAULT, "appspawn"},
{NULL, MODULE_APPSPAWN, "appspawn/appspawn"},
{NULL, MODULE_NWEBSPAWN, "appspawn/nwebspawn"},
{NULL, MODULE_COMMON, "appspawn/common"},
{NULL, MODULE_NATIVESPAWN, "appspawn/nativespawn"},
{NULL, MODULE_HYBRIDSPAWN, "appspawn/appspawn"},
};
static HOOK_MGR *g_appspawnHookMgr = NULL;
int AppSpawnModuleMgrInstall(const char *moduleName)
{
if (moduleName == NULL) {
return -1;
}
int type = MODULE_DEFAULT;
if (g_moduleMgr[type].moduleMgr == NULL) {
g_moduleMgr[type].moduleMgr = ModuleMgrCreate(g_moduleMgr[type].moduleName);
}
if (g_moduleMgr[type].moduleMgr == NULL) {
return -1;
}
#ifndef APPSPAWN_TEST
return ModuleMgrInstall(g_moduleMgr[type].moduleMgr, moduleName, 0, NULL);
#else
return 0;
#endif
}
void AppSpawnModuleMgrUnInstall(int type)
{
if ((type < 0) || (type >= MODULE_MAX)) {
return;
}
if (g_moduleMgr[type].moduleMgr == NULL) {
return;
}
ModuleMgrDestroy(g_moduleMgr[type].moduleMgr);
g_moduleMgr[type].moduleMgr = NULL;
}
int AppSpawnLoadAutoRunModules(int type)
{
if ((type < 0) || (type >= MODULE_MAX)) {
return -1;
}
if (g_moduleMgr[type].moduleMgr != NULL) {
return 0;
}
APPSPAWN_LOGI("AppSpawnLoadAutoRunModules: %{public}d moduleName: %{public}s", type, g_moduleMgr[type].moduleName);
#ifndef APPSPAWN_TEST
g_moduleMgr[type].moduleMgr = ModuleMgrScan(g_moduleMgr[type].moduleName);
return g_moduleMgr[type].moduleMgr == NULL ? -1 : 0;
#else
return 0;
#endif
}
HOOK_MGR *GetAppSpawnHookMgr(void)
{
if (g_appspawnHookMgr != NULL) {
return g_appspawnHookMgr;
}
g_appspawnHookMgr = HookMgrCreate("appspawn");
return g_appspawnHookMgr;
}
void DeleteAppSpawnHookMgr(void)
{
HookMgrDestroy(g_appspawnHookMgr);
g_appspawnHookMgr = NULL;
}
static void UpdateAppSpawnTime(int used)
{
if (used < GetAppSpawnMgr()->spawnTime.minAppspawnTime) {
GetAppSpawnMgr()->spawnTime.minAppspawnTime = used;
APPSPAWN_LOGI("spawn min time: %{public}d", GetAppSpawnMgr()->spawnTime.minAppspawnTime);
}
if (used > GetAppSpawnMgr()->spawnTime.maxAppspawnTime) {
GetAppSpawnMgr()->spawnTime.maxAppspawnTime = used;
APPSPAWN_LOGI("spawn max time: %{public}d", GetAppSpawnMgr()->spawnTime.maxAppspawnTime);
}
}
static int ServerStageHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
ServerStageHook realHook = (ServerStageHook)hookInfo->hookCookie;
return realHook((void *)arg->content);
}
static void PreHookExec(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadStart);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
}
static void PostHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
AppSpawnMgr *spawnMgr = (AppSpawnMgr *)arg->content;
clock_gettime(CLOCK_MONOTONIC, &spawnMgr->perLoadEnd);
uint64_t diff = DiffTime(&spawnMgr->perLoadStart, &spawnMgr->perLoadEnd);
APPSPAWN_LOGI("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
hookInfo->stage, hookInfo->prio, diff, executionRetVal);
UpdateAppSpawnTime(diff);
}
int ServerStageHookExecute(AppSpawnHookStage stage, AppSpawnContent *content)
{
APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID, "Invalid content");
APPSPAWN_CHECK((stage >= STAGE_SERVER_PRELOAD) && (stage <= STAGE_SERVER_EXIT),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
AppSpawnHookArg arg;
arg.content = content;
arg.client = NULL;
HOOK_EXEC_OPTIONS options;
options.flags = TRAVERSE_STOP_WHEN_ERROR;
options.preHook = PreHookExec;
options.postHook = PostHookExec;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&arg), &options);
APPSPAWN_LOGV("Execute hook [%{public}d] result %{public}d", stage, ret);
return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
}
int AddServerStageHook(AppSpawnHookStage stage, int prio, ServerStageHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
APPSPAWN_CHECK((stage >= STAGE_SERVER_PRELOAD) && (stage <= STAGE_SERVER_EXIT),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
HOOK_INFO info;
info.stage = stage;
info.prio = prio;
info.hook = ServerStageHookRun;
info.hookCookie = (void *)hook;
APPSPAWN_LOGI("AddServerStageHook stage: %{public}d prio: %{public}d", stage, prio);
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
static int AppSpawnHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnForkArg *arg = (AppSpawnForkArg *)executionContext;
AppSpawnHook realHook = (AppSpawnHook)hookInfo->hookCookie;
return realHook((AppSpawnMgr *)arg->content, (AppSpawningCtx *)arg->client);
}
static void PreAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
clock_gettime(CLOCK_MONOTONIC, &arg->tmStart);
APPSPAWN_LOGV("Hook stage: %{public}d prio: %{public}d start", hookInfo->stage, hookInfo->prio);
}
static void PostAppSpawnHookExec(const HOOK_INFO *hookInfo, void *executionContext, int executionRetVal)
{
AppSpawnHookArg *arg = (AppSpawnHookArg *)executionContext;
clock_gettime(CLOCK_MONOTONIC, &arg->tmEnd);
uint64_t diff = DiffTime(&arg->tmStart, &arg->tmEnd);
APPSPAWN_LOGV("Hook stage: %{public}d prio: %{public}d end time %{public}" PRId64 " ns result: %{public}d",
hookInfo->stage, hookInfo->prio, diff, executionRetVal);
}
int AppSpawnHookExecute(AppSpawnHookStage stage, uint32_t flags, AppSpawnContent *content, AppSpawnClient *client)
{
APPSPAWN_CHECK(content != NULL && client != NULL, return APPSPAWN_ARG_INVALID, "Invalid arg");
APPSPAWN_LOGV("Execute hook [%{public}d] for app: %{public}s", stage, GetProcessName((AppSpawningCtx *)client));
APPSPAWN_CHECK((stage >= STAGE_PARENT_PRE_FORK) && (stage <= STAGE_CHILD_PRE_RUN),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
AppSpawnHookArg forkArg;
forkArg.client = client;
forkArg.content = content;
HOOK_EXEC_OPTIONS options;
options.flags = (int)flags;
options.preHook = PreAppSpawnHookExec;
options.postHook = PostAppSpawnHookExec;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&forkArg), &options);
ret = (ret == ERR_NO_HOOK_STAGE) ? 0 : ret;
if (ret != 0) {
APPSPAWN_LOGE("Execute hook [%{public}d] result %{public}d", stage, ret);
}
return ret;
}
int AppSpawnExecuteClearEnvHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_PRE_COLDBOOT, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecuteSpawningHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_EXECUTE, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecutePostReplyHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_POST_RELY, HOOK_STOP_WHEN_ERROR, content, client);
}
int AppSpawnExecutePreReplyHook(AppSpawnContent *content, AppSpawnClient *client)
{
return AppSpawnHookExecute(STAGE_CHILD_PRE_RELY, HOOK_STOP_WHEN_ERROR, content, client);
}
void AppSpawnEnvClear(AppSpawnContent *content, AppSpawnClient *client)
{
(void)AppSpawnHookExecute(STAGE_CHILD_PRE_RUN, 0, content, client);
}
int AddAppSpawnHook(AppSpawnHookStage stage, int prio, AppSpawnHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
APPSPAWN_CHECK((stage >= STAGE_PARENT_PRE_FORK) && (stage <= STAGE_CHILD_PRE_RUN),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
HOOK_INFO info;
info.stage = stage;
info.prio = prio;
info.hook = AppSpawnHookRun;
info.hookCookie = (void *)hook;
APPSPAWN_LOGI("AddAppSpawnHook stage: %{public}d prio: %{public}d", stage, prio);
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
int ProcessMgrHookExecute(AppSpawnHookStage stage, const AppSpawnContent *content,
const AppSpawnedProcessInfo *appInfo)
{
APPSPAWN_CHECK(content != NULL && appInfo != NULL,
return APPSPAWN_ARG_INVALID, "Invalid hook");
APPSPAWN_CHECK((stage >= STAGE_SERVER_APP_ADD) && (stage <= STAGE_SERVER_APP_DIED),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
AppSpawnAppArg arg;
arg.appInfo = appInfo;
arg.content = content;
int ret = HookMgrExecute(GetAppSpawnHookMgr(), stage, (void *)(&arg), NULL);
return ret == ERR_NO_HOOK_STAGE ? 0 : ret;
}
static int ProcessMgrHookRun(const HOOK_INFO *hookInfo, void *executionContext)
{
AppSpawnAppArg *arg = (AppSpawnAppArg *)executionContext;
ProcessChangeHook realHook = (ProcessChangeHook)hookInfo->hookCookie;
return realHook((AppSpawnMgr *)arg->content, arg->appInfo);
}
int AddProcessMgrHook(AppSpawnHookStage stage, int prio, ProcessChangeHook hook)
{
APPSPAWN_CHECK(hook != NULL, return APPSPAWN_ARG_INVALID, "Invalid hook");
APPSPAWN_CHECK((stage >= STAGE_SERVER_APP_ADD) && (stage <= STAGE_SERVER_APP_DIED),
return APPSPAWN_ARG_INVALID, "Invalid stage %{public}d", (int)stage);
HOOK_INFO info;
info.stage = stage;
info.prio = prio;
info.hook = ProcessMgrHookRun;
info.hookCookie = hook;
return HookMgrAddEx(GetAppSpawnHookMgr(), &info);
}
void RegChildLooper(struct AppSpawnContent *content, ChildLoop loop)
{
APPSPAWN_CHECK(content != NULL && loop != NULL, return, "Invalid content for RegChildLooper");
content->runChildProcessor = loop;
}