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