]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/cgroup-util.c
final v236 update (#7649)
[thirdparty/systemd.git] / src / basic / cgroup-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
8c6db833
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
8c6db833
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
8c6db833 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
84ac7bea 21#include <dirent.h>
8c6db833 22#include <errno.h>
84ac7bea 23#include <ftw.h>
11c3a366 24#include <limits.h>
8c6db833 25#include <signal.h>
11c3a366 26#include <stddef.h>
35bbbf85 27#include <stdio_ext.h>
8c6db833 28#include <stdlib.h>
84ac7bea 29#include <string.h>
672c48cc 30#include <sys/stat.h>
11c3a366 31#include <sys/statfs.h>
672c48cc 32#include <sys/types.h>
4b58153d 33#include <sys/xattr.h>
84ac7bea 34#include <unistd.h>
8c6db833 35
b5efdb8a 36#include "alloc-util.h"
3ffd4af2 37#include "cgroup-util.h"
93cc7779 38#include "def.h"
a0956174 39#include "dirent-util.h"
84ac7bea 40#include "extract-word.h"
3ffd4af2 41#include "fd-util.h"
84ac7bea 42#include "fileio.h"
f97b34a6 43#include "format-util.h"
f4f15635 44#include "fs-util.h"
93cc7779 45#include "log.h"
84ac7bea
LP
46#include "login-util.h"
47#include "macro.h"
93cc7779 48#include "missing.h"
84ac7bea 49#include "mkdir.h"
6bedfcbb 50#include "parse-util.h"
9eb977db 51#include "path-util.h"
872a590e 52#include "proc-cmdline.h"
84ac7bea
LP
53#include "process-util.h"
54#include "set.h"
9444b1f2 55#include "special.h"
872a590e 56#include "stat-util.h"
d054f0a4 57#include "stdio-util.h"
8b43440b 58#include "string-table.h"
07630cea 59#include "string-util.h"
aae7e17f 60#include "strv.h"
84ac7bea 61#include "unit-name.h"
b1d4f8e1 62#include "user-util.h"
8c6db833 63
c6c18be3 64int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
7027ff61 65 _cleanup_free_ char *fs = NULL;
c6c18be3 66 FILE *f;
7027ff61 67 int r;
c6c18be3 68
c6c18be3
LP
69 assert(_f);
70
c3175a7f
LP
71 r = cg_get_path(controller, path, "cgroup.procs", &fs);
72 if (r < 0)
c6c18be3
LP
73 return r;
74
75 f = fopen(fs, "re");
c6c18be3
LP
76 if (!f)
77 return -errno;
78
79 *_f = f;
80 return 0;
81}
82
c6c18be3
LP
83int cg_read_pid(FILE *f, pid_t *_pid) {
84 unsigned long ul;
85
86 /* Note that the cgroup.procs might contain duplicates! See
87 * cgroups.txt for details. */
88
7027ff61
LP
89 assert(f);
90 assert(_pid);
91
c6c18be3
LP
92 errno = 0;
93 if (fscanf(f, "%lu", &ul) != 1) {
94
95 if (feof(f))
96 return 0;
97
f5e5c28f 98 return errno > 0 ? -errno : -EIO;
c6c18be3
LP
99 }
100
101 if (ul <= 0)
102 return -EIO;
103
104 *_pid = (pid_t) ul;
105 return 1;
106}
107
8b238b13
LP
108int cg_read_event(
109 const char *controller,
110 const char *path,
111 const char *event,
112 char **val) {
113
ab2c3861
TH
114 _cleanup_free_ char *events = NULL, *content = NULL;
115 char *p, *line;
116 int r;
117
118 r = cg_get_path(controller, path, "cgroup.events", &events);
119 if (r < 0)
120 return r;
121
122 r = read_full_file(events, &content, NULL);
123 if (r < 0)
124 return r;
125
126 p = content;
127 while ((line = strsep(&p, "\n"))) {
128 char *key;
129
130 key = strsep(&line, " ");
131 if (!key || !line)
132 return -EINVAL;
133
134 if (strcmp(key, event))
135 continue;
136
137 *val = strdup(line);
138 return 0;
139 }
140
141 return -ENOENT;
142}
143
3228995c
CB
144bool cg_ns_supported(void) {
145 static thread_local int enabled = -1;
146
147 if (enabled >= 0)
148 return enabled;
149
150 if (access("/proc/self/ns/cgroup", F_OK) == 0)
151 enabled = 1;
152 else
153 enabled = 0;
154
155 return enabled;
156}
157
35d2e7ec 158int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
7027ff61 159 _cleanup_free_ char *fs = NULL;
35d2e7ec
LP
160 int r;
161 DIR *d;
162
35d2e7ec
LP
163 assert(_d);
164
165 /* This is not recursive! */
166
c3175a7f
LP
167 r = cg_get_path(controller, path, NULL, &fs);
168 if (r < 0)
35d2e7ec
LP
169 return r;
170
171 d = opendir(fs);
35d2e7ec
LP
172 if (!d)
173 return -errno;
174
175 *_d = d;
176 return 0;
177}
178
179int cg_read_subgroup(DIR *d, char **fn) {
180 struct dirent *de;
181
182 assert(d);
7027ff61 183 assert(fn);
35d2e7ec 184
f01327ad 185 FOREACH_DIRENT_ALL(de, d, return -errno) {
35d2e7ec
LP
186 char *b;
187
188 if (de->d_type != DT_DIR)
189 continue;
190
49bfc877 191 if (dot_or_dot_dot(de->d_name))
35d2e7ec
LP
192 continue;
193
7027ff61
LP
194 b = strdup(de->d_name);
195 if (!b)
35d2e7ec
LP
196 return -ENOMEM;
197
198 *fn = b;
199 return 1;
200 }
201
35d2e7ec
LP
202 return 0;
203}
204
4ad49000 205int cg_rmdir(const char *controller, const char *path) {
7027ff61 206 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
207 int r;
208
ad293f5a
LP
209 r = cg_get_path(controller, path, NULL, &p);
210 if (r < 0)
35d2e7ec
LP
211 return r;
212
213 r = rmdir(p);
7027ff61
LP
214 if (r < 0 && errno != ENOENT)
215 return -errno;
35d2e7ec 216
b4cccbc1
LP
217 r = cg_hybrid_unified();
218 if (r < 0)
219 return r;
220 if (r == 0)
221 return 0;
222
223 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
224 r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
225 if (r < 0)
226 log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
227 }
228
7027ff61 229 return 0;
35d2e7ec
LP
230}
231
1d98fef1
LP
232int cg_kill(
233 const char *controller,
234 const char *path,
235 int sig,
236 CGroupFlags flags,
237 Set *s,
238 cg_kill_log_func_t log_kill,
239 void *userdata) {
240
7027ff61 241 _cleanup_set_free_ Set *allocated_set = NULL;
35d2e7ec 242 bool done = false;
8c6db833 243 int r, ret = 0;
35d2e7ec 244 pid_t my_pid;
8c6db833 245
8c6db833
LP
246 assert(sig >= 0);
247
0d5b4810
LP
248 /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
249 * SIGCONT on SIGKILL. */
250 if (IN_SET(sig, SIGCONT, SIGKILL))
251 flags &= ~CGROUP_SIGCONT;
252
8c6db833
LP
253 /* This goes through the tasks list and kills them all. This
254 * is repeated until no further processes are added to the
255 * tasks list, to properly handle forking processes */
256
7027ff61 257 if (!s) {
d5099efc 258 s = allocated_set = set_new(NULL);
7027ff61 259 if (!s)
ca949c9d 260 return -ENOMEM;
7027ff61 261 }
8c6db833 262
df0ff127 263 my_pid = getpid_cached();
8c6db833
LP
264
265 do {
7027ff61 266 _cleanup_fclose_ FILE *f = NULL;
0b172489 267 pid_t pid = 0;
8c6db833
LP
268 done = true;
269
7027ff61
LP
270 r = cg_enumerate_processes(controller, path, &f);
271 if (r < 0) {
4c633005 272 if (ret >= 0 && r != -ENOENT)
7027ff61 273 return r;
35d2e7ec 274
7027ff61 275 return ret;
35d2e7ec 276 }
c6c18be3
LP
277
278 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 279
1d98fef1 280 if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
c6c18be3 281 continue;
8c6db833 282
fea72cc0 283 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
c6c18be3 284 continue;
8c6db833 285
1d98fef1
LP
286 if (log_kill)
287 log_kill(pid, sig, userdata);
288
8c6db833
LP
289 /* If we haven't killed this process yet, kill
290 * it */
4c633005
LP
291 if (kill(pid, sig) < 0) {
292 if (ret >= 0 && errno != ESRCH)
8c6db833 293 ret = -errno;
6e8314c4 294 } else {
1d98fef1 295 if (flags & CGROUP_SIGCONT)
e155a0aa 296 (void) kill(pid, SIGCONT);
430c18ed 297
6e8314c4
LP
298 if (ret == 0)
299 ret = 1;
430c18ed 300 }
8c6db833 301
8c6db833
LP
302 done = false;
303
fea72cc0 304 r = set_put(s, PID_TO_PTR(pid));
7027ff61 305 if (r < 0) {
35d2e7ec 306 if (ret >= 0)
7027ff61 307 return r;
35d2e7ec 308
7027ff61 309 return ret;
35d2e7ec
LP
310 }
311 }
312
313 if (r < 0) {
314 if (ret >= 0)
7027ff61 315 return r;
35d2e7ec 316
7027ff61 317 return ret;
8c6db833
LP
318 }
319
8c6db833
LP
320 /* To avoid racing against processes which fork
321 * quicker than we can kill them we repeat this until
322 * no new pids need to be killed. */
323
35d2e7ec 324 } while (!done);
8c6db833 325
35d2e7ec 326 return ret;
8c6db833
LP
327}
328
1d98fef1
LP
329int cg_kill_recursive(
330 const char *controller,
331 const char *path,
332 int sig,
333 CGroupFlags flags,
334 Set *s,
335 cg_kill_log_func_t log_kill,
336 void *userdata) {
337
7027ff61
LP
338 _cleanup_set_free_ Set *allocated_set = NULL;
339 _cleanup_closedir_ DIR *d = NULL;
e155a0aa 340 int r, ret;
35d2e7ec 341 char *fn;
8c6db833
LP
342
343 assert(path);
8c6db833
LP
344 assert(sig >= 0);
345
7027ff61 346 if (!s) {
d5099efc 347 s = allocated_set = set_new(NULL);
7027ff61 348 if (!s)
ca949c9d 349 return -ENOMEM;
7027ff61 350 }
ca949c9d 351
1d98fef1 352 ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
8c6db833 353
7027ff61
LP
354 r = cg_enumerate_subgroups(controller, path, &d);
355 if (r < 0) {
4c633005 356 if (ret >= 0 && r != -ENOENT)
7027ff61 357 return r;
8c6db833 358
7027ff61 359 return ret;
35d2e7ec 360 }
8c6db833 361
35d2e7ec 362 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 363 _cleanup_free_ char *p = NULL;
8c6db833 364
605405c6 365 p = strjoin(path, "/", fn);
35d2e7ec 366 free(fn);
7027ff61
LP
367 if (!p)
368 return -ENOMEM;
8c6db833 369
1d98fef1 370 r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
e155a0aa 371 if (r != 0 && ret >= 0)
35d2e7ec 372 ret = r;
8c6db833 373 }
7027ff61 374 if (ret >= 0 && r < 0)
35d2e7ec
LP
375 ret = r;
376
1d98fef1 377 if (flags & CGROUP_REMOVE) {
4ad49000 378 r = cg_rmdir(controller, path);
4c701096 379 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
7027ff61
LP
380 return r;
381 }
ca949c9d 382
8c6db833
LP
383 return ret;
384}
385
1d98fef1
LP
386int cg_migrate(
387 const char *cfrom,
388 const char *pfrom,
389 const char *cto,
390 const char *pto,
391 CGroupFlags flags) {
392
35d2e7ec 393 bool done = false;
246aa6dd 394 _cleanup_set_free_ Set *s = NULL;
8c6db833
LP
395 int r, ret = 0;
396 pid_t my_pid;
397
246aa6dd
LP
398 assert(cfrom);
399 assert(pfrom);
400 assert(cto);
401 assert(pto);
8c6db833 402
d5099efc 403 s = set_new(NULL);
246aa6dd 404 if (!s)
35d2e7ec
LP
405 return -ENOMEM;
406
df0ff127 407 my_pid = getpid_cached();
8c6db833
LP
408
409 do {
7027ff61 410 _cleanup_fclose_ FILE *f = NULL;
0b172489 411 pid_t pid = 0;
8c6db833
LP
412 done = true;
413
b043cd0b 414 r = cg_enumerate_processes(cfrom, pfrom, &f);
246aa6dd 415 if (r < 0) {
4c633005 416 if (ret >= 0 && r != -ENOENT)
7027ff61 417 return r;
35d2e7ec 418
246aa6dd 419 return ret;
35d2e7ec 420 }
c6c18be3
LP
421
422 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 423
35d2e7ec
LP
424 /* This might do weird stuff if we aren't a
425 * single-threaded program. However, we
426 * luckily know we are not */
1d98fef1 427 if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
c6c18be3 428 continue;
8c6db833 429
fea72cc0 430 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
35d2e7ec
LP
431 continue;
432
9b84c7f9
LP
433 /* Ignore kernel threads. Since they can only
434 * exist in the root cgroup, we only check for
435 * them there. */
436 if (cfrom &&
437 (isempty(pfrom) || path_equal(pfrom, "/")) &&
438 is_kernel_thread(pid) > 0)
439 continue;
440
246aa6dd
LP
441 r = cg_attach(cto, pto, pid);
442 if (r < 0) {
4c633005 443 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
444 ret = r;
445 } else if (ret == 0)
446 ret = 1;
8c6db833 447
8c6db833 448 done = false;
35d2e7ec 449
fea72cc0 450 r = set_put(s, PID_TO_PTR(pid));
246aa6dd 451 if (r < 0) {
35d2e7ec 452 if (ret >= 0)
7027ff61 453 return r;
35d2e7ec 454
246aa6dd 455 return ret;
35d2e7ec
LP
456 }
457 }
458
459 if (r < 0) {
460 if (ret >= 0)
7027ff61 461 return r;
35d2e7ec 462
246aa6dd 463 return ret;
8c6db833 464 }
35d2e7ec 465 } while (!done);
8c6db833 466
35d2e7ec 467 return ret;
8c6db833
LP
468}
469
4ad49000
LP
470int cg_migrate_recursive(
471 const char *cfrom,
472 const char *pfrom,
473 const char *cto,
474 const char *pto,
1d98fef1 475 CGroupFlags flags) {
4ad49000 476
246aa6dd 477 _cleanup_closedir_ DIR *d = NULL;
7027ff61 478 int r, ret = 0;
35d2e7ec 479 char *fn;
8c6db833 480
246aa6dd
LP
481 assert(cfrom);
482 assert(pfrom);
483 assert(cto);
484 assert(pto);
8c6db833 485
1d98fef1 486 ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
8c6db833 487
246aa6dd
LP
488 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
489 if (r < 0) {
4c633005 490 if (ret >= 0 && r != -ENOENT)
7027ff61
LP
491 return r;
492
246aa6dd 493 return ret;
35d2e7ec
LP
494 }
495
496 while ((r = cg_read_subgroup(d, &fn)) > 0) {
246aa6dd 497 _cleanup_free_ char *p = NULL;
8c6db833 498
605405c6 499 p = strjoin(pfrom, "/", fn);
35d2e7ec 500 free(fn);
e155a0aa
LP
501 if (!p)
502 return -ENOMEM;
8c6db833 503
1d98fef1 504 r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
35d2e7ec
LP
505 if (r != 0 && ret >= 0)
506 ret = r;
8c6db833
LP
507 }
508
35d2e7ec
LP
509 if (r < 0 && ret >= 0)
510 ret = r;
511
1d98fef1 512 if (flags & CGROUP_REMOVE) {
4ad49000 513 r = cg_rmdir(cfrom, pfrom);
4c701096 514 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
246aa6dd
LP
515 return r;
516 }
8c6db833
LP
517
518 return ret;
519}
520
13b84ec7
LP
521int cg_migrate_recursive_fallback(
522 const char *cfrom,
523 const char *pfrom,
524 const char *cto,
525 const char *pto,
1d98fef1 526 CGroupFlags flags) {
13b84ec7
LP
527
528 int r;
529
530 assert(cfrom);
531 assert(pfrom);
532 assert(cto);
533 assert(pto);
534
1d98fef1 535 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
13b84ec7
LP
536 if (r < 0) {
537 char prefix[strlen(pto) + 1];
538
539 /* This didn't work? Then let's try all prefixes of the destination */
540
fecffe5d 541 PATH_FOREACH_PREFIX(prefix, pto) {
e155a0aa
LP
542 int q;
543
1d98fef1 544 q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
e155a0aa
LP
545 if (q >= 0)
546 return q;
13b84ec7
LP
547 }
548 }
549
e155a0aa 550 return r;
13b84ec7
LP
551}
552
efdb0237
LP
553static const char *controller_to_dirname(const char *controller) {
554 const char *e;
3474ae3c 555
7027ff61
LP
556 assert(controller);
557
efdb0237
LP
558 /* Converts a controller name to the directory name below
559 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
560 * just cuts off the name= prefixed used for named
561 * hierarchies, if it is specified. */
562
2977724b 563 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
b4cccbc1 564 if (cg_hybrid_unified() > 0)
2977724b
TH
565 controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
566 else
567 controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
568 }
b6629c4b 569
efdb0237
LP
570 e = startswith(controller, "name=");
571 if (e)
572 return e;
573
574 return controller;
3474ae3c
LP
575}
576
569b19d8
LP
577static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
578 const char *dn;
018ef268 579 char *t = NULL;
3474ae3c 580
efdb0237 581 assert(fs);
569b19d8
LP
582 assert(controller);
583
584 dn = controller_to_dirname(controller);
efdb0237
LP
585
586 if (isempty(path) && isempty(suffix))
569b19d8 587 t = strappend("/sys/fs/cgroup/", dn);
efdb0237 588 else if (isempty(path))
605405c6 589 t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
efdb0237 590 else if (isempty(suffix))
605405c6 591 t = strjoin("/sys/fs/cgroup/", dn, "/", path);
efdb0237 592 else
605405c6 593 t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
efdb0237
LP
594 if (!t)
595 return -ENOMEM;
3474ae3c 596
efdb0237
LP
597 *fs = t;
598 return 0;
599}
600
601static int join_path_unified(const char *path, const char *suffix, char **fs) {
602 char *t;
603
604 assert(fs);
605
606 if (isempty(path) && isempty(suffix))
607 t = strdup("/sys/fs/cgroup");
608 else if (isempty(path))
609 t = strappend("/sys/fs/cgroup/", suffix);
610 else if (isempty(suffix))
611 t = strappend("/sys/fs/cgroup/", path);
612 else
605405c6 613 t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
3474ae3c
LP
614 if (!t)
615 return -ENOMEM;
616
efdb0237 617 *fs = t;
3474ae3c
LP
618 return 0;
619}
620
8c6db833 621int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
415fc41c 622 int r;
8c6db833 623
dbd821ac
LP
624 assert(fs);
625
efdb0237
LP
626 if (!controller) {
627 char *t;
628
569b19d8
LP
629 /* If no controller is specified, we return the path
630 * *below* the controllers, without any prefix. */
efdb0237
LP
631
632 if (!path && !suffix)
633 return -EINVAL;
634
989189ea 635 if (!suffix)
efdb0237 636 t = strdup(path);
989189ea 637 else if (!path)
efdb0237
LP
638 t = strdup(suffix);
639 else
605405c6 640 t = strjoin(path, "/", suffix);
efdb0237
LP
641 if (!t)
642 return -ENOMEM;
643
644 *fs = path_kill_slashes(t);
645 return 0;
646 }
647
648 if (!cg_controller_is_valid(controller))
78edb35a
LP
649 return -EINVAL;
650
b4cccbc1
LP
651 r = cg_all_unified();
652 if (r < 0)
653 return r;
654 if (r > 0)
efdb0237 655 r = join_path_unified(path, suffix, fs);
569b19d8
LP
656 else
657 r = join_path_legacy(controller, path, suffix, fs);
efdb0237
LP
658 if (r < 0)
659 return r;
7027ff61 660
efdb0237
LP
661 path_kill_slashes(*fs);
662 return 0;
3474ae3c 663}
dbd821ac 664
efdb0237 665static int controller_is_accessible(const char *controller) {
b4cccbc1 666 int r;
37099707 667
efdb0237 668 assert(controller);
37099707 669
efdb0237
LP
670 /* Checks whether a specific controller is accessible,
671 * i.e. its hierarchy mounted. In the unified hierarchy all
672 * controllers are considered accessible, except for the named
673 * hierarchies */
b12afc8c 674
efdb0237
LP
675 if (!cg_controller_is_valid(controller))
676 return -EINVAL;
677
b4cccbc1
LP
678 r = cg_all_unified();
679 if (r < 0)
680 return r;
681 if (r > 0) {
efdb0237
LP
682 /* We don't support named hierarchies if we are using
683 * the unified hierarchy. */
684
685 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
686 return 0;
687
688 if (startswith(controller, "name="))
689 return -EOPNOTSUPP;
690
691 } else {
692 const char *cc, *dn;
693
694 dn = controller_to_dirname(controller);
695 cc = strjoina("/sys/fs/cgroup/", dn);
696
697 if (laccess(cc, F_OK) < 0)
698 return -errno;
699 }
37099707
LP
700
701 return 0;
702}
703
3474ae3c 704int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
37099707 705 int r;
dbd821ac 706
efdb0237 707 assert(controller);
3474ae3c 708 assert(fs);
70132bd0 709
efdb0237
LP
710 /* Check if the specified controller is actually accessible */
711 r = controller_is_accessible(controller);
37099707
LP
712 if (r < 0)
713 return r;
3474ae3c 714
efdb0237 715 return cg_get_path(controller, path, suffix, fs);
8c6db833
LP
716}
717
e27796a0 718static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
719 assert(path);
720 assert(sb);
721 assert(ftwbuf);
e27796a0
LP
722
723 if (typeflag != FTW_DP)
724 return 0;
725
726 if (ftwbuf->level < 1)
727 return 0;
728
e155a0aa 729 (void) rmdir(path);
e27796a0
LP
730 return 0;
731}
732
8c6db833 733int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 734 _cleanup_free_ char *fs = NULL;
2977724b 735 int r = 0, q;
8c6db833 736
8c6db833
LP
737 assert(path);
738
e27796a0
LP
739 r = cg_get_path(controller, path, NULL, &fs);
740 if (r < 0)
8c6db833
LP
741 return r;
742
e27796a0 743 errno = 0;
e155a0aa
LP
744 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
745 if (errno == ENOENT)
746 r = 0;
b3267152 747 else if (errno > 0)
e155a0aa
LP
748 r = -errno;
749 else
750 r = -EIO;
751 }
e27796a0
LP
752
753 if (delete_root) {
4ad49000
LP
754 if (rmdir(fs) < 0 && errno != ENOENT)
755 return -errno;
e27796a0
LP
756 }
757
b4cccbc1
LP
758 q = cg_hybrid_unified();
759 if (q < 0)
760 return q;
761 if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
762 q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
763 if (q < 0)
764 log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
765 }
766
e27796a0 767 return r;
8c6db833
LP
768}
769
1434ae6f
LP
770int cg_create(const char *controller, const char *path) {
771 _cleanup_free_ char *fs = NULL;
772 int r;
773
774 r = cg_get_path_and_check(controller, path, NULL, &fs);
775 if (r < 0)
776 return r;
777
778 r = mkdir_parents(fs, 0755);
779 if (r < 0)
780 return r;
781
782 if (mkdir(fs, 0755) < 0) {
783
784 if (errno == EEXIST)
785 return 0;
786
787 return -errno;
788 }
789
b4cccbc1
LP
790 r = cg_hybrid_unified();
791 if (r < 0)
792 return r;
793
794 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
795 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
796 if (r < 0)
797 log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
798 }
799
1434ae6f
LP
800 return 1;
801}
802
803int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
804 int r, q;
805
806 assert(pid >= 0);
807
808 r = cg_create(controller, path);
809 if (r < 0)
810 return r;
811
812 q = cg_attach(controller, path, pid);
813 if (q < 0)
814 return q;
815
816 /* This does not remove the cgroup on failure */
817 return r;
818}
819
8c6db833 820int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
821 _cleanup_free_ char *fs = NULL;
822 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
823 int r;
824
8c6db833
LP
825 assert(path);
826 assert(pid >= 0);
827
b043cd0b 828 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 829 if (r < 0)
c6c18be3 830 return r;
8c6db833
LP
831
832 if (pid == 0)
df0ff127 833 pid = getpid_cached();
8c6db833 834
d054f0a4 835 xsprintf(c, PID_FMT "\n", pid);
8c6db833 836
2977724b
TH
837 r = write_string_file(fs, c, 0);
838 if (r < 0)
839 return r;
840
b4cccbc1
LP
841 r = cg_hybrid_unified();
842 if (r < 0)
843 return r;
844
845 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
846 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
847 if (r < 0)
bd68e99b 848 log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
2977724b
TH
849 }
850
851 return 0;
8c6db833
LP
852}
853
13b84ec7
LP
854int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
855 int r;
856
857 assert(controller);
858 assert(path);
859 assert(pid >= 0);
860
861 r = cg_attach(controller, path, pid);
862 if (r < 0) {
863 char prefix[strlen(path) + 1];
864
865 /* This didn't work? Then let's try all prefixes of
866 * the destination */
867
fecffe5d 868 PATH_FOREACH_PREFIX(prefix, path) {
e155a0aa
LP
869 int q;
870
871 q = cg_attach(controller, prefix, pid);
872 if (q >= 0)
873 return q;
13b84ec7
LP
874 }
875 }
876
e155a0aa 877 return r;
13b84ec7
LP
878}
879
62b9bb26 880int cg_set_access(
2d76d14e
LP
881 const char *controller,
882 const char *path,
2d76d14e
LP
883 uid_t uid,
884 gid_t gid) {
885
62b9bb26
LP
886 struct Attribute {
887 const char *name;
888 bool fatal;
889 };
890
891 /* cgroupsv1, aka legacy/non-unified */
892 static const struct Attribute legacy_attributes[] = {
893 { "cgroup.procs", true },
894 { "tasks", false },
895 { "cgroup.clone_children", false },
896 {},
897 };
898
899 /* cgroupsv2, aka unified */
900 static const struct Attribute unified_attributes[] = {
901 { "cgroup.procs", true },
902 { "cgroup.subtree_control", true },
903 { "cgroup.threads", false },
904 {},
905 };
906
907 static const struct Attribute* const attributes[] = {
908 [false] = legacy_attributes,
909 [true] = unified_attributes,
910 };
974efc46 911
40853aa5 912 _cleanup_free_ char *fs = NULL;
62b9bb26
LP
913 const struct Attribute *i;
914 int r, unified;
8c6db833 915
8c6db833
LP
916 assert(path);
917
62b9bb26 918 if (uid == UID_INVALID && gid == GID_INVALID)
8d53b453
LP
919 return 0;
920
62b9bb26
LP
921 unified = cg_unified_controller(controller);
922 if (unified < 0)
923 return unified;
8c6db833 924
62b9bb26
LP
925 /* Configure access to the cgroup itself */
926 r = cg_get_path(controller, path, NULL, &fs);
974efc46
LP
927 if (r < 0)
928 return r;
8c6db833 929
62b9bb26 930 r = chmod_and_chown(fs, 0755, uid, gid);
b4cccbc1
LP
931 if (r < 0)
932 return r;
40853aa5 933
62b9bb26
LP
934 /* Configure access to the cgroup's attributes */
935 for (i = attributes[unified]; i->name; i++) {
40853aa5 936 fs = mfree(fs);
40853aa5 937
62b9bb26 938 r = cg_get_path(controller, path, i->name, &fs);
40853aa5
LP
939 if (r < 0)
940 return r;
efdb0237 941
62b9bb26
LP
942 r = chmod_and_chown(fs, 0644, uid, gid);
943 if (r < 0) {
944 if (i->fatal)
945 return r;
5beac75e 946
62b9bb26
LP
947 log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
948 }
949 }
950
951 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
952 r = cg_hybrid_unified();
2977724b 953 if (r < 0)
62b9bb26
LP
954 return r;
955 if (r > 0) {
956 /* Always propagate access mode from unified to legacy controller */
957 r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
958 if (r < 0)
959 log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
960 }
2977724b 961 }
974efc46 962
efdb0237 963 return 0;
8c6db833
LP
964}
965
4b58153d
LP
966int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
967 _cleanup_free_ char *fs = NULL;
968 int r;
969
970 assert(path);
971 assert(name);
972 assert(value || size <= 0);
973
974 r = cg_get_path(controller, path, NULL, &fs);
975 if (r < 0)
976 return r;
977
978 if (setxattr(fs, name, value, size, flags) < 0)
979 return -errno;
980
981 return 0;
982}
983
984int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
985 _cleanup_free_ char *fs = NULL;
986 ssize_t n;
987 int r;
988
989 assert(path);
990 assert(name);
991
992 r = cg_get_path(controller, path, NULL, &fs);
993 if (r < 0)
994 return r;
995
996 n = getxattr(fs, name, value, size);
997 if (n < 0)
998 return -errno;
999
1000 return (int) n;
1001}
1002
7027ff61 1003int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
1004 _cleanup_fclose_ FILE *f = NULL;
1005 char line[LINE_MAX];
b6629c4b 1006 const char *fs, *controller_str;
efdb0237 1007 size_t cs = 0;
b4cccbc1 1008 int unified;
8c6db833 1009
8c6db833 1010 assert(path);
c6c18be3 1011 assert(pid >= 0);
8c6db833 1012
5da38d07
TH
1013 if (controller) {
1014 if (!cg_controller_is_valid(controller))
1015 return -EINVAL;
1016 } else
1017 controller = SYSTEMD_CGROUP_CONTROLLER;
1018
c22800e4 1019 unified = cg_unified_controller(controller);
b4cccbc1
LP
1020 if (unified < 0)
1021 return unified;
1022 if (unified == 0) {
b6629c4b
TH
1023 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1024 controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
1025 else
1026 controller_str = controller;
1027
1028 cs = strlen(controller_str);
1029 }
7027ff61 1030
b68fa010 1031 fs = procfs_file_alloca(pid, "cgroup");
c6c18be3 1032 f = fopen(fs, "re");
4c633005
LP
1033 if (!f)
1034 return errno == ENOENT ? -ESRCH : -errno;
1035
35bbbf85
LP
1036 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
1037
7027ff61 1038 FOREACH_LINE(line, f, return -errno) {
efdb0237 1039 char *e, *p;
c6c18be3
LP
1040
1041 truncate_nl(line);
1042
efdb0237
LP
1043 if (unified) {
1044 e = startswith(line, "0:");
1045 if (!e)
1046 continue;
c6c18be3 1047
efdb0237
LP
1048 e = strchr(e, ':');
1049 if (!e)
1050 continue;
1051 } else {
1052 char *l;
1053 size_t k;
1054 const char *word, *state;
1055 bool found = false;
1056
1057 l = strchr(line, ':');
1058 if (!l)
1059 continue;
8af8afd6 1060
efdb0237
LP
1061 l++;
1062 e = strchr(l, ':');
1063 if (!e)
1064 continue;
8af8afd6 1065
efdb0237
LP
1066 *e = 0;
1067 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
b6629c4b 1068 if (k == cs && memcmp(word, controller_str, cs) == 0) {
efdb0237
LP
1069 found = true;
1070 break;
1071 }
8af8afd6
LP
1072 }
1073
efdb0237
LP
1074 if (!found)
1075 continue;
8af8afd6
LP
1076 }
1077
8af8afd6 1078 p = strdup(e + 1);
7027ff61
LP
1079 if (!p)
1080 return -ENOMEM;
c6c18be3 1081
5e20b0a4
LP
1082 /* Truncate suffix indicating the process is a zombie */
1083 e = endswith(p, " (deleted)");
1084 if (e)
1085 *e = 0;
1086
c6c18be3 1087 *path = p;
7027ff61 1088 return 0;
c6c18be3
LP
1089 }
1090
1c80e425 1091 return -ENODATA;
8c6db833
LP
1092}
1093
1094int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61 1095 _cleanup_free_ char *fs = NULL, *contents = NULL;
efdb0237 1096 const char *sc;
415fc41c 1097 int r;
8c6db833 1098
8c6db833
LP
1099 assert(agent);
1100
c22800e4 1101 r = cg_unified_controller(controller);
b4cccbc1
LP
1102 if (r < 0)
1103 return r;
1104 if (r > 0) /* doesn't apply to unified hierarchy */
efdb0237
LP
1105 return -EOPNOTSUPP;
1106
7027ff61
LP
1107 r = cg_get_path(controller, NULL, "release_agent", &fs);
1108 if (r < 0)
c6c18be3 1109 return r;
8c6db833 1110
7027ff61
LP
1111 r = read_one_line_file(fs, &contents);
1112 if (r < 0)
1113 return r;
8c6db833
LP
1114
1115 sc = strstrip(contents);
e155a0aa 1116 if (isempty(sc)) {
4c1fc3e4 1117 r = write_string_file(fs, agent, 0);
574d5f2d 1118 if (r < 0)
7027ff61 1119 return r;
b8725df8 1120 } else if (!path_equal(sc, agent))
7027ff61 1121 return -EEXIST;
8c6db833 1122
0da16248 1123 fs = mfree(fs);
7027ff61
LP
1124 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1125 if (r < 0)
1126 return r;
8c6db833 1127
0da16248 1128 contents = mfree(contents);
7027ff61
LP
1129 r = read_one_line_file(fs, &contents);
1130 if (r < 0)
1131 return r;
8c6db833
LP
1132
1133 sc = strstrip(contents);
8c6db833 1134 if (streq(sc, "0")) {
4c1fc3e4 1135 r = write_string_file(fs, "1", 0);
7027ff61
LP
1136 if (r < 0)
1137 return r;
c6c18be3 1138
7027ff61
LP
1139 return 1;
1140 }
8c6db833 1141
7027ff61
LP
1142 if (!streq(sc, "1"))
1143 return -EIO;
8c6db833 1144
7027ff61 1145 return 0;
8c6db833
LP
1146}
1147
ad929bcc
KS
1148int cg_uninstall_release_agent(const char *controller) {
1149 _cleanup_free_ char *fs = NULL;
415fc41c 1150 int r;
efdb0237 1151
c22800e4 1152 r = cg_unified_controller(controller);
b4cccbc1
LP
1153 if (r < 0)
1154 return r;
1155 if (r > 0) /* Doesn't apply to unified hierarchy */
efdb0237 1156 return -EOPNOTSUPP;
ad929bcc 1157
ac9ef333
LP
1158 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1159 if (r < 0)
1160 return r;
1161
4c1fc3e4 1162 r = write_string_file(fs, "0", 0);
ac9ef333
LP
1163 if (r < 0)
1164 return r;
1165
0da16248 1166 fs = mfree(fs);
ac9ef333 1167
ad929bcc
KS
1168 r = cg_get_path(controller, NULL, "release_agent", &fs);
1169 if (r < 0)
1170 return r;
1171
4c1fc3e4 1172 r = write_string_file(fs, "", 0);
ad929bcc
KS
1173 if (r < 0)
1174 return r;
1175
ac9ef333 1176 return 0;
ad929bcc
KS
1177}
1178
6f883237 1179int cg_is_empty(const char *controller, const char *path) {
7027ff61 1180 _cleanup_fclose_ FILE *f = NULL;
efdb0237 1181 pid_t pid;
7027ff61 1182 int r;
8c6db833 1183
8c6db833
LP
1184 assert(path);
1185
b043cd0b 1186 r = cg_enumerate_processes(controller, path, &f);
6f883237
LP
1187 if (r == -ENOENT)
1188 return 1;
c3175a7f 1189 if (r < 0)
6f883237 1190 return r;
8c6db833 1191
6f883237 1192 r = cg_read_pid(f, &pid);
c6c18be3
LP
1193 if (r < 0)
1194 return r;
8c6db833 1195
6f883237 1196 return r == 0;
8c6db833
LP
1197}
1198
6f883237 1199int cg_is_empty_recursive(const char *controller, const char *path) {
415fc41c 1200 int r;
8c6db833 1201
8c6db833
LP
1202 assert(path);
1203
6fd66507
LP
1204 /* The root cgroup is always populated */
1205 if (controller && (isempty(path) || path_equal(path, "/")))
efdb0237 1206 return false;
6fd66507 1207
c22800e4 1208 r = cg_unified_controller(controller);
b4cccbc1
LP
1209 if (r < 0)
1210 return r;
1211 if (r > 0) {
ab2c3861 1212 _cleanup_free_ char *t = NULL;
8c6db833 1213
efdb0237 1214 /* On the unified hierarchy we can check empty state
ab2c3861 1215 * via the "populated" attribute of "cgroup.events". */
8c6db833 1216
ab2c3861 1217 r = cg_read_event(controller, path, "populated", &t);
efdb0237
LP
1218 if (r < 0)
1219 return r;
1220
1221 return streq(t, "0");
1222 } else {
1223 _cleanup_closedir_ DIR *d = NULL;
1224 char *fn;
8c6db833 1225
efdb0237 1226 r = cg_is_empty(controller, path);
35d2e7ec 1227 if (r <= 0)
7027ff61 1228 return r;
35d2e7ec 1229
efdb0237
LP
1230 r = cg_enumerate_subgroups(controller, path, &d);
1231 if (r == -ENOENT)
1232 return 1;
1233 if (r < 0)
1234 return r;
35d2e7ec 1235
efdb0237
LP
1236 while ((r = cg_read_subgroup(d, &fn)) > 0) {
1237 _cleanup_free_ char *p = NULL;
1238
605405c6 1239 p = strjoin(path, "/", fn);
efdb0237
LP
1240 free(fn);
1241 if (!p)
1242 return -ENOMEM;
1243
1244 r = cg_is_empty_recursive(controller, p);
1245 if (r <= 0)
1246 return r;
1247 }
1248 if (r < 0)
1249 return r;
1250
1251 return true;
1252 }
35d2e7ec
LP
1253}
1254
1255int cg_split_spec(const char *spec, char **controller, char **path) {
35d2e7ec 1256 char *t = NULL, *u = NULL;
efdb0237 1257 const char *e;
35d2e7ec
LP
1258
1259 assert(spec);
35d2e7ec
LP
1260
1261 if (*spec == '/') {
99be45a4 1262 if (!path_is_normalized(spec))
e884315e 1263 return -EINVAL;
35d2e7ec
LP
1264
1265 if (path) {
246aa6dd
LP
1266 t = strdup(spec);
1267 if (!t)
35d2e7ec
LP
1268 return -ENOMEM;
1269
dbb9401d 1270 *path = path_kill_slashes(t);
8c6db833
LP
1271 }
1272
35d2e7ec
LP
1273 if (controller)
1274 *controller = NULL;
1275
1276 return 0;
8c6db833
LP
1277 }
1278
246aa6dd
LP
1279 e = strchr(spec, ':');
1280 if (!e) {
185a0874 1281 if (!cg_controller_is_valid(spec))
35d2e7ec
LP
1282 return -EINVAL;
1283
1284 if (controller) {
efdb0237 1285 t = strdup(spec);
246aa6dd 1286 if (!t)
35d2e7ec
LP
1287 return -ENOMEM;
1288
1289 *controller = t;
1290 }
1291
1292 if (path)
1293 *path = NULL;
1294
1295 return 0;
8c6db833
LP
1296 }
1297
efdb0237 1298 t = strndup(spec, e-spec);
e884315e
LP
1299 if (!t)
1300 return -ENOMEM;
185a0874 1301 if (!cg_controller_is_valid(t)) {
e884315e 1302 free(t);
35d2e7ec 1303 return -EINVAL;
246aa6dd
LP
1304 }
1305
efdb0237
LP
1306 if (isempty(e+1))
1307 u = NULL;
1308 else {
baa89da4
LP
1309 u = strdup(e+1);
1310 if (!u) {
1311 free(t);
1312 return -ENOMEM;
1313 }
35d2e7ec 1314
99be45a4 1315 if (!path_is_normalized(u) ||
baa89da4
LP
1316 !path_is_absolute(u)) {
1317 free(t);
1318 free(u);
1319 return -EINVAL;
1320 }
1321
1322 path_kill_slashes(u);
1323 }
5954c074 1324
35d2e7ec
LP
1325 if (controller)
1326 *controller = t;
e884315e
LP
1327 else
1328 free(t);
35d2e7ec
LP
1329
1330 if (path)
1331 *path = u;
e884315e
LP
1332 else
1333 free(u);
35d2e7ec
LP
1334
1335 return 0;
8c6db833 1336}
c6c18be3 1337
7027ff61 1338int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1339 _cleanup_free_ char *c = NULL, *p = NULL;
1340 char *t;
35d2e7ec
LP
1341 int r;
1342
1343 assert(path);
1344 assert(result);
1345
73e231ab 1346 /* First, check if it already is a filesystem path */
7027ff61 1347 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1348
b69d29ce
LP
1349 t = strdup(path);
1350 if (!t)
35d2e7ec
LP
1351 return -ENOMEM;
1352
dbb9401d 1353 *result = path_kill_slashes(t);
35d2e7ec
LP
1354 return 0;
1355 }
1356
73e231ab 1357 /* Otherwise, treat it as cg spec */
b69d29ce
LP
1358 r = cg_split_spec(path, &c, &p);
1359 if (r < 0)
35d2e7ec
LP
1360 return r;
1361
efdb0237 1362 return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
35d2e7ec 1363}
1f73f0f1 1364
7027ff61 1365int cg_get_root_path(char **path) {
9444b1f2 1366 char *p, *e;
7027ff61
LP
1367 int r;
1368
1369 assert(path);
1370
9444b1f2 1371 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1372 if (r < 0)
1373 return r;
1374
efdb0237
LP
1375 e = endswith(p, "/" SPECIAL_INIT_SCOPE);
1376 if (!e)
1377 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
1378 if (!e)
1379 e = endswith(p, "/system"); /* even more legacy */
9444b1f2 1380 if (e)
7027ff61
LP
1381 *e = 0;
1382
1f73f0f1
LP
1383 *path = p;
1384 return 0;
1385}
b59e2465 1386
751bc6ac
LP
1387int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1388 _cleanup_free_ char *rt = NULL;
1389 char *p;
ba1261bc
LP
1390 int r;
1391
e9174f29 1392 assert(cgroup);
751bc6ac 1393 assert(shifted);
e9174f29
LP
1394
1395 if (!root) {
1396 /* If the root was specified let's use that, otherwise
1397 * let's determine it from PID 1 */
1398
751bc6ac 1399 r = cg_get_root_path(&rt);
e9174f29
LP
1400 if (r < 0)
1401 return r;
1402
751bc6ac 1403 root = rt;
e9174f29 1404 }
ba1261bc 1405
751bc6ac 1406 p = path_startswith(cgroup, root);
efdb0237 1407 if (p && p > cgroup)
751bc6ac
LP
1408 *shifted = p - 1;
1409 else
1410 *shifted = cgroup;
1411
1412 return 0;
1413}
1414
1415int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1416 _cleanup_free_ char *raw = NULL;
1417 const char *c;
1418 int r;
1419
1420 assert(pid >= 0);
1421 assert(cgroup);
1422
1423 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
7027ff61 1424 if (r < 0)
ba1261bc 1425 return r;
ba1261bc 1426
751bc6ac
LP
1427 r = cg_shift_path(raw, root, &c);
1428 if (r < 0)
1429 return r;
ba1261bc 1430
751bc6ac
LP
1431 if (c == raw) {
1432 *cgroup = raw;
1433 raw = NULL;
1434 } else {
1435 char *n;
ba1261bc 1436
751bc6ac
LP
1437 n = strdup(c);
1438 if (!n)
ba1261bc 1439 return -ENOMEM;
ba1261bc 1440
751bc6ac
LP
1441 *cgroup = n;
1442 }
ba1261bc
LP
1443
1444 return 0;
1445}
1446
9ed794a3 1447int cg_path_decode_unit(const char *cgroup, char **unit) {
8b0849e9
LP
1448 char *c, *s;
1449 size_t n;
ef1673d1
MT
1450
1451 assert(cgroup);
6c03089c 1452 assert(unit);
ef1673d1 1453
8b0849e9
LP
1454 n = strcspn(cgroup, "/");
1455 if (n < 3)
1456 return -ENXIO;
1457
1458 c = strndupa(cgroup, n);
ae018d9b 1459 c = cg_unescape(c);
ef1673d1 1460
7410616c 1461 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
cfeaa44a 1462 return -ENXIO;
ef1673d1 1463
d7bd3de0 1464 s = strdup(c);
6c03089c
LP
1465 if (!s)
1466 return -ENOMEM;
1467
1468 *unit = s;
ef1673d1
MT
1469 return 0;
1470}
1471
8b0849e9
LP
1472static bool valid_slice_name(const char *p, size_t n) {
1473
1474 if (!p)
1475 return false;
1476
fbd0b64f 1477 if (n < STRLEN("x.slice"))
8b0849e9
LP
1478 return false;
1479
1480 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1481 char buf[n+1], *c;
1482
1483 memcpy(buf, p, n);
1484 buf[n] = 0;
1485
1486 c = cg_unescape(buf);
1487
7410616c 1488 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
8b0849e9
LP
1489 }
1490
1491 return false;
1492}
1493
9444b1f2 1494static const char *skip_slices(const char *p) {
8b0849e9
LP
1495 assert(p);
1496
9444b1f2
LP
1497 /* Skips over all slice assignments */
1498
1499 for (;;) {
1021b21b
LP
1500 size_t n;
1501
9444b1f2
LP
1502 p += strspn(p, "/");
1503
1504 n = strcspn(p, "/");
8b0849e9 1505 if (!valid_slice_name(p, n))
9444b1f2
LP
1506 return p;
1507
1508 p += n;
1509 }
1510}
1511
8b0849e9 1512int cg_path_get_unit(const char *path, char **ret) {
6c03089c 1513 const char *e;
8b0849e9
LP
1514 char *unit;
1515 int r;
6c03089c
LP
1516
1517 assert(path);
8b0849e9 1518 assert(ret);
6c03089c 1519
9444b1f2 1520 e = skip_slices(path);
6c03089c 1521
8b0849e9
LP
1522 r = cg_path_decode_unit(e, &unit);
1523 if (r < 0)
1524 return r;
1525
1526 /* We skipped over the slices, don't accept any now */
1527 if (endswith(unit, ".slice")) {
1528 free(unit);
1529 return -ENXIO;
1530 }
1531
1532 *ret = unit;
1533 return 0;
6c03089c
LP
1534}
1535
1536int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1537 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1538 int r;
ba1261bc 1539
ef1673d1
MT
1540 assert(unit);
1541
7027ff61 1542 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1543 if (r < 0)
1544 return r;
1545
6c03089c
LP
1546 return cg_path_get_unit(cgroup, unit);
1547}
ef1673d1 1548
d4fffc4b
ZJS
1549/**
1550 * Skip session-*.scope, but require it to be there.
1551 */
9444b1f2
LP
1552static const char *skip_session(const char *p) {
1553 size_t n;
1554
8b0849e9
LP
1555 if (isempty(p))
1556 return NULL;
9444b1f2
LP
1557
1558 p += strspn(p, "/");
1559
1560 n = strcspn(p, "/");
fbd0b64f 1561 if (n < STRLEN("session-x.scope"))
d4fffc4b
ZJS
1562 return NULL;
1563
8b0849e9
LP
1564 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1565 char buf[n - 8 - 6 + 1];
1566
1567 memcpy(buf, p + 8, n - 8 - 6);
1568 buf[n - 8 - 6] = 0;
d4fffc4b 1569
8b0849e9
LP
1570 /* Note that session scopes never need unescaping,
1571 * since they cannot conflict with the kernel's own
1572 * names, hence we don't need to call cg_unescape()
1573 * here. */
1574
1575 if (!session_id_valid(buf))
1576 return false;
1577
1578 p += n;
1579 p += strspn(p, "/");
1580 return p;
1581 }
1582
1583 return NULL;
d4fffc4b
ZJS
1584}
1585
1586/**
1587 * Skip user@*.service, but require it to be there.
1588 */
1589static const char *skip_user_manager(const char *p) {
1590 size_t n;
1591
8b0849e9
LP
1592 if (isempty(p))
1593 return NULL;
d4fffc4b
ZJS
1594
1595 p += strspn(p, "/");
1596
1597 n = strcspn(p, "/");
fbd0b64f 1598 if (n < STRLEN("user@x.service"))
6c03089c 1599 return NULL;
ef1673d1 1600
8b0849e9
LP
1601 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1602 char buf[n - 5 - 8 + 1];
9444b1f2 1603
8b0849e9
LP
1604 memcpy(buf, p + 5, n - 5 - 8);
1605 buf[n - 5 - 8] = 0;
1606
1607 /* Note that user manager services never need unescaping,
1608 * since they cannot conflict with the kernel's own
1609 * names, hence we don't need to call cg_unescape()
1610 * here. */
1611
1612 if (parse_uid(buf, NULL) < 0)
1613 return NULL;
1614
1615 p += n;
1616 p += strspn(p, "/");
1617
1618 return p;
1619 }
1620
1621 return NULL;
9444b1f2
LP
1622}
1623
329ac4bc 1624static const char *skip_user_prefix(const char *path) {
d4fffc4b 1625 const char *e, *t;
ef1673d1 1626
6c03089c 1627 assert(path);
ba1261bc 1628
9444b1f2
LP
1629 /* Skip slices, if there are any */
1630 e = skip_slices(path);
ba1261bc 1631
329ac4bc 1632 /* Skip the user manager, if it's in the path now... */
8b0849e9 1633 t = skip_user_manager(e);
329ac4bc
LP
1634 if (t)
1635 return t;
8b0849e9 1636
329ac4bc
LP
1637 /* Alternatively skip the user session if it is in the path... */
1638 return skip_session(e);
1639}
32081481 1640
329ac4bc
LP
1641int cg_path_get_user_unit(const char *path, char **ret) {
1642 const char *t;
6c03089c 1643
329ac4bc
LP
1644 assert(path);
1645 assert(ret);
8b0849e9 1646
329ac4bc
LP
1647 t = skip_user_prefix(path);
1648 if (!t)
8b0849e9 1649 return -ENXIO;
8b0849e9 1650
329ac4bc
LP
1651 /* And from here on it looks pretty much the same as for a
1652 * system unit, hence let's use the same parser from here
1653 * on. */
1654 return cg_path_get_unit(t, ret);
ef1673d1 1655}
ba1261bc 1656
ef1673d1 1657int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1658 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1659 int r;
1660
1661 assert(unit);
1662
7027ff61 1663 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1664 if (r < 0)
1665 return r;
1666
1667 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1668}
e884315e 1669
7027ff61 1670int cg_path_get_machine_name(const char *path, char **machine) {
efdb0237
LP
1671 _cleanup_free_ char *u = NULL;
1672 const char *sl;
89f7c846 1673 int r;
374ec6ab 1674
89f7c846
LP
1675 r = cg_path_get_unit(path, &u);
1676 if (r < 0)
1677 return r;
7027ff61 1678
efdb0237 1679 sl = strjoina("/run/systemd/machines/unit:", u);
89f7c846 1680 return readlink_malloc(sl, machine);
7027ff61
LP
1681}
1682
1683int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1684 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1685 int r;
1686
1687 assert(machine);
1688
1689 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1690 if (r < 0)
1691 return r;
1692
1693 return cg_path_get_machine_name(cgroup, machine);
1694}
1695
1696int cg_path_get_session(const char *path, char **session) {
8b0849e9
LP
1697 _cleanup_free_ char *unit = NULL;
1698 char *start, *end;
1699 int r;
7027ff61
LP
1700
1701 assert(path);
7027ff61 1702
8b0849e9
LP
1703 r = cg_path_get_unit(path, &unit);
1704 if (r < 0)
1705 return r;
7027ff61 1706
8b0849e9
LP
1707 start = startswith(unit, "session-");
1708 if (!start)
cfeaa44a 1709 return -ENXIO;
8b0849e9
LP
1710 end = endswith(start, ".scope");
1711 if (!end)
cfeaa44a 1712 return -ENXIO;
8b0849e9
LP
1713
1714 *end = 0;
1715 if (!session_id_valid(start))
cfeaa44a 1716 return -ENXIO;
374ec6ab 1717
af08d2f9 1718 if (session) {
8b0849e9 1719 char *rr;
af08d2f9 1720
8b0849e9
LP
1721 rr = strdup(start);
1722 if (!rr)
af08d2f9
LP
1723 return -ENOMEM;
1724
8b0849e9 1725 *session = rr;
af08d2f9 1726 }
7027ff61 1727
7027ff61
LP
1728 return 0;
1729}
1730
1731int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1732 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1733 int r;
1734
7027ff61
LP
1735 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1736 if (r < 0)
1737 return r;
1738
1739 return cg_path_get_session(cgroup, session);
1740}
1741
ae018d9b 1742int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1743 _cleanup_free_ char *slice = NULL;
8b0849e9 1744 char *start, *end;
374ec6ab 1745 int r;
ae018d9b
LP
1746
1747 assert(path);
ae018d9b 1748
374ec6ab
LP
1749 r = cg_path_get_slice(path, &slice);
1750 if (r < 0)
1751 return r;
ae018d9b 1752
674eb685
LP
1753 start = startswith(slice, "user-");
1754 if (!start)
cfeaa44a 1755 return -ENXIO;
8b0849e9 1756 end = endswith(start, ".slice");
674eb685 1757 if (!end)
cfeaa44a 1758 return -ENXIO;
ae018d9b 1759
8b0849e9
LP
1760 *end = 0;
1761 if (parse_uid(start, uid) < 0)
cfeaa44a 1762 return -ENXIO;
674eb685 1763
674eb685 1764 return 0;
ae018d9b
LP
1765}
1766
1767int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1768 _cleanup_free_ char *cgroup = NULL;
1769 int r;
1770
ae018d9b
LP
1771 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1772 if (r < 0)
1773 return r;
1774
1775 return cg_path_get_owner_uid(cgroup, uid);
1776}
1777
1021b21b
LP
1778int cg_path_get_slice(const char *p, char **slice) {
1779 const char *e = NULL;
1021b21b
LP
1780
1781 assert(p);
1782 assert(slice);
1783
329ac4bc
LP
1784 /* Finds the right-most slice unit from the beginning, but
1785 * stops before we come to the first non-slice unit. */
1786
1021b21b
LP
1787 for (;;) {
1788 size_t n;
1789
1790 p += strspn(p, "/");
1791
1792 n = strcspn(p, "/");
8b0849e9 1793 if (!valid_slice_name(p, n)) {
1021b21b 1794
8b0849e9
LP
1795 if (!e) {
1796 char *s;
1021b21b 1797
e5d855d3 1798 s = strdup(SPECIAL_ROOT_SLICE);
8b0849e9
LP
1799 if (!s)
1800 return -ENOMEM;
1021b21b 1801
8b0849e9
LP
1802 *slice = s;
1803 return 0;
1804 }
1805
1806 return cg_path_decode_unit(e, slice);
1021b21b
LP
1807 }
1808
1809 e = p;
1021b21b
LP
1810 p += n;
1811 }
1812}
1813
1814int cg_pid_get_slice(pid_t pid, char **slice) {
1815 _cleanup_free_ char *cgroup = NULL;
1816 int r;
1817
1818 assert(slice);
1819
1820 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1821 if (r < 0)
1822 return r;
1823
1824 return cg_path_get_slice(cgroup, slice);
1825}
1826
329ac4bc
LP
1827int cg_path_get_user_slice(const char *p, char **slice) {
1828 const char *t;
1829 assert(p);
1830 assert(slice);
1831
1832 t = skip_user_prefix(p);
1833 if (!t)
1834 return -ENXIO;
1835
1836 /* And now it looks pretty much the same as for a system
1837 * slice, so let's just use the same parser from here on. */
1838 return cg_path_get_slice(t, slice);
1839}
1840
1841int cg_pid_get_user_slice(pid_t pid, char **slice) {
1842 _cleanup_free_ char *cgroup = NULL;
1843 int r;
1844
1845 assert(slice);
1846
1847 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1848 if (r < 0)
1849 return r;
1850
1851 return cg_path_get_user_slice(cgroup, slice);
1852}
1853
ae018d9b
LP
1854char *cg_escape(const char *p) {
1855 bool need_prefix = false;
1856
1857 /* This implements very minimal escaping for names to be used
1858 * as file names in the cgroup tree: any name which might
1859 * conflict with a kernel name or is prefixed with '_' is
1860 * prefixed with a '_'. That way, when reading cgroup names it
1861 * is sufficient to remove a single prefixing underscore if
1862 * there is one. */
1863
1864 /* The return value of this function (unlike cg_unescape())
1865 * needs free()! */
1866
4c701096 1867 if (IN_SET(p[0], 0, '_', '.') ||
a0ab5665
LP
1868 streq(p, "notify_on_release") ||
1869 streq(p, "release_agent") ||
efdb0237
LP
1870 streq(p, "tasks") ||
1871 startswith(p, "cgroup."))
ae018d9b
LP
1872 need_prefix = true;
1873 else {
1874 const char *dot;
1875
1876 dot = strrchr(p, '.');
1877 if (dot) {
efdb0237
LP
1878 CGroupController c;
1879 size_t l = dot - p;
ae018d9b 1880
efdb0237
LP
1881 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1882 const char *n;
1883
1884 n = cgroup_controller_to_string(c);
ae018d9b 1885
efdb0237
LP
1886 if (l != strlen(n))
1887 continue;
ae018d9b 1888
efdb0237
LP
1889 if (memcmp(p, n, l) != 0)
1890 continue;
1891
1892 need_prefix = true;
1893 break;
ae018d9b
LP
1894 }
1895 }
1896 }
1897
1898 if (need_prefix)
1899 return strappend("_", p);
efdb0237
LP
1900
1901 return strdup(p);
ae018d9b
LP
1902}
1903
1904char *cg_unescape(const char *p) {
1905 assert(p);
1906
1907 /* The return value of this function (unlike cg_escape())
1908 * doesn't need free()! */
1909
1910 if (p[0] == '_')
1911 return (char*) p+1;
1912
1913 return (char*) p;
1914}
78edb35a
LP
1915
1916#define CONTROLLER_VALID \
4b549144 1917 DIGITS LETTERS \
78edb35a
LP
1918 "_"
1919
185a0874 1920bool cg_controller_is_valid(const char *p) {
78edb35a
LP
1921 const char *t, *s;
1922
1923 if (!p)
1924 return false;
1925
b6629c4b
TH
1926 if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
1927 return true;
1928
185a0874
DJL
1929 s = startswith(p, "name=");
1930 if (s)
1931 p = s;
78edb35a 1932
4c701096 1933 if (IN_SET(*p, 0, '_'))
78edb35a
LP
1934 return false;
1935
1936 for (t = p; *t; t++)
1937 if (!strchr(CONTROLLER_VALID, *t))
1938 return false;
1939
1940 if (t - p > FILENAME_MAX)
1941 return false;
1942
1943 return true;
1944}
a016b922
LP
1945
1946int cg_slice_to_path(const char *unit, char **ret) {
1947 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1948 const char *dash;
7410616c 1949 int r;
a016b922
LP
1950
1951 assert(unit);
1952 assert(ret);
1953
e5d855d3 1954 if (streq(unit, SPECIAL_ROOT_SLICE)) {
c96cc582
LP
1955 char *x;
1956
1957 x = strdup("");
1958 if (!x)
1959 return -ENOMEM;
1960 *ret = x;
1961 return 0;
1962 }
1963
7410616c 1964 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
a016b922
LP
1965 return -EINVAL;
1966
1967 if (!endswith(unit, ".slice"))
1968 return -EINVAL;
1969
7410616c
LP
1970 r = unit_name_to_prefix(unit, &p);
1971 if (r < 0)
1972 return r;
a016b922
LP
1973
1974 dash = strchr(p, '-');
e66e5b61
LP
1975
1976 /* Don't allow initial dashes */
1977 if (dash == p)
1978 return -EINVAL;
1979
a016b922
LP
1980 while (dash) {
1981 _cleanup_free_ char *escaped = NULL;
1982 char n[dash - p + sizeof(".slice")];
1983
e66e5b61 1984 /* Don't allow trailing or double dashes */
4c701096 1985 if (IN_SET(dash[1], 0, '-'))
c96cc582 1986 return -EINVAL;
a016b922 1987
c96cc582 1988 strcpy(stpncpy(n, p, dash - p), ".slice");
7410616c 1989 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
a016b922
LP
1990 return -EINVAL;
1991
1992 escaped = cg_escape(n);
1993 if (!escaped)
1994 return -ENOMEM;
1995
1996 if (!strextend(&s, escaped, "/", NULL))
1997 return -ENOMEM;
1998
1999 dash = strchr(dash+1, '-');
2000 }
2001
2002 e = cg_escape(unit);
2003 if (!e)
2004 return -ENOMEM;
2005
2006 if (!strextend(&s, e, NULL))
2007 return -ENOMEM;
2008
2009 *ret = s;
2010 s = NULL;
2011
2012 return 0;
2013}
4ad49000
LP
2014
2015int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
2016 _cleanup_free_ char *p = NULL;
2017 int r;
2018
2019 r = cg_get_path(controller, path, attribute, &p);
2020 if (r < 0)
2021 return r;
2022
4c1fc3e4 2023 return write_string_file(p, value, 0);
4ad49000
LP
2024}
2025
934277fe
LP
2026int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
2027 _cleanup_free_ char *p = NULL;
2028 int r;
2029
2030 r = cg_get_path(controller, path, attribute, &p);
2031 if (r < 0)
2032 return r;
2033
2034 return read_one_line_file(p, ret);
2035}
2036
66ebf6c0
TH
2037int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) {
2038 _cleanup_free_ char *filename = NULL, *content = NULL;
2039 char *line, *p;
2040 int i, r;
2041
2042 for (i = 0; keys[i]; i++)
2043 values[i] = NULL;
2044
2045 r = cg_get_path(controller, path, attribute, &filename);
2046 if (r < 0)
2047 return r;
2048
2049 r = read_full_file(filename, &content, NULL);
2050 if (r < 0)
2051 return r;
2052
2053 p = content;
2054 while ((line = strsep(&p, "\n"))) {
2055 char *key;
2056
2057 key = strsep(&line, " ");
2058
2059 for (i = 0; keys[i]; i++) {
2060 if (streq(key, keys[i])) {
2061 values[i] = strdup(line);
2062 break;
2063 }
2064 }
2065 }
2066
2067 for (i = 0; keys[i]; i++) {
2068 if (!values[i]) {
2069 for (i = 0; keys[i]; i++) {
95333b2b 2070 values[i] = mfree(values[i]);
66ebf6c0
TH
2071 }
2072 return -ENOENT;
2073 }
2074 }
2075
2076 return 0;
2077}
2078
efdb0237
LP
2079int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2080 CGroupController c;
415fc41c 2081 int r;
4ad49000
LP
2082
2083 /* This one will create a cgroup in our private tree, but also
2084 * duplicate it in the trees specified in mask, and remove it
2085 * in all others */
2086
2087 /* First create the cgroup in our own hierarchy. */
2088 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2089 if (r < 0)
2090 return r;
2091
efdb0237 2092 /* If we are in the unified hierarchy, we are done now */
b4cccbc1
LP
2093 r = cg_all_unified();
2094 if (r < 0)
2095 return r;
2096 if (r > 0)
efdb0237
LP
2097 return 0;
2098
2099 /* Otherwise, do the same in the other hierarchies */
2100 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2101 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2102 const char *n;
2103
2104 n = cgroup_controller_to_string(c);
2105
13b84ec7 2106 if (mask & bit)
efdb0237 2107 (void) cg_create(n, path);
13b84ec7 2108 else if (supported & bit)
efdb0237 2109 (void) cg_trim(n, path, true);
4ad49000
LP
2110 }
2111
13b84ec7 2112 return 0;
4ad49000
LP
2113}
2114
efdb0237
LP
2115int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2116 CGroupController c;
415fc41c 2117 int r;
4ad49000
LP
2118
2119 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
2120 if (r < 0)
2121 return r;
4ad49000 2122
b4cccbc1
LP
2123 r = cg_all_unified();
2124 if (r < 0)
2125 return r;
2126 if (r > 0)
efdb0237 2127 return 0;
7b3fd631 2128
efdb0237
LP
2129 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2130 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2131 const char *p = NULL;
7b3fd631 2132
efdb0237
LP
2133 if (!(supported & bit))
2134 continue;
7b3fd631 2135
efdb0237
LP
2136 if (path_callback)
2137 p = path_callback(bit, userdata);
7b3fd631 2138
efdb0237
LP
2139 if (!p)
2140 p = path;
4ad49000 2141
efdb0237 2142 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
4ad49000
LP
2143 }
2144
13b84ec7 2145 return 0;
4ad49000
LP
2146}
2147
efdb0237 2148int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
2149 Iterator i;
2150 void *pidp;
2151 int r = 0;
2152
2153 SET_FOREACH(pidp, pids, i) {
fea72cc0 2154 pid_t pid = PTR_TO_PID(pidp);
13b84ec7 2155 int q;
6c12b52e 2156
7b3fd631 2157 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
efdb0237 2158 if (q < 0 && r >= 0)
13b84ec7 2159 r = q;
6c12b52e
LP
2160 }
2161
2162 return r;
2163}
2164
efdb0237 2165int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
b3c5bad3 2166 CGroupController c;
b4cccbc1 2167 int r = 0, q;
4ad49000 2168
13b84ec7 2169 if (!path_equal(from, to)) {
1d98fef1 2170 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
13b84ec7
LP
2171 if (r < 0)
2172 return r;
2173 }
4ad49000 2174
b4cccbc1
LP
2175 q = cg_all_unified();
2176 if (q < 0)
2177 return q;
2178 if (q > 0)
efdb0237 2179 return r;
03b90d4b 2180
efdb0237
LP
2181 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2182 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2183 const char *p = NULL;
03b90d4b 2184
efdb0237
LP
2185 if (!(supported & bit))
2186 continue;
03b90d4b 2187
efdb0237
LP
2188 if (to_callback)
2189 p = to_callback(bit, userdata);
4ad49000 2190
efdb0237
LP
2191 if (!p)
2192 p = to;
2193
1d98fef1 2194 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
4ad49000
LP
2195 }
2196
13b84ec7 2197 return 0;
4ad49000
LP
2198}
2199
efdb0237
LP
2200int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2201 CGroupController c;
b4cccbc1 2202 int r, q;
4ad49000
LP
2203
2204 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2205 if (r < 0)
2206 return r;
2207
b4cccbc1
LP
2208 q = cg_all_unified();
2209 if (q < 0)
2210 return q;
2211 if (q > 0)
efdb0237
LP
2212 return r;
2213
2214 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2215 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2216
2217 if (!(supported & bit))
2218 continue;
4ad49000 2219
efdb0237 2220 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
4ad49000
LP
2221 }
2222
13b84ec7 2223 return 0;
4ad49000
LP
2224}
2225
aae7e17f 2226int cg_mask_to_string(CGroupMask mask, char **ret) {
ec635a2d
LP
2227 _cleanup_free_ char *s = NULL;
2228 size_t n = 0, allocated = 0;
2229 bool space = false;
aae7e17f 2230 CGroupController c;
aae7e17f
FB
2231
2232 assert(ret);
2233
2234 if (mask == 0) {
2235 *ret = NULL;
2236 return 0;
2237 }
2238
2239 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
ec635a2d
LP
2240 const char *k;
2241 size_t l;
aae7e17f
FB
2242
2243 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2244 continue;
2245
ec635a2d
LP
2246 k = cgroup_controller_to_string(c);
2247 l = strlen(k);
2248
2249 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2250 return -ENOMEM;
2251
2252 if (space)
2253 s[n] = ' ';
2254 memcpy(s + n + space, k, l);
2255 n += space + l;
2256
2257 space = true;
aae7e17f
FB
2258 }
2259
ec635a2d 2260 assert(s);
aae7e17f 2261
ec635a2d 2262 s[n] = 0;
aae7e17f 2263 *ret = s;
ec635a2d
LP
2264 s = NULL;
2265
aae7e17f
FB
2266 return 0;
2267}
2268
2269int cg_mask_from_string(const char *value, CGroupMask *mask) {
2270 assert(mask);
2271 assert(value);
2272
2273 for (;;) {
2274 _cleanup_free_ char *n = NULL;
2275 CGroupController v;
2276 int r;
2277
2278 r = extract_first_word(&value, &n, NULL, 0);
2279 if (r < 0)
2280 return r;
2281 if (r == 0)
2282 break;
2283
2284 v = cgroup_controller_from_string(n);
2285 if (v < 0)
2286 continue;
2287
2288 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2289 }
2290 return 0;
2291}
2292
efdb0237
LP
2293int cg_mask_supported(CGroupMask *ret) {
2294 CGroupMask mask = 0;
415fc41c 2295 int r;
efdb0237
LP
2296
2297 /* Determines the mask of supported cgroup controllers. Only
2298 * includes controllers we can make sense of and that are
2299 * actually accessible. */
4ad49000 2300
b4cccbc1
LP
2301 r = cg_all_unified();
2302 if (r < 0)
2303 return r;
2304 if (r > 0) {
5f4c5fef 2305 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
efdb0237
LP
2306
2307 /* In the unified hierarchy we can read the supported
2308 * and accessible controllers from a the top-level
2309 * cgroup attribute */
2310
5f4c5fef
LP
2311 r = cg_get_root_path(&root);
2312 if (r < 0)
2313 return r;
2314
2315 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2316 if (r < 0)
2317 return r;
2318
2319 r = read_one_line_file(path, &controllers);
efdb0237
LP
2320 if (r < 0)
2321 return r;
4ad49000 2322
aae7e17f
FB
2323 r = cg_mask_from_string(controllers, &mask);
2324 if (r < 0)
2325 return r;
efdb0237 2326
66ebf6c0 2327 /* Currently, we support the cpu, memory, io and pids
03a7b521
LP
2328 * controller in the unified hierarchy, mask
2329 * everything else off. */
66ebf6c0 2330 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
efdb0237
LP
2331
2332 } else {
2333 CGroupController c;
2334
2335 /* In the legacy hierarchy, we check whether which
2336 * hierarchies are mounted. */
2337
2338 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2339 const char *n;
2340
2341 n = cgroup_controller_to_string(c);
2342 if (controller_is_accessible(n) >= 0)
2343 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2344 }
4ad49000
LP
2345 }
2346
efdb0237
LP
2347 *ret = mask;
2348 return 0;
4ad49000 2349}
b12afc8c 2350
6925a0de
LP
2351int cg_kernel_controllers(Set **ret) {
2352 _cleanup_set_free_free_ Set *controllers = NULL;
b12afc8c 2353 _cleanup_fclose_ FILE *f = NULL;
b12afc8c
LP
2354 int r;
2355
6925a0de 2356 assert(ret);
b12afc8c 2357
e155a0aa
LP
2358 /* Determines the full list of kernel-known controllers. Might
2359 * include controllers we don't actually support, arbitrary
2360 * named hierarchies and controllers that aren't currently
2361 * accessible (because not mounted). */
2362
6925a0de
LP
2363 controllers = set_new(&string_hash_ops);
2364 if (!controllers)
2365 return -ENOMEM;
2366
b12afc8c
LP
2367 f = fopen("/proc/cgroups", "re");
2368 if (!f) {
6925a0de
LP
2369 if (errno == ENOENT) {
2370 *ret = NULL;
b12afc8c 2371 return 0;
6925a0de
LP
2372 }
2373
b12afc8c
LP
2374 return -errno;
2375 }
2376
35bbbf85
LP
2377 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2378
b12afc8c 2379 /* Ignore the header line */
2351e44d 2380 (void) read_line(f, (size_t) -1, NULL);
b12afc8c
LP
2381
2382 for (;;) {
2383 char *controller;
2384 int enabled = 0;
2385
2386 errno = 0;
2387 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2388
2389 if (feof(f))
2390 break;
2391
b3267152 2392 if (ferror(f) && errno > 0)
b12afc8c
LP
2393 return -errno;
2394
2395 return -EBADMSG;
2396 }
2397
2398 if (!enabled) {
2399 free(controller);
2400 continue;
2401 }
2402
efdb0237 2403 if (!cg_controller_is_valid(controller)) {
b12afc8c
LP
2404 free(controller);
2405 return -EBADMSG;
2406 }
2407
2408 r = set_consume(controllers, controller);
2409 if (r < 0)
2410 return r;
2411 }
2412
6925a0de
LP
2413 *ret = controllers;
2414 controllers = NULL;
2415
b12afc8c
LP
2416 return 0;
2417}
efdb0237 2418
5da38d07
TH
2419static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2420
c22800e4
LP
2421/* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2422 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2423 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2424 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
f08e9287 2425 *
c22800e4
LP
2426 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2427 * process management but disable the compat dual layout, we return %true on
2428 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
f08e9287
TH
2429 */
2430static thread_local bool unified_systemd_v232;
2431
1fcca10e 2432static int cg_unified_update(void) {
efdb0237 2433
efdb0237
LP
2434 struct statfs fs;
2435
2436 /* Checks if we support the unified hierarchy. Returns an
2437 * error when the cgroup hierarchies aren't mounted yet or we
2438 * have any other trouble determining if the unified hierarchy
2439 * is supported. */
2440
5da38d07
TH
2441 if (unified_cache >= CGROUP_UNIFIED_NONE)
2442 return 0;
efdb0237
LP
2443
2444 if (statfs("/sys/fs/cgroup/", &fs) < 0)
9aa21133 2445 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m");
efdb0237 2446
9aa21133
ZJS
2447 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2448 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
5da38d07 2449 unified_cache = CGROUP_UNIFIED_ALL;
9aa21133 2450 } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2977724b 2451 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
f08e9287 2452 F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
9aa21133 2453 log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2977724b 2454 unified_cache = CGROUP_UNIFIED_SYSTEMD;
f08e9287 2455 unified_systemd_v232 = false;
f08e9287 2456 } else {
2977724b 2457 if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
9aa21133 2458 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
5535d8f7
EV
2459
2460 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2461 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2462 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2463 unified_systemd_v232 = true;
2464 } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2465 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2466 unified_cache = CGROUP_UNIFIED_NONE;
2467 } else {
2468 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
9aa21133 2469 (unsigned long long) fs.f_type);
5535d8f7 2470 unified_cache = CGROUP_UNIFIED_NONE;
9aa21133 2471 }
2977724b 2472 }
651d47d1
ZJS
2473 } else {
2474 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2475 (unsigned long long) fs.f_type);
8b3aa503 2476 return -ENOMEDIUM;
651d47d1 2477 }
efdb0237 2478
5da38d07
TH
2479 return 0;
2480}
2481
c22800e4 2482int cg_unified_controller(const char *controller) {
b4cccbc1 2483 int r;
5da38d07 2484
1fcca10e 2485 r = cg_unified_update();
b4cccbc1
LP
2486 if (r < 0)
2487 return r;
5da38d07 2488
fc9ae717
LP
2489 if (unified_cache == CGROUP_UNIFIED_NONE)
2490 return false;
2491
2492 if (unified_cache >= CGROUP_UNIFIED_ALL)
2493 return true;
2494
2495 return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
5da38d07
TH
2496}
2497
b4cccbc1 2498int cg_all_unified(void) {
4bb652ac
LP
2499 int r;
2500
2501 r = cg_unified_update();
2502 if (r < 0)
2503 return r;
2504
2505 return unified_cache >= CGROUP_UNIFIED_ALL;
efdb0237
LP
2506}
2507
b4cccbc1
LP
2508int cg_hybrid_unified(void) {
2509 int r;
2977724b 2510
1fcca10e 2511 r = cg_unified_update();
b4cccbc1
LP
2512 if (r < 0)
2513 return r;
2977724b 2514
f08e9287 2515 return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2977724b
TH
2516}
2517
415fc41c 2518int cg_unified_flush(void) {
5da38d07 2519 unified_cache = CGROUP_UNIFIED_UNKNOWN;
415fc41c 2520
1fcca10e 2521 return cg_unified_update();
efdb0237
LP
2522}
2523
2524int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
77fa610b 2525 _cleanup_fclose_ FILE *f = NULL;
efdb0237
LP
2526 _cleanup_free_ char *fs = NULL;
2527 CGroupController c;
415fc41c 2528 int r;
efdb0237
LP
2529
2530 assert(p);
2531
2532 if (supported == 0)
2533 return 0;
2534
b4cccbc1
LP
2535 r = cg_all_unified();
2536 if (r < 0)
2537 return r;
2538 if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
efdb0237
LP
2539 return 0;
2540
2541 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2542 if (r < 0)
2543 return r;
2544
2545 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2546 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2547 const char *n;
2548
2549 if (!(supported & bit))
2550 continue;
2551
2552 n = cgroup_controller_to_string(c);
2553 {
2554 char s[1 + strlen(n) + 1];
2555
2556 s[0] = mask & bit ? '+' : '-';
2557 strcpy(s + 1, n);
2558
77fa610b
LP
2559 if (!f) {
2560 f = fopen(fs, "we");
2561 if (!f) {
2562 log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2563 break;
2564 }
2565 }
2566
2567 r = write_string_stream(f, s, 0);
efdb0237 2568 if (r < 0)
98e4d8d7 2569 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
efdb0237
LP
2570 }
2571 }
2572
2573 return 0;
2574}
2575
2576bool cg_is_unified_wanted(void) {
2577 static thread_local int wanted = -1;
415fc41c 2578 int r;
1d84ad94 2579 bool b;
77fab2a9 2580 const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
efdb0237 2581
77fab2a9 2582 /* If we have a cached value, return that. */
efdb0237
LP
2583 if (wanted >= 0)
2584 return wanted;
2585
239a3d09
ZJS
2586 /* If the hierarchy is already mounted, then follow whatever
2587 * was chosen for it. */
2588 if (cg_unified_flush() >= 0)
b4cccbc1 2589 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
239a3d09 2590
77fab2a9
ZJS
2591 /* Otherwise, let's see what the kernel command line has to say.
2592 * Since checking is expensive, cache a non-error result. */
1d84ad94 2593 r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
efdb0237 2594
77fab2a9 2595 return (wanted = r > 0 ? b : is_default);
efdb0237
LP
2596}
2597
2598bool cg_is_legacy_wanted(void) {
239a3d09
ZJS
2599 static thread_local int wanted = -1;
2600
2601 /* If we have a cached value, return that. */
2602 if (wanted >= 0)
2603 return wanted;
2604
1b59cf04
ZJS
2605 /* Check if we have cgroups2 already mounted. */
2606 if (cg_unified_flush() >= 0 &&
2607 unified_cache == CGROUP_UNIFIED_ALL)
239a3d09 2608 return (wanted = false);
1b59cf04
ZJS
2609
2610 /* Otherwise, assume that at least partial legacy is wanted,
2611 * since cgroups2 should already be mounted at this point. */
239a3d09 2612 return (wanted = true);
efdb0237
LP
2613}
2614
a4464b95 2615bool cg_is_hybrid_wanted(void) {
5da38d07 2616 static thread_local int wanted = -1;
415fc41c 2617 int r;
1d84ad94 2618 bool b;
c19739db
ZJS
2619 const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2620 /* We default to true if the default is "hybrid", obviously,
2621 * but also when the default is "unified", because if we get
2622 * called, it means that unified hierarchy was not mounted. */
5da38d07 2623
77fab2a9 2624 /* If we have a cached value, return that. */
5da38d07
TH
2625 if (wanted >= 0)
2626 return wanted;
2627
239a3d09
ZJS
2628 /* If the hierarchy is already mounted, then follow whatever
2629 * was chosen for it. */
2630 if (cg_unified_flush() >= 0 &&
2631 unified_cache == CGROUP_UNIFIED_ALL)
2632 return (wanted = false);
2633
77fab2a9
ZJS
2634 /* Otherwise, let's see what the kernel command line has to say.
2635 * Since checking is expensive, cache a non-error result. */
1d84ad94 2636 r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
5da38d07 2637
2dcb526d
ZJS
2638 /* The meaning of the kernel option is reversed wrt. to the return value
2639 * of this function, hence the negation. */
77fab2a9 2640 return (wanted = r > 0 ? !b : is_default);
5da38d07
TH
2641}
2642
13c31542
TH
2643int cg_weight_parse(const char *s, uint64_t *ret) {
2644 uint64_t u;
2645 int r;
2646
2647 if (isempty(s)) {
2648 *ret = CGROUP_WEIGHT_INVALID;
2649 return 0;
2650 }
2651
2652 r = safe_atou64(s, &u);
2653 if (r < 0)
2654 return r;
2655
2656 if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2657 return -ERANGE;
2658
2659 *ret = u;
2660 return 0;
2661}
2662
9be57249
TH
2663const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2664 [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX,
2665 [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX,
ac06a0cf
TH
2666 [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX,
2667 [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX,
9be57249
TH
2668};
2669
2670static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2671 [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax",
2672 [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax",
ac06a0cf
TH
2673 [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax",
2674 [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax",
9be57249
TH
2675};
2676
2677DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2678
d53d9474
LP
2679int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2680 uint64_t u;
2681 int r;
2682
2683 if (isempty(s)) {
2684 *ret = CGROUP_CPU_SHARES_INVALID;
2685 return 0;
2686 }
2687
2688 r = safe_atou64(s, &u);
2689 if (r < 0)
2690 return r;
2691
2692 if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2693 return -ERANGE;
2694
2695 *ret = u;
2696 return 0;
2697}
2698
2699int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2700 uint64_t u;
2701 int r;
2702
2703 if (isempty(s)) {
2704 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2705 return 0;
2706 }
2707
2708 r = safe_atou64(s, &u);
2709 if (r < 0)
2710 return r;
2711
2712 if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2713 return -ERANGE;
2714
2715 *ret = u;
2716 return 0;
2717}
2718
f0bef277
EV
2719bool is_cgroup_fs(const struct statfs *s) {
2720 return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2721 is_fs_type(s, CGROUP2_SUPER_MAGIC);
2722}
2723
2724bool fd_is_cgroup_fs(int fd) {
2725 struct statfs s;
2726
2727 if (fstatfs(fd, &s) < 0)
2728 return -errno;
2729
2730 return is_cgroup_fs(&s);
2731}
2732
efdb0237
LP
2733static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2734 [CGROUP_CONTROLLER_CPU] = "cpu",
2735 [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
13c31542 2736 [CGROUP_CONTROLLER_IO] = "io",
efdb0237
LP
2737 [CGROUP_CONTROLLER_BLKIO] = "blkio",
2738 [CGROUP_CONTROLLER_MEMORY] = "memory",
3905f127 2739 [CGROUP_CONTROLLER_DEVICES] = "devices",
03a7b521 2740 [CGROUP_CONTROLLER_PIDS] = "pids",
efdb0237
LP
2741};
2742
2743DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);