dbus: minor coding style fixes
[systemd_ALT/systemd_imz.git] / src / shared / cgroup-util.c
blobb0d378de5ade1e577a0c7f86cd7b294b3e0ec709
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3 /***
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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 ***/
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>
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"
40 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
41 char *fs;
42 int r;
43 FILE *f;
45 assert(path);
46 assert(_f);
48 r = cg_get_path(controller, path, "cgroup.procs", &fs);
49 if (r < 0)
50 return r;
52 f = fopen(fs, "re");
53 free(fs);
55 if (!f)
56 return -errno;
58 *_f = f;
59 return 0;
62 int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
63 char *fs;
64 int r;
65 FILE *f;
67 assert(path);
68 assert(_f);
70 r = cg_get_path(controller, path, "tasks", &fs);
71 if (r < 0)
72 return r;
74 f = fopen(fs, "re");
75 free(fs);
77 if (!f)
78 return -errno;
80 *_f = f;
81 return 0;
84 int cg_read_pid(FILE *f, pid_t *_pid) {
85 unsigned long ul;
87 /* Note that the cgroup.procs might contain duplicates! See
88 * cgroups.txt for details. */
90 errno = 0;
91 if (fscanf(f, "%lu", &ul) != 1) {
93 if (feof(f))
94 return 0;
96 return errno ? -errno : -EIO;
99 if (ul <= 0)
100 return -EIO;
102 *_pid = (pid_t) ul;
103 return 1;
106 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
107 char *fs;
108 int r;
109 DIR *d;
111 assert(path);
112 assert(_d);
114 /* This is not recursive! */
116 r = cg_get_path(controller, path, NULL, &fs);
117 if (r < 0)
118 return r;
120 d = opendir(fs);
121 free(fs);
123 if (!d)
124 return -errno;
126 *_d = d;
127 return 0;
130 int cg_read_subgroup(DIR *d, char **fn) {
131 struct dirent *de;
133 assert(d);
135 errno = 0;
136 while ((de = readdir(d))) {
137 char *b;
139 if (de->d_type != DT_DIR)
140 continue;
142 if (streq(de->d_name, ".") ||
143 streq(de->d_name, ".."))
144 continue;
146 if (!(b = strdup(de->d_name)))
147 return -ENOMEM;
149 *fn = b;
150 return 1;
153 if (errno)
154 return -errno;
156 return 0;
159 int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
160 char *p;
161 int r;
163 r = cg_get_path(controller, path, NULL, &p);
164 if (r < 0)
165 return r;
167 if (honour_sticky) {
168 char *tasks;
170 /* If the sticky bit is set don't remove the directory */
172 tasks = strappend(p, "/tasks");
173 if (!tasks) {
174 free(p);
175 return -ENOMEM;
178 r = file_is_priv_sticky(tasks);
179 free(tasks);
181 if (r > 0) {
182 free(p);
183 return 0;
187 r = rmdir(p);
188 free(p);
190 return (r < 0 && errno != ENOENT) ? -errno : 0;
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;
200 assert(controller);
201 assert(path);
202 assert(sig >= 0);
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 */
208 if (!s)
209 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
210 return -ENOMEM;
212 my_pid = getpid();
214 do {
215 pid_t pid = 0;
216 done = true;
218 if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
219 if (ret >= 0 && r != -ENOENT)
220 ret = r;
222 goto finish;
225 while ((r = cg_read_pid(f, &pid)) > 0) {
227 if (pid == my_pid && ignore_self)
228 continue;
230 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
231 continue;
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) {
240 if (sigcont)
241 kill(pid, SIGCONT);
243 ret = 1;
246 done = false;
248 if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
249 if (ret >= 0)
250 ret = r;
252 goto finish;
256 if (r < 0) {
257 if (ret >= 0)
258 ret = r;
260 goto finish;
263 fclose(f);
264 f = NULL;
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. */
270 } while (!done);
272 finish:
273 if (allocated_set)
274 set_free(allocated_set);
276 if (f)
277 fclose(f);
279 return ret;
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;
288 assert(path);
289 assert(controller);
290 assert(sig >= 0);
292 if (!s)
293 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
294 return -ENOMEM;
296 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
298 if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
299 if (ret >= 0 && r != -ENOENT)
300 ret = r;
302 goto finish;
305 while ((r = cg_read_subgroup(d, &fn)) > 0) {
306 char *p = NULL;
308 r = asprintf(&p, "%s/%s", path, fn);
309 free(fn);
311 if (r < 0) {
312 if (ret >= 0)
313 ret = -ENOMEM;
315 goto finish;
318 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
319 free(p);
321 if (r != 0 && ret >= 0)
322 ret = r;
325 if (r < 0 && ret >= 0)
326 ret = r;
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;
336 finish:
337 if (d)
338 closedir(d);
340 if (allocated_set)
341 set_free(allocated_set);
343 return ret;
346 int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
347 unsigned i;
349 assert(path);
350 assert(controller);
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. */
358 for (i = 0; i < 15; i++) {
359 int sig, r;
361 if (i <= 0)
362 sig = SIGTERM;
363 else if (i == 9)
364 sig = SIGKILL;
365 else
366 sig = 0;
368 if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
369 return r;
371 usleep(200 * USEC_PER_MSEC);
374 return 0;
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;
384 assert(controller);
385 assert(from);
386 assert(to);
388 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
389 return -ENOMEM;
391 my_pid = getpid();
393 do {
394 pid_t pid = 0;
395 done = true;
397 if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
398 if (ret >= 0 && r != -ENOENT)
399 ret = r;
401 goto finish;
404 while ((r = cg_read_pid(f, &pid)) > 0) {
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;
412 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
413 continue;
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;
421 done = false;
423 if ((r = set_put(s, LONG_TO_PTR(pid))) < 0) {
424 if (ret >= 0)
425 ret = r;
427 goto finish;
431 if (r < 0) {
432 if (ret >= 0)
433 ret = r;
435 goto finish;
438 fclose(f);
439 f = NULL;
441 } while (!done);
443 finish:
444 set_free(s);
446 if (f)
447 fclose(f);
449 return ret;
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;
457 assert(controller);
458 assert(from);
459 assert(to);
461 ret = cg_migrate(controller, from, to, ignore_self);
463 if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) {
464 if (ret >= 0 && r != -ENOENT)
465 ret = r;
466 goto finish;
469 while ((r = cg_read_subgroup(d, &fn)) > 0) {
470 char *p = NULL;
472 r = asprintf(&p, "%s/%s", from, fn);
473 free(fn);
475 if (r < 0) {
476 if (ret >= 0)
477 ret = -ENOMEM;
479 goto finish;
482 r = cg_migrate_recursive(controller, p, to, ignore_self, rem);
483 free(p);
485 if (r != 0 && ret >= 0)
486 ret = r;
489 if (r < 0 && ret >= 0)
490 ret = r;
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;
500 finish:
501 if (d)
502 closedir(d);
504 return ret;
507 static const char *normalize_controller(const char *controller) {
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;
517 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
518 char *t = NULL;
520 if (!(controller || path))
521 return -EINVAL;
523 if (controller) {
524 if (path && suffix)
525 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
526 else if (path)
527 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
528 else if (suffix)
529 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
530 else
531 t = strjoin("/sys/fs/cgroup/", controller, NULL);
532 } else {
533 if (path && suffix)
534 t = strjoin(path, "/", suffix, NULL);
535 else if (path)
536 t = strdup(path);
539 if (!t)
540 return -ENOMEM;
542 path_kill_slashes(t);
544 *fs = t;
545 return 0;
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;
552 assert(fs);
554 if (_unlikely_(!good)) {
555 int r;
557 r = path_is_mount_point("/sys/fs/cgroup", false);
558 if (r <= 0)
559 return r < 0 ? r : -ENOENT;
561 /* Cache this to save a few stat()s */
562 good = true;
565 p = controller ? normalize_controller(controller) : NULL;
566 return join_path(p, path, suffix, fs);
569 static int check(const char *p) {
570 char *cc;
572 assert(p);
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;
580 return 0;
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;
587 assert(controller);
588 assert(fs);
590 if (isempty(controller))
591 return -EINVAL;
593 /* Normalize the controller syntax */
594 p = normalize_controller(controller);
596 /* Check if this controller actually really exists */
597 r = check(p);
598 if (r < 0)
599 return r;
601 return join_path(p, path, suffix, fs);
604 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
605 char *p;
606 bool is_sticky;
608 if (typeflag != FTW_DP)
609 return 0;
611 if (ftwbuf->level < 1)
612 return 0;
614 p = strappend(path, "/tasks");
615 if (!p) {
616 errno = ENOMEM;
617 return 1;
620 is_sticky = file_is_priv_sticky(p) > 0;
621 free(p);
623 if (is_sticky)
624 return 0;
626 rmdir(path);
627 return 0;
630 int cg_trim(const char *controller, const char *path, bool delete_root) {
631 char *fs;
632 int r = 0;
634 assert(controller);
635 assert(path);
637 r = cg_get_path(controller, path, NULL, &fs);
638 if (r < 0)
639 return r;
641 errno = 0;
642 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
643 r = errno ? -errno : -EIO;
645 if (delete_root) {
646 bool is_sticky;
647 char *p;
649 p = strappend(fs, "/tasks");
650 if (!p) {
651 free(fs);
652 return -ENOMEM;
655 is_sticky = file_is_priv_sticky(p) > 0;
656 free(p);
658 if (!is_sticky)
659 if (rmdir(fs) < 0 && errno != ENOENT) {
660 if (r == 0)
661 r = -errno;
665 free(fs);
667 return r;
670 int cg_delete(const char *controller, const char *path) {
671 char *parent;
672 int r;
674 assert(controller);
675 assert(path);
677 if ((r = path_get_parent(path, &parent)) < 0)
678 return r;
680 r = cg_migrate_recursive(controller, path, parent, false, true);
681 free(parent);
683 return r == -ENOENT ? 0 : r;
686 int cg_attach(const char *controller, const char *path, pid_t pid) {
687 char *fs;
688 int r;
689 char c[32];
691 assert(controller);
692 assert(path);
693 assert(pid >= 0);
695 r = cg_get_path_and_check(controller, path, "tasks", &fs);
696 if (r < 0)
697 return r;
699 if (pid == 0)
700 pid = getpid();
702 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
703 char_array_0(c);
705 r = write_one_line_file(fs, c);
706 free(fs);
708 return r;
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;
715 assert(controller);
716 assert(path);
718 if (mode != (mode_t) -1)
719 mode &= 0777;
721 r = cg_get_path(controller, path, NULL, &fs);
722 if (r < 0)
723 return r;
725 r = chmod_and_chown(fs, mode, uid, gid);
726 free(fs);
728 return r;
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;
735 assert(controller);
736 assert(path);
738 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
739 return 0;
741 if (mode != (mode_t) -1)
742 mode &= 0666;
744 r = cg_get_path(controller, path, "tasks", &fs);
745 if (r < 0)
746 return r;
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;
755 /* Only one param is passed, hence read the current
756 * mode from the file itself */
758 r = lstat(fs, &st);
759 if (r < 0) {
760 free(fs);
761 return -errno;
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;
772 r = chmod_and_chown(fs, mode, uid, gid);
773 free(fs);
775 return r;
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;
785 assert(controller);
786 assert(path);
787 assert(pid >= 0);
789 if (pid == 0)
790 pid = getpid();
792 if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
793 return -ENOMEM;
795 f = fopen(fs, "re");
796 free(fs);
798 if (!f)
799 return errno == ENOENT ? -ESRCH : -errno;
801 cs = strlen(controller);
803 while (!feof(f)) {
804 char line[LINE_MAX];
805 char *l;
807 errno = 0;
808 if (!(fgets(line, sizeof(line), f))) {
809 if (feof(f))
810 break;
812 r = errno ? -errno : -EIO;
813 goto finish;
816 truncate_nl(line);
818 if (!(l = strchr(line, ':')))
819 continue;
821 l++;
822 if (strncmp(l, controller, cs) != 0)
823 continue;
825 if (l[cs] != ':')
826 continue;
828 if (!(p = strdup(l + cs + 1))) {
829 r = -ENOMEM;
830 goto finish;
833 *path = p;
834 r = 0;
835 goto finish;
838 r = -ENOENT;
840 finish:
841 fclose(f);
843 return r;
846 int cg_install_release_agent(const char *controller, const char *agent) {
847 char *fs = NULL, *contents = NULL, *line = NULL, *sc;
848 int r;
850 assert(controller);
851 assert(agent);
853 if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0)
854 return r;
856 if ((r = read_one_line_file(fs, &contents)) < 0)
857 goto finish;
859 sc = strstrip(contents);
860 if (sc[0] == 0) {
862 if (asprintf(&line, "%s\n", agent) < 0) {
863 r = -ENOMEM;
864 goto finish;
867 if ((r = write_one_line_file(fs, line)) < 0)
868 goto finish;
870 } else if (!streq(sc, agent)) {
871 r = -EEXIST;
872 goto finish;
875 free(fs);
876 fs = NULL;
877 if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0)
878 goto finish;
880 free(contents);
881 contents = NULL;
882 if ((r = read_one_line_file(fs, &contents)) < 0)
883 goto finish;
885 sc = strstrip(contents);
887 if (streq(sc, "0")) {
888 if ((r = write_one_line_file(fs, "1\n")) < 0)
889 goto finish;
891 r = 1;
892 } else if (!streq(sc, "1")) {
893 r = -EIO;
894 goto finish;
895 } else
896 r = 0;
898 finish:
899 free(fs);
900 free(contents);
901 free(line);
903 return r;
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;
912 assert(path);
914 r = cg_enumerate_tasks(controller, path, &f);
915 if (r < 0)
916 return r == -ENOENT ? 1 : r;
918 self_pid = getpid();
920 while ((r = cg_read_pid(f, &pid)) > 0) {
922 if (ignore_self && pid == self_pid)
923 continue;
925 found = true;
926 break;
929 fclose(f);
931 if (r < 0)
932 return r;
934 return !found;
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;
942 assert(path);
944 r = cg_is_empty(controller, path, ignore_self);
945 if (r <= 0)
946 return r;
948 r = cg_enumerate_subgroups(controller, path, &d);
949 if (r < 0)
950 return r == -ENOENT ? 1 : r;
952 while ((r = cg_read_subgroup(d, &fn)) > 0) {
953 char *p = NULL;
955 r = asprintf(&p, "%s/%s", path, fn);
956 free(fn);
958 if (r < 0) {
959 r = -ENOMEM;
960 goto finish;
963 r = cg_is_empty_recursive(controller, p, ignore_self);
964 free(p);
966 if (r <= 0)
967 goto finish;
970 if (r >= 0)
971 r = 1;
973 finish:
975 if (d)
976 closedir(d);
978 return r;
981 int cg_split_spec(const char *spec, char **controller, char **path) {
982 const char *e;
983 char *t = NULL, *u = NULL;
985 assert(spec);
986 assert(controller || path);
988 if (*spec == '/') {
990 if (path) {
991 if (!(t = strdup(spec)))
992 return -ENOMEM;
994 *path = t;
997 if (controller)
998 *controller = NULL;
1000 return 0;
1003 if (!(e = strchr(spec, ':'))) {
1005 if (strchr(spec, '/') || spec[0] == 0)
1006 return -EINVAL;
1008 if (controller) {
1009 if (!(t = strdup(spec)))
1010 return -ENOMEM;
1012 *controller = t;
1015 if (path)
1016 *path = NULL;
1018 return 0;
1021 if (e[1] != '/' ||
1022 e == spec ||
1023 memchr(spec, '/', e-spec))
1024 return -EINVAL;
1026 if (controller)
1027 if (!(t = strndup(spec, e-spec)))
1028 return -ENOMEM;
1030 if (path)
1031 if (!(u = strdup(e+1))) {
1032 free(t);
1033 return -ENOMEM;
1036 if (controller)
1037 *controller = t;
1039 if (path)
1040 *path = u;
1042 return 0;
1045 int cg_join_spec(const char *controller, const char *path, char **spec) {
1046 assert(controller);
1047 assert(path);
1049 if (!path_is_absolute(path) ||
1050 controller[0] == 0 ||
1051 strchr(controller, ':') ||
1052 strchr(controller, '/'))
1053 return -EINVAL;
1055 if (asprintf(spec, "%s:%s", controller, path) < 0)
1056 return -ENOMEM;
1058 return 0;
1061 int cg_fix_path(const char *path, char **result) {
1062 char *t, *c, *p;
1063 int r;
1065 assert(path);
1066 assert(result);
1068 /* First check if it already is a filesystem path */
1069 if (path_startswith(path, "/sys/fs/cgroup") &&
1070 access(path, F_OK) >= 0) {
1072 t = strdup(path);
1073 if (!t)
1074 return -ENOMEM;
1076 *result = t;
1077 return 0;
1080 /* Otherwise treat it as cg spec */
1081 r = cg_split_spec(path, &c, &p);
1082 if (r < 0)
1083 return r;
1085 r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1086 free(c);
1087 free(p);
1089 return r;
1092 int cg_get_user_path(char **path) {
1093 char *root, *p;
1095 assert(path);
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" */
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;
1109 p = strappend(root, "/user");
1110 free(root);
1113 if (!p)
1114 return -ENOMEM;
1116 *path = p;
1117 return 0;
1120 char **cg_shorten_controllers(char **controllers) {
1121 char **f, **t;
1123 controllers = strv_uniq(controllers);
1125 if (!controllers)
1126 return controllers;
1128 for (f = controllers, t = controllers; *f; f++) {
1129 int r;
1130 const char *p;
1132 if (streq(*f, "systemd") || streq(*f, SYSTEMD_CGROUP_CONTROLLER)) {
1133 free(*f);
1134 continue;
1137 p = normalize_controller(*f);
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;
1146 *(t++) = *f;
1149 *t = NULL;
1150 return controllers;
1153 int cg_pid_get_cgroup(pid_t pid, char **root, char **cgroup) {
1154 char *cg_process, *cg_init, *p;
1155 int r;
1157 assert(pid >= 0);
1159 if (pid == 0)
1160 pid = getpid();
1162 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
1163 if (r < 0)
1164 return r;
1166 r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &cg_init);
1167 if (r < 0) {
1168 free(cg_process);
1169 return r;
1172 if (endswith(cg_init, "/system"))
1173 cg_init[strlen(cg_init)-7] = 0;
1174 else if (streq(cg_init, "/"))
1175 cg_init[0] = 0;
1177 if (startswith(cg_process, cg_init))
1178 p = cg_process + strlen(cg_init);
1179 else
1180 p = cg_process;
1182 free(cg_init);
1184 if (cgroup) {
1185 char* c;
1187 c = strdup(p);
1188 if (!c) {
1189 free(cg_process);
1190 return -ENOMEM;
1193 *cgroup = c;
1196 if (root) {
1197 cg_process[p-cg_process] = 0;
1198 *root = cg_process;
1199 } else
1200 free(cg_process);
1202 return 0;
1205 int cg_pid_get_unit(pid_t pid, char **unit) {
1206 int r;
1207 char *cgroup, *p, *at, *b;
1208 size_t k;
1210 assert(pid >= 0);
1211 assert(unit);
1213 r = cg_pid_get_cgroup(pid, NULL, &cgroup);
1214 if (r < 0)
1215 return r;
1217 if (!startswith(cgroup, "/system/")) {
1218 free(cgroup);
1219 return -ENOENT;
1222 p = cgroup + 8;
1223 k = strcspn(p, "/");
1225 at = memchr(p, '@', k);
1226 if (at && at[1] == '.') {
1227 size_t j;
1229 /* This is a templated service */
1230 if (p[k] != '/') {
1231 free(cgroup);
1232 return -EIO;
1235 j = strcspn(p+k+1, "/");
1237 b = malloc(k + j + 1);
1239 if (b) {
1240 memcpy(b, p, at - p + 1);
1241 memcpy(b + (at - p) + 1, p + k + 1, j);
1242 memcpy(b + (at - p) + 1 + j, at + 1, k - (at - p) - 1);
1243 b[k+j] = 0;
1245 } else
1246 b = strndup(p, k);
1248 free(cgroup);
1250 if (!b)
1251 return -ENOMEM;
1253 *unit = b;
1254 return 0;