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