169570cc8Sopenharmony_ci/*
269570cc8Sopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
369570cc8Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
469570cc8Sopenharmony_ci * you may not use this file except in compliance with the License.
569570cc8Sopenharmony_ci * You may obtain a copy of the License at
669570cc8Sopenharmony_ci *
769570cc8Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
869570cc8Sopenharmony_ci *
969570cc8Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1069570cc8Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1169570cc8Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1269570cc8Sopenharmony_ci * See the License for the specific language governing permissions and
1369570cc8Sopenharmony_ci * limitations under the License.
1469570cc8Sopenharmony_ci */
1569570cc8Sopenharmony_ci
1669570cc8Sopenharmony_ci#include <errno.h>
1769570cc8Sopenharmony_ci#include <inttypes.h>
1869570cc8Sopenharmony_ci#include <limits.h>
1969570cc8Sopenharmony_ci#include <fcntl.h>
2069570cc8Sopenharmony_ci#include <signal.h>
2169570cc8Sopenharmony_ci#include <stdio.h>
2269570cc8Sopenharmony_ci#include <stdlib.h>
2369570cc8Sopenharmony_ci#include <string.h>
2469570cc8Sopenharmony_ci#include <unistd.h>
2569570cc8Sopenharmony_ci#include <sys/stat.h>
2669570cc8Sopenharmony_ci#include <sys/types.h>
2769570cc8Sopenharmony_ci
2869570cc8Sopenharmony_ci#include "appspawn_adapter.h"
2969570cc8Sopenharmony_ci#include "appspawn_hook.h"
3069570cc8Sopenharmony_ci#include "appspawn_manager.h"
3169570cc8Sopenharmony_ci#include "appspawn_utils.h"
3269570cc8Sopenharmony_ci#include "securec.h"
3369570cc8Sopenharmony_ci#include "cJSON.h"
3469570cc8Sopenharmony_ci#include <sys/ioctl.h>
3569570cc8Sopenharmony_ci
3669570cc8Sopenharmony_ciAPPSPAWN_STATIC int GetCgroupPath(const AppSpawnedProcessInfo *appInfo, char *buffer, uint32_t buffLen)
3769570cc8Sopenharmony_ci{
3869570cc8Sopenharmony_ci    const int userId = appInfo->uid / UID_BASE;
3969570cc8Sopenharmony_ci#ifdef APPSPAWN_TEST
4069570cc8Sopenharmony_ci    int ret = snprintf_s(buffer, buffLen, buffLen - 1, APPSPAWN_BASE_DIR "/dev/pids/testpids/%d/%s/%d/",
4169570cc8Sopenharmony_ci        userId, appInfo->name, appInfo->pid);
4269570cc8Sopenharmony_ci#else
4369570cc8Sopenharmony_ci    int ret = snprintf_s(buffer, buffLen, buffLen - 1, "/dev/pids/%d/%s/app_%d/", userId, appInfo->name, appInfo->pid);
4469570cc8Sopenharmony_ci#endif
4569570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret > 0, return ret, "Failed to snprintf_s errno: %{public}d", errno);
4669570cc8Sopenharmony_ci    APPSPAWN_LOGV("Cgroup path %{public}s ", buffer);
4769570cc8Sopenharmony_ci    return 0;
4869570cc8Sopenharmony_ci}
4969570cc8Sopenharmony_ci
5069570cc8Sopenharmony_ci
5169570cc8Sopenharmony_ciAPPSPAWN_STATIC int WriteToFile(const char *path, int truncated, pid_t pids[], uint32_t count)
5269570cc8Sopenharmony_ci{
5369570cc8Sopenharmony_ci    char pidName[32] = {0}; // 32 max len
5469570cc8Sopenharmony_ci    int fd = open(path, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
5569570cc8Sopenharmony_ci    APPSPAWN_CHECK(fd >= 0, return APPSPAWN_SYSTEM_ERROR,
5669570cc8Sopenharmony_ci        "Failed to open file errno: %{public}d path: %{public}s", errno, path);
5769570cc8Sopenharmony_ci    int ret = 0;
5869570cc8Sopenharmony_ci    for (uint32_t i = 0; i < count; i++) {
5969570cc8Sopenharmony_ci        APPSPAWN_LOGV(" WriteToFile pid %{public}d ", pids[i]);
6069570cc8Sopenharmony_ci        ret = snprintf_s(pidName, sizeof(pidName), sizeof(pidName) - 1, "%d\n", pids[i]);
6169570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
6269570cc8Sopenharmony_ci        ret = write(fd, pidName, strlen(pidName));
6369570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret > 0, break,
6469570cc8Sopenharmony_ci            "Failed to write file errno: %{public}d path: %{public}s %{public}s", errno, path, pidName);
6569570cc8Sopenharmony_ci        ret = 0;
6669570cc8Sopenharmony_ci    }
6769570cc8Sopenharmony_ci    close(fd);
6869570cc8Sopenharmony_ci    return ret;
6969570cc8Sopenharmony_ci}
7069570cc8Sopenharmony_ci
7169570cc8Sopenharmony_ci#define APP_PIDS_MAX_ENCAPS "encaps"
7269570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_COUNT_KEY "ohos.encaps.count"
7369570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_FORK_KEV "ohos.encaps.fork"
7469570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_KEY "ohos.encaps.fork.dfx"
7569570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_FORK_WEBDFX_KEY "ohos.encaps.fork.webdfx"
7669570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_COUNT_VALUE 3
7769570cc8Sopenharmony_ci#define APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE 5
7869570cc8Sopenharmony_ci#define APP_ENABLE_ENCAPS 1
7969570cc8Sopenharmony_ci#define ASSICN_ENCAPS_CMD _lOW('E', 0x1A, char *)
8069570cc8Sopenharmony_cistatic int WritePidMax(const char *path, uint32_t max)
8169570cc8Sopenharmony_ci{
8269570cc8Sopenharmony_ci#if APP_ENABLE_ENCAPS == 0
8369570cc8Sopenharmony_ci    cJSON *encaps = cJSON_CreateObject();
8469570cc8Sopenharmony_ci    cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_COUNT_KEY,
8569570cc8Sopenharmony_ci        APP_PIDS_MAX_OHOS_ENCAPS_COUNT_VALUE);
8669570cc8Sopenharmony_ci    cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_KEV, max);
8769570cc8Sopenharmony_ci    cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_KEY,
8869570cc8Sopenharmony_ci        APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE);
8969570cc8Sopenharmony_ci    cJSON_AddNumberToObject(encaps, APP_PIDS_MAX_OHOS_ENCAPS_FORK_WEBDFX_KEY,
9069570cc8Sopenharmony_ci        APP_PIDS_MAX_OHOS_ENCAPS_FORK_DFX_AND_WEBDFX_VALUE);
9169570cc8Sopenharmony_ci    cJSON *addGinseng = cJSON_CreateObject();
9269570cc8Sopenharmony_ci    cJSON_AddItemToObject(addGinseng, APP_PIDS_MAX_ENCAPS, encaps);
9369570cc8Sopenharmony_ci    char *maxPid = cJSON_PrintUnformatted(addGinseng);
9469570cc8Sopenharmony_ci    int ret = 0;
9569570cc8Sopenharmony_ci    int fd = 0;
9669570cc8Sopenharmony_ci    fd = open("dev/encaps", O_RDWR);
9769570cc8Sopenharmony_ci    ret = ioctl(fd, ASSICN_ENCAPS_CMD, maxPid);
9869570cc8Sopenharmony_ci    close(fd);
9969570cc8Sopenharmony_ci    free(maxPid);
10069570cc8Sopenharmony_ci    cJSON_Delete(addGinseng);
10169570cc8Sopenharmony_ci    cJSON_Delete(encaps);
10269570cc8Sopenharmony_ci    return ret;
10369570cc8Sopenharmony_ci#else
10469570cc8Sopenharmony_ci    char value[32] = {0}; // 32 max len
10569570cc8Sopenharmony_ci    int fd = open(path, O_RDWR | O_TRUNC);
10669570cc8Sopenharmony_ci    APPSPAWN_CHECK(fd >= 0, return -1,
10769570cc8Sopenharmony_ci        "Failed to open file errno: %{public}d path: %{public}s", errno, path);
10869570cc8Sopenharmony_ci    int ret = 0;
10969570cc8Sopenharmony_ci    do {
11069570cc8Sopenharmony_ci        ret = snprintf_s(value, sizeof(value), sizeof(value) - 1, "%u\n", max);
11169570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret > 0, break, "Failed to snprintf_s errno: %{public}d", errno);
11269570cc8Sopenharmony_ci        ret = write(fd, value, strlen(value));
11369570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret > 0, break,
11469570cc8Sopenharmony_ci            "Failed to write file errno: %{public}d path: %{public}s %{public}s %{public}d", errno, path, value, ret);
11569570cc8Sopenharmony_ci        ret = 0;
11669570cc8Sopenharmony_ci    } while (0);
11769570cc8Sopenharmony_ci    close(fd);
11869570cc8Sopenharmony_ci    return ret;
11969570cc8Sopenharmony_ci#endif
12069570cc8Sopenharmony_ci}
12169570cc8Sopenharmony_ci
12269570cc8Sopenharmony_cistatic void SetForkDenied(const AppSpawnedProcessInfo *appInfo)
12369570cc8Sopenharmony_ci{
12469570cc8Sopenharmony_ci    char pathForkDenied[PATH_MAX] = {};
12569570cc8Sopenharmony_ci    int ret = GetCgroupPath(appInfo, pathForkDenied, sizeof(pathForkDenied));
12669570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return, "Failed to get cgroup path errno: %{public}d", errno);
12769570cc8Sopenharmony_ci    ret = strcat_s(pathForkDenied, sizeof(pathForkDenied), "pids.fork_denied");
12869570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return, "Failed to strcat_s fork_denied path errno: %{public}d", errno);
12969570cc8Sopenharmony_ci    int fd = open(pathForkDenied, O_RDWR);
13069570cc8Sopenharmony_ci    if (fd < 0) {
13169570cc8Sopenharmony_ci        APPSPAWN_LOGW("SetForkDenied %{public}d open failed ", appInfo->pid);
13269570cc8Sopenharmony_ci        return;
13369570cc8Sopenharmony_ci    }
13469570cc8Sopenharmony_ci    do {
13569570cc8Sopenharmony_ci        ret = write(fd, "1", 1);
13669570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret >= 0, break,
13769570cc8Sopenharmony_ci        "Failed to write file errno: %{public}d path: %{public}s %{public}d", errno, pathForkDenied, ret);
13869570cc8Sopenharmony_ci        fsync(fd);
13969570cc8Sopenharmony_ci        APPSPAWN_LOGI("SetForkDenied success, cgroup's owner:%{public}d", appInfo->pid);
14069570cc8Sopenharmony_ci    } while (0);
14169570cc8Sopenharmony_ci    close(fd);
14269570cc8Sopenharmony_ci}
14369570cc8Sopenharmony_ci
14469570cc8Sopenharmony_cistatic void KillProcessesByCGroup(const char *path, AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
14569570cc8Sopenharmony_ci{
14669570cc8Sopenharmony_ci    SetForkDenied(appInfo);
14769570cc8Sopenharmony_ci    FILE *file = fopen(path, "r");
14869570cc8Sopenharmony_ci    APPSPAWN_CHECK(file != NULL, return, "Open file fail %{public}s errno: %{public}d", path, errno);
14969570cc8Sopenharmony_ci    pid_t pid = 0;
15069570cc8Sopenharmony_ci    while (fscanf_s(file, "%d\n", &pid) == 1 && pid > 0) {
15169570cc8Sopenharmony_ci        APPSPAWN_LOGV(" KillProcessesByCGroup pid %{public}d ", pid);
15269570cc8Sopenharmony_ci        if (pid == appInfo->pid) {
15369570cc8Sopenharmony_ci            continue;
15469570cc8Sopenharmony_ci        }
15569570cc8Sopenharmony_ci        AppSpawnedProcessInfo *tmp = GetSpawnedProcess(pid);
15669570cc8Sopenharmony_ci        if (tmp != NULL) {
15769570cc8Sopenharmony_ci            APPSPAWN_LOGI("Got app %{public}s in same group for pid %{public}d.", tmp->name, pid);
15869570cc8Sopenharmony_ci            continue;
15969570cc8Sopenharmony_ci        }
16069570cc8Sopenharmony_ci        APPSPAWN_LOGI("Kill app pid %{public}d now ...", pid);
16169570cc8Sopenharmony_ci#ifndef APPSPAWN_TEST
16269570cc8Sopenharmony_ci        if (kill(pid, SIGKILL) != 0) {
16369570cc8Sopenharmony_ci            APPSPAWN_LOGE("unable to kill process, pid: %{public}d ret %{public}d", pid, errno);
16469570cc8Sopenharmony_ci        }
16569570cc8Sopenharmony_ci#endif
16669570cc8Sopenharmony_ci    }
16769570cc8Sopenharmony_ci    (void)fclose(file);
16869570cc8Sopenharmony_ci}
16969570cc8Sopenharmony_ci
17069570cc8Sopenharmony_ciAPPSPAWN_STATIC int ProcessMgrRemoveApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
17169570cc8Sopenharmony_ci{
17269570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
17369570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
17469570cc8Sopenharmony_ci    if (IsNWebSpawnMode(content) || strcmp(appInfo->name, NWEBSPAWN_SERVER_NAME) == 0) {
17569570cc8Sopenharmony_ci        return 0;
17669570cc8Sopenharmony_ci    }
17769570cc8Sopenharmony_ci    char cgroupPath[PATH_MAX] = {};
17869570cc8Sopenharmony_ci    APPSPAWN_LOGV("ProcessMgrRemoveApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
17969570cc8Sopenharmony_ci    int ret = GetCgroupPath(appInfo, cgroupPath, sizeof(cgroupPath));
18069570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %{public}d", errno);
18169570cc8Sopenharmony_ci    char procPath[PATH_MAX] = {};
18269570cc8Sopenharmony_ci    ret = memcpy_s(procPath, sizeof(procPath), cgroupPath, sizeof(cgroupPath));
18369570cc8Sopenharmony_ci    if (ret != 0) {
18469570cc8Sopenharmony_ci        return APPSPAWN_ERROR_UTILS_MEM_FAIL;
18569570cc8Sopenharmony_ci    }
18669570cc8Sopenharmony_ci    ret = strcat_s(procPath, sizeof(procPath), "cgroup.procs");
18769570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
18869570cc8Sopenharmony_ci    KillProcessesByCGroup(procPath, (AppSpawnMgr *)content, appInfo);
18969570cc8Sopenharmony_ci    ret = rmdir(cgroupPath);
19069570cc8Sopenharmony_ci    if (ret != 0) {
19169570cc8Sopenharmony_ci        return APPSPAWN_ERROR_FILE_RMDIR_FAIL;
19269570cc8Sopenharmony_ci    }
19369570cc8Sopenharmony_ci    return ret;
19469570cc8Sopenharmony_ci}
19569570cc8Sopenharmony_ci
19669570cc8Sopenharmony_ciAPPSPAWN_STATIC int ProcessMgrAddApp(const AppSpawnMgr *content, const AppSpawnedProcessInfo *appInfo)
19769570cc8Sopenharmony_ci{
19869570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(content != NULL, return -1);
19969570cc8Sopenharmony_ci    APPSPAWN_CHECK_ONLY_EXPER(appInfo != NULL, return -1);
20069570cc8Sopenharmony_ci    if (IsNWebSpawnMode(content)) {
20169570cc8Sopenharmony_ci        return 0;
20269570cc8Sopenharmony_ci    }
20369570cc8Sopenharmony_ci    char path[PATH_MAX] = {};
20469570cc8Sopenharmony_ci    APPSPAWN_LOGV("ProcessMgrAddApp %{public}d %{public}d to cgroup ", appInfo->pid, appInfo->uid);
20569570cc8Sopenharmony_ci    int ret = GetCgroupPath(appInfo, path, sizeof(path));
20669570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return -1, "Failed to get real path errno: %{public}d", errno);
20769570cc8Sopenharmony_ci    (void)CreateSandboxDir(path, 0750);  // 0750 default mode
20869570cc8Sopenharmony_ci    uint32_t pathLen = strlen(path);
20969570cc8Sopenharmony_ci    ret = strcat_s(path, sizeof(path), "cgroup.procs");
21069570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
21169570cc8Sopenharmony_ci    ret = WriteToFile(path, 0, (pid_t *)&appInfo->pid, 1);
21269570cc8Sopenharmony_ci    APPSPAWN_CHECK(ret == 0, return ret, "write pid to cgroup.procs fail %{public}s", path);
21369570cc8Sopenharmony_ci    if (appInfo->max != 0) {
21469570cc8Sopenharmony_ci        path[pathLen] = '\0';
21569570cc8Sopenharmony_ci        ret = strcat_s(path, sizeof(path), "pids.max");
21669570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %{public}d", errno);
21769570cc8Sopenharmony_ci        ret = WritePidMax(path, appInfo->max);
21869570cc8Sopenharmony_ci        APPSPAWN_CHECK(ret == 0, return ret, "write max to pids.max fail %{public}s", path);
21969570cc8Sopenharmony_ci    }
22069570cc8Sopenharmony_ci    APPSPAWN_LOGV("Add app %{public}d to cgroup %{public}s success", appInfo->pid, path);
22169570cc8Sopenharmony_ci    return 0;
22269570cc8Sopenharmony_ci}
22369570cc8Sopenharmony_ci
22469570cc8Sopenharmony_ciMODULE_CONSTRUCTOR(void)
22569570cc8Sopenharmony_ci{
22669570cc8Sopenharmony_ci    AddProcessMgrHook(STAGE_SERVER_APP_ADD, 0, ProcessMgrAddApp);
22769570cc8Sopenharmony_ci    AddProcessMgrHook(STAGE_SERVER_APP_DIED, 0, ProcessMgrRemoveApp);
22869570cc8Sopenharmony_ci}
229