diff options
Diffstat (limited to 'libprocessgroup/processgroup.cpp')
-rw-r--r-- | libprocessgroup/processgroup.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp new file mode 100644 index 0000000..c32e741 --- /dev/null +++ b/libprocessgroup/processgroup.cpp @@ -0,0 +1,316 @@ +/* + * Copyright 2014 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "libprocessgroup" + +#include <assert.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <log/log.h> +#include <private/android_filesystem_config.h> + +#include <processgroup/processgroup.h> +#include "processgroup_priv.h" + +struct ctx { + bool initialized; + int fd; + char buf[128]; + char *buf_ptr; + size_t buf_len; +}; + +static int convertUidToPath(char *path, size_t size, uid_t uid) +{ + return snprintf(path, size, "%s/%s%d", + PROCESSGROUP_CGROUP_PATH, + PROCESSGROUP_UID_PREFIX, + uid); +} + +static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid) +{ + return snprintf(path, size, "%s/%s%d/%s%d", + PROCESSGROUP_CGROUP_PATH, + PROCESSGROUP_UID_PREFIX, + uid, + PROCESSGROUP_PID_PREFIX, + pid); +} + +static int initCtx(uid_t uid, int pid, struct ctx *ctx) +{ + int ret; + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + convertUidPidToPath(path, sizeof(path), uid, pid); + strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + SLOGV("failed to open %s: %s", path, strerror(errno)); + return ret; + } + + ctx->fd = fd; + ctx->buf_ptr = ctx->buf; + ctx->buf_len = 0; + ctx->initialized = true; + + return 0; +} + +static int refillBuffer(struct ctx *ctx) +{ + memmove(ctx->buf, ctx->buf_ptr, ctx->buf_len); + ctx->buf_ptr = ctx->buf; + + ssize_t ret = read(ctx->fd, ctx->buf_ptr + ctx->buf_len, + sizeof(ctx->buf) - ctx->buf_len); + if (ret < 0) { + return -errno; + } else if (ret == 0) { + return 0; + } + + ctx->buf_len += ret; + assert(ctx->buf_len <= sizeof(ctx->buf)); + + return ret; +} + +static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx) +{ + if (!ctx->initialized) { + int ret = initCtx(uid, appProcessPid, ctx); + if (ret < 0) { + return ret; + } + } + + char *eptr; + while ((eptr = (char *)memchr(ctx->buf_ptr, '\n', ctx->buf_len)) == NULL) { + int ret = refillBuffer(ctx); + if (ret == 0) { + return -ERANGE; + } + if (ret < 0) { + return ret; + } + } + + *eptr = '\0'; + char *pid_eptr = NULL; + errno = 0; + long pid = strtol(ctx->buf_ptr, &pid_eptr, 10); + if (errno != 0) { + return -errno; + } + if (pid_eptr != eptr) { + return -EINVAL; + } + + ctx->buf_ptr = eptr + 1; + + return (pid_t)pid; +} + +static int removeProcessGroup(uid_t uid, int pid) +{ + int ret; + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + + convertUidPidToPath(path, sizeof(path), uid, pid); + ret = rmdir(path); + + convertUidToPath(path, sizeof(path), uid); + rmdir(path); + + return ret; +} + +static void removeUidProcessGroups(const char *uid_path) +{ + DIR *uid = opendir(uid_path); + if (uid != NULL) { + struct dirent cur; + struct dirent *dir; + while ((readdir_r(uid, &cur, &dir) == 0) && dir) { + char path[PROCESSGROUP_MAX_PATH_LEN]; + + if (dir->d_type != DT_DIR) { + continue; + } + + if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) { + continue; + } + + snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name); + SLOGV("removing %s\n", path); + rmdir(path); + } + } +} + +void removeAllProcessGroups() +{ + SLOGV("removeAllProcessGroups()"); + DIR *root = opendir(PROCESSGROUP_CGROUP_PATH); + if (root == NULL) { + SLOGE("failed to open %s: %s", PROCESSGROUP_CGROUP_PATH, strerror(errno)); + } + if (root != NULL) { + struct dirent cur; + struct dirent *dir; + while ((readdir_r(root, &cur, &dir) == 0) && dir) { + char path[PROCESSGROUP_MAX_PATH_LEN]; + + if (dir->d_type != DT_DIR) { + continue; + } + if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) { + continue; + } + + snprintf(path, sizeof(path), "%s/%s", PROCESSGROUP_CGROUP_PATH, dir->d_name); + removeUidProcessGroups(path); + SLOGV("removing %s\n", path); + rmdir(path); + } + } +} + +static int killProcessGroupOnce(uid_t uid, int initialPid, int signal) +{ + int processes = 0; + struct ctx ctx; + pid_t pid; + + ctx.initialized = false; + + while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) { + processes++; + SLOGV("sending processgroup kill to pid %d\n", pid); + int ret = kill(pid, signal); + if (ret == -1) { + SLOGV("failed to kill pid %d: %s", pid, strerror(errno)); + } + } + + if (ctx.initialized) { + close(ctx.fd); + } + + return processes; +} + +int killProcessGroup(uid_t uid, int initialPid, int signal) +{ + int processes; + int sleep_us = 100; + + while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) { + SLOGV("killed %d processes for processgroup %d\n", processes, initialPid); + if (sleep_us < 128000) { + usleep(sleep_us); + sleep_us *= 2; + } else { + SLOGE("failed to kill %d processes for processgroup %d\n", + processes, initialPid); + break; + } + } + + if (processes == 0) { + return removeProcessGroup(uid, initialPid); + } else { + return -1; + } +} + +static int mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + + ret = mkdir(path, 0750); + if (ret < 0 && errno != EEXIST) { + return -errno; + } + + ret = chown(path, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + ret = -errno; + rmdir(path); + return ret; + } + + return 0; +} + +int createProcessGroup(uid_t uid, int initialPid) +{ + char path[PROCESSGROUP_MAX_PATH_LEN] = {0}; + int ret; + + convertUidToPath(path, sizeof(path), uid); + + ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + SLOGE("failed to make and chown %s: %s", path, strerror(-ret)); + return ret; + } + + convertUidPidToPath(path, sizeof(path), uid, initialPid); + + ret = mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM); + if (ret < 0) { + SLOGE("failed to make and chown %s: %s", path, strerror(-ret)); + return ret; + } + + strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path)); + + int fd = open(path, O_WRONLY); + if (fd < 0) { + ret = -errno; + SLOGE("failed to open %s: %s", path, strerror(errno)); + return ret; + } + + char pid[PROCESSGROUP_MAX_PID_LEN + 1] = {0}; + int len = snprintf(pid, sizeof(pid), "%d", initialPid); + + ret = write(fd, pid, len); + if (ret < 0) { + ret = -errno; + SLOGE("failed to write '%s' to %s: %s", pid, path, strerror(errno)); + } else { + ret = 0; + } + + close(fd); + return ret; +} + |