]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/cgroup-util.c
fuzz-unit-file: add __has_feature(memory_sanitizer) when skipping ListenNetlink=
[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
dae8b82e
ZJS
782 r = mkdir_errno_wrapper(fs, 0755);
783 if (r == -EEXIST)
784 return 0;
785 if (r < 0)
786 return r;
1434ae6f 787
b4cccbc1
LP
788 r = cg_hybrid_unified();
789 if (r < 0)
790 return r;
791
792 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
793 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
794 if (r < 0)
795 log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
796 }
797
1434ae6f
LP
798 return 1;
799}
800
801int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
802 int r, q;
803
804 assert(pid >= 0);
805
806 r = cg_create(controller, path);
807 if (r < 0)
808 return r;
809
810 q = cg_attach(controller, path, pid);
811 if (q < 0)
812 return q;
813
814 /* This does not remove the cgroup on failure */
815 return r;
816}
817
8c6db833 818int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
819 _cleanup_free_ char *fs = NULL;
820 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
821 int r;
822
8c6db833
LP
823 assert(path);
824 assert(pid >= 0);
825
b043cd0b 826 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 827 if (r < 0)
c6c18be3 828 return r;
8c6db833
LP
829
830 if (pid == 0)
df0ff127 831 pid = getpid_cached();
8c6db833 832
d054f0a4 833 xsprintf(c, PID_FMT "\n", pid);
8c6db833 834
2977724b
TH
835 r = write_string_file(fs, c, 0);
836 if (r < 0)
837 return r;
838
b4cccbc1
LP
839 r = cg_hybrid_unified();
840 if (r < 0)
841 return r;
842
843 if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
2977724b
TH
844 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
845 if (r < 0)
bd68e99b 846 log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
2977724b
TH
847 }
848
849 return 0;
8c6db833
LP
850}
851
13b84ec7
LP
852int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
853 int r;
854
855 assert(controller);
856 assert(path);
857 assert(pid >= 0);
858
859 r = cg_attach(controller, path, pid);
860 if (r < 0) {
861 char prefix[strlen(path) + 1];
862
863 /* This didn't work? Then let's try all prefixes of
864 * the destination */
865
fecffe5d 866 PATH_FOREACH_PREFIX(prefix, path) {
e155a0aa
LP
867 int q;
868
869 q = cg_attach(controller, prefix, pid);
870 if (q >= 0)
871 return q;
13b84ec7
LP
872 }
873 }
874
e155a0aa 875 return r;
13b84ec7
LP
876}
877
62b9bb26 878int cg_set_access(
2d76d14e
LP
879 const char *controller,
880 const char *path,
2d76d14e
LP
881 uid_t uid,
882 gid_t gid) {
883
62b9bb26
LP
884 struct Attribute {
885 const char *name;
886 bool fatal;
887 };
888
889 /* cgroupsv1, aka legacy/non-unified */
890 static const struct Attribute legacy_attributes[] = {
891 { "cgroup.procs", true },
892 { "tasks", false },
893 { "cgroup.clone_children", false },
894 {},
895 };
896
897 /* cgroupsv2, aka unified */
898 static const struct Attribute unified_attributes[] = {
899 { "cgroup.procs", true },
900 { "cgroup.subtree_control", true },
901 { "cgroup.threads", false },
902 {},
903 };
904
905 static const struct Attribute* const attributes[] = {
906 [false] = legacy_attributes,
907 [true] = unified_attributes,
908 };
974efc46 909
40853aa5 910 _cleanup_free_ char *fs = NULL;
62b9bb26
LP
911 const struct Attribute *i;
912 int r, unified;
8c6db833 913
8c6db833
LP
914 assert(path);
915
62b9bb26 916 if (uid == UID_INVALID && gid == GID_INVALID)
8d53b453
LP
917 return 0;
918
62b9bb26
LP
919 unified = cg_unified_controller(controller);
920 if (unified < 0)
921 return unified;
8c6db833 922
62b9bb26
LP
923 /* Configure access to the cgroup itself */
924 r = cg_get_path(controller, path, NULL, &fs);
974efc46
LP
925 if (r < 0)
926 return r;
8c6db833 927
62b9bb26 928 r = chmod_and_chown(fs, 0755, uid, gid);
b4cccbc1
LP
929 if (r < 0)
930 return r;
40853aa5 931
62b9bb26
LP
932 /* Configure access to the cgroup's attributes */
933 for (i = attributes[unified]; i->name; i++) {
40853aa5 934 fs = mfree(fs);
40853aa5 935
62b9bb26 936 r = cg_get_path(controller, path, i->name, &fs);
40853aa5
LP
937 if (r < 0)
938 return r;
efdb0237 939
62b9bb26
LP
940 r = chmod_and_chown(fs, 0644, uid, gid);
941 if (r < 0) {
942 if (i->fatal)
943 return r;
5beac75e 944
62b9bb26
LP
945 log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
946 }
947 }
948
949 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
950 r = cg_hybrid_unified();
2977724b 951 if (r < 0)
62b9bb26
LP
952 return r;
953 if (r > 0) {
954 /* Always propagate access mode from unified to legacy controller */
955 r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
956 if (r < 0)
957 log_debug_errno(r, "Failed to set access on compatibility systemd cgroup %s, ignoring: %m", path);
958 }
2977724b 959 }
974efc46 960
efdb0237 961 return 0;
8c6db833
LP
962}
963
4b58153d
LP
964int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
965 _cleanup_free_ char *fs = NULL;
966 int r;
967
968 assert(path);
969 assert(name);
970 assert(value || size <= 0);
971
972 r = cg_get_path(controller, path, NULL, &fs);
973 if (r < 0)
974 return r;
975
976 if (setxattr(fs, name, value, size, flags) < 0)
977 return -errno;
978
979 return 0;
980}
981
982int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
983 _cleanup_free_ char *fs = NULL;
984 ssize_t n;
985 int r;
986
987 assert(path);
988 assert(name);
989
990 r = cg_get_path(controller, path, NULL, &fs);
991 if (r < 0)
992 return r;
993
994 n = getxattr(fs, name, value, size);
995 if (n < 0)
996 return -errno;
997
998 return (int) n;
999}
1000
7027ff61 1001int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
1002 _cleanup_fclose_ FILE *f = NULL;
1003 char line[LINE_MAX];
b6629c4b 1004 const char *fs, *controller_str;
efdb0237 1005 size_t cs = 0;
b4cccbc1 1006 int unified;
8c6db833 1007
8c6db833 1008 assert(path);
c6c18be3 1009 assert(pid >= 0);
8c6db833 1010
5da38d07
TH
1011 if (controller) {
1012 if (!cg_controller_is_valid(controller))
1013 return -EINVAL;
1014 } else
1015 controller = SYSTEMD_CGROUP_CONTROLLER;
1016
c22800e4 1017 unified = cg_unified_controller(controller);
b4cccbc1
LP
1018 if (unified < 0)
1019 return unified;
1020 if (unified == 0) {
b6629c4b
TH
1021 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1022 controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
1023 else
1024 controller_str = controller;
1025
1026 cs = strlen(controller_str);
1027 }
7027ff61 1028
b68fa010 1029 fs = procfs_file_alloca(pid, "cgroup");
c6c18be3 1030 f = fopen(fs, "re");
4c633005
LP
1031 if (!f)
1032 return errno == ENOENT ? -ESRCH : -errno;
1033
35bbbf85
LP
1034 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
1035
7027ff61 1036 FOREACH_LINE(line, f, return -errno) {
efdb0237 1037 char *e, *p;
c6c18be3
LP
1038
1039 truncate_nl(line);
1040
efdb0237
LP
1041 if (unified) {
1042 e = startswith(line, "0:");
1043 if (!e)
1044 continue;
c6c18be3 1045
efdb0237
LP
1046 e = strchr(e, ':');
1047 if (!e)
1048 continue;
1049 } else {
1050 char *l;
1051 size_t k;
1052 const char *word, *state;
1053 bool found = false;
1054
1055 l = strchr(line, ':');
1056 if (!l)
1057 continue;
8af8afd6 1058
efdb0237
LP
1059 l++;
1060 e = strchr(l, ':');
1061 if (!e)
1062 continue;
8af8afd6 1063
efdb0237 1064 *e = 0;
00d4b1e6 1065 FOREACH_WORD_SEPARATOR(word, k, l, ",", state)
b6629c4b 1066 if (k == cs && memcmp(word, controller_str, cs) == 0) {
efdb0237
LP
1067 found = true;
1068 break;
1069 }
efdb0237
LP
1070 if (!found)
1071 continue;
8af8afd6
LP
1072 }
1073
8af8afd6 1074 p = strdup(e + 1);
7027ff61
LP
1075 if (!p)
1076 return -ENOMEM;
c6c18be3 1077
5e20b0a4
LP
1078 /* Truncate suffix indicating the process is a zombie */
1079 e = endswith(p, " (deleted)");
1080 if (e)
1081 *e = 0;
1082
c6c18be3 1083 *path = p;
7027ff61 1084 return 0;
c6c18be3
LP
1085 }
1086
1c80e425 1087 return -ENODATA;
8c6db833
LP
1088}
1089
1090int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61 1091 _cleanup_free_ char *fs = NULL, *contents = NULL;
efdb0237 1092 const char *sc;
415fc41c 1093 int r;
8c6db833 1094
8c6db833
LP
1095 assert(agent);
1096
c22800e4 1097 r = cg_unified_controller(controller);
b4cccbc1
LP
1098 if (r < 0)
1099 return r;
1100 if (r > 0) /* doesn't apply to unified hierarchy */
efdb0237
LP
1101 return -EOPNOTSUPP;
1102
7027ff61
LP
1103 r = cg_get_path(controller, NULL, "release_agent", &fs);
1104 if (r < 0)
c6c18be3 1105 return r;
8c6db833 1106
7027ff61
LP
1107 r = read_one_line_file(fs, &contents);
1108 if (r < 0)
1109 return r;
8c6db833
LP
1110
1111 sc = strstrip(contents);
e155a0aa 1112 if (isempty(sc)) {
4c1fc3e4 1113 r = write_string_file(fs, agent, 0);
574d5f2d 1114 if (r < 0)
7027ff61 1115 return r;
b8725df8 1116 } else if (!path_equal(sc, agent))
7027ff61 1117 return -EEXIST;
8c6db833 1118
0da16248 1119 fs = mfree(fs);
7027ff61
LP
1120 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1121 if (r < 0)
1122 return r;
8c6db833 1123
0da16248 1124 contents = mfree(contents);
7027ff61
LP
1125 r = read_one_line_file(fs, &contents);
1126 if (r < 0)
1127 return r;
8c6db833
LP
1128
1129 sc = strstrip(contents);
8c6db833 1130 if (streq(sc, "0")) {
4c1fc3e4 1131 r = write_string_file(fs, "1", 0);
7027ff61
LP
1132 if (r < 0)
1133 return r;
c6c18be3 1134
7027ff61
LP
1135 return 1;
1136 }
8c6db833 1137
7027ff61
LP
1138 if (!streq(sc, "1"))
1139 return -EIO;
8c6db833 1140
7027ff61 1141 return 0;
8c6db833
LP
1142}
1143
ad929bcc
KS
1144int cg_uninstall_release_agent(const char *controller) {
1145 _cleanup_free_ char *fs = NULL;
415fc41c 1146 int r;
efdb0237 1147
c22800e4 1148 r = cg_unified_controller(controller);
b4cccbc1
LP
1149 if (r < 0)
1150 return r;
1151 if (r > 0) /* Doesn't apply to unified hierarchy */
efdb0237 1152 return -EOPNOTSUPP;
ad929bcc 1153
ac9ef333
LP
1154 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1155 if (r < 0)
1156 return r;
1157
4c1fc3e4 1158 r = write_string_file(fs, "0", 0);
ac9ef333
LP
1159 if (r < 0)
1160 return r;
1161
0da16248 1162 fs = mfree(fs);
ac9ef333 1163
ad929bcc
KS
1164 r = cg_get_path(controller, NULL, "release_agent", &fs);
1165 if (r < 0)
1166 return r;
1167
4c1fc3e4 1168 r = write_string_file(fs, "", 0);
ad929bcc
KS
1169 if (r < 0)
1170 return r;
1171
ac9ef333 1172 return 0;
ad929bcc
KS
1173}
1174
6f883237 1175int cg_is_empty(const char *controller, const char *path) {
7027ff61 1176 _cleanup_fclose_ FILE *f = NULL;
efdb0237 1177 pid_t pid;
7027ff61 1178 int r;
8c6db833 1179
8c6db833
LP
1180 assert(path);
1181
b043cd0b 1182 r = cg_enumerate_processes(controller, path, &f);
6f883237
LP
1183 if (r == -ENOENT)
1184 return 1;
c3175a7f 1185 if (r < 0)
6f883237 1186 return r;
8c6db833 1187
6f883237 1188 r = cg_read_pid(f, &pid);
c6c18be3
LP
1189 if (r < 0)
1190 return r;
8c6db833 1191
6f883237 1192 return r == 0;
8c6db833
LP
1193}
1194
6f883237 1195int cg_is_empty_recursive(const char *controller, const char *path) {
415fc41c 1196 int r;
8c6db833 1197
8c6db833
LP
1198 assert(path);
1199
6fd66507
LP
1200 /* The root cgroup is always populated */
1201 if (controller && (isempty(path) || path_equal(path, "/")))
efdb0237 1202 return false;
6fd66507 1203
c22800e4 1204 r = cg_unified_controller(controller);
b4cccbc1
LP
1205 if (r < 0)
1206 return r;
1207 if (r > 0) {
ab2c3861 1208 _cleanup_free_ char *t = NULL;
8c6db833 1209
efdb0237 1210 /* On the unified hierarchy we can check empty state
ab2c3861 1211 * via the "populated" attribute of "cgroup.events". */
8c6db833 1212
ab2c3861 1213 r = cg_read_event(controller, path, "populated", &t);
efdb0237
LP
1214 if (r < 0)
1215 return r;
1216
1217 return streq(t, "0");
1218 } else {
1219 _cleanup_closedir_ DIR *d = NULL;
1220 char *fn;
8c6db833 1221
efdb0237 1222 r = cg_is_empty(controller, path);
35d2e7ec 1223 if (r <= 0)
7027ff61 1224 return r;
35d2e7ec 1225
efdb0237
LP
1226 r = cg_enumerate_subgroups(controller, path, &d);
1227 if (r == -ENOENT)
1228 return 1;
1229 if (r < 0)
1230 return r;
35d2e7ec 1231
efdb0237
LP
1232 while ((r = cg_read_subgroup(d, &fn)) > 0) {
1233 _cleanup_free_ char *p = NULL;
1234
605405c6 1235 p = strjoin(path, "/", fn);
efdb0237
LP
1236 free(fn);
1237 if (!p)
1238 return -ENOMEM;
1239
1240 r = cg_is_empty_recursive(controller, p);
1241 if (r <= 0)
1242 return r;
1243 }
1244 if (r < 0)
1245 return r;
1246
1247 return true;
1248 }
35d2e7ec
LP
1249}
1250
1251int cg_split_spec(const char *spec, char **controller, char **path) {
35d2e7ec 1252 char *t = NULL, *u = NULL;
efdb0237 1253 const char *e;
35d2e7ec
LP
1254
1255 assert(spec);
35d2e7ec
LP
1256
1257 if (*spec == '/') {
99be45a4 1258 if (!path_is_normalized(spec))
e884315e 1259 return -EINVAL;
35d2e7ec
LP
1260
1261 if (path) {
246aa6dd
LP
1262 t = strdup(spec);
1263 if (!t)
35d2e7ec
LP
1264 return -ENOMEM;
1265
dbb9401d 1266 *path = path_kill_slashes(t);
8c6db833
LP
1267 }
1268
35d2e7ec
LP
1269 if (controller)
1270 *controller = NULL;
1271
1272 return 0;
8c6db833
LP
1273 }
1274
246aa6dd
LP
1275 e = strchr(spec, ':');
1276 if (!e) {
185a0874 1277 if (!cg_controller_is_valid(spec))
35d2e7ec
LP
1278 return -EINVAL;
1279
1280 if (controller) {
efdb0237 1281 t = strdup(spec);
246aa6dd 1282 if (!t)
35d2e7ec
LP
1283 return -ENOMEM;
1284
1285 *controller = t;
1286 }
1287
1288 if (path)
1289 *path = NULL;
1290
1291 return 0;
8c6db833
LP
1292 }
1293
efdb0237 1294 t = strndup(spec, e-spec);
e884315e
LP
1295 if (!t)
1296 return -ENOMEM;
185a0874 1297 if (!cg_controller_is_valid(t)) {
e884315e 1298 free(t);
35d2e7ec 1299 return -EINVAL;
246aa6dd
LP
1300 }
1301
efdb0237
LP
1302 if (isempty(e+1))
1303 u = NULL;
1304 else {
baa89da4
LP
1305 u = strdup(e+1);
1306 if (!u) {
1307 free(t);
1308 return -ENOMEM;
1309 }
35d2e7ec 1310
99be45a4 1311 if (!path_is_normalized(u) ||
baa89da4
LP
1312 !path_is_absolute(u)) {
1313 free(t);
1314 free(u);
1315 return -EINVAL;
1316 }
1317
1318 path_kill_slashes(u);
1319 }
5954c074 1320
35d2e7ec
LP
1321 if (controller)
1322 *controller = t;
e884315e
LP
1323 else
1324 free(t);
35d2e7ec
LP
1325
1326 if (path)
1327 *path = u;
e884315e
LP
1328 else
1329 free(u);
35d2e7ec
LP
1330
1331 return 0;
8c6db833 1332}
c6c18be3 1333
7027ff61 1334int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1335 _cleanup_free_ char *c = NULL, *p = NULL;
1336 char *t;
35d2e7ec
LP
1337 int r;
1338
1339 assert(path);
1340 assert(result);
1341
73e231ab 1342 /* First, check if it already is a filesystem path */
7027ff61 1343 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1344
b69d29ce
LP
1345 t = strdup(path);
1346 if (!t)
35d2e7ec
LP
1347 return -ENOMEM;
1348
dbb9401d 1349 *result = path_kill_slashes(t);
35d2e7ec
LP
1350 return 0;
1351 }
1352
73e231ab 1353 /* Otherwise, treat it as cg spec */
b69d29ce
LP
1354 r = cg_split_spec(path, &c, &p);
1355 if (r < 0)
35d2e7ec
LP
1356 return r;
1357
efdb0237 1358 return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
35d2e7ec 1359}
1f73f0f1 1360
7027ff61 1361int cg_get_root_path(char **path) {
9444b1f2 1362 char *p, *e;
7027ff61
LP
1363 int r;
1364
1365 assert(path);
1366
9444b1f2 1367 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1368 if (r < 0)
1369 return r;
1370
efdb0237
LP
1371 e = endswith(p, "/" SPECIAL_INIT_SCOPE);
1372 if (!e)
1373 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
1374 if (!e)
1375 e = endswith(p, "/system"); /* even more legacy */
9444b1f2 1376 if (e)
7027ff61
LP
1377 *e = 0;
1378
1f73f0f1
LP
1379 *path = p;
1380 return 0;
1381}
b59e2465 1382
751bc6ac
LP
1383int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1384 _cleanup_free_ char *rt = NULL;
1385 char *p;
ba1261bc
LP
1386 int r;
1387
e9174f29 1388 assert(cgroup);
751bc6ac 1389 assert(shifted);
e9174f29
LP
1390
1391 if (!root) {
1392 /* If the root was specified let's use that, otherwise
1393 * let's determine it from PID 1 */
1394
751bc6ac 1395 r = cg_get_root_path(&rt);
e9174f29
LP
1396 if (r < 0)
1397 return r;
1398
751bc6ac 1399 root = rt;
e9174f29 1400 }
ba1261bc 1401
751bc6ac 1402 p = path_startswith(cgroup, root);
efdb0237 1403 if (p && p > cgroup)
751bc6ac
LP
1404 *shifted = p - 1;
1405 else
1406 *shifted = cgroup;
1407
1408 return 0;
1409}
1410
1411int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1412 _cleanup_free_ char *raw = NULL;
1413 const char *c;
1414 int r;
1415
1416 assert(pid >= 0);
1417 assert(cgroup);
1418
1419 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
7027ff61 1420 if (r < 0)
ba1261bc 1421 return r;
ba1261bc 1422
751bc6ac
LP
1423 r = cg_shift_path(raw, root, &c);
1424 if (r < 0)
1425 return r;
ba1261bc 1426
ae2a15bc
LP
1427 if (c == raw)
1428 *cgroup = TAKE_PTR(raw);
1429 else {
751bc6ac 1430 char *n;
ba1261bc 1431
751bc6ac
LP
1432 n = strdup(c);
1433 if (!n)
ba1261bc 1434 return -ENOMEM;
ba1261bc 1435
751bc6ac
LP
1436 *cgroup = n;
1437 }
ba1261bc
LP
1438
1439 return 0;
1440}
1441
9ed794a3 1442int cg_path_decode_unit(const char *cgroup, char **unit) {
8b0849e9
LP
1443 char *c, *s;
1444 size_t n;
ef1673d1
MT
1445
1446 assert(cgroup);
6c03089c 1447 assert(unit);
ef1673d1 1448
8b0849e9
LP
1449 n = strcspn(cgroup, "/");
1450 if (n < 3)
1451 return -ENXIO;
1452
1453 c = strndupa(cgroup, n);
ae018d9b 1454 c = cg_unescape(c);
ef1673d1 1455
7410616c 1456 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
cfeaa44a 1457 return -ENXIO;
ef1673d1 1458
d7bd3de0 1459 s = strdup(c);
6c03089c
LP
1460 if (!s)
1461 return -ENOMEM;
1462
1463 *unit = s;
ef1673d1
MT
1464 return 0;
1465}
1466
8b0849e9
LP
1467static bool valid_slice_name(const char *p, size_t n) {
1468
1469 if (!p)
1470 return false;
1471
fbd0b64f 1472 if (n < STRLEN("x.slice"))
8b0849e9
LP
1473 return false;
1474
1475 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1476 char buf[n+1], *c;
1477
1478 memcpy(buf, p, n);
1479 buf[n] = 0;
1480
1481 c = cg_unescape(buf);
1482
7410616c 1483 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
8b0849e9
LP
1484 }
1485
1486 return false;
1487}
1488
9444b1f2 1489static const char *skip_slices(const char *p) {
8b0849e9
LP
1490 assert(p);
1491
9444b1f2
LP
1492 /* Skips over all slice assignments */
1493
1494 for (;;) {
1021b21b
LP
1495 size_t n;
1496
9444b1f2
LP
1497 p += strspn(p, "/");
1498
1499 n = strcspn(p, "/");
8b0849e9 1500 if (!valid_slice_name(p, n))
9444b1f2
LP
1501 return p;
1502
1503 p += n;
1504 }
1505}
1506
8b0849e9 1507int cg_path_get_unit(const char *path, char **ret) {
6c03089c 1508 const char *e;
8b0849e9
LP
1509 char *unit;
1510 int r;
6c03089c
LP
1511
1512 assert(path);
8b0849e9 1513 assert(ret);
6c03089c 1514
9444b1f2 1515 e = skip_slices(path);
6c03089c 1516
8b0849e9
LP
1517 r = cg_path_decode_unit(e, &unit);
1518 if (r < 0)
1519 return r;
1520
1521 /* We skipped over the slices, don't accept any now */
1522 if (endswith(unit, ".slice")) {
1523 free(unit);
1524 return -ENXIO;
1525 }
1526
1527 *ret = unit;
1528 return 0;
6c03089c
LP
1529}
1530
1531int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1532 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1533 int r;
ba1261bc 1534
ef1673d1
MT
1535 assert(unit);
1536
7027ff61 1537 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1538 if (r < 0)
1539 return r;
1540
6c03089c
LP
1541 return cg_path_get_unit(cgroup, unit);
1542}
ef1673d1 1543
d4fffc4b
ZJS
1544/**
1545 * Skip session-*.scope, but require it to be there.
1546 */
9444b1f2
LP
1547static const char *skip_session(const char *p) {
1548 size_t n;
1549
8b0849e9
LP
1550 if (isempty(p))
1551 return NULL;
9444b1f2
LP
1552
1553 p += strspn(p, "/");
1554
1555 n = strcspn(p, "/");
fbd0b64f 1556 if (n < STRLEN("session-x.scope"))
d4fffc4b
ZJS
1557 return NULL;
1558
8b0849e9
LP
1559 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1560 char buf[n - 8 - 6 + 1];
1561
1562 memcpy(buf, p + 8, n - 8 - 6);
1563 buf[n - 8 - 6] = 0;
d4fffc4b 1564
8b0849e9
LP
1565 /* Note that session scopes never need unescaping,
1566 * since they cannot conflict with the kernel's own
1567 * names, hence we don't need to call cg_unescape()
1568 * here. */
1569
1570 if (!session_id_valid(buf))
1571 return false;
1572
1573 p += n;
1574 p += strspn(p, "/");
1575 return p;
1576 }
1577
1578 return NULL;
d4fffc4b
ZJS
1579}
1580
1581/**
1582 * Skip user@*.service, but require it to be there.
1583 */
1584static const char *skip_user_manager(const char *p) {
1585 size_t n;
1586
8b0849e9
LP
1587 if (isempty(p))
1588 return NULL;
d4fffc4b
ZJS
1589
1590 p += strspn(p, "/");
1591
1592 n = strcspn(p, "/");
fbd0b64f 1593 if (n < STRLEN("user@x.service"))
6c03089c 1594 return NULL;
ef1673d1 1595
8b0849e9
LP
1596 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1597 char buf[n - 5 - 8 + 1];
9444b1f2 1598
8b0849e9
LP
1599 memcpy(buf, p + 5, n - 5 - 8);
1600 buf[n - 5 - 8] = 0;
1601
1602 /* Note that user manager services never need unescaping,
1603 * since they cannot conflict with the kernel's own
1604 * names, hence we don't need to call cg_unescape()
1605 * here. */
1606
1607 if (parse_uid(buf, NULL) < 0)
1608 return NULL;
1609
1610 p += n;
1611 p += strspn(p, "/");
1612
1613 return p;
1614 }
1615
1616 return NULL;
9444b1f2
LP
1617}
1618
329ac4bc 1619static const char *skip_user_prefix(const char *path) {
d4fffc4b 1620 const char *e, *t;
ef1673d1 1621
6c03089c 1622 assert(path);
ba1261bc 1623
9444b1f2
LP
1624 /* Skip slices, if there are any */
1625 e = skip_slices(path);
ba1261bc 1626
329ac4bc 1627 /* Skip the user manager, if it's in the path now... */
8b0849e9 1628 t = skip_user_manager(e);
329ac4bc
LP
1629 if (t)
1630 return t;
8b0849e9 1631
329ac4bc
LP
1632 /* Alternatively skip the user session if it is in the path... */
1633 return skip_session(e);
1634}
32081481 1635
329ac4bc
LP
1636int cg_path_get_user_unit(const char *path, char **ret) {
1637 const char *t;
6c03089c 1638
329ac4bc
LP
1639 assert(path);
1640 assert(ret);
8b0849e9 1641
329ac4bc
LP
1642 t = skip_user_prefix(path);
1643 if (!t)
8b0849e9 1644 return -ENXIO;
8b0849e9 1645
329ac4bc
LP
1646 /* And from here on it looks pretty much the same as for a
1647 * system unit, hence let's use the same parser from here
1648 * on. */
1649 return cg_path_get_unit(t, ret);
ef1673d1 1650}
ba1261bc 1651
ef1673d1 1652int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1653 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1654 int r;
1655
1656 assert(unit);
1657
7027ff61 1658 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1659 if (r < 0)
1660 return r;
1661
1662 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1663}
e884315e 1664
7027ff61 1665int cg_path_get_machine_name(const char *path, char **machine) {
efdb0237
LP
1666 _cleanup_free_ char *u = NULL;
1667 const char *sl;
89f7c846 1668 int r;
374ec6ab 1669
89f7c846
LP
1670 r = cg_path_get_unit(path, &u);
1671 if (r < 0)
1672 return r;
7027ff61 1673
efdb0237 1674 sl = strjoina("/run/systemd/machines/unit:", u);
89f7c846 1675 return readlink_malloc(sl, machine);
7027ff61
LP
1676}
1677
1678int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1679 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1680 int r;
1681
1682 assert(machine);
1683
1684 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1685 if (r < 0)
1686 return r;
1687
1688 return cg_path_get_machine_name(cgroup, machine);
1689}
1690
1691int cg_path_get_session(const char *path, char **session) {
8b0849e9
LP
1692 _cleanup_free_ char *unit = NULL;
1693 char *start, *end;
1694 int r;
7027ff61
LP
1695
1696 assert(path);
7027ff61 1697
8b0849e9
LP
1698 r = cg_path_get_unit(path, &unit);
1699 if (r < 0)
1700 return r;
7027ff61 1701
8b0849e9
LP
1702 start = startswith(unit, "session-");
1703 if (!start)
cfeaa44a 1704 return -ENXIO;
8b0849e9
LP
1705 end = endswith(start, ".scope");
1706 if (!end)
cfeaa44a 1707 return -ENXIO;
8b0849e9
LP
1708
1709 *end = 0;
1710 if (!session_id_valid(start))
cfeaa44a 1711 return -ENXIO;
374ec6ab 1712
af08d2f9 1713 if (session) {
8b0849e9 1714 char *rr;
af08d2f9 1715
8b0849e9
LP
1716 rr = strdup(start);
1717 if (!rr)
af08d2f9
LP
1718 return -ENOMEM;
1719
8b0849e9 1720 *session = rr;
af08d2f9 1721 }
7027ff61 1722
7027ff61
LP
1723 return 0;
1724}
1725
1726int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1727 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1728 int r;
1729
7027ff61
LP
1730 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1731 if (r < 0)
1732 return r;
1733
1734 return cg_path_get_session(cgroup, session);
1735}
1736
ae018d9b 1737int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1738 _cleanup_free_ char *slice = NULL;
8b0849e9 1739 char *start, *end;
374ec6ab 1740 int r;
ae018d9b
LP
1741
1742 assert(path);
ae018d9b 1743
374ec6ab
LP
1744 r = cg_path_get_slice(path, &slice);
1745 if (r < 0)
1746 return r;
ae018d9b 1747
674eb685
LP
1748 start = startswith(slice, "user-");
1749 if (!start)
cfeaa44a 1750 return -ENXIO;
8b0849e9 1751 end = endswith(start, ".slice");
674eb685 1752 if (!end)
cfeaa44a 1753 return -ENXIO;
ae018d9b 1754
8b0849e9
LP
1755 *end = 0;
1756 if (parse_uid(start, uid) < 0)
cfeaa44a 1757 return -ENXIO;
674eb685 1758
674eb685 1759 return 0;
ae018d9b
LP
1760}
1761
1762int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1763 _cleanup_free_ char *cgroup = NULL;
1764 int r;
1765
ae018d9b
LP
1766 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1767 if (r < 0)
1768 return r;
1769
1770 return cg_path_get_owner_uid(cgroup, uid);
1771}
1772
1021b21b
LP
1773int cg_path_get_slice(const char *p, char **slice) {
1774 const char *e = NULL;
1021b21b
LP
1775
1776 assert(p);
1777 assert(slice);
1778
329ac4bc
LP
1779 /* Finds the right-most slice unit from the beginning, but
1780 * stops before we come to the first non-slice unit. */
1781
1021b21b
LP
1782 for (;;) {
1783 size_t n;
1784
1785 p += strspn(p, "/");
1786
1787 n = strcspn(p, "/");
8b0849e9 1788 if (!valid_slice_name(p, n)) {
1021b21b 1789
8b0849e9
LP
1790 if (!e) {
1791 char *s;
1021b21b 1792
e5d855d3 1793 s = strdup(SPECIAL_ROOT_SLICE);
8b0849e9
LP
1794 if (!s)
1795 return -ENOMEM;
1021b21b 1796
8b0849e9
LP
1797 *slice = s;
1798 return 0;
1799 }
1800
1801 return cg_path_decode_unit(e, slice);
1021b21b
LP
1802 }
1803
1804 e = p;
1021b21b
LP
1805 p += n;
1806 }
1807}
1808
1809int cg_pid_get_slice(pid_t pid, char **slice) {
1810 _cleanup_free_ char *cgroup = NULL;
1811 int r;
1812
1813 assert(slice);
1814
1815 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1816 if (r < 0)
1817 return r;
1818
1819 return cg_path_get_slice(cgroup, slice);
1820}
1821
329ac4bc
LP
1822int cg_path_get_user_slice(const char *p, char **slice) {
1823 const char *t;
1824 assert(p);
1825 assert(slice);
1826
1827 t = skip_user_prefix(p);
1828 if (!t)
1829 return -ENXIO;
1830
1831 /* And now it looks pretty much the same as for a system
1832 * slice, so let's just use the same parser from here on. */
1833 return cg_path_get_slice(t, slice);
1834}
1835
1836int cg_pid_get_user_slice(pid_t pid, char **slice) {
1837 _cleanup_free_ char *cgroup = NULL;
1838 int r;
1839
1840 assert(slice);
1841
1842 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1843 if (r < 0)
1844 return r;
1845
1846 return cg_path_get_user_slice(cgroup, slice);
1847}
1848
ae018d9b
LP
1849char *cg_escape(const char *p) {
1850 bool need_prefix = false;
1851
1852 /* This implements very minimal escaping for names to be used
1853 * as file names in the cgroup tree: any name which might
1854 * conflict with a kernel name or is prefixed with '_' is
1855 * prefixed with a '_'. That way, when reading cgroup names it
1856 * is sufficient to remove a single prefixing underscore if
1857 * there is one. */
1858
1859 /* The return value of this function (unlike cg_unescape())
1860 * needs free()! */
1861
4c701096 1862 if (IN_SET(p[0], 0, '_', '.') ||
a0ab5665
LP
1863 streq(p, "notify_on_release") ||
1864 streq(p, "release_agent") ||
efdb0237
LP
1865 streq(p, "tasks") ||
1866 startswith(p, "cgroup."))
ae018d9b
LP
1867 need_prefix = true;
1868 else {
1869 const char *dot;
1870
1871 dot = strrchr(p, '.');
1872 if (dot) {
efdb0237
LP
1873 CGroupController c;
1874 size_t l = dot - p;
ae018d9b 1875
efdb0237
LP
1876 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1877 const char *n;
1878
1879 n = cgroup_controller_to_string(c);
ae018d9b 1880
efdb0237
LP
1881 if (l != strlen(n))
1882 continue;
ae018d9b 1883
efdb0237
LP
1884 if (memcmp(p, n, l) != 0)
1885 continue;
1886
1887 need_prefix = true;
1888 break;
ae018d9b
LP
1889 }
1890 }
1891 }
1892
1893 if (need_prefix)
1894 return strappend("_", p);
efdb0237
LP
1895
1896 return strdup(p);
ae018d9b
LP
1897}
1898
1899char *cg_unescape(const char *p) {
1900 assert(p);
1901
1902 /* The return value of this function (unlike cg_escape())
1903 * doesn't need free()! */
1904
1905 if (p[0] == '_')
1906 return (char*) p+1;
1907
1908 return (char*) p;
1909}
78edb35a
LP
1910
1911#define CONTROLLER_VALID \
4b549144 1912 DIGITS LETTERS \
78edb35a
LP
1913 "_"
1914
185a0874 1915bool cg_controller_is_valid(const char *p) {
78edb35a
LP
1916 const char *t, *s;
1917
1918 if (!p)
1919 return false;
1920
b6629c4b
TH
1921 if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
1922 return true;
1923
185a0874
DJL
1924 s = startswith(p, "name=");
1925 if (s)
1926 p = s;
78edb35a 1927
4c701096 1928 if (IN_SET(*p, 0, '_'))
78edb35a
LP
1929 return false;
1930
1931 for (t = p; *t; t++)
1932 if (!strchr(CONTROLLER_VALID, *t))
1933 return false;
1934
1935 if (t - p > FILENAME_MAX)
1936 return false;
1937
1938 return true;
1939}
a016b922
LP
1940
1941int cg_slice_to_path(const char *unit, char **ret) {
1942 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1943 const char *dash;
7410616c 1944 int r;
a016b922
LP
1945
1946 assert(unit);
1947 assert(ret);
1948
e5d855d3 1949 if (streq(unit, SPECIAL_ROOT_SLICE)) {
c96cc582
LP
1950 char *x;
1951
1952 x = strdup("");
1953 if (!x)
1954 return -ENOMEM;
1955 *ret = x;
1956 return 0;
1957 }
1958
7410616c 1959 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
a016b922
LP
1960 return -EINVAL;
1961
1962 if (!endswith(unit, ".slice"))
1963 return -EINVAL;
1964
7410616c
LP
1965 r = unit_name_to_prefix(unit, &p);
1966 if (r < 0)
1967 return r;
a016b922
LP
1968
1969 dash = strchr(p, '-');
e66e5b61
LP
1970
1971 /* Don't allow initial dashes */
1972 if (dash == p)
1973 return -EINVAL;
1974
a016b922
LP
1975 while (dash) {
1976 _cleanup_free_ char *escaped = NULL;
1977 char n[dash - p + sizeof(".slice")];
1978
989290db 1979#if HAS_FEATURE_MEMORY_SANITIZER
1c56d501
ZJS
1980 /* msan doesn't instrument stpncpy, so it thinks
1981 * n is later used unitialized:
1982 * https://github.com/google/sanitizers/issues/926
1983 */
1984 zero(n);
1985#endif
1986
e66e5b61 1987 /* Don't allow trailing or double dashes */
4c701096 1988 if (IN_SET(dash[1], 0, '-'))
c96cc582 1989 return -EINVAL;
a016b922 1990
c96cc582 1991 strcpy(stpncpy(n, p, dash - p), ".slice");
7410616c 1992 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
a016b922
LP
1993 return -EINVAL;
1994
1995 escaped = cg_escape(n);
1996 if (!escaped)
1997 return -ENOMEM;
1998
1999 if (!strextend(&s, escaped, "/", NULL))
2000 return -ENOMEM;
2001
2002 dash = strchr(dash+1, '-');
2003 }
2004
2005 e = cg_escape(unit);
2006 if (!e)
2007 return -ENOMEM;
2008
2009 if (!strextend(&s, e, NULL))
2010 return -ENOMEM;
2011
ae2a15bc 2012 *ret = TAKE_PTR(s);
a016b922
LP
2013
2014 return 0;
2015}
4ad49000
LP
2016
2017int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
2018 _cleanup_free_ char *p = NULL;
2019 int r;
2020
2021 r = cg_get_path(controller, path, attribute, &p);
2022 if (r < 0)
2023 return r;
2024
4c1fc3e4 2025 return write_string_file(p, value, 0);
4ad49000
LP
2026}
2027
934277fe
LP
2028int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
2029 _cleanup_free_ char *p = NULL;
2030 int r;
2031
2032 r = cg_get_path(controller, path, attribute, &p);
2033 if (r < 0)
2034 return r;
2035
2036 return read_one_line_file(p, ret);
2037}
2038
b734a4ff
LP
2039int cg_get_keyed_attribute(
2040 const char *controller,
2041 const char *path,
2042 const char *attribute,
2043 char **keys,
2044 char **ret_values) {
66ebf6c0 2045
b734a4ff 2046 _cleanup_free_ char *filename = NULL, *contents = NULL;
b734a4ff 2047 const char *p;
9177fa9f 2048 size_t n, i, n_done = 0;
b734a4ff
LP
2049 char **v;
2050 int r;
2051
2052 /* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
2053 * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
2054 * entries as 'keys'. On success each entry will be set to the value of the matching key.
2055 *
2056 * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
66ebf6c0
TH
2057
2058 r = cg_get_path(controller, path, attribute, &filename);
2059 if (r < 0)
2060 return r;
2061
b734a4ff 2062 r = read_full_file(filename, &contents, NULL);
66ebf6c0
TH
2063 if (r < 0)
2064 return r;
2065
b734a4ff
LP
2066 n = strv_length(keys);
2067 if (n == 0) /* No keys to retrieve? That's easy, we are done then */
2068 return 0;
66ebf6c0 2069
b734a4ff
LP
2070 /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
2071 v = newa0(char*, n);
66ebf6c0 2072
b734a4ff
LP
2073 for (p = contents; *p;) {
2074 const char *w = NULL;
b734a4ff 2075
9177fa9f
ZJS
2076 for (i = 0; i < n; i++)
2077 if (!v[i]) {
b734a4ff
LP
2078 w = first_word(p, keys[i]);
2079 if (w)
2080 break;
66ebf6c0 2081 }
66ebf6c0 2082
b734a4ff 2083 if (w) {
b734a4ff
LP
2084 size_t l;
2085
2086 l = strcspn(w, NEWLINE);
9177fa9f
ZJS
2087 v[i] = strndup(w, l);
2088 if (!v[i]) {
b734a4ff
LP
2089 r = -ENOMEM;
2090 goto fail;
66ebf6c0 2091 }
b734a4ff 2092
b734a4ff 2093 n_done++;
b734a4ff
LP
2094 if (n_done >= n)
2095 goto done;
2096
2097 p = w + l;
9177fa9f 2098 } else
b734a4ff 2099 p += strcspn(p, NEWLINE);
b734a4ff
LP
2100
2101 p += strspn(p, NEWLINE);
66ebf6c0
TH
2102 }
2103
b734a4ff
LP
2104 r = -ENXIO;
2105
2106fail:
2107 for (i = 0; i < n; i++)
2108 free(v[i]);
2109
2110 return r;
2111
2112done:
2113 memcpy(ret_values, v, sizeof(char*) * n);
66ebf6c0 2114 return 0;
b734a4ff 2115
66ebf6c0
TH
2116}
2117
efdb0237
LP
2118int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2119 CGroupController c;
415fc41c 2120 int r;
4ad49000
LP
2121
2122 /* This one will create a cgroup in our private tree, but also
2123 * duplicate it in the trees specified in mask, and remove it
2124 * in all others */
2125
2126 /* First create the cgroup in our own hierarchy. */
2127 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2128 if (r < 0)
2129 return r;
2130
efdb0237 2131 /* If we are in the unified hierarchy, we are done now */
b4cccbc1
LP
2132 r = cg_all_unified();
2133 if (r < 0)
2134 return r;
2135 if (r > 0)
efdb0237
LP
2136 return 0;
2137
2138 /* Otherwise, do the same in the other hierarchies */
2139 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2140 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2141 const char *n;
2142
2143 n = cgroup_controller_to_string(c);
2144
13b84ec7 2145 if (mask & bit)
efdb0237 2146 (void) cg_create(n, path);
13b84ec7 2147 else if (supported & bit)
efdb0237 2148 (void) cg_trim(n, path, true);
4ad49000
LP
2149 }
2150
13b84ec7 2151 return 0;
4ad49000
LP
2152}
2153
efdb0237
LP
2154int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2155 CGroupController c;
415fc41c 2156 int r;
4ad49000
LP
2157
2158 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
2159 if (r < 0)
2160 return r;
4ad49000 2161
b4cccbc1
LP
2162 r = cg_all_unified();
2163 if (r < 0)
2164 return r;
2165 if (r > 0)
efdb0237 2166 return 0;
7b3fd631 2167
efdb0237
LP
2168 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2169 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2170 const char *p = NULL;
7b3fd631 2171
efdb0237
LP
2172 if (!(supported & bit))
2173 continue;
7b3fd631 2174
efdb0237
LP
2175 if (path_callback)
2176 p = path_callback(bit, userdata);
7b3fd631 2177
efdb0237
LP
2178 if (!p)
2179 p = path;
4ad49000 2180
efdb0237 2181 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
4ad49000
LP
2182 }
2183
13b84ec7 2184 return 0;
4ad49000
LP
2185}
2186
efdb0237 2187int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
2188 Iterator i;
2189 void *pidp;
2190 int r = 0;
2191
2192 SET_FOREACH(pidp, pids, i) {
fea72cc0 2193 pid_t pid = PTR_TO_PID(pidp);
13b84ec7 2194 int q;
6c12b52e 2195
7b3fd631 2196 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
efdb0237 2197 if (q < 0 && r >= 0)
13b84ec7 2198 r = q;
6c12b52e
LP
2199 }
2200
2201 return r;
2202}
2203
efdb0237 2204int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
b3c5bad3 2205 CGroupController c;
b4cccbc1 2206 int r = 0, q;
4ad49000 2207
13b84ec7 2208 if (!path_equal(from, to)) {
1d98fef1 2209 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
13b84ec7
LP
2210 if (r < 0)
2211 return r;
2212 }
4ad49000 2213
b4cccbc1
LP
2214 q = cg_all_unified();
2215 if (q < 0)
2216 return q;
2217 if (q > 0)
efdb0237 2218 return r;
03b90d4b 2219
efdb0237
LP
2220 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2221 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2222 const char *p = NULL;
03b90d4b 2223
efdb0237
LP
2224 if (!(supported & bit))
2225 continue;
03b90d4b 2226
efdb0237
LP
2227 if (to_callback)
2228 p = to_callback(bit, userdata);
4ad49000 2229
efdb0237
LP
2230 if (!p)
2231 p = to;
2232
1d98fef1 2233 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
4ad49000
LP
2234 }
2235
13b84ec7 2236 return 0;
4ad49000
LP
2237}
2238
efdb0237
LP
2239int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2240 CGroupController c;
b4cccbc1 2241 int r, q;
4ad49000
LP
2242
2243 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2244 if (r < 0)
2245 return r;
2246
b4cccbc1
LP
2247 q = cg_all_unified();
2248 if (q < 0)
2249 return q;
2250 if (q > 0)
efdb0237
LP
2251 return r;
2252
2253 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2254 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2255
2256 if (!(supported & bit))
2257 continue;
4ad49000 2258
efdb0237 2259 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
4ad49000
LP
2260 }
2261
13b84ec7 2262 return 0;
4ad49000
LP
2263}
2264
aae7e17f 2265int cg_mask_to_string(CGroupMask mask, char **ret) {
ec635a2d
LP
2266 _cleanup_free_ char *s = NULL;
2267 size_t n = 0, allocated = 0;
2268 bool space = false;
aae7e17f 2269 CGroupController c;
aae7e17f
FB
2270
2271 assert(ret);
2272
2273 if (mask == 0) {
2274 *ret = NULL;
2275 return 0;
2276 }
2277
2278 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
ec635a2d
LP
2279 const char *k;
2280 size_t l;
aae7e17f
FB
2281
2282 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2283 continue;
2284
ec635a2d
LP
2285 k = cgroup_controller_to_string(c);
2286 l = strlen(k);
2287
2288 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2289 return -ENOMEM;
2290
2291 if (space)
2292 s[n] = ' ';
2293 memcpy(s + n + space, k, l);
2294 n += space + l;
2295
2296 space = true;
aae7e17f
FB
2297 }
2298
ec635a2d 2299 assert(s);
aae7e17f 2300
ec635a2d 2301 s[n] = 0;
ae2a15bc 2302 *ret = TAKE_PTR(s);
ec635a2d 2303
aae7e17f
FB
2304 return 0;
2305}
2306
2307int cg_mask_from_string(const char *value, CGroupMask *mask) {
2308 assert(mask);
2309 assert(value);
2310
2311 for (;;) {
2312 _cleanup_free_ char *n = NULL;
2313 CGroupController v;
2314 int r;
2315
2316 r = extract_first_word(&value, &n, NULL, 0);
2317 if (r < 0)
2318 return r;
2319 if (r == 0)
2320 break;
2321
2322 v = cgroup_controller_from_string(n);
2323 if (v < 0)
2324 continue;
2325
2326 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2327 }
2328 return 0;
2329}
2330
efdb0237
LP
2331int cg_mask_supported(CGroupMask *ret) {
2332 CGroupMask mask = 0;
415fc41c 2333 int r;
efdb0237
LP
2334
2335 /* Determines the mask of supported cgroup controllers. Only
2336 * includes controllers we can make sense of and that are
2337 * actually accessible. */
4ad49000 2338
b4cccbc1
LP
2339 r = cg_all_unified();
2340 if (r < 0)
2341 return r;
2342 if (r > 0) {
5f4c5fef 2343 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
efdb0237
LP
2344
2345 /* In the unified hierarchy we can read the supported
2346 * and accessible controllers from a the top-level
2347 * cgroup attribute */
2348
5f4c5fef
LP
2349 r = cg_get_root_path(&root);
2350 if (r < 0)
2351 return r;
2352
2353 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2354 if (r < 0)
2355 return r;
2356
2357 r = read_one_line_file(path, &controllers);
efdb0237
LP
2358 if (r < 0)
2359 return r;
4ad49000 2360
aae7e17f
FB
2361 r = cg_mask_from_string(controllers, &mask);
2362 if (r < 0)
2363 return r;
efdb0237 2364
66ebf6c0 2365 /* Currently, we support the cpu, memory, io and pids
03a7b521
LP
2366 * controller in the unified hierarchy, mask
2367 * everything else off. */
66ebf6c0 2368 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
efdb0237
LP
2369
2370 } else {
2371 CGroupController c;
2372
2373 /* In the legacy hierarchy, we check whether which
2374 * hierarchies are mounted. */
2375
2376 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2377 const char *n;
2378
2379 n = cgroup_controller_to_string(c);
2380 if (controller_is_accessible(n) >= 0)
2381 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2382 }
4ad49000
LP
2383 }
2384
efdb0237
LP
2385 *ret = mask;
2386 return 0;
4ad49000 2387}
b12afc8c 2388
6925a0de
LP
2389int cg_kernel_controllers(Set **ret) {
2390 _cleanup_set_free_free_ Set *controllers = NULL;
b12afc8c 2391 _cleanup_fclose_ FILE *f = NULL;
b12afc8c
LP
2392 int r;
2393
6925a0de 2394 assert(ret);
b12afc8c 2395
e155a0aa
LP
2396 /* Determines the full list of kernel-known controllers. Might
2397 * include controllers we don't actually support, arbitrary
2398 * named hierarchies and controllers that aren't currently
2399 * accessible (because not mounted). */
2400
6925a0de
LP
2401 controllers = set_new(&string_hash_ops);
2402 if (!controllers)
2403 return -ENOMEM;
2404
b12afc8c
LP
2405 f = fopen("/proc/cgroups", "re");
2406 if (!f) {
6925a0de
LP
2407 if (errno == ENOENT) {
2408 *ret = NULL;
b12afc8c 2409 return 0;
6925a0de
LP
2410 }
2411
b12afc8c
LP
2412 return -errno;
2413 }
2414
35bbbf85
LP
2415 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2416
b12afc8c 2417 /* Ignore the header line */
2351e44d 2418 (void) read_line(f, (size_t) -1, NULL);
b12afc8c
LP
2419
2420 for (;;) {
2421 char *controller;
2422 int enabled = 0;
2423
2424 errno = 0;
2425 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2426
2427 if (feof(f))
2428 break;
2429
b3267152 2430 if (ferror(f) && errno > 0)
b12afc8c
LP
2431 return -errno;
2432
2433 return -EBADMSG;
2434 }
2435
2436 if (!enabled) {
2437 free(controller);
2438 continue;
2439 }
2440
efdb0237 2441 if (!cg_controller_is_valid(controller)) {
b12afc8c
LP
2442 free(controller);
2443 return -EBADMSG;
2444 }
2445
2446 r = set_consume(controllers, controller);
2447 if (r < 0)
2448 return r;
2449 }
2450
6925a0de
LP
2451 *ret = controllers;
2452 controllers = NULL;
2453
b12afc8c
LP
2454 return 0;
2455}
efdb0237 2456
5da38d07
TH
2457static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2458
c22800e4
LP
2459/* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
2460 * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2461 * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2462 * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
f08e9287 2463 *
c22800e4
LP
2464 * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
2465 * process management but disable the compat dual layout, we return %true on
2466 * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
f08e9287
TH
2467 */
2468static thread_local bool unified_systemd_v232;
2469
1fcca10e 2470static int cg_unified_update(void) {
efdb0237 2471
efdb0237
LP
2472 struct statfs fs;
2473
2474 /* Checks if we support the unified hierarchy. Returns an
2475 * error when the cgroup hierarchies aren't mounted yet or we
2476 * have any other trouble determining if the unified hierarchy
2477 * is supported. */
2478
5da38d07
TH
2479 if (unified_cache >= CGROUP_UNIFIED_NONE)
2480 return 0;
efdb0237
LP
2481
2482 if (statfs("/sys/fs/cgroup/", &fs) < 0)
c028bed1 2483 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\") failed: %m");
efdb0237 2484
9aa21133
ZJS
2485 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2486 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
5da38d07 2487 unified_cache = CGROUP_UNIFIED_ALL;
9aa21133 2488 } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2977724b 2489 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
f08e9287 2490 F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
9aa21133 2491 log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2977724b 2492 unified_cache = CGROUP_UNIFIED_SYSTEMD;
f08e9287 2493 unified_systemd_v232 = false;
f08e9287 2494 } else {
2977724b 2495 if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
9aa21133 2496 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
5535d8f7
EV
2497
2498 if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2499 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2500 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2501 unified_systemd_v232 = true;
2502 } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2503 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2504 unified_cache = CGROUP_UNIFIED_NONE;
2505 } else {
2506 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
9aa21133 2507 (unsigned long long) fs.f_type);
5535d8f7 2508 unified_cache = CGROUP_UNIFIED_NONE;
9aa21133 2509 }
2977724b 2510 }
651d47d1
ZJS
2511 } else {
2512 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2513 (unsigned long long) fs.f_type);
8b3aa503 2514 return -ENOMEDIUM;
651d47d1 2515 }
efdb0237 2516
5da38d07
TH
2517 return 0;
2518}
2519
c22800e4 2520int cg_unified_controller(const char *controller) {
b4cccbc1 2521 int r;
5da38d07 2522
1fcca10e 2523 r = cg_unified_update();
b4cccbc1
LP
2524 if (r < 0)
2525 return r;
5da38d07 2526
fc9ae717
LP
2527 if (unified_cache == CGROUP_UNIFIED_NONE)
2528 return false;
2529
2530 if (unified_cache >= CGROUP_UNIFIED_ALL)
2531 return true;
2532
2533 return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
5da38d07
TH
2534}
2535
b4cccbc1 2536int cg_all_unified(void) {
4bb652ac
LP
2537 int r;
2538
2539 r = cg_unified_update();
2540 if (r < 0)
2541 return r;
2542
2543 return unified_cache >= CGROUP_UNIFIED_ALL;
efdb0237
LP
2544}
2545
b4cccbc1
LP
2546int cg_hybrid_unified(void) {
2547 int r;
2977724b 2548
1fcca10e 2549 r = cg_unified_update();
b4cccbc1
LP
2550 if (r < 0)
2551 return r;
2977724b 2552
f08e9287 2553 return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2977724b
TH
2554}
2555
415fc41c 2556int cg_unified_flush(void) {
5da38d07 2557 unified_cache = CGROUP_UNIFIED_UNKNOWN;
415fc41c 2558
1fcca10e 2559 return cg_unified_update();
efdb0237
LP
2560}
2561
2562int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
77fa610b 2563 _cleanup_fclose_ FILE *f = NULL;
efdb0237
LP
2564 _cleanup_free_ char *fs = NULL;
2565 CGroupController c;
415fc41c 2566 int r;
efdb0237
LP
2567
2568 assert(p);
2569
2570 if (supported == 0)
2571 return 0;
2572
b4cccbc1
LP
2573 r = cg_all_unified();
2574 if (r < 0)
2575 return r;
2576 if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
efdb0237
LP
2577 return 0;
2578
2579 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2580 if (r < 0)
2581 return r;
2582
2583 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2584 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2585 const char *n;
2586
2587 if (!(supported & bit))
2588 continue;
2589
2590 n = cgroup_controller_to_string(c);
2591 {
2592 char s[1 + strlen(n) + 1];
2593
2594 s[0] = mask & bit ? '+' : '-';
2595 strcpy(s + 1, n);
2596
77fa610b
LP
2597 if (!f) {
2598 f = fopen(fs, "we");
2599 if (!f) {
2600 log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2601 break;
2602 }
2603 }
2604
2605 r = write_string_stream(f, s, 0);
efdb0237 2606 if (r < 0)
98e4d8d7 2607 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
efdb0237
LP
2608 }
2609 }
2610
2611 return 0;
2612}
2613
2614bool cg_is_unified_wanted(void) {
2615 static thread_local int wanted = -1;
415fc41c 2616 int r;
1d84ad94 2617 bool b;
77fab2a9 2618 const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
efdb0237 2619
77fab2a9 2620 /* If we have a cached value, return that. */
efdb0237
LP
2621 if (wanted >= 0)
2622 return wanted;
2623
239a3d09
ZJS
2624 /* If the hierarchy is already mounted, then follow whatever
2625 * was chosen for it. */
2626 if (cg_unified_flush() >= 0)
b4cccbc1 2627 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
239a3d09 2628
77fab2a9
ZJS
2629 /* Otherwise, let's see what the kernel command line has to say.
2630 * Since checking is expensive, cache a non-error result. */
1d84ad94 2631 r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
efdb0237 2632
77fab2a9 2633 return (wanted = r > 0 ? b : is_default);
efdb0237
LP
2634}
2635
2636bool cg_is_legacy_wanted(void) {
239a3d09
ZJS
2637 static thread_local int wanted = -1;
2638
2639 /* If we have a cached value, return that. */
2640 if (wanted >= 0)
2641 return wanted;
2642
1b59cf04
ZJS
2643 /* Check if we have cgroups2 already mounted. */
2644 if (cg_unified_flush() >= 0 &&
2645 unified_cache == CGROUP_UNIFIED_ALL)
239a3d09 2646 return (wanted = false);
1b59cf04
ZJS
2647
2648 /* Otherwise, assume that at least partial legacy is wanted,
2649 * since cgroups2 should already be mounted at this point. */
239a3d09 2650 return (wanted = true);
efdb0237
LP
2651}
2652
a4464b95 2653bool cg_is_hybrid_wanted(void) {
5da38d07 2654 static thread_local int wanted = -1;
415fc41c 2655 int r;
1d84ad94 2656 bool b;
c19739db
ZJS
2657 const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2658 /* We default to true if the default is "hybrid", obviously,
2659 * but also when the default is "unified", because if we get
2660 * called, it means that unified hierarchy was not mounted. */
5da38d07 2661
77fab2a9 2662 /* If we have a cached value, return that. */
5da38d07
TH
2663 if (wanted >= 0)
2664 return wanted;
2665
239a3d09
ZJS
2666 /* If the hierarchy is already mounted, then follow whatever
2667 * was chosen for it. */
2668 if (cg_unified_flush() >= 0 &&
2669 unified_cache == CGROUP_UNIFIED_ALL)
2670 return (wanted = false);
2671
77fab2a9
ZJS
2672 /* Otherwise, let's see what the kernel command line has to say.
2673 * Since checking is expensive, cache a non-error result. */
1d84ad94 2674 r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
5da38d07 2675
2dcb526d
ZJS
2676 /* The meaning of the kernel option is reversed wrt. to the return value
2677 * of this function, hence the negation. */
77fab2a9 2678 return (wanted = r > 0 ? !b : is_default);
5da38d07
TH
2679}
2680
13c31542
TH
2681int cg_weight_parse(const char *s, uint64_t *ret) {
2682 uint64_t u;
2683 int r;
2684
2685 if (isempty(s)) {
2686 *ret = CGROUP_WEIGHT_INVALID;
2687 return 0;
2688 }
2689
2690 r = safe_atou64(s, &u);
2691 if (r < 0)
2692 return r;
2693
2694 if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2695 return -ERANGE;
2696
2697 *ret = u;
2698 return 0;
2699}
2700
9be57249
TH
2701const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2702 [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX,
2703 [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX,
ac06a0cf
TH
2704 [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX,
2705 [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX,
9be57249
TH
2706};
2707
2708static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2709 [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax",
2710 [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax",
ac06a0cf
TH
2711 [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax",
2712 [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax",
9be57249
TH
2713};
2714
2715DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2716
d53d9474
LP
2717int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2718 uint64_t u;
2719 int r;
2720
2721 if (isempty(s)) {
2722 *ret = CGROUP_CPU_SHARES_INVALID;
2723 return 0;
2724 }
2725
2726 r = safe_atou64(s, &u);
2727 if (r < 0)
2728 return r;
2729
2730 if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2731 return -ERANGE;
2732
2733 *ret = u;
2734 return 0;
2735}
2736
2737int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2738 uint64_t u;
2739 int r;
2740
2741 if (isempty(s)) {
2742 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2743 return 0;
2744 }
2745
2746 r = safe_atou64(s, &u);
2747 if (r < 0)
2748 return r;
2749
2750 if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2751 return -ERANGE;
2752
2753 *ret = u;
2754 return 0;
2755}
2756
f0bef277
EV
2757bool is_cgroup_fs(const struct statfs *s) {
2758 return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2759 is_fs_type(s, CGROUP2_SUPER_MAGIC);
2760}
2761
2762bool fd_is_cgroup_fs(int fd) {
2763 struct statfs s;
2764
2765 if (fstatfs(fd, &s) < 0)
2766 return -errno;
2767
2768 return is_cgroup_fs(&s);
2769}
2770
efdb0237
LP
2771static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2772 [CGROUP_CONTROLLER_CPU] = "cpu",
2773 [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
13c31542 2774 [CGROUP_CONTROLLER_IO] = "io",
efdb0237
LP
2775 [CGROUP_CONTROLLER_BLKIO] = "blkio",
2776 [CGROUP_CONTROLLER_MEMORY] = "memory",
3905f127 2777 [CGROUP_CONTROLLER_DEVICES] = "devices",
03a7b521 2778 [CGROUP_CONTROLLER_PIDS] = "pids",
efdb0237
LP
2779};
2780
2781DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);