1 /* 2 * Copyright 2014 Google, Inc 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 <assert.h> 21 #include <dirent.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <inttypes.h> 25 #include <signal.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <sys/stat.h> 30 #include <sys/types.h> 31 #include <unistd.h> 32 33 #include <chrono> 34 #include <memory> 35 #include <mutex> 36 #include <set> 37 #include <string> 38 #include <thread> 39 40 #include <android-base/file.h> 41 #include <android-base/logging.h> 42 #ifdef __ANDROID__ 43 #include <android-base/properties.h> 44 #endif 45 #include <android-base/stringprintf.h> 46 #include <android-base/strings.h> 47 #include <private/android_filesystem_config.h> 48 49 #include <processgroup/processgroup.h> 50 51 #ifdef __ANDROID__ 52 using android::base::GetBoolProperty; 53 #endif 54 using android::base::StartsWith; 55 using android::base::StringPrintf; 56 using android::base::WriteStringToFile; 57 58 using namespace std::chrono_literals; 59 60 #define MEM_CGROUP_PATH "/dev/memcg/apps" 61 #define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks" 62 #define ACCT_CGROUP_PATH "/acct" 63 64 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs" 65 66 std::once_flag init_path_flag; 67 68 static const std::string& GetCgroupRootPath() { 69 static std::string cgroup_root_path; 70 std::call_once(init_path_flag, [&]() { 71 #ifdef __ANDROID__ 72 // low-ram devices use per-app memcg by default, unlike high-end ones 73 bool low_ram_device = GetBoolProperty("ro.config.low_ram", false); 74 bool per_app_memcg = 75 GetBoolProperty("ro.config.per_app_memcg", low_ram_device); 76 #else 77 // host does not support Android properties 78 bool per_app_memcg = false; 79 #endif 80 if (per_app_memcg) { 81 // Check if mem cgroup is mounted, only then check for 82 // write-access to avoid SELinux denials 83 cgroup_root_path = 84 (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? 85 ACCT_CGROUP_PATH : MEM_CGROUP_PATH); 86 } else { 87 cgroup_root_path = ACCT_CGROUP_PATH; 88 } 89 }); 90 return cgroup_root_path; 91 } 92 93 static std::string ConvertUidToPath(uid_t uid) { 94 return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid); 95 } 96 97 static std::string ConvertUidPidToPath(uid_t uid, int pid) { 98 return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid); 99 } 100 101 static int RemoveProcessGroup(uid_t uid, int pid) { 102 int ret; 103 104 auto uid_pid_path = ConvertUidPidToPath(uid, pid); 105 ret = rmdir(uid_pid_path.c_str()); 106 107 auto uid_path = ConvertUidToPath(uid); 108 rmdir(uid_path.c_str()); 109 110 return ret; 111 } 112 113 static void RemoveUidProcessGroups(const std::string& uid_path) { 114 std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir); 115 if (uid != NULL) { 116 dirent* dir; 117 while ((dir = readdir(uid.get())) != nullptr) { 118 if (dir->d_type != DT_DIR) { 119 continue; 120 } 121 122 if (!StartsWith(dir->d_name, "pid_")) { 123 continue; 124 } 125 126 auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name); 127 LOG(VERBOSE) << "Removing " << path; 128 if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path; 129 } 130 } 131 } 132 133 void removeAllProcessGroups() 134 { 135 LOG(VERBOSE) << "removeAllProcessGroups()"; 136 const auto& cgroup_root_path = GetCgroupRootPath(); 137 std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir); 138 if (root == NULL) { 139 PLOG(ERROR) << "Failed to open " << cgroup_root_path; 140 } else { 141 dirent* dir; 142 while ((dir = readdir(root.get())) != nullptr) { 143 if (dir->d_type != DT_DIR) { 144 continue; 145 } 146 147 if (!StartsWith(dir->d_name, "uid_")) { 148 continue; 149 } 150 151 auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name); 152 RemoveUidProcessGroups(path); 153 LOG(VERBOSE) << "Removing " << path; 154 if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path; 155 } 156 } 157 } 158 159 // Returns number of processes killed on success 160 // Returns 0 if there are no processes in the process cgroup left to kill 161 // Returns -1 on error 162 static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) { 163 auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE; 164 std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose); 165 if (!fd) { 166 PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid; 167 return -1; 168 } 169 170 // We separate all of the pids in the cgroup into those pids that are also the leaders of 171 // process groups (stored in the pgids set) and those that are not (stored in the pids set). 172 std::set<pid_t> pgids; 173 pgids.emplace(initialPid); 174 std::set<pid_t> pids; 175 176 pid_t pid; 177 int processes = 0; 178 while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) { 179 processes++; 180 if (pid == 0) { 181 // Should never happen... but if it does, trying to kill this 182 // will boomerang right back and kill us! Let's not let that happen. 183 LOG(WARNING) << "Yikes, we've been told to kill pid 0! How about we don't do that?"; 184 continue; 185 } 186 pid_t pgid = getpgid(pid); 187 if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed"; 188 if (pgid == pid) { 189 pgids.emplace(pid); 190 } else { 191 pids.emplace(pid); 192 } 193 } 194 195 // Erase all pids that will be killed when we kill the process groups. 196 for (auto it = pids.begin(); it != pids.end();) { 197 pid_t pgid = getpgid(pid); 198 if (pgids.count(pgid) == 1) { 199 it = pids.erase(it); 200 } else { 201 ++it; 202 } 203 } 204 205 // Kill all process groups. 206 for (const auto pgid : pgids) { 207 LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid 208 << " as part of process cgroup " << initialPid; 209 210 if (kill(-pgid, signal) == -1) { 211 PLOG(WARNING) << "kill(" << -pgid << ", " << signal << ") failed"; 212 } 213 } 214 215 // Kill remaining pids. 216 for (const auto pid : pids) { 217 LOG(VERBOSE) << "Killing pid " << pid << " in uid " << uid << " as part of process cgroup " 218 << initialPid; 219 220 if (kill(pid, signal) == -1) { 221 PLOG(WARNING) << "kill(" << pid << ", " << signal << ") failed"; 222 } 223 } 224 225 return feof(fd.get()) ? processes : -1; 226 } 227 228 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) { 229 std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now(); 230 231 int retry = retries; 232 int processes; 233 while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) { 234 LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid; 235 if (retry > 0) { 236 std::this_thread::sleep_for(5ms); 237 --retry; 238 } else { 239 break; 240 } 241 } 242 243 if (processes < 0) { 244 PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid " 245 << initialPid; 246 return -1; 247 } 248 249 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 250 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); 251 252 // We only calculate the number of 'processes' when killing the processes. 253 // In the retries == 0 case, we only kill the processes once and therefore 254 // will not have waited then recalculated how many processes are remaining 255 // after the first signals have been sent. 256 // Logging anything regarding the number of 'processes' here does not make sense. 257 258 if (processes == 0) { 259 if (retries > 0) { 260 LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid 261 << " in " << static_cast<int>(ms) << "ms"; 262 } 263 return RemoveProcessGroup(uid, initialPid); 264 } else { 265 if (retries > 0) { 266 LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid 267 << " in " << static_cast<int>(ms) << "ms, " << processes 268 << " processes remain"; 269 } 270 return -1; 271 } 272 } 273 274 int killProcessGroup(uid_t uid, int initialPid, int signal) { 275 return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/); 276 } 277 278 int killProcessGroupOnce(uid_t uid, int initialPid, int signal) { 279 return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/); 280 } 281 282 static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) { 283 if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) { 284 return false; 285 } 286 287 if (chown(path.c_str(), uid, gid) == -1) { 288 int saved_errno = errno; 289 rmdir(path.c_str()); 290 errno = saved_errno; 291 return false; 292 } 293 294 return true; 295 } 296 297 int createProcessGroup(uid_t uid, int initialPid) 298 { 299 auto uid_path = ConvertUidToPath(uid); 300 301 if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) { 302 PLOG(ERROR) << "Failed to make and chown " << uid_path; 303 return -errno; 304 } 305 306 auto uid_pid_path = ConvertUidPidToPath(uid, initialPid); 307 308 if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) { 309 PLOG(ERROR) << "Failed to make and chown " << uid_pid_path; 310 return -errno; 311 } 312 313 auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE; 314 315 int ret = 0; 316 if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) { 317 ret = -errno; 318 PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file; 319 } 320 321 return ret; 322 } 323 324 static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) { 325 if (GetCgroupRootPath() != MEM_CGROUP_PATH) { 326 PLOG(ERROR) << "Memcg is not mounted."; 327 return false; 328 } 329 330 auto path = ConvertUidPidToPath(uid, pid) + file_name; 331 332 if (!WriteStringToFile(std::to_string(value), path)) { 333 PLOG(ERROR) << "Failed to write '" << value << "' to " << path; 334 return false; 335 } 336 return true; 337 } 338 339 bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) { 340 return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness); 341 } 342 343 bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) { 344 return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes); 345 } 346 347 bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) { 348 return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes); 349 } 350