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