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 return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path);
636 /* Keep track that this is now realized */
637 u->cgroup_realized = true;
638 u->cgroup_realized_mask = mask;
640 /* Then, possibly move things over */
641 r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u);
643 log_warning_errno(r, "Failed to migrate cgroup from to %s: %m", u->cgroup_path);
648 static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
651 return u->cgroup_realized && u->cgroup_realized_mask == mask;
654 /* Check if necessary controllers and attributes for a unit are in place.
657 * If not, create paths, move processes over, and set attributes.
659 * Returns 0 on success and < 0 on failure. */
660 static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
661 CGroupControllerMask mask;
666 if (u->in_cgroup_queue) {
667 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
668 u->in_cgroup_queue = false;
671 mask = unit_get_target_mask(u);
673 if (unit_has_mask_realized(u, mask))
676 /* First, realize parents */
677 if (UNIT_ISSET(u->slice)) {
678 r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state);
683 /* And then do the real work */
684 r = unit_create_cgroups(u, mask);
688 /* Finally, apply the necessary attributes. */
689 cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path, state);
694 static void unit_add_to_cgroup_queue(Unit *u) {
696 if (u->in_cgroup_queue)
699 LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u);
700 u->in_cgroup_queue = true;
703 unsigned manager_dispatch_cgroup_queue(Manager *m) {
709 state = manager_state(m);
711 while ((i = m->cgroup_queue)) {
712 assert(i->in_cgroup_queue);
714 r = unit_realize_cgroup_now(i, state);
716 log_warning_errno(r, "Failed to realize cgroups for queued unit %s: %m", i->id);
724 static void unit_queue_siblings(Unit *u) {
727 /* This adds the siblings of the specified unit and the
728 * siblings of all parent units to the cgroup queue. (But
729 * neither the specified unit itself nor the parents.) */
731 while ((slice = UNIT_DEREF(u->slice))) {
735 SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
739 /* Skip units that have a dependency on the slice
740 * but aren't actually in it. */
741 if (UNIT_DEREF(m->slice) != slice)
744 /* No point in doing cgroup application for units
745 * without active processes. */
746 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
749 /* If the unit doesn't need any new controllers
750 * and has current ones realized, it doesn't need
752 if (unit_has_mask_realized(m, unit_get_target_mask(m)))
755 unit_add_to_cgroup_queue(m);
762 int unit_realize_cgroup(Unit *u) {
767 c = unit_get_cgroup_context(u);
771 /* So, here's the deal: when realizing the cgroups for this
772 * unit, we need to first create all parents, but there's more
773 * actually: for the weight-based controllers we also need to
774 * make sure that all our siblings (i.e. units that are in the
775 * same slice as we are) have cgroups, too. Otherwise, things
776 * would become very uneven as each of their processes would
777 * get as much resources as all our group together. This call
778 * will synchronously create the parent cgroups, but will
779 * defer work on the siblings to the next event loop
782 /* Add all sibling slices to the cgroup queue. */
783 unit_queue_siblings(u);
785 /* And realize this one now (and apply the values) */
786 return unit_realize_cgroup_now(u, manager_state(u->manager));
789 void unit_destroy_cgroup(Unit *u) {
797 r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
799 log_debug_errno(r, "Failed to destroy cgroup %s: %m", u->cgroup_path);
801 hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
803 free(u->cgroup_path);
804 u->cgroup_path = NULL;
805 u->cgroup_realized = false;
806 u->cgroup_realized_mask = 0;
810 pid_t unit_search_main_pid(Unit *u) {
811 _cleanup_fclose_ FILE *f = NULL;
812 pid_t pid = 0, npid, mypid;
819 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
823 while (cg_read_pid(f, &npid) > 0) {
829 /* Ignore processes that aren't our kids */
830 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
834 /* Dang, there's more than one daemonized PID
835 in this group, so we don't know what process
836 is the main process. */
847 int manager_setup_cgroup(Manager *m) {
848 _cleanup_free_ char *path = NULL;
853 /* 1. Determine hierarchy */
854 free(m->cgroup_root);
855 m->cgroup_root = NULL;
857 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
859 return log_error_errno(r, "Cannot determine cgroup we are running in: %m");
861 /* LEGACY: Already in /system.slice? If so, let's cut this
862 * off. This is to support live upgrades from older systemd
863 * versions where PID 1 was moved there. */
864 if (m->running_as == SYSTEMD_SYSTEM) {
867 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
869 e = endswith(m->cgroup_root, "/system");
874 /* And make sure to store away the root value without trailing
875 * slash, even for the root dir, so that we can easily prepend
877 if (streq(m->cgroup_root, "/"))
878 m->cgroup_root[0] = 0;
881 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
883 return log_error_errno(r, "Cannot find cgroup mount point: %m");
885 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
888 /* 3. Install agent */
889 if (m->running_as == SYSTEMD_SYSTEM) {
890 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
892 log_warning_errno(r, "Failed to install release agent, ignoring: %m");
894 log_debug("Installed release agent.");
896 log_debug("Release agent already installed.");
899 /* 4. Make sure we are in the root cgroup */
900 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
902 return log_error_errno(r, "Failed to create root cgroup hierarchy: %m");
904 /* 5. And pin it, so that it cannot be unmounted */
905 safe_close(m->pin_cgroupfs_fd);
907 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
908 if (m->pin_cgroupfs_fd < 0) {
909 log_error("Failed to open pin file: %m");
913 /* 6. Always enable hierarchial support if it exists... */
914 cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
917 /* 7. Figure out which controllers are supported */
918 m->cgroup_supported = cg_mask_supported();
923 void manager_shutdown_cgroup(Manager *m, bool delete) {
926 /* We can't really delete the group, since we are in it. But
928 if (delete && m->cgroup_root)
929 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
931 m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd);
933 free(m->cgroup_root);
934 m->cgroup_root = NULL;
937 Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
944 u = hashmap_get(m->cgroup_unit, cgroup);
958 u = hashmap_get(m->cgroup_unit, p);
964 Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
965 _cleanup_free_ char *cgroup = NULL;
973 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
977 return manager_get_unit_by_cgroup(m, cgroup);
980 int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
987 u = manager_get_unit_by_cgroup(m, cgroup);
989 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
991 if (UNIT_VTABLE(u)->notify_cgroup_empty)
992 UNIT_VTABLE(u)->notify_cgroup_empty(u);
994 unit_add_to_gc_queue(u);
1001 static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
1002 [CGROUP_AUTO] = "auto",
1003 [CGROUP_CLOSED] = "closed",
1004 [CGROUP_STRICT] = "strict",
1007 DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);