1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <fcntl.h>
17#include <limits.h>
18#include <sched.h>
19#include <stdlib.h>
20#include <unistd.h>
21
22#include <sys/ipc.h>
23#include <sys/mman.h>
24#include <sys/mount.h>
25#include <sys/signalfd.h>
26#include <sys/socket.h>
27#include <sys/stat.h>
28#include <sys/wait.h>
29
30#include "appspawn_adapter.h"
31#include "appspawn_hook.h"
32#include "appspawn_msg.h"
33#include "appspawn_manager.h"
34#include "securec.h"
35
36#define SLEEP_DURATION 3000 // us
37#define EXIT_APP_TIMEOUT 1000000 // us
38
39static AppSpawnMgr *g_appSpawnMgr = NULL;
40
41AppSpawnMgr *CreateAppSpawnMgr(int mode)
42{
43    APPSPAWN_CHECK_ONLY_EXPER(mode < MODE_INVALID, return NULL);
44    if (g_appSpawnMgr != NULL) {
45        return g_appSpawnMgr;
46    }
47    AppSpawnMgr *appMgr = (AppSpawnMgr *)calloc(1, sizeof(AppSpawnMgr));
48    APPSPAWN_CHECK(appMgr != NULL, return NULL, "Failed to alloc memory for appspawn");
49    appMgr->content.longProcName = NULL;
50    appMgr->content.longProcNameLen = 0;
51    appMgr->content.mode = mode;
52    appMgr->content.sandboxNsFlags = 0;
53    appMgr->content.wdgOpened = 0;
54    appMgr->servicePid = getpid();
55    appMgr->server = NULL;
56    appMgr->sigHandler = NULL;
57    OH_ListInit(&appMgr->appQueue);
58    OH_ListInit(&appMgr->diedQueue);
59    OH_ListInit(&appMgr->appSpawnQueue);
60    appMgr->diedAppCount = 0;
61    OH_ListInit(&appMgr->extData);
62    g_appSpawnMgr = appMgr;
63    g_appSpawnMgr->spawnTime.minAppspawnTime = APPSPAWN_MAX_TIME;
64    g_appSpawnMgr->spawnTime.maxAppspawnTime = 0;
65    return appMgr;
66}
67
68AppSpawnMgr *GetAppSpawnMgr(void)
69{
70    return g_appSpawnMgr;
71}
72
73AppSpawnContent *GetAppSpawnContent(void)
74{
75    return g_appSpawnMgr == NULL ? NULL : &g_appSpawnMgr->content;
76}
77
78static void SpawningQueueDestroy(ListNode *node)
79{
80    AppSpawningCtx *property = ListEntry(node, AppSpawningCtx, node);
81    DeleteAppSpawningCtx(property);
82}
83
84static void ExtDataDestroy(ListNode *node)
85{
86    AppSpawnExtData *extData = ListEntry(node, AppSpawnExtData, node);
87    AppSpawnExtDataFree freeNode = extData->freeNode;
88    if (freeNode) {
89        freeNode(extData);
90    }
91}
92
93void DeleteAppSpawnMgr(AppSpawnMgr *mgr)
94{
95    APPSPAWN_CHECK_ONLY_EXPER(mgr != NULL, return);
96    OH_ListRemoveAll(&mgr->appQueue, NULL);
97    OH_ListRemoveAll(&mgr->diedQueue, NULL);
98    OH_ListRemoveAll(&mgr->appSpawnQueue, SpawningQueueDestroy);
99    OH_ListRemoveAll(&mgr->extData, ExtDataDestroy);
100
101    APPSPAWN_LOGV("DeleteAppSpawnMgr %{public}d %{public}d", mgr->servicePid, getpid());
102    free(mgr);
103    if (g_appSpawnMgr == mgr) {
104        g_appSpawnMgr = NULL;
105    }
106}
107
108void TraversalSpawnedProcess(AppTraversal traversal, void *data)
109{
110    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL && traversal != NULL, return);
111    ListNode *node = g_appSpawnMgr->appQueue.next;
112    while (node != &g_appSpawnMgr->appQueue) {
113        ListNode *next = node->next;
114        AppSpawnedProcess *appInfo = ListEntry(node, AppSpawnedProcess, node);
115        traversal(g_appSpawnMgr, appInfo, data);
116        node = next;
117    }
118}
119
120static int AppInfoPidComparePro(ListNode *node, void *data)
121{
122    AppSpawnedProcess *node1 = ListEntry(node, AppSpawnedProcess, node);
123    pid_t pid = *(pid_t *)data;
124    return node1->pid - pid;
125}
126
127static int AppInfoNameComparePro(ListNode *node, void *data)
128{
129    AppSpawnedProcess *node1 = ListEntry(node, AppSpawnedProcess, node);
130    return strcmp(node1->name, (char *)data);
131}
132
133static int AppInfoCompareProc(ListNode *node, ListNode *newNode)
134{
135    AppSpawnedProcess *node1 = ListEntry(node, AppSpawnedProcess, node);
136    AppSpawnedProcess *node2 = ListEntry(newNode, AppSpawnedProcess, node);
137    return node1->pid - node2->pid;
138}
139
140AppSpawnedProcess *AddSpawnedProcess(pid_t pid, const char *processName, bool isDebuggable)
141{
142    APPSPAWN_CHECK(g_appSpawnMgr != NULL && processName != NULL, return NULL, "Invalid mgr or process name");
143    APPSPAWN_CHECK(pid > 0, return NULL, "Invalid pid for %{public}s", processName);
144    size_t len = strlen(processName) + 1;
145    APPSPAWN_CHECK(len > 1, return NULL, "Invalid processName for %{public}s", processName);
146    AppSpawnedProcess *node = (AppSpawnedProcess *)calloc(1, sizeof(AppSpawnedProcess) + len + 1);
147    APPSPAWN_CHECK(node != NULL, return NULL, "Failed to malloc for appinfo");
148
149    node->pid = pid;
150    node->max = 0;
151    node->uid = 0;
152    node->exitStatus = 0;
153    node->isDebuggable = isDebuggable;
154    int ret = strcpy_s(node->name, len, processName);
155    APPSPAWN_CHECK(ret == 0, free(node);
156        return NULL, "Failed to strcpy process name");
157
158    OH_ListInit(&node->node);
159    APPSPAWN_LOGI("Add %{public}s, pid=%{public}d success", processName, pid);
160    OH_ListAddWithOrder(&g_appSpawnMgr->appQueue, &node->node, AppInfoCompareProc);
161    return node;
162}
163
164void TerminateSpawnedProcess(AppSpawnedProcess *node)
165{
166    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL && node != NULL, return);
167    // delete node
168    OH_ListRemove(&node->node);
169    OH_ListInit(&node->node);
170    if (!IsNWebSpawnMode(g_appSpawnMgr)) {
171        free(node);
172        return;
173    }
174    if (g_appSpawnMgr->diedAppCount >= MAX_DIED_PROCESS_COUNT) {
175        AppSpawnedProcess *oldApp = ListEntry(g_appSpawnMgr->diedQueue.next, AppSpawnedProcess, node);
176        OH_ListRemove(&oldApp->node);
177        OH_ListInit(&oldApp->node);
178        free(oldApp);
179        g_appSpawnMgr->diedAppCount--;
180    }
181    APPSPAWN_LOGI("ProcessAppDied %{public}s, pid=%{public}d", node->name, node->pid);
182    OH_ListAddTail(&g_appSpawnMgr->diedQueue, &node->node);
183    g_appSpawnMgr->diedAppCount++;
184}
185
186AppSpawnedProcess *GetSpawnedProcess(pid_t pid)
187{
188    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL, return NULL);
189    ListNode *node = OH_ListFind(&g_appSpawnMgr->appQueue, &pid, AppInfoPidComparePro);
190    APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
191    return ListEntry(node, AppSpawnedProcess, node);
192}
193
194AppSpawnedProcess *GetSpawnedProcessByName(const char *name)
195{
196    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL, return NULL);
197    APPSPAWN_CHECK_ONLY_EXPER(name != NULL, return NULL);
198
199    ListNode *node = OH_ListFind(&g_appSpawnMgr->appQueue, (void *)name, AppInfoNameComparePro);
200    APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
201    return ListEntry(node, AppSpawnedProcess, node);
202}
203
204static void DumpProcessSpawnStack(pid_t pid)
205{
206#if (!defined(CJAPP_SPAWN) && !defined(NATIVE_SPAWN))
207    DumpSpawnStack(pid);
208    DumpSpawnStack(getpid());
209#endif
210#ifndef APPSPAWN_TEST
211    kill(pid, SIGKILL);
212#endif
213    APPSPAWN_LOGI("Dump stack finished");
214}
215
216int KillAndWaitStatus(pid_t pid, int sig, int *exitStatus)
217{
218    APPSPAWN_CHECK_ONLY_EXPER(exitStatus != NULL, return 0);
219    *exitStatus = -1;
220    if (pid <= 0) {
221        return 0;
222    }
223
224    if (kill(pid, sig) != 0) {
225        APPSPAWN_LOGE("unable to kill process, pid: %{public}d ret %{public}d", pid, errno);
226        return -1;
227    }
228    pid_t exitPid = 0;
229    int retry = 0;
230    while (retry * SLEEP_DURATION < EXIT_APP_TIMEOUT) {
231        exitPid = waitpid(pid, exitStatus, WNOHANG);
232        if (exitPid == pid) {
233            return 0;
234        }
235        usleep(SLEEP_DURATION);
236        retry++;
237    }
238
239    DumpProcessSpawnStack(pid);
240    APPSPAWN_LOGE("waitpid failed, pid: %{public}d %{public}d, status: %{public}d", exitPid, pid, *exitStatus);
241
242    return -1;
243}
244
245static int GetProcessTerminationStatus(pid_t pid)
246{
247    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL, return -1);
248    APPSPAWN_LOGV("GetProcessTerminationStatus pid: %{public}d ", pid);
249    if (pid <= 0) {
250        return 0;
251    }
252    int exitStatus = 0;
253    ListNode *node = OH_ListFind(&g_appSpawnMgr->diedQueue, &pid, AppInfoPidComparePro);
254    if (node != NULL) {
255        AppSpawnedProcess *info = ListEntry(node, AppSpawnedProcess, node);
256        exitStatus = info->exitStatus;
257        OH_ListRemove(node);
258        OH_ListInit(node);
259        free(info);
260        if (g_appSpawnMgr->diedAppCount > 0) {
261            g_appSpawnMgr->diedAppCount--;
262        }
263        return exitStatus;
264    }
265    AppSpawnedProcess *app = GetSpawnedProcess(pid);
266    if (app == NULL) {
267        APPSPAWN_LOGE("unable to get process, pid: %{public}d ", pid);
268        return -1;
269    }
270
271    if (KillAndWaitStatus(pid, SIGKILL, &exitStatus) == 0) { // kill success, delete app
272        OH_ListRemove(&app->node);
273        OH_ListInit(&app->node);
274        free(app);
275    }
276    return exitStatus;
277}
278
279AppSpawningCtx *CreateAppSpawningCtx(void)
280{
281    static uint32_t requestId = 0;
282    AppSpawningCtx *property = (AppSpawningCtx *)malloc(sizeof(AppSpawningCtx));
283    APPSPAWN_CHECK(property != NULL, return NULL, "Failed to create AppSpawningCtx ");
284    property->client.id = ++requestId;
285    property->client.flags = 0;
286    property->forkCtx.watcherHandle = NULL;
287    property->forkCtx.pidFdWatcherHandle = NULL;
288    property->forkCtx.coldRunPath = NULL;
289    property->forkCtx.timer = NULL;
290    property->forkCtx.fd[0] = -1;
291    property->forkCtx.fd[1] = -1;
292    property->isPrefork = false;
293    property->forkCtx.childMsg = NULL;
294    property->message = NULL;
295    property->pid = 0;
296    property->state = APP_STATE_IDLE;
297    OH_ListInit(&property->node);
298    if (g_appSpawnMgr) {
299        OH_ListAddTail(&g_appSpawnMgr->appSpawnQueue, &property->node);
300    }
301    return property;
302}
303
304void DeleteAppSpawningCtx(AppSpawningCtx *property)
305{
306    APPSPAWN_CHECK_ONLY_EXPER(property != NULL, return);
307    APPSPAWN_LOGV("DeleteAppSpawningCtx");
308
309    DeleteAppSpawnMsg(property->message);
310
311    OH_ListRemove(&property->node);
312    if (property->forkCtx.timer) {
313        LE_StopTimer(LE_GetDefaultLoop(), property->forkCtx.timer);
314        property->forkCtx.timer = NULL;
315    }
316    if (property->forkCtx.watcherHandle) {
317        LE_RemoveWatcher(LE_GetDefaultLoop(), property->forkCtx.watcherHandle);
318        property->forkCtx.watcherHandle = NULL;
319    }
320    if (property->forkCtx.coldRunPath) {
321        free(property->forkCtx.coldRunPath);
322        property->forkCtx.coldRunPath = NULL;
323    }
324    if (property->forkCtx.fd[0] >= 0) {
325        close(property->forkCtx.fd[0]);
326    }
327    if (property->forkCtx.fd[1] >= 0) {
328        close(property->forkCtx.fd[1]);
329    }
330
331    free(property);
332}
333
334static int AppPropertyComparePid(ListNode *node, void *data)
335{
336    AppSpawningCtx *property = ListEntry(node, AppSpawningCtx, node);
337    if (property->pid == *(pid_t *)data) {
338        return 0;
339    }
340    return 1;
341}
342
343AppSpawningCtx *GetAppSpawningCtxByPid(pid_t pid)
344{
345    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL, return NULL);
346    ListNode *node = OH_ListFind(&g_appSpawnMgr->appSpawnQueue, (void *)&pid, AppPropertyComparePid);
347    APPSPAWN_CHECK_ONLY_EXPER(node != NULL, return NULL);
348    return ListEntry(node, AppSpawningCtx, node);
349}
350
351void AppSpawningCtxTraversal(ProcessTraversal traversal, void *data)
352{
353    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL && traversal != NULL, return);
354    ListNode *node = g_appSpawnMgr->appSpawnQueue.next;
355    while (node != &g_appSpawnMgr->appSpawnQueue) {
356        ListNode *next = node->next;
357        AppSpawningCtx *ctx = ListEntry(node, AppSpawningCtx, node);
358        traversal(g_appSpawnMgr, ctx, data);
359        node = next;
360    }
361}
362
363static int DumpAppSpawnQueue(ListNode *node, void *data)
364{
365    AppSpawningCtx *property = ListEntry(node, AppSpawningCtx, node);
366    APPSPAPWN_DUMP("app property id: %{public}u flags: %{public}x",
367        property->client.id, property->client.flags);
368    APPSPAPWN_DUMP("app property state: %{public}d", property->state);
369
370    DumpAppSpawnMsg(property->message);
371    return 0;
372}
373
374static int DumpAppQueue(ListNode *node, void *data)
375{
376    AppSpawnedProcess *appInfo = ListEntry(node, AppSpawnedProcess, node);
377    uint64_t diff = DiffTime(&appInfo->spawnStart, &appInfo->spawnEnd);
378    APPSPAPWN_DUMP("App info uid: %{public}u pid: %{public}x", appInfo->uid, appInfo->pid);
379    APPSPAPWN_DUMP("App info name: %{public}s exitStatus: 0x%{public}x spawn time: %{public}" PRIu64 " us ",
380        appInfo->name, appInfo->exitStatus, diff);
381    return 0;
382}
383
384static int DumpExtData(ListNode *node, void *data)
385{
386    AppSpawnExtData *extData = ListEntry(node, AppSpawnExtData, node);
387    if (extData->dumpNode) {
388        extData->dumpNode(extData);
389    }
390    return 0;
391}
392
393void ProcessAppSpawnDumpMsg(const AppSpawnMsgNode *message)
394{
395    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL && message != NULL, return);
396    FILE *stream = NULL;
397    uint32_t len = 0;
398    char *ptyName = GetAppSpawnMsgExtInfo(message, "pty-name", &len);
399    if (ptyName != NULL) {
400        APPSPAWN_LOGI("Dump info to file '%{public}s'", ptyName);
401        char canonicalPtyPath[PATH_MAX] = {0};
402        if (realpath(ptyName, canonicalPtyPath) == NULL) {
403            return;
404        }
405        stream = fopen(canonicalPtyPath, "w");
406        SetDumpToStream(stream);
407    } else {
408        SetDumpToStream(stdout);
409    }
410    APPSPAPWN_DUMP("Dump appspawn info start ... ");
411    APPSPAPWN_DUMP("APP spawning queue: ");
412    OH_ListTraversal((ListNode *)&g_appSpawnMgr->appSpawnQueue, NULL, DumpAppSpawnQueue, 0);
413    APPSPAPWN_DUMP("APP queue: ");
414    OH_ListTraversal((ListNode *)&g_appSpawnMgr->appQueue, "App queue", DumpAppQueue, 0);
415    APPSPAPWN_DUMP("APP died queue: ");
416    OH_ListTraversal((ListNode *)&g_appSpawnMgr->diedQueue, "App died queue", DumpAppQueue, 0);
417    APPSPAPWN_DUMP("Ext data: ");
418    OH_ListTraversal((ListNode *)&g_appSpawnMgr->extData, "Ext data", DumpExtData, 0);
419    APPSPAPWN_DUMP("Dump appspawn info finish ");
420    if (stream != NULL) {
421        (void)fflush(stream);
422        fclose(stream);
423#ifdef APPSPAWN_TEST
424        SetDumpToStream(stdout);
425#else
426        SetDumpToStream(NULL);
427#endif
428    }
429}
430
431int ProcessTerminationStatusMsg(const AppSpawnMsgNode *message, AppSpawnResult *result)
432{
433    APPSPAWN_CHECK_ONLY_EXPER(g_appSpawnMgr != NULL && message != NULL, return -1);
434    APPSPAWN_CHECK_ONLY_EXPER(result != NULL, return -1);
435    if (!IsNWebSpawnMode(g_appSpawnMgr)) {
436        return APPSPAWN_MSG_INVALID;
437    }
438    result->result = -1;
439    result->pid = 0;
440    pid_t *pid = (pid_t *)GetAppSpawnMsgInfo(message, TLV_RENDER_TERMINATION_INFO);
441    if (pid == NULL) {
442        return -1;
443    }
444    // get render process termination status, only nwebspawn need this logic.
445    result->pid = *pid;
446    result->result = GetProcessTerminationStatus(*pid);
447    return 0;
448}
449