xref: /aosp_15_r20/system/core/libprocessgroup/cgroup_map.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 /*
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 <unistd.h>
22 
23 #include <regex>
24 
25 #include <android-base/file.h>
26 #include <android-base/logging.h>
27 #include <android-base/stringprintf.h>
28 #include <cgroup_map.h>
29 #include <processgroup/processgroup.h>
30 #include <processgroup/util.h>
31 
32 using android::base::StringPrintf;
33 using android::base::WriteStringToFile;
34 
35 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
36 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
37 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.threads";
38 
version() const39 uint32_t CgroupControllerWrapper::version() const {
40     CHECK(HasValue());
41     return controller_->version();
42 }
43 
name() const44 const char* CgroupControllerWrapper::name() const {
45     CHECK(HasValue());
46     return controller_->name();
47 }
48 
path() const49 const char* CgroupControllerWrapper::path() const {
50     CHECK(HasValue());
51     return controller_->path();
52 }
53 
HasValue() const54 bool CgroupControllerWrapper::HasValue() const {
55     return controller_ != nullptr;
56 }
57 
IsUsable()58 bool CgroupControllerWrapper::IsUsable() {
59     if (!HasValue()) return false;
60 
61     if (state_ == UNKNOWN) {
62         if (__builtin_available(android 30, *)) {
63             uint32_t flags = controller_->flags();
64             state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
65         } else {
66             state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
67         }
68     }
69 
70     return state_ == USABLE;
71 }
72 
GetTasksFilePath(const std::string & rel_path) const73 std::string CgroupControllerWrapper::GetTasksFilePath(const std::string& rel_path) const {
74     std::string tasks_path = path();
75 
76     if (!rel_path.empty()) {
77         tasks_path += "/" + rel_path;
78     }
79     return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
80 }
81 
GetProcsFilePath(const std::string & rel_path,uid_t uid,pid_t pid) const82 std::string CgroupControllerWrapper::GetProcsFilePath(const std::string& rel_path, uid_t uid,
83                                                       pid_t pid) const {
84     std::string proc_path(path());
85     proc_path.append("/").append(rel_path);
86     proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
87     proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
88 
89     return proc_path.append(CGROUP_PROCS_FILE);
90 }
91 
GetTaskGroup(pid_t tid,std::string * group) const92 bool CgroupControllerWrapper::GetTaskGroup(pid_t tid, std::string* group) const {
93     std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
94     std::string content;
95     if (!android::base::ReadFileToString(file_name, &content)) {
96         PLOG(ERROR) << "Failed to read " << file_name;
97         return false;
98     }
99 
100     // if group is null and tid exists return early because
101     // user is not interested in cgroup membership
102     if (group == nullptr) {
103         return true;
104     }
105 
106     std::string cg_tag;
107 
108     if (version() == 2) {
109         cg_tag = "0::";
110     } else {
111         cg_tag = StringPrintf(":%s:", name());
112     }
113     size_t start_pos = content.find(cg_tag);
114     if (start_pos == std::string::npos) {
115         return false;
116     }
117 
118     start_pos += cg_tag.length() + 1;  // skip '/'
119     size_t end_pos = content.find('\n', start_pos);
120     if (end_pos == std::string::npos) {
121         *group = content.substr(start_pos, std::string::npos);
122     } else {
123         *group = content.substr(start_pos, end_pos - start_pos);
124     }
125 
126     return true;
127 }
128 
CgroupMap()129 CgroupMap::CgroupMap() {
130     if (!LoadDescriptors()) {
131         LOG(ERROR) << "CgroupMap::LoadDescriptors called for [" << getpid() << "] failed";
132     }
133 }
134 
GetInstance()135 CgroupMap& CgroupMap::GetInstance() {
136     // Deliberately leak this object to avoid a race between destruction on
137     // process exit and concurrent access from another thread.
138     static auto* instance = new CgroupMap;
139     return *instance;
140 }
141 
LoadDescriptors()142 bool CgroupMap::LoadDescriptors() {
143     if (!loaded_) {
144         loaded_ = ReadDescriptors(&descriptors_);
145     }
146     return loaded_;
147 }
148 
Print() const149 void CgroupMap::Print() const {
150     if (!loaded_) {
151         LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
152                    << "] failed, cgroups were not initialized properly";
153         return;
154     }
155     LOG(INFO) << "Controller count = " << descriptors_.size();
156 
157     LOG(INFO) << "Mounted cgroups:";
158 
159     for (const auto& [name, descriptor] : descriptors_) {
160         LOG(INFO) << "\t" << descriptor.controller()->name() << " ver "
161                   << descriptor.controller()->version() << " path "
162                   << descriptor.controller()->path() << " flags "
163                   << descriptor.controller()->flags();
164     }
165 }
166 
FindController(const std::string & name) const167 CgroupControllerWrapper CgroupMap::FindController(const std::string& name) const {
168     if (!loaded_) {
169         LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
170                    << "] failed, cgroups were not initialized properly";
171         return CgroupControllerWrapper(nullptr);
172     }
173 
174     if (const auto it = descriptors_.find(name); it != descriptors_.end()) {
175         return CgroupControllerWrapper(it->second.controller());
176     }
177 
178     return CgroupControllerWrapper(nullptr);
179 }
180 
FindControllerByPath(const std::string & path) const181 CgroupControllerWrapper CgroupMap::FindControllerByPath(const std::string& path) const {
182     if (!loaded_) {
183         LOG(ERROR) << "CgroupMap::FindControllerByPath called for [" << getpid()
184                    << "] failed, cgroups were not initialized properly";
185         return CgroupControllerWrapper(nullptr);
186     }
187 
188     for (const auto& [name, descriptor] : descriptors_) {
189         if (path.starts_with(descriptor.controller()->path())) {
190             return CgroupControllerWrapper(descriptor.controller());
191         }
192     }
193 
194     return CgroupControllerWrapper(nullptr);
195 }
196 
ActivateControllers(const std::string & path) const197 bool CgroupMap::ActivateControllers(const std::string& path) const {
198     return ::ActivateControllers(path, descriptors_);
199 }
200