chiark / gitweb /
split selinux label operations out of cgroup-util, socket-util
[elogind.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 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 #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 }