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/unique_fd.h>
38 #include <cgroup_map.h>
39 #include <json/reader.h>
40 #include <json/value.h>
41 #include <processgroup/processgroup.h>
42
43 using android::base::GetBoolProperty;
44 using android::base::StringPrintf;
45 using android::base::unique_fd;
46
47 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
48 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
49 static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
50
version() const51 uint32_t CgroupController::version() const {
52 CHECK(HasValue());
53 return ACgroupController_getVersion(controller_);
54 }
55
name() const56 const char* CgroupController::name() const {
57 CHECK(HasValue());
58 return ACgroupController_getName(controller_);
59 }
60
path() const61 const char* CgroupController::path() const {
62 CHECK(HasValue());
63 return ACgroupController_getPath(controller_);
64 }
65
HasValue() const66 bool CgroupController::HasValue() const {
67 return controller_ != nullptr;
68 }
69
IsUsable()70 bool CgroupController::IsUsable() {
71 if (!HasValue()) return false;
72
73 if (state_ == UNKNOWN) {
74 state_ = access(GetProcsFilePath("", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;
75 }
76
77 return state_ == USABLE;
78 }
79
GetTasksFilePath(const std::string & rel_path) const80 std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
81 std::string tasks_path = path();
82
83 if (!rel_path.empty()) {
84 tasks_path += "/" + rel_path;
85 }
86 return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
87 }
88
GetProcsFilePath(const std::string & rel_path,uid_t uid,pid_t pid) const89 std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
90 pid_t pid) const {
91 std::string proc_path(path());
92 proc_path.append("/").append(rel_path);
93 proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
94 proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
95
96 return proc_path.append(CGROUP_PROCS_FILE);
97 }
98
GetTaskGroup(int tid,std::string * group) const99 bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
100 std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
101 std::string content;
102 if (!android::base::ReadFileToString(file_name, &content)) {
103 PLOG(ERROR) << "Failed to read " << file_name;
104 return false;
105 }
106
107 // if group is null and tid exists return early because
108 // user is not interested in cgroup membership
109 if (group == nullptr) {
110 return true;
111 }
112
113 std::string cg_tag = StringPrintf(":%s:", name());
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 LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
165 << ACgroupController_getVersion(controller) << " path "
166 << ACgroupController_getPath(controller);
167 }
168 }
169
FindController(const std::string & name) const170 CgroupController CgroupMap::FindController(const std::string& name) const {
171 if (!loaded_) {
172 LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
173 << "] failed, RC file was not initialized properly";
174 return CgroupController(nullptr);
175 }
176
177 auto controller_count = ACgroupFile_getControllerCount();
178 for (uint32_t i = 0; i < controller_count; ++i) {
179 const ACgroupController* controller = ACgroupFile_getController(i);
180 if (name == ACgroupController_getName(controller)) {
181 return CgroupController(controller);
182 }
183 }
184
185 return CgroupController(nullptr);
186 }
187