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