]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/cgroup.c
bus: deal with duplicate values from enumerators
[thirdparty/systemd.git] / src / core / cgroup.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8e274523
LP
2
3/***
4 This file is part of systemd.
5
4ad49000 6 Copyright 2013 Lennart Poettering
8e274523
LP
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
8e274523
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
8e274523 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8e274523
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
c6c18be3 22#include <fcntl.h>
8c6db833 23
9eb977db 24#include "path-util.h"
9444b1f2 25#include "special.h"
4ad49000
LP
26#include "cgroup-util.h"
27#include "cgroup.h"
8e274523 28
4ad49000
LP
29void cgroup_context_init(CGroupContext *c) {
30 assert(c);
31
32 /* Initialize everything to the kernel defaults, assuming the
33 * structure is preinitialized to 0 */
34
35 c->cpu_shares = 1024;
ddca82ac 36 c->memory_limit = (uint64_t) -1;
4ad49000
LP
37 c->blockio_weight = 1000;
38}
8e274523 39
4ad49000
LP
40void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
41 assert(c);
42 assert(a);
43
71fda00f 44 LIST_REMOVE(device_allow, c->device_allow, a);
4ad49000
LP
45 free(a->path);
46 free(a);
47}
48
49void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
50 assert(c);
51 assert(w);
52
71fda00f 53 LIST_REMOVE(device_weights, c->blockio_device_weights, w);
4ad49000
LP
54 free(w->path);
55 free(w);
56}
57
58void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
59 assert(c);
8e274523 60 assert(b);
8e274523 61
71fda00f 62 LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b);
4ad49000
LP
63 free(b->path);
64 free(b);
65}
66
67void cgroup_context_done(CGroupContext *c) {
68 assert(c);
69
70 while (c->blockio_device_weights)
71 cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights);
72
73 while (c->blockio_device_bandwidths)
74 cgroup_context_free_blockio_device_bandwidth(c, c->blockio_device_bandwidths);
75
76 while (c->device_allow)
77 cgroup_context_free_device_allow(c, c->device_allow);
78}
79
80void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
81 CGroupBlockIODeviceBandwidth *b;
82 CGroupBlockIODeviceWeight *w;
83 CGroupDeviceAllow *a;
84
85 assert(c);
86 assert(f);
87
88 prefix = strempty(prefix);
89
90 fprintf(f,
91 "%sCPUAccounting=%s\n"
92 "%sBlockIOAccounting=%s\n"
93 "%sMemoryAccounting=%s\n"
94 "%sCPUShares=%lu\n"
112a7f46 95 "%sBlockIOWeight=%lu\n"
4ad49000 96 "%sMemoryLimit=%" PRIu64 "\n"
4ad49000
LP
97 "%sDevicePolicy=%s\n",
98 prefix, yes_no(c->cpu_accounting),
99 prefix, yes_no(c->blockio_accounting),
100 prefix, yes_no(c->memory_accounting),
101 prefix, c->cpu_shares,
102 prefix, c->blockio_weight,
103 prefix, c->memory_limit,
4ad49000
LP
104 prefix, cgroup_device_policy_to_string(c->device_policy));
105
106 LIST_FOREACH(device_allow, a, c->device_allow)
107 fprintf(f,
108 "%sDeviceAllow=%s %s%s%s\n",
109 prefix,
110 a->path,
111 a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : "");
112
113 LIST_FOREACH(device_weights, w, c->blockio_device_weights)
114 fprintf(f,
8e7076ca 115 "%sBlockIODeviceWeight=%s %lu",
4ad49000
LP
116 prefix,
117 w->path,
118 w->weight);
119
120 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
121 char buf[FORMAT_BYTES_MAX];
122
123 fprintf(f,
124 "%s%s=%s %s\n",
125 prefix,
126 b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth",
127 b->path,
128 format_bytes(buf, sizeof(buf), b->bandwidth));
129 }
130}
131
132static int lookup_blkio_device(const char *p, dev_t *dev) {
133 struct stat st;
134 int r;
135
136 assert(p);
137 assert(dev);
138
139 r = stat(p, &st);
ab1f0633 140 if (r < 0) {
4ad49000
LP
141 log_warning("Couldn't stat device %s: %m", p);
142 return -errno;
ab1f0633 143 }
8e274523 144
4ad49000
LP
145 if (S_ISBLK(st.st_mode))
146 *dev = st.st_rdev;
147 else if (major(st.st_dev) != 0) {
148 /* If this is not a device node then find the block
149 * device this file is stored on */
150 *dev = st.st_dev;
151
152 /* If this is a partition, try to get the originating
153 * block device */
154 block_get_whole_disk(*dev, dev);
155 } else {
156 log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p);
157 return -ENODEV;
158 }
8e274523 159
8e274523 160 return 0;
8e274523
LP
161}
162
4ad49000
LP
163static int whitelist_device(const char *path, const char *node, const char *acc) {
164 char buf[2+DECIMAL_STR_MAX(dev_t)*2+2+4];
165 struct stat st;
8c6db833 166 int r;
8e274523 167
4ad49000
LP
168 assert(path);
169 assert(acc);
8e274523 170
4ad49000
LP
171 if (stat(node, &st) < 0) {
172 log_warning("Couldn't stat device %s", node);
173 return -errno;
174 }
175
176 if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
177 log_warning("%s is not a device.", node);
178 return -ENODEV;
179 }
180
181 sprintf(buf,
182 "%c %u:%u %s",
183 S_ISCHR(st.st_mode) ? 'c' : 'b',
184 major(st.st_rdev), minor(st.st_rdev),
185 acc);
186
187 r = cg_set_attribute("devices", path, "devices.allow", buf);
188 if (r < 0)
189 log_warning("Failed to set devices.allow on %s: %s", path, strerror(-r));
190
191 return r;
8e274523
LP
192}
193
4ad49000
LP
194void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) {
195 int r;
196
197 assert(c);
198 assert(path);
8e274523 199
4ad49000
LP
200 if (mask == 0)
201 return;
8e274523 202
4ad49000
LP
203 if (mask & CGROUP_CPU) {
204 char buf[DECIMAL_STR_MAX(unsigned long) + 1];
8e274523 205
4ad49000
LP
206 sprintf(buf, "%lu\n", c->cpu_shares);
207 r = cg_set_attribute("cpu", path, "cpu.shares", buf);
208 if (r < 0)
209 log_warning("Failed to set cpu.shares on %s: %s", path, strerror(-r));
210 }
211
212 if (mask & CGROUP_BLKIO) {
213 char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1,
214 DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1,
215 DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)];
216 CGroupBlockIODeviceWeight *w;
217 CGroupBlockIODeviceBandwidth *b;
218
219 sprintf(buf, "%lu\n", c->blockio_weight);
220 r = cg_set_attribute("blkio", path, "blkio.weight", buf);
221 if (r < 0)
222 log_warning("Failed to set blkio.weight on %s: %s", path, strerror(-r));
223
224 /* FIXME: no way to reset this list */
225 LIST_FOREACH(device_weights, w, c->blockio_device_weights) {
226 dev_t dev;
227
228 r = lookup_blkio_device(w->path, &dev);
229 if (r < 0)
230 continue;
8e274523 231
4ad49000
LP
232 sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight);
233 r = cg_set_attribute("blkio", path, "blkio.weight_device", buf);
234 if (r < 0)
235 log_error("Failed to set blkio.weight_device on %s: %s", path, strerror(-r));
236 }
237
238 /* FIXME: no way to reset this list */
239 LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) {
240 const char *a;
241 dev_t dev;
242
243 r = lookup_blkio_device(b->path, &dev);
244 if (r < 0)
245 continue;
246
247 a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device";
248
249 sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth);
250 r = cg_set_attribute("blkio", path, a, buf);
251 if (r < 0)
252 log_error("Failed to set %s on %s: %s", a, path, strerror(-r));
d686d8a9 253 }
8e274523
LP
254 }
255
4ad49000 256 if (mask & CGROUP_MEMORY) {
6a94f2e9 257 if (c->memory_limit != (uint64_t) -1) {
e58cec11
LP
258 char buf[DECIMAL_STR_MAX(uint64_t) + 1];
259
6a94f2e9
G
260 sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
261 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
262 } else
263 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
8e274523 264
4ad49000
LP
265 if (r < 0)
266 log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
4ad49000 267 }
8e274523 268
4ad49000
LP
269 if (mask & CGROUP_DEVICE) {
270 CGroupDeviceAllow *a;
8e274523 271
4ad49000
LP
272 if (c->device_allow || c->device_policy != CGROUP_AUTO)
273 r = cg_set_attribute("devices", path, "devices.deny", "a");
274 else
275 r = cg_set_attribute("devices", path, "devices.allow", "a");
276 if (r < 0)
277 log_error("Failed to reset devices.list on %s: %s", path, strerror(-r));
fb385181 278
4ad49000
LP
279 if (c->device_policy == CGROUP_CLOSED ||
280 (c->device_policy == CGROUP_AUTO && c->device_allow)) {
281 static const char auto_devices[] =
282 "/dev/null\0" "rw\0"
283 "/dev/zero\0" "rw\0"
284 "/dev/full\0" "rw\0"
285 "/dev/random\0" "rw\0"
286 "/dev/urandom\0" "rw\0";
287
288 const char *x, *y;
289
290 NULSTR_FOREACH_PAIR(x, y, auto_devices)
291 whitelist_device(path, x, y);
292 }
293
294 LIST_FOREACH(device_allow, a, c->device_allow) {
295 char acc[4];
296 unsigned k = 0;
297
298 if (a->r)
299 acc[k++] = 'r';
300 if (a->w)
301 acc[k++] = 'w';
302 if (a->m)
303 acc[k++] = 'm';
fb385181 304
4ad49000
LP
305 if (k == 0)
306 continue;
fb385181 307
4ad49000
LP
308 acc[k++] = 0;
309 whitelist_device(path, a->path, acc);
310 }
311 }
fb385181
LP
312}
313
4ad49000
LP
314CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
315 CGroupControllerMask mask = 0;
8e274523 316
4ad49000 317 /* Figure out which controllers we need */
8e274523 318
4ad49000
LP
319 if (c->cpu_accounting || c->cpu_shares != 1024)
320 mask |= CGROUP_CPUACCT | CGROUP_CPU;
ecedd90f 321
4ad49000
LP
322 if (c->blockio_accounting ||
323 c->blockio_weight != 1000 ||
324 c->blockio_device_weights ||
325 c->blockio_device_bandwidths)
326 mask |= CGROUP_BLKIO;
ecedd90f 327
4ad49000 328 if (c->memory_accounting ||
ddca82ac 329 c->memory_limit != (uint64_t) -1)
4ad49000 330 mask |= CGROUP_MEMORY;
8e274523 331
4ad49000
LP
332 if (c->device_allow || c->device_policy != CGROUP_AUTO)
333 mask |= CGROUP_DEVICE;
334
335 return mask;
8e274523
LP
336}
337
4ad49000
LP
338static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
339 CGroupContext *c;
8e274523 340
4ad49000
LP
341 c = unit_get_cgroup_context(u);
342 if (!c)
343 return 0;
8e274523 344
4ad49000 345 return cgroup_context_get_mask(c);
8e274523
LP
346}
347
4ad49000
LP
348static CGroupControllerMask unit_get_members_mask(Unit *u) {
349 CGroupControllerMask mask = 0;
350 Unit *m;
351 Iterator i;
246aa6dd 352
4ad49000 353 assert(u);
246aa6dd 354
4ad49000 355 SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
246aa6dd 356
4ad49000 357 if (UNIT_DEREF(m->slice) != u)
246aa6dd
LP
358 continue;
359
4ad49000 360 mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
246aa6dd
LP
361 }
362
4ad49000 363 return mask;
246aa6dd
LP
364}
365
4ad49000
LP
366static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
367 assert(u);
246aa6dd 368
4ad49000
LP
369 if (!UNIT_ISSET(u->slice))
370 return 0;
371
372 /* Sibling propagation is only relevant for weight-based
373 * controllers, so let's mask out everything else */
374 return unit_get_members_mask(UNIT_DEREF(u->slice)) &
375 (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
246aa6dd
LP
376}
377
4ad49000 378static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
a94042fa 379 _cleanup_free_ char *path;
4ad49000 380 int r;
a94042fa 381 bool was_in_hash = false;
64747e2d 382
4ad49000 383 assert(u);
64747e2d 384
4ad49000
LP
385 path = unit_default_cgroup_path(u);
386 if (!path)
a94042fa 387 return log_oom();
64747e2d 388
0a1eb06d 389 r = hashmap_put(u->manager->cgroup_unit, path, u);
b58b8e11 390 if (r == 0)
a94042fa
ZJS
391 was_in_hash = true;
392 else if (r < 0) {
393 log_error(r == -EEXIST ?
394 "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s",
395 path, strerror(-r));
0a1eb06d 396 return r;
b58b8e11 397 }
0a1eb06d 398
4ad49000 399 /* First, create our own group */
13b84ec7 400 r = cg_create_everywhere(u->manager->cgroup_supported, mask, path);
4ad49000
LP
401 if (r < 0)
402 log_error("Failed to create cgroup %s: %s", path, strerror(-r));
64747e2d 403
4ad49000 404 /* Then, possibly move things over */
13b84ec7
LP
405 if (u->cgroup_path) {
406 r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, path);
64747e2d 407 if (r < 0)
a94042fa
ZJS
408 log_error("Failed to migrate cgroup from %s to %s: %s",
409 u->cgroup_path, path, strerror(-r));
64747e2d
LP
410 }
411
a94042fa
ZJS
412 if (!was_in_hash) {
413 /* Remember the new data */
b58b8e11
HH
414 free(u->cgroup_path);
415 u->cgroup_path = path;
a94042fa 416 path = NULL;
b58b8e11
HH
417 }
418
4ad49000
LP
419 u->cgroup_realized = true;
420 u->cgroup_mask = mask;
421
64747e2d
LP
422 return 0;
423}
424
0a1eb06d 425static int unit_realize_cgroup_now(Unit *u) {
4ad49000 426 CGroupControllerMask mask;
64747e2d 427
4ad49000 428 assert(u);
64747e2d 429
4ad49000 430 if (u->in_cgroup_queue) {
71fda00f 431 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
4ad49000
LP
432 u->in_cgroup_queue = false;
433 }
64747e2d 434
4ad49000
LP
435 mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
436 mask &= u->manager->cgroup_supported;
64747e2d 437
4ad49000
LP
438 if (u->cgroup_realized &&
439 u->cgroup_mask == mask)
0a1eb06d 440 return 0;
64747e2d 441
4ad49000 442 /* First, realize parents */
3d040cf2
HH
443 if (UNIT_ISSET(u->slice))
444 unit_realize_cgroup_now(UNIT_DEREF(u->slice));
4ad49000
LP
445
446 /* And then do the real work */
0a1eb06d 447 return unit_create_cgroups(u, mask);
64747e2d
LP
448}
449
4ad49000 450static void unit_add_to_cgroup_queue(Unit *u) {
ecedd90f 451
4ad49000
LP
452 if (u->in_cgroup_queue)
453 return;
8e274523 454
71fda00f 455 LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u);
4ad49000
LP
456 u->in_cgroup_queue = true;
457}
8c6db833 458
4ad49000
LP
459unsigned manager_dispatch_cgroup_queue(Manager *m) {
460 Unit *i;
461 unsigned n = 0;
ecedd90f 462
4ad49000
LP
463 while ((i = m->cgroup_queue)) {
464 assert(i->in_cgroup_queue);
ecedd90f 465
0a1eb06d
LP
466 if (unit_realize_cgroup_now(i) >= 0)
467 cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
468
4ad49000
LP
469 n++;
470 }
ecedd90f 471
4ad49000 472 return n;
8e274523
LP
473}
474
4ad49000
LP
475static void unit_queue_siblings(Unit *u) {
476 Unit *slice;
ca949c9d 477
4ad49000
LP
478 /* This adds the siblings of the specified unit and the
479 * siblings of all parent units to the cgroup queue. (But
480 * neither the specified unit itself nor the parents.) */
481
482 while ((slice = UNIT_DEREF(u->slice))) {
483 Iterator i;
484 Unit *m;
8f53a7b8 485
4ad49000
LP
486 SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
487 if (m == u)
488 continue;
8e274523 489
4ad49000 490 if (UNIT_DEREF(m->slice) != slice)
50159e6a 491 continue;
8e274523 492
4ad49000 493 unit_add_to_cgroup_queue(m);
50159e6a
LP
494 }
495
4ad49000 496 u = slice;
8e274523 497 }
4ad49000
LP
498}
499
0a1eb06d 500int unit_realize_cgroup(Unit *u) {
4ad49000 501 CGroupContext *c;
0a1eb06d 502 int r;
4ad49000
LP
503
504 assert(u);
505
506 c = unit_get_cgroup_context(u);
507 if (!c)
0a1eb06d 508 return 0;
8e274523 509
4ad49000
LP
510 /* So, here's the deal: when realizing the cgroups for this
511 * unit, we need to first create all parents, but there's more
512 * actually: for the weight-based controllers we also need to
513 * make sure that all our siblings (i.e. units that are in the
f3669545 514 * same slice as we are) have cgroups, too. Otherwise things
4ad49000
LP
515 * would become very uneven as each of their processes would
516 * get as much resources as all our group together. This call
517 * will synchronously create the parent cgroups, but will
518 * defer work on the siblings to the next event loop
519 * iteration. */
ca949c9d 520
4ad49000
LP
521 /* Add all sibling slices to the cgroup queue. */
522 unit_queue_siblings(u);
523
524 /* And realize this one now */
0a1eb06d 525 r = unit_realize_cgroup_now(u);
4ad49000
LP
526
527 /* And apply the values */
0a1eb06d
LP
528 if (r >= 0)
529 cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
530
531 return r;
8e274523
LP
532}
533
4ad49000 534void unit_destroy_cgroup(Unit *u) {
8e274523
LP
535 int r;
536
4ad49000 537 assert(u);
8e274523 538
4ad49000
LP
539 if (!u->cgroup_path)
540 return;
8e274523 541
13b84ec7 542 r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
4ad49000 543 if (r < 0)
376dd21d 544 log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
8e274523 545
0a1eb06d
LP
546 hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
547
4ad49000
LP
548 free(u->cgroup_path);
549 u->cgroup_path = NULL;
550 u->cgroup_realized = false;
551 u->cgroup_mask = 0;
0a1eb06d 552
8e274523
LP
553}
554
4ad49000
LP
555pid_t unit_search_main_pid(Unit *u) {
556 _cleanup_fclose_ FILE *f = NULL;
557 pid_t pid = 0, npid, mypid;
558
559 assert(u);
560
561 if (!u->cgroup_path)
562 return 0;
563
564 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
565 return 0;
566
567 mypid = getpid();
568 while (cg_read_pid(f, &npid) > 0) {
569 pid_t ppid;
570
571 if (npid == pid)
572 continue;
8e274523 573
4ad49000
LP
574 /* Ignore processes that aren't our kids */
575 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
576 continue;
8e274523 577
4ad49000
LP
578 if (pid != 0) {
579 /* Dang, there's more than one daemonized PID
580 in this group, so we don't know what process
581 is the main process. */
582 pid = 0;
583 break;
584 }
8e274523 585
4ad49000 586 pid = npid;
8e274523
LP
587 }
588
4ad49000 589 return pid;
8e274523
LP
590}
591
8e274523 592int manager_setup_cgroup(Manager *m) {
9444b1f2 593 _cleanup_free_ char *path = NULL;
15c60e99 594 char *e;
8e274523 595 int r;
8e274523
LP
596
597 assert(m);
598
e5a53dc7 599 /* 0. Be nice to Ingo Molnar #628004 */
0c85a4f3 600 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
e5a53dc7
LP
601 log_warning("No control group support available, not creating root group.");
602 return 0;
603 }
604
35d2e7ec 605 /* 1. Determine hierarchy */
9444b1f2
LP
606 free(m->cgroup_root);
607 m->cgroup_root = NULL;
608
609 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
9156e799 610 if (r < 0) {
12235040 611 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
a32360f1 612 return r;
12235040 613 }
8e274523 614
15c60e99
LP
615 /* LEGACY: Already in /system.slice? If so, let's cut this
616 * off. This is to support live upgrades from older systemd
617 * versions where PID 1 was moved there. */
9444b1f2
LP
618 if (m->running_as == SYSTEMD_SYSTEM) {
619 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
15c60e99
LP
620 if (!e)
621 e = endswith(m->cgroup_root, "/system");
9444b1f2
LP
622 if (e)
623 *e = 0;
0baf24dd 624 }
7ccfb64a 625
9444b1f2
LP
626 /* And make sure to store away the root value without trailing
627 * slash, even for the root dir, so that we can easily prepend
628 * it everywhere. */
629 if (streq(m->cgroup_root, "/"))
630 m->cgroup_root[0] = 0;
8e274523 631
35d2e7ec 632 /* 2. Show data */
9444b1f2 633 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
3474ae3c 634 if (r < 0) {
12235040 635 log_error("Cannot find cgroup mount point: %s", strerror(-r));
a32360f1 636 return r;
12235040 637 }
8e274523 638
c6c18be3
LP
639 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
640
35d2e7ec 641 /* 3. Install agent */
a32360f1
LP
642 if (m->running_as == SYSTEMD_SYSTEM) {
643 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
644 if (r < 0)
645 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
646 else if (r > 0)
647 log_debug("Installed release agent.");
648 else
649 log_debug("Release agent already installed.");
650 }
8e274523 651
15c60e99
LP
652 /* 4. Make sure we are in the root cgroup */
653 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
9156e799 654 if (r < 0) {
8e274523 655 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
a32360f1 656 return r;
c6c18be3
LP
657 }
658
35d2e7ec 659 /* 5. And pin it, so that it cannot be unmounted */
c6c18be3
LP
660 if (m->pin_cgroupfs_fd >= 0)
661 close_nointr_nofail(m->pin_cgroupfs_fd);
662
9156e799
LP
663 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
664 if (r < 0) {
12235040 665 log_error("Failed to open pin file: %m");
a32360f1 666 return -errno;
c6c18be3
LP
667 }
668
4ad49000
LP
669 /* 6. Figure out which controllers are supported */
670 m->cgroup_supported = cg_mask_supported();
9156e799 671
e58cec11
LP
672 /* 7. Always enable hierarchial support if it exists... */
673 cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
674
a32360f1 675 return 0;
8e274523
LP
676}
677
c6c18be3 678void manager_shutdown_cgroup(Manager *m, bool delete) {
8e274523
LP
679 assert(m);
680
9444b1f2
LP
681 /* We can't really delete the group, since we are in it. But
682 * let's trim it. */
683 if (delete && m->cgroup_root)
684 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
8e274523 685
c6c18be3
LP
686 if (m->pin_cgroupfs_fd >= 0) {
687 close_nointr_nofail(m->pin_cgroupfs_fd);
688 m->pin_cgroupfs_fd = -1;
689 }
690
9444b1f2
LP
691 free(m->cgroup_root);
692 m->cgroup_root = NULL;
8e274523
LP
693}
694
4ad49000 695Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
acb14d31 696 char *p;
4ad49000 697 Unit *u;
acb14d31
LP
698
699 assert(m);
700 assert(cgroup);
acb14d31 701
4ad49000
LP
702 u = hashmap_get(m->cgroup_unit, cgroup);
703 if (u)
704 return u;
acb14d31 705
8e70580b 706 p = strdupa(cgroup);
acb14d31
LP
707 for (;;) {
708 char *e;
709
710 e = strrchr(p, '/');
4ad49000
LP
711 if (e == p || !e)
712 return NULL;
acb14d31
LP
713
714 *e = 0;
715
4ad49000
LP
716 u = hashmap_get(m->cgroup_unit, p);
717 if (u)
718 return u;
acb14d31
LP
719 }
720}
721
4ad49000
LP
722Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
723 _cleanup_free_ char *cgroup = NULL;
acb14d31 724 int r;
8e274523 725
8c47c732
LP
726 assert(m);
727
728 if (pid <= 1)
729 return NULL;
730
4ad49000
LP
731 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
732 if (r < 0)
6dde1f33
LP
733 return NULL;
734
4ad49000 735 return manager_get_unit_by_cgroup(m, cgroup);
6dde1f33 736}
4fbf50b3 737
4ad49000
LP
738int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
739 Unit *u;
740 int r;
4fbf50b3 741
4ad49000
LP
742 assert(m);
743 assert(cgroup);
4fbf50b3 744
4ad49000 745 u = manager_get_unit_by_cgroup(m, cgroup);
b56c28c3 746 if (u) {
06025d91
LP
747 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
748 if (r > 0) {
749 if (UNIT_VTABLE(u)->notify_cgroup_empty)
750 UNIT_VTABLE(u)->notify_cgroup_empty(u);
b56c28c3 751
06025d91
LP
752 unit_add_to_gc_queue(u);
753 }
b56c28c3 754 }
2633eb83 755
4ad49000 756 return 0;
4fbf50b3
LP
757}
758
4ad49000
LP
759static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
760 [CGROUP_AUTO] = "auto",
761 [CGROUP_CLOSED] = "closed",
762 [CGROUP_STRICT] = "strict",
763};
4fbf50b3 764
4ad49000 765DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);