* 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_service.h"
#include <dlfcn.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <signal.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sched.h>
#include "appspawn.h"
#include "appspawn_hook.h"
#include "appspawn_modulemgr.h"
#include "appspawn_manager.h"
#include "appspawn_msg.h"
#include "appspawn_server.h"
#include "appspawn_trace.h"
#include "appspawn_utils.h"
#include "init_socket.h"
#include "init_utils.h"
#include "parameter.h"
#include "appspawn_adapter.h"
#include "securec.h"
#include "cJSON.h"
#ifdef APPSPAWN_HISYSEVENT
#include "hisysevent_adapter.h"
#endif
#define PARAM_BUFFER_SIZE 10
#define PATH_SIZE 256
#define FD_PATH_SIZE 128
#define UNLOCK_MOUNT_TIMEOUT_MS 30000
#define PREFORK_PROCESS "apppool"
#define APPSPAWN_MSG_USER_CHECK_COUNT 4
#define USER_ID_MIN_VALUE 100
#define USER_ID_MAX_VALUE 10736
#define LOCK_STATUS_PARAM_SIZE 64
#ifndef PIDFD_NONBLOCK
#define PIDFD_NONBLOCK O_NONBLOCK
#endif
static void WaitChildTimeout(const TimerHandle taskHandle, void *context);
static void ProcessChildResponse(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context);
static void WaitChildDied(pid_t pid, int status);
static void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen);
static void ProcessRecvMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message);
static void UnlockChildTimeout(const TimerHandle taskHandle, void *context);
static void ProcessUnlockChildResponse(const WatcherHandle taskHandle, int fd,
uint32_t *events, const void *context);
APPSPAWN_STATIC bool HandleUnlockEvent(AppSpawnContent *content, int uid, AppSpawnMsgNode *message,
AppSpawnConnection *connection);
APPSPAWN_STATIC int SendUnlockMsgToPrefork(AppSpawnContent *content, int uid);
APPSPAWN_STATIC int DoUnlockMountSerial(AppSpawnContent *content, int uid);
APPSPAWN_STATIC int ProcessUnlockMessage(int uid);
APPSPAWN_STATIC int ForkAndDoUnlockMount(AppSpawnContent *content, int uid, AppSpawningCtx *property);
static inline void SetFdCtrl(int fd, int opt)
{
int option = fcntl(fd, F_GETFD);
APPSPAWN_CHECK(option >= 0, return, "SetFdCtrl fcntl failed %{public}d, %{public}d", option, errno);
int ret = fcntl(fd, F_SETFD, (unsigned int)option | (unsigned int)opt);
if (ret < 0) {
APPSPAWN_LOGI("Set fd %{public}d option %{public}d %{public}d result: %{public}d", fd, option, opt, errno);
}
}
static void AppQueueDestroyProc(const AppSpawnMgr *mgr, AppSpawnedProcess *appInfo, void *data)
{
pid_t pid = appInfo->pid;
APPSPAWN_LOGI("kill %{public}s pid: %{public}d", appInfo->name, appInfo->pid);
ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
ProcessMgrHookExecute(STAGE_SERVER_APP_CLEANUP, GetAppSpawnContent(), appInfo);
OH_ListRemove(&appInfo->node);
OH_ListInit(&appInfo->node);
free(appInfo);
if (pid > 0 && kill(pid, SIGKILL) != 0) {
APPSPAWN_LOGE("unable to kill process, pid: %{public}d errno: %{public}d", pid, errno);
}
}
APPSPAWN_STATIC void StopAppSpawn(void)
{
AppSpawnContent *content = GetAppSpawnContent();
if (content != NULL && content->reservedPid > 0) {
pid_t reservedPid = content->reservedPid;
int ret = kill(reservedPid, SIGKILL);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "kill reserved pid %{public}d failed %{public}d %{public}d",
reservedPid, ret, errno);
content->reservedPid = 0;
CleanupSpawningFdsByPid((AppSpawnMgr *)content, reservedPid);
}
TraversalSpawnedProcess(AppQueueDestroyProc, NULL);
APPSPAWN_LOGI("StopAppSpawn ");
#ifdef APPSPAWN_HISYSEVENT
AppSpawnHiSysEventWrite();
#endif
LE_StopLoop(LE_GetDefaultLoop());
}
static inline void DumpStatus(const char *appName, pid_t pid, int status, int *signal)
{
if (WIFSIGNALED(status)) {
*signal = WTERMSIG(status);
APPSPAWN_LOGW("%{public}s with pid %{public}d exit with signal:%{public}d", appName, pid, *signal);
}
if (WIFEXITED(status)) {
*signal = WEXITSTATUS(status);
APPSPAWN_LOGW("%{public}s with pid %{public}d exit with code:%{public}d", appName, pid, *signal);
}
}
APPSPAWN_STATIC void WriteSignalInfoToFd(AppSpawnedProcess *appInfo, AppSpawnContent *content, int signal)
{
APPSPAWN_CHECK(content->signalFd > 0, return, "Invalid signal fd[%{public}d]", content->signalFd);
APPSPAWN_CHECK(appInfo->pid > 0, return, "Invalid pid[%{public}d]", appInfo->pid);
APPSPAWN_CHECK(appInfo->uid > 0, return, "Invalid uid[%{public}d]", appInfo->uid);
APPSPAWN_CHECK(appInfo->name != NULL, return, "Invalid name");
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
APPSPAWN_LOGE("signal json write create root object unsuccess");
return;
}
cJSON_AddNumberToObject(root, "pid", appInfo->pid);
cJSON_AddNumberToObject(root, "uid", appInfo->uid);
cJSON_AddNumberToObject(root, "signal", signal);
cJSON_AddStringToObject(root, "bundleName", appInfo->name);
char *jsonString = cJSON_Print(root);
cJSON_Delete(root);
APPSPAWN_CHECK(jsonString != NULL, return, "jsonString is NULL");
int ret = write(content->signalFd, jsonString, strlen(jsonString) + 1);
if (ret < 0) {
free(jsonString);
APPSPAWN_LOGE("Spawn Listen failed to write signal info to fd errno %{public}d", errno);
return;
}
APPSPAWN_LOGV("Spawn Listen write signal info %{public}s to fd %{public}d success", jsonString, content->signalFd);
free(jsonString);
}
APPSPAWN_STATIC void HandleDiedPid(pid_t pid, uid_t uid, int status)
{
AppSpawnContent *content = GetAppSpawnContent();
APPSPAWN_CHECK(content != NULL, return, "Invalid content");
if (pid == content->reservedPid) {
APPSPAWN_LOGW("HandleDiedPid with reservedPid %{public}d", pid);
CleanupSpawningFdsByPid((AppSpawnMgr *)content, pid);
content->reservedPid = 0;
}
int signal = 0;
AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
if (appInfo == NULL) {
WaitChildDied(pid, status);
DumpStatus("unknown", pid, status, &signal);
return;
}
appInfo->exitStatus = status;
APPSPAWN_CHECK_ONLY_LOG(appInfo->uid == uid, "Invalid uid %{public}u %{public}u", appInfo->uid, uid);
DumpStatus(appInfo->name, pid, status, &signal);
WriteSignalInfoToFd(appInfo, content, signal);
ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
ProcessMgrHookExecute(STAGE_SERVER_APP_CLEANUP, GetAppSpawnContent(), appInfo);
TerminateSpawnedProcess(appInfo);
}
APPSPAWN_STATIC void ProcessSignal(const struct signalfd_siginfo *siginfo)
{
APPSPAWN_DUMPI("ProcessSignal signum %{public}d %{public}d", siginfo->ssi_signo, siginfo->ssi_pid);
switch (siginfo->ssi_signo) {
case SIGCHLD: {
pid_t pid;
int status;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
APPSPAWN_CHECK(WIFSIGNALED(status) || WIFEXITED(status), return,
"ProcessSignal with wrong status:%{public}d", status);
HandleDiedPid(pid, siginfo->ssi_uid, status);
}
#if (defined(CJAPP_SPAWN) || defined(NATIVE_SPAWN))
if (OH_ListGetCnt(&GetAppSpawnMgr()->appQueue) == 0 &&
OH_ListGetCnt(&GetAppSpawnMgr()->appSpawnQueue) == 0) {
LE_StopLoop(LE_GetDefaultLoop());
}
#endif
break;
}
case SIGTERM: {
StopAppSpawn();
break;
}
default:
APPSPAWN_LOGI("SigHandler, unsupported signal %{public}d.", siginfo->ssi_signo);
break;
}
}
static void AppSpawningCtxOnClose(const AppSpawnMgr *mgr, AppSpawningCtx *ctx, void *data)
{
if (ctx == NULL || ctx->message == NULL || ctx->message->connection != data) {
return;
}
APPSPAWN_LOGI("Kill process, pid: %{public}d app: %{public}s", ctx->pid, GetProcessName(ctx));
if (ctx->pid > 0 && kill(ctx->pid, SIGKILL) != 0) {
APPSPAWN_LOGE("unable to kill process, pid: %{public}d errno: %{public}d", ctx->pid, errno);
}
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &ctx->client);
DeleteAppSpawningCtx(ctx);
}
static void OnClose(const TaskHandle taskHandle)
{
int fd = LE_GetSocketFd(taskHandle);
APPSPAWN_LOGV("OnClose socket fd %{public}d", fd);
if (!IsSpawnServer(GetAppSpawnMgr())) {
if (fd > 0) {
close(fd);
fd = -1;
}
return;
}
AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
if (connection->receiverCtx.timer) {
LE_StopTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer);
connection->receiverCtx.timer = NULL;
}
APPSPAWN_LOGI("OnClose connectionId: %{public}u socket %{public}d", connection->connectionId, fd);
DeleteAppSpawnMsg(&connection->receiverCtx.incompleteMsg);
connection->receiverCtx.incompleteMsg = NULL;
AppSpawningCtxTraversal(AppSpawningCtxOnClose, connection);
}
static void OnDisConnect(const TaskHandle taskHandle)
{
AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
APPSPAWN_LOGI("OnDisConnect connectionId: %{public}u socket %{public}d",
connection->connectionId, LE_GetSocketFd(taskHandle));
OnClose(taskHandle);
}
static void SendMessageComplete(const TaskHandle taskHandle, BufferHandle handle)
{
AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != NULL, return, "Invalid connection");
uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
AppSpawnResponseMsg *msg = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
if (msg == NULL) {
return;
}
AppSpawnedProcess *appInfo = GetSpawnedProcess(msg->result.pid);
if (appInfo == NULL) {
return;
}
APPSPAWN_DUMPI("SendMessageComplete connectionId:%{public}u result:%{public}d app:%{public}s pid:%{public}d",
connection->connectionId, LE_GetSendResult(handle), appInfo->name, msg->result.pid);
if (LE_GetSendResult(handle) != 0 && msg->result.pid > 0) {
kill(msg->result.pid, SIGKILL);
}
}
static int SendResponse(const AppSpawnConnection *connection, const AppSpawnMsg *msg, int result, pid_t pid)
{
APPSPAWN_LOGV("SendResponse connectionId: %{public}u result: 0x%{public}x pid: %{public}d",
connection->connectionId, result, pid);
uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_ERROR_UTILS_MEM_FAIL, "buffer is null");
int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
APPSPAWN_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), NULL, handle);
return -1, "Failed to memcpy_s bufferSize");
buffer->result.result = result;
buffer->result.pid = pid;
buffer->result.checkPointId = 0;
return LE_Send(LE_GetDefaultLoop(), connection->stream, handle, bufferSize);
}
* @brief 发送扩展响应(包含checkpoint信息)
*
* @param connection 连接对象
* @param msg 消息头
* @param result 结果码
* @param pid 进程PID
* @param checkPointId checkpoint ID
* @return 成功返回0,失败返回错误码
*/
static int SendResponseEx(const AppSpawnConnection *connection, const AppSpawnMsg *msg,
int result, pid_t pid, uint64_t checkPointId)
{
APPSPAWN_LOGI("SendResponseEx connectionId: %{public}u result: 0x%{public}x pid: %{public}d "
"checkPointId: %{public}" PRId64"", connection->connectionId, result, pid, checkPointId);
uint32_t bufferSize = sizeof(AppSpawnResponseMsg);
BufferHandle handle = LE_CreateBuffer(LE_GetDefaultLoop(), bufferSize);
AppSpawnResponseMsg *buffer = (AppSpawnResponseMsg *)LE_GetBufferInfo(handle, NULL, &bufferSize);
APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_ERROR_UTILS_MEM_FAIL, "buffer is null");
int ret = memcpy_s(buffer, bufferSize, msg, sizeof(AppSpawnMsg));
APPSPAWN_CHECK(ret == 0, LE_FreeBuffer(LE_GetDefaultLoop(), NULL, handle);
return -1, "Failed to memcpy_s bufferSize");
buffer->result.result = result;
buffer->result.pid = pid;
buffer->result.checkPointId = checkPointId;
return LE_Send(LE_GetDefaultLoop(), connection->stream, handle, bufferSize);
}
static void WaitMsgCompleteTimeOut(const TimerHandle taskHandle, void *context)
{
AppSpawnConnection *connection = (AppSpawnConnection *)context;
APPSPAWN_LOGE("Long time no msg complete so close connectionId: %{public}u", connection->connectionId);
DeleteAppSpawnMsg(&connection->receiverCtx.incompleteMsg);
connection->receiverCtx.incompleteMsg = NULL;
LE_CloseStreamTask(LE_GetDefaultLoop(), connection->stream);
}
static inline int StartTimerForCheckMsg(AppSpawnConnection *connection)
{
if (connection->receiverCtx.timer != NULL) {
return 0;
}
int ret = LE_CreateTimer(LE_GetDefaultLoop(), &connection->receiverCtx.timer, WaitMsgCompleteTimeOut, connection);
if (ret == 0) {
ret = LE_StartTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer, MAX_WAIT_MSG_COMPLETE, 1);
}
return ret;
}
static int HandleRecvMessage(const TaskHandle taskHandle, uint8_t * buffer, int bufferSize, int flags)
{
int socketFd = LE_GetSocketFd(taskHandle);
struct iovec iov = {
.iov_base = buffer,
.iov_len = bufferSize,
};
char ctrlBuffer[CMSG_SPACE(APP_MAX_FD_COUNT * sizeof(int))];
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = ctrlBuffer,
.msg_controllen = sizeof(ctrlBuffer),
};
AppSpawnConnection *connection = (AppSpawnConnection *) LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != NULL, return -1, "Invalid connection");
errno = 0;
int recvLen = recvmsg(socketFd, &msg, flags);
APPSPAWN_CHECK_ONLY_LOG(errno == 0, "recvmsg with errno %{public}d", errno);
struct cmsghdr *cmsg = NULL;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int fdCount = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
int *fd = (int *) CMSG_DATA(cmsg);
APPSPAWN_CHECK(fdCount <= APP_MAX_FD_COUNT, return -1,
"failed to recv fd %{public}d %{public}d", connection->receiverCtx.fdCount, fdCount);
int ret = memcpy_s(connection->receiverCtx.fds,
fdCount * sizeof(int), fd, fdCount * sizeof(int));
APPSPAWN_CHECK(ret == 0, return -1, "memcpy_s fd ret %{public}d", ret);
connection->receiverCtx.fdCount = fdCount;
}
}
return recvLen;
}
APPSPAWN_STATIC bool OnConnectionUserCheck(uid_t uid)
{
const uid_t uids[APPSPAWN_MSG_USER_CHECK_COUNT] = {
0,
3350,
5523,
1090,
};
for (int i = 0; i < APPSPAWN_MSG_USER_CHECK_COUNT; i++) {
if (uid == uids[i]) {
return true;
}
}
if (uid == 2000 && IsDeveloperModeOpen()) {
return true;
}
return false;
}
static int OnConnection(const LoopHandle loopHandle, const TaskHandle server)
{
APPSPAWN_CHECK(server != NULL && loopHandle != NULL, return -1, "Error server");
static uint32_t connectionId = 0;
TaskHandle stream;
LE_StreamInfo info = {};
info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_CONNECT;
info.baseInfo.close = OnClose;
info.baseInfo.userDataSize = sizeof(AppSpawnConnection);
info.disConnectComplete = OnDisConnect;
info.sendMessageComplete = SendMessageComplete;
info.recvMessage = OnReceiveRequest;
info.handleRecvMsg = HandleRecvMessage;
LE_STATUS ret = LE_AcceptStreamClient(loopHandle, server, &stream, &info);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to alloc stream");
AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(stream);
APPSPAWN_CHECK(connection != NULL, return -1, "Failed to alloc stream");
struct ucred cred = {-1, -1, -1};
socklen_t credSize = sizeof(struct ucred);
if ((getsockopt(LE_GetSocketFd(stream), SOL_SOCKET, SO_PEERCRED, &cred, &credSize) < 0) ||
!OnConnectionUserCheck(cred.uid)) {
APPSPAWN_LOGE("Invalid uid %{public}d from client", cred.uid);
LE_CloseStreamTask(LE_GetDefaultLoop(), stream);
return -1;
}
SetFdCtrl(LE_GetSocketFd(stream), FD_CLOEXEC);
connection->connectionId = ++connectionId;
connection->stream = stream;
connection->receiverCtx.fdCount = 0;
connection->receiverCtx.incompleteMsg = NULL;
connection->receiverCtx.timer = NULL;
connection->receiverCtx.msgRecvLen = 0;
connection->receiverCtx.nextMsgId = 1;
APPSPAWN_LOGI("OnConnection connectionId: %{public}u fd %{public}d ",
connection->connectionId, LE_GetSocketFd(stream));
return 0;
}
static void OnListenFdClose(const TaskHandle taskHandle)
{
* The socket fd of the parent process appspawn is executed in the AppSpawnDestroyContent function,
* and executing it in the OnListen function will result in a badfd
*/
if (IsSpawnServer(GetAppSpawnMgr())) {
return;
}
int fd = LE_GetSocketFd(taskHandle);
APPSPAWN_LOGV("OnListenFdClose listen fd: %{public}d", fd);
if (fd > 0) {
close(fd);
fd = -1;
}
}
APPSPAWN_STATIC bool MsgDevicedebugCheck(TaskHandle stream, AppSpawnMsgNode *message)
{
struct ucred cred = {0, 0, 0};
socklen_t credSize = sizeof(cred);
if (getsockopt(LE_GetSocketFd(stream), SOL_SOCKET, SO_PEERCRED, &cred, &credSize) < 0) {
return false;
}
if (cred.uid != DecodeUid("shell")) {
return true;
}
AppSpawnMsg *msg = &message->msgHeader;
APPSPAWN_CHECK(msg->msgType == MSG_DEVICE_DEBUG, return false,
"appspawn devicedebug msg type is not devicedebug [%{public}d]", msg->msgType);
return true;
}
static void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)
{
AppSpawnConnection *connection = (AppSpawnConnection *)LE_GetUserData(taskHandle);
APPSPAWN_CHECK(connection != NULL, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
return, "Failed to get client form socket");
APPSPAWN_CHECK(buffLen < MAX_MSG_TOTAL_LENGTH, LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
return, "Message too long %{public}u", buffLen);
uint32_t reminder = 0;
uint32_t currLen = 0;
AppSpawnMsgNode *message = connection->receiverCtx.incompleteMsg;
connection->receiverCtx.incompleteMsg = NULL;
int ret = 0;
do {
APPSPAWN_DUMPI("connectionId:%{public}u buffLen:%{public}d", connection->connectionId, buffLen - currLen);
ret = GetAppSpawnMsgFromBuffer(buffer + currLen, buffLen - currLen,
&message, &connection->receiverCtx.msgRecvLen, &reminder);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
if (connection->receiverCtx.msgRecvLen != message->msgHeader.msgLen) {
connection->receiverCtx.incompleteMsg = message;
message = NULL;
break;
}
connection->receiverCtx.msgRecvLen = 0;
if (connection->receiverCtx.timer) {
LE_StopTimer(LE_GetDefaultLoop(), connection->receiverCtx.timer);
connection->receiverCtx.timer = NULL;
}
APPSPAWN_CHECK_ONLY_EXPER(MsgDevicedebugCheck(connection->stream, message),
LE_CloseTask(LE_GetDefaultLoop(), taskHandle); return);
ret = DecodeAppSpawnMsg(message);
APPSPAWN_CHECK_ONLY_EXPER(ret == 0, break);
StartAppspawnTrace("ProcessRecvMsg");
(void)ProcessRecvMsg(connection, message);
FinishAppspawnTrace();
message = NULL;
currLen = buffLen - reminder;
} while (reminder > 0);
if (message) {
DeleteAppSpawnMsg(&message);
}
if (ret != 0) {
LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
return;
}
if (connection->receiverCtx.incompleteMsg != NULL) {
ret = StartTimerForCheckMsg(connection);
APPSPAWN_CHECK(ret == 0, LE_CloseStreamTask(LE_GetDefaultLoop(), taskHandle);
return, "Failed to create time for connection");
}
}
APPSPAWN_STATIC char *GetSpawnNameByRunMode(RunMode mode)
{
if (mode == MODE_FOR_APP_SPAWN || mode == MODE_FOR_APP_COLD_RUN) {
return APPSPAWN_SERVER_NAME;
} else if (mode == MODE_FOR_NWEB_SPAWN || mode == MODE_FOR_NWEB_COLD_RUN) {
return NWEBSPAWN_SERVER_NAME;
} else if (mode == MODE_FOR_HYBRID_SPAWN || mode == MODE_FOR_HYBRID_COLD_RUN) {
return HYBRIDSPAWN_SERVER_NAME;
} else if (mode == MODE_FOR_NATIVE_SPAWN || mode == MODE_FOR_NATIVE_COLD_RUN) {
return NATIVESPAWN_SERVER_NAME;
} else if (mode == MODE_FOR_CJAPP_SPAWN || mode == MODE_FOR_CJAPP_COLD_RUN) {
return CJAPPSPAWN_SERVER_NAME;
}
return "";
}
APPSPAWN_STATIC char *GetMapMem(uint32_t clientId, const char *processName,
uint32_t size, bool readOnly, RunMode runMode)
{
char path[PATH_MAX] = {};
int len = sprintf_s(path, sizeof(path), APPSPAWN_MSG_DIR "%s/%s_%u",
GetSpawnNameByRunMode(runMode), processName, clientId);
APPSPAWN_CHECK(len > 0, return NULL, "Failed to format path %{public}s", processName);
APPSPAWN_LOGV("GetMapMem for child %{public}s memSize %{public}u", path, size);
int prot = PROT_READ;
int mode = O_RDONLY;
if (!readOnly) {
mode = O_CREAT | O_RDWR | O_TRUNC;
prot = PROT_READ | PROT_WRITE;
}
int fd = open(path, mode, S_IRWXU);
APPSPAWN_CHECK(fd >= 0, return NULL, "Failed to open errno %{public}d path %{public}s", errno, path);
if (!readOnly) {
APPSPAWN_CHECK(fallocate(fd, 0, 0, size) == 0, close(fd);
return NULL, "failed fallocate %{public}s %{public}d", processName, errno);
}
void *areaAddr = (void *)mmap(NULL, size, prot, MAP_SHARED, fd, 0);
close(fd);
APPSPAWN_CHECK(areaAddr != MAP_FAILED && areaAddr != NULL,
return NULL, "Failed to map memory error %{public}d fileName %{public}s ", errno, path);
return (char *)areaAddr;
}
APPSPAWN_STATIC int WriteMsgToChild(AppSpawningCtx *property, RunMode mode)
{
APPSPAWN_CHECK(property != NULL && property->message != NULL, return APPSPAWN_MSG_INVALID,
"Failed to WriteMsgToChild property invalid");
const uint32_t memSize = (property->message->msgHeader.msgLen / 4096 + 1) * 4096;
char *buffer = GetMapMem(property->client.id, GetProcessName(property), memSize, false, mode);
APPSPAWN_CHECK(buffer != NULL, return APPSPAWN_SYSTEM_ERROR,
"Failed to map memory error %{public}d fileName %{public}s ", errno, GetProcessName(property));
int ret = memcpy_s(buffer, memSize, &property->message->msgHeader, sizeof(AppSpawnMsg));
if (ret == 0) {
ret = memcpy_s((char *)buffer + sizeof(AppSpawnMsg), memSize - sizeof(AppSpawnMsg),
property->message->buffer, property->message->msgHeader.msgLen - sizeof(AppSpawnMsg));
}
if (ret != 0) {
APPSPAWN_LOGE("Failed to copy msg fileName %{public}s ", GetProcessName(property));
munmap((char *)buffer, memSize);
return APPSPAWN_SYSTEM_ERROR;
}
property->forkCtx.msgSize = memSize;
property->forkCtx.childMsg = buffer;
APPSPAWN_LOGV("Write msg to child: %{public}u success", property->client.id);
return 0;
}
* @brief Initialize fork context by creating a non-blocking pipe.
*
* Creates a pipe for fork result notification (child writes, parent reads).
* The read end is set to O_NONBLOCK so the parent can poll without blocking.
*
* @param property Spawning context to initialize
* @return 0 on success, errno on pipe creation failure
*/
static int InitForkContext(AppSpawningCtx *property)
{
if (pipe(property->forkCtx.fd) == -1) {
APPSPAWN_LOGE("create pipe fail, errno: %{public}d", errno);
return errno;
}
int flags = fcntl(property->forkCtx.fd[0], F_GETFL);
if (flags >= 0) {
(void)fcntl(property->forkCtx.fd[0], F_SETFL, (unsigned int)flags | O_NONBLOCK);
}
return 0;
}
static void ClosePidfdWatcher(const TaskHandle taskHandle)
{
int fd = LE_GetSocketFd(taskHandle);
if (fd >= 0) {
close(fd);
}
void *p = LE_GetUserData(taskHandle);
if (p != NULL) {
free(*(void **)p);
}
}
static void ProcessChildProcessFd(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
{
APPSPAWN_CHECK_ONLY_EXPER(context != NULL, return);
pid_t pid = *(pid_t *)context;
APPSPAWN_DUMPI("Clear process group with pid:%{public}d,pidFd:%{public}d", pid, fd);
AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
if (appInfo == NULL) {
APPSPAWN_LOGW("Cannot get app info by bundle name: %{public}d", pid);
return;
}
ProcessMgrHookExecute(STAGE_SERVER_APP_DIED, GetAppSpawnContent(), appInfo);
LE_CloseTask(LE_GetDefaultLoop(), taskHandle);
}
static int OpenPidFd(pid_t pid, unsigned int flags)
{
return syscall(SYS_pidfd_open, pid, flags);
}
static void WatchChildProcessFd(AppSpawningCtx *property)
{
if (property->pid <= 0) {
APPSPAWN_LOGW("Invalid child process pid, skip watch");
return;
}
if (IsNWebSpawnMode((AppSpawnMgr *)GetAppSpawnContent())) {
APPSPAWN_LOGV("Nwebspawn don't need add pidfd");
return;
}
AppSpawnedProcess *appInfo = GetSpawnedProcess(property->pid);
if (appInfo == NULL) {
APPSPAWN_LOGW("Cannot get app info of pid %{public}d", property->pid);
return;
}
int fd = OpenPidFd(property->pid, PIDFD_NONBLOCK);
if (fd < 0) {
APPSPAWN_LOGW("Failed to open pid fd for app: %{public}s, err = %{public}d",
GetBundleName(property), errno);
return;
}
APPSPAWN_DUMPI("watch app process pid:%{public}d,pidFd:%{public}d", property->pid, fd);
LE_WatchInfo watchInfo = {};
watchInfo.fd = fd;
watchInfo.flags = WATCHER_ONCE;
watchInfo.events = EVENT_READ;
watchInfo.close = ClosePidfdWatcher;
watchInfo.processEvent = ProcessChildProcessFd;
pid_t *appPid = (pid_t *)malloc(sizeof(pid_t));
APPSPAWN_CHECK_ONLY_EXPER(appPid != NULL, return);
*appPid = property->pid;
LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.pidFdWatcherHandle, &watchInfo, appPid);
if (status != LE_SUCCESS) {
#ifndef APPSPAWN_TEST
close(fd);
free(appPid);
appPid = NULL;
#endif
APPSPAWN_LOGW("Failed to watch child pid fd, pid is %{public}d", property->pid);
}
}
static int IsChildColdRun(AppSpawningCtx *property)
{
return CheckAppMsgFlagsSet(property, APP_FLAGS_UBSAN_ENABLED) ||
CheckAppMsgFlagsSet(property, APP_FLAGS_ASANENABLED) ||
CheckAppMsgFlagsSet(property, APP_FLAGS_TSAN_ENABLED) ||
CheckAppMsgFlagsSet(property, APP_FLAGS_HWASAN_ENABLED) ||
(property->client.flags & APP_COLD_START);
}
static int AddChildWatcher(AppSpawningCtx *property)
{
uint32_t defTimeout = IsChildColdRun(property) ? COLD_CHILD_RESPONSE_TIMEOUT : WAIT_CHILD_RESPONSE_TIMEOUT;
uint32_t timeout = GetSpawnTimeout(defTimeout, IsChildColdRun(property));
LE_WatchInfo watchInfo = {};
watchInfo.fd = property->forkCtx.fd[0];
watchInfo.flags = WATCHER_ONCE;
watchInfo.events = EVENT_READ;
watchInfo.processEvent = ProcessChildResponse;
LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.watcherHandle, &watchInfo, property);
APPSPAWN_LOGV("AddChildWatcher with timeout %{public}u fd %{public}d", timeout, watchInfo.fd);
APPSPAWN_CHECK(status == LE_SUCCESS,
return APPSPAWN_SYSTEM_ERROR, "Failed to watch child %{public}d", property->pid);
status = LE_CreateTimer(LE_GetDefaultLoop(), &property->forkCtx.timer, WaitChildTimeout, property);
if (status == LE_SUCCESS) {
status = LE_StartTimer(LE_GetDefaultLoop(), property->forkCtx.timer, timeout * 1000, 0);
}
if (status != LE_SUCCESS) {
if (property->forkCtx.timer != NULL) {
LE_StopTimer(LE_GetDefaultLoop(), property->forkCtx.timer);
}
property->forkCtx.timer = NULL;
LE_RemoveWatcher(LE_GetDefaultLoop(), property->forkCtx.watcherHandle);
property->forkCtx.watcherHandle = NULL;
APPSPAWN_LOGE("Failed to watch child %{public}d", property->pid);
return APPSPAWN_SYSTEM_ERROR;
}
return 0;
}
static bool IsSupportRunHnp()
{
char buffer[PARAM_BUFFER_SIZE] = {0};
int ret = GetParameter("const.startup.hnp.execute.enable", "false", buffer, PARAM_BUFFER_SIZE);
if (ret <= 0) {
APPSPAWN_LOGE("Get hnp execute enable param unsuccess! ret =%{public}d", ret);
return false;
}
if (strcmp(buffer, "true") == 0) {
return true;
}
return false;
}
APPSPAWN_STATIC void ClearMMAP(int clientId, uint32_t memSize)
{
AppSpawnContent *content = GetAppSpawnContent();
if (content != NULL && content->propertyBuffer != NULL) {
int ret = munmap(content->propertyBuffer, memSize);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d %{public}d", ret, errno);
content->propertyBuffer = NULL;
}
char path[PATH_MAX] = {0};
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, APPSPAWN_MSG_DIR "appspawn/prefork_%d", clientId);
APPSPAWN_CHECK_ONLY_LOG(ret > 0, "snprintf failed with %{public}d %{public}d", ret, errno);
if (ret > 0 && access(path, F_OK) == 0) {
ret = unlink(path);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "prefork unlink result %{public}d %{public}d", ret, errno);
}
}
APPSPAWN_STATIC void ClearPreforkInfo(AppSpawningCtx *property)
{
APPSPAWN_CHECK(property != NULL, return, "invalid property");
if (property->forkCtx.childMsg != NULL) {
int ret = munmap(property->forkCtx.childMsg, property->forkCtx.msgSize);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d %{public}d", ret, errno);
property->forkCtx.childMsg = NULL;
}
char path[PATH_MAX] = {0};
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, APPSPAWN_MSG_DIR "appspawn/prefork_%u",
property->client.id);
APPSPAWN_CHECK_ONLY_LOG(ret > 0, "snprintf failed with %{public}d %{public}d", ret, errno);
if (ret > 0 && access(path, F_OK) == 0) {
ret = unlink(path);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "prefork unlink result %{public}d %{public}d", ret, errno);
}
}
APPSPAWN_STATIC int WritePreforkMsg(AppSpawningCtx *property, uint32_t memSize)
{
AppSpawnContent *content = GetAppSpawnContent();
if (content == NULL || content->propertyBuffer == NULL) {
APPSPAWN_LOGE("buffer is null can not write propery");
return -1;
}
int ret = memcpy_s(content->propertyBuffer, memSize, &property->message->msgHeader, sizeof(AppSpawnMsg));
if (ret != 0) {
APPSPAWN_LOGE("memcpys_s msgHeader failed");
ClearMMAP(property->client.id, memSize);
return ret;
}
ret = memcpy_s((char *)content->propertyBuffer + sizeof(AppSpawnMsg), memSize - sizeof(AppSpawnMsg),
property->message->buffer, property->message->msgHeader.msgLen - sizeof(AppSpawnMsg));
if (ret != 0) {
APPSPAWN_LOGE("memcpys_s AppSpawnMsg failed");
ClearMMAP(property->client.id, memSize);
return ret;
}
ret = munmap((char *)content->propertyBuffer, memSize);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "munmap failed %{public}d,%{public}d", ret, errno);
content->propertyBuffer = NULL;
return ret;
}
APPSPAWN_STATIC int GetAppSpawnMsg(AppSpawningCtx *property, uint32_t memSize, RunMode mode)
{
uint8_t *buffer = (uint8_t *)GetMapMem(property->client.id, "prefork", memSize, true, mode);
APPSPAWN_CHECK(buffer != NULL, return -1, "prefork buffer is null can not write propery");
uint32_t msgRecvLen = 0;
uint32_t remainLen = 0;
property->forkCtx.childMsg = (char *)buffer;
property->forkCtx.msgSize = memSize;
AppSpawnMsgNode *message = NULL;
int ret = GetAppSpawnMsgFromBuffer(buffer, ((AppSpawnMsg *)buffer)->msgLen, &message, &msgRecvLen, &remainLen);
APPSPAWN_LOGV("prefork GetAppSpawnMsg ret:%{public}d", ret);
if (ret == 0 && DecodeAppSpawnMsg(message) == 0 && CheckAppSpawnMsg(message) == 0) {
property->message = message;
message = NULL;
return 0;
}
DeleteAppSpawnMsg(&message);
return -1;
}
APPSPAWN_STATIC int SetPreforkProcessName(AppSpawnContent *content)
{
int ret = prctl(PR_SET_NAME, PREFORK_PROCESS);
if (ret == -1) {
return errno;
}
ret = memset_s(content->longProcName,
(size_t)content->longProcNameLen, 0, (size_t)content->longProcNameLen);
if (ret != EOK) {
return EINVAL;
}
ret = strncpy_s(content->longProcName, content->longProcNameLen,
PREFORK_PROCESS, strlen(PREFORK_PROCESS));
if (ret != EOK) {
return EINVAL;
}
return 0;
}
APPSPAWN_STATIC void ClearPipeFd(int pipe[], int length)
{
for (int i = 0; i < length; i++) {
if (pipe[i] > 0) {
close(pipe[i]);
pipe[i] = -1;
}
}
}
* @brief Set unlock mount result to system parameter
* @param uid User ID
* @param result Mount result (0 = success, non-zero = failure)
*/
APPSPAWN_STATIC void SetUnlockMountResult(int uid, int result)
{
char unlockMountParam[LOCK_STATUS_PARAM_SIZE] = {0};
int ret = snprintf_s(unlockMountParam, sizeof(unlockMountParam), sizeof(unlockMountParam) - 1,
"startup.appspawn.unlock_mount.%d", uid);
APPSPAWN_ONLY_EXPER(ret <= 0,
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent("UNLOCK_MOUNT_PARAM_FAIL");
#endif
APPSPAWN_LOGE("snprintf_s failed for uid=%{public}d, ret=%{public}d", uid, ret);
return);
int setRet = SetParameter(unlockMountParam, result == 0 ? "0" : "1");
APPSPAWN_ONLY_EXPER(setRet != 0,
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent("UNLOCK_MOUNT_PARAM_FAIL");
#endif
APPSPAWN_LOGE("SetParameter failed for uid=%{public}d, ret=%{public}d", uid, setRet));
}
static int AddUnlockChildWatcher(AppSpawningCtx *property, uint64_t timeoutMs)
{
APPSPAWN_CHECK(property != NULL, return APPSPAWN_SYSTEM_ERROR, "Invalid property");
LE_WatchInfo watchInfo = {};
watchInfo.fd = property->forkCtx.fd[0];
watchInfo.flags = WATCHER_ONCE;
watchInfo.events = EVENT_READ;
watchInfo.processEvent = ProcessUnlockChildResponse;
APPSPAWN_LOGI("AddUnlockChildWatcher fd=%{public}d pid=%{public}d",
watchInfo.fd, property->pid);
LE_STATUS status = LE_StartWatcher(LE_GetDefaultLoop(), &property->forkCtx.watcherHandle, &watchInfo, property);
APPSPAWN_CHECK(status == LE_SUCCESS,
return APPSPAWN_SYSTEM_ERROR, "Failed to start unlock watcher fd=%{public}d status=%{public}d",
watchInfo.fd, status);
status = LE_CreateTimer(LE_GetDefaultLoop(), &property->forkCtx.timer, UnlockChildTimeout, property);
APPSPAWN_ONLY_EXPER(status == LE_SUCCESS,
status = LE_StartTimer(LE_GetDefaultLoop(), property->forkCtx.timer, timeoutMs, 0));
APPSPAWN_ONLY_EXPER(status == LE_SUCCESS, return 0);
APPSPAWN_ONLY_EXPER(property->forkCtx.timer != NULL,
LE_StopTimer(LE_GetDefaultLoop(), property->forkCtx.timer));
property->forkCtx.timer = NULL;
LE_RemoveWatcher(LE_GetDefaultLoop(), property->forkCtx.watcherHandle);
property->forkCtx.watcherHandle = NULL;
APPSPAWN_LOGE("Failed to start unlock timer, status=%{public}d", status);
return APPSPAWN_SYSTEM_ERROR;
}
* @brief Watcher callback for processing unlock mount result from child process
* @param taskHandle Watcher handle (unused)
* @param fd Pipe read end file descriptor
* @param events Event flags (unused)
* @param context AppSpawningCtx pointer
*
* Called when child process writes result to pipe. Reads result from pipe,
* sends response to client, and cleans up resources.
*/
static void ProcessUnlockChildResponse(const WatcherHandle taskHandle, int fd,
uint32_t *events, const void *context)
{
AppSpawningCtx *property = (AppSpawningCtx *)context;
APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return);
property->forkCtx.watcherHandle = NULL;
LE_RemoveWatcher(LE_GetDefaultLoop(), (WatcherHandle)taskHandle);
int result = APPSPAWN_SYSTEM_ERROR;
ssize_t readSize = read(fd, &result, sizeof(result));
APPSPAWN_ONLY_EXPER(readSize != sizeof(result),
APPSPAWN_LOGE("ProcessUnlockChildResponse read failed, ret=%{public}zd errno=%{public}d", readSize, errno);
result = APPSPAWN_SYSTEM_ERROR);
APPSPAWN_LOGI("ProcessUnlockChildResponse pid=%{public}d result=%{public}d", property->pid, result);
APPSPAWN_ONLY_EXPER(property->message != NULL && property->message->connection != NULL,
SendResponse(property->message->connection, &property->message->msgHeader, result, 0));
property->message = NULL;
DeleteAppSpawningCtx(property);
}
static void UnlockChildTimeout(const TimerHandle taskHandle, void *context)
{
AppSpawningCtx *property = (AppSpawningCtx *)context;
APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return);
APPSPAWN_LOGE("UnlockChildTimeout childPid=%{public}d", property->pid);
APPSPAWN_ONLY_EXPER(property->pid > 0, kill(property->pid, SIGKILL));
APPSPAWN_ONLY_EXPER(property->message != NULL && property->message->connection != NULL,
SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_SPAWN_TIMEOUT, 0));
property->message = NULL;
DeleteAppSpawningCtx(property);
}
APPSPAWN_STATIC void HandlePreforkUnlockMsg(AppSpawnContent *content, const AppSpawnUnlockMsg *unlockMsg)
{
APPSPAWN_LOGI("HandlePreforkUnlockMsg: uid=%{public}d pid=%{public}d",
unlockMsg->uid, getpid());
int result = ProcessUnlockMessage(unlockMsg->uid);
SetUnlockMountResult(unlockMsg->uid, result);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(result == 0 ? UNLOCK_SUCCESS : "UNLOCK_MOUNT_FAIL");
#endif
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
AppSpawnFds *childToParentFd = FindSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);
APPSPAWN_ONLY_EXPER(childToParentFd == NULL || childToParentFd->fds[1] < 0,
APPSPAWN_LOGE("L1 prefork childToParentFd not found, uid=%{public}d result=%{public}d dropped",
unlockMsg->uid, result);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent("UNLOCK_MOUNT_L1_RESULT_DROPPED");
#endif
ProcessExit(0));
APPSPAWN_LOGI("L1 prefork write result: uid=%{public}d result=%{public}d fd=%{public}d",
unlockMsg->uid, result, childToParentFd->fds[1]);
ssize_t writeSize = write(childToParentFd->fds[1], &result, sizeof(result));
APPSPAWN_ONLY_EXPER(writeSize != sizeof(result),
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent("UNLOCK_MOUNT_L1_WRITE_FAIL");
#endif
APPSPAWN_LOGE("prefork write result failed %{public}zd %{public}d", writeSize, errno));
ProcessExit(0);
}
* @brief Handle FORK message in prefork child process.
*
* Called by PreforkChildLoop when a MSG_APP_SPAWN pipe message is received.
* This function runs in the prefork child process context:
* 1. Validates msgLen from the parent
* 2. Sets up client identity (id, flags)
* 3. Retrieves childToParentFd from spawningFdsQueue (registered by ForkAndRegisterFds)
* 4. Reads app spawn message from shared memory (mmap)
* 5. Calls AppSpawnChild to complete the actual app spawning
*
* @note This function never returns normally - always calls ProcessExit().
*/
APPSPAWN_STATIC void HandlePreforkForkMsg(AppSpawnContent *content, AppSpawningCtx *property,
const AppSpawnPreforkMsg *preforkMsg)
{
APPSPAWN_CHECK(preforkMsg != NULL && content != NULL && property != NULL, ProcessExit(0),
"preforkMsg is NULL");
if (preforkMsg->msgLen > MAX_MSG_TOTAL_LENGTH || preforkMsg->msgLen < sizeof(AppSpawnMsg)) {
APPSPAWN_LOGE("prefork process invalid msgLen %{public}u", preforkMsg->msgLen);
ProcessExit(0);
}
property->client.id = preforkMsg->id;
property->client.flags = preforkMsg->flags;
property->isPrefork = true;
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
AppSpawnFds *pfFds = FindSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);
APPSPAWN_CHECK(pfFds != NULL, ProcessExit(0), "prefork fds not found in queue");
property->forkCtx.fd[0] = pfFds->fds[0];
property->forkCtx.fd[1] = pfFds->fds[1];
RemoveSpawningFdsByPid(mgr, getpid(), TYPE_CHILD_PARENT);
property->state = APP_STATE_SPAWNING;
const uint32_t memSize = (preforkMsg->msgLen / MAX_MSG_BLOCK_LEN + 1) * MAX_MSG_BLOCK_LEN;
if (GetAppSpawnMsg(property, memSize, content->mode) == -1) {
APPSPAWN_LOGE("prefork child read GetAppSpawnMsg failed");
ClearPreforkInfo(property);
content->notifyResToParent(content, &property->client, APPSPAWN_MSG_INVALID);
ProcessExit(0);
}
ClearPreforkInfo(property);
ProcessExit(AppSpawnChild(content, &property->client));
}
* @brief Creates two pipes before fork:
* - childToParentFd: used for subsequent fork result notification (child -> parent)
* - parentToChildFd: used for sending fork requests (parent -> prefork child)
* Registers pipe fds to spawningFdsQueue BEFORE fork (with pid=-1 as placeholder),
* then updates the pid after fork. This simplifies error handling since pre-fork
* failures don't require killing a child process.
*
* @param mgr AppSpawn manager instance
* @param property Current spawning context
* @param childToParentFd [out] Created prefork pipe (fd[0]=read, fd[1]=write)
* @param parentToChildFd [out] Created parent-to-child pipe
* @return fork result: >0 parent (child pid), 0 child, <0 fork failed
*/
APPSPAWN_STATIC pid_t ForkAndRegisterFds(AppSpawnMgr *mgr, AppSpawningCtx *property,
int childToParentFd[PIPE_FD_LENGTH], int parentToChildFd[PIPE_FD_LENGTH])
{
APPSPAWN_CHECK(pipe(childToParentFd) == 0, return -1,
"prefork with prefork pipe failed %{public}d", errno);
APPSPAWN_CHECK(pipe(parentToChildFd) == 0, ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
return -1, "prefork with parent-child pipe failed %{public}d", errno);
SpawningFdRegInfo pfRegInfo = { TYPE_CHILD_PARENT, PIPE_FD_LENGTH, childToParentFd, -1 };
AppSpawnFds *pfFds = RegisterSpawningFds(mgr, &pfRegInfo);
APPSPAWN_CHECK(pfFds != NULL, ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
return -1, "regist pfFds failed");
SpawningFdRegInfo pcRegInfo = { TYPE_PARENT_CHILD, PIPE_FD_LENGTH, parentToChildFd, -1 };
AppSpawnFds *pcFds = RegisterSpawningFds(mgr, &pcRegInfo);
APPSPAWN_CHECK(pcFds != NULL, DeleteSpawningFds(&pfFds);
ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
return -1, "regist pcFds failed");
StartAppspawnTrace("AppspawnPreFork");
pid_t pid = fork();
if (pid > 0) {
pfFds->pid = pid;
pcFds->pid = pid;
APPSPAWN_LOGV("create pipefd %{public}d %{public}d,%{public}d %{public}d,%{public}d",
pid, childToParentFd[0], childToParentFd[1], parentToChildFd[0], parentToChildFd[1]);
FinishAppspawnTrace();
} else if (pid == 0) {
HilogCloseSocketFd();
pfFds->pid = getpid();
pcFds->pid = getpid();
} else {
DeleteSpawningFds(&pfFds);
DeleteSpawningFds(&pcFds);
ClearPipeFd(childToParentFd, PIPE_FD_LENGTH);
ClearPipeFd(parentToChildFd, PIPE_FD_LENGTH);
APPSPAWN_LOGE("prefork fork failed err %{public}d", errno);
FinishAppspawnTrace();
}
return pid;
}
* @brief Prefork child process main loop: wait for pipe message and dispatch.
*
* This function runs in the prefork child after fork(). It:
* 1. Clears the inherited forkCtx fds (not needed in prefork child)
* 2. Sets process name to "PreforkProcess" for debugging
* 3. Blocks on reading parentToChildFd[0] for a pipe message from parent
* 4. Dispatches to the appropriate handler based on message type
*
* The prefork child is a long-lived process: it blocks on read() and handles
* one fork request at a time. After handling, it calls ProcessExit() because
* AppSpawnChild reuses the prefork child's pid for the actual app process.
*
* @param content AppSpawn content
* @param property Spawning context
* @param mgr AppSpawn manager
* @param errorLevel fdsan error level saved before fork (to inherit in child)
*/
APPSPAWN_STATIC void PreforkChildLoop(AppSpawnContent *content, AppSpawningCtx *property,
AppSpawnMgr *mgr, enum fdsan_error_level errorLevel)
{
ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
int isRet = SetPreforkProcessName(content);
APPSPAWN_LOGV("prefork process start wait read msg with set processname %{public}d", isRet);
AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, getpid(), TYPE_PARENT_CHILD);
APPSPAWN_CHECK(pcFds != NULL, ProcessExit(0), "parent child fds not found");
AppSpawnPipeMsg pipeMsg = {0};
int infoSize = read(pcFds->fds[0], &pipeMsg, sizeof(AppSpawnPipeMsg));
if (infoSize != sizeof(AppSpawnPipeMsg)) {
APPSPAWN_LOGE("prefork process read msg failed %{public}d,%{public}d", infoSize, errno);
ProcessExit(0);
}
(void)fdsan_set_error_level(errorLevel);
switch (pipeMsg.type) {
case MSG_LOCK_STATUS:
HandlePreforkUnlockMsg(content, &pipeMsg.msg.unlockMsg);
break;
case MSG_APP_SPAWN:
HandlePreforkForkMsg(content, property, &pipeMsg.msg.preforkMsg);
break;
default:
APPSPAWN_LOGE("prefork process received unknown msg type %{public}d", pipeMsg.type);
ProcessExit(0);
}
}
* @brief Fork a prefork child process and dispatch based on fork result.
*
* Called by AppSpawnProcessMsgForPrefork to maintain the prefork pool.
* - On first call (reservedPid <= 0): creates the prefork child
* - On subsequent calls: the prefork child already exists, this is a no-op
* (ForkAndRegisterFds will return -1 and be ignored)
*
* Pipe fd lifecycle:
* - ForkAndRegisterFds creates pipes and registers them to spawningFdsQueue
* - Parent side: holds fds in spawningFdsQueue, transfers to forkCtx in
* TransferPreforkFdToForkCtx when sending a fork request
* - Child side: PreforkChildLoop reads from parentToChildFd, then
* HandlePreforkForkMsg takes over childToParentFd for app spawning
*/
static void ProcessPreFork(AppSpawnContent *content, AppSpawningCtx *property)
{
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
content->reservedPid = -1;
int childToParentFd[PIPE_FD_LENGTH] = {-1, -1};
int parentToChildFd[PIPE_FD_LENGTH] = {-1, -1};
enum fdsan_error_level errorLevel = fdsan_get_error_level();
content->reservedPid = ForkAndRegisterFds(mgr, property, childToParentFd, parentToChildFd);
APPSPAWN_ONLY_EXPER(content->reservedPid == 0, PreforkChildLoop(content, property, mgr, errorLevel));
}
static int NormalSpawnChild(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
APPSPAWN_CHECK(client != NULL, return APPSPAWN_ARG_INVALID, "client is null");
int ret = InitForkContext((AppSpawningCtx *)client);
APPSPAWN_CHECK(ret == 0, return ret, "init fork context failed");
return AppSpawnProcessMsg(content, client, childPid);
}
* @brief Prepare prefork message: allocate mmap, write msg via shared memory, build pipe message.
*
* Three-step preparation:
* 1. Allocate shared memory (mmap) for the app spawn message
* 2. Write the message to shared memory via WritePreforkMsg
* 3. Build a lightweight AppSpawnPipeMsg containing only client id, flags, and msgLen
* (the prefork child will read the full message from mmap using these hints)
*
* @param content AppSpawn content
* @param property Spawning context containing the full message
* @param client Client info (id, flags)
* @param memSize Shared memory size (already aligned to MAX_MSG_BLOCK_LEN)
* @param outPipeMsg [out] Allocated pipe message (caller must free)
* @return APPSPAWN_OK on success, APPSPAWN_SYSTEM_ERROR on failure
*/
APPSPAWN_STATIC int PreparePreforkMsg(AppSpawnContent *content, AppSpawningCtx *property,
const AppSpawnClient *client, uint32_t memSize, AppSpawnPipeMsg **outPipeMsg)
{
content->propertyBuffer = GetMapMem(property->client.id, "prefork", memSize, false, content->mode);
APPSPAWN_ONLY_EXPER(content->propertyBuffer == NULL, ClearMMAP(property->client.id, memSize);
return APPSPAWN_SYSTEM_ERROR);
int ret = WritePreforkMsg(property, memSize);
APPSPAWN_ONLY_EXPER(ret != 0, ClearMMAP(property->client.id, memSize);
return APPSPAWN_SYSTEM_ERROR);
AppSpawnPipeMsg *pipeMsg = (AppSpawnPipeMsg *)calloc(1, sizeof(AppSpawnPipeMsg));
APPSPAWN_ONLY_EXPER(pipeMsg == NULL, APPSPAWN_LOGE("calloc failed");
ClearMMAP(property->client.id, memSize);
return APPSPAWN_SYSTEM_ERROR);
pipeMsg->type = MSG_APP_SPAWN;
pipeMsg->msg.preforkMsg.id = client->id;
pipeMsg->msg.preforkMsg.flags = client->flags;
pipeMsg->msg.preforkMsg.msgLen = property->message->msgHeader.msgLen;
*outPipeMsg = pipeMsg;
return APPSPAWN_OK;
}
* @brief Send pipe message to prefork child process.
*
* Writes the AppSpawnPipeMsg to parentToChildFd[1] (write end of the pipe).
* On success, immediately closes and unregisters the parent-child pipe fds
* since they are no longer needed after the message is sent.
*
* @param mgr AppSpawn manager instance
* @param childPid Prefork child process ID
* @param pipeMsg Pipe message to send
* @return APPSPAWN_OK on success, APPSPAWN_ARG_INVALID if fd not found, APPSPAWN_SYSTEM_ERROR on write failure
*/
APPSPAWN_STATIC int SendPipeMsgToChild(AppSpawnMgr *mgr, pid_t childPid, AppSpawnPipeMsg *pipeMsg)
{
AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, childPid, TYPE_PARENT_CHILD);
if (pcFds == NULL) {
APPSPAWN_LOGE("parent child fds not found for pid %{public}d", childPid);
return APPSPAWN_ARG_INVALID;
}
ssize_t writesize = write(pcFds->fds[1], pipeMsg, sizeof(AppSpawnPipeMsg));
if (writesize < 0 || (size_t)writesize != sizeof(AppSpawnPipeMsg)) {
APPSPAWN_LOGE("write msg to child failed %{public}d", errno);
return APPSPAWN_SYSTEM_ERROR;
}
UnregisterSpawningFdsByPid(mgr, childPid, TYPE_PARENT_CHILD);
return APPSPAWN_OK;
}
* @brief Cleanup prefork child on failure: kill child, cleanup spawning fds.
*
* Called when sending a fork request to the prefork child fails.
* Kills the prefork child and removes all its fds from spawningFdsQueue.
* The next spawn request will create a new prefork child via ProcessPreFork.
*/
APPSPAWN_STATIC void CleanupPreforkChild(AppSpawnMgr *mgr, pid_t childPid)
{
(void)kill(childPid, SIGKILL);
CleanupSpawningFdsByPid(mgr, childPid);
}
* @brief Transfer childToParentFd from spawningFdsQueue to forkCtx.
*
* After the prefork child has been notified via pipe, the parent needs to
* take over the prefork pipe for result notification. This function:
* 1. Looks up TYPE_CHILD_PARENT fds from the queue
* 2. Copies fd values to property->forkCtx.fd
* 3. Removes the node from queue WITHOUT closing fds (ownership transfer)
* 4. Sets the read end to non-blocking mode
*
* @return APPSPAWN_OK on success, APPSPAWN_ARG_INVALID if prefork fds not found
*/
APPSPAWN_STATIC int TransferPreforkFdToForkCtx(AppSpawnMgr *mgr, pid_t childPid, AppSpawningCtx *property)
{
AppSpawnFds *pfFds = FindSpawningFdsByPid(mgr, childPid, TYPE_CHILD_PARENT);
if (pfFds == NULL) {
APPSPAWN_LOGE("prefork fds not found for pid %{public}d", childPid);
return APPSPAWN_ARG_INVALID;
}
property->forkCtx.fd[0] = pfFds->fds[0];
property->forkCtx.fd[1] = pfFds->fds[1];
RemoveSpawningFdsByPid(mgr, childPid, TYPE_CHILD_PARENT);
int flags = fcntl(property->forkCtx.fd[0], F_GETFL);
if (flags >= 0) {
int ret = fcntl(property->forkCtx.fd[0], F_SETFL, (unsigned int)flags | O_NONBLOCK);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "fcntl failed %{public}d,%{public}d", ret, errno);
}
return APPSPAWN_OK;
}
* @brief Process app spawn message using prefork optimization.
*
* Prefork flow:
* 1. If no prefork child exists (reservedPid <= 0):
* - Fall back to normal fork for this request
* - Fork a new prefork child for future requests (ProcessPreFork)
*
* 2. If prefork child exists (reservedPid > 0):
* - Prepare message in shared memory (PreparePreforkMsg)
* - Send pipe message to prefork child (SendPipeMsgToChild)
* - Take over prefork pipe for result notification (TransferPreforkFdToForkCtx)
* - On failure: kill prefork child, cleanup fds, fork a new one (ProcessPreFork)
* - On success: fork a new prefork child for next request (ProcessPreFork)
*
* @return APPSPAWN_OK on success, APPSPAWN_SYSTEM_ERROR on failure
*/
static int AppSpawnProcessMsgForPrefork(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
int ret = APPSPAWN_OK;
AppSpawningCtx *property = (AppSpawningCtx *)client;
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
if (content->reservedPid <= 0) {
ret = NormalSpawnChild(content, client, childPid);
ProcessPreFork(content, property);
return ret;
}
const uint32_t memSize = (property->message->msgHeader.msgLen / MAX_MSG_BLOCK_LEN + 1) *
MAX_MSG_BLOCK_LEN;
AppSpawnPipeMsg *pipeMsg = NULL;
APPSPAWN_ONLY_EXPER(PreparePreforkMsg(content, property, client, memSize, &pipeMsg) != APPSPAWN_OK,
return NormalSpawnChild(content, client, childPid));
*childPid = content->reservedPid;
if (SendPipeMsgToChild(mgr, *childPid, pipeMsg) != APPSPAWN_OK ||
TransferPreforkFdToForkCtx(mgr, *childPid, property) != APPSPAWN_OK) {
CleanupPreforkChild(mgr, *childPid);
*childPid = 0;
ret = APPSPAWN_SYSTEM_ERROR;
}
free(pipeMsg);
ProcessPreFork(content, property);
return ret;
}
static bool IsSupportPrefork(AppSpawnContent *content, AppSpawnClient *client)
{
#ifdef APPSPAWN_SUPPORT_PREFORK
if (client == NULL || content == NULL) {
return false;
}
if (!content->enablePerfork) {
APPSPAWN_LOGV("g_enablePrefork %{public}d", content->enablePerfork);
return false;
}
AppSpawningCtx *property = (AppSpawningCtx *)client;
if (content->mode == MODE_FOR_APP_SPAWN && !IsChildColdRun(property)
&& !CheckAppMsgFlagsSet(property, APP_FLAGS_CHILDPROCESS)) {
return true;
}
#endif
return false;
}
static bool IsBootFinished(void)
{
char buffer[32] = {0};
int ret = GetParameter("bootevent.boot.completed", "false", buffer, sizeof(buffer));
bool isBootCompleted = (ret > 0 && strcmp(buffer, "true") == 0);
return isBootCompleted;
}
static int RunAppSpawnProcessMsg(AppSpawnContent *content, AppSpawnClient *client, pid_t *childPid)
{
int ret = 0;
if (IsBootFinished() && IsSupportPrefork(content, client)) {
ret = AppSpawnProcessMsgForPrefork(content, client, childPid);
} else {
ret = NormalSpawnChild(content, client, childPid);
}
return ret;
}
static void ProcessSpawnReqMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
int ret = CheckAppSpawnMsg(message);
if (ret != 0) {
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawnMsg(&message);
return;
}
if (IsDeveloperModeOpen()) {
if (IsSupportRunHnp()) {
SetAppSpawnMsgFlag(message, TLV_MSG_FLAGS, APP_FLAGS_DEVELOPER_MODE);
} else {
APPSPAWN_LOGV("Not support execute hnp file!");
}
}
AppSpawningCtx *property = CreateAppSpawningCtx();
if (property == NULL) {
SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
DeleteAppSpawnMsg(&message);
return;
}
property->state = APP_STATE_SPAWNING;
property->message = message;
message->connection = connection;
StartAppspawnTrace("STAGE_PARENT_MSG_DECODE");
ret = AppSpawnHookExecute(STAGE_PARENT_MSG_DECODE, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
FinishAppspawnTrace();
APPSPAWN_ONLY_EXPER(ret != 0, APPSPAWN_LOGE("rebuild hook failed: %{public}d, aborting spawn", ret);
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawningCtx(property);
return);
AppSpawnHookExecute(STAGE_PARENT_PRE_FORK, 0, GetAppSpawnContent(), &property->client);
DumpAppSpawnMsg(property->message);
clock_gettime(CLOCK_MONOTONIC, &property->spawnStart);
ret = RunAppSpawnProcessMsg(GetAppSpawnContent(), &property->client, &property->pid);
AppSpawnHookExecute(STAGE_PARENT_POST_FORK, 0, GetAppSpawnContent(), &property->client);
if (ret != 0) {
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawningCtx(property);
return;
}
if (AddChildWatcher(property) != 0) {
kill(property->pid, SIGKILL);
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawningCtx(property);
return;
}
}
static uint32_t g_lastDiedAppId = 0;
static uint32_t g_crashTimes = 0;
#define MAX_CRASH_TIME 5
static void WaitChildDied(pid_t pid, int status)
{
AppSpawningCtx *property = GetAppSpawningCtxByPid(pid);
if (property != NULL && property->message != NULL && property->state == APP_STATE_SPAWNING) {
const char *processName = GetProcessName(property);
APPSPAWN_LOGI("Child process %{public}s fail \'child crash \'pid %{public}d appId: %{public}d",
processName, property->pid, property->client.id);
#ifdef APPSPAWN_HISYSEVENT
ReportSpawnChildProcessFail(processName, ERR_APPSPAWN_CHILD_CRASH, APPSPAWN_CHILD_CRASH);
#endif
if (WIFSIGNALED(status)) {
if (property->client.id == g_lastDiedAppId + 1) {
g_crashTimes++;
} else {
g_crashTimes = 1;
}
g_lastDiedAppId = property->client.id;
} else {
g_crashTimes = 0;
}
SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_CHILD_CRASH, 0);
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
DeleteAppSpawningCtx(property);
if (g_crashTimes >= MAX_CRASH_TIME) {
APPSPAWN_LOGW("Continuous failures in spawning the app, restart appspawn");
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(APPSPAWN_MAX_FAILURES_EXCEEDED);
#endif
StopAppSpawn();
}
}
}
static void WaitChildTimeout(const TimerHandle taskHandle, void *context)
{
AppSpawningCtx *property = (AppSpawningCtx *)context;
APPSPAWN_LOGI("Child process %{public}s fail \'wait child timeout \'pid %{public}d appId: %{public}d",
GetProcessName(property), property->pid, property->client.id);
if (property->pid > 0) {
#if (!defined(CJAPP_SPAWN) && !defined(NATIVE_SPAWN))
DumpSpawnStack(property->pid);
#endif
kill(property->pid, SIGKILL);
}
#ifdef APPSPAWN_HISYSEVENT
ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_SPAWN_TIMEOUT, APPSPAWN_SPAWN_TIMEOUT);
#endif
SendResponse(property->message->connection, &property->message->msgHeader, APPSPAWN_SPAWN_TIMEOUT, 0);
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
DeleteAppSpawningCtx(property);
}
static int ProcessChildFdCheck(int fd, AppSpawningCtx *property)
{
int result = 0;
(void)read(fd, &result, sizeof(result));
APPSPAWN_DUMPI("Child process:%{public}s success pid:%{public}d appId:%{public}d result:%{public}d",
GetProcessName(property), property->pid, property->client.id, result);
APPSPAWN_CHECK(property->message != NULL, return -1, "Invalid message in ctx %{public}d", property->client.id);
if (result != 0) {
#ifdef APPSPAWN_HISYSEVENT
ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_SPAWN_FAIL, result);
#endif
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
SendResponse(property->message->connection, &property->message->msgHeader, result, property->pid);
DeleteAppSpawningCtx(property);
return -1;
}
return 0;
}
static uint32_t GetAppCloneIndex(AppSpawningCtx *property)
{
APPSPAWN_ONLY_EXPER(!CheckAppMsgFlagsSet(property, APP_FLAGS_CLONE_ENABLE), return 0);
AppSpawnMsgBundleInfo *bundleInfo = (AppSpawnMsgBundleInfo *)GetAppProperty(property, TLV_BUNDLE_INFO);
return (bundleInfo != NULL) ? bundleInfo->bundleIndex : 0;
}
static AppSpawnedProcess *InitSpawnedProcessInfo(AppSpawningCtx *property)
{
bool isDebuggable = CheckAppMsgFlagsSet(property, APP_FLAGS_DEBUGGABLE);
uint32_t appIndex = GetAppCloneIndex(property);
const AppSpawnMsgAccessToken *tokenInfo = (const AppSpawnMsgAccessToken *)
GetAppSpawnMsgInfo(property->message, TLV_ACCESS_TOKEN_INFO);
uint64_t tokenid = (tokenInfo != NULL) ? tokenInfo->accessTokenIdEx : 0;
AppSpawnedProcess *appInfo = AddSpawnedProcess(property->pid, GetBundleName(property),
appIndex, isDebuggable, tokenid);
APPSPAWN_CHECK(appInfo != NULL, return NULL, "Failed to add spawned process");
AppSpawnMsgDacInfo *dacInfo = GetAppProperty(property, TLV_DAC_INFO);
appInfo->uid = (dacInfo != NULL) ? dacInfo->uid : 0;
appInfo->spawnStart.tv_sec = property->spawnStart.tv_sec;
appInfo->spawnStart.tv_nsec = property->spawnStart.tv_nsec;
appInfo->lockBundleRefAdded = property->lockBundleRefAdded;
appInfo->spmRefAdded = property->spmRefAdded;
appInfo->lockPath = property->lockPath;
property->lockPath = NULL;
return appInfo;
}
static void HandleSpawnProcessInfoFail(AppSpawningCtx *property)
{
APPSPAWN_LOGE("Failed to init spawned process info, killing pid %{public}d", property->pid);
kill(property->pid, SIGKILL);
#ifdef APPSPAWN_HISYSEVENT
ReportSpawnChildProcessFail(GetProcessName(property), ERR_APPSPAWN_PROCESS_INFO_INIT_FAIL,
ERR_APPSPAWN_PROCESS_INFO_INIT_FAIL);
#endif
AppSpawnHookExecute(STAGE_SERVER_SPAWN_ABORT, 0, GetAppSpawnContent(), &property->client);
SendResponse(property->message->connection, &property->message->msgHeader,
APPSPAWN_SYSTEM_ERROR, property->pid);
DeleteAppSpawningCtx(property);
}
static void ProcessChildResponse(const WatcherHandle taskHandle, int fd, uint32_t *events, const void *context)
{
AppSpawningCtx *property = (AppSpawningCtx *)context;
property->forkCtx.watcherHandle = NULL;
LE_RemoveWatcher(LE_GetDefaultLoop(), (WatcherHandle)taskHandle);
if (ProcessChildFdCheck(fd, property) != 0) {
return;
}
AppSpawnedProcess *appInfo = InitSpawnedProcessInfo(property);
APPSPAWN_ONLY_EXPER(appInfo == NULL, HandleSpawnProcessInfoFail(property);
return);
#ifdef DEBUG_BEGETCTL_BOOT
if (IsDeveloperModeOpen()) {
appInfo->message = property->message;
}
#endif
clock_gettime(CLOCK_MONOTONIC, &appInfo->spawnEnd);
#ifdef APPSPAWN_HISYSEVENT
uint32_t spawnProcessDuration = (uint32_t)((appInfo->spawnEnd.tv_sec - appInfo->spawnStart.tv_sec) *
(APPSPAWN_USEC_TO_NSEC)) +(uint32_t)((appInfo->spawnEnd.tv_nsec - appInfo->spawnStart.tv_nsec) /
(APPSPAWN_MSEC_TO_NSEC));
AppSpawnMgr *appspawnMgr = GetAppSpawnMgr();
if (appspawnMgr != NULL) {
AddStatisticEventInfo(appspawnMgr->hisyseventInfo, spawnProcessDuration, IsBootFinished());
}
#ifndef ASAN_DETECTOR
uint64_t diff = DiffTime(&appInfo->spawnStart, &appInfo->spawnEnd);
APPSPAWN_CHECK_ONLY_EXPER(diff < (IsChildColdRun(property) ? SPAWN_COLDRUN_DURATION : SPAWN_DURATION),
ReportAbnormalDuration("SPAWNCHILD", diff));
#endif
#endif
WatchChildProcessFd(property);
ProcessMgrHookExecute(STAGE_SERVER_APP_ADD, GetAppSpawnContent(), appInfo);
AppSpawnHookExecute(STAGE_PARENT_PRE_RELY, 0, GetAppSpawnContent(), &property->client);
StartAppspawnTrace("SendResponse");
SendResponse(property->message->connection, &property->message->msgHeader, 0, property->pid);
FinishAppspawnTrace();
AppSpawnHookExecute(STAGE_PARENT_POST_RELY, 0, GetAppSpawnContent(), &property->client);
#ifdef DEBUG_BEGETCTL_BOOT
if (IsDeveloperModeOpen()) {
property->message = NULL;
}
#endif
DeleteAppSpawningCtx(property);
}
static void NotifyResToParent(AppSpawnContent *content, AppSpawnClient *client, int result)
{
AppSpawningCtx *property = (AppSpawningCtx *)client;
APPSPAWN_CHECK(property != NULL && client != NULL, return, "invalid param");
int fd = property->forkCtx.fd[1];
if (fd >= 0) {
(void)write(fd, &result, sizeof(result));
(void)close(fd);
property->forkCtx.fd[1] = -1;
}
APPSPAWN_LOGV("NotifyResToParent client id: %{public}u result: 0x%{public}x", client->id, result);
}
static int CreateAppSpawnServer(TaskHandle *server, const char *socketName)
{
char path[128] = {0};
int ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%s%s", APPSPAWN_SOCKET_DIR, socketName);
APPSPAWN_CHECK(ret > 0, return -1, "Failed to snprintf_s %{public}d", ret);
int socketId = GetControlSocket(socketName);
APPSPAWN_LOGI("get socket form env %{public}s socketId %{public}d", socketName, socketId);
LE_StreamServerInfo info = {};
info.baseInfo.flags = TASK_STREAM | TASK_PIPE | TASK_SERVER;
info.socketId = socketId;
info.server = path;
info.baseInfo.close = OnListenFdClose;
info.incommingConnect = OnConnection;
MakeDirRec(path, 0711, 0);
ret = LE_CreateStreamServer(LE_GetDefaultLoop(), server, &info);
APPSPAWN_CHECK(ret == 0, return -1, "Failed to create socket for %{public}s errno: %{public}d", path, errno);
SetFdCtrl(LE_GetSocketFd(*server), FD_CLOEXEC);
APPSPAWN_LOGI("CreateAppSpawnServer path %{public}s fd %{public}d", path, LE_GetSocketFd(*server));
return 0;
}
void AppSpawnDestroyContent(AppSpawnContent *content)
{
if (content == NULL) {
return;
}
AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
if (appSpawnContent->sigHandler != NULL) {
APPSPAWN_ONLY_EXPER(appSpawnContent->servicePid == getpid(),
LE_CloseSignalTask(LE_GetDefaultLoop(), appSpawnContent->sigHandler);
appSpawnContent->sigHandler = NULL);
int fd = LE_GetSocketFd(appSpawnContent->sigHandler);
APPSPAWN_ONLY_EXPER(appSpawnContent->servicePid != getpid() && fd > 0, close(fd));
}
APPSPAWN_ONLY_EXPER(content->signalFd > 0, close(content->signalFd);
content->signalFd = -1);
if (appSpawnContent->server != NULL && appSpawnContent->servicePid == getpid()) {
LE_CloseStreamTask(LE_GetDefaultLoop(), appSpawnContent->server);
appSpawnContent->server = NULL;
}
DeleteAppSpawnMgr(appSpawnContent);
LE_StopLoop(LE_GetDefaultLoop());
LE_CloseLoop(LE_GetDefaultLoop());
}
APPSPAWN_STATIC int AppSpawnColdStartApp(struct AppSpawnContent *content, AppSpawnClient *client)
{
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
AppSpawningCtx *property = (AppSpawningCtx *)client;
const char *processName = GetProcessName(property);
APPSPAWN_CHECK(processName != NULL, return APPSPAWN_ARG_INVALID, "Failed to get process name");
APPSPAWN_LOGV("Write msg to child %{public}s", processName);
int ret = WriteMsgToChild(property, content->mode);
APPSPAWN_CHECK(ret == 0, return APPSPAWN_SYSTEM_ERROR, "Failed to write msg to child");
#ifdef CJAPP_SPAWN
char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/cjappspawn";
#elif NATIVE_SPAWN
char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/nativespawn";
#elif HYBRID_SPAWN
char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/hybridspawn";
#elif NWEB_SPAWN
char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/nwebspawn";
#else
char *path = property->forkCtx.coldRunPath != NULL ? property->forkCtx.coldRunPath : "/system/bin/appspawn";
#endif
char buffer[4][32] = {0};
int len = sprintf_s(buffer[0], sizeof(buffer[0]), " %d ", property->forkCtx.fd[1]);
APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format fd");
len = sprintf_s(buffer[1], sizeof(buffer[1]), " %u ", property->client.flags);
APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format flags");
len = sprintf_s(buffer[2], sizeof(buffer[2]), " %u ", property->forkCtx.msgSize);
APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format msgSize");
len = sprintf_s(buffer[3], sizeof(buffer[3]), " %u ", property->client.id);
APPSPAWN_CHECK(len > 0, return APPSPAWN_SYSTEM_ERROR, "Invalid to format clientId");
char *mode = IsAppSpawnMode(mgr) ? "app_cold" : (IsNWebSpawnMode(mgr) ? "nweb_cold" :
(IsCJSpawnMode(mgr) ? "cj_app_cold" : (IsNativeSpawnMode(mgr) ? "native_cold" : "hybrid_cold")));
APPSPAWN_LOGI("ColdStartApp::processName:%{public}s path:%{public}s mode:%{public}s", processName, path, mode);
#ifndef APPSPAWN_TEST
const char *const formatCmds[] = {
path, "-mode", mode, "-fd", buffer[0], buffer[1], buffer[2], "-param", processName, buffer[3], NULL
};
ret = execv(path, (char **)formatCmds);
if (ret != 0) {
APPSPAWN_LOGE("Failed to execv, errno: %{public}d", errno);
}
#endif
APPSPAWN_LOGV("ColdStartApp::processName:%{public}s end", processName);
return 0;
}
static AppSpawningCtx *GetAppSpawningCtxFromArg(AppSpawnMgr *content, int argc, char *const argv[])
{
AppSpawningCtx *property = CreateAppSpawningCtx();
APPSPAWN_CHECK(property != NULL, return NULL, "Create app spawning ctx fail");
property->forkCtx.fd[1] = atoi(argv[FD_VALUE_INDEX]);
property->client.flags = (uint32_t)atoi(argv[FLAGS_VALUE_INDEX]);
property->client.flags &= ~APP_COLD_START;
uint32_t size = (uint32_t)atoi(argv[SHM_SIZE_INDEX]);
property->client.id = (uint32_t)atoi(argv[CLIENT_ID_INDEX]);
uint8_t *buffer = (uint8_t *)GetMapMem(property->client.id,
argv[PARAM_VALUE_INDEX], size, true, content->content.mode);
if (buffer == NULL) {
APPSPAWN_LOGE("Failed to map errno %{public}d %{public}s", property->client.id, argv[PARAM_VALUE_INDEX]);
NotifyResToParent(&content->content, &property->client, APPSPAWN_SYSTEM_ERROR);
DeleteAppSpawningCtx(property);
return NULL;
}
uint32_t msgRecvLen = 0;
uint32_t remainLen = 0;
AppSpawnMsgNode *message = NULL;
int ret = GetAppSpawnMsgFromBuffer(buffer, ((AppSpawnMsg *)buffer)->msgLen, &message, &msgRecvLen, &remainLen);
munmap((char *)buffer, size);
char path[PATH_MAX] = {0};
int len = sprintf_s(path, sizeof(path), APPSPAWN_MSG_DIR "%s/%s_%u",
GetSpawnNameByRunMode(content->content.mode), argv[PARAM_VALUE_INDEX], property->client.id);
if (len > 0) {
unlink(path);
}
if (ret == 0 && DecodeAppSpawnMsg(message) == 0 && CheckAppSpawnMsg(message) == 0) {
property->message = message;
message = NULL;
return property;
}
NotifyResToParent(&content->content, &property->client, APPSPAWN_MSG_INVALID);
DeleteAppSpawnMsg(&message);
DeleteAppSpawningCtx(property);
return NULL;
}
static void AppSpawnColdRun(AppSpawnContent *content, int argc, char *const argv[])
{
APPSPAWN_CHECK(argc >= ARG_NULL, return, "Invalid arg for cold start %{public}d", argc);
AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
APPSPAWN_CHECK(appSpawnContent != NULL, return, "Invalid appspawn content");
AppSpawningCtx *property = GetAppSpawningCtxFromArg(appSpawnContent, argc, argv);
if (property == NULL) {
APPSPAWN_LOGE("Failed to get property from arg");
return;
}
DumpAppSpawnMsg(property->message);
int ret = AppSpawnExecuteSpawningHook(content, &property->client);
if (ret == 0) {
ret = AppSpawnExecutePreReplyHook(content, &property->client);
NotifyResToParent(content, &property->client, ret);
(void)AppSpawnExecutePostReplyHook(content, &property->client);
ret = APPSPAWN_SYSTEM_ERROR;
if (content->runChildProcessor != NULL) {
ret = content->runChildProcessor(content, &property->client);
}
} else {
NotifyResToParent(content, &property->client, ret);
}
if (ret != 0) {
AppSpawnEnvClear(content, &property->client);
}
APPSPAWN_LOGI("AppSpawnColdRun exit %{public}d.", getpid());
}
static void AppSpawnRun(AppSpawnContent *content, int argc, char *const argv[])
{
APPSPAWN_LOGI("AppSpawnRun");
AppSpawnMgr *appSpawnContent = (AppSpawnMgr *)content;
APPSPAWN_CHECK(appSpawnContent != NULL, return, "Invalid appspawn content");
LE_STATUS status = LE_CreateSignalTask(LE_GetDefaultLoop(), &appSpawnContent->sigHandler, ProcessSignal);
if (status == 0) {
(void)LE_AddSignal(LE_GetDefaultLoop(), appSpawnContent->sigHandler, SIGCHLD);
(void)LE_AddSignal(LE_GetDefaultLoop(), appSpawnContent->sigHandler, SIGTERM);
}
if (IsAppSpawnMode(appSpawnContent) || IsHybridSpawnMode(appSpawnContent)) {
struct sched_param param = { 0 };
param.sched_priority = 1;
int ret = sched_setscheduler(0, SCHED_FIFO, ¶m);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "UpdateSchedPrio failed ret: %{public}d, %{public}d", ret, errno);
}
LE_RunLoop(LE_GetDefaultLoop());
APPSPAWN_LOGI("AppSpawnRun exit mode: %{public}d ", content->mode);
(void)ServerStageHookExecute(STAGE_SERVER_EXIT, content);
AppSpawnDestroyContent(content);
}
APPSPAWN_STATIC int AppSpawnClearEnv(AppSpawnMgr *content, AppSpawningCtx *property)
{
APPSPAWN_CHECK(content != NULL, return 0, "Invalid appspawn content");
StartAppspawnTrace("AppSpawnClearEnv");
DeleteAppSpawningCtx(property);
AppSpawnDestroyContent(&content->content);
APPSPAWN_LOGV("clear %{public}d end", getpid());
FinishAppspawnTrace();
return 0;
}
static int IsEnablePrefork(void)
{
char buffer[32] = {0};
int ret = GetParameter("persist.sys.prefork.enable", "true", buffer, sizeof(buffer));
APPSPAWN_LOGV("IsEnablePrefork result %{public}d, %{public}s", ret, buffer);
return strcmp(buffer, "true") == 0;
}
AppSpawnContent *AppSpawnCreateContent(const char *socketName, char *longProcName, uint32_t nameLen, int mode)
{
APPSPAWN_CHECK(socketName != NULL && longProcName != NULL, return NULL, "Invalid name");
APPSPAWN_LOGI("AppSpawnCreateContent %{public}s %{public}u mode %{public}d", socketName, nameLen, mode);
AppSpawnMgr *appSpawnContent = CreateAppSpawnMgr(mode);
APPSPAWN_CHECK(appSpawnContent != NULL, return NULL, "Failed to alloc memory for appspawn");
#ifdef APPSPAWN_HISYSEVENT
appSpawnContent->hisyseventInfo = InitHisyseventTimer();
#endif
appSpawnContent->content.longProcName = longProcName;
appSpawnContent->content.longProcNameLen = nameLen;
appSpawnContent->content.notifyResToParent = NotifyResToParent;
if (IsColdRunMode(appSpawnContent)) {
appSpawnContent->content.runAppSpawn = AppSpawnColdRun;
} else {
appSpawnContent->content.runAppSpawn = AppSpawnRun;
appSpawnContent->content.coldStartApp = AppSpawnColdStartApp;
int ret = CreateAppSpawnServer(&appSpawnContent->server, socketName);
APPSPAWN_CHECK(ret == 0, AppSpawnDestroyContent(&appSpawnContent->content);
return NULL, "Failed to create server");
}
appSpawnContent->content.enablePerfork = IsEnablePrefork();
return &appSpawnContent->content;
}
AppSpawnContent *StartSpawnService(const AppSpawnStartArg *startArg, uint32_t argvSize, int argc, char *const argv[])
{
APPSPAWN_CHECK(startArg != NULL && argv != NULL, return NULL, "Invalid start arg");
AppSpawnStartArg *arg = (AppSpawnStartArg *)startArg;
APPSPAWN_LOGV("Start appspawn argvSize %{public}d mode %{public}d service %{public}s",
argvSize, arg->mode, arg->serviceName);
if (arg->initArg) {
int ret = memset_s(argv[0], argvSize, 0, (size_t)argvSize);
APPSPAWN_CHECK(ret == EOK, return NULL, "Failed to memset argv[0]");
ret = strncpy_s(argv[0], argvSize, arg->serviceName, strlen(arg->serviceName));
APPSPAWN_CHECK(ret == EOK, return NULL, "Failed to copy service name %{public}s", arg->serviceName);
}
StartAppspawnTrace("AppSpawnLoadCommonModules");
AppSpawnLoadAutoRunModules(MODULE_COMMON);
FinishAppspawnTrace();
AppSpawnModuleMgrInstall(ASAN_MODULE_PATH);
APPSPAWN_CHECK(LE_GetDefaultLoop() != NULL, return NULL, "Invalid default loop");
AppSpawnContent *content = AppSpawnCreateContent(arg->socketName, argv[0], argvSize, arg->mode);
APPSPAWN_CHECK(content != NULL, return NULL, "Failed to create content for %{public}s", arg->socketName);
StartAppspawnTrace("AppSpawnLoadAutoRunModules");
AppSpawnLoadAutoRunModules(arg->moduleType);
FinishAppspawnTrace();
int ret = ServerStageHookExecute(STAGE_SERVER_PRELOAD, content);
APPSPAWN_CHECK(ret == 0, AppSpawnDestroyContent(content);
return NULL, "Failed to prepare load %{public}s result: %{public}d", arg->serviceName, ret);
#ifndef APPSPAWN_TEST
if (content->runChildProcessor == NULL) {
APPSPAWN_LOGE("ChildLooper is not registered for %{public}s", arg->serviceName);
APPSPAWN_KLOGE("ChildLooper is not registered for %{public}s", arg->serviceName);
AppSpawnDestroyContent(content);
return NULL;
}
#endif
AddAppSpawnHook(STAGE_CHILD_PRE_RUN, HOOK_PRIO_LOWEST, AppSpawnClearEnv);
if (arg->mode == MODE_FOR_APP_SPAWN) {
SetParameter("bootevent.appspawn.started", "true");
}
return content;
}
static AppSpawnMsgNode *ProcessSpawnBegetctlMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
uint32_t len = 0;
const char *cmdMsg = (const char *)GetAppSpawnMsgExtInfo(message, MSG_EXT_NAME_BEGET_PID, &len);
APPSPAWN_CHECK(cmdMsg != NULL, return NULL, "Failed to get extInfo");
AppSpawnedProcess *appInfo = GetSpawnedProcess(atoi(cmdMsg));
APPSPAWN_CHECK(appInfo != NULL, return NULL, "Failed to get app info");
AppSpawnMsgNode *msgNode = RebuildAppSpawnMsgNode(message, appInfo);
APPSPAWN_CHECK(msgNode != NULL, return NULL, "Failed to rebuild app message node");
int ret = DecodeAppSpawnMsg(msgNode);
if (ret != 0) {
DeleteAppSpawnMsg(&msgNode);
return NULL;
}
return msgNode;
}
static void ProcessBegetCmdMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
AppSpawnMsg *msg = &message->msgHeader;
if (!IsDeveloperModeOpen()) {
SendResponse(connection, msg, APPSPAWN_DEBUG_MODE_NOT_SUPPORT, 0);
DeleteAppSpawnMsg(&message);
return;
}
AppSpawnMsgNode *msgNode = ProcessSpawnBegetctlMsg(connection, message);
if (msgNode == NULL) {
SendResponse(connection, msg, APPSPAWN_DEBUG_MODE_NOT_SUPPORT, 0);
DeleteAppSpawnMsg(&message);
return;
}
ProcessSpawnReqMsg(connection, msgNode);
DeleteAppSpawnMsg(&message);
DeleteAppSpawnMsg(&msgNode);
}
static int ProcessSpawnRemountMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
APPSPAWN_LOGI("ProcessSpawnRemountMsg, do not handle it");
return 0;
}
static void ProcessSpawnRestartMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
APPSPAWN_LOGI("ProcessSpawnRestartMsg, do not handle it");
SendResponse(connection, &message->msgHeader, 0, 0);
}
* @brief 添加已生成的进程信息到进程管理器
*
* @param property 孵化上下文
* @param connection 连接对象
* @param message 消息节点
* @return 0 on success, error code on failure
*/
static int AddSpawnedProcessInfo(AppSpawningCtx *property, AppSpawnConnection *connection,
AppSpawnMsgNode *message)
{
if (!CheckAppMsgFlagsSet(property, APP_FLAGS_SPAWN_IMAGE_PROCESS)) {
const AppSpawnMsgAccessToken *tokenInfo = (const AppSpawnMsgAccessToken *)
GetAppSpawnMsgInfo(property->message, TLV_ACCESS_TOKEN_INFO);
uint64_t tokenid = (tokenInfo != NULL) ? tokenInfo->accessTokenIdEx : 0;
AppSpawnedProcess *appInfo = AddSpawnedProcess(property->pid, GetBundleName(property),
0, false, tokenid);
APPSPAWN_CHECK(appInfo != NULL, return APPSPAWN_SYSTEM_ERROR,
"Failed to add spawned process for worker pid=%{public}d", property->pid);
AppSpawnMsgDacInfo *dacInfo = GetAppProperty(property, TLV_DAC_INFO);
appInfo->uid = dacInfo != NULL ? dacInfo->uid : 0;
appInfo->spmRefAdded = property->spmRefAdded;
WatchChildProcessFd(property);
ProcessMgrHookExecute(STAGE_SERVER_APP_ADD, GetAppSpawnContent(), appInfo);
}
return 0;
}
* @brief 处理 checkpoint 进程创建请求(镜像进程和工作进程)
*
* 该函数独立处理 checkpoint 进程的创建,只执行 STAGE_PARENT_BOOT_IMG hook。
* 与普通的进程创建(ProcessSpawnReqMsg)分离,避免耦合。
*
* @param connection 连接对象
* @param message 消息节点
*/
APPSPAWN_STATIC void ProcessCheckpointReqMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
APPSPAWN_CHECK((connection != NULL && message != NULL), return, "Invalid input param");
APPSPAWN_LOGI("ProcessCheckpointReqMsg: processName=%{public}s, msgType=%{public}u",
message->msgHeader.processName, message->msgHeader.msgType);
int ret = CheckAppSpawnMsg(message);
if (ret != 0) {
APPSPAWN_LOGE("Checkpoint msg check failed: %{public}d", ret);
SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
DeleteAppSpawnMsg(&message);
return;
}
AppSpawningCtx *property = CreateAppSpawningCtx();
if (property == NULL) {
APPSPAWN_LOGE("Failed to create spawning context for checkpoint");
SendResponseEx(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0, 0);
DeleteAppSpawnMsg(&message);
return;
}
property->state = APP_STATE_SPAWNING;
property->message = message;
message->connection = connection;
ret = AppSpawnHookExecute(STAGE_PARENT_MSG_DECODE, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
APPSPAWN_ONLY_EXPER(ret != 0, APPSPAWN_LOGE("rebuild hook failed: %{public}d, aborting spawn", ret);
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawningCtx(property);
return);
ret = AppSpawnHookExecute(STAGE_PARENT_BOOT_IMG, HOOK_STOP_WHEN_ERROR, GetAppSpawnContent(), &property->client);
if (ret != 0) {
APPSPAWN_LOGE("STAGE_PARENT_BOOT_IMG hook failed: %{public}d", ret);
ret = ret >= APPSPAWN_SYSTEM_ERROR ? FORK_ALL_INVALID : ret;
SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
DeleteAppSpawningCtx(property);
return;
}
ret = AddSpawnedProcessInfo(property, connection, message);
APPSPAWN_CHECK(ret == 0,
SendResponseEx(connection, &message->msgHeader, ret, 0, 0);
DeleteAppSpawningCtx(property); return,
"AddSpawnedProcessInfo failed %{public}d", ret);
APPSPAWN_LOGI("Checkpoint process created successfully: pid=%{public}d, checkPointId=%{public}" PRId64"",
property->pid, property->checkPointId);
SendResponseEx(connection, &message->msgHeader, ret, property->pid, property->checkPointId);
DeleteAppSpawningCtx(property);
}
APPSPAWN_STATIC void ProcessUninstallDebugHap(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
APPSPAWN_LOGI("ProcessUninstallDebugHap start");
AppSpawningCtx *property = CreateAppSpawningCtx();
if (property == NULL) {
SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
DeleteAppSpawnMsg(&message);
return;
}
property->message = message;
property->message->connection = connection;
int ret = AppSpawnHookExecute(STAGE_PARENT_UNINSTALL, 0, GetAppSpawnContent(), &property->client);
SendResponse(connection, &message->msgHeader, ret, 0);
DeleteAppSpawningCtx(property);
}
APPSPAWN_STATIC int AppspawpnDevicedebugKill(int pid, cJSON *args)
{
cJSON *signal = cJSON_GetObjectItem(args, "signal");
APPSPAWN_CHECK(cJSON_IsNumber(signal), return -1, "appspawn devicedebug json get signal fail");
AppSpawnedProcess *appInfo = GetSpawnedProcess(pid);
APPSPAWN_CHECK(appInfo != NULL, return APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_EXIST,
"appspawn devicedebug get app info unsuccess, pid=%{public}d", pid);
APPSPAWN_CHECK(appInfo->isDebuggable, return APPSPAWN_DEVICEDEBUG_ERROR_APP_NOT_DEBUGGABLE,
"appspawn devicedebug process is not debuggable, pid=%{public}d", pid);
APPSPAWN_LOGI("appspawn devicedebug debugable=%{public}d, pid=%{public}d, signal=%{public}d",
appInfo->isDebuggable, pid, signal->valueint);
if (kill(pid, signal->valueint) != 0) {
APPSPAWN_LOGE("appspawn devicedebug unable to kill process, pid: %{public}d ret %{public}d", pid, errno);
return -1;
}
return 0;
}
APPSPAWN_STATIC int AppspawnDevicedebugDeal(const char* op, int pid, cJSON *args)
{
if (strcmp(op, "kill") == 0) {
return AppspawpnDevicedebugKill(pid, args);
}
APPSPAWN_LOGE("appspawn devicedebug op:%{public}s invalid", op);
return -1;
}
APPSPAWN_STATIC int ProcessAppSpawnDeviceDebugMsg(AppSpawnMsgNode *message)
{
APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return -1);
uint32_t len = 0;
const char* jsonString = (char *)GetAppSpawnMsgExtInfo(message, "devicedebug", &len);
if (jsonString == NULL || len == 0) {
APPSPAWN_LOGE("appspawn devicedebug get devicedebug fail");
return -1;
}
cJSON *json = cJSON_Parse(jsonString);
APPSPAWN_CHECK(json != NULL, return -1, "appspawn devicedebug json parse fail");
cJSON *app = cJSON_GetObjectItem(json, "app");
APPSPAWN_CHECK(cJSON_IsNumber(app), cJSON_Delete(json);
return -1, "appspawn devicedebug json get app fail");
cJSON *op = cJSON_GetObjectItem(json, "op");
APPSPAWN_CHECK(cJSON_IsString(op) && op->valuestring != NULL, cJSON_Delete(json);
return -1, "appspawn devicedebug json get op fail");
cJSON *args = cJSON_GetObjectItem(json, "args");
APPSPAWN_CHECK(cJSON_IsObject(args), cJSON_Delete(json);
return -1, "appspawn devicedebug json get args fail");
int result = AppspawnDevicedebugDeal(op->valuestring, app->valueint, args);
cJSON_Delete(json);
return result;
}
APPSPAWN_STATIC bool ProcessAppSpawnLockStatusMsg(AppSpawnConnection *connection,
AppSpawnMsgNode *message, int *result)
{
APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return false);
uint32_t len = 0;
char *lockstatus = (char *)GetAppSpawnMsgExtInfo(message, "lockstatus", &len);
APPSPAWN_CHECK(lockstatus != NULL, return false, "failed to get lockstatus");
APPSPAWN_LOGI("appspawn get lockstatus %{public}s from storage_manager", lockstatus);
char *userLockStatus = NULL;
char *userIdStr = strtok_r(lockstatus, ":", &userLockStatus);
APPSPAWN_CHECK(userIdStr != NULL && userLockStatus != NULL, return false,
"lockstatus not satisfied format, failed to get userLockStatus");
int userId = atoi(userIdStr);
if (userId < USER_ID_MIN_VALUE || userId > USER_ID_MAX_VALUE) {
APPSPAWN_LOGE("userId err %{public}s", userIdStr);
return false;
}
if (strcmp(userLockStatus, "0") != 0 && strcmp(userLockStatus, "1") != 0) {
APPSPAWN_LOGE("userLockStatus err %{public}s", userLockStatus);
return false;
}
char lockStatusParam[LOCK_STATUS_PARAM_SIZE] = {0};
int ret = snprintf_s(lockStatusParam, sizeof(lockStatusParam), sizeof(lockStatusParam) - 1,
"startup.appspawn.lockstatus_%d", userId);
APPSPAWN_CHECK(ret > 0, return false, "get lock status param failed, errno %{public}d", errno);
ret = SetParameter(lockStatusParam, userLockStatus);
APPSPAWN_CHECK(ret == 0, return false, "failed to set lockstatus param value ret %{public}d", ret);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(strcmp(userLockStatus, "0") == 0 ? UNLOCK_SUCCESS : LOCK_SUCCESS);
#endif
if (strcmp(userLockStatus, "0") == 0) {
bool async = HandleUnlockEvent(GetAppSpawnContent(), userId, message, connection);
APPSPAWN_ONLY_EXPER(async,
return true);
int mountResult = DoUnlockMountSerial(GetAppSpawnContent(), userId);
*result = mountResult;
APPSPAWN_LOGI("L3 serial mount done: uid=%{public}d result=%{public}d", userId, mountResult);
return false;
}
return false;
}
APPSPAWN_STATIC int AppSpawnReqMsgFdGet(AppSpawnConnection *connection, AppSpawnMsgNode *message,
const char *fdName, int *fd)
{
APPSPAWN_CHECK_ONLY_EXPER(message != NULL && message->buffer != NULL && connection != NULL, return -1);
APPSPAWN_CHECK_ONLY_EXPER(message->tlvOffset != NULL, return -1);
int findFdIndex = 0;
AppSpawnMsgReceiverCtx recvCtx = connection->receiverCtx;
APPSPAWN_CHECK(recvCtx.fds != NULL && recvCtx.fdCount > 0, return 0,
"no need get fd info %{public}d, %{public}d", recvCtx.fds != NULL, recvCtx.fdCount);
for (uint32_t index = TLV_MAX; index < (TLV_MAX + message->tlvCount); index++) {
if (message->tlvOffset[index] == INVALID_OFFSET) {
return APPSPAWN_SYSTEM_ERROR;
}
uint8_t *data = message->buffer + message->tlvOffset[index];
if (((AppSpawnTlv *)data)->tlvType != TLV_MAX) {
continue;
}
AppSpawnTlvExt *tlv = (AppSpawnTlvExt *)data;
if (strcmp(tlv->tlvName, MSG_EXT_NAME_APP_FD) != 0) {
continue;
}
APPSPAWN_CHECK(findFdIndex < recvCtx.fdCount && recvCtx.fds[findFdIndex] > 0, return -1,
"check get fd args failed %{public}d, %{public}d, %{public}d",
findFdIndex, recvCtx.fdCount, recvCtx.fds[findFdIndex]);
if (strcmp((const char *)(data + sizeof(AppSpawnTlvExt)), fdName) == 0 && recvCtx.fds[findFdIndex] > 0) {
*fd = recvCtx.fds[findFdIndex];
APPSPAWN_LOGI("Spawn Listen fd %{public}s get success %{public}d", fdName, recvCtx.fds[findFdIndex]);
break;
}
findFdIndex++;
if (findFdIndex >= recvCtx.fdCount) {
break;
}
}
return 0;
}
APPSPAWN_STATIC void ProcessObserveProcessSignalMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
APPSPAWN_CHECK_ONLY_EXPER(message != NULL, return);
int fd = 0;
int ret = AppSpawnReqMsgFdGet(connection, message, SPAWN_LISTEN_FD_NAME, &fd);
if (fd <= 0 || ret != 0) {
APPSPAWN_LOGE("Spawn Listen appspawn signal fd get unsuccess");
SendResponse(connection, &message->msgHeader, APPSPAWN_SYSTEM_ERROR, 0);
DeleteAppSpawnMsg(&message);
return;
}
AppSpawnContent *content = GetAppSpawnContent();
APPSPAWN_CHECK(content != NULL, return, "Spawn Listen appspawn content is null");
APPSPAWN_ONLY_EXPER(content->signalFd > 0, close(content->signalFd));
content->signalFd = fd;
connection->receiverCtx.fdCount = 0;
SendResponse(connection, &message->msgHeader, 0, 0);
DeleteAppSpawnMsg(&message);
}
static void ProcessRecvMsg(AppSpawnConnection *connection, AppSpawnMsgNode *message)
{
AppSpawnMsg *msg = &message->msgHeader;
APPSPAWN_DUMPI("Recv msgHeader magic:0x%{public}x type:%{public}u id:%{public}u len:%{public}u %{public}s",
msg->magic, msg->msgType, msg->msgId, msg->msgLen, msg->processName);
APPSPAWN_CHECK_ONLY_LOG(connection->receiverCtx.nextMsgId == msg->msgId,
"Invalid msg id %{public}u %{public}u", connection->receiverCtx.nextMsgId, msg->msgId);
connection->receiverCtx.nextMsgId++;
int ret;
switch (msg->msgType) {
case MSG_GET_RENDER_TERMINATION_STATUS: {
AppSpawnResult result = {0};
ret = ProcessTerminationStatusMsg(message, &result);
SendResponse(connection, msg, ret == 0 ? result.result : ret, result.pid);
DeleteAppSpawnMsg(&message);
break;
}
case MSG_SPAWN_NATIVE_PROCESS:
case MSG_APP_SPAWN: {
ProcessSpawnReqMsg(connection, message);
break;
}
case MSG_DUMP:
ProcessAppSpawnDumpMsg(message);
SendResponse(connection, msg, 0, 0);
DeleteAppSpawnMsg(&message);
break;
case MSG_BEGET_CMD: {
ProcessBegetCmdMsg(connection, message);
break;
}
case MSG_BEGET_SPAWNTIME:
SendResponse(connection, msg, GetAppSpawnMgr()->spawnTime.minAppspawnTime,
GetAppSpawnMgr()->spawnTime.maxAppspawnTime);
DeleteAppSpawnMsg(&message);
break;
case MSG_UPDATE_MOUNT_POINTS:
ret = ProcessSpawnRemountMsg(connection, message);
SendResponse(connection, msg, ret, 0);
break;
case MSG_RESTART_SPAWNER:
ProcessSpawnRestartMsg(connection, message);
break;
case MSG_DEVICE_DEBUG:
ret = ProcessAppSpawnDeviceDebugMsg(message);
SendResponse(connection, msg, ret, 0);
DeleteAppSpawnMsg(&message);
break;
case MSG_UNINSTALL_DEBUG_HAP:
ProcessUninstallDebugHap(connection, message);
break;
case MSG_LOCK_STATUS: {
APPSPAWN_LOGI("ProcessRecvMsg MSG_LOCK_STATUS: connId=%{public}u",
connection ? connection->connectionId : 0);
int result = 0;
bool async = ProcessAppSpawnLockStatusMsg(connection, message, &result);
APPSPAWN_LOGI("ProcessRecvMsg MSG_LOCK_STATUS: async=%{public}d result=%{public}d",
async, result);
APPSPAWN_ONLY_EXPER(!async,
SendResponse(connection, msg, result, 0);
DeleteAppSpawnMsg(&message));
break;
}
case MSG_OBSERVE_PROCESS_SIGNAL_STATUS:
ProcessObserveProcessSignalMsg(connection, message);
break;
case MSG_UNLOAD_WEBLIB_IN_APPSPAWN:
ret = ServerStageHookExecute(STAGE_SERVER_ARKWEB_UNLOAD, GetAppSpawnContent());
SendResponse(connection, msg, ret, 0);
DeleteAppSpawnMsg(&message);
break;
case MSG_LOAD_WEBLIB_IN_APPSPAWN:
(void)ServerStageHookExecute(STAGE_SERVER_ARKWEB_PRELOAD, GetAppSpawnContent());
SendResponse(connection, msg, 0, 0);
DeleteAppSpawnMsg(&message);
break;
case MSG_SPAWN_IMAGE_PROCESS:
case MSG_SPAWN_WORKER_PROCESS: {
ProcessCheckpointReqMsg(connection, message);
break;
}
default:
SendResponse(connection, msg, APPSPAWN_MSG_INVALID, 0);
DeleteAppSpawnMsg(&message);
break;
}
}
static void ReforkPreforkIfNeeded(AppSpawnContent *content)
{
if (!content->enablePerfork) {
return;
}
AppSpawningCtx *newProperty = CreateAppSpawningCtx();
if (newProperty != NULL) {
ProcessPreFork(content, newProperty);
DeleteAppSpawningCtx(newProperty);
}
}
static int TryLevel1PreforkUnlock(AppSpawnContent *content, int uid, AppSpawningCtx *property)
{
APPSPAWN_ONLY_EXPER(content->reservedPid <= 0, return APPSPAWN_SYSTEM_ERROR);
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
APPSPAWN_LOGI("TryLevel1PreforkUnlock: uid=%{public}d reservedPid=%{public}d",
uid, content->reservedPid);
do {
AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
APPSPAWN_CHECK(pcFds != NULL && pcFds->fds[1] >= 0 && SendUnlockMsgToPrefork(content, uid) == 0, break,
"L1 failed: send unlock msg to prefork failed");
AppSpawnFds *cpFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
APPSPAWN_CHECK(cpFds != NULL && cpFds->fds[0] >= 0, break,
"L1 failed: childToParentFd not found");
property->forkCtx.fd[0] = cpFds->fds[0];
cpFds->fds[0] = -1;
property->forkCtx.fd[1] = -1;
property->pid = content->reservedPid;
APPSPAWN_LOGI("L1 fd transfer done: fd[0]=%{public}d fd[1]=%{public}d pid=%{public}d",
property->forkCtx.fd[0], property->forkCtx.fd[1], property->pid);
APPSPAWN_CHECK(AddUnlockChildWatcher(property, UNLOCK_MOUNT_TIMEOUT_MS) == 0, break,
"L1 AddUnlockChildWatcher failed");
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(UNLOCK_MOUNT_L1_SUCCESS);
#endif
UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
content->reservedPid = 0;
return 0;
} while (0);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(UNLOCK_MOUNT_L1_FAIL);
#endif
ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
property->pid = 0;
UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
UnregisterSpawningFdsByPid(mgr, content->reservedPid, TYPE_CHILD_PARENT);
pid_t oldPid = content->reservedPid;
content->reservedPid = 0;
kill(oldPid, SIGKILL);
return APPSPAWN_SYSTEM_ERROR;
}
APPSPAWN_STATIC bool HandleUnlockEvent(AppSpawnContent *content, int uid, AppSpawnMsgNode *message,
AppSpawnConnection *connection)
{
APPSPAWN_CHECK(content != NULL, return false, "HandleUnlockEvent: Invalid content");
APPSPAWN_CHECK(uid > 0, return false, "HandleUnlockEvent: Invalid uid");
APPSPAWN_CHECK(message != NULL, return false, "HandleUnlockEvent: Invalid message");
APPSPAWN_CHECK(connection != NULL, return false, "HandleUnlockEvent: Invalid connection");
APPSPAWN_LOGI("HandleUnlockEvent start: uid=%{public}d reservedPid=%{public}d",
uid, content->reservedPid);
AppSpawningCtx *property = CreateAppSpawningCtx();
APPSPAWN_CHECK(property != NULL, return false, "Failed to create AppSpawningCtx");
property->message = message;
message->connection = connection;
bool async = false;
do {
APPSPAWN_ONLY_EXPER(TryLevel1PreforkUnlock(content, uid, property) == 0,
APPSPAWN_LOGI("HandleUnlockEvent L1 async started: pid=%{public}d", property->pid);
async = true;
break);
APPSPAWN_ONLY_EXPER(ForkAndDoUnlockMount(content, uid, property) == 0, async = true;
break);
APPSPAWN_LOGI("HandleUnlockEvent L1/L2 both failed, uid=%{public}d", uid);
property->message = NULL;
DeleteAppSpawningCtx(property);
} while (0);
ReforkPreforkIfNeeded(content);
return async;
}
APPSPAWN_STATIC int SendUnlockMsgToPrefork(AppSpawnContent *content, int uid)
{
APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID,
"SendUnlockMsgToPrefork: Invalid content");
AppSpawnMgr *mgr = (AppSpawnMgr *)content;
AppSpawnFds *pcFds = FindSpawningFdsByPid(mgr, content->reservedPid, TYPE_PARENT_CHILD);
APPSPAWN_CHECK(pcFds != NULL, return APPSPAWN_ARG_INVALID,
"parent child fds not found");
AppSpawnPipeMsg msg = {0};
msg.type = MSG_LOCK_STATUS;
msg.msg.unlockMsg.uid = uid;
ssize_t writeSize = write(pcFds->fds[1], &msg, sizeof(msg));
if (writeSize != sizeof(msg)) {
APPSPAWN_LOGE("SendUnlockMsgToPrefork write failed, ret=%{public}zd errno=%{public}d",
writeSize, errno);
return APPSPAWN_PIPE_ERROR;
}
APPSPAWN_LOGI("SendUnlockMsgToPrefork success: uid=%{public}d fd=%{public}d",
uid, pcFds->fds[1]);
return 0;
}
APPSPAWN_STATIC int ForkAndDoUnlockMount(AppSpawnContent *content, int uid, AppSpawningCtx *property)
{
APPSPAWN_CHECK(content != NULL, return APPSPAWN_ARG_INVALID, "ForkAndDoUnlockMount: Invalid content");
APPSPAWN_CHECK(property != NULL, return APPSPAWN_ARG_INVALID, "ForkAndDoUnlockMount: Invalid property");
APPSPAWN_CHECK(pipe(property->forkCtx.fd) == 0, return APPSPAWN_SYSTEM_ERROR,
"ForkAndDoUnlockMount: pipe failed, errno=%{public}d", errno);
APPSPAWN_LOGI("ForkAndDoUnlockMount: pipe created fd[0]=%{public}d fd[1]=%{public}d",
property->forkCtx.fd[0], property->forkCtx.fd[1]);
pid_t pid = fork();
if (pid < 0) {
APPSPAWN_LOGE("Fork for unlock failed, errno=%{public}d", errno);
ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
return APPSPAWN_SYSTEM_ERROR;
}
if (pid == 0) {
HilogCloseSocketFd();
int result = ProcessUnlockMessage(uid);
SetUnlockMountResult(uid, result);
APPSPAWN_LOGI("L2 child write result: uid=%{public}d result=%{public}d fd=%{public}d",
uid, result, property->forkCtx.fd[1]);
ssize_t writeSize = write(property->forkCtx.fd[1], &result, sizeof(result));
APPSPAWN_CHECK_ONLY_LOG(writeSize == sizeof(result),
"L2 write result failed %{public}zd %{public}d", writeSize, errno);
close(property->forkCtx.fd[1]);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(result == 0 ? UNLOCK_SUCCESS : "UNLOCK_MOUNT_FAIL");
#endif
ProcessExit(0);
}
close(property->forkCtx.fd[1]);
property->forkCtx.fd[1] = -1;
property->pid = pid;
APPSPAWN_LOGI("ForkAndDoUnlockMount parent: pid=%{public}d fd[0]=%{public}d",
pid, property->forkCtx.fd[0]);
APPSPAWN_CHECK(AddUnlockChildWatcher(property, UNLOCK_MOUNT_TIMEOUT_MS) == 0,
kill(pid, SIGKILL);
ClearPipeFd(property->forkCtx.fd, PIPE_FD_LENGTH);
property->pid = 0;
return APPSPAWN_SYSTEM_ERROR,
"L2 AddUnlockChildWatcher failed");
APPSPAWN_LOGI("L2 async started: pid=%{public}d fd[0]=%{public}d", pid, property->forkCtx.fd[0]);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(UNLOCK_MOUNT_L2_SUCCESS);
#endif
return 0;
}
APPSPAWN_STATIC int DoUnlockMountSerial(AppSpawnContent *content, int uid)
{
APPSPAWN_LOGI("DoUnlockMountSerial: uid=%{public}d", uid);
int result = ProcessUnlockMessage(uid);
SetUnlockMountResult(uid, result);
APPSPAWN_LOGI("DoUnlockMountSerial done: uid=%{public}d result=%{public}d", uid, result);
#ifdef APPSPAWN_HISYSEVENT
ReportKeyEvent(UNLOCK_MOUNT_L3_DONE);
#endif
return result;
}
APPSPAWN_STATIC int ProcessUnlockMessage(int uid)
{
APPSPAWN_LOGI("ProcessUnlockMessage start, uid=%{public}d", uid);
AppSpawnContent *content = GetAppSpawnContent();
if (content != NULL) {
content->currentUnlockUid = uid;
}
struct sched_param oldParam = { 0 };
int oldSchedPolicy = sched_getscheduler(0);
int ret = sched_getparam(0, &oldParam);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "sched_getparam failed ret=%{public}d", ret);
struct sched_param param = { 0 };
param.sched_priority = 1;
ret = sched_setscheduler(0, SCHED_FIFO, ¶m);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "sched_setscheduler failed ret=%{public}d", ret);
StartAppspawnTrace("ProcessUnlockHook");
int result = ServerStageHookExecute(STAGE_SERVER_LOCK, GetAppSpawnContent());
FinishAppspawnTrace();
ret = sched_setscheduler(0, oldSchedPolicy, &oldParam);
APPSPAWN_CHECK_ONLY_LOG(ret == 0, "restore scheduler failed ret=%{public}d", ret);
APPSPAWN_LOGI("ProcessUnlockMessage done, uid=%{public}d, result=%{public}d", uid, result);
return result;
}