summaryrefslogtreecommitdiff
path: root/src/core/cgroup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/cgroup.c')
-rw-r--r--src/core/cgroup.c831
1 files changed, 475 insertions, 356 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c
index 5065329739..79467a82ce 100644
--- a/src/core/cgroup.c
+++ b/src/core/cgroup.c
@@ -3,7 +3,7 @@
/***
This file is part of systemd.
- Copyright 2010 Lennart Poettering
+ Copyright 2013 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
@@ -19,305 +19,554 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
-#include <assert.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-#include <sys/mount.h>
#include <fcntl.h>
-#include "cgroup.h"
-#include "cgroup-util.h"
-#include "log.h"
-#include "strv.h"
#include "path-util.h"
#include "special.h"
+#include "cgroup-util.h"
+#include "cgroup.h"
-int cgroup_bonding_realize(CGroupBonding *b) {
- int r;
+void cgroup_context_init(CGroupContext *c) {
+ assert(c);
+
+ /* Initialize everything to the kernel defaults, assuming the
+ * structure is preinitialized to 0 */
+
+ c->cpu_shares = 1024;
+ c->memory_limit = c->memory_soft_limit = (uint64_t) -1;
+ c->blockio_weight = 1000;
+}
+void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
+ assert(c);
+ assert(a);
+
+ LIST_REMOVE(CGroupDeviceAllow, device_allow, c->device_allow, a);
+ free(a->path);
+ free(a);
+}
+
+void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
+ assert(c);
+ assert(w);
+
+ LIST_REMOVE(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
+ free(w->path);
+ free(w);
+}
+
+void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
+ assert(c);
assert(b);
- assert(b->path);
- assert(b->controller);
- r = cg_create(b->controller, b->path, NULL);
+ LIST_REMOVE(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
+ free(b->path);
+ free(b);
+}
+
+void cgroup_context_done(CGroupContext *c) {
+ assert(c);
+
+ while (c->blockio_device_weights)
+ cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
+
+ while (c->blockio_device_bandwidths)
+ cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths);
+
+ while (c->device_allow)
+ cgroup_context_free_device_allow(c, c->device_allow);
+}
+
+void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
+ CGroupBlockIODeviceBandwidth *b;
+ CGroupBlockIODeviceWeight *w;
+ CGroupDeviceAllow *a;
+
+ assert(c);
+ assert(f);
+
+ prefix = strempty(prefix);
+
+ fprintf(f,
+ "%sCPUAccounting=%s\n"
+ "%sBlockIOAccounting=%s\n"
+ "%sMemoryAccounting=%s\n"
+ "%sCPUShares=%lu\n"
+ "%sBlockIOWeight%lu\n"
+ "%sMemoryLimit=%" PRIu64 "\n"
+ "%sMemorySoftLimit=%" PRIu64 "\n"
+ "%sDevicePolicy=%s\n",
+ prefix, yes_no(c->cpu_accounting),
+ prefix, yes_no(c->blockio_accounting),
+ prefix, yes_no(c->memory_accounting),
+ prefix, c->cpu_shares,
+ prefix, c->blockio_weight,
+ prefix, c->memory_limit,
+ prefix, c->memory_soft_limit,
+ prefix, cgroup_device_policy_to_string(c->device_policy));
+
+ LIST_FOREACH(device_allow, a, c->device_allow)
+ fprintf(f,
+ "%sDeviceAllow=%s %s%s%s\n",
+ prefix,
+ a->path,
+ a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
+
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights)
+ fprintf(f,
+ "%sBlockIOWeight=%s %lu",
+ prefix,
+ w->path,
+ w->weight);
+
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ char buf[FORMAT_BYTES_MAX];
+
+ fprintf(f,
+ "%s%s=%s %s\n",
+ prefix,
+ b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth",
+ b->path,
+ format_bytes(buf, sizeof(buf), b->bandwidth));
+ }
+}
+
+static int lookup_blkio_device(const char *p, dev_t *dev) {
+ struct stat st;
+ int r;
+
+ assert(p);
+ assert(dev);
+
+ r = stat(p, &st);
if (r < 0) {
- log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
- return r;
+ log_warning("Couldn't stat device %s: %m", p);
+ return -errno;
}
- b->realized = true;
+ if (S_ISBLK(st.st_mode))
+ *dev = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ *dev = st.st_dev;
+
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(*dev, dev);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
+ return -ENODEV;
+ }
return 0;
}
-int cgroup_bonding_realize_list(CGroupBonding *first) {
- CGroupBonding *b;
+static int whitelist_device(const char *path, const char *node, const char *acc) {
+ char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
+ struct stat st;
int r;
- LIST_FOREACH(by_unit, b, first)
- if ((r = cgroup_bonding_realize(b)) < 0 && b->essential)
- return r;
+ assert(path);
+ assert(acc);
- return 0;
+ if (stat(node, &st) < 0) {
+ log_warning("Couldn't stat device %s", node);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", node);
+ return -ENODEV;
+ }
+
+ sprintf(buf,
+ "%c %u:%u %s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ acc);
+
+ r = cg_set_attribute("devices", path, "devices.allow", buf);
+ if (r < 0)
+ log_warning("Failed to set devices.allow on %s: %s", path, strerror(-r));
+
+ return r;
}
-void cgroup_bonding_free(CGroupBonding *b, bool trim) {
- assert(b);
+void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) {
+ int r;
+
+ assert(c);
+ assert(path);
- if (b->unit) {
- CGroupBonding *f;
+ if (mask == 0)
+ return;
- LIST_REMOVE(CGroupBonding, by_unit, b->unit->cgroup_bondings, b);
+ if (mask & CGROUP_CPU) {
+ char buf[DECIMAL_STR_MAX(unsigned long) + 1];
- if (streq(b->controller, SYSTEMD_CGROUP_CONTROLLER)) {
- assert_se(f = hashmap_get(b->unit->manager->cgroup_bondings, b->path));
- LIST_REMOVE(CGroupBonding, by_path, f, b);
+ sprintf(buf, "%lu\n", c->cpu_shares);
+ r = cg_set_attribute("cpu", path, "cpu.shares", buf);
+ if (r < 0)
+ log_warning("Failed to set cpu.shares on %s: %s", path, strerror(-r));
+ }
+
+ if (mask & CGROUP_BLKIO) {
+ char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
+ DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
+ DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
+ CGroupBlockIODeviceWeight *w;
+ CGroupBlockIODeviceBandwidth *b;
+
+ sprintf(buf, "%lu\n", c->blockio_weight);
+ r = cg_set_attribute("blkio", path, "blkio.weight", buf);
+ if (r < 0)
+ log_warning("Failed to set blkio.weight on %s: %s", path, strerror(-r));
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
+ dev_t dev;
+
+ r = lookup_blkio_device(w->path, &dev);
+ if (r < 0)
+ continue;
- if (f)
- hashmap_replace(b->unit->manager->cgroup_bondings, b->path, f);
- else
- hashmap_remove(b->unit->manager->cgroup_bondings, b->path);
+ sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight);
+ r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
+ if (r < 0)
+ log_error("Failed to set blkio.weight_device on %s: %s", path, strerror(-r));
+ }
+
+ /* FIXME: no way to reset this list */
+ LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
+ const char *a;
+ dev_t dev;
+
+ r = lookup_blkio_device(b->path, &dev);
+ if (r < 0)
+ continue;
+
+ a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device";
+
+ sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
+ r = cg_set_attribute("blkio", path, a, buf);
+ if (r < 0)
+ log_error("Failed to set %s on %s: %s", a, path, strerror(-r));
}
}
- if (b->realized && b->ours && trim)
- cg_trim(b->controller, b->path, false);
+ if (mask & CGROUP_MEMORY) {
+ char buf[DECIMAL_STR_MAX(uint64_t) + 1];
- free(b->controller);
- free(b->path);
- free(b);
-}
+ sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
+ r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
+ if (r < 0)
+ log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
-void cgroup_bonding_free_list(CGroupBonding *first, bool remove_or_trim) {
- CGroupBonding *b, *n;
+ sprintf(buf, "%" PRIu64 "\n", c->memory_soft_limit);
+ cg_set_attribute("memory", path, "memory.soft_limit_in_bytes", buf);
+ if (r < 0)
+ log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
+ }
- LIST_FOREACH_SAFE(by_unit, b, n, first)
- cgroup_bonding_free(b, remove_or_trim);
-}
+ if (mask & CGROUP_DEVICE) {
+ CGroupDeviceAllow *a;
-void cgroup_bonding_trim(CGroupBonding *b, bool delete_root) {
- assert(b);
+ if (c->device_allow || c->device_policy != CGROUP_AUTO)
+ r = cg_set_attribute("devices", path, "devices.deny", "a");
+ else
+ r = cg_set_attribute("devices", path, "devices.allow", "a");
+ if (r < 0)
+ log_error("Failed to reset devices.list on %s: %s", path, strerror(-r));
- if (b->realized && b->ours)
- cg_trim(b->controller, b->path, delete_root);
-}
+ if (c->device_policy == CGROUP_CLOSED ||
+ (c->device_policy == CGROUP_AUTO && c->device_allow)) {
+ static const char auto_devices[] =
+ "/dev/null\0" "rw\0"
+ "/dev/zero\0" "rw\0"
+ "/dev/full\0" "rw\0"
+ "/dev/random\0" "rw\0"
+ "/dev/urandom\0" "rw\0";
+
+ const char *x, *y;
+
+ NULSTR_FOREACH_PAIR(x, y, auto_devices)
+ whitelist_device(path, x, y);
+ }
+
+ LIST_FOREACH(device_allow, a, c->device_allow) {
+ char acc[4];
+ unsigned k = 0;
+
+ if (a->r)
+ acc[k++] = 'r';
+ if (a->w)
+ acc[k++] = 'w';
+ if (a->m)
+ acc[k++] = 'm';
-void cgroup_bonding_trim_list(CGroupBonding *first, bool delete_root) {
- CGroupBonding *b;
+ if (k == 0)
+ continue;
- LIST_FOREACH(by_unit, b, first)
- cgroup_bonding_trim(b, delete_root);
+ acc[k++] = 0;
+ whitelist_device(path, a->path, acc);
+ }
+ }
}
-int cgroup_bonding_install(CGroupBonding *b, pid_t pid, const char *cgroup_suffix) {
- _cleanup_free_ char *p = NULL;
- const char *path;
- int r;
+CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
+ CGroupControllerMask mask = 0;
- assert(b);
- assert(pid >= 0);
+ /* Figure out which controllers we need */
- if (cgroup_suffix) {
- p = strjoin(b->path, "/", cgroup_suffix, NULL);
- if (!p)
- return -ENOMEM;
+ if (c->cpu_accounting || c->cpu_shares != 1024)
+ mask |= CGROUP_CPUACCT | CGROUP_CPU;
- path = p;
- } else
- path = b->path;
+ if (c->blockio_accounting ||
+ c->blockio_weight != 1000 ||
+ c->blockio_device_weights ||
+ c->blockio_device_bandwidths)
+ mask |= CGROUP_BLKIO;
- r = cg_create_and_attach(b->controller, path, pid);
- if (r < 0)
- return r;
+ if (c->memory_accounting ||
+ c->memory_limit != (uint64_t) -1 ||
+ c->memory_soft_limit != (uint64_t) -1)
+ mask |= CGROUP_MEMORY;
- b->realized = true;
- return 0;
+ if (c->device_allow || c->device_policy != CGROUP_AUTO)
+ mask |= CGROUP_DEVICE;
+
+ return mask;
}
-int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid, const char *cgroup_suffix) {
- CGroupBonding *b;
- int r;
+static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
+ CGroupContext *c;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_install(b, pid, cgroup_suffix);
- if (r < 0 && b->essential)
- return r;
- }
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return 0;
- return 0;
+ return cgroup_context_get_mask(c);
}
-int cgroup_bonding_migrate(CGroupBonding *b, CGroupBonding *list) {
- CGroupBonding *q;
- int ret = 0;
+static CGroupControllerMask unit_get_members_mask(Unit *u) {
+ CGroupControllerMask mask = 0;
+ Unit *m;
+ Iterator i;
- LIST_FOREACH(by_unit, q, list) {
- int r;
+ assert(u);
- if (q == b)
- continue;
+ SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
- if (!q->ours)
+ if (UNIT_DEREF(m->slice) != u)
continue;
- r = cg_migrate_recursive(q->controller, q->path, b->controller, b->path, true, false);
- if (r < 0 && ret == 0)
- ret = r;
+ mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
}
- return ret;
+ return mask;
}
-int cgroup_bonding_migrate_to(CGroupBonding *b, const char *target, bool rem) {
- assert(b);
- assert(target);
+static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
+ assert(u);
- return cg_migrate_recursive(b->controller, b->path, b->controller, target, true, rem);
+ if (!UNIT_ISSET(u->slice))
+ return 0;
+
+ /* Sibling propagation is only relevant for weight-based
+ * controllers, so let's mask out everything else */
+ return unit_get_members_mask(UNIT_DEREF(u->slice)) &
+ (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
}
-int cgroup_bonding_set_group_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid) {
- assert(b);
+static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
+ char *path = NULL;
+ int r;
- if (!b->realized)
- return -EINVAL;
+ assert(u);
- return cg_set_group_access(b->controller, b->path, mode, uid, gid);
-}
+ path = unit_default_cgroup_path(u);
+ if (!path)
+ return -ENOMEM;
-int cgroup_bonding_set_group_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid) {
- CGroupBonding *b;
- int r;
+ /* First, create our own group */
+ r = cg_create_with_mask(mask, path);
+ if (r < 0)
+ log_error("Failed to create cgroup %s: %s", path, strerror(-r));
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_group_access(b, mode, uid, gid);
+ /* Then, possibly move things over */
+ if (u->cgroup_path && !streq(path, u->cgroup_path)) {
+ r = cg_migrate_with_mask(mask, u->cgroup_path, path);
if (r < 0)
- return r;
+ log_error("Failed to migrate cgroup %s: %s", path, strerror(-r));
}
+ /* And remember the new data */
+ free(u->cgroup_path);
+ u->cgroup_path = path;
+ u->cgroup_realized = true;
+ u->cgroup_mask = mask;
+
return 0;
}
-int cgroup_bonding_set_task_access(CGroupBonding *b, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- assert(b);
+static void unit_realize_cgroup_now(Unit *u) {
+ CGroupControllerMask mask;
- if (!b->realized)
- return -EINVAL;
+ assert(u);
- return cg_set_task_access(b->controller, b->path, mode, uid, gid, sticky);
-}
+ if (u->in_cgroup_queue) {
+ LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+ u->in_cgroup_queue = false;
+ }
-int cgroup_bonding_set_task_access_list(CGroupBonding *first, mode_t mode, uid_t uid, gid_t gid, int sticky) {
- CGroupBonding *b;
- int r;
+ mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
+ mask &= u->manager->cgroup_supported;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_set_task_access(b, mode, uid, gid, sticky);
- if (r < 0)
- return r;
- }
+ if (u->cgroup_realized &&
+ u->cgroup_mask == mask)
+ return;
- return 0;
+ /* First, realize parents */
+ if (UNIT_ISSET(u->slice))
+ unit_realize_cgroup_now(UNIT_DEREF(u->slice));
+
+ /* And then do the real work */
+ unit_create_cgroups(u, mask);
}
-int cgroup_bonding_kill(CGroupBonding *b, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
- char *p = NULL;
- const char *path;
- int r;
+static void unit_add_to_cgroup_queue(Unit *u) {
- assert(b);
- assert(sig >= 0);
+ if (u->in_cgroup_queue)
+ return;
- /* Don't kill cgroups that aren't ours */
- if (!b->ours)
- return 0;
+ LIST_PREPEND(Unit, cgroup_queue, u->manager->cgroup_queue, u);
+ u->in_cgroup_queue = true;
+}
- if (cgroup_suffix) {
- p = strjoin(b->path, "/", cgroup_suffix, NULL);
- if (!p)
- return -ENOMEM;
+unsigned manager_dispatch_cgroup_queue(Manager *m) {
+ Unit *i;
+ unsigned n = 0;
- path = p;
- } else
- path = b->path;
+ while ((i = m->cgroup_queue)) {
+ assert(i->in_cgroup_queue);
- r = cg_kill_recursive(b->controller, path, sig, sigcont, true, rem, s);
- free(p);
+ unit_realize_cgroup_now(i);
+ cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
+ n++;
+ }
- return r;
+ return n;
}
-int cgroup_bonding_kill_list(CGroupBonding *first, int sig, bool sigcont, bool rem, Set *s, const char *cgroup_suffix) {
- CGroupBonding *b;
- Set *allocated_set = NULL;
- int ret = -EAGAIN, r;
+static void unit_queue_siblings(Unit *u) {
+ Unit *slice;
- if (!first)
- return 0;
+ /* This adds the siblings of the specified unit and the
+ * siblings of all parent units to the cgroup queue. (But
+ * neither the specified unit itself nor the parents.) */
+
+ while ((slice = UNIT_DEREF(u->slice))) {
+ Iterator i;
+ Unit *m;
- if (!s)
- if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
- return -ENOMEM;
+ SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
+ if (m == u)
+ continue;
- LIST_FOREACH(by_unit, b, first) {
- r = cgroup_bonding_kill(b, sig, sigcont, rem, s, cgroup_suffix);
- if (r < 0) {
- if (r == -EAGAIN || r == -ESRCH)
+ if (UNIT_DEREF(m->slice) != slice)
continue;
- ret = r;
- goto finish;
+ unit_add_to_cgroup_queue(m);
}
- if (ret < 0 || r > 0)
- ret = r;
+ u = slice;
}
+}
+
+void unit_realize_cgroup(Unit *u) {
+ CGroupContext *c;
+
+ assert(u);
+
+ c = unit_get_cgroup_context(u);
+ if (!c)
+ return;
-finish:
- if (allocated_set)
- set_free(allocated_set);
+ /* So, here's the deal: when realizing the cgroups for this
+ * unit, we need to first create all parents, but there's more
+ * actually: for the weight-based controllers we also need to
+ * make sure that all our siblings (i.e. units that are in the
+ * same slice as we are) have cgroup too. Otherwise things
+ * would become very uneven as each of their processes would
+ * get as much resources as all our group together. This call
+ * will synchronously create the parent cgroups, but will
+ * defer work on the siblings to the next event loop
+ * iteration. */
- return ret;
+ /* Add all sibling slices to the cgroup queue. */
+ unit_queue_siblings(u);
+
+ /* And realize this one now */
+ unit_realize_cgroup_now(u);
+
+ /* And apply the values */
+ cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
}
-/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
- * cannot know */
-int cgroup_bonding_is_empty(CGroupBonding *b) {
+void unit_destroy_cgroup(Unit *u) {
int r;
- assert(b);
+ assert(u);
- if ((r = cg_is_empty_recursive(b->controller, b->path, true)) < 0)
- return r;
+ if (!u->cgroup_path)
+ return;
- /* If it is empty it is empty */
- if (r > 0)
- return 1;
+ r = cg_trim_with_mask(u->cgroup_mask, u->cgroup_path, true);
+ if (r < 0)
+ log_error("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
- /* It's not only us using this cgroup, so we just don't know */
- return b->ours ? 0 : -EAGAIN;
+ free(u->cgroup_path);
+ u->cgroup_path = NULL;
+ u->cgroup_realized = false;
+ u->cgroup_mask = 0;
}
-int cgroup_bonding_is_empty_list(CGroupBonding *first) {
- CGroupBonding *b;
+pid_t unit_search_main_pid(Unit *u) {
+ _cleanup_fclose_ FILE *f = NULL;
+ pid_t pid = 0, npid, mypid;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return 0;
+
+ if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
+ return 0;
+
+ mypid = getpid();
+ while (cg_read_pid(f, &npid) > 0) {
+ pid_t ppid;
+
+ if (npid == pid)
+ continue;
- LIST_FOREACH(by_unit, b, first) {
- int r;
+ /* Ignore processes that aren't our kids */
+ if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
+ continue;
- r = cgroup_bonding_is_empty(b);
- if (r < 0) {
- /* If this returned -EAGAIN, then we don't know if the
- * group is empty, so let's see if another group can
- * tell us */
+ if (pid != 0) {
+ /* Dang, there's more than one daemonized PID
+ in this group, so we don't know what process
+ is the main process. */
+ pid = 0;
+ break;
+ }
- if (r != -EAGAIN)
- return r;
- } else
- return r;
+ pid = npid;
}
- return -EAGAIN;
+ return pid;
}
int manager_setup_cgroup(Manager *m) {
@@ -394,8 +643,8 @@ int manager_setup_cgroup(Manager *m) {
return -errno;
}
- /* 6. Remove non-existing controllers from the default controllers list */
- cg_shorten_controllers(m->default_controllers);
+ /* 6. Figure out which controllers are supported */
+ m->cgroup_supported = cg_mask_supported();
return 0;
}
@@ -417,201 +666,71 @@ void manager_shutdown_cgroup(Manager *m, bool delete) {
m->cgroup_root = NULL;
}
-int cgroup_bonding_get(Manager *m, const char *cgroup, CGroupBonding **bonding) {
- CGroupBonding *b;
+Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
char *p;
+ Unit *u;
assert(m);
assert(cgroup);
- assert(bonding);
- b = hashmap_get(m->cgroup_bondings, cgroup);
- if (b) {
- *bonding = b;
- return 1;
- }
+ u = hashmap_get(m->cgroup_unit, cgroup);
+ if (u)
+ return u;
p = strdupa(cgroup);
- if (!p)
- return -ENOMEM;
-
for (;;) {
char *e;
e = strrchr(p, '/');
- if (e == p || !e) {
- *bonding = NULL;
- return 0;
- }
+ if (e == p || !e)
+ return NULL;
*e = 0;
- b = hashmap_get(m->cgroup_bondings, p);
- if (b) {
- *bonding = b;
- return 1;
- }
+ u = hashmap_get(m->cgroup_unit, p);
+ if (u)
+ return u;
}
}
-int cgroup_notify_empty(Manager *m, const char *group) {
- CGroupBonding *l, *b;
+Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
+ _cleanup_free_ char *cgroup = NULL;
int r;
assert(m);
- assert(group);
-
- r = cgroup_bonding_get(m, group, &l);
- if (r <= 0)
- return r;
-
- LIST_FOREACH(by_path, b, l) {
- int t;
-
- if (!b->unit)
- continue;
-
- t = cgroup_bonding_is_empty_list(b);
- if (t < 0) {
-
- /* If we don't know, we don't know */
- if (t != -EAGAIN)
- log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
-
- continue;
- }
-
- if (t > 0) {
- /* If it is empty, let's delete it */
- cgroup_bonding_trim_list(b->unit->cgroup_bondings, true);
-
- if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
- UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
- }
- }
-
- return 0;
-}
-
-Unit* cgroup_unit_by_pid(Manager *m, pid_t pid) {
- CGroupBonding *l, *b;
- char *group = NULL;
-
- assert(m);
if (pid <= 1)
return NULL;
- if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &group) < 0)
- return NULL;
-
- l = hashmap_get(m->cgroup_bondings, group);
-
- if (!l) {
- char *slash;
-
- while ((slash = strrchr(group, '/'))) {
- if (slash == group)
- break;
-
- *slash = 0;
-
- if ((l = hashmap_get(m->cgroup_bondings, group)))
- break;
- }
- }
-
- free(group);
-
- LIST_FOREACH(by_path, b, l) {
-
- if (!b->unit)
- continue;
-
- if (b->ours)
- return b->unit;
- }
-
- return NULL;
-}
-
-CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
- CGroupBonding *b;
-
- if (!controller)
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
- LIST_FOREACH(by_unit, b, first)
- if (streq(b->controller, controller))
- return b;
-
- return NULL;
-}
-
-char *cgroup_bonding_to_string(CGroupBonding *b) {
- char *r;
-
- assert(b);
-
- if (asprintf(&r, "%s:%s", b->controller, b->path) < 0)
+ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
+ if (r < 0)
return NULL;
- return r;
+ return manager_get_unit_by_cgroup(m, cgroup);
}
-pid_t cgroup_bonding_search_main_pid(CGroupBonding *b) {
- FILE *f;
- pid_t pid = 0, npid, mypid;
-
- assert(b);
+int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
+ Unit *u;
+ int r;
- if (!b->ours)
- return 0;
+ assert(m);
+ assert(cgroup);
- if (cg_enumerate_processes(b->controller, b->path, &f) < 0)
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, true);
+ if (r == 0)
return 0;
- mypid = getpid();
+ u = manager_get_unit_by_cgroup(m, cgroup);
+ if (u && UNIT_VTABLE(u)->notify_cgroup_empty)
+ UNIT_VTABLE(u)->notify_cgroup_empty(u);
- while (cg_read_pid(f, &npid) > 0) {
- pid_t ppid;
-
- if (npid == pid)
- continue;
-
- /* Ignore processes that aren't our kids */
- if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
- continue;
-
- if (pid != 0) {
- /* Dang, there's more than one daemonized PID
- in this group, so we don't know what process
- is the main process. */
- pid = 0;
- break;
- }
-
- pid = npid;
- }
-
- fclose(f);
-
- return pid;
+ return 0;
}
-pid_t cgroup_bonding_search_main_pid_list(CGroupBonding *first) {
- CGroupBonding *b;
- pid_t pid;
-
- /* Try to find a main pid from this cgroup, but checking if
- * there's only one PID in the cgroup and returning it. Later
- * on we might want to add additional, smarter heuristics
- * here. */
-
- LIST_FOREACH(by_unit, b, first)
- if ((pid = cgroup_bonding_search_main_pid(b)) != 0)
- return pid;
+static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
+ [CGROUP_AUTO] = "auto",
+ [CGROUP_CLOSED] = "closed",
+ [CGROUP_STRICT] = "strict",
+};
- return 0;
-
-}
+DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);