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 <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 
     33 #include <utils/SystemClock.h>
     34 
     35 #include <processgroup/processgroup.h>
     36 #include "processgroup_priv.h"
     37 
     38 struct ctx {
     39     bool initialized;
     40     int fd;
     41     char buf[128];
     42     char *buf_ptr;
     43     size_t buf_len;
     44 };
     45 
     46 static 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 
     54 static 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 
     64 static 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;
     74         SLOGW("failed to open %s: %s", path, strerror(errno));
     75         return ret;
     76     }
     77 
     78     ctx->fd = fd;
     79     ctx->buf_ptr = ctx->buf;
     80     ctx->buf_len = 0;
     81     ctx->initialized = true;
     82 
     83     SLOGV("Initialized context for %s", path);
     84 
     85     return 0;
     86 }
     87 
     88 static 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,
     94                 sizeof(ctx->buf) - ctx->buf_len - 1);
     95     if (ret < 0) {
     96         return -errno;
     97     } else if (ret == 0) {
     98         return 0;
     99     }
    100 
    101     ctx->buf_len += ret;
    102     ctx->buf[ctx->buf_len] = 0;
    103     SLOGV("Read %d to buffer: %s", ret, ctx->buf);
    104 
    105     assert(ctx->buf_len <= sizeof(ctx->buf));
    106 
    107     return ret;
    108 }
    109 
    110 static 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 
    141     ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
    142     ctx->buf_ptr = eptr + 1;
    143 
    144     return (pid_t)pid;
    145 }
    146 
    147 static 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 
    161 static 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         }
    182         closedir(uid);
    183     }
    184 }
    185 
    186 void 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));
    192     } else {
    193         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         }
    210         closedir(root);
    211     }
    212 }
    213 
    214 static 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++;
    224         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         }
    237         int ret = kill(pid, signal);
    238         if (ret == -1) {
    239             SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
    240         }
    241     }
    242 
    243     if (ctx.initialized) {
    244         close(ctx.fd);
    245     }
    246 
    247     return processes;
    248 }
    249 
    250 int killProcessGroup(uid_t uid, int initialPid, int signal)
    251 {
    252     int processes;
    253     int sleep_us = 100;
    254     long startTime = android::uptimeMillis();
    255 
    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 
    268     SLOGV("Killed process group uid %d pid %d in %ldms, %d procs remain", uid, initialPid,
    269             android::uptimeMillis()-startTime, processes);
    270 
    271     if (processes == 0) {
    272         return removeProcessGroup(uid, initialPid);
    273     } else {
    274         return -1;
    275     }
    276 }
    277 
    278 static 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 
    297 int 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 
    342