1 /*
2 * Copyright (c) 2025 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 <errno.h>
17 #include <inttypes.h>
18 #include <limits.h>
19 #include <signal.h>
20
21 #include "securec.h"
22 #include "init.h"
23 #include "init_log.h"
24 #include "init_utils.h"
25 #include "init_service.h"
26
GetCgroupPath(Service * service,char * buffer,uint32_t buffLen)27 INIT_STATIC int GetCgroupPath(Service *service, char *buffer, uint32_t buffLen)
28 {
29 int ret = snprintf_s(buffer, buffLen, buffLen - 1, "/dev/pids/native/%s/pid_%d/", service->name, service->pid);
30 INIT_ERROR_CHECK(ret > 0, return ret, "Failed to snprintf_s in GetCgroupPath, errno: %d", errno);
31 INIT_LOGV("Cgroup path %s ", buffer);
32 return 0;
33 }
34
WriteToFile(const char * path,pid_t pid)35 static int WriteToFile(const char *path, pid_t pid)
36 {
37 INIT_CHECK_RETURN_VALUE(pid != 0, -1);
38 char pidName[PATH_MAX] = {0};
39 int fd = open(path, O_RDWR | O_APPEND);
40 INIT_ERROR_CHECK(fd >= 0, return -1, "Failed to open file errno: %d path: %s", errno, path);
41 int ret = 0;
42 INIT_LOGV(" WriteToFile pid %d", pid);
43 do {
44 ret = snprintf_s(pidName, sizeof(pidName), sizeof(pidName) - 1, "%d", pid);
45 INIT_ERROR_CHECK(ret > 0, break, "Failed to snprintf_s in WriteToFile, errno: %d", errno);
46 ret = write(fd, pidName, strlen(pidName));
47 INIT_ERROR_CHECK(ret > 0, break, "Failed to write file errno: %d path: %s %s", errno, path, pidName);
48 ret = 0;
49 } while (0);
50
51 close(fd);
52 return ret;
53 }
54
KillProcessesByCGroup(const char * path,Service * service)55 static void KillProcessesByCGroup(const char *path, Service *service)
56 {
57 FILE *file = fopen(path, "r");
58 INIT_ERROR_CHECK(file != NULL, return, "Open file fail %s errno: %d", path, errno);
59 pid_t pid = 0;
60 while (fscanf_s(file, "%d\n", &pid) == 1 && pid > 0) {
61 INIT_LOGV(" KillProcessesByCGroup pid %d ", pid);
62 if (pid == service->pid) {
63 continue;
64 }
65 INIT_LOGI("Kill service pid %d now ...", pid);
66 if (kill(pid, SIGKILL) != 0) {
67 INIT_LOGE("Unable to kill process, pid: %d ret: %d", pid, errno);
68 }
69 }
70 (void)fclose(file);
71 }
72
DoRmdir(const TimerHandle handler,void * context)73 static void DoRmdir(const TimerHandle handler, void *context)
74 {
75 UNUSED(handler);
76 Service *service = (Service *)context;
77 INIT_ERROR_CHECK(service != NULL, return, "Service timer process with invalid service");
78 ServiceStopTimer(service);
79
80 char path[PATH_MAX] = {};
81 int ret = GetCgroupPath(service, path, sizeof(path));
82 free(service->name);
83 free(service);
84 INIT_ERROR_CHECK(ret == 0, return, "Failed to get real path errno: %d", errno);
85 ret = rmdir(path);
86 if (ret != 0) {
87 INIT_LOGE("Failed to rmdir %s errno: %d", path, errno);
88 }
89 }
90
RmdirTimer(Service * service,uint64_t timeout)91 static void RmdirTimer(Service *service, uint64_t timeout)
92 {
93 Service *serviceRmdir = (Service *)calloc(1, sizeof(Service));
94 INIT_ERROR_CHECK(serviceRmdir != NULL, return, "Failed to malloc for serviceRmdir");
95 serviceRmdir->pid = service->pid;
96 int strLen = strlen(service->name);
97 serviceRmdir->name = (char *)malloc(sizeof(char)*(strLen + 1));
98 int ret = strcpy_s(serviceRmdir->name, strLen + 1, service->name);
99 if (ret != 0) {
100 free(serviceRmdir->name);
101 free(serviceRmdir);
102 INIT_LOGE("Failed to copy, ret: %d", errno);
103 return;
104 }
105 LE_STATUS status = LE_CreateTimer(LE_GetDefaultLoop(), &serviceRmdir->timer, DoRmdir, (void *)serviceRmdir);
106 if (status != LE_SUCCESS) {
107 INIT_LOGE("Create service timer for service \' %s Rmdir \' failed, status = %d", serviceRmdir->name, status);
108 free(serviceRmdir->name);
109 free(serviceRmdir);
110 return;
111 }
112 status = LE_StartTimer(LE_GetDefaultLoop(), serviceRmdir->timer, timeout, 1);
113 if (status != LE_SUCCESS) {
114 INIT_LOGE("Start service timer for service \' %s Rmdir \' failed, status = %d", serviceRmdir->name, status);
115 free(serviceRmdir->name);
116 free(serviceRmdir);
117 }
118 }
119
ProcessServiceDied(Service * service)120 int ProcessServiceDied(Service *service)
121 {
122 INIT_CHECK_RETURN_VALUE(service != NULL, -1);
123 INIT_CHECK_RETURN_VALUE(service->isCgroupEnabled, -1);
124 char path[PATH_MAX] = {};
125 INIT_LOGV("ProcessServiceDied %d to cgroup ", service->pid);
126 int ret = GetCgroupPath(service, path, sizeof(path));
127 INIT_ERROR_CHECK(ret == 0, return -1, "Failed to get real path errno: %d", errno);
128 ret = strcat_s(path, sizeof(path), "cgroup.procs");
129 INIT_ERROR_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %d", errno);
130 KillProcessesByCGroup(path, service);
131 RmdirTimer(service, 200); // 200ms
132 return ret;
133 }
134
ProcessServiceAdd(Service * service)135 int ProcessServiceAdd(Service *service)
136 {
137 INIT_CHECK_RETURN_VALUE(service != NULL, -1);
138 INIT_CHECK_RETURN_VALUE(service->isCgroupEnabled, -1);
139 char path[PATH_MAX] = {};
140 INIT_LOGV("ProcessServiceAdd %d to cgroup ", service->pid);
141 int ret = GetCgroupPath(service, path, sizeof(path));
142 INIT_ERROR_CHECK(ret == 0, return -1, "Failed to get real path errno: %d", errno);
143 (void)MakeDirRecursive(path, 0755); // 0755 default mode
144 ret = strcat_s(path, sizeof(path), "cgroup.procs");
145 INIT_ERROR_CHECK(ret == 0, return ret, "Failed to strcat_s errno: %d", errno);
146 ret = WriteToFile(path, service->pid);
147 INIT_ERROR_CHECK(ret == 0, return ret, "write pid to cgroup.procs fail %s", path);
148 INIT_LOGV("Add service %d to cgroup %s success", service->pid, path);
149 return 0;
150 }