blob: 3b852ae9b33b6edd6ddf4a44f834708def0e6f22 [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;
46
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080047static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
48static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
49static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
50
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080051CgroupController::CgroupController(uint32_t version, const std::string& name,
52 const std::string& path) {
53 version_ = version;
54 strncpy(name_, name.c_str(), sizeof(name_) - 1);
55 name_[sizeof(name_) - 1] = '\0';
56 strncpy(path_, path.c_str(), sizeof(path_) - 1);
57 path_[sizeof(path_) - 1] = '\0';
58}
59
60std::string CgroupController::GetTasksFilePath(const std::string& path) const {
61 std::string tasks_path = path_;
62
63 if (!path.empty()) {
64 tasks_path += "/" + path;
65 }
66 return (version_ == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
67}
68
69std::string CgroupController::GetProcsFilePath(const std::string& path, uid_t uid,
70 pid_t pid) const {
71 std::string proc_path(path_);
72 proc_path.append("/").append(path);
73 proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
74 proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
75
76 return proc_path.append(CGROUP_PROCS_FILE);
77}
78
79bool CgroupController::GetTaskGroup(int tid, std::string* group) const {
80 std::string file_name = StringPrintf("/proc/%d/cgroup", tid);
81 std::string content;
82 if (!android::base::ReadFileToString(file_name, &content)) {
Wei Wangd71d3012019-03-07 11:59:12 -080083 PLOG(ERROR) << "Failed to read " << file_name;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -080084 return false;
85 }
86
87 // if group is null and tid exists return early because
88 // user is not interested in cgroup membership
89 if (group == nullptr) {
90 return true;
91 }
92
93 std::string cg_tag = StringPrintf(":%s:", name_);
94 size_t start_pos = content.find(cg_tag);
95 if (start_pos == std::string::npos) {
96 return false;
97 }
98
99 start_pos += cg_tag.length() + 1; // skip '/'
100 size_t end_pos = content.find('\n', start_pos);
101 if (end_pos == std::string::npos) {
102 *group = content.substr(start_pos, std::string::npos);
103 } else {
104 *group = content.substr(start_pos, end_pos - start_pos);
105 }
106
107 return true;
108}
109
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800110CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
111 if (!LoadRcFile()) {
Wei Wangd71d3012019-03-07 11:59:12 -0800112 LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800113 }
114}
115
116CgroupMap::~CgroupMap() {
117 if (cg_file_data_) {
118 munmap(cg_file_data_, cg_file_size_);
119 cg_file_data_ = nullptr;
120 cg_file_size_ = 0;
121 }
122}
123
124CgroupMap& CgroupMap::GetInstance() {
Peter Collingbournedba6d442019-03-20 21:09:46 -0700125 // Deliberately leak this object to avoid a race between destruction on
126 // process exit and concurrent access from another thread.
127 static auto* instance = new CgroupMap;
128 return *instance;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800129}
130
131bool CgroupMap::LoadRcFile() {
132 struct stat sb;
133
134 if (cg_file_data_) {
135 // Data already initialized
136 return true;
137 }
138
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000139 unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800140 if (fd < 0) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000141 PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800142 return false;
143 }
144
145 if (fstat(fd, &sb) < 0) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000146 PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800147 return false;
148 }
149
Wei Wangd71d3012019-03-07 11:59:12 -0800150 size_t file_size = sb.st_size;
151 if (file_size < sizeof(CgroupFile)) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000152 LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800153 return false;
154 }
155
Wei Wangd71d3012019-03-07 11:59:12 -0800156 CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
157 if (file_data == MAP_FAILED) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000158 PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800159 return false;
160 }
161
Wei Wangd71d3012019-03-07 11:59:12 -0800162 if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000163 LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
Wei Wangd71d3012019-03-07 11:59:12 -0800164 munmap(file_data, file_size);
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800165 return false;
166 }
167
Wei Wangd71d3012019-03-07 11:59:12 -0800168 if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
Suren Baghdasaryan5b535732019-03-26 20:34:32 +0000169 LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size";
Wei Wangd71d3012019-03-07 11:59:12 -0800170 munmap(file_data, file_size);
171 return false;
172 }
173
174 cg_file_data_ = file_data;
175 cg_file_size_ = file_size;
176
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800177 return true;
178}
179
Wei Wangd71d3012019-03-07 11:59:12 -0800180void CgroupMap::Print() const {
181 if (!cg_file_data_) {
182 LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
183 << "] failed, RC file was not initialized properly";
184 return;
185 }
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800186 LOG(INFO) << "File version = " << cg_file_data_->version_;
187 LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
188
189 LOG(INFO) << "Mounted cgroups:";
190 CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
191 for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
192 LOG(INFO) << "\t" << controller->name() << " ver " << controller->version() << " path "
193 << controller->path();
194 }
195}
196
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800197const CgroupController* CgroupMap::FindController(const std::string& name) const {
198 if (!cg_file_data_) {
Wei Wangd71d3012019-03-07 11:59:12 -0800199 LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
200 << "] failed, RC file was not initialized properly";
Suren Baghdasaryan82b72a52018-12-21 11:41:50 -0800201 return nullptr;
202 }
203
204 // skip the file header to get to the first controller
205 CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
206 for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
207 if (name == controller->name()) {
208 return controller;
209 }
210 }
211
212 return nullptr;
213}