Exemplo n.º 1
0
static void show_cg_info(const char *controller, const char *path) {

        if (cg_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER))
                printf("Controller %s; ", controller);

        printf("Control group %s:\n", isempty(path) ? "/" : path);
        fflush(stdout);
}
Exemplo n.º 2
0
int sync_cgroup(pid_t pid, CGroupUnified unified_requested) {
        _cleanup_free_ char *cgroup = NULL;
        char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
        bool undo_mount = false;
        const char *fn;
        int unified, r;

        unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
        if (unified < 0)
                return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");

        if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
                return 0;

        /* When the host uses the legacy cgroup setup, but the
         * container shall use the unified hierarchy, let's make sure
         * we copy the path from the name=systemd hierarchy into the
         * unified hierarchy. Similar for the reverse situation. */

        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
        if (r < 0)
                return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);

        /* In order to access the unified hierarchy we need to mount it */
        if (!mkdtemp(tree))
                return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");

        if (unified)
                r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
        else
                r = mount("cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
        if (r < 0) {
                r = log_error_errno(errno, "Failed to mount unified hierarchy: %m");
                goto finish;
        }

        undo_mount = true;

        fn = strjoina(tree, cgroup, "/cgroup.procs");
        (void) mkdir_parents(fn, 0755);

        sprintf(pid_string, PID_FMT, pid);
        r = write_string_file(fn, pid_string, 0);
        if (r < 0)
                log_error_errno(r, "Failed to move process: %m");

finish:
        if (undo_mount)
                (void) umount(tree);

        (void) rmdir(tree);
        return r;
}
Exemplo n.º 3
0
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {

        /* If we get a SIGCHLD event for one of the processes we were
           interested in, then we look for others to watch, under the
           assumption that we'll sooner or later get a SIGCHLD for
           them, as the original process we watched was probably the
           parent of them, and they are hence now our children. */

        unit_tidy_watch_pids(u, 0, 0);
        unit_watch_all_pids(u);

        /* If the PID set is empty now, then let's finish this off
           (On unified we use proper notifications) */
        if (cg_unified() <= 0 && set_isempty(u->pids))
                scope_notify_cgroup_empty_event(u);
}
Exemplo n.º 4
0
int create_subcgroup(pid_t pid, CGroupUnified unified_requested) {
        _cleanup_free_ char *cgroup = NULL;
        const char *child;
        int unified, r;
        CGroupMask supported;

        /* In the unified hierarchy inner nodes may only contain
         * subgroups, but not processes. Hence, if we running in the
         * unified hierarchy and the container does the same, and we
         * did not create a scope unit for the container move us and
         * the container into two separate subcgroups. */

        if (unified_requested == CGROUP_UNIFIED_NONE)
                return 0;

        unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
        if (unified < 0)
                return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");
        if (unified == 0)
                return 0;

        r = cg_mask_supported(&supported);
        if (r < 0)
                return log_error_errno(r, "Failed to determine supported controllers: %m");

        r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup);
        if (r < 0)
                return log_error_errno(r, "Failed to get our control group: %m");

        child = strjoina(cgroup, "/payload");
        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, pid);
        if (r < 0)
                return log_error_errno(r, "Failed to create %s subcgroup: %m", child);

        child = strjoina(cgroup, "/supervisor");
        r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, 0);
        if (r < 0)
                return log_error_errno(r, "Failed to create %s subcgroup: %m", child);

        /* Try to enable as many controllers as possible for the new payload. */
        (void) cg_enable_everywhere(supported, supported, cgroup);
        return 0;
}
Exemplo n.º 5
0
static void show_cg_info(const char *controller, const char *path) {
        if (cg_unified() <= 0)
                printf("Controller %s; ", controller);
        printf("Control group %s:\n", isempty(path) ? "/" : path);
        fflush(stdout);
}
Exemplo n.º 6
0
static int process(
                const char *controller,
                const char *path,
                Hashmap *a,
                Hashmap *b,
                unsigned iteration,
                Group **ret) {

        Group *g;
        int r;

        assert(controller);
        assert(path);
        assert(a);

        g = hashmap_get(a, path);
        if (!g) {
                g = hashmap_get(b, path);
                if (!g) {
                        g = new0(Group, 1);
                        if (!g)
                                return -ENOMEM;

                        g->path = strdup(path);
                        if (!g->path) {
                                group_free(g);
                                return -ENOMEM;
                        }

                        r = hashmap_put(a, g->path, g);
                        if (r < 0) {
                                group_free(g);
                                return r;
                        }
                } else {
                        r = hashmap_move_one(a, b, path);
                        if (r < 0)
                                return r;

                        g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
                }
        }

        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) {
                _cleanup_fclose_ FILE *f = NULL;
                pid_t pid;

                r = cg_enumerate_processes(controller, path, &f);
                if (r == -ENOENT)
                        return 0;
                if (r < 0)
                        return r;

                g->n_tasks = 0;
                while (cg_read_pid(f, &pid) > 0) {

                        if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0)
                                continue;

                        g->n_tasks++;
                }

                if (g->n_tasks > 0)
                        g->n_tasks_valid = true;

        } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) {
                _cleanup_free_ char *p = NULL, *v = NULL;

                r = cg_get_path(controller, path, "pids.current", &p);
                if (r < 0)
                        return r;

                r = read_one_line_file(p, &v);
                if (r == -ENOENT)
                        return 0;
                if (r < 0)
                        return r;

                r = safe_atou64(v, &g->n_tasks);
                if (r < 0)
                        return r;

                if (g->n_tasks > 0)
                        g->n_tasks_valid = true;

        } else if (streq(controller, "cpuacct") && cg_unified() <= 0) {
                _cleanup_free_ char *p = NULL, *v = NULL;
                uint64_t new_usage;
                nsec_t timestamp;

                r = cg_get_path(controller, path, "cpuacct.usage", &p);
                if (r < 0)
                        return r;

                r = read_one_line_file(p, &v);
                if (r == -ENOENT)
                        return 0;
                if (r < 0)
                        return r;

                r = safe_atou64(v, &new_usage);
                if (r < 0)
                        return r;

                timestamp = now_nsec(CLOCK_MONOTONIC);

                if (g->cpu_iteration == iteration - 1 &&
                    (nsec_t) new_usage > g->cpu_usage) {

                        nsec_t x, y;

                        x = timestamp - g->cpu_timestamp;
                        if (x < 1)
                                x = 1;

                        y = (nsec_t) new_usage - g->cpu_usage;
                        g->cpu_fraction = (double) y / (double) x;
                        g->cpu_valid = true;
                }

                g->cpu_usage = (nsec_t) new_usage;
                g->cpu_timestamp = timestamp;
                g->cpu_iteration = iteration;

        } else if (streq(controller, "memory")) {
                _cleanup_free_ char *p = NULL, *v = NULL;

                if (cg_unified() <= 0)
                        r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
                else
                        r = cg_get_path(controller, path, "memory.current", &p);
                if (r < 0)
                        return r;

                r = read_one_line_file(p, &v);
                if (r == -ENOENT)
                        return 0;
                if (r < 0)
                        return r;

                r = safe_atou64(v, &g->memory);
                if (r < 0)
                        return r;

                if (g->memory > 0)
                        g->memory_valid = true;

        } else if (streq(controller, "blkio") && cg_unified() <= 0) {
                _cleanup_fclose_ FILE *f = NULL;
                _cleanup_free_ char *p = NULL;
                uint64_t wr = 0, rd = 0;
                nsec_t timestamp;

                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
                if (r < 0)
                        return r;

                f = fopen(p, "re");
                if (!f) {
                        if (errno == ENOENT)
                                return 0;
                        return -errno;
                }

                for (;;) {
                        char line[LINE_MAX], *l;
                        uint64_t k, *q;

                        if (!fgets(line, sizeof(line), f))
                                break;

                        l = strstrip(line);
                        l += strcspn(l, WHITESPACE);
                        l += strspn(l, WHITESPACE);

                        if (first_word(l, "Read")) {
                                l += 4;
                                q = &rd;
                        } else if (first_word(l, "Write")) {
                                l += 5;
                                q = &wr;
                        } else
                                continue;

                        l += strspn(l, WHITESPACE);
                        r = safe_atou64(l, &k);
                        if (r < 0)
                                continue;

                        *q += k;
                }

                timestamp = now_nsec(CLOCK_MONOTONIC);

                if (g->io_iteration == iteration - 1) {
                        uint64_t x, yr, yw;

                        x = (uint64_t) (timestamp - g->io_timestamp);
                        if (x < 1)
                                x = 1;

                        if (rd > g->io_input)
                                yr = rd - g->io_input;
                        else
                                yr = 0;

                        if (wr > g->io_output)
                                yw = wr - g->io_output;
                        else
                                yw = 0;

                        if (yr > 0 || yw > 0) {
                                g->io_input_bps = (yr * 1000000000ULL) / x;
                                g->io_output_bps = (yw * 1000000000ULL) / x;
                                g->io_valid = true;
                        }
                }

                g->io_input = rd;
                g->io_output = wr;
                g->io_timestamp = timestamp;
                g->io_iteration = iteration;
        }

        if (ret)
                *ret = g;

        return 0;
}
Exemplo n.º 7
0
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift) {
    _cleanup_free_ char *cgroup = NULL;
    char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1];
    bool undo_mount = false;
    const char *fn;
    int unified, r;

    unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
    if (unified < 0)
        return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m");

    if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD))
        return 0;

    /* When the host uses the legacy cgroup setup, but the
     * container shall use the unified hierarchy, let's make sure
     * we copy the path from the name=systemd hierarchy into the
     * unified hierarchy. Similar for the reverse situation. */

    r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
    if (r < 0)
        return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid);

    /* In order to access the unified hierarchy we need to mount it */
    if (!mkdtemp(tree))
        return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m");

    if (unified)
        r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup",
                          MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr");
    else
        r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup2",
                          MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
    if (r < 0)
        goto finish;

    undo_mount = true;

    /* If nspawn dies abruptly the cgroup hierarchy created below
     * its unit isn't cleaned up. So, let's remove it
     * https://github.com/systemd/systemd/pull/4223#issuecomment-252519810 */
    fn = strjoina(tree, cgroup);
    (void) rm_rf(fn, REMOVE_ROOT|REMOVE_ONLY_DIRECTORIES);

    fn = strjoina(tree, cgroup, "/cgroup.procs");
    (void) mkdir_parents(fn, 0755);

    sprintf(pid_string, PID_FMT, pid);
    r = write_string_file(fn, pid_string, 0);
    if (r < 0) {
        log_error_errno(r, "Failed to move process: %m");
        goto finish;
    }

    fn = strjoina(tree, cgroup);
    r = chown_cgroup_path(fn, arg_uid_shift);
    if (r < 0)
        log_error_errno(r, "Failed to chown() cgroup %s: %m", fn);
finish:
    if (undo_mount)
        (void) umount_verbose(tree);

    (void) rmdir(tree);
    return r;
}