diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk new file mode 100644 index 000000000..501321f30 --- /dev/null +++ b/libprocessgroup/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := processgroup.cpp +LOCAL_MODULE := libprocessgroup +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Wall -Werror +LOCAL_REQUIRED_MODULE := processgroup_cleanup +include external/libcxx/libcxx.mk +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := cleanup.cpp +LOCAL_MODULE := processgroup_cleanup +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := -Wall -Werror +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libc libcutils +include $(BUILD_EXECUTABLE) diff --git a/libprocessgroup/cleanup.cpp b/libprocessgroup/cleanup.cpp new file mode 100644 index 000000000..cca8dc428 --- /dev/null +++ b/libprocessgroup/cleanup.cpp @@ -0,0 +1,31 @@ +/* + * 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. + */ +#include +#include +#include + +#include "processgroup_priv.h" + +int main(int argc, char **argv) +{ + char buf[PATH_MAX]; + if (argc != 2) + return -1; + + memcpy(buf, PROCESSGROUP_CGROUP_PATH, sizeof(PROCESSGROUP_CGROUP_PATH)); + strlcat(buf, argv[1], sizeof(buf)); + return rmdir(buf); +} diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h new file mode 100644 index 000000000..11bd8ccdf --- /dev/null +++ b/libprocessgroup/include/processgroup/processgroup.h @@ -0,0 +1,33 @@ +/* + * 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. + */ + +#ifndef _PROCESSGROUP_H_ +#define _PROCESSGROUP_H_ + +#include +#include + +__BEGIN_DECLS + +int killProcessGroup(uid_t uid, int initialPid, int signal); + +int createProcessGroup(uid_t uid, int initialPid); + +void removeAllProcessGroups(void); + +__END_DECLS + +#endif diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp new file mode 100644 index 000000000..c32e7413a --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#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; +} + diff --git a/libprocessgroup/processgroup_priv.h b/libprocessgroup/processgroup_priv.h new file mode 100644 index 000000000..1895bf9d5 --- /dev/null +++ b/libprocessgroup/processgroup_priv.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef _PROCESSGROUP_PRIV_H_ +#define _PROCESSGROUP_PRIV_H_ + +#define PROCESSGROUP_CGROUP_PATH "/acct" +#define PROCESSGROUP_UID_PREFIX "uid_" +#define PROCESSGROUP_PID_PREFIX "pid_" +#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs" +#define PROCESSGROUP_MAX_UID_LEN 11 +#define PROCESSGROUP_MAX_PID_LEN 11 +#define PROCESSGROUP_MAX_PATH_LEN \ + (sizeof(PROCESSGROUP_CGROUP_PATH) + \ + sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \ + PROCESSGROUP_MAX_UID_LEN + \ + sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \ + PROCESSGROUP_MAX_PID_LEN + \ + sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \ + 1) + +#endif