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