static void test_mask_supported(void) { CGroupMask m; CGroupController c; assert_se(cg_mask_supported(&m) >= 0); for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) printf("'%s' is supported: %s\n", cgroup_controller_to_string(c), yes_no(m & CGROUP_CONTROLLER_TO_MASK(c))); }
int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) { _cleanup_free_ char *cgroup = NULL; CGroupMask supported; const char *payload; int r; assert(pid > 1); /* 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. * * Moreover, container payloads such as systemd try to manage the cgroup they run in in full (i.e. including * its attributes), while the host systemd will only delegate cgroups for children of the cgroup created for a * delegation unit, instead of the cgroup itself. This means, if we'd pass on the cgroup allocated from the * host systemd directly to the payload, the host and payload systemd might fight for the cgroup * attributes. Hence, let's insert an intermediary cgroup to cover that case too. * * Note that we only bother with the main hierarchy here, not with any secondary ones. On the unified setup * that's fine because there's only one hiearchy anyway and controllers are enabled directly on it. On the * legacy setup, this is fine too, since delegation of controllers is generally not safe there, hence we won't * do it. */ r = cg_mask_supported(&supported); if (r < 0) return log_error_errno(r, "Failed to determine supported controllers: %m"); if (keep_unit) r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); else r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); if (r < 0) return log_error_errno(r, "Failed to get our control group: %m"); payload = strjoina(cgroup, "/payload"); r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, payload, pid); if (r < 0) return log_error_errno(r, "Failed to create %s subcgroup: %m", payload); if (keep_unit) { const char *supervisor; supervisor = strjoina(cgroup, "/supervisor"); r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, supervisor, 0); if (r < 0) return log_error_errno(r, "Failed to create %s subcgroup: %m", supervisor); } /* Try to enable as many controllers as possible for the new payload. */ (void) cg_enable_everywhere(supported, supported, cgroup); return 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; }
static int condition_test_control_group_controller(Condition *c) { int r; CGroupMask system_mask, wanted_mask = 0; assert(c); assert(c->parameter); assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER); r = cg_mask_supported(&system_mask); if (r < 0) return log_debug_errno(r, "Failed to determine supported controllers: %m"); r = cg_mask_from_string(c->parameter, &wanted_mask); if (r < 0 || wanted_mask <= 0) { /* This won't catch the case that we have an unknown controller * mixed in with valid ones -- these are only assessed on the * validity of the valid controllers found. */ log_debug("Failed to parse cgroup string: %s", c->parameter); return 1; } return FLAGS_SET(system_mask, wanted_mask); }
int main(int argc, char *argv[]) { int r; Hashmap *a = NULL, *b = NULL; unsigned iteration = 0; usec_t last_refresh = 0; bool quit = false, immediate_refresh = false; _cleanup_free_ char *root = NULL; CGroupMask mask; log_parse_environment(); log_open(); r = cg_mask_supported(&mask); if (r < 0) { log_error_errno(r, "Failed to determine supported controllers: %m"); goto finish; } arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; r = parse_argv(argc, argv); if (r <= 0) goto finish; r = get_cgroup_root(&root); if (r < 0) { log_error_errno(r, "Failed to get root control group path: %m"); goto finish; } a = hashmap_new(&string_hash_ops); b = hashmap_new(&string_hash_ops); if (!a || !b) { r = log_oom(); goto finish; } signal(SIGWINCH, columns_lines_cache_reset); if (arg_iterations == (unsigned) -1) arg_iterations = on_tty() ? 0 : 1; while (!quit) { Hashmap *c; usec_t t; char key; char h[FORMAT_TIMESPAN_MAX]; t = now(CLOCK_MONOTONIC); if (t >= last_refresh + arg_delay || immediate_refresh) { r = refresh(root, a, b, iteration++); if (r < 0) { log_error_errno(r, "Failed to refresh: %m"); goto finish; } group_hashmap_clear(b); c = a; a = b; b = c; last_refresh = t; immediate_refresh = false; } display(b); if (arg_iterations && iteration >= arg_iterations) break; if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */ fputs("\n", stdout); fflush(stdout); if (arg_batch) (void) usleep(last_refresh + arg_delay - t); else { r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); if (r == -ETIMEDOUT) continue; if (r < 0) { log_error_errno(r, "Couldn't read key: %m"); goto finish; } } if (on_tty()) { /* TTY: Clear any user keystroke */ fputs("\r \r", stdout); fflush(stdout); } if (arg_batch) continue; switch (key) { case ' ': immediate_refresh = true; break; case 'q': quit = true; break; case 'p': arg_order = ORDER_PATH; break; case 't': arg_order = ORDER_TASKS; break; case 'c': arg_order = ORDER_CPU; break; case 'm': arg_order = ORDER_MEMORY; break; case 'i': arg_order = ORDER_IO; break; case '%': arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; break; case 'k': arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS; fprintf(stdout, "\nCounting: %s.", counting_what()); fflush(stdout); sleep(1); break; case 'P': arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS; fprintf(stdout, "\nCounting: %s.", counting_what()); fflush(stdout); sleep(1); break; case 'r': if (arg_count == COUNT_PIDS) fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode."); else { arg_recursive = !arg_recursive; fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive)); } fflush(stdout); sleep(1); break; case '+': if (arg_delay < USEC_PER_SEC) arg_delay += USEC_PER_MSEC*250; else arg_delay += USEC_PER_SEC; fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); fflush(stdout); sleep(1); break; case '-': if (arg_delay <= USEC_PER_MSEC*500) arg_delay = USEC_PER_MSEC*250; else if (arg_delay < USEC_PER_MSEC*1250) arg_delay -= USEC_PER_MSEC*250; else arg_delay -= USEC_PER_SEC; fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0)); fflush(stdout); sleep(1); break; case '?': case 'h': #define ON ANSI_HIGHLIGHT #define OFF ANSI_NORMAL fprintf(stdout, "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n" "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit"); fflush(stdout); sleep(3); break; default: if (key < ' ') fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); else fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); fflush(stdout); sleep(1); break; } } r = 0; finish: group_hashmap_free(a); group_hashmap_free(b); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }