processgroup.cpp revision b82bab66f318896ebad80c1feee2347c58e3ce37
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 <mutex>
26#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>
32
33#include <log/log.h>
34#include <private/android_filesystem_config.h>
35
36#include <utils/SystemClock.h>
37
38#include <processgroup/processgroup.h>
39
40#define MEM_CGROUP_PATH "/dev/memcg/apps"
41#define ACCT_CGROUP_PATH "/acct"
42
43#define PROCESSGROUP_UID_PREFIX "uid_"
44#define PROCESSGROUP_PID_PREFIX "pid_"
45#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
46#define PROCESSGROUP_MAX_UID_LEN 11
47#define PROCESSGROUP_MAX_PID_LEN 11
48#define PROCESSGROUP_MAX_PATH_LEN \
49        ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
50          sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
51         sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
52         PROCESSGROUP_MAX_UID_LEN + \
53         sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
54         PROCESSGROUP_MAX_PID_LEN + \
55         sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
56         1)
57
58std::once_flag init_path_flag;
59
60struct ctx {
61    bool initialized;
62    int fd;
63    char buf[128];
64    char *buf_ptr;
65    size_t buf_len;
66};
67
68static const char* getCgroupRootPath() {
69    static const char* cgroup_root_path = NULL;
70    std::call_once(init_path_flag, [&]() {
71            cgroup_root_path = access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
72            });
73    return cgroup_root_path;
74}
75
76static int convertUidToPath(char *path, size_t size, uid_t uid)
77{
78    return snprintf(path, size, "%s/%s%d",
79            getCgroupRootPath(),
80            PROCESSGROUP_UID_PREFIX,
81            uid);
82}
83
84static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
85{
86    return snprintf(path, size, "%s/%s%d/%s%d",
87            getCgroupRootPath(),
88            PROCESSGROUP_UID_PREFIX,
89            uid,
90            PROCESSGROUP_PID_PREFIX,
91            pid);
92}
93
94static int initCtx(uid_t uid, int pid, struct ctx *ctx)
95{
96    int ret;
97    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
98    convertUidPidToPath(path, sizeof(path), uid, pid);
99    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
100
101    int fd = open(path, O_RDONLY);
102    if (fd < 0) {
103        ret = -errno;
104        SLOGW("failed to open %s: %s", path, strerror(errno));
105        return ret;
106    }
107
108    ctx->fd = fd;
109    ctx->buf_ptr = ctx->buf;
110    ctx->buf_len = 0;
111    ctx->initialized = true;
112
113    SLOGV("Initialized context for %s", path);
114
115    return 0;
116}
117
118static int refillBuffer(struct ctx *ctx)
119{
120    memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len);
121    ctx->buf_ptr = ctx->buf;
122
123    ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len,
124                sizeof(ctx->buf) - ctx->buf_len - 1);
125    if (ret < 0) {
126        return -errno;
127    } else if (ret == 0) {
128        return 0;
129    }
130
131    ctx->buf_len += ret;
132    ctx->buf[ctx->buf_len] = 0;
133    SLOGV("Read %zd to buffer: %s", ret, ctx->buf);
134
135    assert(ctx->buf_len <= sizeof(ctx->buf));
136
137    return ret;
138}
139
140static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
141{
142    if (!ctx->initialized) {
143        int ret = initCtx(uid, appProcessPid, ctx);
144        if (ret < 0) {
145            return ret;
146        }
147    }
148
149    char *eptr;
150    while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) {
151        int ret = refillBuffer(ctx);
152        if (ret == 0) {
153            return -ERANGE;
154        }
155        if (ret < 0) {
156            return ret;
157        }
158    }
159
160    *eptr = '\0';
161    char *pid_eptr = NULL;
162    errno = 0;
163    long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
164    if (errno != 0) {
165        return -errno;
166    }
167    if (pid_eptr != eptr) {
168        return -EINVAL;
169    }
170
171    ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
172    ctx->buf_ptr = eptr + 1;
173
174    return (pid_t)pid;
175}
176
177static int removeProcessGroup(uid_t uid, int pid)
178{
179    int ret;
180    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
181
182    convertUidPidToPath(path, sizeof(path), uid, pid);
183    ret = rmdir(path);
184
185    convertUidToPath(path, sizeof(path), uid);
186    rmdir(path);
187
188    return ret;
189}
190
191static void removeUidProcessGroups(const char *uid_path)
192{
193    DIR *uid = opendir(uid_path);
194    if (uid != NULL) {
195        struct dirent cur;
196        struct dirent *dir;
197        while ((readdir_r(uid, &cur, &dir) == 0) && dir) {
198            char path[PROCESSGROUP_MAX_PATH_LEN];
199
200            if (dir->d_type != DT_DIR) {
201                continue;
202            }
203
204            if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
205                continue;
206            }
207
208            snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
209            SLOGV("removing %s\n", path);
210            rmdir(path);
211        }
212        closedir(uid);
213    }
214}
215
216void removeAllProcessGroups()
217{
218    SLOGV("removeAllProcessGroups()");
219    const char *cgroup_root_path = getCgroupRootPath();
220    DIR *root = opendir(cgroup_root_path);
221    if (root == NULL) {
222        SLOGE("failed to open %s: %s", cgroup_root_path, strerror(errno));
223    } else {
224        struct dirent cur;
225        struct dirent *dir;
226        while ((readdir_r(root, &cur, &dir) == 0) && dir) {
227            char path[PROCESSGROUP_MAX_PATH_LEN];
228
229            if (dir->d_type != DT_DIR) {
230                continue;
231            }
232            if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
233                continue;
234            }
235
236            snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
237            removeUidProcessGroups(path);
238            SLOGV("removing %s\n", path);
239            rmdir(path);
240        }
241        closedir(root);
242    }
243}
244
245static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
246{
247    int processes = 0;
248    struct ctx ctx;
249    pid_t pid;
250
251    ctx.initialized = false;
252
253    while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) {
254        processes++;
255        if (pid == 0) {
256            // Should never happen...  but if it does, trying to kill this
257            // will boomerang right back and kill us!  Let's not let that happen.
258            SLOGW("Yikes, we've been told to kill pid 0!  How about we don't do that.");
259            continue;
260        }
261        if (pid != initialPid) {
262            // We want to be noisy about killing processes so we can understand
263            // what is going on in the log; however, don't be noisy about the base
264            // process, since that it something we always kill, and we have already
265            // logged elsewhere about killing it.
266            SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
267        }
268        int ret = kill(pid, signal);
269        if (ret == -1) {
270            SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
271        }
272    }
273
274    if (ctx.initialized) {
275        close(ctx.fd);
276    }
277
278    return processes;
279}
280
281int killProcessGroup(uid_t uid, int initialPid, int signal)
282{
283    int processes;
284    const int sleep_us = 5 * 1000;  // 5ms
285    int64_t startTime = android::uptimeMillis();
286    int retry = 40;
287
288    while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
289        SLOGV("killed %d processes for processgroup %d\n", processes, initialPid);
290        if (retry > 0) {
291            usleep(sleep_us);
292            --retry;
293        } else {
294            SLOGE("failed to kill %d processes for processgroup %d\n",
295                    processes, initialPid);
296            break;
297        }
298    }
299
300    SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
301            android::uptimeMillis()-startTime, processes);
302
303    if (processes == 0) {
304        return removeProcessGroup(uid, initialPid);
305    } else {
306        return -1;
307    }
308}
309
310static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
311{
312    int ret;
313
314    ret = mkdir(path, mode);
315    if (ret < 0 && errno != EEXIST) {
316        return -errno;
317    }
318
319    ret = chown(path, uid, gid);
320    if (ret < 0) {
321        ret = -errno;
322        rmdir(path);
323        return ret;
324    }
325
326    return 0;
327}
328
329int createProcessGroup(uid_t uid, int initialPid)
330{
331    char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
332    int ret;
333
334    convertUidToPath(path, sizeof(path), uid);
335
336    ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
337    if (ret < 0) {
338        SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
339        return ret;
340    }
341
342    convertUidPidToPath(path, sizeof(path), uid, initialPid);
343
344    ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM);
345    if (ret < 0) {
346        SLOGE("failed to make and chown %s: %s", path, strerror(-ret));
347        return ret;
348    }
349
350    strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
351
352    int fd = open(path, O_WRONLY);
353    if (fd < 0) {
354        ret = -errno;
355        SLOGE("failed to open %s: %s", path, strerror(errno));
356        return ret;
357    }
358
359    char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0};
360    int len = snprintf(pid, sizeof(pid), "%d", initialPid);
361
362    ret = write(fd, pid, len);
363    if (ret < 0) {
364        ret = -errno;
365        SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno));
366    } else {
367        ret = 0;
368    }
369
370    close(fd);
371    return ret;
372}
373
374