]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/cgroup-util.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / basic / cgroup-util.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8c6db833
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8c6db833
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
8c6db833 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
84ac7bea 22#include <dirent.h>
8c6db833 23#include <errno.h>
84ac7bea 24#include <ftw.h>
8c6db833 25#include <signal.h>
8c6db833 26#include <stdlib.h>
84ac7bea 27#include <string.h>
672c48cc
LP
28#include <sys/stat.h>
29#include <sys/types.h>
84ac7bea 30#include <unistd.h>
8c6db833 31
84ac7bea
LP
32#include "extract-word.h"
33#include "fileio.h"
6482f626 34#include "formats-util.h"
84ac7bea
LP
35#include "login-util.h"
36#include "macro.h"
37#include "mkdir.h"
9eb977db 38#include "path-util.h"
84ac7bea
LP
39#include "process-util.h"
40#include "set.h"
9444b1f2 41#include "special.h"
07630cea 42#include "string-util.h"
84ac7bea
LP
43#include "unit-name.h"
44#include "util.h"
e155a0aa 45#include "cgroup-util.h"
8c6db833 46
c6c18be3 47int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
7027ff61 48 _cleanup_free_ char *fs = NULL;
c6c18be3 49 FILE *f;
7027ff61 50 int r;
c6c18be3 51
c6c18be3
LP
52 assert(_f);
53
c3175a7f
LP
54 r = cg_get_path(controller, path, "cgroup.procs", &fs);
55 if (r < 0)
c6c18be3
LP
56 return r;
57
58 f = fopen(fs, "re");
c6c18be3
LP
59 if (!f)
60 return -errno;
61
62 *_f = f;
63 return 0;
64}
65
c6c18be3
LP
66int cg_read_pid(FILE *f, pid_t *_pid) {
67 unsigned long ul;
68
69 /* Note that the cgroup.procs might contain duplicates! See
70 * cgroups.txt for details. */
71
7027ff61
LP
72 assert(f);
73 assert(_pid);
74
c6c18be3
LP
75 errno = 0;
76 if (fscanf(f, "%lu", &ul) != 1) {
77
78 if (feof(f))
79 return 0;
80
81 return errno ? -errno : -EIO;
82 }
83
84 if (ul <= 0)
85 return -EIO;
86
87 *_pid = (pid_t) ul;
88 return 1;
89}
90
35d2e7ec 91int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
7027ff61 92 _cleanup_free_ char *fs = NULL;
35d2e7ec
LP
93 int r;
94 DIR *d;
95
35d2e7ec
LP
96 assert(_d);
97
98 /* This is not recursive! */
99
c3175a7f
LP
100 r = cg_get_path(controller, path, NULL, &fs);
101 if (r < 0)
35d2e7ec
LP
102 return r;
103
104 d = opendir(fs);
35d2e7ec
LP
105 if (!d)
106 return -errno;
107
108 *_d = d;
109 return 0;
110}
111
112int cg_read_subgroup(DIR *d, char **fn) {
113 struct dirent *de;
114
115 assert(d);
7027ff61 116 assert(fn);
35d2e7ec 117
f01327ad 118 FOREACH_DIRENT_ALL(de, d, return -errno) {
35d2e7ec
LP
119 char *b;
120
121 if (de->d_type != DT_DIR)
122 continue;
123
124 if (streq(de->d_name, ".") ||
125 streq(de->d_name, ".."))
126 continue;
127
7027ff61
LP
128 b = strdup(de->d_name);
129 if (!b)
35d2e7ec
LP
130 return -ENOMEM;
131
132 *fn = b;
133 return 1;
134 }
135
35d2e7ec
LP
136 return 0;
137}
138
4ad49000 139int cg_rmdir(const char *controller, const char *path) {
7027ff61 140 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
141 int r;
142
ad293f5a
LP
143 r = cg_get_path(controller, path, NULL, &p);
144 if (r < 0)
35d2e7ec
LP
145 return r;
146
147 r = rmdir(p);
7027ff61
LP
148 if (r < 0 && errno != ENOENT)
149 return -errno;
35d2e7ec 150
7027ff61 151 return 0;
35d2e7ec
LP
152}
153
430c18ed 154int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
7027ff61 155 _cleanup_set_free_ Set *allocated_set = NULL;
35d2e7ec 156 bool done = false;
8c6db833 157 int r, ret = 0;
35d2e7ec 158 pid_t my_pid;
8c6db833 159
8c6db833
LP
160 assert(sig >= 0);
161
162 /* This goes through the tasks list and kills them all. This
163 * is repeated until no further processes are added to the
164 * tasks list, to properly handle forking processes */
165
7027ff61 166 if (!s) {
d5099efc 167 s = allocated_set = set_new(NULL);
7027ff61 168 if (!s)
ca949c9d 169 return -ENOMEM;
7027ff61 170 }
8c6db833
LP
171
172 my_pid = getpid();
173
174 do {
7027ff61 175 _cleanup_fclose_ FILE *f = NULL;
0b172489 176 pid_t pid = 0;
8c6db833
LP
177 done = true;
178
7027ff61
LP
179 r = cg_enumerate_processes(controller, path, &f);
180 if (r < 0) {
4c633005 181 if (ret >= 0 && r != -ENOENT)
7027ff61 182 return r;
35d2e7ec 183
7027ff61 184 return ret;
35d2e7ec 185 }
c6c18be3
LP
186
187 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 188
7027ff61 189 if (ignore_self && pid == my_pid)
c6c18be3 190 continue;
8c6db833 191
fea72cc0 192 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
c6c18be3 193 continue;
8c6db833
LP
194
195 /* If we haven't killed this process yet, kill
196 * it */
4c633005
LP
197 if (kill(pid, sig) < 0) {
198 if (ret >= 0 && errno != ESRCH)
8c6db833 199 ret = -errno;
6e8314c4 200 } else {
dc8962da 201 if (sigcont && sig != SIGKILL)
e155a0aa 202 (void) kill(pid, SIGCONT);
430c18ed 203
6e8314c4
LP
204 if (ret == 0)
205 ret = 1;
430c18ed 206 }
8c6db833 207
8c6db833
LP
208 done = false;
209
fea72cc0 210 r = set_put(s, PID_TO_PTR(pid));
7027ff61 211 if (r < 0) {
35d2e7ec 212 if (ret >= 0)
7027ff61 213 return r;
35d2e7ec 214
7027ff61 215 return ret;
35d2e7ec
LP
216 }
217 }
218
219 if (r < 0) {
220 if (ret >= 0)
7027ff61 221 return r;
35d2e7ec 222
7027ff61 223 return ret;
8c6db833
LP
224 }
225
8c6db833
LP
226 /* To avoid racing against processes which fork
227 * quicker than we can kill them we repeat this until
228 * no new pids need to be killed. */
229
35d2e7ec 230 } while (!done);
8c6db833 231
35d2e7ec 232 return ret;
8c6db833
LP
233}
234
430c18ed 235int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
7027ff61
LP
236 _cleanup_set_free_ Set *allocated_set = NULL;
237 _cleanup_closedir_ DIR *d = NULL;
e155a0aa 238 int r, ret;
35d2e7ec 239 char *fn;
8c6db833
LP
240
241 assert(path);
8c6db833
LP
242 assert(sig >= 0);
243
7027ff61 244 if (!s) {
d5099efc 245 s = allocated_set = set_new(NULL);
7027ff61 246 if (!s)
ca949c9d 247 return -ENOMEM;
7027ff61 248 }
ca949c9d 249
430c18ed 250 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
8c6db833 251
7027ff61
LP
252 r = cg_enumerate_subgroups(controller, path, &d);
253 if (r < 0) {
4c633005 254 if (ret >= 0 && r != -ENOENT)
7027ff61 255 return r;
8c6db833 256
7027ff61 257 return ret;
35d2e7ec 258 }
8c6db833 259
35d2e7ec 260 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 261 _cleanup_free_ char *p = NULL;
8c6db833 262
7027ff61 263 p = strjoin(path, "/", fn, NULL);
35d2e7ec 264 free(fn);
7027ff61
LP
265 if (!p)
266 return -ENOMEM;
8c6db833 267
430c18ed 268 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
e155a0aa 269 if (r != 0 && ret >= 0)
35d2e7ec 270 ret = r;
8c6db833
LP
271 }
272
7027ff61 273 if (ret >= 0 && r < 0)
35d2e7ec
LP
274 ret = r;
275
7027ff61 276 if (rem) {
4ad49000 277 r = cg_rmdir(controller, path);
7027ff61
LP
278 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
279 return r;
280 }
ca949c9d 281
8c6db833
LP
282 return ret;
283}
284
246aa6dd 285int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
35d2e7ec 286 bool done = false;
246aa6dd 287 _cleanup_set_free_ Set *s = NULL;
8c6db833
LP
288 int r, ret = 0;
289 pid_t my_pid;
290
246aa6dd
LP
291 assert(cfrom);
292 assert(pfrom);
293 assert(cto);
294 assert(pto);
8c6db833 295
d5099efc 296 s = set_new(NULL);
246aa6dd 297 if (!s)
35d2e7ec
LP
298 return -ENOMEM;
299
8c6db833
LP
300 my_pid = getpid();
301
302 do {
7027ff61 303 _cleanup_fclose_ FILE *f = NULL;
0b172489 304 pid_t pid = 0;
8c6db833
LP
305 done = true;
306
b043cd0b 307 r = cg_enumerate_processes(cfrom, pfrom, &f);
246aa6dd 308 if (r < 0) {
4c633005 309 if (ret >= 0 && r != -ENOENT)
7027ff61 310 return r;
35d2e7ec 311
246aa6dd 312 return ret;
35d2e7ec 313 }
c6c18be3
LP
314
315 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 316
35d2e7ec
LP
317 /* This might do weird stuff if we aren't a
318 * single-threaded program. However, we
319 * luckily know we are not */
7027ff61 320 if (ignore_self && pid == my_pid)
c6c18be3 321 continue;
8c6db833 322
fea72cc0 323 if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
35d2e7ec
LP
324 continue;
325
9b84c7f9
LP
326 /* Ignore kernel threads. Since they can only
327 * exist in the root cgroup, we only check for
328 * them there. */
329 if (cfrom &&
330 (isempty(pfrom) || path_equal(pfrom, "/")) &&
331 is_kernel_thread(pid) > 0)
332 continue;
333
246aa6dd
LP
334 r = cg_attach(cto, pto, pid);
335 if (r < 0) {
4c633005 336 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
337 ret = r;
338 } else if (ret == 0)
339 ret = 1;
8c6db833 340
8c6db833 341 done = false;
35d2e7ec 342
fea72cc0 343 r = set_put(s, PID_TO_PTR(pid));
246aa6dd 344 if (r < 0) {
35d2e7ec 345 if (ret >= 0)
7027ff61 346 return r;
35d2e7ec 347
246aa6dd 348 return ret;
35d2e7ec
LP
349 }
350 }
351
352 if (r < 0) {
353 if (ret >= 0)
7027ff61 354 return r;
35d2e7ec 355
246aa6dd 356 return ret;
8c6db833 357 }
35d2e7ec 358 } while (!done);
8c6db833 359
35d2e7ec 360 return ret;
8c6db833
LP
361}
362
4ad49000
LP
363int cg_migrate_recursive(
364 const char *cfrom,
365 const char *pfrom,
366 const char *cto,
367 const char *pto,
368 bool ignore_self,
369 bool rem) {
370
246aa6dd 371 _cleanup_closedir_ DIR *d = NULL;
7027ff61 372 int r, ret = 0;
35d2e7ec 373 char *fn;
8c6db833 374
246aa6dd
LP
375 assert(cfrom);
376 assert(pfrom);
377 assert(cto);
378 assert(pto);
8c6db833 379
246aa6dd 380 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
8c6db833 381
246aa6dd
LP
382 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
383 if (r < 0) {
4c633005 384 if (ret >= 0 && r != -ENOENT)
7027ff61
LP
385 return r;
386
246aa6dd 387 return ret;
35d2e7ec
LP
388 }
389
390 while ((r = cg_read_subgroup(d, &fn)) > 0) {
246aa6dd 391 _cleanup_free_ char *p = NULL;
8c6db833 392
246aa6dd 393 p = strjoin(pfrom, "/", fn, NULL);
35d2e7ec 394 free(fn);
e155a0aa
LP
395 if (!p)
396 return -ENOMEM;
8c6db833 397
246aa6dd 398 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
35d2e7ec
LP
399 if (r != 0 && ret >= 0)
400 ret = r;
8c6db833
LP
401 }
402
35d2e7ec
LP
403 if (r < 0 && ret >= 0)
404 ret = r;
405
246aa6dd 406 if (rem) {
4ad49000 407 r = cg_rmdir(cfrom, pfrom);
246aa6dd
LP
408 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
409 return r;
410 }
8c6db833
LP
411
412 return ret;
413}
414
13b84ec7
LP
415int cg_migrate_recursive_fallback(
416 const char *cfrom,
417 const char *pfrom,
418 const char *cto,
419 const char *pto,
420 bool ignore_self,
421 bool rem) {
422
423 int r;
424
425 assert(cfrom);
426 assert(pfrom);
427 assert(cto);
428 assert(pto);
429
430 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
431 if (r < 0) {
432 char prefix[strlen(pto) + 1];
433
434 /* This didn't work? Then let's try all prefixes of the destination */
435
fecffe5d 436 PATH_FOREACH_PREFIX(prefix, pto) {
e155a0aa
LP
437 int q;
438
439 q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
440 if (q >= 0)
441 return q;
13b84ec7
LP
442 }
443 }
444
e155a0aa 445 return r;
13b84ec7
LP
446}
447
efdb0237
LP
448static const char *controller_to_dirname(const char *controller) {
449 const char *e;
3474ae3c 450
7027ff61
LP
451 assert(controller);
452
efdb0237
LP
453 /* Converts a controller name to the directory name below
454 * /sys/fs/cgroup/ we want to mount it to. Effectively, this
455 * just cuts off the name= prefixed used for named
456 * hierarchies, if it is specified. */
457
458 e = startswith(controller, "name=");
459 if (e)
460 return e;
461
462 return controller;
3474ae3c
LP
463}
464
569b19d8
LP
465static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
466 const char *dn;
018ef268 467 char *t = NULL;
3474ae3c 468
efdb0237 469 assert(fs);
569b19d8
LP
470 assert(controller);
471
472 dn = controller_to_dirname(controller);
efdb0237
LP
473
474 if (isempty(path) && isempty(suffix))
569b19d8 475 t = strappend("/sys/fs/cgroup/", dn);
efdb0237 476 else if (isempty(path))
569b19d8 477 t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL);
efdb0237 478 else if (isempty(suffix))
569b19d8 479 t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL);
efdb0237 480 else
569b19d8 481 t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL);
efdb0237
LP
482 if (!t)
483 return -ENOMEM;
3474ae3c 484
efdb0237
LP
485 *fs = t;
486 return 0;
487}
488
489static int join_path_unified(const char *path, const char *suffix, char **fs) {
490 char *t;
491
492 assert(fs);
493
494 if (isempty(path) && isempty(suffix))
495 t = strdup("/sys/fs/cgroup");
496 else if (isempty(path))
497 t = strappend("/sys/fs/cgroup/", suffix);
498 else if (isempty(suffix))
499 t = strappend("/sys/fs/cgroup/", path);
500 else
501 t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL);
3474ae3c
LP
502 if (!t)
503 return -ENOMEM;
504
efdb0237 505 *fs = t;
3474ae3c
LP
506 return 0;
507}
508
8c6db833 509int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
efdb0237 510 int unified, r;
8c6db833 511
dbd821ac
LP
512 assert(fs);
513
efdb0237
LP
514 if (!controller) {
515 char *t;
516
569b19d8
LP
517 /* If no controller is specified, we return the path
518 * *below* the controllers, without any prefix. */
efdb0237
LP
519
520 if (!path && !suffix)
521 return -EINVAL;
522
989189ea 523 if (!suffix)
efdb0237 524 t = strdup(path);
989189ea 525 else if (!path)
efdb0237
LP
526 t = strdup(suffix);
527 else
528 t = strjoin(path, "/", suffix, NULL);
529 if (!t)
530 return -ENOMEM;
531
532 *fs = path_kill_slashes(t);
533 return 0;
534 }
535
536 if (!cg_controller_is_valid(controller))
78edb35a
LP
537 return -EINVAL;
538
efdb0237
LP
539 unified = cg_unified();
540 if (unified < 0)
541 return unified;
70132bd0 542
efdb0237
LP
543 if (unified > 0)
544 r = join_path_unified(path, suffix, fs);
569b19d8
LP
545 else
546 r = join_path_legacy(controller, path, suffix, fs);
efdb0237
LP
547 if (r < 0)
548 return r;
7027ff61 549
efdb0237
LP
550 path_kill_slashes(*fs);
551 return 0;
3474ae3c 552}
dbd821ac 553
efdb0237
LP
554static int controller_is_accessible(const char *controller) {
555 int unified;
37099707 556
efdb0237 557 assert(controller);
37099707 558
efdb0237
LP
559 /* Checks whether a specific controller is accessible,
560 * i.e. its hierarchy mounted. In the unified hierarchy all
561 * controllers are considered accessible, except for the named
562 * hierarchies */
b12afc8c 563
efdb0237
LP
564 if (!cg_controller_is_valid(controller))
565 return -EINVAL;
566
567 unified = cg_unified();
568 if (unified < 0)
569 return unified;
570 if (unified > 0) {
571 /* We don't support named hierarchies if we are using
572 * the unified hierarchy. */
573
574 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
575 return 0;
576
577 if (startswith(controller, "name="))
578 return -EOPNOTSUPP;
579
580 } else {
581 const char *cc, *dn;
582
583 dn = controller_to_dirname(controller);
584 cc = strjoina("/sys/fs/cgroup/", dn);
585
586 if (laccess(cc, F_OK) < 0)
587 return -errno;
588 }
37099707
LP
589
590 return 0;
591}
592
3474ae3c 593int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
37099707 594 int r;
dbd821ac 595
efdb0237 596 assert(controller);
3474ae3c 597 assert(fs);
70132bd0 598
efdb0237
LP
599 /* Check if the specified controller is actually accessible */
600 r = controller_is_accessible(controller);
37099707
LP
601 if (r < 0)
602 return r;
3474ae3c 603
efdb0237 604 return cg_get_path(controller, path, suffix, fs);
8c6db833
LP
605}
606
e27796a0 607static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
608 assert(path);
609 assert(sb);
610 assert(ftwbuf);
e27796a0
LP
611
612 if (typeflag != FTW_DP)
613 return 0;
614
615 if (ftwbuf->level < 1)
616 return 0;
617
e155a0aa 618 (void) rmdir(path);
e27796a0
LP
619 return 0;
620}
621
8c6db833 622int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 623 _cleanup_free_ char *fs = NULL;
e27796a0 624 int r = 0;
8c6db833 625
8c6db833
LP
626 assert(path);
627
e27796a0
LP
628 r = cg_get_path(controller, path, NULL, &fs);
629 if (r < 0)
8c6db833
LP
630 return r;
631
e27796a0 632 errno = 0;
e155a0aa
LP
633 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
634 if (errno == ENOENT)
635 r = 0;
636 else if (errno != 0)
637 r = -errno;
638 else
639 r = -EIO;
640 }
e27796a0
LP
641
642 if (delete_root) {
4ad49000
LP
643 if (rmdir(fs) < 0 && errno != ENOENT)
644 return -errno;
e27796a0
LP
645 }
646
e27796a0 647 return r;
8c6db833
LP
648}
649
1434ae6f
LP
650int cg_create(const char *controller, const char *path) {
651 _cleanup_free_ char *fs = NULL;
652 int r;
653
654 r = cg_get_path_and_check(controller, path, NULL, &fs);
655 if (r < 0)
656 return r;
657
658 r = mkdir_parents(fs, 0755);
659 if (r < 0)
660 return r;
661
662 if (mkdir(fs, 0755) < 0) {
663
664 if (errno == EEXIST)
665 return 0;
666
667 return -errno;
668 }
669
670 return 1;
671}
672
673int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
674 int r, q;
675
676 assert(pid >= 0);
677
678 r = cg_create(controller, path);
679 if (r < 0)
680 return r;
681
682 q = cg_attach(controller, path, pid);
683 if (q < 0)
684 return q;
685
686 /* This does not remove the cgroup on failure */
687 return r;
688}
689
8c6db833 690int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
691 _cleanup_free_ char *fs = NULL;
692 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
693 int r;
694
8c6db833
LP
695 assert(path);
696 assert(pid >= 0);
697
b043cd0b 698 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 699 if (r < 0)
c6c18be3 700 return r;
8c6db833
LP
701
702 if (pid == 0)
703 pid = getpid();
704
de0671ee 705 snprintf(c, sizeof(c), PID_FMT"\n", pid);
8c6db833 706
4c1fc3e4 707 return write_string_file(fs, c, 0);
8c6db833
LP
708}
709
13b84ec7
LP
710int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
711 int r;
712
713 assert(controller);
714 assert(path);
715 assert(pid >= 0);
716
717 r = cg_attach(controller, path, pid);
718 if (r < 0) {
719 char prefix[strlen(path) + 1];
720
721 /* This didn't work? Then let's try all prefixes of
722 * the destination */
723
fecffe5d 724 PATH_FOREACH_PREFIX(prefix, path) {
e155a0aa
LP
725 int q;
726
727 q = cg_attach(controller, prefix, pid);
728 if (q >= 0)
729 return q;
13b84ec7
LP
730 }
731 }
732
e155a0aa 733 return r;
13b84ec7
LP
734}
735
2d76d14e
LP
736int cg_set_group_access(
737 const char *controller,
738 const char *path,
739 mode_t mode,
740 uid_t uid,
741 gid_t gid) {
742
574d5f2d 743 _cleanup_free_ char *fs = NULL;
8c6db833
LP
744 int r;
745
e155a0aa
LP
746 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
747 return 0;
8c6db833 748
fed1e721 749 if (mode != MODE_INVALID)
8d53b453
LP
750 mode &= 0777;
751
752 r = cg_get_path(controller, path, NULL, &fs);
753 if (r < 0)
8c6db833
LP
754 return r;
755
574d5f2d 756 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
757}
758
974efc46
LP
759int cg_set_task_access(
760 const char *controller,
761 const char *path,
762 mode_t mode,
763 uid_t uid,
4ad49000 764 gid_t gid) {
974efc46
LP
765
766 _cleanup_free_ char *fs = NULL, *procs = NULL;
efdb0237 767 int r, unified;
8c6db833 768
8c6db833
LP
769 assert(path);
770
fed1e721 771 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
8d53b453
LP
772 return 0;
773
fed1e721 774 if (mode != MODE_INVALID)
8d53b453
LP
775 mode &= 0666;
776
b043cd0b 777 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 778 if (r < 0)
8c6db833
LP
779 return r;
780
781 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
782 if (r < 0)
783 return r;
8c6db833 784
efdb0237
LP
785 unified = cg_unified();
786 if (unified < 0)
787 return unified;
788 if (unified)
789 return 0;
790
b043cd0b
LP
791 /* Compatibility, Always keep values for "tasks" in sync with
792 * "cgroup.procs" */
efdb0237
LP
793 if (cg_get_path(controller, path, "tasks", &procs) >= 0)
794 (void) chmod_and_chown(procs, mode, uid, gid);
974efc46 795
efdb0237 796 return 0;
8c6db833
LP
797}
798
7027ff61 799int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
800 _cleanup_fclose_ FILE *f = NULL;
801 char line[LINE_MAX];
8af8afd6 802 const char *fs;
efdb0237
LP
803 size_t cs = 0;
804 int unified;
8c6db833 805
8c6db833 806 assert(path);
c6c18be3 807 assert(pid >= 0);
8c6db833 808
efdb0237
LP
809 unified = cg_unified();
810 if (unified < 0)
811 return unified;
812 if (unified == 0) {
813 if (controller) {
814 if (!cg_controller_is_valid(controller))
815 return -EINVAL;
816 } else
817 controller = SYSTEMD_CGROUP_CONTROLLER;
78edb35a 818
efdb0237
LP
819 cs = strlen(controller);
820 }
7027ff61 821
b68fa010 822 fs = procfs_file_alloca(pid, "cgroup");
c6c18be3 823 f = fopen(fs, "re");
4c633005
LP
824 if (!f)
825 return errno == ENOENT ? -ESRCH : -errno;
826
7027ff61 827 FOREACH_LINE(line, f, return -errno) {
efdb0237 828 char *e, *p;
c6c18be3
LP
829
830 truncate_nl(line);
831
efdb0237
LP
832 if (unified) {
833 e = startswith(line, "0:");
834 if (!e)
835 continue;
c6c18be3 836
efdb0237
LP
837 e = strchr(e, ':');
838 if (!e)
839 continue;
840 } else {
841 char *l;
842 size_t k;
843 const char *word, *state;
844 bool found = false;
845
846 l = strchr(line, ':');
847 if (!l)
848 continue;
8af8afd6 849
efdb0237
LP
850 l++;
851 e = strchr(l, ':');
852 if (!e)
853 continue;
8af8afd6 854
efdb0237
LP
855 *e = 0;
856 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
857 if (k == cs && memcmp(word, controller, cs) == 0) {
858 found = true;
859 break;
860 }
8af8afd6
LP
861 }
862
efdb0237
LP
863 if (!found)
864 continue;
8af8afd6
LP
865 }
866
8af8afd6 867 p = strdup(e + 1);
7027ff61
LP
868 if (!p)
869 return -ENOMEM;
c6c18be3
LP
870
871 *path = p;
7027ff61 872 return 0;
c6c18be3
LP
873 }
874
1c80e425 875 return -ENODATA;
8c6db833
LP
876}
877
878int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61 879 _cleanup_free_ char *fs = NULL, *contents = NULL;
efdb0237
LP
880 const char *sc;
881 int r, unified;
8c6db833 882
8c6db833
LP
883 assert(agent);
884
efdb0237
LP
885 unified = cg_unified();
886 if (unified < 0)
887 return unified;
888 if (unified) /* doesn't apply to unified hierarchy */
889 return -EOPNOTSUPP;
890
7027ff61
LP
891 r = cg_get_path(controller, NULL, "release_agent", &fs);
892 if (r < 0)
c6c18be3 893 return r;
8c6db833 894
7027ff61
LP
895 r = read_one_line_file(fs, &contents);
896 if (r < 0)
897 return r;
8c6db833
LP
898
899 sc = strstrip(contents);
e155a0aa 900 if (isempty(sc)) {
4c1fc3e4 901 r = write_string_file(fs, agent, 0);
574d5f2d 902 if (r < 0)
7027ff61 903 return r;
b8725df8 904 } else if (!path_equal(sc, agent))
7027ff61 905 return -EEXIST;
8c6db833 906
0da16248 907 fs = mfree(fs);
7027ff61
LP
908 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
909 if (r < 0)
910 return r;
8c6db833 911
0da16248 912 contents = mfree(contents);
7027ff61
LP
913 r = read_one_line_file(fs, &contents);
914 if (r < 0)
915 return r;
8c6db833
LP
916
917 sc = strstrip(contents);
8c6db833 918 if (streq(sc, "0")) {
4c1fc3e4 919 r = write_string_file(fs, "1", 0);
7027ff61
LP
920 if (r < 0)
921 return r;
c6c18be3 922
7027ff61
LP
923 return 1;
924 }
8c6db833 925
7027ff61
LP
926 if (!streq(sc, "1"))
927 return -EIO;
8c6db833 928
7027ff61 929 return 0;
8c6db833
LP
930}
931
ad929bcc
KS
932int cg_uninstall_release_agent(const char *controller) {
933 _cleanup_free_ char *fs = NULL;
efdb0237
LP
934 int r, unified;
935
936 unified = cg_unified();
937 if (unified < 0)
938 return unified;
939 if (unified) /* Doesn't apply to unified hierarchy */
940 return -EOPNOTSUPP;
ad929bcc 941
ac9ef333
LP
942 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
943 if (r < 0)
944 return r;
945
4c1fc3e4 946 r = write_string_file(fs, "0", 0);
ac9ef333
LP
947 if (r < 0)
948 return r;
949
0da16248 950 fs = mfree(fs);
ac9ef333 951
ad929bcc
KS
952 r = cg_get_path(controller, NULL, "release_agent", &fs);
953 if (r < 0)
954 return r;
955
4c1fc3e4 956 r = write_string_file(fs, "", 0);
ad929bcc
KS
957 if (r < 0)
958 return r;
959
ac9ef333 960 return 0;
ad929bcc
KS
961}
962
6f883237 963int cg_is_empty(const char *controller, const char *path) {
7027ff61 964 _cleanup_fclose_ FILE *f = NULL;
efdb0237 965 pid_t pid;
7027ff61 966 int r;
8c6db833 967
8c6db833
LP
968 assert(path);
969
b043cd0b 970 r = cg_enumerate_processes(controller, path, &f);
6f883237
LP
971 if (r == -ENOENT)
972 return 1;
c3175a7f 973 if (r < 0)
6f883237 974 return r;
8c6db833 975
6f883237 976 r = cg_read_pid(f, &pid);
c6c18be3
LP
977 if (r < 0)
978 return r;
8c6db833 979
6f883237 980 return r == 0;
8c6db833
LP
981}
982
6f883237 983int cg_is_empty_recursive(const char *controller, const char *path) {
efdb0237 984 int unified, r;
8c6db833 985
8c6db833
LP
986 assert(path);
987
6fd66507
LP
988 /* The root cgroup is always populated */
989 if (controller && (isempty(path) || path_equal(path, "/")))
efdb0237 990 return false;
6fd66507 991
efdb0237
LP
992 unified = cg_unified();
993 if (unified < 0)
994 return unified;
35d2e7ec 995
efdb0237
LP
996 if (unified > 0) {
997 _cleanup_free_ char *populated = NULL, *t = NULL;
8c6db833 998
efdb0237
LP
999 /* On the unified hierarchy we can check empty state
1000 * via the "cgroup.populated" attribute. */
8c6db833 1001
efdb0237
LP
1002 r = cg_get_path(controller, path, "cgroup.populated", &populated);
1003 if (r < 0)
1004 return r;
1005
1006 r = read_one_line_file(populated, &t);
9a66c87a
LP
1007 if (r == -ENOENT)
1008 return 1;
efdb0237
LP
1009 if (r < 0)
1010 return r;
1011
1012 return streq(t, "0");
1013 } else {
1014 _cleanup_closedir_ DIR *d = NULL;
1015 char *fn;
8c6db833 1016
efdb0237 1017 r = cg_is_empty(controller, path);
35d2e7ec 1018 if (r <= 0)
7027ff61 1019 return r;
35d2e7ec 1020
efdb0237
LP
1021 r = cg_enumerate_subgroups(controller, path, &d);
1022 if (r == -ENOENT)
1023 return 1;
1024 if (r < 0)
1025 return r;
35d2e7ec 1026
efdb0237
LP
1027 while ((r = cg_read_subgroup(d, &fn)) > 0) {
1028 _cleanup_free_ char *p = NULL;
1029
1030 p = strjoin(path, "/", fn, NULL);
1031 free(fn);
1032 if (!p)
1033 return -ENOMEM;
1034
1035 r = cg_is_empty_recursive(controller, p);
1036 if (r <= 0)
1037 return r;
1038 }
1039 if (r < 0)
1040 return r;
1041
1042 return true;
1043 }
35d2e7ec
LP
1044}
1045
1046int cg_split_spec(const char *spec, char **controller, char **path) {
35d2e7ec 1047 char *t = NULL, *u = NULL;
efdb0237 1048 const char *e;
35d2e7ec
LP
1049
1050 assert(spec);
35d2e7ec
LP
1051
1052 if (*spec == '/') {
e884315e
LP
1053 if (!path_is_safe(spec))
1054 return -EINVAL;
35d2e7ec
LP
1055
1056 if (path) {
246aa6dd
LP
1057 t = strdup(spec);
1058 if (!t)
35d2e7ec
LP
1059 return -ENOMEM;
1060
dbb9401d 1061 *path = path_kill_slashes(t);
8c6db833
LP
1062 }
1063
35d2e7ec
LP
1064 if (controller)
1065 *controller = NULL;
1066
1067 return 0;
8c6db833
LP
1068 }
1069
246aa6dd
LP
1070 e = strchr(spec, ':');
1071 if (!e) {
185a0874 1072 if (!cg_controller_is_valid(spec))
35d2e7ec
LP
1073 return -EINVAL;
1074
1075 if (controller) {
efdb0237 1076 t = strdup(spec);
246aa6dd 1077 if (!t)
35d2e7ec
LP
1078 return -ENOMEM;
1079
1080 *controller = t;
1081 }
1082
1083 if (path)
1084 *path = NULL;
1085
1086 return 0;
8c6db833
LP
1087 }
1088
efdb0237 1089 t = strndup(spec, e-spec);
e884315e
LP
1090 if (!t)
1091 return -ENOMEM;
185a0874 1092 if (!cg_controller_is_valid(t)) {
e884315e 1093 free(t);
35d2e7ec 1094 return -EINVAL;
246aa6dd
LP
1095 }
1096
efdb0237
LP
1097 if (isempty(e+1))
1098 u = NULL;
1099 else {
baa89da4
LP
1100 u = strdup(e+1);
1101 if (!u) {
1102 free(t);
1103 return -ENOMEM;
1104 }
35d2e7ec 1105
baa89da4
LP
1106 if (!path_is_safe(u) ||
1107 !path_is_absolute(u)) {
1108 free(t);
1109 free(u);
1110 return -EINVAL;
1111 }
1112
1113 path_kill_slashes(u);
1114 }
5954c074 1115
35d2e7ec
LP
1116 if (controller)
1117 *controller = t;
e884315e
LP
1118 else
1119 free(t);
35d2e7ec
LP
1120
1121 if (path)
1122 *path = u;
e884315e
LP
1123 else
1124 free(u);
35d2e7ec
LP
1125
1126 return 0;
8c6db833 1127}
c6c18be3 1128
7027ff61 1129int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1130 _cleanup_free_ char *c = NULL, *p = NULL;
1131 char *t;
35d2e7ec
LP
1132 int r;
1133
1134 assert(path);
1135 assert(result);
1136
73e231ab 1137 /* First, check if it already is a filesystem path */
7027ff61 1138 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1139
b69d29ce
LP
1140 t = strdup(path);
1141 if (!t)
35d2e7ec
LP
1142 return -ENOMEM;
1143
dbb9401d 1144 *result = path_kill_slashes(t);
35d2e7ec
LP
1145 return 0;
1146 }
1147
73e231ab 1148 /* Otherwise, treat it as cg spec */
b69d29ce
LP
1149 r = cg_split_spec(path, &c, &p);
1150 if (r < 0)
35d2e7ec
LP
1151 return r;
1152
efdb0237 1153 return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
35d2e7ec 1154}
1f73f0f1 1155
7027ff61 1156int cg_get_root_path(char **path) {
9444b1f2 1157 char *p, *e;
7027ff61
LP
1158 int r;
1159
1160 assert(path);
1161
9444b1f2 1162 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1163 if (r < 0)
1164 return r;
1165
efdb0237
LP
1166 e = endswith(p, "/" SPECIAL_INIT_SCOPE);
1167 if (!e)
1168 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
1169 if (!e)
1170 e = endswith(p, "/system"); /* even more legacy */
9444b1f2 1171 if (e)
7027ff61
LP
1172 *e = 0;
1173
1f73f0f1
LP
1174 *path = p;
1175 return 0;
1176}
b59e2465 1177
751bc6ac
LP
1178int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1179 _cleanup_free_ char *rt = NULL;
1180 char *p;
ba1261bc
LP
1181 int r;
1182
e9174f29 1183 assert(cgroup);
751bc6ac 1184 assert(shifted);
e9174f29
LP
1185
1186 if (!root) {
1187 /* If the root was specified let's use that, otherwise
1188 * let's determine it from PID 1 */
1189
751bc6ac 1190 r = cg_get_root_path(&rt);
e9174f29
LP
1191 if (r < 0)
1192 return r;
1193
751bc6ac 1194 root = rt;
e9174f29 1195 }
ba1261bc 1196
751bc6ac 1197 p = path_startswith(cgroup, root);
efdb0237 1198 if (p && p > cgroup)
751bc6ac
LP
1199 *shifted = p - 1;
1200 else
1201 *shifted = cgroup;
1202
1203 return 0;
1204}
1205
1206int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1207 _cleanup_free_ char *raw = NULL;
1208 const char *c;
1209 int r;
1210
1211 assert(pid >= 0);
1212 assert(cgroup);
1213
1214 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
7027ff61 1215 if (r < 0)
ba1261bc 1216 return r;
ba1261bc 1217
751bc6ac
LP
1218 r = cg_shift_path(raw, root, &c);
1219 if (r < 0)
1220 return r;
ba1261bc 1221
751bc6ac
LP
1222 if (c == raw) {
1223 *cgroup = raw;
1224 raw = NULL;
1225 } else {
1226 char *n;
ba1261bc 1227
751bc6ac
LP
1228 n = strdup(c);
1229 if (!n)
ba1261bc 1230 return -ENOMEM;
ba1261bc 1231
751bc6ac
LP
1232 *cgroup = n;
1233 }
ba1261bc
LP
1234
1235 return 0;
1236}
1237
7027ff61 1238int cg_path_decode_unit(const char *cgroup, char **unit){
8b0849e9
LP
1239 char *c, *s;
1240 size_t n;
ef1673d1
MT
1241
1242 assert(cgroup);
6c03089c 1243 assert(unit);
ef1673d1 1244
8b0849e9
LP
1245 n = strcspn(cgroup, "/");
1246 if (n < 3)
1247 return -ENXIO;
1248
1249 c = strndupa(cgroup, n);
ae018d9b 1250 c = cg_unescape(c);
ef1673d1 1251
7410616c 1252 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
cfeaa44a 1253 return -ENXIO;
ef1673d1 1254
d7bd3de0 1255 s = strdup(c);
6c03089c
LP
1256 if (!s)
1257 return -ENOMEM;
1258
1259 *unit = s;
ef1673d1
MT
1260 return 0;
1261}
1262
8b0849e9
LP
1263static bool valid_slice_name(const char *p, size_t n) {
1264
1265 if (!p)
1266 return false;
1267
1268 if (n < strlen("x.slice"))
1269 return false;
1270
1271 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1272 char buf[n+1], *c;
1273
1274 memcpy(buf, p, n);
1275 buf[n] = 0;
1276
1277 c = cg_unescape(buf);
1278
7410616c 1279 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
8b0849e9
LP
1280 }
1281
1282 return false;
1283}
1284
9444b1f2 1285static const char *skip_slices(const char *p) {
8b0849e9
LP
1286 assert(p);
1287
9444b1f2
LP
1288 /* Skips over all slice assignments */
1289
1290 for (;;) {
1021b21b
LP
1291 size_t n;
1292
9444b1f2
LP
1293 p += strspn(p, "/");
1294
1295 n = strcspn(p, "/");
8b0849e9 1296 if (!valid_slice_name(p, n))
9444b1f2
LP
1297 return p;
1298
1299 p += n;
1300 }
1301}
1302
8b0849e9 1303int cg_path_get_unit(const char *path, char **ret) {
6c03089c 1304 const char *e;
8b0849e9
LP
1305 char *unit;
1306 int r;
6c03089c
LP
1307
1308 assert(path);
8b0849e9 1309 assert(ret);
6c03089c 1310
9444b1f2 1311 e = skip_slices(path);
6c03089c 1312
8b0849e9
LP
1313 r = cg_path_decode_unit(e, &unit);
1314 if (r < 0)
1315 return r;
1316
1317 /* We skipped over the slices, don't accept any now */
1318 if (endswith(unit, ".slice")) {
1319 free(unit);
1320 return -ENXIO;
1321 }
1322
1323 *ret = unit;
1324 return 0;
6c03089c
LP
1325}
1326
1327int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1328 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1329 int r;
ba1261bc 1330
ef1673d1
MT
1331 assert(unit);
1332
7027ff61 1333 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1334 if (r < 0)
1335 return r;
1336
6c03089c
LP
1337 return cg_path_get_unit(cgroup, unit);
1338}
ef1673d1 1339
d4fffc4b
ZJS
1340/**
1341 * Skip session-*.scope, but require it to be there.
1342 */
9444b1f2
LP
1343static const char *skip_session(const char *p) {
1344 size_t n;
1345
8b0849e9
LP
1346 if (isempty(p))
1347 return NULL;
9444b1f2
LP
1348
1349 p += strspn(p, "/");
1350
1351 n = strcspn(p, "/");
8b0849e9 1352 if (n < strlen("session-x.scope"))
d4fffc4b
ZJS
1353 return NULL;
1354
8b0849e9
LP
1355 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1356 char buf[n - 8 - 6 + 1];
1357
1358 memcpy(buf, p + 8, n - 8 - 6);
1359 buf[n - 8 - 6] = 0;
d4fffc4b 1360
8b0849e9
LP
1361 /* Note that session scopes never need unescaping,
1362 * since they cannot conflict with the kernel's own
1363 * names, hence we don't need to call cg_unescape()
1364 * here. */
1365
1366 if (!session_id_valid(buf))
1367 return false;
1368
1369 p += n;
1370 p += strspn(p, "/");
1371 return p;
1372 }
1373
1374 return NULL;
d4fffc4b
ZJS
1375}
1376
1377/**
1378 * Skip user@*.service, but require it to be there.
1379 */
1380static const char *skip_user_manager(const char *p) {
1381 size_t n;
1382
8b0849e9
LP
1383 if (isempty(p))
1384 return NULL;
d4fffc4b
ZJS
1385
1386 p += strspn(p, "/");
1387
1388 n = strcspn(p, "/");
8b0849e9 1389 if (n < strlen("user@x.service"))
6c03089c 1390 return NULL;
ef1673d1 1391
8b0849e9
LP
1392 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1393 char buf[n - 5 - 8 + 1];
9444b1f2 1394
8b0849e9
LP
1395 memcpy(buf, p + 5, n - 5 - 8);
1396 buf[n - 5 - 8] = 0;
1397
1398 /* Note that user manager services never need unescaping,
1399 * since they cannot conflict with the kernel's own
1400 * names, hence we don't need to call cg_unescape()
1401 * here. */
1402
1403 if (parse_uid(buf, NULL) < 0)
1404 return NULL;
1405
1406 p += n;
1407 p += strspn(p, "/");
1408
1409 return p;
1410 }
1411
1412 return NULL;
9444b1f2
LP
1413}
1414
329ac4bc 1415static const char *skip_user_prefix(const char *path) {
d4fffc4b 1416 const char *e, *t;
ef1673d1 1417
6c03089c 1418 assert(path);
ba1261bc 1419
9444b1f2
LP
1420 /* Skip slices, if there are any */
1421 e = skip_slices(path);
ba1261bc 1422
329ac4bc 1423 /* Skip the user manager, if it's in the path now... */
8b0849e9 1424 t = skip_user_manager(e);
329ac4bc
LP
1425 if (t)
1426 return t;
8b0849e9 1427
329ac4bc
LP
1428 /* Alternatively skip the user session if it is in the path... */
1429 return skip_session(e);
1430}
32081481 1431
329ac4bc
LP
1432int cg_path_get_user_unit(const char *path, char **ret) {
1433 const char *t;
6c03089c 1434
329ac4bc
LP
1435 assert(path);
1436 assert(ret);
8b0849e9 1437
329ac4bc
LP
1438 t = skip_user_prefix(path);
1439 if (!t)
8b0849e9 1440 return -ENXIO;
8b0849e9 1441
329ac4bc
LP
1442 /* And from here on it looks pretty much the same as for a
1443 * system unit, hence let's use the same parser from here
1444 * on. */
1445 return cg_path_get_unit(t, ret);
ef1673d1 1446}
ba1261bc 1447
ef1673d1 1448int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1449 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1450 int r;
1451
1452 assert(unit);
1453
7027ff61 1454 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1455 if (r < 0)
1456 return r;
1457
1458 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1459}
e884315e 1460
7027ff61 1461int cg_path_get_machine_name(const char *path, char **machine) {
efdb0237
LP
1462 _cleanup_free_ char *u = NULL;
1463 const char *sl;
89f7c846 1464 int r;
374ec6ab 1465
89f7c846
LP
1466 r = cg_path_get_unit(path, &u);
1467 if (r < 0)
1468 return r;
7027ff61 1469
efdb0237 1470 sl = strjoina("/run/systemd/machines/unit:", u);
89f7c846 1471 return readlink_malloc(sl, machine);
7027ff61
LP
1472}
1473
1474int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1475 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1476 int r;
1477
1478 assert(machine);
1479
1480 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1481 if (r < 0)
1482 return r;
1483
1484 return cg_path_get_machine_name(cgroup, machine);
1485}
1486
1487int cg_path_get_session(const char *path, char **session) {
8b0849e9
LP
1488 _cleanup_free_ char *unit = NULL;
1489 char *start, *end;
1490 int r;
7027ff61
LP
1491
1492 assert(path);
7027ff61 1493
8b0849e9
LP
1494 r = cg_path_get_unit(path, &unit);
1495 if (r < 0)
1496 return r;
7027ff61 1497
8b0849e9
LP
1498 start = startswith(unit, "session-");
1499 if (!start)
cfeaa44a 1500 return -ENXIO;
8b0849e9
LP
1501 end = endswith(start, ".scope");
1502 if (!end)
cfeaa44a 1503 return -ENXIO;
8b0849e9
LP
1504
1505 *end = 0;
1506 if (!session_id_valid(start))
cfeaa44a 1507 return -ENXIO;
374ec6ab 1508
af08d2f9 1509 if (session) {
8b0849e9 1510 char *rr;
af08d2f9 1511
8b0849e9
LP
1512 rr = strdup(start);
1513 if (!rr)
af08d2f9
LP
1514 return -ENOMEM;
1515
8b0849e9 1516 *session = rr;
af08d2f9 1517 }
7027ff61 1518
7027ff61
LP
1519 return 0;
1520}
1521
1522int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1523 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1524 int r;
1525
7027ff61
LP
1526 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1527 if (r < 0)
1528 return r;
1529
1530 return cg_path_get_session(cgroup, session);
1531}
1532
ae018d9b 1533int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1534 _cleanup_free_ char *slice = NULL;
8b0849e9 1535 char *start, *end;
374ec6ab 1536 int r;
ae018d9b
LP
1537
1538 assert(path);
ae018d9b 1539
374ec6ab
LP
1540 r = cg_path_get_slice(path, &slice);
1541 if (r < 0)
1542 return r;
ae018d9b 1543
674eb685
LP
1544 start = startswith(slice, "user-");
1545 if (!start)
cfeaa44a 1546 return -ENXIO;
8b0849e9 1547 end = endswith(start, ".slice");
674eb685 1548 if (!end)
cfeaa44a 1549 return -ENXIO;
ae018d9b 1550
8b0849e9
LP
1551 *end = 0;
1552 if (parse_uid(start, uid) < 0)
cfeaa44a 1553 return -ENXIO;
674eb685 1554
674eb685 1555 return 0;
ae018d9b
LP
1556}
1557
1558int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1559 _cleanup_free_ char *cgroup = NULL;
1560 int r;
1561
ae018d9b
LP
1562 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1563 if (r < 0)
1564 return r;
1565
1566 return cg_path_get_owner_uid(cgroup, uid);
1567}
1568
1021b21b
LP
1569int cg_path_get_slice(const char *p, char **slice) {
1570 const char *e = NULL;
1021b21b
LP
1571
1572 assert(p);
1573 assert(slice);
1574
329ac4bc
LP
1575 /* Finds the right-most slice unit from the beginning, but
1576 * stops before we come to the first non-slice unit. */
1577
1021b21b
LP
1578 for (;;) {
1579 size_t n;
1580
1581 p += strspn(p, "/");
1582
1583 n = strcspn(p, "/");
8b0849e9 1584 if (!valid_slice_name(p, n)) {
1021b21b 1585
8b0849e9
LP
1586 if (!e) {
1587 char *s;
1021b21b 1588
8b0849e9
LP
1589 s = strdup("-.slice");
1590 if (!s)
1591 return -ENOMEM;
1021b21b 1592
8b0849e9
LP
1593 *slice = s;
1594 return 0;
1595 }
1596
1597 return cg_path_decode_unit(e, slice);
1021b21b
LP
1598 }
1599
1600 e = p;
1021b21b
LP
1601 p += n;
1602 }
1603}
1604
1605int cg_pid_get_slice(pid_t pid, char **slice) {
1606 _cleanup_free_ char *cgroup = NULL;
1607 int r;
1608
1609 assert(slice);
1610
1611 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1612 if (r < 0)
1613 return r;
1614
1615 return cg_path_get_slice(cgroup, slice);
1616}
1617
329ac4bc
LP
1618int cg_path_get_user_slice(const char *p, char **slice) {
1619 const char *t;
1620 assert(p);
1621 assert(slice);
1622
1623 t = skip_user_prefix(p);
1624 if (!t)
1625 return -ENXIO;
1626
1627 /* And now it looks pretty much the same as for a system
1628 * slice, so let's just use the same parser from here on. */
1629 return cg_path_get_slice(t, slice);
1630}
1631
1632int cg_pid_get_user_slice(pid_t pid, char **slice) {
1633 _cleanup_free_ char *cgroup = NULL;
1634 int r;
1635
1636 assert(slice);
1637
1638 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1639 if (r < 0)
1640 return r;
1641
1642 return cg_path_get_user_slice(cgroup, slice);
1643}
1644
ae018d9b
LP
1645char *cg_escape(const char *p) {
1646 bool need_prefix = false;
1647
1648 /* This implements very minimal escaping for names to be used
1649 * as file names in the cgroup tree: any name which might
1650 * conflict with a kernel name or is prefixed with '_' is
1651 * prefixed with a '_'. That way, when reading cgroup names it
1652 * is sufficient to remove a single prefixing underscore if
1653 * there is one. */
1654
1655 /* The return value of this function (unlike cg_unescape())
1656 * needs free()! */
1657
a0ab5665
LP
1658 if (p[0] == 0 ||
1659 p[0] == '_' ||
1660 p[0] == '.' ||
1661 streq(p, "notify_on_release") ||
1662 streq(p, "release_agent") ||
efdb0237
LP
1663 streq(p, "tasks") ||
1664 startswith(p, "cgroup."))
ae018d9b
LP
1665 need_prefix = true;
1666 else {
1667 const char *dot;
1668
1669 dot = strrchr(p, '.');
1670 if (dot) {
efdb0237
LP
1671 CGroupController c;
1672 size_t l = dot - p;
ae018d9b 1673
efdb0237
LP
1674 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1675 const char *n;
1676
1677 n = cgroup_controller_to_string(c);
ae018d9b 1678
efdb0237
LP
1679 if (l != strlen(n))
1680 continue;
ae018d9b 1681
efdb0237
LP
1682 if (memcmp(p, n, l) != 0)
1683 continue;
1684
1685 need_prefix = true;
1686 break;
ae018d9b
LP
1687 }
1688 }
1689 }
1690
1691 if (need_prefix)
1692 return strappend("_", p);
efdb0237
LP
1693
1694 return strdup(p);
ae018d9b
LP
1695}
1696
1697char *cg_unescape(const char *p) {
1698 assert(p);
1699
1700 /* The return value of this function (unlike cg_escape())
1701 * doesn't need free()! */
1702
1703 if (p[0] == '_')
1704 return (char*) p+1;
1705
1706 return (char*) p;
1707}
78edb35a
LP
1708
1709#define CONTROLLER_VALID \
4b549144 1710 DIGITS LETTERS \
78edb35a
LP
1711 "_"
1712
185a0874 1713bool cg_controller_is_valid(const char *p) {
78edb35a
LP
1714 const char *t, *s;
1715
1716 if (!p)
1717 return false;
1718
185a0874
DJL
1719 s = startswith(p, "name=");
1720 if (s)
1721 p = s;
78edb35a
LP
1722
1723 if (*p == 0 || *p == '_')
1724 return false;
1725
1726 for (t = p; *t; t++)
1727 if (!strchr(CONTROLLER_VALID, *t))
1728 return false;
1729
1730 if (t - p > FILENAME_MAX)
1731 return false;
1732
1733 return true;
1734}
a016b922
LP
1735
1736int cg_slice_to_path(const char *unit, char **ret) {
1737 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1738 const char *dash;
7410616c 1739 int r;
a016b922
LP
1740
1741 assert(unit);
1742 assert(ret);
1743
c96cc582
LP
1744 if (streq(unit, "-.slice")) {
1745 char *x;
1746
1747 x = strdup("");
1748 if (!x)
1749 return -ENOMEM;
1750 *ret = x;
1751 return 0;
1752 }
1753
7410616c 1754 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
a016b922
LP
1755 return -EINVAL;
1756
1757 if (!endswith(unit, ".slice"))
1758 return -EINVAL;
1759
7410616c
LP
1760 r = unit_name_to_prefix(unit, &p);
1761 if (r < 0)
1762 return r;
a016b922
LP
1763
1764 dash = strchr(p, '-');
e66e5b61
LP
1765
1766 /* Don't allow initial dashes */
1767 if (dash == p)
1768 return -EINVAL;
1769
a016b922
LP
1770 while (dash) {
1771 _cleanup_free_ char *escaped = NULL;
1772 char n[dash - p + sizeof(".slice")];
1773
e66e5b61
LP
1774 /* Don't allow trailing or double dashes */
1775 if (dash[1] == 0 || dash[1] == '-')
c96cc582 1776 return -EINVAL;
a016b922 1777
c96cc582 1778 strcpy(stpncpy(n, p, dash - p), ".slice");
7410616c 1779 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
a016b922
LP
1780 return -EINVAL;
1781
1782 escaped = cg_escape(n);
1783 if (!escaped)
1784 return -ENOMEM;
1785
1786 if (!strextend(&s, escaped, "/", NULL))
1787 return -ENOMEM;
1788
1789 dash = strchr(dash+1, '-');
1790 }
1791
1792 e = cg_escape(unit);
1793 if (!e)
1794 return -ENOMEM;
1795
1796 if (!strextend(&s, e, NULL))
1797 return -ENOMEM;
1798
1799 *ret = s;
1800 s = NULL;
1801
1802 return 0;
1803}
4ad49000
LP
1804
1805int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1806 _cleanup_free_ char *p = NULL;
1807 int r;
1808
1809 r = cg_get_path(controller, path, attribute, &p);
1810 if (r < 0)
1811 return r;
1812
4c1fc3e4 1813 return write_string_file(p, value, 0);
4ad49000
LP
1814}
1815
934277fe
LP
1816int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1817 _cleanup_free_ char *p = NULL;
1818 int r;
1819
1820 r = cg_get_path(controller, path, attribute, &p);
1821 if (r < 0)
1822 return r;
1823
1824 return read_one_line_file(p, ret);
1825}
1826
efdb0237
LP
1827int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
1828 CGroupController c;
1829 int r, unified;
4ad49000
LP
1830
1831 /* This one will create a cgroup in our private tree, but also
1832 * duplicate it in the trees specified in mask, and remove it
1833 * in all others */
1834
1835 /* First create the cgroup in our own hierarchy. */
1836 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1837 if (r < 0)
1838 return r;
1839
efdb0237
LP
1840 /* If we are in the unified hierarchy, we are done now */
1841 unified = cg_unified();
1842 if (unified < 0)
1843 return unified;
1844 if (unified > 0)
1845 return 0;
1846
1847 /* Otherwise, do the same in the other hierarchies */
1848 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1849 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
1850 const char *n;
1851
1852 n = cgroup_controller_to_string(c);
1853
13b84ec7 1854 if (mask & bit)
efdb0237 1855 (void) cg_create(n, path);
13b84ec7 1856 else if (supported & bit)
efdb0237 1857 (void) cg_trim(n, path, true);
4ad49000
LP
1858 }
1859
13b84ec7 1860 return 0;
4ad49000
LP
1861}
1862
efdb0237
LP
1863int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
1864 CGroupController c;
1865 int r, unified;
4ad49000
LP
1866
1867 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1868 if (r < 0)
1869 return r;
4ad49000 1870
efdb0237
LP
1871 unified = cg_unified();
1872 if (unified < 0)
1873 return unified;
1874 if (unified > 0)
1875 return 0;
7b3fd631 1876
efdb0237
LP
1877 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1878 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
1879 const char *p = NULL;
7b3fd631 1880
efdb0237
LP
1881 if (!(supported & bit))
1882 continue;
7b3fd631 1883
efdb0237
LP
1884 if (path_callback)
1885 p = path_callback(bit, userdata);
7b3fd631 1886
efdb0237
LP
1887 if (!p)
1888 p = path;
4ad49000 1889
efdb0237 1890 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
4ad49000
LP
1891 }
1892
13b84ec7 1893 return 0;
4ad49000
LP
1894}
1895
efdb0237 1896int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
1897 Iterator i;
1898 void *pidp;
1899 int r = 0;
1900
1901 SET_FOREACH(pidp, pids, i) {
fea72cc0 1902 pid_t pid = PTR_TO_PID(pidp);
13b84ec7 1903 int q;
6c12b52e 1904
7b3fd631 1905 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
efdb0237 1906 if (q < 0 && r >= 0)
13b84ec7 1907 r = q;
6c12b52e
LP
1908 }
1909
1910 return r;
1911}
1912
efdb0237 1913int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
b3c5bad3 1914 CGroupController c;
ba09d9c6 1915 int r = 0, unified;
4ad49000 1916
13b84ec7
LP
1917 if (!path_equal(from, to)) {
1918 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1919 if (r < 0)
1920 return r;
1921 }
4ad49000 1922
efdb0237
LP
1923 unified = cg_unified();
1924 if (unified < 0)
1925 return unified;
1926 if (unified > 0)
1927 return r;
03b90d4b 1928
efdb0237
LP
1929 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1930 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
1931 const char *p = NULL;
03b90d4b 1932
efdb0237
LP
1933 if (!(supported & bit))
1934 continue;
03b90d4b 1935
efdb0237
LP
1936 if (to_callback)
1937 p = to_callback(bit, userdata);
4ad49000 1938
efdb0237
LP
1939 if (!p)
1940 p = to;
1941
1942 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, false, false);
4ad49000
LP
1943 }
1944
13b84ec7 1945 return 0;
4ad49000
LP
1946}
1947
efdb0237
LP
1948int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
1949 CGroupController c;
1950 int r, unified;
4ad49000
LP
1951
1952 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1953 if (r < 0)
1954 return r;
1955
efdb0237
LP
1956 unified = cg_unified();
1957 if (unified < 0)
1958 return unified;
1959 if (unified > 0)
1960 return r;
1961
1962 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1963 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
1964
1965 if (!(supported & bit))
1966 continue;
4ad49000 1967
efdb0237 1968 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
4ad49000
LP
1969 }
1970
13b84ec7 1971 return 0;
4ad49000
LP
1972}
1973
efdb0237
LP
1974int cg_mask_supported(CGroupMask *ret) {
1975 CGroupMask mask = 0;
1976 int r, unified;
1977
1978 /* Determines the mask of supported cgroup controllers. Only
1979 * includes controllers we can make sense of and that are
1980 * actually accessible. */
4ad49000 1981
efdb0237
LP
1982 unified = cg_unified();
1983 if (unified < 0)
1984 return unified;
1985 if (unified > 0) {
5f4c5fef 1986 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
efdb0237
LP
1987 const char *c;
1988
1989 /* In the unified hierarchy we can read the supported
1990 * and accessible controllers from a the top-level
1991 * cgroup attribute */
1992
5f4c5fef
LP
1993 r = cg_get_root_path(&root);
1994 if (r < 0)
1995 return r;
1996
1997 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
1998 if (r < 0)
1999 return r;
2000
2001 r = read_one_line_file(path, &controllers);
efdb0237
LP
2002 if (r < 0)
2003 return r;
4ad49000 2004
efdb0237
LP
2005 c = controllers;
2006 for (;;) {
2007 _cleanup_free_ char *n = NULL;
2008 CGroupController v;
2009
2010 r = extract_first_word(&c, &n, NULL, 0);
2011 if (r < 0)
2012 return r;
2013 if (r == 0)
2014 break;
2015
2016 v = cgroup_controller_from_string(n);
2017 if (v < 0)
2018 continue;
2019
2020 mask |= CGROUP_CONTROLLER_TO_MASK(v);
2021 }
2022
03a7b521
LP
2023 /* Currently, we only support the memory and pids
2024 * controller in the unified hierarchy, mask
2025 * everything else off. */
2026 mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_PIDS;
efdb0237
LP
2027
2028 } else {
2029 CGroupController c;
2030
2031 /* In the legacy hierarchy, we check whether which
2032 * hierarchies are mounted. */
2033
2034 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2035 const char *n;
2036
2037 n = cgroup_controller_to_string(c);
2038 if (controller_is_accessible(n) >= 0)
2039 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2040 }
4ad49000
LP
2041 }
2042
efdb0237
LP
2043 *ret = mask;
2044 return 0;
4ad49000 2045}
b12afc8c
LP
2046
2047int cg_kernel_controllers(Set *controllers) {
2048 _cleanup_fclose_ FILE *f = NULL;
2049 char buf[LINE_MAX];
2050 int r;
2051
2052 assert(controllers);
2053
e155a0aa
LP
2054 /* Determines the full list of kernel-known controllers. Might
2055 * include controllers we don't actually support, arbitrary
2056 * named hierarchies and controllers that aren't currently
2057 * accessible (because not mounted). */
2058
b12afc8c
LP
2059 f = fopen("/proc/cgroups", "re");
2060 if (!f) {
2061 if (errno == ENOENT)
2062 return 0;
2063 return -errno;
2064 }
2065
2066 /* Ignore the header line */
2067 (void) fgets(buf, sizeof(buf), f);
2068
2069 for (;;) {
2070 char *controller;
2071 int enabled = 0;
2072
2073 errno = 0;
2074 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2075
2076 if (feof(f))
2077 break;
2078
e155a0aa 2079 if (ferror(f) && errno != 0)
b12afc8c
LP
2080 return -errno;
2081
2082 return -EBADMSG;
2083 }
2084
2085 if (!enabled) {
2086 free(controller);
2087 continue;
2088 }
2089
efdb0237 2090 if (!cg_controller_is_valid(controller)) {
b12afc8c
LP
2091 free(controller);
2092 return -EBADMSG;
2093 }
2094
2095 r = set_consume(controllers, controller);
2096 if (r < 0)
2097 return r;
2098 }
2099
2100 return 0;
2101}
efdb0237
LP
2102
2103static thread_local int unified_cache = -1;
2104
2105int cg_unified(void) {
2106 struct statfs fs;
2107
2108 /* Checks if we support the unified hierarchy. Returns an
2109 * error when the cgroup hierarchies aren't mounted yet or we
2110 * have any other trouble determining if the unified hierarchy
2111 * is supported. */
2112
2113 if (unified_cache >= 0)
2114 return unified_cache;
2115
2116 if (statfs("/sys/fs/cgroup/", &fs) < 0)
2117 return -errno;
2118
2119 if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC))
2120 unified_cache = true;
2121 else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
2122 unified_cache = false;
2123 else
2124 return -ENOEXEC;
2125
2126 return unified_cache;
2127}
2128
2129void cg_unified_flush(void) {
2130 unified_cache = -1;
2131}
2132
2133int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
2134 _cleanup_free_ char *fs = NULL;
2135 CGroupController c;
2136 int r, unified;
2137
2138 assert(p);
2139
2140 if (supported == 0)
2141 return 0;
2142
2143 unified = cg_unified();
2144 if (unified < 0)
2145 return unified;
2146 if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
2147 return 0;
2148
2149 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2150 if (r < 0)
2151 return r;
2152
2153 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2154 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2155 const char *n;
2156
2157 if (!(supported & bit))
2158 continue;
2159
2160 n = cgroup_controller_to_string(c);
2161 {
2162 char s[1 + strlen(n) + 1];
2163
2164 s[0] = mask & bit ? '+' : '-';
2165 strcpy(s + 1, n);
2166
2167 r = write_string_file(fs, s, 0);
2168 if (r < 0)
98e4d8d7 2169 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
efdb0237
LP
2170 }
2171 }
2172
2173 return 0;
2174}
2175
2176bool cg_is_unified_wanted(void) {
2177 static thread_local int wanted = -1;
2178 int r, unified;
2179
2180 /* If the hierarchy is already mounted, then follow whatever
2181 * was chosen for it. */
2182 unified = cg_unified();
2183 if (unified >= 0)
2184 return unified;
2185
2186 /* Otherwise, let's see what the kernel command line has to
2187 * say. Since checking that is expensive, let's cache the
2188 * result. */
2189 if (wanted >= 0)
2190 return wanted;
2191
2192 r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
2193 if (r > 0)
2194 return (wanted = true);
2195 else {
2196 _cleanup_free_ char *value = NULL;
2197
2198 r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
2199 if (r < 0)
2200 return false;
2201 if (r == 0)
2202 return (wanted = false);
2203
2204 return (wanted = parse_boolean(value) > 0);
2205 }
2206}
2207
2208bool cg_is_legacy_wanted(void) {
2209 return !cg_is_unified_wanted();
2210}
2211
d53d9474
LP
2212int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2213 uint64_t u;
2214 int r;
2215
2216 if (isempty(s)) {
2217 *ret = CGROUP_CPU_SHARES_INVALID;
2218 return 0;
2219 }
2220
2221 r = safe_atou64(s, &u);
2222 if (r < 0)
2223 return r;
2224
2225 if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2226 return -ERANGE;
2227
2228 *ret = u;
2229 return 0;
2230}
2231
2232int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2233 uint64_t u;
2234 int r;
2235
2236 if (isempty(s)) {
2237 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2238 return 0;
2239 }
2240
2241 r = safe_atou64(s, &u);
2242 if (r < 0)
2243 return r;
2244
2245 if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2246 return -ERANGE;
2247
2248 *ret = u;
2249 return 0;
2250}
2251
efdb0237
LP
2252static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2253 [CGROUP_CONTROLLER_CPU] = "cpu",
2254 [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2255 [CGROUP_CONTROLLER_BLKIO] = "blkio",
2256 [CGROUP_CONTROLLER_MEMORY] = "memory",
3905f127 2257 [CGROUP_CONTROLLER_DEVICES] = "devices",
03a7b521 2258 [CGROUP_CONTROLLER_PIDS] = "pids",
32ee7d33 2259 [CGROUP_CONTROLLER_NET_CLS] = "net_cls",
efdb0237
LP
2260};
2261
2262DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);