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