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