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