1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 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 "path-util.h"
27 #include "cgroup-util.h"
30 #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC)
32 void cgroup_context_init(CGroupContext *c) {
35 /* Initialize everything to the kernel defaults, assuming the
36 * structure is preinitialized to 0 */
38 c->cpu_shares = (unsigned long) -1;
39 c->startup_cpu_shares = (unsigned long) -1;
40 c->memory_limit = (uint64_t) -1;
41 c->blockio_weight = (unsigned long) -1;
42 c->startup_blockio_weight = (unsigned long) -1;
44 c->cpu_quota_per_sec_usec = USEC_INFINITY;
47 void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
51 LIST_REMOVE(device_allow, c->device_allow, a);
56 void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
60 LIST_REMOVE(device_weights, c->blockio_device_weights, w);
65 void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
69 LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b);
74 void cgroup_context_done(CGroupContext *c) {
77 while (c->blockio_device_weights)
78 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
80 while (c->blockio_device_bandwidths)
81 cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths);
83 while (c->device_allow)
84 cgroup_context_free_device_allow(c, c->device_allow);
87 void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
88 CGroupBlockIODeviceBandwidth *b;
89 CGroupBlockIODeviceWeight *w;
91 char u[FORMAT_TIMESPAN_MAX];
96 prefix = strempty(prefix);
99 "%sCPUAccounting=%s\n"
100 "%sBlockIOAccounting=%s\n"
101 "%sMemoryAccounting=%s\n"
103 "%sStartupCPUShares=%lu\n"
104 "%sCPUQuotaPerSecSec=%s\n"
105 "%sBlockIOWeight=%lu\n"
106 "%sStartupBlockIOWeight=%lu\n"
107 "%sMemoryLimit=%" PRIu64 "\n"
108 "%sDevicePolicy=%s\n"
110 prefix, yes_no(c->cpu_accounting),
111 prefix, yes_no(c->blockio_accounting),
112 prefix, yes_no(c->memory_accounting),
113 prefix, c->cpu_shares,
114 prefix, c->startup_cpu_shares,
115 prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1),
116 prefix, c->blockio_weight,
117 prefix, c->startup_blockio_weight,
118 prefix, c->memory_limit,
119 prefix, cgroup_device_policy_to_string(c->device_policy),
120 prefix, yes_no(c->delegate));
122 LIST_FOREACH(device_allow, a, c->device_allow)
124 "%sDeviceAllow=%s %s%s%s\n",
127 a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
129 LIST_FOREACH(device_weights, w, c->blockio_device_weights)
131 "%sBlockIODeviceWeight=%s %lu",
136 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
137 char buf[FORMAT_BYTES_MAX];
142 b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth",
144 format_bytes(buf, sizeof(buf), b->bandwidth));
148 static int lookup_blkio_device(const char *p, dev_t *dev) {
157 log_warning("Couldn't stat device %s: %m", p);
161 if (S_ISBLK(st.st_mode))
163 else if (major(st.st_dev) != 0) {
164 /* If this is not a device node then find the block
165 * device this file is stored on */
168 /* If this is a partition, try to get the originating
170 block_get_whole_disk(*dev, dev);
172 log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
179 static int whitelist_device(const char *path, const char *node, const char *acc) {
180 char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
187 if (stat(node, &st) < 0) {
188 log_warning("Couldn't stat device %s", node);
192 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
193 log_warning("%s is not a device.", node);
199 S_ISCHR(st.st_mode) ? 'c' : 'b',
200 major(st.st_rdev), minor(st.st_rdev),
203 r = cg_set_attribute("devices", path, "devices.allow", buf);
205 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set devices.allow on %s: %s", path, strerror(-r));
210 static int whitelist_major(const char *path, const char *name, char type, const char *acc) {
211 _cleanup_fclose_ FILE *f = NULL;
218 assert(type == 'b' || type == 'c');
220 f = fopen("/proc/devices", "re");
222 log_warning("Cannot open /proc/devices to resolve %s (%c): %m", name, type);
226 FOREACH_LINE(line, f, goto fail) {
227 char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w;
232 if (type == 'c' && streq(line, "Character devices:")) {
237 if (type == 'b' && streq(line, "Block devices:")) {
252 w = strpbrk(p, WHITESPACE);
257 r = safe_atou(p, &maj);
264 w += strspn(w, WHITESPACE);
266 if (fnmatch(name, w, 0) != 0)
275 r = cg_set_attribute("devices", path, "devices.allow", buf);
277 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set devices.allow on %s: %s", path, strerror(-r));
283 log_warning("Failed to read /proc/devices: %m");
287 void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state) {
297 /* Some cgroup attributes are not support on the root cgroup,
298 * hence silently ignore */
299 is_root = isempty(path) || path_equal(path, "/");
301 if ((mask & CGROUP_CPU) && !is_root) {
302 char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1];
304 sprintf(buf, "%lu\n",
305 IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares :
306 c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024);
307 r = cg_set_attribute("cpu", path, "cpu.shares", buf);
309 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set cpu.shares on %s: %s", path, strerror(-r));
311 sprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC);
312 r = cg_set_attribute("cpu", path, "cpu.cfs_period_us", buf);
314 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set cpu.cfs_period_us on %s: %s", path, strerror(-r));
316 if (c->cpu_quota_per_sec_usec != USEC_INFINITY) {
317 sprintf(buf, USEC_FMT "\n", c->cpu_quota_per_sec_usec * CGROUP_CPU_QUOTA_PERIOD_USEC / USEC_PER_SEC);
318 r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", buf);
320 r = cg_set_attribute("cpu", path, "cpu.cfs_quota_us", "-1");
322 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set cpu.cfs_quota_us on %s: %s", path, strerror(-r));
325 if (mask & CGROUP_BLKIO) {
326 char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
327 DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
328 DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
329 CGroupBlockIODeviceWeight *w;
330 CGroupBlockIODeviceBandwidth *b;
333 sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight :
334 c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000);
335 r = cg_set_attribute("blkio", path, "blkio.weight", buf);
337 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set blkio.weight on %s: %s", path, strerror(-r));
339 /* FIXME: no way to reset this list */
340 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
343 r = lookup_blkio_device(w->path, &dev);
347 sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight);
348 r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
350 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set blkio.weight_device on %s: %s", path, strerror(-r));
354 /* FIXME: no way to reset this list */
355 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
359 r = lookup_blkio_device(b->path, &dev);
363 a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device";
365 sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
366 r = cg_set_attribute("blkio", path, a, buf);
368 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set %s on %s: %s", a, path, strerror(-r));
372 if (mask & CGROUP_MEMORY) {
373 if (c->memory_limit != (uint64_t) -1) {
374 char buf[DECIMAL_STR_MAX(uint64_t) + 1];
376 sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
377 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
379 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
382 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
385 if ((mask & CGROUP_DEVICE) && !is_root) {
386 CGroupDeviceAllow *a;
388 if (c->device_allow || c->device_policy != CGROUP_AUTO)
389 r = cg_set_attribute("devices", path, "devices.deny", "a");
391 r = cg_set_attribute("devices", path, "devices.allow", "a");
393 log_full(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to reset devices.list on %s: %s", path, strerror(-r));
395 if (c->device_policy == CGROUP_CLOSED ||
396 (c->device_policy == CGROUP_AUTO && c->device_allow)) {
397 static const char auto_devices[] =
398 "/dev/null\0" "rwm\0"
399 "/dev/zero\0" "rwm\0"
400 "/dev/full\0" "rwm\0"
401 "/dev/random\0" "rwm\0"
402 "/dev/urandom\0" "rwm\0"
404 "/dev/pts/ptmx\0" "rw\0"; /* /dev/pts/ptmx may not be duplicated, but accessed */
408 NULSTR_FOREACH_PAIR(x, y, auto_devices)
409 whitelist_device(path, x, y);
411 whitelist_major(path, "pts", 'c', "rw");
412 whitelist_major(path, "kdbus", 'c', "rw");
413 whitelist_major(path, "kdbus/*", 'c', "rw");
416 LIST_FOREACH(device_allow, a, c->device_allow) {
432 if (startswith(a->path, "/dev/"))
433 whitelist_device(path, a->path, acc);
434 else if (startswith(a->path, "block-"))
435 whitelist_major(path, a->path + 6, 'b', acc);
436 else if (startswith(a->path, "char-"))
437 whitelist_major(path, a->path + 5, 'c', acc);
439 log_debug("Ignoring device %s while writing cgroup attribute.", a->path);
444 CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
445 CGroupControllerMask mask = 0;
447 /* Figure out which controllers we need */
449 if (c->cpu_accounting ||
450 c->cpu_shares != (unsigned long) -1 ||
451 c->startup_cpu_shares != (unsigned long) -1 ||
452 c->cpu_quota_per_sec_usec != USEC_INFINITY)
453 mask |= CGROUP_CPUACCT | CGROUP_CPU;
455 if (c->blockio_accounting ||
456 c->blockio_weight != (unsigned long) -1 ||
457 c->startup_blockio_weight != (unsigned long) -1 ||
458 c->blockio_device_weights ||
459 c->blockio_device_bandwidths)
460 mask |= CGROUP_BLKIO;
462 if (c->memory_accounting ||
463 c->memory_limit != (uint64_t) -1)
464 mask |= CGROUP_MEMORY;
466 if (c->device_allow ||
467 c->device_policy != CGROUP_AUTO)
468 mask |= CGROUP_DEVICE;
473 CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
476 c = unit_get_cgroup_context(u);
480 /* If delegation is turned on, then turn on all cgroups,
481 * unless the process we fork into it is known to drop
482 * privileges anyway, and shouldn't get access to the
483 * controllers anyway. */
488 e = unit_get_exec_context(u);
489 if (!e || exec_context_maintains_privileges(e))
490 return _CGROUP_CONTROLLER_MASK_ALL;
493 return cgroup_context_get_mask(c);
496 CGroupControllerMask unit_get_members_mask(Unit *u) {
499 if (u->cgroup_members_mask_valid)
500 return u->cgroup_members_mask;
502 u->cgroup_members_mask = 0;
504 if (u->type == UNIT_SLICE) {
508 SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) {
513 if (UNIT_DEREF(member->slice) != u)
516 u->cgroup_members_mask |=
517 unit_get_cgroup_mask(member) |
518 unit_get_members_mask(member);
522 u->cgroup_members_mask_valid = true;
523 return u->cgroup_members_mask;
526 CGroupControllerMask unit_get_siblings_mask(Unit *u) {
529 if (UNIT_ISSET(u->slice))
530 return unit_get_members_mask(UNIT_DEREF(u->slice));
532 return unit_get_cgroup_mask(u) | unit_get_members_mask(u);
535 CGroupControllerMask unit_get_target_mask(Unit *u) {
536 CGroupControllerMask mask;
538 mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
539 mask &= u->manager->cgroup_supported;
544 /* Recurse from a unit up through its containing slices, propagating
545 * mask bits upward. A unit is also member of itself. */
546 void unit_update_cgroup_members_masks(Unit *u) {
547 CGroupControllerMask m;
552 /* Calculate subtree mask */
553 m = unit_get_cgroup_mask(u) | unit_get_members_mask(u);
555 /* See if anything changed from the previous invocation. If
556 * not, we're done. */
557 if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask)
561 u->cgroup_subtree_mask_valid &&
562 ((m & ~u->cgroup_subtree_mask) != 0) &&
563 ((~m & u->cgroup_subtree_mask) == 0);
565 u->cgroup_subtree_mask = m;
566 u->cgroup_subtree_mask_valid = true;
568 if (UNIT_ISSET(u->slice)) {
569 Unit *s = UNIT_DEREF(u->slice);
572 /* There's more set now than before. We
573 * propagate the new mask to the parent's mask
574 * (not caring if it actually was valid or
577 s->cgroup_members_mask |= m;
580 /* There's less set now than before (or we
581 * don't know), we need to recalculate
582 * everything, so let's invalidate the
583 * parent's members mask */
585 s->cgroup_members_mask_valid = false;
587 /* And now make sure that this change also hits our
589 unit_update_cgroup_members_masks(s);
593 static const char *migrate_callback(CGroupControllerMask mask, void *userdata) {
600 if (u->cgroup_path &&
601 u->cgroup_realized &&
602 (u->cgroup_realized_mask & mask) == mask)
603 return u->cgroup_path;
605 u = UNIT_DEREF(u->slice);
611 static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
612 _cleanup_free_ char *path = NULL;
617 path = unit_default_cgroup_path(u);
621 r = hashmap_put(u->manager->cgroup_unit, path, u);
623 log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r));
627 u->cgroup_path = path;
631 /* First, create our own group */
632 r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path);
634 log_error("Failed to create cgroup %s: %s", u->cgroup_path, strerror(-r));
638 /* Keep track that this is now realized */
639 u->cgroup_realized = true;
640 u->cgroup_realized_mask = mask;
642 /* Then, possibly move things over */
643 r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u);
645 log_warning("Failed to migrate cgroup from to %s: %s", u->cgroup_path, strerror(-r));
650 static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
653 return u->cgroup_realized && u->cgroup_realized_mask == mask;
656 /* Check if necessary controllers and attributes for a unit are in place.
659 * If not, create paths, move processes over, and set attributes.
661 * Returns 0 on success and < 0 on failure. */
662 static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
663 CGroupControllerMask mask;
668 if (u->in_cgroup_queue) {
669 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
670 u->in_cgroup_queue = false;
673 mask = unit_get_target_mask(u);
675 if (unit_has_mask_realized(u, mask))
678 /* First, realize parents */
679 if (UNIT_ISSET(u->slice)) {
680 r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state);
685 /* And then do the real work */
686 r = unit_create_cgroups(u, mask);
690 /* Finally, apply the necessary attributes. */
691 cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path, state);
696 static void unit_add_to_cgroup_queue(Unit *u) {
698 if (u->in_cgroup_queue)
701 LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u);
702 u->in_cgroup_queue = true;
705 unsigned manager_dispatch_cgroup_queue(Manager *m) {
711 state = manager_state(m);
713 while ((i = m->cgroup_queue)) {
714 assert(i->in_cgroup_queue);
716 r = unit_realize_cgroup_now(i, state);
718 log_warning("Failed to realize cgroups for queued unit %s: %s", i->id, strerror(-r));
726 static void unit_queue_siblings(Unit *u) {
729 /* This adds the siblings of the specified unit and the
730 * siblings of all parent units to the cgroup queue. (But
731 * neither the specified unit itself nor the parents.) */
733 while ((slice = UNIT_DEREF(u->slice))) {
737 SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
741 /* Skip units that have a dependency on the slice
742 * but aren't actually in it. */
743 if (UNIT_DEREF(m->slice) != slice)
746 /* No point in doing cgroup application for units
747 * without active processes. */
748 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
751 /* If the unit doesn't need any new controllers
752 * and has current ones realized, it doesn't need
754 if (unit_has_mask_realized(m, unit_get_target_mask(m)))
757 unit_add_to_cgroup_queue(m);
764 int unit_realize_cgroup(Unit *u) {
769 c = unit_get_cgroup_context(u);
773 /* So, here's the deal: when realizing the cgroups for this
774 * unit, we need to first create all parents, but there's more
775 * actually: for the weight-based controllers we also need to
776 * make sure that all our siblings (i.e. units that are in the
777 * same slice as we are) have cgroups, too. Otherwise, things
778 * would become very uneven as each of their processes would
779 * get as much resources as all our group together. This call
780 * will synchronously create the parent cgroups, but will
781 * defer work on the siblings to the next event loop
784 /* Add all sibling slices to the cgroup queue. */
785 unit_queue_siblings(u);
787 /* And realize this one now (and apply the values) */
788 return unit_realize_cgroup_now(u, manager_state(u->manager));
791 void unit_destroy_cgroup(Unit *u) {
799 r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
801 log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
803 hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
805 free(u->cgroup_path);
806 u->cgroup_path = NULL;
807 u->cgroup_realized = false;
808 u->cgroup_realized_mask = 0;
812 pid_t unit_search_main_pid(Unit *u) {
813 _cleanup_fclose_ FILE *f = NULL;
814 pid_t pid = 0, npid, mypid;
821 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
825 while (cg_read_pid(f, &npid) > 0) {
831 /* Ignore processes that aren't our kids */
832 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
836 /* Dang, there's more than one daemonized PID
837 in this group, so we don't know what process
838 is the main process. */
849 int manager_setup_cgroup(Manager *m) {
850 _cleanup_free_ char *path = NULL;
855 /* 1. Determine hierarchy */
856 free(m->cgroup_root);
857 m->cgroup_root = NULL;
859 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
861 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
865 /* LEGACY: Already in /system.slice? If so, let's cut this
866 * off. This is to support live upgrades from older systemd
867 * versions where PID 1 was moved there. */
868 if (m->running_as == SYSTEMD_SYSTEM) {
871 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
873 e = endswith(m->cgroup_root, "/system");
878 /* And make sure to store away the root value without trailing
879 * slash, even for the root dir, so that we can easily prepend
881 if (streq(m->cgroup_root, "/"))
882 m->cgroup_root[0] = 0;
885 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
887 log_error("Cannot find cgroup mount point: %s", strerror(-r));
891 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
894 /* 3. Install agent */
895 if (m->running_as == SYSTEMD_SYSTEM) {
896 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
898 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
900 log_debug("Installed release agent.");
902 log_debug("Release agent already installed.");
905 /* 4. Make sure we are in the root cgroup */
906 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
908 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
912 /* 5. And pin it, so that it cannot be unmounted */
913 safe_close(m->pin_cgroupfs_fd);
915 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
916 if (m->pin_cgroupfs_fd < 0) {
917 log_error("Failed to open pin file: %m");
921 /* 6. Always enable hierarchial support if it exists... */
922 cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
925 /* 7. Figure out which controllers are supported */
926 m->cgroup_supported = cg_mask_supported();
931 void manager_shutdown_cgroup(Manager *m, bool delete) {
934 /* We can't really delete the group, since we are in it. But
936 if (delete && m->cgroup_root)
937 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
939 m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd);
941 free(m->cgroup_root);
942 m->cgroup_root = NULL;
945 Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
952 u = hashmap_get(m->cgroup_unit, cgroup);
966 u = hashmap_get(m->cgroup_unit, p);
972 Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
973 _cleanup_free_ char *cgroup = NULL;
981 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
985 return manager_get_unit_by_cgroup(m, cgroup);
988 int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
995 u = manager_get_unit_by_cgroup(m, cgroup);
997 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
999 if (UNIT_VTABLE(u)->notify_cgroup_empty)
1000 UNIT_VTABLE(u)->notify_cgroup_empty(u);
1002 unit_add_to_gc_queue(u);
1009 static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
1010 [CGROUP_AUTO] = "auto",
1011 [CGROUP_CLOSED] = "closed",
1012 [CGROUP_STRICT] = "strict",
1015 DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);