blob: d5f3ad3c2e7b77ab0ef80aa26d201a76cf9da8cd [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>
22#include <fcntl.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <sys/types.h>
29
30#include <log/log.h>
31#include <private/android_filesystem_config.h>
32
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -070033#include <utils/SystemClock.h>
34
Colin Crosscf8d1c22014-06-03 13:24:21 -070035#include <processgroup/processgroup.h>
36#include "processgroup_priv.h"
37
38struct ctx {
39 bool initialized;
40 int fd;
41 char buf[128];
42 char *buf_ptr;
43 size_t buf_len;
44};
45
46static int convertUidToPath(char *path, size_t size, uid_t uid)
47{
48 return snprintf(path, size, "%s/%s%d",
49 PROCESSGROUP_CGROUP_PATH,
50 PROCESSGROUP_UID_PREFIX,
51 uid);
52}
53
54static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
55{
56 return snprintf(path, size, "%s/%s%d/%s%d",
57 PROCESSGROUP_CGROUP_PATH,
58 PROCESSGROUP_UID_PREFIX,
59 uid,
60 PROCESSGROUP_PID_PREFIX,
61 pid);
62}
63
64static int initCtx(uid_t uid, int pid, struct ctx *ctx)
65{
66 int ret;
67 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
68 convertUidPidToPath(path, sizeof(path), uid, pid);
69 strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
70
71 int fd = open(path, O_RDONLY);
72 if (fd < 0) {
73 ret = -errno;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -070074 SLOGW("failed to open %s: %s", path, strerror(errno));
Colin Crosscf8d1c22014-06-03 13:24:21 -070075 return ret;
76 }
77
78 ctx->fd = fd;
79 ctx->buf_ptr = ctx->buf;
80 ctx->buf_len = 0;
81 ctx->initialized = true;
82
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -070083 SLOGV("Initialized context for %s", path);
84
Colin Crosscf8d1c22014-06-03 13:24:21 -070085 return 0;
86}
87
88static int refillBuffer(struct ctx *ctx)
89{
90 memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
91 ctx->buf_ptr = ctx->buf;
92
93 ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -070094 sizeof(ctx->buf) - ctx->buf_len - 1);
Colin Crosscf8d1c22014-06-03 13:24:21 -070095 if (ret < 0) {
96 return -errno;
97 } else if (ret == 0) {
98 return 0;
99 }
100
101 ctx->buf_len += ret;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700102 ctx->buf[ctx->buf_len-1] = 0;
103 SLOGV("Read %d to buffer: %s", ret, ctx->buf);
104
Colin Crosscf8d1c22014-06-03 13:24:21 -0700105 assert(ctx->buf_len <= sizeof(ctx->buf));
106
107 return ret;
108}
109
110static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
111{
112 if (!ctx->initialized) {
113 int ret = initCtx(uid, appProcessPid, ctx);
114 if (ret < 0) {
115 return ret;
116 }
117 }
118
119 char *eptr;
120 while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
121 int ret = refillBuffer(ctx);
122 if (ret == 0) {
123 return -ERANGE;
124 }
125 if (ret < 0) {
126 return ret;
127 }
128 }
129
130 *eptr = '\0';
131 char *pid_eptr = NULL;
132 errno = 0;
133 long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
134 if (errno != 0) {
135 return -errno;
136 }
137 if (pid_eptr != eptr) {
138 return -EINVAL;
139 }
140
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700141 ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
Colin Crosscf8d1c22014-06-03 13:24:21 -0700142 ctx->buf_ptr = eptr + 1;
143
144 return (pid_t)pid;
145}
146
147static int removeProcessGroup(uid_t uid, int pid)
148{
149 int ret;
150 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
151
152 convertUidPidToPath(path, sizeof(path), uid, pid);
153 ret = rmdir(path);
154
155 convertUidToPath(path, sizeof(path), uid);
156 rmdir(path);
157
158 return ret;
159}
160
161static void removeUidProcessGroups(const char *uid_path)
162{
163 DIR *uid = opendir(uid_path);
164 if (uid != NULL) {
165 struct dirent cur;
166 struct dirent *dir;
167 while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
168 char path[PROCESSGROUP_MAX_PATH_LEN];
169
170 if (dir->d_type != DT_DIR) {
171 continue;
172 }
173
174 if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
175 continue;
176 }
177
178 snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
179 SLOGV("removing %s\n", path);
180 rmdir(path);
181 }
Colin Crossc15dd042014-08-20 14:11:13 -0700182 closedir(uid);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700183 }
184}
185
186void removeAllProcessGroups()
187{
188 SLOGV("removeAllProcessGroups()");
189 DIR *root = opendir(PROCESSGROUP_CGROUP_PATH);
190 if (root == NULL) {
191 SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno));
Colin Crossc15dd042014-08-20 14:11:13 -0700192 } else {
Colin Crosscf8d1c22014-06-03 13:24:21 -0700193 struct dirent cur;
194 struct dirent *dir;
195 while ((readdir_r(root, &cur, &dir) == 0) && dir) {
196 char path[PROCESSGROUP_MAX_PATH_LEN];
197
198 if (dir->d_type != DT_DIR) {
199 continue;
200 }
201 if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
202 continue;
203 }
204
205 snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name);
206 removeUidProcessGroups(path);
207 SLOGV("removing %s\n", path);
208 rmdir(path);
209 }
Colin Crossc15dd042014-08-20 14:11:13 -0700210 closedir(root);
Colin Crosscf8d1c22014-06-03 13:24:21 -0700211 }
212}
213
214static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
215{
216 int processes = 0;
217 struct ctx ctx;
218 pid_t pid;
219
220 ctx.initialized = false;
221
222 while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
223 processes++;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700224 if (pid == 0) {
225 // Should never happen... but if it does, trying to kill this
226 // will boomerang right back and kill us! Let's not let that happen.
227 SLOGW("Yikes, we've been told to kill pid 0! How about we don't do that.");
228 continue;
229 }
230 if (pid != initialPid) {
231 // We want to be noisy about killing processes so we can understand
232 // what is going on in the log; however, don't be noisy about the base
233 // process, since that it something we always kill, and we have already
234 // logged elsewhere about killing it.
235 SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
236 }
Colin Crosscf8d1c22014-06-03 13:24:21 -0700237 int ret = kill(pid, signal);
238 if (ret == -1) {
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700239 SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
Colin Crosscf8d1c22014-06-03 13:24:21 -0700240 }
241 }
242
243 if (ctx.initialized) {
244 close(ctx.fd);
245 }
246
247 return processes;
248}
249
250int killProcessGroup(uid_t uid, int initialPid, int signal)
251{
252 int processes;
253 int sleep_us = 100;
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700254 long startTime = android::uptimeMillis();
Colin Crosscf8d1c22014-06-03 13:24:21 -0700255
256 while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
257 SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
258 if (sleep_us < 128000) {
259 usleep(sleep_us);
260 sleep_us *= 2;
261 } else {
262 SLOGE("failed to kill %d processes for processgroup %d\n",
263 processes, initialPid);
264 break;
265 }
266 }
267
Dianne Hackborn2c5e7e12014-10-13 17:45:22 -0700268 SLOGV("Killed process group uid %d pid %d in %ldms, %d procs remain", uid, initialPid,
269 android::uptimeMillis()-startTime, processes);
270
Colin Crosscf8d1c22014-06-03 13:24:21 -0700271 if (processes == 0) {
272 return removeProcessGroup(uid, initialPid);
273 } else {
274 return -1;
275 }
276}
277
278static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
279{
280 int ret;
281
282 ret = mkdir(path, 0750);
283 if (ret < 0 && errno != EEXIST) {
284 return -errno;
285 }
286
287 ret = chown(path, AID_SYSTEM, AID_SYSTEM);
288 if (ret < 0) {
289 ret = -errno;
290 rmdir(path);
291 return ret;
292 }
293
294 return 0;
295}
296
297int createProcessGroup(uid_t uid, int initialPid)
298{
299 char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
300 int ret;
301
302 convertUidToPath(path, sizeof(path), uid);
303
304 ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
305 if (ret < 0) {
306 SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
307 return ret;
308 }
309
310 convertUidPidToPath(path, sizeof(path), uid, initialPid);
311
312 ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
313 if (ret < 0) {
314 SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
315 return ret;
316 }
317
318 strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
319
320 int fd = open(path, O_WRONLY);
321 if (fd < 0) {
322 ret = -errno;
323 SLOGE("failed to open %s: %s", path, strerror(errno));
324 return ret;
325 }
326
327 char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
328 int len = snprintf(pid, sizeof(pid), "%d", initialPid);
329
330 ret = write(fd, pid, len);
331 if (ret < 0) {
332 ret = -errno;
333 SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
334 } else {
335 ret = 0;
336 }
337
338 close(fd);
339 return ret;
340}
341