cgroup.c revision be2c1bd2a843aa61901086fccbae15b3aa085fb1
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2013 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <fcntl.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "path-util.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "special.h"
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen#include "cgroup-util.h"
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include "cgroup.h"
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poetteringvoid cgroup_context_init(CGroupContext *c) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(c);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* Initialize everything to the kernel defaults, assuming the
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * structure is preinitialized to 0 */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering c->cpu_shares = 1024;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering c->memory_limit = c->memory_soft_limit = (uint64_t) -1;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering c->blockio_weight = 1000;
b93312f5960b276bae915906ccde36f545bae3e0Zbigniew Jędrzejewski-Szmek}
b93312f5960b276bae915906ccde36f545bae3e0Zbigniew Jędrzejewski-Szmek
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringvoid cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(c);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(a);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering LIST_REMOVE(CGroupDeviceAllow, device_allow, c->device_allow, a);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering free(a->path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering free(a);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringvoid cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering assert(c);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(w);
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering LIST_REMOVE(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering free(w->path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering free(w);
f5430a3ef308f3a102899fcaf7fbece757082f2aLennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringvoid cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering assert(c);
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering assert(b);
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering LIST_REMOVE(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering free(b->path);
9c92ce6d67f88beb31dd6555d12ae3f632218a39Lennart Poettering free(b);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringvoid cgroup_context_done(CGroupContext *c) {
8ac4e9e1e54397f6d1745c2a7a806132418c7da2Lennart Poettering assert(c);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering while (c->blockio_device_weights)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering while (c->blockio_device_bandwidths)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths);
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek
2e276efc7b0398a3086629a52970bdd4ab7252f9Zbigniew Jędrzejewski-Szmek while (c->device_allow)
c0eb11cfd016381fe02875a4ef29c1ade00c94e7Lennart Poettering cgroup_context_free_device_allow(c, c->device_allow);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringvoid cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering CGroupBlockIODeviceBandwidth *b;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering CGroupBlockIODeviceWeight *w;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering CGroupDeviceAllow *a;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(c);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering assert(f);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering prefix = strempty(prefix);
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering fprintf(f,
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering "%sCPUAccounting=%s\n"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering "%sBlockIOAccounting=%s\n"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering "%sMemoryAccounting=%s\n"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering "%sCPUShares=%lu\n"
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering "%sBlockIOWeight%lu\n"
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek "%sMemoryLimit=%" PRIu64 "\n"
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek "%sMemorySoftLimit=%" PRIu64 "\n"
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek "%sDevicePolicy=%s\n",
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek prefix, yes_no(c->cpu_accounting),
946c70944ebdf428ffeb9991a7449edbd4011461Zbigniew Jędrzejewski-Szmek prefix, yes_no(c->blockio_accounting),
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, yes_no(c->memory_accounting),
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, c->cpu_shares,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, c->blockio_weight,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, c->memory_limit,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, c->memory_soft_limit,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek prefix, cgroup_device_policy_to_string(c->device_policy));
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek LIST_FOREACH(device_allow, a, c->device_allow)
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek fprintf(f,
0dae31d468b1a0e22d98921f7b0dbd92fd217167Zbigniew Jędrzejewski-Szmek "%sDeviceAllow=%s %s%s%s\n",
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering prefix,
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen a->path,
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen LIST_FOREACH(device_weights, w, c->blockio_device_weights)
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen fprintf(f,
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen "%sBlockIOWeight=%s %lu",
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen prefix,
abf126a355e2f2b62b6c51ab3bb37895d1e3eee7Tom Gundersen w->path,
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen w->weight);
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering char buf[FORMAT_BYTES_MAX];
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen
549c1a2564b56f2bb38f1203d59c747ea15817f3Tom Gundersen fprintf(f,
42cc2eebb01056beb7acd3ecfe8e533558237f84Lennart Poettering "%s%s=%s %s\n",
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek prefix,
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth",
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek b->path,
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek format_bytes(buf, sizeof(buf), b->bandwidth));
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek }
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek}
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmekstatic int lookup_blkio_device(const char *p, dev_t *dev) {
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek struct stat st;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek int r;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek assert(p);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek assert(dev);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek r = stat(p, &st);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek if (r < 0) {
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek log_warning("Couldn't stat device %s: %m", p);
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek return -errno;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek }
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek if (S_ISBLK(st.st_mode))
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek *dev = st.st_rdev;
151226ab4bf276d60d51864330a99f886b923697Zbigniew Jędrzejewski-Szmek else if (major(st.st_dev) != 0) {
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen /* If this is not a device node then find the block
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen * device this file is stored on */
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen *dev = st.st_dev;
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen
50f1e641a93cacfc693b0c3d300bee5df0c8c460Tom Gundersen /* If this is a partition, try to get the originating
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen * block device */
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen block_get_whole_disk(*dev, dev);
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen } else {
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen return -ENODEV;
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen }
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen return 0;
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen}
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersen
5d45a8808431987c370706d365fb0cc95cf03d52Tom Gundersenstatic int whitelist_device(const char *path, const char *node, const char *acc) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering struct stat st;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering assert(path);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering assert(acc);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering if (stat(node, &st) < 0) {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering log_warning("Couldn't stat device %s", node);
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering return -errno;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering }
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering log_warning("%s is not a device.", node);
7c1ff6ac3d9e3acae1d601d40728cf7ccc9a7730Tom Gundersen return -ENODEV;
36d9205d669bcdcb04fa730d1f3549a9fc9a9001Tom Gundersen }
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering sprintf(buf,
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering "%c %u:%u %s",
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering S_ISCHR(st.st_mode) ? 'c' : 'b',
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering major(st.st_rdev), minor(st.st_rdev),
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering acc);
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering r = cg_set_attribute("devices", path, "devices.allow", buf);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering if (r < 0)
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering log_warning("Failed to set devices.allow on %s: %s", path, strerror(-r));
8bf52d3d17d364438191077d0750b8b80b5dc53aLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return r;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering
78c6a153c47f8d597c827bdcaf8c4e42ac87f738Lennart Poetteringvoid cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) {
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering int r;
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering assert(c);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering assert(path);
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
2d4c5cbc0ed3ccb09dc086a040088b454c22c644Lennart Poettering if (mask == 0)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt return;
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt
if (mask & CGROUP_CPU) {
char buf[DECIMAL_STR_MAX(unsigned long) + 1];
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;
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 (mask & CGROUP_MEMORY) {
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
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));
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));
}
if (mask & CGROUP_DEVICE) {
CGroupDeviceAllow *a;
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 (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';
if (k == 0)
continue;
acc[k++] = 0;
whitelist_device(path, a->path, acc);
}
}
}
CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
CGroupControllerMask mask = 0;
/* Figure out which controllers we need */
if (c->cpu_accounting || c->cpu_shares != 1024)
mask |= CGROUP_CPUACCT | CGROUP_CPU;
if (c->blockio_accounting ||
c->blockio_weight != 1000 ||
c->blockio_device_weights ||
c->blockio_device_bandwidths)
mask |= CGROUP_BLKIO;
if (c->memory_accounting ||
c->memory_limit != (uint64_t) -1 ||
c->memory_soft_limit != (uint64_t) -1)
mask |= CGROUP_MEMORY;
if (c->device_allow || c->device_policy != CGROUP_AUTO)
mask |= CGROUP_DEVICE;
return mask;
}
static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
CGroupContext *c;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
return cgroup_context_get_mask(c);
}
static CGroupControllerMask unit_get_members_mask(Unit *u) {
CGroupControllerMask mask = 0;
Unit *m;
Iterator i;
assert(u);
SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
if (UNIT_DEREF(m->slice) != u)
continue;
mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
}
return mask;
}
static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
assert(u);
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);
}
static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
char *path = NULL;
int r;
assert(u);
path = unit_default_cgroup_path(u);
if (!path)
return -ENOMEM;
r = hashmap_put(u->manager->cgroup_unit, path, u);
if (r < 0)
return 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));
/* 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)
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;
}
static int unit_realize_cgroup_now(Unit *u) {
CGroupControllerMask mask;
assert(u);
if (u->in_cgroup_queue) {
LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
u->in_cgroup_queue = false;
}
mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
mask &= u->manager->cgroup_supported;
if (u->cgroup_realized &&
u->cgroup_mask == mask)
return 0;
/* First, realize parents */
if (UNIT_ISSET(u->slice))
unit_realize_cgroup_now(UNIT_DEREF(u->slice));
/* And then do the real work */
return unit_create_cgroups(u, mask);
}
static void unit_add_to_cgroup_queue(Unit *u) {
if (u->in_cgroup_queue)
return;
LIST_PREPEND(Unit, cgroup_queue, u->manager->cgroup_queue, u);
u->in_cgroup_queue = true;
}
unsigned manager_dispatch_cgroup_queue(Manager *m) {
Unit *i;
unsigned n = 0;
while ((i = m->cgroup_queue)) {
assert(i->in_cgroup_queue);
if (unit_realize_cgroup_now(i) >= 0)
cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
n++;
}
return n;
}
static void unit_queue_siblings(Unit *u) {
Unit *slice;
/* 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;
SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
if (m == u)
continue;
if (UNIT_DEREF(m->slice) != slice)
continue;
unit_add_to_cgroup_queue(m);
}
u = slice;
}
}
int unit_realize_cgroup(Unit *u) {
CGroupContext *c;
int r;
assert(u);
c = unit_get_cgroup_context(u);
if (!c)
return 0;
/* 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. */
/* Add all sibling slices to the cgroup queue. */
unit_queue_siblings(u);
/* And realize this one now */
r = unit_realize_cgroup_now(u);
/* And apply the values */
if (r >= 0)
cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
return r;
}
void unit_destroy_cgroup(Unit *u) {
int r;
assert(u);
if (!u->cgroup_path)
return;
r = cg_trim_with_mask(u->cgroup_mask, u->cgroup_path, true);
if (r < 0)
log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
free(u->cgroup_path);
u->cgroup_path = NULL;
u->cgroup_realized = false;
u->cgroup_mask = 0;
}
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;
/* 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;
}
return pid;
}
int manager_setup_cgroup(Manager *m) {
_cleanup_free_ char *path = NULL;
int r;
char *e, *a;
assert(m);
/* 0. Be nice to Ingo Molnar #628004 */
if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
log_warning("No control group support available, not creating root group.");
return 0;
}
/* 1. Determine hierarchy */
free(m->cgroup_root);
m->cgroup_root = NULL;
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
if (r < 0) {
log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
return r;
}
/* Already in /system.slice? If so, let's cut this off again */
if (m->running_as == SYSTEMD_SYSTEM) {
e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
if (e)
*e = 0;
}
/* And make sure to store away the root value without trailing
* slash, even for the root dir, so that we can easily prepend
* it everywhere. */
if (streq(m->cgroup_root, "/"))
m->cgroup_root[0] = 0;
/* 2. Show data */
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
if (r < 0) {
log_error("Cannot find cgroup mount point: %s", strerror(-r));
return r;
}
log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
/* 3. Install agent */
if (m->running_as == SYSTEMD_SYSTEM) {
r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
if (r < 0)
log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
else if (r > 0)
log_debug("Installed release agent.");
else
log_debug("Release agent already installed.");
}
/* 4. Realize the system slice and put us in there */
if (m->running_as == SYSTEMD_SYSTEM) {
a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
} else
r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
if (r < 0) {
log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
return r;
}
/* 5. And pin it, so that it cannot be unmounted */
if (m->pin_cgroupfs_fd >= 0)
close_nointr_nofail(m->pin_cgroupfs_fd);
m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
if (r < 0) {
log_error("Failed to open pin file: %m");
return -errno;
}
/* 6. Figure out which controllers are supported */
m->cgroup_supported = cg_mask_supported();
return 0;
}
void manager_shutdown_cgroup(Manager *m, bool delete) {
assert(m);
/* We can't really delete the group, since we are in it. But
* let's trim it. */
if (delete && m->cgroup_root)
cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
if (m->pin_cgroupfs_fd >= 0) {
close_nointr_nofail(m->pin_cgroupfs_fd);
m->pin_cgroupfs_fd = -1;
}
free(m->cgroup_root);
m->cgroup_root = NULL;
}
Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
char *p;
Unit *u;
assert(m);
assert(cgroup);
u = hashmap_get(m->cgroup_unit, cgroup);
if (u)
return u;
p = strdupa(cgroup);
for (;;) {
char *e;
e = strrchr(p, '/');
if (e == p || !e)
return NULL;
*e = 0;
u = hashmap_get(m->cgroup_unit, p);
if (u)
return u;
}
}
Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
_cleanup_free_ char *cgroup = NULL;
int r;
assert(m);
if (pid <= 1)
return NULL;
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
if (r < 0)
return NULL;
return manager_get_unit_by_cgroup(m, cgroup);
}
int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
Unit *u;
int r;
assert(m);
assert(cgroup);
u = manager_get_unit_by_cgroup(m, cgroup);
if (u) {
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
if (r > 0) {
if (UNIT_VTABLE(u)->notify_cgroup_empty)
UNIT_VTABLE(u)->notify_cgroup_empty(u);
unit_add_to_gc_queue(u);
}
}
return 0;
}
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
[CGROUP_AUTO] = "auto",
[CGROUP_CLOSED] = "closed",
[CGROUP_STRICT] = "strict",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);