2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "helper-cgroup.h"
19 #define RELEASE_AGENT "release_agent"
20 #define NOTIFY_ON_RELEASE "notify_on_release"
22 #define CGROUP_FILE_NAME "cgroup.procs"
23 #define MAX_PATH_LENGTH 512
25 static int read_uint(FILE *handler, uint32_t *out)
27 return fscanf(handler, "%u", out);
30 static int write_uint(FILE *handler, uint32_t number)
32 _cleanup_free_ char *digit_buf = NULL;
35 ret = asprintf(&digit_buf, "%u\n", number);
36 ret_value_errno_msg_if(ret < 0, -ENOMEM, "asprintf failed\n");
38 ret = fputs(digit_buf, handler);
39 ret_value_errno_msg_if(ret == EOF, errno ? -errno : -EIO,
40 "Fail to write file");
45 static bool cgroup_is_exists(const char *cgroup_full_path)
48 return stat(cgroup_full_path, &stat_buf) == 0;
51 static int cgroup_create(const char *cgroup_full_path)
53 if (mkdir(cgroup_full_path,
54 S_IRUSR | S_IWUSR | S_IRGRP) < 0)
61 * @desc place pid to cgroup.procs file
62 * @return 0 in case of success, errno value in case of failure
64 stc_error_e cgroup_write_pid_fullpath(const char *cgroup_full_path,
70 STC_LOGE("try to write empty pid to %s", cgroup_full_path);
71 return STC_ERROR_NO_DATA;
74 ret = cgroup_write_node_uint32(cgroup_full_path, CGROUP_FILE_NAME,
77 ret_value_msg_if(ret < 0, STC_ERROR_FAIL,
78 "Failed place all pid to cgroup %s", cgroup_full_path);
79 return STC_ERROR_NONE;
82 stc_error_e cgroup_write_pid(const char *cgroup_subsystem,
83 const char *cgroup_name, const int pid)
85 char buf[MAX_PATH_LENGTH];
86 snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name);
87 return cgroup_write_pid_fullpath(buf, pid);
90 stc_error_e cgroup_write_pidtree(const char *cgroup_subsystem,
91 const char *cgroup_name, const int pid)
93 char buf[MAX_PATH_LENGTH];
95 /*/proc/%d/task/%d/children */
96 char child_buf[21 + MAX_DEC_SIZE(int) + MAX_DEC_SIZE(int)];
97 char pidbuf[MAX_DEC_SIZE(int)];
102 snprintf(buf, sizeof(buf), "%s/%s", cgroup_subsystem, cgroup_name);
104 ret = cgroup_write_pid_fullpath(buf, pid);
105 ret_value_msg_if(ret != STC_ERROR_NONE, ret,
106 "Failed to put parent process %d into %s cgroup",
109 snprintf(child_buf, sizeof(child_buf), PROC_TASK_CHILDREN,
111 f = fopen(child_buf, "r");
112 ret_value_msg_if(!f, STC_ERROR_FAIL, "Failed to get child pids!");
113 while (fgets(pidbuf, sizeof(pidbuf), f) != NULL) {
114 int child_pid = atoi(pidbuf);
116 STC_LOGE("Invalid child pid!");
118 return STC_ERROR_FAIL;
120 stc_error_e ret = cgroup_write_pid_fullpath(buf, child_pid);
121 if (ret != STC_ERROR_NONE) {
122 STC_LOGE("Failed to put parent process %d into %s cgroup",
129 return STC_ERROR_NONE;
132 int cgroup_write_node_uint32(const char *cgroup_name,
133 const char *file_name, uint32_t value)
135 char buf[MAX_PATH_LENGTH];
136 snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
137 STC_LOGD("cgroup_buf %s, value %d\n", buf, value);
138 return fwrite_uint(buf, value);
141 int cgroup_write_node_str(const char *cgroup_name,
142 const char *file_name, const char *string)
144 char buf[MAX_PATH_LENGTH];
145 snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
146 STC_LOGD("cgroup_buf %s, string %s\n", buf, string);
147 return fwrite_str(buf, string);
150 int cgroup_read_node_uint32(const char *cgroup_name,
151 const char *file_name, uint32_t *value)
153 char buf[MAX_PATH_LENGTH];
155 snprintf(buf, sizeof(buf), "%s/%s", cgroup_name, file_name);
156 ret = fread_uint(buf, value);
157 STC_LOGD("cgroup_buf %s, value %d\n", buf, *value);
161 int cgroup_make_subdir(const char *parentdir, const char *cgroup_name,
164 char buf[MAX_PATH_LENGTH];
169 ret = snprintf(buf, sizeof(buf), "%s/%s",
170 parentdir, cgroup_name);
172 ret = snprintf(buf, sizeof(buf), "%s",
175 ret_value_msg_if(ret > sizeof(buf), STC_ERROR_FAIL,
176 "Not enought buffer size for %s%s",
177 parentdir, cgroup_name);
179 cgroup_exists = cgroup_is_exists(buf);
180 if (!cgroup_exists) {
181 bool cgroup_remount = false;
183 if (parentdir && !strncmp(parentdir, DEFAULT_CGROUP,
184 sizeof(DEFAULT_CGROUP))) {
185 ret = mount("tmpfs", DEFAULT_CGROUP, "tmpfs",
186 MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
189 STC_LOGE("Fail to RW mount cgroup directory. Can't make %s cgroup", cgroup_name);
190 return STC_ERROR_FAIL;
192 cgroup_remount = true;
195 ret = cgroup_create(buf);
196 ret_value_msg_if(ret < 0, STC_ERROR_FAIL,
197 "Fail to create cgroup %s : err %d",
200 if (cgroup_remount) {
201 ret = mount("tmpfs", DEFAULT_CGROUP, "tmpfs",
202 MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY,
205 STC_LOGD("Fail to RO mount");
210 *already = cgroup_exists;
212 return STC_ERROR_NONE;
215 /* FIXME: tasks is not removed from tasks list */
216 int cgroup_remove_pid(const char *cgroup_subsystem, const char *cgroup_name,
219 char cgroup_tasks_file_path[MAX_PATH_LENGTH];
222 pid_t pid_for_read = 0;
224 guint pid_count = 0;;
226 snprintf(cgroup_tasks_file_path, sizeof(cgroup_tasks_file_path),
227 "%s/%s/tasks", cgroup_subsystem, cgroup_name);
229 handler = fopen(cgroup_tasks_file_path, "r");
231 STC_LOGE("Read file open failed");
235 pids = g_array_new(FALSE, FALSE, sizeof(pid_t));
237 while (read_uint(handler, (uint32_t *)&pid_for_read) >= 0) {
238 if (pid_for_read != pid) {
239 pids = g_array_append_val(pids, pid_for_read);
246 handler = fopen(cgroup_tasks_file_path, "w");
248 STC_LOGE("Write file open failed");
252 for (i = 0; i < pid_count; i++)
253 write_uint(handler, g_array_index(pids, pid_t, i));
256 g_array_free(pids, TRUE);
260 int cgroup_set_release_agent(const char *cgroup_subsys,
261 const char *release_agent)
263 _cleanup_free_ char *buf = NULL;
266 r = asprintf(&buf, "%s/%s", DEFAULT_CGROUP, cgroup_subsys);
270 r = cgroup_write_node_str(buf, RELEASE_AGENT, release_agent);
274 return cgroup_write_node_str(buf, NOTIFY_ON_RELEASE, "1");