chiark / gitweb /
core: rework cgroup mask propagation
[elogind.git] / src / core / cgroup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
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
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23
24 #include "path-util.h"
25 #include "special.h"
26 #include "cgroup-util.h"
27 #include "cgroup.h"
28
29 void 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;
36         c->memory_limit = (uint64_t) -1;
37         c->blockio_weight = 1000;
38 }
39
40 void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) {
41         assert(c);
42         assert(a);
43
44         LIST_REMOVE(device_allow, c->device_allow, a);
45         free(a->path);
46         free(a);
47 }
48
49 void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) {
50         assert(c);
51         assert(w);
52
53         LIST_REMOVE(device_weights, c->blockio_device_weights, w);
54         free(w->path);
55         free(w);
56 }
57
58 void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b) {
59         assert(c);
60         assert(b);
61
62         LIST_REMOVE(device_bandwidths, c->blockio_device_bandwidths, b);
63         free(b->path);
64         free(b);
65 }
66
67 void 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
80 void 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"
95                 "%sBlockIOWeight=%lu\n"
96                 "%sMemoryLimit=%" PRIu64 "\n"
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,
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,
115                         "%sBlockIODeviceWeight=%s %lu",
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
132 static 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);
140         if (r < 0) {
141                 log_warning("Couldn't stat device %s: %m", p);
142                 return -errno;
143         }
144
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         }
159
160         return 0;
161 }
162
163 static 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;
166         int r;
167
168         assert(path);
169         assert(acc);
170
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;
192 }
193
194 void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path) {
195         int r;
196
197         assert(c);
198         assert(path);
199
200         if (mask == 0)
201                 return;
202
203         if (mask & CGROUP_CPU) {
204                 char buf[DECIMAL_STR_MAX(unsigned long) + 1];
205
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;
231
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));
253                 }
254         }
255
256         if (mask & CGROUP_MEMORY) {
257                 if (c->memory_limit != (uint64_t) -1) {
258                         char buf[DECIMAL_STR_MAX(uint64_t) + 1];
259
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");
264
265                 if (r < 0)
266                         log_error("Failed to set memory.limit_in_bytes on %s: %s", path, strerror(-r));
267         }
268
269         if (mask & CGROUP_DEVICE) {
270                 CGroupDeviceAllow *a;
271
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));
278
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';
304
305                         if (k == 0)
306                                 continue;
307
308                         acc[k++] = 0;
309                         whitelist_device(path, a->path, acc);
310                 }
311         }
312 }
313
314 CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) {
315         CGroupControllerMask mask = 0;
316
317         /* Figure out which controllers we need */
318
319         if (c->cpu_accounting || c->cpu_shares != 1024)
320                 mask |= CGROUP_CPUACCT | CGROUP_CPU;
321
322         if (c->blockio_accounting ||
323             c->blockio_weight != 1000 ||
324             c->blockio_device_weights ||
325             c->blockio_device_bandwidths)
326                 mask |= CGROUP_BLKIO;
327
328         if (c->memory_accounting ||
329             c->memory_limit != (uint64_t) -1)
330                 mask |= CGROUP_MEMORY;
331
332         if (c->device_allow || c->device_policy != CGROUP_AUTO)
333                 mask |= CGROUP_DEVICE;
334
335         return mask;
336 }
337
338 CGroupControllerMask unit_get_cgroup_mask(Unit *u) {
339         CGroupContext *c;
340
341         c = unit_get_cgroup_context(u);
342         if (!c)
343                 return 0;
344
345         return cgroup_context_get_mask(c);
346 }
347
348 CGroupControllerMask unit_get_members_mask(Unit *u) {
349         assert(u);
350
351         if (u->cgroup_members_mask_valid)
352                 return u->cgroup_members_mask;
353
354         u->cgroup_members_mask = 0;
355
356         if (u->type == UNIT_SLICE) {
357                 Unit *member;
358                 Iterator i;
359
360                 SET_FOREACH(member, u->dependencies[UNIT_BEFORE], i) {
361
362                         if (member == u)
363                                 continue;
364
365                          if (UNIT_DEREF(member->slice) != u)
366                                 continue;
367
368                         u->cgroup_members_mask |=
369                                 unit_get_cgroup_mask(member) |
370                                 unit_get_members_mask(member);
371                 }
372         }
373
374         u->cgroup_members_mask_valid = true;
375         return u->cgroup_members_mask;
376 }
377
378 CGroupControllerMask unit_get_siblings_mask(Unit *u) {
379         CGroupControllerMask m;
380
381         assert(u);
382
383         if (UNIT_ISSET(u->slice))
384                 m = unit_get_members_mask(UNIT_DEREF(u->slice));
385         else
386                 m = unit_get_cgroup_mask(u) | unit_get_members_mask(u);
387
388         /* Sibling propagation is only relevant for weight-based
389          * controllers, so let's mask out everything else */
390         return m & (CGROUP_CPU|CGROUP_BLKIO|CGROUP_CPUACCT);
391 }
392
393 CGroupControllerMask unit_get_target_mask(Unit *u) {
394         CGroupControllerMask mask;
395
396         mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
397         mask &= u->manager->cgroup_supported;
398
399         return mask;
400 }
401
402 /* Recurse from a unit up through its containing slices, propagating
403  * mask bits upward. A unit is also member of itself. */
404 void unit_update_cgroup_members_masks(Unit *u) {
405         CGroupControllerMask m;
406         bool more;
407
408         assert(u);
409
410         /* Calculate subtree mask */
411         m = unit_get_cgroup_mask(u) | unit_get_members_mask(u);
412
413         /* See if anything changed from the previous invocation. If
414          * not, we're done. */
415         if (u->cgroup_subtree_mask_valid && m == u->cgroup_subtree_mask)
416                 return;
417
418         more =
419                 u->cgroup_subtree_mask_valid &&
420                 ((m & ~u->cgroup_subtree_mask) != 0) &&
421                 ((~m & u->cgroup_subtree_mask) == 0);
422
423         u->cgroup_subtree_mask = m;
424         u->cgroup_subtree_mask_valid = true;
425
426         if (UNIT_ISSET(u->slice)) {
427                 Unit *s = UNIT_DEREF(u->slice);
428
429                 if (more)
430                         /* There's more set now than before. We
431                          * propagate the new mask to the parent's mask
432                          * (not caring if it actually was valid or
433                          * not). */
434
435                         s->cgroup_members_mask |= m;
436
437                 else
438                         /* There's less set now than before (or we
439                          * don't know), we need to recalculate
440                          * everything, so let's invalidate the
441                          * parent's members mask */
442
443                         s->cgroup_members_mask_valid = false;
444
445                 /* And now make sure that this change also hits our
446                  * grandparents */
447                 unit_update_cgroup_members_masks(s);
448         }
449 }
450
451 static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) {
452         _cleanup_free_ char *path;
453         bool was_in_hash = false;
454         int r;
455
456         assert(u);
457
458         path = unit_default_cgroup_path(u);
459         if (!path)
460                 return log_oom();
461
462         r = hashmap_put(u->manager->cgroup_unit, path, u);
463         if (r == 0)
464                 was_in_hash = true;
465         else if (r < 0) {
466                 log_error(r == -EEXIST ?
467                           "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s",
468                           path, strerror(-r));
469                 return r;
470         }
471
472         /* First, create our own group */
473         r = cg_create_everywhere(u->manager->cgroup_supported, mask, path);
474         if (r < 0)
475                 log_error("Failed to create cgroup %s: %s", path, strerror(-r));
476
477         /* Then, possibly move things over */
478         if (u->cgroup_path) {
479                 r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, path);
480                 if (r < 0)
481                         log_error("Failed to migrate cgroup from %s to %s: %s",
482                                   u->cgroup_path, path, strerror(-r));
483         }
484
485         if (!was_in_hash) {
486                 /* Remember the new data */
487                 free(u->cgroup_path);
488                 u->cgroup_path = path;
489                 path = NULL;
490         }
491
492         u->cgroup_realized = true;
493         u->cgroup_realized_mask = mask;
494
495         return 0;
496 }
497
498 static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) {
499         assert(u);
500
501         return u->cgroup_realized && u->cgroup_realized_mask == mask;
502 }
503
504 /* Check if necessary controllers and attributes for a unit are in place.
505  *
506  * If so, do nothing.
507  * If not, create paths, move processes over, and set attributes.
508  *
509  * Returns 0 on success and < 0 on failure. */
510 static int unit_realize_cgroup_now(Unit *u) {
511         CGroupControllerMask mask;
512         int r;
513
514         assert(u);
515
516         if (u->in_cgroup_queue) {
517                 LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u);
518                 u->in_cgroup_queue = false;
519         }
520
521         mask = unit_get_target_mask(u);
522
523         if (unit_has_mask_realized(u, mask))
524                 return 0;
525
526         /* First, realize parents */
527         if (UNIT_ISSET(u->slice)) {
528                 r = unit_realize_cgroup_now(UNIT_DEREF(u->slice));
529                 if (r < 0)
530                         return r;
531         }
532
533         /* And then do the real work */
534         r = unit_create_cgroups(u, mask);
535         if (r < 0)
536                 return r;
537
538         /* Finally, apply the necessary attributes. */
539         cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path);
540
541         return 0;
542 }
543
544 static void unit_add_to_cgroup_queue(Unit *u) {
545
546         if (u->in_cgroup_queue)
547                 return;
548
549         LIST_PREPEND(cgroup_queue, u->manager->cgroup_queue, u);
550         u->in_cgroup_queue = true;
551 }
552
553 unsigned manager_dispatch_cgroup_queue(Manager *m) {
554         Unit *i;
555         unsigned n = 0;
556         int r;
557
558         while ((i = m->cgroup_queue)) {
559                 assert(i->in_cgroup_queue);
560
561                 r = unit_realize_cgroup_now(i);
562                 if (r < 0)
563                         log_warning("Failed to realize cgroups for queued unit %s: %s", i->id, strerror(-r));
564
565                 n++;
566         }
567
568         return n;
569 }
570
571 static void unit_queue_siblings(Unit *u) {
572         Unit *slice;
573
574         /* This adds the siblings of the specified unit and the
575          * siblings of all parent units to the cgroup queue. (But
576          * neither the specified unit itself nor the parents.) */
577
578         while ((slice = UNIT_DEREF(u->slice))) {
579                 Iterator i;
580                 Unit *m;
581
582                 SET_FOREACH(m, slice->dependencies[UNIT_BEFORE], i) {
583                         if (m == u)
584                                 continue;
585
586                         /* Skip units that have a dependency on the slice
587                          * but aren't actually in it. */
588                         if (UNIT_DEREF(m->slice) != slice)
589                                 continue;
590
591                         /* No point in doing cgroup application for units
592                          * without active processes. */
593                         if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(m)))
594                                 continue;
595
596                         /* If the unit doesn't need any new controllers
597                          * and has current ones realized, it doesn't need
598                          * any changes. */
599                         if (unit_has_mask_realized(m, unit_get_target_mask(m)))
600                                 continue;
601
602                         unit_add_to_cgroup_queue(m);
603                 }
604
605                 u = slice;
606         }
607 }
608
609 int unit_realize_cgroup(Unit *u) {
610         CGroupContext *c;
611
612         assert(u);
613
614         c = unit_get_cgroup_context(u);
615         if (!c)
616                 return 0;
617
618         /* So, here's the deal: when realizing the cgroups for this
619          * unit, we need to first create all parents, but there's more
620          * actually: for the weight-based controllers we also need to
621          * make sure that all our siblings (i.e. units that are in the
622          * same slice as we are) have cgroups, too. Otherwise things
623          * would become very uneven as each of their processes would
624          * get as much resources as all our group together. This call
625          * will synchronously create the parent cgroups, but will
626          * defer work on the siblings to the next event loop
627          * iteration. */
628
629         /* Add all sibling slices to the cgroup queue. */
630         unit_queue_siblings(u);
631
632         /* And realize this one now (and apply the values) */
633         return unit_realize_cgroup_now(u);
634 }
635
636 void unit_destroy_cgroup(Unit *u) {
637         int r;
638
639         assert(u);
640
641         if (!u->cgroup_path)
642                 return;
643
644         r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE));
645         if (r < 0)
646                 log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r));
647
648         hashmap_remove(u->manager->cgroup_unit, u->cgroup_path);
649
650         free(u->cgroup_path);
651         u->cgroup_path = NULL;
652         u->cgroup_realized = false;
653         u->cgroup_realized_mask = 0;
654
655 }
656
657 pid_t unit_search_main_pid(Unit *u) {
658         _cleanup_fclose_ FILE *f = NULL;
659         pid_t pid = 0, npid, mypid;
660
661         assert(u);
662
663         if (!u->cgroup_path)
664                 return 0;
665
666         if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0)
667                 return 0;
668
669         mypid = getpid();
670         while (cg_read_pid(f, &npid) > 0)  {
671                 pid_t ppid;
672
673                 if (npid == pid)
674                         continue;
675
676                 /* Ignore processes that aren't our kids */
677                 if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid)
678                         continue;
679
680                 if (pid != 0) {
681                         /* Dang, there's more than one daemonized PID
682                         in this group, so we don't know what process
683                         is the main process. */
684                         pid = 0;
685                         break;
686                 }
687
688                 pid = npid;
689         }
690
691         return pid;
692 }
693
694 int manager_setup_cgroup(Manager *m) {
695         _cleanup_free_ char *path = NULL;
696         char *e;
697         int r;
698
699         assert(m);
700
701         /* 0. Be nice to Ingo Molnar #628004 */
702         if (path_is_mount_point("/sys/fs/cgroup/systemd", false) <= 0) {
703                 log_warning("No control group support available, not creating root group.");
704                 return 0;
705         }
706
707         /* 1. Determine hierarchy */
708         free(m->cgroup_root);
709         m->cgroup_root = NULL;
710
711         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root);
712         if (r < 0) {
713                 log_error("Cannot determine cgroup we are running in: %s", strerror(-r));
714                 return r;
715         }
716
717         /* LEGACY: Already in /system.slice? If so, let's cut this
718          * off. This is to support live upgrades from older systemd
719          * versions where PID 1 was moved there. */
720         if (m->running_as == SYSTEMD_SYSTEM) {
721                 e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE);
722                 if (!e)
723                         e = endswith(m->cgroup_root, "/system");
724                 if (e)
725                         *e = 0;
726         }
727
728         /* And make sure to store away the root value without trailing
729          * slash, even for the root dir, so that we can easily prepend
730          * it everywhere. */
731         if (streq(m->cgroup_root, "/"))
732                 m->cgroup_root[0] = 0;
733
734         /* 2. Show data */
735         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path);
736         if (r < 0) {
737                 log_error("Cannot find cgroup mount point: %s", strerror(-r));
738                 return r;
739         }
740
741         log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path);
742
743         /* 3. Install agent */
744         if (m->running_as == SYSTEMD_SYSTEM) {
745                 r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH);
746                 if (r < 0)
747                         log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
748                 else if (r > 0)
749                         log_debug("Installed release agent.");
750                 else
751                         log_debug("Release agent already installed.");
752         }
753
754         /* 4. Make sure we are in the root cgroup */
755         r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0);
756         if (r < 0) {
757                 log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
758                 return r;
759         }
760
761         /* 5. And pin it, so that it cannot be unmounted */
762         if (m->pin_cgroupfs_fd >= 0)
763                 close_nointr_nofail(m->pin_cgroupfs_fd);
764
765         m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK);
766         if (r < 0) {
767                 log_error("Failed to open pin file: %m");
768                 return -errno;
769         }
770
771         /* 6. Figure out which controllers are supported */
772         m->cgroup_supported = cg_mask_supported();
773
774         /* 7.  Always enable hierarchial support if it exists... */
775         cg_set_attribute("memory", "/", "memory.use_hierarchy", "1");
776
777         return 0;
778 }
779
780 void manager_shutdown_cgroup(Manager *m, bool delete) {
781         assert(m);
782
783         /* We can't really delete the group, since we are in it. But
784          * let's trim it. */
785         if (delete && m->cgroup_root)
786                 cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false);
787
788         if (m->pin_cgroupfs_fd >= 0) {
789                 close_nointr_nofail(m->pin_cgroupfs_fd);
790                 m->pin_cgroupfs_fd = -1;
791         }
792
793         free(m->cgroup_root);
794         m->cgroup_root = NULL;
795 }
796
797 Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) {
798         char *p;
799         Unit *u;
800
801         assert(m);
802         assert(cgroup);
803
804         u = hashmap_get(m->cgroup_unit, cgroup);
805         if (u)
806                 return u;
807
808         p = strdupa(cgroup);
809         for (;;) {
810                 char *e;
811
812                 e = strrchr(p, '/');
813                 if (e == p || !e)
814                         return NULL;
815
816                 *e = 0;
817
818                 u = hashmap_get(m->cgroup_unit, p);
819                 if (u)
820                         return u;
821         }
822 }
823
824 Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) {
825         _cleanup_free_ char *cgroup = NULL;
826         int r;
827
828         assert(m);
829
830         if (pid <= 1)
831                 return NULL;
832
833         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup);
834         if (r < 0)
835                 return NULL;
836
837         return manager_get_unit_by_cgroup(m, cgroup);
838 }
839
840 int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
841         Unit *u;
842         int r;
843
844         assert(m);
845         assert(cgroup);
846
847         u = manager_get_unit_by_cgroup(m, cgroup);
848         if (u) {
849                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
850                 if (r > 0) {
851                         if (UNIT_VTABLE(u)->notify_cgroup_empty)
852                                 UNIT_VTABLE(u)->notify_cgroup_empty(u);
853
854                         unit_add_to_gc_queue(u);
855                 }
856         }
857
858         return 0;
859 }
860
861 static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
862         [CGROUP_AUTO] = "auto",
863         [CGROUP_CLOSED] = "closed",
864         [CGROUP_STRICT] = "strict",
865 };
866
867 DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);