• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/strings.h>
29 #include <cgroup_map.h>
30 #include <processgroup/processgroup.h>
31 
32 using android::base::StartsWith;
33 using android::base::StringPrintf;
34 using android::base::WriteStringToFile;
35 
36 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
37 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
38 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.threads";
39 
version() const40 uint32_t CgroupController::version() const {
41     CHECK(HasValue());
42     return ACgroupController_getVersion(controller_);
43 }
44 
name() const45 const char* CgroupController::name() const {
46     CHECK(HasValue());
47     return ACgroupController_getName(controller_);
48 }
49 
path() const50 const char* CgroupController::path() const {
51     CHECK(HasValue());
52     return ACgroupController_getPath(controller_);
53 }
54 
HasValue() const55 bool CgroupController::HasValue() const {
56     return controller_ != nullptr;
57 }
58 
IsUsable()59 bool CgroupController::IsUsable() {
60     if (!HasValue()) return false;
61 
62     if (state_ == UNKNOWN) {
63         if (__builtin_available(android 30, *)) {
64             uint32_t flags = ACgroupController_getFlags(controller_);
65             state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;
66         } else {
67             state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
68         }
69     }
70 
71     return state_ == USABLE;
72 }
73 
GetTasksFilePath(const std::string & rel_path) const74 std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
75     std::string tasks_path = path();
76 
77     if (!rel_path.empty()) {
78         tasks_path += "/" + rel_path;
79     }
80     return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
81 }
82 
GetProcsFilePath(const std::string & rel_path,uid_t uid,pid_t pid) const83 std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
84                                                pid_t pid) const {
85     std::string proc_path(path());
86     proc_path.append("/").append(rel_path);
87     proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
88     proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
89 
90     return proc_path.append(CGROUP_PROCS_FILE);
91 }
92 
GetTaskGroup(pid_t tid,std::string * group) const93 bool CgroupController::GetTaskGroup(pid_t tid, std::string* group) const {
94     std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
95     std::string content;
96     if (!android::base::ReadFileToString(file_name, &content)) {
97         PLOG(ERROR) << "Failed to read " << file_name;
98         return false;
99     }
100 
101     // if group is null and tid exists return early because
102     // user is not interested in cgroup membership
103     if (group == nullptr) {
104         return true;
105     }
106 
107     std::string cg_tag;
108 
109     if (version() == 2) {
110         cg_tag = "0::";
111     } else {
112         cg_tag = StringPrintf(":%s:", name());
113     }
114     size_t start_pos = content.find(cg_tag);
115     if (start_pos == std::string::npos) {
116         return false;
117     }
118 
119     start_pos += cg_tag.length() + 1;  // skip '/'
120     size_t end_pos = content.find('\n', start_pos);
121     if (end_pos == std::string::npos) {
122         *group = content.substr(start_pos, std::string::npos);
123     } else {
124         *group = content.substr(start_pos, end_pos - start_pos);
125     }
126 
127     return true;
128 }
129 
CgroupMap()130 CgroupMap::CgroupMap() {
131     if (!LoadRcFile()) {
132         LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
133     }
134 }
135 
GetInstance()136 CgroupMap& CgroupMap::GetInstance() {
137     // Deliberately leak this object to avoid a race between destruction on
138     // process exit and concurrent access from another thread.
139     static auto* instance = new CgroupMap;
140     return *instance;
141 }
142 
LoadRcFile()143 bool CgroupMap::LoadRcFile() {
144     if (!loaded_) {
145         loaded_ = (ACgroupFile_getVersion() != 0);
146     }
147     return loaded_;
148 }
149 
Print() const150 void CgroupMap::Print() const {
151     if (!loaded_) {
152         LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
153                    << "] failed, RC file was not initialized properly";
154         return;
155     }
156     LOG(INFO) << "File version = " << ACgroupFile_getVersion();
157     LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
158 
159     LOG(INFO) << "Mounted cgroups:";
160 
161     auto controller_count = ACgroupFile_getControllerCount();
162     for (uint32_t i = 0; i < controller_count; ++i) {
163         const ACgroupController* controller = ACgroupFile_getController(i);
164         if (__builtin_available(android 30, *)) {
165             LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
166                       << ACgroupController_getVersion(controller) << " path "
167                       << ACgroupController_getPath(controller) << " flags "
168                       << ACgroupController_getFlags(controller);
169         } else {
170             LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
171                       << ACgroupController_getVersion(controller) << " path "
172                       << ACgroupController_getPath(controller);
173         }
174     }
175 }
176 
FindController(const std::string & name) const177 CgroupController CgroupMap::FindController(const std::string& name) const {
178     if (!loaded_) {
179         LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
180                    << "] failed, RC file was not initialized properly";
181         return CgroupController(nullptr);
182     }
183 
184     auto controller_count = ACgroupFile_getControllerCount();
185     for (uint32_t i = 0; i < controller_count; ++i) {
186         const ACgroupController* controller = ACgroupFile_getController(i);
187         if (name == ACgroupController_getName(controller)) {
188             return CgroupController(controller);
189         }
190     }
191 
192     return CgroupController(nullptr);
193 }
194 
FindControllerByPath(const std::string & path) const195 CgroupController CgroupMap::FindControllerByPath(const std::string& path) const {
196     if (!loaded_) {
197         LOG(ERROR) << "CgroupMap::FindControllerByPath called for [" << getpid()
198                    << "] failed, RC file was not initialized properly";
199         return CgroupController(nullptr);
200     }
201 
202     auto controller_count = ACgroupFile_getControllerCount();
203     for (uint32_t i = 0; i < controller_count; ++i) {
204         const ACgroupController* controller = ACgroupFile_getController(i);
205         if (StartsWith(path, ACgroupController_getPath(controller))) {
206             return CgroupController(controller);
207         }
208     }
209 
210     return CgroupController(nullptr);
211 }
212 
ActivateControllers(const std::string & path) const213 int CgroupMap::ActivateControllers(const std::string& path) const {
214     if (__builtin_available(android 30, *)) {
215         auto controller_count = ACgroupFile_getControllerCount();
216         for (uint32_t i = 0; i < controller_count; ++i) {
217             const ACgroupController* controller = ACgroupFile_getController(i);
218             const uint32_t flags = ACgroupController_getFlags(controller);
219             if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
220                 std::string str("+");
221                 str.append(ACgroupController_getName(controller));
222                 if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
223                     if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) {
224                         PLOG(WARNING) << "Activation of cgroup controller " << str
225                                       << " failed in path " << path;
226                     } else {
227                         return -errno;
228                     }
229                 }
230             }
231         }
232         return 0;
233     }
234     return -ENOSYS;
235 }
236