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); }
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; }
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); }
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; }
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); }
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 = ≀ } 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; }
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; }