blob: ba97f32f7c4487aa3ff6ed41900c8f412ce18cca [file] [log] [blame]
Colin Crosscf8d1c22014-06-03 13:24:21 -07001/*
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>
Elliott Hughes0badbd62014-12-29 12:24:25 -080022#include <errno.h>
Colin Crosscf8d1c22014-06-03 13:24:21 -070023#include <fcntl.h>
Chih-Hung Hsiehfcc81152014-11-20 17:05:15 -080024#include <inttypes.h>
Martijn Coenenb82bab62016-01-20 16:39:16 -080025#include <mutex>
Colin Crosscf8d1c22014-06-03 13:24:21 -070026#include <stdbool.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <sys/types.h>
James Hawkins588a2ca2016-02-18 14:52:46 -080032#include <memory>
Colin Crosscf8d1c22014-06-03 13:24:21 -070033
34#include <log/log.h>
35#include <private/android_filesystem_config.h>
36
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -070037#include <utils/SystemClock.h>
38
Colin Crosscf8d1c22014-06-03 13:24:21 -070039#include <processgroup/processgroup.h>
Martijn Coenenb82bab62016-01-20 16:39:16 -080040
41#define MEM_CGROUP_PATH "/dev/memcg/apps"
Martijn Coenen623b56a2016-02-08 11:42:25 +010042#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
Martijn Coenenb82bab62016-01-20 16:39:16 -080043#define ACCT_CGROUP_PATH "/acct"
44
45#define PROCESSGROUP_UID_PREFIX "uid_"
46#define PROCESSGROUP_PID_PREFIX "pid_"
47#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
48#define PROCESSGROUP_MAX_UID_LEN 11
49#define PROCESSGROUP_MAX_PID_LEN 11
50#define PROCESSGROUP_MAX_PATH_LEN \
51 ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
52 sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
53 sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
54 PROCESSGROUP_MAX_UID_LEN + \
55 sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
56 PROCESSGROUP_MAX_PID_LEN + \
57 sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
58 1)
59
60std::once_flag init_path_flag;
Colin Crosscf8d1c22014-06-03 13:24:21 -070061
62struct ctx {
63 bool initialized;
64 int fd;
65 char buf[128];
66 char *buf_ptr;
67 size_t buf_len;
68};
69
Martijn Coenenb82bab62016-01-20 16:39:16 -080070static const char* getCgroupRootPath() {
71 static const char* cgroup_root_path = NULL;
72 std::call_once(init_path_flag, [&]() {
Martijn Coenen623b56a2016-02-08 11:42:25 +010073 // Check if mem cgroup is mounted, only then check for write-access to avoid
74 // SELinux denials
75 cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
76 ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
Martijn Coenenb82bab62016-01-20 16:39:16 -080077 });
78 return cgroup_root_path;
79}
80
Colin Crosscf8d1c22014-06-03 13:24:21 -070081static int convertUidToPath(char *path, size_t size, uid_t uid)
82{
83 return snprintf(path, size, "%s/%s%d",
Martijn Coenenb82bab62016-01-20 16:39:16 -080084 getCgroupRootPath(),
Colin Crosscf8d1c22014-06-03 13:24:21 -070085 PROCESSGROUP_UID_PREFIX,
86 uid);
87}
88
89static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
90{
91 return snprintf(path, size, "%s/%s%d/%s%d",
Martijn Coenenb82bab62016-01-20 16:39:16 -080092 getCgroupRootPath(),
Colin Crosscf8d1c22014-06-03 13:24:21 -070093 PROCESSGROUP_UID_PREFIX,
94 uid,
95 PROCESSGROUP_PID_PREFIX,
96 pid);
97}
98
99static int initCtx(uid_t uid, int pid, struct ctx *ctx)
100{
101 int ret;
102 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
103 convertUidPidToPath(path, sizeof(path), uid, pid);
104 strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
105
106 int fd = open(path, O_RDONLY);
107 if (fd < 0) {
108 ret = -errno;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700109 SLOGW("failed to open %s: %s", path, strerror(errno));
Colin Crosscf8d1c22014-06-03 13:24:21 -0700110 return ret;
111 }
112
113 ctx->fd = fd;
114 ctx->buf_ptr = ctx->buf;
115 ctx->buf_len = 0;
116 ctx->initialized = true;
117
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700118 SLOGV("Initialized context for %s", path);
119
Colin Crosscf8d1c22014-06-03 13:24:21 -0700120 return 0;
121}
122
123static int refillBuffer(struct ctx *ctx)
124{
125 memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
126 ctx->buf_ptr = ctx->buf;
127
128 ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700129 sizeof(ctx->buf) - ctx->buf_len - 1);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700130 if (ret < 0) {
131 return -errno;
132 } else if (ret == 0) {
133 return 0;
134 }
135
136 ctx->buf_len += ret;
Dianne Hackborn67f46cb2014-10-15 11:36:28 -0700137 ctx->buf[ctx->buf_len] = 0;
Chih-Hung Hsiehfcc81152014-11-20 17:05:15 -0800138 SLOGV("Read %zd to buffer: %s", ret, ctx->buf);
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700139
Colin Crosscf8d1c22014-06-03 13:24:21 -0700140 assert(ctx->buf_len <= sizeof(ctx->buf));
141
142 return ret;
143}
144
145static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
146{
147 if (!ctx->initialized) {
148 int ret = initCtx(uid, appProcessPid, ctx);
149 if (ret < 0) {
150 return ret;
151 }
152 }
153
154 char *eptr;
155 while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
156 int ret = refillBuffer(ctx);
157 if (ret == 0) {
158 return -ERANGE;
159 }
160 if (ret < 0) {
161 return ret;
162 }
163 }
164
165 *eptr = '\0';
166 char *pid_eptr = NULL;
167 errno = 0;
168 long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
169 if (errno != 0) {
170 return -errno;
171 }
172 if (pid_eptr != eptr) {
173 return -EINVAL;
174 }
175
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700176 ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
Colin Crosscf8d1c22014-06-03 13:24:21 -0700177 ctx->buf_ptr = eptr + 1;
178
179 return (pid_t)pid;
180}
181
182static int removeProcessGroup(uid_t uid, int pid)
183{
184 int ret;
185 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
186
187 convertUidPidToPath(path, sizeof(path), uid, pid);
188 ret = rmdir(path);
189
190 convertUidToPath(path, sizeof(path), uid);
191 rmdir(path);
192
193 return ret;
194}
195
196static void removeUidProcessGroups(const char *uid_path)
197{
James Hawkins588a2ca2016-02-18 14:52:46 -0800198 std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700199 if (uid != NULL) {
200 struct dirent cur;
201 struct dirent *dir;
James Hawkins588a2ca2016-02-18 14:52:46 -0800202 while ((readdir_r(uid.get(), &cur, &dir) == 0) && dir) {
Colin Crosscf8d1c22014-06-03 13:24:21 -0700203 char path[PROCESSGROUP_MAX_PATH_LEN];
204
205 if (dir->d_type != DT_DIR) {
206 continue;
207 }
208
209 if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
210 continue;
211 }
212
213 snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
214 SLOGV("removing %s\n", path);
215 rmdir(path);
216 }
217 }
218}
219
220void removeAllProcessGroups()
221{
222 SLOGV("removeAllProcessGroups()");
Martijn Coenenb82bab62016-01-20 16:39:16 -0800223 const char *cgroup_root_path = getCgroupRootPath();
James Hawkins22b6f7a2016-02-19 11:10:30 -0800224 std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700225 if (root == NULL) {
Martijn Coenenb82bab62016-01-20 16:39:16 -0800226 SLOGE("failed to open %s: %s", cgroup_root_path, strerror(errno));
Colin Crossc15dd042014-08-20 14:11:13 -0700227 } else {
Colin Crosscf8d1c22014-06-03 13:24:21 -0700228 struct dirent cur;
229 struct dirent *dir;
James Hawkins588a2ca2016-02-18 14:52:46 -0800230 while ((readdir_r(root.get(), &cur, &dir) == 0) && dir) {
Colin Crosscf8d1c22014-06-03 13:24:21 -0700231 char path[PROCESSGROUP_MAX_PATH_LEN];
232
233 if (dir->d_type != DT_DIR) {
234 continue;
235 }
236 if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
237 continue;
238 }
239
Martijn Coenenb82bab62016-01-20 16:39:16 -0800240 snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700241 removeUidProcessGroups(path);
242 SLOGV("removing %s\n", path);
243 rmdir(path);
244 }
Colin Crosscf8d1c22014-06-03 13:24:21 -0700245 }
246}
247
248static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
249{
250 int processes = 0;
251 struct ctx ctx;
252 pid_t pid;
253
254 ctx.initialized = false;
255
256 while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
257 processes++;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700258 if (pid == 0) {
259 // Should never happen... but if it does, trying to kill this
260 // will boomerang right back and kill us! Let's not let that happen.
261 SLOGW("Yikes, we've been told to kill pid 0! How about we don't do that.");
262 continue;
263 }
264 if (pid != initialPid) {
265 // We want to be noisy about killing processes so we can understand
266 // what is going on in the log; however, don't be noisy about the base
267 // process, since that it something we always kill, and we have already
268 // logged elsewhere about killing it.
269 SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
270 }
Colin Crosscf8d1c22014-06-03 13:24:21 -0700271 int ret = kill(pid, signal);
272 if (ret == -1) {
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700273 SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
Colin Crosscf8d1c22014-06-03 13:24:21 -0700274 }
275 }
276
277 if (ctx.initialized) {
278 close(ctx.fd);
279 }
280
281 return processes;
282}
283
284int killProcessGroup(uid_t uid, int initialPid, int signal)
285{
286 int processes;
Yusuke Satod5039302015-06-16 13:51:14 -0700287 const int sleep_us = 5 * 1000; // 5ms
Chih-Hung Hsiehfcc81152014-11-20 17:05:15 -0800288 int64_t startTime = android::uptimeMillis();
Yusuke Satod5039302015-06-16 13:51:14 -0700289 int retry = 40;
Colin Crosscf8d1c22014-06-03 13:24:21 -0700290
291 while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
292 SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
Yusuke Satod5039302015-06-16 13:51:14 -0700293 if (retry > 0) {
Colin Crosscf8d1c22014-06-03 13:24:21 -0700294 usleep(sleep_us);
Yusuke Satod5039302015-06-16 13:51:14 -0700295 --retry;
Colin Crosscf8d1c22014-06-03 13:24:21 -0700296 } else {
297 SLOGE("failed to kill %d processes for processgroup %d\n",
298 processes, initialPid);
299 break;
300 }
301 }
302
Chih-Hung Hsiehfcc81152014-11-20 17:05:15 -0800303 SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700304 android::uptimeMillis()-startTime, processes);
305
Colin Crosscf8d1c22014-06-03 13:24:21 -0700306 if (processes == 0) {
307 return removeProcessGroup(uid, initialPid);
308 } else {
309 return -1;
310 }
311}
312
313static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
314{
315 int ret;
316
Bernhard Rosenkränzer758aeb72014-11-17 20:46:00 +0100317 ret = mkdir(path, mode);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700318 if (ret < 0 && errno != EEXIST) {
319 return -errno;
320 }
321
Bernhard Rosenkränzer758aeb72014-11-17 20:46:00 +0100322 ret = chown(path, uid, gid);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700323 if (ret < 0) {
324 ret = -errno;
325 rmdir(path);
326 return ret;
327 }
328
329 return 0;
330}
331
332int createProcessGroup(uid_t uid, int initialPid)
333{
334 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
335 int ret;
336
337 convertUidToPath(path, sizeof(path), uid);
338
339 ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
340 if (ret < 0) {
341 SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
342 return ret;
343 }
344
345 convertUidPidToPath(path, sizeof(path), uid, initialPid);
346
347 ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
348 if (ret < 0) {
349 SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
350 return ret;
351 }
352
353 strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
354
355 int fd = open(path, O_WRONLY);
356 if (fd < 0) {
357 ret = -errno;
358 SLOGE("failed to open %s: %s", path, strerror(errno));
359 return ret;
360 }
361
362 char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
363 int len = snprintf(pid, sizeof(pid), "%d", initialPid);
364
365 ret = write(fd, pid, len);
366 if (ret < 0) {
367 ret = -errno;
368 SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
369 } else {
370 ret = 0;
371 }
372
373 close(fd);
374 return ret;
375}
376