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