]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/cgroup-util.c
b9b27862ff3f5c5b00f202170f0f03afa1e3da55
[thirdparty/systemd.git] / src / basic / cgroup-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <ftw.h>
31
32 #include "set.h"
33 #include "macro.h"
34 #include "util.h"
35 #include "formats-util.h"
36 #include "process-util.h"
37 #include "path-util.h"
38 #include "unit-name.h"
39 #include "fileio.h"
40 #include "special.h"
41 #include "mkdir.h"
42 #include "login-util.h"
43 #include "cgroup-util.h"
44
45 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
46 _cleanup_free_ char *fs = NULL;
47 FILE *f;
48 int r;
49
50 assert(_f);
51
52 r = cg_get_path(controller, path, "cgroup.procs", &fs);
53 if (r < 0)
54 return r;
55
56 f = fopen(fs, "re");
57 if (!f)
58 return -errno;
59
60 *_f = f;
61 return 0;
62 }
63
64 int cg_read_pid(FILE *f, pid_t *_pid) {
65 unsigned long ul;
66
67 /* Note that the cgroup.procs might contain duplicates! See
68 * cgroups.txt for details. */
69
70 assert(f);
71 assert(_pid);
72
73 errno = 0;
74 if (fscanf(f, "%lu", &ul) != 1) {
75
76 if (feof(f))
77 return 0;
78
79 return errno ? -errno : -EIO;
80 }
81
82 if (ul <= 0)
83 return -EIO;
84
85 *_pid = (pid_t) ul;
86 return 1;
87 }
88
89 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
90 _cleanup_free_ char *fs = NULL;
91 int r;
92 DIR *d;
93
94 assert(_d);
95
96 /* This is not recursive! */
97
98 r = cg_get_path(controller, path, NULL, &fs);
99 if (r < 0)
100 return r;
101
102 d = opendir(fs);
103 if (!d)
104 return -errno;
105
106 *_d = d;
107 return 0;
108 }
109
110 int cg_read_subgroup(DIR *d, char **fn) {
111 struct dirent *de;
112
113 assert(d);
114 assert(fn);
115
116 FOREACH_DIRENT_ALL(de, d, return -errno) {
117 char *b;
118
119 if (de->d_type != DT_DIR)
120 continue;
121
122 if (streq(de->d_name, ".") ||
123 streq(de->d_name, ".."))
124 continue;
125
126 b = strdup(de->d_name);
127 if (!b)
128 return -ENOMEM;
129
130 *fn = b;
131 return 1;
132 }
133
134 return 0;
135 }
136
137 int cg_rmdir(const char *controller, const char *path) {
138 _cleanup_free_ char *p = NULL;
139 int r;
140
141 r = cg_get_path(controller, path, NULL, &p);
142 if (r < 0)
143 return r;
144
145 r = rmdir(p);
146 if (r < 0 && errno != ENOENT)
147 return -errno;
148
149 return 0;
150 }
151
152 int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
153 _cleanup_set_free_ Set *allocated_set = NULL;
154 bool done = false;
155 int r, ret = 0;
156 pid_t my_pid;
157
158 assert(sig >= 0);
159
160 /* This goes through the tasks list and kills them all. This
161 * is repeated until no further processes are added to the
162 * tasks list, to properly handle forking processes */
163
164 if (!s) {
165 s = allocated_set = set_new(NULL);
166 if (!s)
167 return -ENOMEM;
168 }
169
170 my_pid = getpid();
171
172 do {
173 _cleanup_fclose_ FILE *f = NULL;
174 pid_t pid = 0;
175 done = true;
176
177 r = cg_enumerate_processes(controller, path, &f);
178 if (r < 0) {
179 if (ret >= 0 && r != -ENOENT)
180 return r;
181
182 return ret;
183 }
184
185 while ((r = cg_read_pid(f, &pid)) > 0) {
186
187 if (ignore_self && pid == my_pid)
188 continue;
189
190 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
191 continue;
192
193 /* If we haven't killed this process yet, kill
194 * it */
195 if (kill(pid, sig) < 0) {
196 if (ret >= 0 && errno != ESRCH)
197 ret = -errno;
198 } else {
199 if (sigcont && sig != SIGKILL)
200 (void) kill(pid, SIGCONT);
201
202 if (ret == 0)
203 ret = 1;
204 }
205
206 done = false;
207
208 r = set_put(s, LONG_TO_PTR(pid));
209 if (r < 0) {
210 if (ret >= 0)
211 return r;
212
213 return ret;
214 }
215 }
216
217 if (r < 0) {
218 if (ret >= 0)
219 return r;
220
221 return ret;
222 }
223
224 /* To avoid racing against processes which fork
225 * quicker than we can kill them we repeat this until
226 * no new pids need to be killed. */
227
228 } while (!done);
229
230 return ret;
231 }
232
233 int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
234 _cleanup_set_free_ Set *allocated_set = NULL;
235 _cleanup_closedir_ DIR *d = NULL;
236 int r, ret;
237 char *fn;
238
239 assert(path);
240 assert(sig >= 0);
241
242 if (!s) {
243 s = allocated_set = set_new(NULL);
244 if (!s)
245 return -ENOMEM;
246 }
247
248 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
249
250 r = cg_enumerate_subgroups(controller, path, &d);
251 if (r < 0) {
252 if (ret >= 0 && r != -ENOENT)
253 return r;
254
255 return ret;
256 }
257
258 while ((r = cg_read_subgroup(d, &fn)) > 0) {
259 _cleanup_free_ char *p = NULL;
260
261 p = strjoin(path, "/", fn, NULL);
262 free(fn);
263 if (!p)
264 return -ENOMEM;
265
266 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
267 if (r != 0 && ret >= 0)
268 ret = r;
269 }
270
271 if (ret >= 0 && r < 0)
272 ret = r;
273
274 if (rem) {
275 r = cg_rmdir(controller, path);
276 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
277 return r;
278 }
279
280 return ret;
281 }
282
283 int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
284 bool done = false;
285 _cleanup_set_free_ Set *s = NULL;
286 int r, ret = 0;
287 pid_t my_pid;
288
289 assert(cfrom);
290 assert(pfrom);
291 assert(cto);
292 assert(pto);
293
294 s = set_new(NULL);
295 if (!s)
296 return -ENOMEM;
297
298 my_pid = getpid();
299
300 do {
301 _cleanup_fclose_ FILE *f = NULL;
302 pid_t pid = 0;
303 done = true;
304
305 r = cg_enumerate_processes(cfrom, pfrom, &f);
306 if (r < 0) {
307 if (ret >= 0 && r != -ENOENT)
308 return r;
309
310 return ret;
311 }
312
313 while ((r = cg_read_pid(f, &pid)) > 0) {
314
315 /* This might do weird stuff if we aren't a
316 * single-threaded program. However, we
317 * luckily know we are not */
318 if (ignore_self && pid == my_pid)
319 continue;
320
321 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
322 continue;
323
324 /* Ignore kernel threads. Since they can only
325 * exist in the root cgroup, we only check for
326 * them there. */
327 if (cfrom &&
328 (isempty(pfrom) || path_equal(pfrom, "/")) &&
329 is_kernel_thread(pid) > 0)
330 continue;
331
332 r = cg_attach(cto, pto, pid);
333 if (r < 0) {
334 if (ret >= 0 && r != -ESRCH)
335 ret = r;
336 } else if (ret == 0)
337 ret = 1;
338
339 done = false;
340
341 r = set_put(s, LONG_TO_PTR(pid));
342 if (r < 0) {
343 if (ret >= 0)
344 return r;
345
346 return ret;
347 }
348 }
349
350 if (r < 0) {
351 if (ret >= 0)
352 return r;
353
354 return ret;
355 }
356 } while (!done);
357
358 return ret;
359 }
360
361 int cg_migrate_recursive(
362 const char *cfrom,
363 const char *pfrom,
364 const char *cto,
365 const char *pto,
366 bool ignore_self,
367 bool rem) {
368
369 _cleanup_closedir_ DIR *d = NULL;
370 int r, ret = 0;
371 char *fn;
372
373 assert(cfrom);
374 assert(pfrom);
375 assert(cto);
376 assert(pto);
377
378 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
379
380 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
381 if (r < 0) {
382 if (ret >= 0 && r != -ENOENT)
383 return r;
384
385 return ret;
386 }
387
388 while ((r = cg_read_subgroup(d, &fn)) > 0) {
389 _cleanup_free_ char *p = NULL;
390
391 p = strjoin(pfrom, "/", fn, NULL);
392 free(fn);
393 if (!p)
394 return -ENOMEM;
395
396 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
397 if (r != 0 && ret >= 0)
398 ret = r;
399 }
400
401 if (r < 0 && ret >= 0)
402 ret = r;
403
404 if (rem) {
405 r = cg_rmdir(cfrom, pfrom);
406 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
407 return r;
408 }
409
410 return ret;
411 }
412
413 int cg_migrate_recursive_fallback(
414 const char *cfrom,
415 const char *pfrom,
416 const char *cto,
417 const char *pto,
418 bool ignore_self,
419 bool rem) {
420
421 int r;
422
423 assert(cfrom);
424 assert(pfrom);
425 assert(cto);
426 assert(pto);
427
428 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
429 if (r < 0) {
430 char prefix[strlen(pto) + 1];
431
432 /* This didn't work? Then let's try all prefixes of the destination */
433
434 PATH_FOREACH_PREFIX(prefix, pto) {
435 int q;
436
437 q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
438 if (q >= 0)
439 return q;
440 }
441 }
442
443 return r;
444 }
445
446 static const char *normalize_controller(const char *controller) {
447
448 assert(controller);
449
450 if (startswith(controller, "name="))
451 return controller + 5;
452 else
453 return controller;
454 }
455
456 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
457 char *t = NULL;
458
459 if (!isempty(controller)) {
460 if (!isempty(path) && !isempty(suffix))
461 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
462 else if (!isempty(path))
463 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
464 else if (!isempty(suffix))
465 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
466 else
467 t = strappend("/sys/fs/cgroup/", controller);
468 } else {
469 if (!isempty(path) && !isempty(suffix))
470 t = strjoin(path, "/", suffix, NULL);
471 else if (!isempty(path))
472 t = strdup(path);
473 else
474 return -EINVAL;
475 }
476
477 if (!t)
478 return -ENOMEM;
479
480 *fs = path_kill_slashes(t);
481 return 0;
482 }
483
484 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
485 const char *p;
486 static thread_local bool good = false;
487
488 assert(fs);
489
490 if (controller && !cg_controller_is_valid(controller))
491 return -EINVAL;
492
493 if (_unlikely_(!good)) {
494 int r;
495
496 r = path_is_mount_point("/sys/fs/cgroup", 0);
497 if (r < 0)
498 return r;
499 if (r == 0)
500 return -ENOENT;
501
502 /* Cache this to save a few stat()s */
503 good = true;
504 }
505
506 p = controller ? normalize_controller(controller) : NULL;
507
508 return join_path(p, path, suffix, fs);
509 }
510
511 static int check_hierarchy(const char *p) {
512 const char *cc;
513
514 assert(p);
515
516 if (!filename_is_valid(p))
517 return 0;
518
519 /* Check if this controller actually really exists */
520 cc = strjoina("/sys/fs/cgroup/", p);
521 if (laccess(cc, F_OK) < 0)
522 return -errno;
523
524 return 0;
525 }
526
527 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
528 const char *p;
529 int r;
530
531 assert(fs);
532
533 if (!cg_controller_is_valid(controller))
534 return -EINVAL;
535
536 /* Normalize the controller syntax */
537 p = normalize_controller(controller);
538
539 /* Check if this controller actually really exists */
540 r = check_hierarchy(p);
541 if (r < 0)
542 return r;
543
544 return join_path(p, path, suffix, fs);
545 }
546
547 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
548 assert(path);
549 assert(sb);
550 assert(ftwbuf);
551
552 if (typeflag != FTW_DP)
553 return 0;
554
555 if (ftwbuf->level < 1)
556 return 0;
557
558 (void) rmdir(path);
559 return 0;
560 }
561
562 int cg_trim(const char *controller, const char *path, bool delete_root) {
563 _cleanup_free_ char *fs = NULL;
564 int r = 0;
565
566 assert(path);
567
568 r = cg_get_path(controller, path, NULL, &fs);
569 if (r < 0)
570 return r;
571
572 errno = 0;
573 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
574 if (errno == ENOENT)
575 r = 0;
576 else if (errno != 0)
577 r = -errno;
578 else
579 r = -EIO;
580 }
581
582 if (delete_root) {
583 if (rmdir(fs) < 0 && errno != ENOENT)
584 return -errno;
585 }
586
587 return r;
588 }
589
590 int cg_delete(const char *controller, const char *path) {
591 _cleanup_free_ char *parent = NULL;
592 int r;
593
594 assert(path);
595
596 r = path_get_parent(path, &parent);
597 if (r < 0)
598 return r;
599
600 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
601 return r == -ENOENT ? 0 : r;
602 }
603
604 int cg_create(const char *controller, const char *path) {
605 _cleanup_free_ char *fs = NULL;
606 int r;
607
608 r = cg_get_path_and_check(controller, path, NULL, &fs);
609 if (r < 0)
610 return r;
611
612 r = mkdir_parents(fs, 0755);
613 if (r < 0)
614 return r;
615
616 if (mkdir(fs, 0755) < 0) {
617
618 if (errno == EEXIST)
619 return 0;
620
621 return -errno;
622 }
623
624 return 1;
625 }
626
627 int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
628 int r, q;
629
630 assert(pid >= 0);
631
632 r = cg_create(controller, path);
633 if (r < 0)
634 return r;
635
636 q = cg_attach(controller, path, pid);
637 if (q < 0)
638 return q;
639
640 /* This does not remove the cgroup on failure */
641 return r;
642 }
643
644 int cg_attach(const char *controller, const char *path, pid_t pid) {
645 _cleanup_free_ char *fs = NULL;
646 char c[DECIMAL_STR_MAX(pid_t) + 2];
647 int r;
648
649 assert(path);
650 assert(pid >= 0);
651
652 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
653 if (r < 0)
654 return r;
655
656 if (pid == 0)
657 pid = getpid();
658
659 snprintf(c, sizeof(c), PID_FMT"\n", pid);
660
661 return write_string_file(fs, c, 0);
662 }
663
664 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
665 int r;
666
667 assert(controller);
668 assert(path);
669 assert(pid >= 0);
670
671 r = cg_attach(controller, path, pid);
672 if (r < 0) {
673 char prefix[strlen(path) + 1];
674
675 /* This didn't work? Then let's try all prefixes of
676 * the destination */
677
678 PATH_FOREACH_PREFIX(prefix, path) {
679 int q;
680
681 q = cg_attach(controller, prefix, pid);
682 if (q >= 0)
683 return q;
684 }
685 }
686
687 return r;
688 }
689
690 int cg_set_group_access(
691 const char *controller,
692 const char *path,
693 mode_t mode,
694 uid_t uid,
695 gid_t gid) {
696
697 _cleanup_free_ char *fs = NULL;
698 int r;
699
700 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
701 return 0;
702
703 if (mode != MODE_INVALID)
704 mode &= 0777;
705
706 r = cg_get_path(controller, path, NULL, &fs);
707 if (r < 0)
708 return r;
709
710 return chmod_and_chown(fs, mode, uid, gid);
711 }
712
713 int cg_set_task_access(
714 const char *controller,
715 const char *path,
716 mode_t mode,
717 uid_t uid,
718 gid_t gid) {
719
720 _cleanup_free_ char *fs = NULL, *procs = NULL;
721 int r;
722
723 assert(path);
724
725 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
726 return 0;
727
728 if (mode != MODE_INVALID)
729 mode &= 0666;
730
731 r = cg_get_path(controller, path, "cgroup.procs", &fs);
732 if (r < 0)
733 return r;
734
735 r = chmod_and_chown(fs, mode, uid, gid);
736 if (r < 0)
737 return r;
738
739 /* Compatibility, Always keep values for "tasks" in sync with
740 * "cgroup.procs" */
741 r = cg_get_path(controller, path, "tasks", &procs);
742 if (r < 0)
743 return r;
744
745 return chmod_and_chown(procs, mode, uid, gid);
746 }
747
748 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
749 _cleanup_fclose_ FILE *f = NULL;
750 char line[LINE_MAX];
751 const char *fs;
752 size_t cs;
753
754 assert(path);
755 assert(pid >= 0);
756
757 if (controller) {
758 if (!cg_controller_is_valid(controller))
759 return -EINVAL;
760
761 controller = normalize_controller(controller);
762 } else
763 controller = SYSTEMD_CGROUP_CONTROLLER;
764
765 fs = procfs_file_alloca(pid, "cgroup");
766
767 f = fopen(fs, "re");
768 if (!f)
769 return errno == ENOENT ? -ESRCH : -errno;
770
771 cs = strlen(controller);
772
773 FOREACH_LINE(line, f, return -errno) {
774 char *l, *p, *e;
775 size_t k;
776 const char *word, *state;
777 bool found = false;
778
779 truncate_nl(line);
780
781 l = strchr(line, ':');
782 if (!l)
783 continue;
784
785 l++;
786 e = strchr(l, ':');
787 if (!e)
788 continue;
789
790 *e = 0;
791
792 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
793
794 if (k == cs && memcmp(word, controller, cs) == 0) {
795 found = true;
796 break;
797 }
798
799 if (k == 5 + cs &&
800 memcmp(word, "name=", 5) == 0 &&
801 memcmp(word+5, controller, cs) == 0) {
802 found = true;
803 break;
804 }
805 }
806
807 if (!found)
808 continue;
809
810 p = strdup(e + 1);
811 if (!p)
812 return -ENOMEM;
813
814 *path = p;
815 return 0;
816 }
817
818 return -ENOENT;
819 }
820
821 int cg_install_release_agent(const char *controller, const char *agent) {
822 _cleanup_free_ char *fs = NULL, *contents = NULL;
823 char *sc;
824 int r;
825
826 assert(agent);
827
828 r = cg_get_path(controller, NULL, "release_agent", &fs);
829 if (r < 0)
830 return r;
831
832 r = read_one_line_file(fs, &contents);
833 if (r < 0)
834 return r;
835
836 sc = strstrip(contents);
837 if (isempty(sc)) {
838 r = write_string_file(fs, agent, 0);
839 if (r < 0)
840 return r;
841 } else if (!streq(sc, agent))
842 return -EEXIST;
843
844 fs = mfree(fs);
845 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
846 if (r < 0)
847 return r;
848
849 contents = mfree(contents);
850 r = read_one_line_file(fs, &contents);
851 if (r < 0)
852 return r;
853
854 sc = strstrip(contents);
855 if (streq(sc, "0")) {
856 r = write_string_file(fs, "1", 0);
857 if (r < 0)
858 return r;
859
860 return 1;
861 }
862
863 if (!streq(sc, "1"))
864 return -EIO;
865
866 return 0;
867 }
868
869 int cg_uninstall_release_agent(const char *controller) {
870 _cleanup_free_ char *fs = NULL;
871 int r;
872
873 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
874 if (r < 0)
875 return r;
876
877 r = write_string_file(fs, "0", 0);
878 if (r < 0)
879 return r;
880
881 fs = mfree(fs);
882
883 r = cg_get_path(controller, NULL, "release_agent", &fs);
884 if (r < 0)
885 return r;
886
887 r = write_string_file(fs, "", 0);
888 if (r < 0)
889 return r;
890
891 return 0;
892 }
893
894 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
895 _cleanup_fclose_ FILE *f = NULL;
896 pid_t pid = 0, self_pid;
897 bool found = false;
898 int r;
899
900 assert(path);
901
902 r = cg_enumerate_processes(controller, path, &f);
903 if (r < 0)
904 return r == -ENOENT ? 1 : r;
905
906 self_pid = getpid();
907
908 while ((r = cg_read_pid(f, &pid)) > 0) {
909
910 if (ignore_self && pid == self_pid)
911 continue;
912
913 found = true;
914 break;
915 }
916
917 if (r < 0)
918 return r;
919
920 return !found;
921 }
922
923 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
924 _cleanup_closedir_ DIR *d = NULL;
925 char *fn;
926 int r;
927
928 assert(path);
929
930 r = cg_is_empty(controller, path, ignore_self);
931 if (r <= 0)
932 return r;
933
934 r = cg_enumerate_subgroups(controller, path, &d);
935 if (r < 0)
936 return r == -ENOENT ? 1 : r;
937
938 while ((r = cg_read_subgroup(d, &fn)) > 0) {
939 _cleanup_free_ char *p = NULL;
940
941 p = strjoin(path, "/", fn, NULL);
942 free(fn);
943 if (!p)
944 return -ENOMEM;
945
946 r = cg_is_empty_recursive(controller, p, ignore_self);
947 if (r <= 0)
948 return r;
949 }
950
951 if (r < 0)
952 return r;
953
954 return 1;
955 }
956
957 int cg_split_spec(const char *spec, char **controller, char **path) {
958 const char *e;
959 char *t = NULL, *u = NULL;
960 _cleanup_free_ char *v = NULL;
961
962 assert(spec);
963
964 if (*spec == '/') {
965 if (!path_is_safe(spec))
966 return -EINVAL;
967
968 if (path) {
969 t = strdup(spec);
970 if (!t)
971 return -ENOMEM;
972
973 *path = path_kill_slashes(t);
974 }
975
976 if (controller)
977 *controller = NULL;
978
979 return 0;
980 }
981
982 e = strchr(spec, ':');
983 if (!e) {
984 if (!cg_controller_is_valid(spec))
985 return -EINVAL;
986
987 if (controller) {
988 t = strdup(normalize_controller(spec));
989 if (!t)
990 return -ENOMEM;
991
992 *controller = t;
993 }
994
995 if (path)
996 *path = NULL;
997
998 return 0;
999 }
1000
1001 v = strndup(spec, e-spec);
1002 if (!v)
1003 return -ENOMEM;
1004 t = strdup(normalize_controller(v));
1005 if (!t)
1006 return -ENOMEM;
1007 if (!cg_controller_is_valid(t)) {
1008 free(t);
1009 return -EINVAL;
1010 }
1011
1012 if (streq(e+1, "")) {
1013 u = strdup("/");
1014 if (!u) {
1015 free(t);
1016 return -ENOMEM;
1017 }
1018 } else {
1019 u = strdup(e+1);
1020 if (!u) {
1021 free(t);
1022 return -ENOMEM;
1023 }
1024
1025 if (!path_is_safe(u) ||
1026 !path_is_absolute(u)) {
1027 free(t);
1028 free(u);
1029 return -EINVAL;
1030 }
1031
1032 path_kill_slashes(u);
1033 }
1034
1035 if (controller)
1036 *controller = t;
1037 else
1038 free(t);
1039
1040 if (path)
1041 *path = u;
1042 else
1043 free(u);
1044
1045 return 0;
1046 }
1047
1048 int cg_mangle_path(const char *path, char **result) {
1049 _cleanup_free_ char *c = NULL, *p = NULL;
1050 char *t;
1051 int r;
1052
1053 assert(path);
1054 assert(result);
1055
1056 /* First, check if it already is a filesystem path */
1057 if (path_startswith(path, "/sys/fs/cgroup")) {
1058
1059 t = strdup(path);
1060 if (!t)
1061 return -ENOMEM;
1062
1063 *result = path_kill_slashes(t);
1064 return 0;
1065 }
1066
1067 /* Otherwise, treat it as cg spec */
1068 r = cg_split_spec(path, &c, &p);
1069 if (r < 0)
1070 return r;
1071
1072 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1073 }
1074
1075 int cg_get_root_path(char **path) {
1076 char *p, *e;
1077 int r;
1078
1079 assert(path);
1080
1081 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1082 if (r < 0)
1083 return r;
1084
1085 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1086 if (e)
1087 *e = 0;
1088
1089 *path = p;
1090 return 0;
1091 }
1092
1093 int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1094 _cleanup_free_ char *rt = NULL;
1095 char *p;
1096 int r;
1097
1098 assert(cgroup);
1099 assert(shifted);
1100
1101 if (!root) {
1102 /* If the root was specified let's use that, otherwise
1103 * let's determine it from PID 1 */
1104
1105 r = cg_get_root_path(&rt);
1106 if (r < 0)
1107 return r;
1108
1109 root = rt;
1110 }
1111
1112 p = path_startswith(cgroup, root);
1113 if (p)
1114 *shifted = p - 1;
1115 else
1116 *shifted = cgroup;
1117
1118 return 0;
1119 }
1120
1121 int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1122 _cleanup_free_ char *raw = NULL;
1123 const char *c;
1124 int r;
1125
1126 assert(pid >= 0);
1127 assert(cgroup);
1128
1129 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
1130 if (r < 0)
1131 return r;
1132
1133 r = cg_shift_path(raw, root, &c);
1134 if (r < 0)
1135 return r;
1136
1137 if (c == raw) {
1138 *cgroup = raw;
1139 raw = NULL;
1140 } else {
1141 char *n;
1142
1143 n = strdup(c);
1144 if (!n)
1145 return -ENOMEM;
1146
1147 *cgroup = n;
1148 }
1149
1150 return 0;
1151 }
1152
1153 int cg_path_decode_unit(const char *cgroup, char **unit){
1154 char *c, *s;
1155 size_t n;
1156
1157 assert(cgroup);
1158 assert(unit);
1159
1160 n = strcspn(cgroup, "/");
1161 if (n < 3)
1162 return -ENXIO;
1163
1164 c = strndupa(cgroup, n);
1165 c = cg_unescape(c);
1166
1167 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1168 return -ENXIO;
1169
1170 s = strdup(c);
1171 if (!s)
1172 return -ENOMEM;
1173
1174 *unit = s;
1175 return 0;
1176 }
1177
1178 static bool valid_slice_name(const char *p, size_t n) {
1179
1180 if (!p)
1181 return false;
1182
1183 if (n < strlen("x.slice"))
1184 return false;
1185
1186 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1187 char buf[n+1], *c;
1188
1189 memcpy(buf, p, n);
1190 buf[n] = 0;
1191
1192 c = cg_unescape(buf);
1193
1194 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
1195 }
1196
1197 return false;
1198 }
1199
1200 static const char *skip_slices(const char *p) {
1201 assert(p);
1202
1203 /* Skips over all slice assignments */
1204
1205 for (;;) {
1206 size_t n;
1207
1208 p += strspn(p, "/");
1209
1210 n = strcspn(p, "/");
1211 if (!valid_slice_name(p, n))
1212 return p;
1213
1214 p += n;
1215 }
1216 }
1217
1218 int cg_path_get_unit(const char *path, char **ret) {
1219 const char *e;
1220 char *unit;
1221 int r;
1222
1223 assert(path);
1224 assert(ret);
1225
1226 e = skip_slices(path);
1227
1228 r = cg_path_decode_unit(e, &unit);
1229 if (r < 0)
1230 return r;
1231
1232 /* We skipped over the slices, don't accept any now */
1233 if (endswith(unit, ".slice")) {
1234 free(unit);
1235 return -ENXIO;
1236 }
1237
1238 *ret = unit;
1239 return 0;
1240 }
1241
1242 int cg_pid_get_unit(pid_t pid, char **unit) {
1243 _cleanup_free_ char *cgroup = NULL;
1244 int r;
1245
1246 assert(unit);
1247
1248 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1249 if (r < 0)
1250 return r;
1251
1252 return cg_path_get_unit(cgroup, unit);
1253 }
1254
1255 /**
1256 * Skip session-*.scope, but require it to be there.
1257 */
1258 static const char *skip_session(const char *p) {
1259 size_t n;
1260
1261 if (isempty(p))
1262 return NULL;
1263
1264 p += strspn(p, "/");
1265
1266 n = strcspn(p, "/");
1267 if (n < strlen("session-x.scope"))
1268 return NULL;
1269
1270 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1271 char buf[n - 8 - 6 + 1];
1272
1273 memcpy(buf, p + 8, n - 8 - 6);
1274 buf[n - 8 - 6] = 0;
1275
1276 /* Note that session scopes never need unescaping,
1277 * since they cannot conflict with the kernel's own
1278 * names, hence we don't need to call cg_unescape()
1279 * here. */
1280
1281 if (!session_id_valid(buf))
1282 return false;
1283
1284 p += n;
1285 p += strspn(p, "/");
1286 return p;
1287 }
1288
1289 return NULL;
1290 }
1291
1292 /**
1293 * Skip user@*.service, but require it to be there.
1294 */
1295 static const char *skip_user_manager(const char *p) {
1296 size_t n;
1297
1298 if (isempty(p))
1299 return NULL;
1300
1301 p += strspn(p, "/");
1302
1303 n = strcspn(p, "/");
1304 if (n < strlen("user@x.service"))
1305 return NULL;
1306
1307 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1308 char buf[n - 5 - 8 + 1];
1309
1310 memcpy(buf, p + 5, n - 5 - 8);
1311 buf[n - 5 - 8] = 0;
1312
1313 /* Note that user manager services never need unescaping,
1314 * since they cannot conflict with the kernel's own
1315 * names, hence we don't need to call cg_unescape()
1316 * here. */
1317
1318 if (parse_uid(buf, NULL) < 0)
1319 return NULL;
1320
1321 p += n;
1322 p += strspn(p, "/");
1323
1324 return p;
1325 }
1326
1327 return NULL;
1328 }
1329
1330 static const char *skip_user_prefix(const char *path) {
1331 const char *e, *t;
1332
1333 assert(path);
1334
1335 /* Skip slices, if there are any */
1336 e = skip_slices(path);
1337
1338 /* Skip the user manager, if it's in the path now... */
1339 t = skip_user_manager(e);
1340 if (t)
1341 return t;
1342
1343 /* Alternatively skip the user session if it is in the path... */
1344 return skip_session(e);
1345 }
1346
1347 int cg_path_get_user_unit(const char *path, char **ret) {
1348 const char *t;
1349
1350 assert(path);
1351 assert(ret);
1352
1353 t = skip_user_prefix(path);
1354 if (!t)
1355 return -ENXIO;
1356
1357 /* And from here on it looks pretty much the same as for a
1358 * system unit, hence let's use the same parser from here
1359 * on. */
1360 return cg_path_get_unit(t, ret);
1361 }
1362
1363 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1364 _cleanup_free_ char *cgroup = NULL;
1365 int r;
1366
1367 assert(unit);
1368
1369 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1370 if (r < 0)
1371 return r;
1372
1373 return cg_path_get_user_unit(cgroup, unit);
1374 }
1375
1376 int cg_path_get_machine_name(const char *path, char **machine) {
1377 _cleanup_free_ char *u = NULL, *sl = NULL;
1378 int r;
1379
1380 r = cg_path_get_unit(path, &u);
1381 if (r < 0)
1382 return r;
1383
1384 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1385 if (!sl)
1386 return -ENOMEM;
1387
1388 return readlink_malloc(sl, machine);
1389 }
1390
1391 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1392 _cleanup_free_ char *cgroup = NULL;
1393 int r;
1394
1395 assert(machine);
1396
1397 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1398 if (r < 0)
1399 return r;
1400
1401 return cg_path_get_machine_name(cgroup, machine);
1402 }
1403
1404 int cg_path_get_session(const char *path, char **session) {
1405 _cleanup_free_ char *unit = NULL;
1406 char *start, *end;
1407 int r;
1408
1409 assert(path);
1410
1411 r = cg_path_get_unit(path, &unit);
1412 if (r < 0)
1413 return r;
1414
1415 start = startswith(unit, "session-");
1416 if (!start)
1417 return -ENXIO;
1418 end = endswith(start, ".scope");
1419 if (!end)
1420 return -ENXIO;
1421
1422 *end = 0;
1423 if (!session_id_valid(start))
1424 return -ENXIO;
1425
1426 if (session) {
1427 char *rr;
1428
1429 rr = strdup(start);
1430 if (!rr)
1431 return -ENOMEM;
1432
1433 *session = rr;
1434 }
1435
1436 return 0;
1437 }
1438
1439 int cg_pid_get_session(pid_t pid, char **session) {
1440 _cleanup_free_ char *cgroup = NULL;
1441 int r;
1442
1443 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1444 if (r < 0)
1445 return r;
1446
1447 return cg_path_get_session(cgroup, session);
1448 }
1449
1450 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1451 _cleanup_free_ char *slice = NULL;
1452 char *start, *end;
1453 int r;
1454
1455 assert(path);
1456
1457 r = cg_path_get_slice(path, &slice);
1458 if (r < 0)
1459 return r;
1460
1461 start = startswith(slice, "user-");
1462 if (!start)
1463 return -ENXIO;
1464 end = endswith(start, ".slice");
1465 if (!end)
1466 return -ENXIO;
1467
1468 *end = 0;
1469 if (parse_uid(start, uid) < 0)
1470 return -ENXIO;
1471
1472 return 0;
1473 }
1474
1475 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1476 _cleanup_free_ char *cgroup = NULL;
1477 int r;
1478
1479 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1480 if (r < 0)
1481 return r;
1482
1483 return cg_path_get_owner_uid(cgroup, uid);
1484 }
1485
1486 int cg_path_get_slice(const char *p, char **slice) {
1487 const char *e = NULL;
1488
1489 assert(p);
1490 assert(slice);
1491
1492 /* Finds the right-most slice unit from the beginning, but
1493 * stops before we come to the first non-slice unit. */
1494
1495 for (;;) {
1496 size_t n;
1497
1498 p += strspn(p, "/");
1499
1500 n = strcspn(p, "/");
1501 if (!valid_slice_name(p, n)) {
1502
1503 if (!e) {
1504 char *s;
1505
1506 s = strdup("-.slice");
1507 if (!s)
1508 return -ENOMEM;
1509
1510 *slice = s;
1511 return 0;
1512 }
1513
1514 return cg_path_decode_unit(e, slice);
1515 }
1516
1517 e = p;
1518 p += n;
1519 }
1520 }
1521
1522 int cg_pid_get_slice(pid_t pid, char **slice) {
1523 _cleanup_free_ char *cgroup = NULL;
1524 int r;
1525
1526 assert(slice);
1527
1528 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1529 if (r < 0)
1530 return r;
1531
1532 return cg_path_get_slice(cgroup, slice);
1533 }
1534
1535 int cg_path_get_user_slice(const char *p, char **slice) {
1536 const char *t;
1537 assert(p);
1538 assert(slice);
1539
1540 t = skip_user_prefix(p);
1541 if (!t)
1542 return -ENXIO;
1543
1544 /* And now it looks pretty much the same as for a system
1545 * slice, so let's just use the same parser from here on. */
1546 return cg_path_get_slice(t, slice);
1547 }
1548
1549 int cg_pid_get_user_slice(pid_t pid, char **slice) {
1550 _cleanup_free_ char *cgroup = NULL;
1551 int r;
1552
1553 assert(slice);
1554
1555 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1556 if (r < 0)
1557 return r;
1558
1559 return cg_path_get_user_slice(cgroup, slice);
1560 }
1561
1562 char *cg_escape(const char *p) {
1563 bool need_prefix = false;
1564
1565 /* This implements very minimal escaping for names to be used
1566 * as file names in the cgroup tree: any name which might
1567 * conflict with a kernel name or is prefixed with '_' is
1568 * prefixed with a '_'. That way, when reading cgroup names it
1569 * is sufficient to remove a single prefixing underscore if
1570 * there is one. */
1571
1572 /* The return value of this function (unlike cg_unescape())
1573 * needs free()! */
1574
1575 if (p[0] == 0 ||
1576 p[0] == '_' ||
1577 p[0] == '.' ||
1578 streq(p, "notify_on_release") ||
1579 streq(p, "release_agent") ||
1580 streq(p, "tasks"))
1581 need_prefix = true;
1582 else {
1583 const char *dot;
1584
1585 dot = strrchr(p, '.');
1586 if (dot) {
1587
1588 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1589 need_prefix = true;
1590 else {
1591 char *n;
1592
1593 n = strndupa(p, dot - p);
1594
1595 if (check_hierarchy(n) >= 0)
1596 need_prefix = true;
1597 }
1598 }
1599 }
1600
1601 if (need_prefix)
1602 return strappend("_", p);
1603 else
1604 return strdup(p);
1605 }
1606
1607 char *cg_unescape(const char *p) {
1608 assert(p);
1609
1610 /* The return value of this function (unlike cg_escape())
1611 * doesn't need free()! */
1612
1613 if (p[0] == '_')
1614 return (char*) p+1;
1615
1616 return (char*) p;
1617 }
1618
1619 #define CONTROLLER_VALID \
1620 DIGITS LETTERS \
1621 "_"
1622
1623 bool cg_controller_is_valid(const char *p) {
1624 const char *t, *s;
1625
1626 if (!p)
1627 return false;
1628
1629 s = startswith(p, "name=");
1630 if (s)
1631 p = s;
1632
1633 if (*p == 0 || *p == '_')
1634 return false;
1635
1636 for (t = p; *t; t++)
1637 if (!strchr(CONTROLLER_VALID, *t))
1638 return false;
1639
1640 if (t - p > FILENAME_MAX)
1641 return false;
1642
1643 return true;
1644 }
1645
1646 int cg_slice_to_path(const char *unit, char **ret) {
1647 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1648 const char *dash;
1649 int r;
1650
1651 assert(unit);
1652 assert(ret);
1653
1654 if (streq(unit, "-.slice")) {
1655 char *x;
1656
1657 x = strdup("");
1658 if (!x)
1659 return -ENOMEM;
1660 *ret = x;
1661 return 0;
1662 }
1663
1664 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
1665 return -EINVAL;
1666
1667 if (!endswith(unit, ".slice"))
1668 return -EINVAL;
1669
1670 r = unit_name_to_prefix(unit, &p);
1671 if (r < 0)
1672 return r;
1673
1674 dash = strchr(p, '-');
1675
1676 /* Don't allow initial dashes */
1677 if (dash == p)
1678 return -EINVAL;
1679
1680 while (dash) {
1681 _cleanup_free_ char *escaped = NULL;
1682 char n[dash - p + sizeof(".slice")];
1683
1684 /* Don't allow trailing or double dashes */
1685 if (dash[1] == 0 || dash[1] == '-')
1686 return -EINVAL;
1687
1688 strcpy(stpncpy(n, p, dash - p), ".slice");
1689 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
1690 return -EINVAL;
1691
1692 escaped = cg_escape(n);
1693 if (!escaped)
1694 return -ENOMEM;
1695
1696 if (!strextend(&s, escaped, "/", NULL))
1697 return -ENOMEM;
1698
1699 dash = strchr(dash+1, '-');
1700 }
1701
1702 e = cg_escape(unit);
1703 if (!e)
1704 return -ENOMEM;
1705
1706 if (!strextend(&s, e, NULL))
1707 return -ENOMEM;
1708
1709 *ret = s;
1710 s = NULL;
1711
1712 return 0;
1713 }
1714
1715 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1716 _cleanup_free_ char *p = NULL;
1717 int r;
1718
1719 r = cg_get_path(controller, path, attribute, &p);
1720 if (r < 0)
1721 return r;
1722
1723 return write_string_file(p, value, 0);
1724 }
1725
1726 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1727 _cleanup_free_ char *p = NULL;
1728 int r;
1729
1730 r = cg_get_path(controller, path, attribute, &p);
1731 if (r < 0)
1732 return r;
1733
1734 return read_one_line_file(p, ret);
1735 }
1736
1737 static const char mask_names[] =
1738 "cpu\0"
1739 "cpuacct\0"
1740 "blkio\0"
1741 "memory\0"
1742 "devices\0";
1743
1744 int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
1745 CGroupControllerMask bit = 1;
1746 const char *n;
1747 int r;
1748
1749 /* This one will create a cgroup in our private tree, but also
1750 * duplicate it in the trees specified in mask, and remove it
1751 * in all others */
1752
1753 /* First create the cgroup in our own hierarchy. */
1754 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1755 if (r < 0)
1756 return r;
1757
1758 /* Then, do the same in the other hierarchies */
1759 NULSTR_FOREACH(n, mask_names) {
1760 if (mask & bit)
1761 cg_create(n, path);
1762 else if (supported & bit)
1763 cg_trim(n, path, true);
1764
1765 bit <<= 1;
1766 }
1767
1768 return 0;
1769 }
1770
1771 int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
1772 CGroupControllerMask bit = 1;
1773 const char *n;
1774 int r;
1775
1776 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
1777 if (r < 0)
1778 return r;
1779
1780 NULSTR_FOREACH(n, mask_names) {
1781
1782 if (supported & bit) {
1783 const char *p = NULL;
1784
1785 if (path_callback)
1786 p = path_callback(bit, userdata);
1787
1788 if (!p)
1789 p = path;
1790
1791 cg_attach_fallback(n, p, pid);
1792 }
1793
1794 bit <<= 1;
1795 }
1796
1797 return 0;
1798 }
1799
1800 int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
1801 Iterator i;
1802 void *pidp;
1803 int r = 0;
1804
1805 SET_FOREACH(pidp, pids, i) {
1806 pid_t pid = PTR_TO_LONG(pidp);
1807 int q;
1808
1809 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
1810 if (q < 0)
1811 r = q;
1812 }
1813
1814 return r;
1815 }
1816
1817 int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
1818 CGroupControllerMask bit = 1;
1819 const char *n;
1820 int r;
1821
1822 if (!path_equal(from, to)) {
1823 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1824 if (r < 0)
1825 return r;
1826 }
1827
1828 NULSTR_FOREACH(n, mask_names) {
1829 if (supported & bit) {
1830 const char *p = NULL;
1831
1832 if (to_callback)
1833 p = to_callback(bit, userdata);
1834
1835 if (!p)
1836 p = to;
1837
1838 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1839 }
1840
1841 bit <<= 1;
1842 }
1843
1844 return 0;
1845 }
1846
1847 int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
1848 CGroupControllerMask bit = 1;
1849 const char *n;
1850 int r;
1851
1852 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1853 if (r < 0)
1854 return r;
1855
1856 NULSTR_FOREACH(n, mask_names) {
1857 if (supported & bit)
1858 cg_trim(n, path, delete_root);
1859
1860 bit <<= 1;
1861 }
1862
1863 return 0;
1864 }
1865
1866 CGroupControllerMask cg_mask_supported(void) {
1867 CGroupControllerMask bit = 1, mask = 0;
1868 const char *n;
1869
1870 NULSTR_FOREACH(n, mask_names) {
1871 if (check_hierarchy(n) >= 0)
1872 mask |= bit;
1873
1874 bit <<= 1;
1875 }
1876
1877 return mask;
1878 }
1879
1880 int cg_kernel_controllers(Set *controllers) {
1881 _cleanup_fclose_ FILE *f = NULL;
1882 char buf[LINE_MAX];
1883 int r;
1884
1885 assert(controllers);
1886
1887 /* Determines the full list of kernel-known controllers. Might
1888 * include controllers we don't actually support, arbitrary
1889 * named hierarchies and controllers that aren't currently
1890 * accessible (because not mounted). */
1891
1892 f = fopen("/proc/cgroups", "re");
1893 if (!f) {
1894 if (errno == ENOENT)
1895 return 0;
1896 return -errno;
1897 }
1898
1899 /* Ignore the header line */
1900 (void) fgets(buf, sizeof(buf), f);
1901
1902 for (;;) {
1903 char *controller;
1904 int enabled = 0;
1905
1906 errno = 0;
1907 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
1908
1909 if (feof(f))
1910 break;
1911
1912 if (ferror(f) && errno != 0)
1913 return -errno;
1914
1915 return -EBADMSG;
1916 }
1917
1918 if (!enabled) {
1919 free(controller);
1920 continue;
1921 }
1922
1923 if (!filename_is_valid(controller)) {
1924 free(controller);
1925 return -EBADMSG;
1926 }
1927
1928 r = set_consume(controllers, controller);
1929 if (r < 0)
1930 return r;
1931 }
1932
1933 return 0;
1934 }