blob: 0734f256526c72599f4e89c915eaa26e59016b1f [file] [log] [blame]
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -08001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "libprocessgroup"
19
20#include <errno.h>
21#include <fcntl.h>
Suren Baghdasaryane3ad8882019-02-06 13:25:29 -080022#include <grp.h>
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080023#include <pwd.h>
24#include <sys/mman.h>
25#include <sys/mount.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <time.h>
29#include <unistd.h>
30
31#include <regex>
32
33#include <android-base/file.h>
34#include <android-base/logging.h>
35#include <android-base/properties.h>
36#include <android-base/stringprintf.h>
37#include <android-base/unique_fd.h>
38#include <cgroup_map.h>
39#include <json/reader.h>
40#include <json/value.h>
41#include <processgroup/processgroup.h>
42
43using android::base::GetBoolProperty;
44using android::base::StringPrintf;
45using android::base::unique_fd;
Suren Baghdasaryan25ad3f92021-07-30 13:42:39 -070046using android::base::WriteStringToFile;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080047
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080048static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
49static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
50static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
51
Yifan Hong53e0deb2019-03-22 17:01:08 -070052uint32_t CgroupController::version() const {
53 CHECK(HasValue());
54 return ACgroupController_getVersion(controller_);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080055}
56
Yifan Hong53e0deb2019-03-22 17:01:08 -070057const char* CgroupController::name() const {
58 CHECK(HasValue());
59 return ACgroupController_getName(controller_);
60}
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080061
Yifan Hong53e0deb2019-03-22 17:01:08 -070062const char* CgroupController::path() const {
63 CHECK(HasValue());
64 return ACgroupController_getPath(controller_);
65}
66
67bool CgroupController::HasValue() const {
68 return controller_ != nullptr;
69}
70
Suren Baghdasaryan25eb1bf2019-06-26 11:08:50 -070071bool CgroupController::IsUsable() {
Suren Baghdasaryanfa7a05f2019-05-08 17:59:55 -070072 if (!HasValue()) return false;
73
Suren Baghdasaryan25eb1bf2019-06-26 11:08:50 -070074 if (state_ == UNKNOWN) {
Jiyong Parkab8a7d22020-08-11 09:32:55 +090075 if (__builtin_available(android 30, *)) {
Suren Baghdasaryancee468f2020-01-23 16:18:13 -080076 uint32_t flags = ACgroupController_getFlags(controller_);
77 state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
78 } else {
79 state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
80 }
Suren Baghdasaryan25eb1bf2019-06-26 11:08:50 -070081 }
82
83 return state_ == USABLE;
Suren Baghdasaryanfa7a05f2019-05-08 17:59:55 -070084}
85
Yifan Hong53e0deb2019-03-22 17:01:08 -070086std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
87 std::string tasks_path = path();
88
89 if (!rel_path.empty()) {
90 tasks_path += "/" + rel_path;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080091 }
Yifan Hong53e0deb2019-03-22 17:01:08 -070092 return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080093}
94
Yifan Hong53e0deb2019-03-22 17:01:08 -070095std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080096 pid_t pid) const {
Yifan Hong53e0deb2019-03-22 17:01:08 -070097 std::string proc_path(path());
98 proc_path.append("/").append(rel_path);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080099 proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
100 proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
101
102 return proc_path.append(CGROUP_PROCS_FILE);
103}
104
105bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
106 std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
107 std::string content;
108 if (!android::base::ReadFileToString(file_name, &content)) {
Wei Wangd71d3012019-03-07 11:59:12 -0800109 PLOG(ERROR) << "Failed to read " << file_name;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800110 return false;
111 }
112
113 // if group is null and tid exists return early because
114 // user is not interested in cgroup membership
115 if (group == nullptr) {
116 return true;
117 }
118
Marco Ballesiof16f9412020-06-02 15:21:13 -0700119 std::string cg_tag;
120
121 if (version() == 2) {
122 cg_tag = "0::";
123 } else {
124 cg_tag = StringPrintf(":%s:", name());
125 }
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800126 size_t start_pos = content.find(cg_tag);
127 if (start_pos == std::string::npos) {
128 return false;
129 }
130
131 start_pos += cg_tag.length() + 1; // skip '/'
132 size_t end_pos = content.find('\n', start_pos);
133 if (end_pos == std::string::npos) {
134 *group = content.substr(start_pos, std::string::npos);
135 } else {
136 *group = content.substr(start_pos, end_pos - start_pos);
137 }
138
139 return true;
140}
141
Yifan Hong53e0deb2019-03-22 17:01:08 -0700142CgroupMap::CgroupMap() {
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800143 if (!LoadRcFile()) {
Wei Wangd71d3012019-03-07 11:59:12 -0800144 LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800145 }
146}
147
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800148CgroupMap& CgroupMap::GetInstance() {
Peter Collingbournedba6d442019-03-20 21:09:46 -0700149 // Deliberately leak this object to avoid a race between destruction on
150 // process exit and concurrent access from another thread.
151 static auto* instance = new CgroupMap;
152 return *instance;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800153}
154
155bool CgroupMap::LoadRcFile() {
Yifan Hong53e0deb2019-03-22 17:01:08 -0700156 if (!loaded_) {
157 loaded_ = (ACgroupFile_getVersion() != 0);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800158 }
Yifan Hong53e0deb2019-03-22 17:01:08 -0700159 return loaded_;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800160}
161
Wei Wangd71d3012019-03-07 11:59:12 -0800162void CgroupMap::Print() const {
Yifan Hong53e0deb2019-03-22 17:01:08 -0700163 if (!loaded_) {
Wei Wangd71d3012019-03-07 11:59:12 -0800164 LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
165 << "] failed, RC file was not initialized properly";
166 return;
167 }
Yifan Hong53e0deb2019-03-22 17:01:08 -0700168 LOG(INFO) << "File version = " << ACgroupFile_getVersion();
169 LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800170
171 LOG(INFO) << "Mounted cgroups:";
Yifan Hong53e0deb2019-03-22 17:01:08 -0700172
173 auto controller_count = ACgroupFile_getControllerCount();
174 for (uint32_t i = 0; i < controller_count; ++i) {
175 const ACgroupController* controller = ACgroupFile_getController(i);
Jiyong Parkab8a7d22020-08-11 09:32:55 +0900176 if (__builtin_available(android 30, *)) {
Suren Baghdasaryancee468f2020-01-23 16:18:13 -0800177 LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
178 << ACgroupController_getVersion(controller) << " path "
179 << ACgroupController_getPath(controller) << " flags "
180 << ACgroupController_getFlags(controller);
181 } else {
182 LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
183 << ACgroupController_getVersion(controller) << " path "
184 << ACgroupController_getPath(controller);
185 }
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800186 }
187}
188
Yifan Hong53e0deb2019-03-22 17:01:08 -0700189CgroupController CgroupMap::FindController(const std::string& name) const {
190 if (!loaded_) {
Wei Wangd71d3012019-03-07 11:59:12 -0800191 LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
192 << "] failed, RC file was not initialized properly";
Yifan Hong53e0deb2019-03-22 17:01:08 -0700193 return CgroupController(nullptr);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800194 }
195
Yifan Hong53e0deb2019-03-22 17:01:08 -0700196 auto controller_count = ACgroupFile_getControllerCount();
197 for (uint32_t i = 0; i < controller_count; ++i) {
198 const ACgroupController* controller = ACgroupFile_getController(i);
199 if (name == ACgroupController_getName(controller)) {
200 return CgroupController(controller);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800201 }
202 }
203
Yifan Hong53e0deb2019-03-22 17:01:08 -0700204 return CgroupController(nullptr);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800205}
Suren Baghdasaryan25ad3f92021-07-30 13:42:39 -0700206
207int CgroupMap::ActivateControllers(const std::string& path) const {
208 if (__builtin_available(android 30, *)) {
209 auto controller_count = ACgroupFile_getControllerCount();
210 for (uint32_t i = 0; i < controller_count; ++i) {
211 const ACgroupController* controller = ACgroupFile_getController(i);
212 if (ACgroupController_getFlags(controller) &
213 CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
214 std::string str = std::string("+") + ACgroupController_getName(controller);
215 if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
216 return -errno;
217 }
218 }
219 }
220 return 0;
221 }
222 return -ENOSYS;
223}