]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/cgroup.c
Fix obsolete references to systemd-random-seed-load.service
[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
44 LIST_REMOVE(CGroupDeviceAllow, device_allow, c->device_allow, a);
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
53 LIST_REMOVE(CGroupBlockIODeviceWeight, device_weights, c->blockio_device_weights, w);
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
4ad49000
LP
62 LIST_REMOVE(CGroupBlockIODeviceBandwidth, device_bandwidths, c->blockio_device_bandwidths, b);
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
LP
256 if (mask & CGROUP_MEMORY) {
257 char buf[DECIMAL_STR_MAX(uint64_t) + 1];
6a94f2e9
G
258 if (c->memory_limit != (uint64_t) -1) {
259 sprintf(buf, "%" PRIu64 "\n", c->memory_limit);
260 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf);
261 } else
262 r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1");
8e274523 263
4ad49000
LP
264 if (r < 0)
265 log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
4ad49000 266 }
8e274523 267
4ad49000
LP
268 if (mask & CGROUP_DEVICE) {
269 CGroupDeviceAllow *a;
8e274523 270
4ad49000
LP
271 if (c->device_allow || c->device_policy != CGROUP_AUTO)
272 r = cg_set_attribute("devices", path, "devices.deny", "a");
273 else
274 r = cg_set_attribute("devices", path, "devices.allow", "a");
275 if (r < 0)
276 log_error("Failed to reset devices.list on %s: %s", path, strerror(-r));
fb385181 277
4ad49000
LP
278 if (c->device_policy == CGROUP_CLOSED ||
279 (c->device_policy == CGROUP_AUTO && c->device_allow)) {
280 static const char auto_devices[] =
281 "/dev/null\0" "rw\0"
282 "/dev/zero\0" "rw\0"
283 "/dev/full\0" "rw\0"
284 "/dev/random\0" "rw\0"
285 "/dev/urandom\0" "rw\0";
286
287 const char *x, *y;
288
289 NULSTR_FOREACH_PAIR(x, y, auto_devices)
290 whitelist_device(path, x, y);
291 }
292
293 LIST_FOREACH(device_allow, a, c->device_allow) {
294 char acc[4];
295 unsigned k = 0;
296
297 if (a->r)
298 acc[k++] = 'r';
299 if (a->w)
300 acc[k++] = 'w';
301 if (a->m)
302 acc[k++] = 'm';
fb385181 303
4ad49000
LP
304 if (k == 0)
305 continue;
fb385181 306
4ad49000
LP
307 acc[k++] = 0;
308 whitelist_device(path, a->path, acc);
309 }
310 }
fb385181
LP
311}
312
4ad49000
LP
313CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
314 CGroupControllerMask mask = 0;
8e274523 315
4ad49000 316 /* Figure out which controllers we need */
8e274523 317
4ad49000
LP
318 if (c->cpu_accounting || c->cpu_shares != 1024)
319 mask |= CGROUP_CPUACCT | CGROUP_CPU;
ecedd90f 320
4ad49000
LP
321 if (c->blockio_accounting ||
322 c->blockio_weight != 1000 ||
323 c->blockio_device_weights ||
324 c->blockio_device_bandwidths)
325 mask |= CGROUP_BLKIO;
ecedd90f 326
4ad49000 327 if (c->memory_accounting ||
ddca82ac 328 c->memory_limit != (uint64_t) -1)
4ad49000 329 mask |= CGROUP_MEMORY;
8e274523 330
4ad49000
LP
331 if (c->device_allow || c->device_policy != CGROUP_AUTO)
332 mask |= CGROUP_DEVICE;
333
334 return mask;
8e274523
LP
335}
336
4ad49000
LP
337static CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
338 CGroupContext *c;
8e274523 339
4ad49000
LP
340 c = unit_get_cgroup_context(u);
341 if (!c)
342 return 0;
8e274523 343
4ad49000 344 return cgroup_context_get_mask(c);
8e274523
LP
345}
346
4ad49000
LP
347static CGroupControllerMask unit_get_members_mask(Unit *u) {
348 CGroupControllerMask mask = 0;
349 Unit *m;
350 Iterator i;
246aa6dd 351
4ad49000 352 assert(u);
246aa6dd 353
4ad49000 354 SET_FOREACH(m, u->dependencies[UNIT_BEFORE], i) {
246aa6dd 355
4ad49000 356 if (UNIT_DEREF(m->slice) != u)
246aa6dd
LP
357 continue;
358
4ad49000 359 mask |= unit_get_cgroup_mask(m) | unit_get_members_mask(m);
246aa6dd
LP
360 }
361
4ad49000 362 return mask;
246aa6dd
LP
363}
364
4ad49000
LP
365static CGroupControllerMask unit_get_siblings_mask(Unit *u) {
366 assert(u);
246aa6dd 367
4ad49000
LP
368 if (!UNIT_ISSET(u->slice))
369 return 0;
370
371 /* Sibling propagation is only relevant for weight-based
372 * controllers, so let's mask out everything else */
373 return unit_get_members_mask(UNIT_DEREF(u->slice)) &
374 (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
246aa6dd
LP
375}
376
4ad49000
LP
377static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
378 char *path = NULL;
379 int r;
b58b8e11 380 bool is_in_hash = false;
64747e2d 381
4ad49000 382 assert(u);
64747e2d 383
4ad49000
LP
384 path = unit_default_cgroup_path(u);
385 if (!path)
386 return -ENOMEM;
64747e2d 387
0a1eb06d 388 r = hashmap_put(u->manager->cgroup_unit, path, u);
b58b8e11
HH
389 if (r == 0)
390 is_in_hash = true;
391
392 if (r < 0) {
b58b8e11 393 log_error("cgroup %s exists already: %s", path, strerror(-r));
81c68af0 394 free(path);
0a1eb06d 395 return r;
b58b8e11 396 }
0a1eb06d 397
4ad49000
LP
398 /* First, create our own group */
399 r = cg_create_with_mask(mask, path);
400 if (r < 0)
401 log_error("Failed to create cgroup %s: %s", path, strerror(-r));
64747e2d 402
4ad49000
LP
403 /* Then, possibly move things over */
404 if (u->cgroup_path && !streq(path, u->cgroup_path)) {
405 r = cg_migrate_with_mask(mask, u->cgroup_path, path);
64747e2d 406 if (r < 0)
4ad49000 407 log_error("Failed to migrate cgroup %s: %s", path, strerror(-r));
64747e2d
LP
408 }
409
b58b8e11
HH
410 if (!is_in_hash) {
411 /* And remember the new data */
412 free(u->cgroup_path);
413 u->cgroup_path = path;
414 }
415
4ad49000
LP
416 u->cgroup_realized = true;
417 u->cgroup_mask = mask;
418
64747e2d
LP
419 return 0;
420}
421
0a1eb06d 422static int unit_realize_cgroup_now(Unit *u) {
4ad49000 423 CGroupControllerMask mask;
64747e2d 424
4ad49000 425 assert(u);
64747e2d 426
4ad49000
LP
427 if (u->in_cgroup_queue) {
428 LIST_REMOVE(Unit, cgroup_queue, u->manager->cgroup_queue, u);
429 u->in_cgroup_queue = false;
430 }
64747e2d 431
4ad49000
LP
432 mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
433 mask &= u->manager->cgroup_supported;
64747e2d 434
4ad49000
LP
435 if (u->cgroup_realized &&
436 u->cgroup_mask == mask)
0a1eb06d 437 return 0;
64747e2d 438
4ad49000 439 /* First, realize parents */
3d040cf2
HH
440 if (UNIT_ISSET(u->slice))
441 unit_realize_cgroup_now(UNIT_DEREF(u->slice));
4ad49000
LP
442
443 /* And then do the real work */
0a1eb06d 444 return unit_create_cgroups(u, mask);
64747e2d
LP
445}
446
4ad49000 447static void unit_add_to_cgroup_queue(Unit *u) {
ecedd90f 448
4ad49000
LP
449 if (u->in_cgroup_queue)
450 return;
8e274523 451
4ad49000
LP
452 LIST_PREPEND(Unit, cgroup_queue, u->manager->cgroup_queue, u);
453 u->in_cgroup_queue = true;
454}
8c6db833 455
4ad49000
LP
456unsigned manager_dispatch_cgroup_queue(Manager *m) {
457 Unit *i;
458 unsigned n = 0;
ecedd90f 459
4ad49000
LP
460 while ((i = m->cgroup_queue)) {
461 assert(i->in_cgroup_queue);
ecedd90f 462
0a1eb06d
LP
463 if (unit_realize_cgroup_now(i) >= 0)
464 cgroup_context_apply(unit_get_cgroup_context(i), i->cgroup_mask, i->cgroup_path);
465
4ad49000
LP
466 n++;
467 }
ecedd90f 468
4ad49000 469 return n;
8e274523
LP
470}
471
4ad49000
LP
472static void unit_queue_siblings(Unit *u) {
473 Unit *slice;
ca949c9d 474
4ad49000
LP
475 /* This adds the siblings of the specified unit and the
476 * siblings of all parent units to the cgroup queue. (But
477 * neither the specified unit itself nor the parents.) */
478
479 while ((slice = UNIT_DEREF(u->slice))) {
480 Iterator i;
481 Unit *m;
8f53a7b8 482
4ad49000
LP
483 SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
484 if (m == u)
485 continue;
8e274523 486
4ad49000 487 if (UNIT_DEREF(m->slice) != slice)
50159e6a 488 continue;
8e274523 489
4ad49000 490 unit_add_to_cgroup_queue(m);
50159e6a
LP
491 }
492
4ad49000 493 u = slice;
8e274523 494 }
4ad49000
LP
495}
496
0a1eb06d 497int unit_realize_cgroup(Unit *u) {
4ad49000 498 CGroupContext *c;
0a1eb06d 499 int r;
4ad49000
LP
500
501 assert(u);
502
503 c = unit_get_cgroup_context(u);
504 if (!c)
0a1eb06d 505 return 0;
8e274523 506
4ad49000
LP
507 /* So, here's the deal: when realizing the cgroups for this
508 * unit, we need to first create all parents, but there's more
509 * actually: for the weight-based controllers we also need to
510 * make sure that all our siblings (i.e. units that are in the
511 * same slice as we are) have cgroup too. Otherwise things
512 * would become very uneven as each of their processes would
513 * get as much resources as all our group together. This call
514 * will synchronously create the parent cgroups, but will
515 * defer work on the siblings to the next event loop
516 * iteration. */
ca949c9d 517
4ad49000
LP
518 /* Add all sibling slices to the cgroup queue. */
519 unit_queue_siblings(u);
520
521 /* And realize this one now */
0a1eb06d 522 r = unit_realize_cgroup_now(u);
4ad49000
LP
523
524 /* And apply the values */
0a1eb06d
LP
525 if (r >= 0)
526 cgroup_context_apply(c, u->cgroup_mask, u->cgroup_path);
527
528 return r;
8e274523
LP
529}
530
4ad49000 531void unit_destroy_cgroup(Unit *u) {
8e274523
LP
532 int r;
533
4ad49000 534 assert(u);
8e274523 535
4ad49000
LP
536 if (!u->cgroup_path)
537 return;
8e274523 538
8a841929 539 r = cg_trim_with_mask(u->cgroup_mask, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
4ad49000 540 if (r < 0)
376dd21d 541 log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
8e274523 542
0a1eb06d
LP
543 hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
544
4ad49000
LP
545 free(u->cgroup_path);
546 u->cgroup_path = NULL;
547 u->cgroup_realized = false;
548 u->cgroup_mask = 0;
0a1eb06d 549
8e274523
LP
550}
551
4ad49000
LP
552pid_t unit_search_main_pid(Unit *u) {
553 _cleanup_fclose_ FILE *f = NULL;
554 pid_t pid = 0, npid, mypid;
555
556 assert(u);
557
558 if (!u->cgroup_path)
559 return 0;
560
561 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
562 return 0;
563
564 mypid = getpid();
565 while (cg_read_pid(f, &npid) > 0) {
566 pid_t ppid;
567
568 if (npid == pid)
569 continue;
8e274523 570
4ad49000
LP
571 /* Ignore processes that aren't our kids */
572 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
573 continue;
8e274523 574
4ad49000
LP
575 if (pid != 0) {
576 /* Dang, there's more than one daemonized PID
577 in this group, so we don't know what process
578 is the main process. */
579 pid = 0;
580 break;
581 }
8e274523 582
4ad49000 583 pid = npid;
8e274523
LP
584 }
585
4ad49000 586 return pid;
8e274523
LP
587}
588
8e274523 589int manager_setup_cgroup(Manager *m) {
9444b1f2 590 _cleanup_free_ char *path = NULL;
8e274523 591 int r;
9444b1f2 592 char *e, *a;
8e274523
LP
593
594 assert(m);
595
e5a53dc7 596 /* 0. Be nice to Ingo Molnar #628004 */
0c85a4f3 597 if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
e5a53dc7
LP
598 log_warning("No control group support available, not creating root group.");
599 return 0;
600 }
601
35d2e7ec 602 /* 1. Determine hierarchy */
9444b1f2
LP
603 free(m->cgroup_root);
604 m->cgroup_root = NULL;
605
606 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
9156e799 607 if (r < 0) {
12235040 608 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
a32360f1 609 return r;
12235040 610 }
8e274523 611
9444b1f2
LP
612 /* Already in /system.slice? If so, let's cut this off again */
613 if (m->running_as == SYSTEMD_SYSTEM) {
614 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
615 if (e)
616 *e = 0;
0baf24dd 617 }
7ccfb64a 618
9444b1f2
LP
619 /* And make sure to store away the root value without trailing
620 * slash, even for the root dir, so that we can easily prepend
621 * it everywhere. */
622 if (streq(m->cgroup_root, "/"))
623 m->cgroup_root[0] = 0;
8e274523 624
35d2e7ec 625 /* 2. Show data */
9444b1f2 626 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
3474ae3c 627 if (r < 0) {
12235040 628 log_error("Cannot find cgroup mount point: %s", strerror(-r));
a32360f1 629 return r;
12235040 630 }
8e274523 631
c6c18be3
LP
632 log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
633
35d2e7ec 634 /* 3. Install agent */
a32360f1
LP
635 if (m->running_as == SYSTEMD_SYSTEM) {
636 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
637 if (r < 0)
638 log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
639 else if (r > 0)
640 log_debug("Installed release agent.");
641 else
642 log_debug("Release agent already installed.");
643 }
8e274523 644
9444b1f2 645 /* 4. Realize the system slice and put us in there */
be2c1bd2
LP
646 if (m->running_as == SYSTEMD_SYSTEM) {
647 a = strappenda(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
648 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, a, 0);
649 } else
650 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
9156e799 651 if (r < 0) {
8e274523 652 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
a32360f1 653 return r;
c6c18be3
LP
654 }
655
35d2e7ec 656 /* 5. And pin it, so that it cannot be unmounted */
c6c18be3
LP
657 if (m->pin_cgroupfs_fd >= 0)
658 close_nointr_nofail(m->pin_cgroupfs_fd);
659
9156e799
LP
660 m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
661 if (r < 0) {
12235040 662 log_error("Failed to open pin file: %m");
a32360f1 663 return -errno;
c6c18be3
LP
664 }
665
4ad49000
LP
666 /* 6. Figure out which controllers are supported */
667 m->cgroup_supported = cg_mask_supported();
9156e799 668
a32360f1 669 return 0;
8e274523
LP
670}
671
c6c18be3 672void manager_shutdown_cgroup(Manager *m, bool delete) {
8e274523
LP
673 assert(m);
674
9444b1f2
LP
675 /* We can't really delete the group, since we are in it. But
676 * let's trim it. */
677 if (delete && m->cgroup_root)
678 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
8e274523 679
c6c18be3
LP
680 if (m->pin_cgroupfs_fd >= 0) {
681 close_nointr_nofail(m->pin_cgroupfs_fd);
682 m->pin_cgroupfs_fd = -1;
683 }
684
9444b1f2
LP
685 free(m->cgroup_root);
686 m->cgroup_root = NULL;
8e274523
LP
687}
688
4ad49000 689Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
acb14d31 690 char *p;
4ad49000 691 Unit *u;
acb14d31
LP
692
693 assert(m);
694 assert(cgroup);
acb14d31 695
4ad49000
LP
696 u = hashmap_get(m->cgroup_unit, cgroup);
697 if (u)
698 return u;
acb14d31 699
8e70580b 700 p = strdupa(cgroup);
acb14d31
LP
701 for (;;) {
702 char *e;
703
704 e = strrchr(p, '/');
4ad49000
LP
705 if (e == p || !e)
706 return NULL;
acb14d31
LP
707
708 *e = 0;
709
4ad49000
LP
710 u = hashmap_get(m->cgroup_unit, p);
711 if (u)
712 return u;
acb14d31
LP
713 }
714}
715
4ad49000
LP
716Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
717 _cleanup_free_ char *cgroup = NULL;
acb14d31 718 int r;
8e274523 719
8c47c732
LP
720 assert(m);
721
722 if (pid <= 1)
723 return NULL;
724
4ad49000
LP
725 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
726 if (r < 0)
6dde1f33
LP
727 return NULL;
728
4ad49000 729 return manager_get_unit_by_cgroup(m, cgroup);
6dde1f33 730}
4fbf50b3 731
4ad49000
LP
732int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
733 Unit *u;
734 int r;
4fbf50b3 735
4ad49000
LP
736 assert(m);
737 assert(cgroup);
4fbf50b3 738
4ad49000 739 u = manager_get_unit_by_cgroup(m, cgroup);
b56c28c3 740 if (u) {
06025d91
LP
741 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
742 if (r > 0) {
743 if (UNIT_VTABLE(u)->notify_cgroup_empty)
744 UNIT_VTABLE(u)->notify_cgroup_empty(u);
b56c28c3 745
06025d91
LP
746 unit_add_to_gc_queue(u);
747 }
b56c28c3 748 }
2633eb83 749
4ad49000 750 return 0;
4fbf50b3
LP
751}
752
4ad49000
LP
753static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
754 [CGROUP_AUTO] = "auto",
755 [CGROUP_CLOSED] = "closed",
756 [CGROUP_STRICT] = "strict",
757};
4fbf50b3 758
4ad49000 759DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);