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