]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/cgroup-util.c
util: split-out path-util.[ch]
[thirdparty/systemd.git] / src / shared / 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 "cgroup-util.h"
33 #include "log.h"
34 #include "set.h"
35 #include "macro.h"
36 #include "util.h"
37 #include "path-util.h"
38 #include "strv.h"
39
40 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
41 char *fs;
42 int r;
43 FILE *f;
44
45 assert(path);
46 assert(_f);
47
48 r = cg_get_path(controller, path, "cgroup.procs", &fs);
49 if (r < 0)
50 return r;
51
52 f = fopen(fs, "re");
53 free(fs);
54
55 if (!f)
56 return -errno;
57
58 *_f = f;
59 return 0;
60 }
61
62 int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
63 char *fs;
64 int r;
65 FILE *f;
66
67 assert(path);
68 assert(_f);
69
70 r = cg_get_path(controller, path, "tasks", &fs);
71 if (r < 0)
72 return r;
73
74 f = fopen(fs, "re");
75 free(fs);
76
77 if (!f)
78 return -errno;
79
80 *_f = f;
81 return 0;
82 }
83
84 int cg_read_pid(FILE *f, pid_t *_pid) {
85 unsigned long ul;
86
87 /* Note that the cgroup.procs might contain duplicates! See
88 * cgroups.txt for details. */
89
90 errno = 0;
91 if (fscanf(f, "%lu", &ul) != 1) {
92
93 if (feof(f))
94 return 0;
95
96 return errno ? -errno : -EIO;
97 }
98
99 if (ul <= 0)
100 return -EIO;
101
102 *_pid = (pid_t) ul;
103 return 1;
104 }
105
106 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
107 char *fs;
108 int r;
109 DIR *d;
110
111 assert(path);
112 assert(_d);
113
114 /* This is not recursive! */
115
116 r = cg_get_path(controller, path, NULL, &fs);
117 if (r < 0)
118 return r;
119
120 d = opendir(fs);
121 free(fs);
122
123 if (!d)
124 return -errno;
125
126 *_d = d;
127 return 0;
128 }
129
130 int cg_read_subgroup(DIR *d, char **fn) {
131 struct dirent *de;
132
133 assert(d);
134
135 errno = 0;
136 while ((de = readdir(d))) {
137 char *b;
138
139 if (de->d_type != DT_DIR)
140 continue;
141
142 if (streq(de->d_name, ".") ||
143 streq(de->d_name, ".."))
144 continue;
145
146 if (!(b = strdup(de->d_name)))
147 return -ENOMEM;
148
149 *fn = b;
150 return 1;
151 }
152
153 if (errno)
154 return -errno;
155
156 return 0;
157 }
158
159 int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
160 char *p;
161 int r;
162
163 r = cg_get_path(controller, path, NULL, &p);
164 if (r < 0)
165 return r;
166
167 if (honour_sticky) {
168 char *tasks;
169
170 /* If the sticky bit is set don't remove the directory */
171
172 tasks = strappend(p, "/tasks");
173 if (!tasks) {
174 free(p);
175 return -ENOMEM;
176 }
177
178 r = file_is_priv_sticky(tasks);
179 free(tasks);
180
181 if (r > 0) {
182 free(p);
183 return 0;
184 }
185 }
186
187 r = rmdir(p);
188 free(p);
189
190 return (r < 0 && errno != ENOENT) ? -errno : 0;
191 }
192
193 int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
194 bool done = false;
195 int r, ret = 0;
196 pid_t my_pid;
197 FILE *f = NULL;
198 Set *allocated_set = NULL;
199
200 assert(controller);
201 assert(path);
202 assert(sig >= 0);
203
204 /* This goes through the tasks list and kills them all. This
205 * is repeated until no further processes are added to the
206 * tasks list, to properly handle forking processes */
207
208 if (!s)
209 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
210 return -ENOMEM;
211
212 my_pid = getpid();
213
214 do {
215 pid_t pid = 0;
216 done = true;
217
218 if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
219 if (ret >= 0 && r != -ENOENT)
220 ret = r;
221
222 goto finish;
223 }
224
225 while ((r = cg_read_pid(f, &pid)) > 0) {
226
227 if (pid == my_pid && ignore_self)
228 continue;
229
230 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
231 continue;
232
233 /* If we haven't killed this process yet, kill
234 * it */
235 if (kill(pid, sig) < 0) {
236 if (ret >= 0 && errno != ESRCH)
237 ret = -errno;
238 } else if (ret == 0) {
239
240 if (sigcont)
241 kill(pid, SIGCONT);
242
243 ret = 1;
244 }
245
246 done = false;
247
248 if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
249 if (ret >= 0)
250 ret = r;
251
252 goto finish;
253 }
254 }
255
256 if (r < 0) {
257 if (ret >= 0)
258 ret = r;
259
260 goto finish;
261 }
262
263 fclose(f);
264 f = NULL;
265
266 /* To avoid racing against processes which fork
267 * quicker than we can kill them we repeat this until
268 * no new pids need to be killed. */
269
270 } while (!done);
271
272 finish:
273 if (allocated_set)
274 set_free(allocated_set);
275
276 if (f)
277 fclose(f);
278
279 return ret;
280 }
281
282 int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
283 int r, ret = 0;
284 DIR *d = NULL;
285 char *fn;
286 Set *allocated_set = NULL;
287
288 assert(path);
289 assert(controller);
290 assert(sig >= 0);
291
292 if (!s)
293 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
294 return -ENOMEM;
295
296 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
297
298 if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
299 if (ret >= 0 && r != -ENOENT)
300 ret = r;
301
302 goto finish;
303 }
304
305 while ((r = cg_read_subgroup(d, &fn)) > 0) {
306 char *p = NULL;
307
308 r = asprintf(&p, "%s/%s", path, fn);
309 free(fn);
310
311 if (r < 0) {
312 if (ret >= 0)
313 ret = -ENOMEM;
314
315 goto finish;
316 }
317
318 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
319 free(p);
320
321 if (r != 0 && ret >= 0)
322 ret = r;
323 }
324
325 if (r < 0 && ret >= 0)
326 ret = r;
327
328 if (rem)
329 if ((r = cg_rmdir(controller, path, true)) < 0) {
330 if (ret >= 0 &&
331 r != -ENOENT &&
332 r != -EBUSY)
333 ret = r;
334 }
335
336 finish:
337 if (d)
338 closedir(d);
339
340 if (allocated_set)
341 set_free(allocated_set);
342
343 return ret;
344 }
345
346 int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
347 unsigned i;
348
349 assert(path);
350 assert(controller);
351
352 /* This safely kills all processes; first it sends a SIGTERM,
353 * then checks 8 times after 200ms whether the group is now
354 * empty, then kills everything that is left with SIGKILL and
355 * finally checks 5 times after 200ms each whether the group
356 * is finally empty. */
357
358 for (i = 0; i < 15; i++) {
359 int sig, r;
360
361 if (i <= 0)
362 sig = SIGTERM;
363 else if (i == 9)
364 sig = SIGKILL;
365 else
366 sig = 0;
367
368 if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
369 return r;
370
371 usleep(200 * USEC_PER_MSEC);
372 }
373
374 return 0;
375 }
376
377 int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) {
378 bool done = false;
379 Set *s;
380 int r, ret = 0;
381 pid_t my_pid;
382 FILE *f = NULL;
383
384 assert(controller);
385 assert(from);
386 assert(to);
387
388 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
389 return -ENOMEM;
390
391 my_pid = getpid();
392
393 do {
394 pid_t pid = 0;
395 done = true;
396
397 if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
398 if (ret >= 0 && r != -ENOENT)
399 ret = r;
400
401 goto finish;
402 }
403
404 while ((r = cg_read_pid(f, &pid)) > 0) {
405
406 /* This might do weird stuff if we aren't a
407 * single-threaded program. However, we
408 * luckily know we are not */
409 if (pid == my_pid && ignore_self)
410 continue;
411
412 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
413 continue;
414
415 if ((r = cg_attach(controller, to, pid)) < 0) {
416 if (ret >= 0 && r != -ESRCH)
417 ret = r;
418 } else if (ret == 0)
419 ret = 1;
420
421 done = false;
422
423 if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
424 if (ret >= 0)
425 ret = r;
426
427 goto finish;
428 }
429 }
430
431 if (r < 0) {
432 if (ret >= 0)
433 ret = r;
434
435 goto finish;
436 }
437
438 fclose(f);
439 f = NULL;
440
441 } while (!done);
442
443 finish:
444 set_free(s);
445
446 if (f)
447 fclose(f);
448
449 return ret;
450 }
451
452 int cg_migrate_recursive(const char *controller, const char *from, const char *to, bool ignore_self, bool rem) {
453 int r, ret = 0;
454 DIR *d = NULL;
455 char *fn;
456
457 assert(controller);
458 assert(from);
459 assert(to);
460
461 ret = cg_migrate(controller, from, to, ignore_self);
462
463 if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) {
464 if (ret >= 0 && r != -ENOENT)
465 ret = r;
466 goto finish;
467 }
468
469 while ((r = cg_read_subgroup(d, &fn)) > 0) {
470 char *p = NULL;
471
472 r = asprintf(&p, "%s/%s", from, fn);
473 free(fn);
474
475 if (r < 0) {
476 if (ret >= 0)
477 ret = -ENOMEM;
478
479 goto finish;
480 }
481
482 r = cg_migrate_recursive(controller, p, to, ignore_self, rem);
483 free(p);
484
485 if (r != 0 && ret >= 0)
486 ret = r;
487 }
488
489 if (r < 0 && ret >= 0)
490 ret = r;
491
492 if (rem)
493 if ((r = cg_rmdir(controller, from, true)) < 0) {
494 if (ret >= 0 &&
495 r != -ENOENT &&
496 r != -EBUSY)
497 ret = r;
498 }
499
500 finish:
501 if (d)
502 closedir(d);
503
504 return ret;
505 }
506
507 static const char *normalize_controller(const char *controller) {
508
509 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
510 return "systemd";
511 else if (startswith(controller, "name="))
512 return controller + 5;
513 else
514 return controller;
515 }
516
517 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
518 char *t = NULL;
519
520 if (!(controller || path))
521 return -EINVAL;
522
523 if (controller) {
524 if (path && suffix)
525 t = join("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
526 else if (path)
527 t = join("/sys/fs/cgroup/", controller, "/", path, NULL);
528 else if (suffix)
529 t = join("/sys/fs/cgroup/", controller, "/", suffix, NULL);
530 else
531 t = join("/sys/fs/cgroup/", controller, NULL);
532 } else {
533 if (path && suffix)
534 t = join(path, "/", suffix, NULL);
535 else if (path)
536 t = strdup(path);
537 }
538
539 if (!t)
540 return -ENOMEM;
541
542 path_kill_slashes(t);
543
544 *fs = t;
545 return 0;
546 }
547
548 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
549 const char *p;
550 static __thread bool good = false;
551
552 assert(fs);
553
554 if (_unlikely_(!good)) {
555 int r;
556
557 r = path_is_mount_point("/sys/fs/cgroup", false);
558 if (r <= 0)
559 return r < 0 ? r : -ENOENT;
560
561 /* Cache this to save a few stat()s */
562 good = true;
563 }
564
565 p = controller ? normalize_controller(controller) : NULL;
566 return join_path(p, path, suffix, fs);
567 }
568
569 static int check(const char *p) {
570 char *cc;
571
572 assert(p);
573
574 /* Check if this controller actually really exists */
575 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
576 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
577 if (access(cc, F_OK) < 0)
578 return -errno;
579
580 return 0;
581 }
582
583 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
584 const char *p;
585 int r;
586
587 assert(controller);
588 assert(fs);
589
590 if (isempty(controller))
591 return -EINVAL;
592
593 /* Normalize the controller syntax */
594 p = normalize_controller(controller);
595
596 /* Check if this controller actually really exists */
597 r = check(p);
598 if (r < 0)
599 return r;
600
601 return join_path(p, path, suffix, fs);
602 }
603
604 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
605 char *p;
606 bool is_sticky;
607
608 if (typeflag != FTW_DP)
609 return 0;
610
611 if (ftwbuf->level < 1)
612 return 0;
613
614 p = strappend(path, "/tasks");
615 if (!p) {
616 errno = ENOMEM;
617 return 1;
618 }
619
620 is_sticky = file_is_priv_sticky(p) > 0;
621 free(p);
622
623 if (is_sticky)
624 return 0;
625
626 rmdir(path);
627 return 0;
628 }
629
630 int cg_trim(const char *controller, const char *path, bool delete_root) {
631 char *fs;
632 int r = 0;
633
634 assert(controller);
635 assert(path);
636
637 r = cg_get_path(controller, path, NULL, &fs);
638 if (r < 0)
639 return r;
640
641 errno = 0;
642 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
643 r = errno ? -errno : -EIO;
644
645 if (delete_root) {
646 bool is_sticky;
647 char *p;
648
649 p = strappend(fs, "/tasks");
650 if (!p) {
651 free(fs);
652 return -ENOMEM;
653 }
654
655 is_sticky = file_is_priv_sticky(p) > 0;
656 free(p);
657
658 if (!is_sticky)
659 if (rmdir(fs) < 0 && errno != ENOENT) {
660 if (r == 0)
661 r = -errno;
662 }
663 }
664
665 free(fs);
666
667 return r;
668 }
669
670 int cg_delete(const char *controller, const char *path) {
671 char *parent;
672 int r;
673
674 assert(controller);
675 assert(path);
676
677 if ((r = path_get_parent(path, &parent)) < 0)
678 return r;
679
680 r = cg_migrate_recursive(controller, path, parent, false, true);
681 free(parent);
682
683 return r == -ENOENT ? 0 : r;
684 }
685
686 int cg_attach(const char *controller, const char *path, pid_t pid) {
687 char *fs;
688 int r;
689 char c[32];
690
691 assert(controller);
692 assert(path);
693 assert(pid >= 0);
694
695 r = cg_get_path_and_check(controller, path, "tasks", &fs);
696 if (r < 0)
697 return r;
698
699 if (pid == 0)
700 pid = getpid();
701
702 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
703 char_array_0(c);
704
705 r = write_one_line_file(fs, c);
706 free(fs);
707
708 return r;
709 }
710
711 int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
712 char *fs;
713 int r;
714
715 assert(controller);
716 assert(path);
717
718 if (mode != (mode_t) -1)
719 mode &= 0777;
720
721 r = cg_get_path(controller, path, NULL, &fs);
722 if (r < 0)
723 return r;
724
725 r = chmod_and_chown(fs, mode, uid, gid);
726 free(fs);
727
728 return r;
729 }
730
731 int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
732 char *fs;
733 int r;
734
735 assert(controller);
736 assert(path);
737
738 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
739 return 0;
740
741 if (mode != (mode_t) -1)
742 mode &= 0666;
743
744 r = cg_get_path(controller, path, "tasks", &fs);
745 if (r < 0)
746 return r;
747
748 if (sticky >= 0 && mode != (mode_t) -1)
749 /* Both mode and sticky param are passed */
750 mode |= (sticky ? S_ISVTX : 0);
751 else if ((sticky >= 0 && mode == (mode_t) -1) ||
752 (mode != (mode_t) -1 && sticky < 0)) {
753 struct stat st;
754
755 /* Only one param is passed, hence read the current
756 * mode from the file itself */
757
758 r = lstat(fs, &st);
759 if (r < 0) {
760 free(fs);
761 return -errno;
762 }
763
764 if (mode == (mode_t) -1)
765 /* No mode set, we just shall set the sticky bit */
766 mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
767 else
768 /* Only mode set, leave sticky bit untouched */
769 mode = (st.st_mode & ~0777) | mode;
770 }
771
772 r = chmod_and_chown(fs, mode, uid, gid);
773 free(fs);
774
775 return r;
776 }
777
778 int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
779 int r;
780 char *p = NULL;
781 FILE *f;
782 char *fs;
783 size_t cs;
784
785 assert(controller);
786 assert(path);
787 assert(pid >= 0);
788
789 if (pid == 0)
790 pid = getpid();
791
792 if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
793 return -ENOMEM;
794
795 f = fopen(fs, "re");
796 free(fs);
797
798 if (!f)
799 return errno == ENOENT ? -ESRCH : -errno;
800
801 cs = strlen(controller);
802
803 while (!feof(f)) {
804 char line[LINE_MAX];
805 char *l;
806
807 errno = 0;
808 if (!(fgets(line, sizeof(line), f))) {
809 if (feof(f))
810 break;
811
812 r = errno ? -errno : -EIO;
813 goto finish;
814 }
815
816 truncate_nl(line);
817
818 if (!(l = strchr(line, ':')))
819 continue;
820
821 l++;
822 if (strncmp(l, controller, cs) != 0)
823 continue;
824
825 if (l[cs] != ':')
826 continue;
827
828 if (!(p = strdup(l + cs + 1))) {
829 r = -ENOMEM;
830 goto finish;
831 }
832
833 *path = p;
834 r = 0;
835 goto finish;
836 }
837
838 r = -ENOENT;
839
840 finish:
841 fclose(f);
842
843 return r;
844 }
845
846 int cg_install_release_agent(const char *controller, const char *agent) {
847 char *fs = NULL, *contents = NULL, *line = NULL, *sc;
848 int r;
849
850 assert(controller);
851 assert(agent);
852
853 if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0)
854 return r;
855
856 if ((r = read_one_line_file(fs, &contents)) < 0)
857 goto finish;
858
859 sc = strstrip(contents);
860 if (sc[0] == 0) {
861
862 if (asprintf(&line, "%s\n", agent) < 0) {
863 r = -ENOMEM;
864 goto finish;
865 }
866
867 if ((r = write_one_line_file(fs, line)) < 0)
868 goto finish;
869
870 } else if (!streq(sc, agent)) {
871 r = -EEXIST;
872 goto finish;
873 }
874
875 free(fs);
876 fs = NULL;
877 if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0)
878 goto finish;
879
880 free(contents);
881 contents = NULL;
882 if ((r = read_one_line_file(fs, &contents)) < 0)
883 goto finish;
884
885 sc = strstrip(contents);
886
887 if (streq(sc, "0")) {
888 if ((r = write_one_line_file(fs, "1\n")) < 0)
889 goto finish;
890
891 r = 1;
892 } else if (!streq(sc, "1")) {
893 r = -EIO;
894 goto finish;
895 } else
896 r = 0;
897
898 finish:
899 free(fs);
900 free(contents);
901 free(line);
902
903 return r;
904 }
905
906 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
907 pid_t pid = 0, self_pid;
908 int r;
909 FILE *f = NULL;
910 bool found = false;
911
912 assert(path);
913
914 r = cg_enumerate_tasks(controller, path, &f);
915 if (r < 0)
916 return r == -ENOENT ? 1 : r;
917
918 self_pid = getpid();
919
920 while ((r = cg_read_pid(f, &pid)) > 0) {
921
922 if (ignore_self && pid == self_pid)
923 continue;
924
925 found = true;
926 break;
927 }
928
929 fclose(f);
930
931 if (r < 0)
932 return r;
933
934 return !found;
935 }
936
937 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
938 int r;
939 DIR *d = NULL;
940 char *fn;
941
942 assert(path);
943
944 r = cg_is_empty(controller, path, ignore_self);
945 if (r <= 0)
946 return r;
947
948 r = cg_enumerate_subgroups(controller, path, &d);
949 if (r < 0)
950 return r == -ENOENT ? 1 : r;
951
952 while ((r = cg_read_subgroup(d, &fn)) > 0) {
953 char *p = NULL;
954
955 r = asprintf(&p, "%s/%s", path, fn);
956 free(fn);
957
958 if (r < 0) {
959 r = -ENOMEM;
960 goto finish;
961 }
962
963 r = cg_is_empty_recursive(controller, p, ignore_self);
964 free(p);
965
966 if (r <= 0)
967 goto finish;
968 }
969
970 if (r >= 0)
971 r = 1;
972
973 finish:
974
975 if (d)
976 closedir(d);
977
978 return r;
979 }
980
981 int cg_split_spec(const char *spec, char **controller, char **path) {
982 const char *e;
983 char *t = NULL, *u = NULL;
984
985 assert(spec);
986 assert(controller || path);
987
988 if (*spec == '/') {
989
990 if (path) {
991 if (!(t = strdup(spec)))
992 return -ENOMEM;
993
994 *path = t;
995 }
996
997 if (controller)
998 *controller = NULL;
999
1000 return 0;
1001 }
1002
1003 if (!(e = strchr(spec, ':'))) {
1004
1005 if (strchr(spec, '/') || spec[0] == 0)
1006 return -EINVAL;
1007
1008 if (controller) {
1009 if (!(t = strdup(spec)))
1010 return -ENOMEM;
1011
1012 *controller = t;
1013 }
1014
1015 if (path)
1016 *path = NULL;
1017
1018 return 0;
1019 }
1020
1021 if (e[1] != '/' ||
1022 e == spec ||
1023 memchr(spec, '/', e-spec))
1024 return -EINVAL;
1025
1026 if (controller)
1027 if (!(t = strndup(spec, e-spec)))
1028 return -ENOMEM;
1029
1030 if (path)
1031 if (!(u = strdup(e+1))) {
1032 free(t);
1033 return -ENOMEM;
1034 }
1035
1036 if (controller)
1037 *controller = t;
1038
1039 if (path)
1040 *path = u;
1041
1042 return 0;
1043 }
1044
1045 int cg_join_spec(const char *controller, const char *path, char **spec) {
1046 assert(controller);
1047 assert(path);
1048
1049 if (!path_is_absolute(path) ||
1050 controller[0] == 0 ||
1051 strchr(controller, ':') ||
1052 strchr(controller, '/'))
1053 return -EINVAL;
1054
1055 if (asprintf(spec, "%s:%s", controller, path) < 0)
1056 return -ENOMEM;
1057
1058 return 0;
1059 }
1060
1061 int cg_fix_path(const char *path, char **result) {
1062 char *t, *c, *p;
1063 int r;
1064
1065 assert(path);
1066 assert(result);
1067
1068 /* First check if it already is a filesystem path */
1069 if (path_startswith(path, "/sys/fs/cgroup") &&
1070 access(path, F_OK) >= 0) {
1071
1072 t = strdup(path);
1073 if (!t)
1074 return -ENOMEM;
1075
1076 *result = t;
1077 return 0;
1078 }
1079
1080 /* Otherwise treat it as cg spec */
1081 r = cg_split_spec(path, &c, &p);
1082 if (r < 0)
1083 return r;
1084
1085 r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1086 free(c);
1087 free(p);
1088
1089 return r;
1090 }
1091
1092 int cg_get_user_path(char **path) {
1093 char *root, *p;
1094
1095 assert(path);
1096
1097 /* Figure out the place to put user cgroups below. We use the
1098 * same as PID 1 has but with the "/system" suffix replaced by
1099 * "/user" */
1100
1101 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
1102 p = strdup("/user");
1103 else {
1104 if (endswith(root, "/system"))
1105 root[strlen(root) - 7] = 0;
1106 else if (streq(root, "/"))
1107 root[0] = 0;
1108
1109 p = strappend(root, "/user");
1110 free(root);
1111 }
1112
1113 if (!p)
1114 return -ENOMEM;
1115
1116 *path = p;
1117 return 0;
1118 }
1119
1120 char **cg_shorten_controllers(char **controllers) {
1121 char **f, **t;
1122
1123 controllers = strv_uniq(controllers);
1124
1125 if (!controllers)
1126 return controllers;
1127
1128 for (f = controllers, t = controllers; *f; f++) {
1129 int r;
1130 const char *p;
1131
1132 if (streq(*f, "systemd") || streq(*f, SYSTEMD_CGROUP_CONTROLLER)) {
1133 free(*f);
1134 continue;
1135 }
1136
1137 p = normalize_controller(*f);
1138
1139 r = check(p);
1140 if (r < 0) {
1141 log_debug("Controller %s is not available, removing from controllers list.", *f);
1142 free(*f);
1143 continue;
1144 }
1145
1146 *(t++) = *f;
1147 }
1148
1149 *t = NULL;
1150 return controllers;
1151 }