Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf8d1c22 authored by Colin Cross's avatar Colin Cross
Browse files

Add libprocessgroup

libprocessgroup will allow Zygote to place processes
in a cgroup, and then kill the process and all forked processes
later.  lmkd will also kill all forked processes.

Bug: 15313911
Change-Id: I20404d0dbe448f1c475d2f4c61cccca462f9e544
parent 038975a5
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
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)
+31 −0
Original line number Diff line number Diff line
/*
 *  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 <string.h>
#include <unistd.h>
#include <sys/syslimits.h>

#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);
}
+33 −0
Original line number Diff line number Diff line
/*
 *  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 <sys/cdefs.h>
#include <sys/types.h>

__BEGIN_DECLS

int killProcessGroup(uid_t uid, int initialPid, int signal);

int createProcessGroup(uid_t uid, int initialPid);

void removeAllProcessGroups(void);

__END_DECLS

#endif
+316 −0
Original line number Diff line number Diff line
/*
 *  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;
}
+35 −0
Original line number Diff line number Diff line
/*
 *  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