1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/types.h>
27 #include <sys/mount.h>
31 #include "cgroup-util.h"
34 #include "path-util.h"
36 int cgroup_bonding_realize(CGroupBonding *b) {
41 assert(b->controller);
43 r = cg_create(b->controller, b->path, NULL);
45 log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
54 int cgroup_bonding_realize_list(CGroupBonding *first) {
58 LIST_FOREACH(by_unit, b, first)
59 if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
65 void cgroup_bonding_free(CGroupBonding *b, bool trim) {
71 LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
73 if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
74 assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
75 LIST_REMOVE(CGroupBonding, by_path, f, b);
78 hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
80 hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
84 if (b->realized && b->ours && trim)
85 cg_trim(b->controller, b->path, false);
92 void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
95 LIST_FOREACH_SAFE(by_unit, b, n, first)
96 cgroup_bonding_free(b, remove_or_trim);
99 void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
102 if (b->realized && b->ours)
103 cg_trim(b->controller, b->path, delete_root);
106 void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
109 LIST_FOREACH(by_unit, b, first)
110 cgroup_bonding_trim(b, delete_root);
113 int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
114 _cleanup_free_ char *p = NULL;
122 p = strjoin(b->path, "/", cgroup_suffix, NULL);
130 r = cg_create_and_attach(b->controller, path, pid);
138 int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
142 LIST_FOREACH(by_unit, b, first) {
143 r = cgroup_bonding_install(b, pid, cgroup_suffix);
144 if (r < 0 && b->essential)
151 int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
155 LIST_FOREACH(by_unit, q, list) {
164 r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
165 if (r < 0 && ret == 0)
172 int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
176 return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
179 int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
185 return cg_set_group_access(b->controller, b->path, mode, uid, gid);
188 int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
192 LIST_FOREACH(by_unit, b, first) {
193 r = cgroup_bonding_set_group_access(b, mode, uid, gid);
201 int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
207 return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
210 int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
214 LIST_FOREACH(by_unit, b, first) {
215 r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
223 int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
231 /* Don't kill cgroups that aren't ours */
236 p = strjoin(b->path, "/", cgroup_suffix, NULL);
244 r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
250 int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
252 Set *allocated_set = NULL;
253 int ret = -EAGAIN, r;
259 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
262 LIST_FOREACH(by_unit, b, first) {
263 r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
265 if (r == -EAGAIN || r == -ESRCH)
272 if (ret < 0 || r > 0)
278 set_free(allocated_set);
283 /* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
285 int cgroup_bonding_is_empty(CGroupBonding *b) {
290 if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
293 /* If it is empty it is empty */
297 /* It's not only us using this cgroup, so we just don't know */
298 return b->ours ? 0 : -EAGAIN;
301 int cgroup_bonding_is_empty_list(CGroupBonding *first) {
304 LIST_FOREACH(by_unit, b, first) {
307 if ((r = cgroup_bonding_is_empty(b)) < 0) {
308 /* If this returned -EAGAIN, then we don't know if the
309 * group is empty, so let's see if another group can
321 int manager_setup_cgroup(Manager *m) {
322 _cleanup_free_ char *current = NULL, *path = NULL;
324 char suffix[sizeof("/systemd-") + DECIMAL_STR_MAX(pid_t)];
328 /* 0. Be nice to Ingo Molnar #628004 */
329 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
330 log_warning("No control group support available, not creating root group.");
334 /* 1. Determine hierarchy */
335 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, ¤t);
337 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
341 if (m->running_as == SYSTEMD_SYSTEM)
342 strcpy(suffix, "/system");
344 snprintf(suffix, sizeof(suffix), "/systemd-%lu", (unsigned long) getpid());
345 char_array_0(suffix);
348 free(m->cgroup_hierarchy);
349 if (endswith(current, suffix)) {
350 /* We probably got reexecuted and can continue to use our root cgroup */
351 m->cgroup_hierarchy = current;
355 /* We need a new root cgroup */
356 m->cgroup_hierarchy = NULL;
357 if (asprintf(&m->cgroup_hierarchy, "%s%s", streq(current, "/") ? "" : current, suffix) < 0)
362 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, NULL, &path);
364 log_error("Cannot find cgroup mount point: %s", strerror(-r));
368 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
370 /* 3. Install agent */
371 if (m->running_as == SYSTEMD_SYSTEM) {
372 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
374 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
376 log_debug("Installed release agent.");
378 log_debug("Release agent already installed.");
381 /* 4. Realize the group */
382 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, 0);
384 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
388 /* 5. And pin it, so that it cannot be unmounted */
389 if (m->pin_cgroupfs_fd >= 0)
390 close_nointr_nofail(m->pin_cgroupfs_fd);
392 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
394 log_error("Failed to open pin file: %m");
398 /* 6. Remove non-existing controllers from the default controllers list */
399 cg_shorten_controllers(m->default_controllers);
401 /* 7. Let's create the user and machine hierarchies
402 * right-away, so that people can inotify on them, if they
403 * wish, without this being racy. */
404 if (m->running_as == SYSTEMD_SYSTEM) {
405 cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../user");
406 cg_create(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy, "../machine");
412 void manager_shutdown_cgroup(Manager *m, bool delete) {
415 if (delete && m->cgroup_hierarchy)
416 cg_delete(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_hierarchy);
418 if (m->pin_cgroupfs_fd >= 0) {
419 close_nointr_nofail(m->pin_cgroupfs_fd);
420 m->pin_cgroupfs_fd = -1;
423 free(m->cgroup_hierarchy);
424 m->cgroup_hierarchy = NULL;
427 int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
435 b = hashmap_get(m->cgroup_bondings, cgroup);
456 b = hashmap_get(m->cgroup_bondings, p);
464 int cgroup_notify_empty(Manager *m, const char *group) {
465 CGroupBonding *l, *b;
471 r = cgroup_bonding_get(m, group, &l);
475 LIST_FOREACH(by_path, b, l) {
481 t = cgroup_bonding_is_empty_list(b);
484 /* If we don't know, we don't know */
486 log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
492 /* If it is empty, let's delete it */
493 cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
495 if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
496 UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
503 Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
504 CGroupBonding *l, *b;
512 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
515 l = hashmap_get(m->cgroup_bondings, group);
520 while ((slash = strrchr(group, '/'))) {
526 if ((l = hashmap_get(m->cgroup_bondings, group)))
533 LIST_FOREACH(by_path, b, l) {
545 CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
549 controller = SYSTEMD_CGROUP_CONTROLLER;
551 LIST_FOREACH(by_unit, b, first)
552 if (streq(b->controller, controller))
558 char *cgroup_bonding_to_string(CGroupBonding *b) {
563 if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
569 pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
571 pid_t pid = 0, npid, mypid;
578 if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
583 while (cg_read_pid(f, &npid) > 0) {
589 /* Ignore processes that aren't our kids */
590 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
594 /* Dang, there's more than one daemonized PID
595 in this group, so we don't know what process
596 is the main process. */
609 pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
613 /* Try to find a main pid from this cgroup, but checking if
614 * there's only one PID in the cgroup and returning it. Later
615 * on we might want to add additional, smarter heuristics
618 LIST_FOREACH(by_unit, b, first)
619 if ((pid = cgroup_bonding_search_main_pid(b)) != 0)