chiark / gitweb /
8ceb382820d8e9adea51df7199b79567442b43c4
[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 Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <ftw.h>
31
32 #include "cgroup-util.h"
33 #include "log.h"
34 #include "set.h"
35 #include "macro.h"
36 #include "util.h"
37
38 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
39         char *fs;
40         int r;
41         FILE *f;
42
43         assert(path);
44         assert(_f);
45
46         r = cg_get_path(controller, path, "cgroup.procs", &fs);
47         if (r < 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(path);
66         assert(_f);
67
68         r = cg_get_path(controller, path, "tasks", &fs);
69         if (r < 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(path);
110         assert(_d);
111
112         /* This is not recursive! */
113
114         r = cg_get_path(controller, path, NULL, &fs);
115         if (r < 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 static const char *normalize_controller(const char *controller) {
506
507         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
508                 return "systemd";
509         else if (startswith(controller, "name="))
510                 return controller + 5;
511         else
512                 return controller;
513 }
514
515 static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
516         char *t;
517
518         if (!(controller || path))
519                 return -EINVAL;
520
521         if (controller) {
522                 if (path && suffix)
523                         t = join("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
524                 else if (path)
525                         t = join("/sys/fs/cgroup/", controller, "/", path, NULL);
526                 else if (suffix)
527                         t = join("/sys/fs/cgroup/", controller, "/", suffix, NULL);
528                 else
529                         t = join("/sys/fs/cgroup/", controller, NULL);
530         } else {
531                 if (path && suffix)
532                         t = join(path, "/", suffix, NULL);
533                 else if (path)
534                         t = strdup(path);
535         }
536
537         if (!t)
538                 return -ENOMEM;
539
540         path_kill_slashes(t);
541
542         *fs = t;
543         return 0;
544 }
545
546 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
547         const char *p;
548         static __thread bool good = false;
549
550         assert(fs);
551
552         if (_unlikely_(!good)) {
553                 int r;
554
555                 r = path_is_mount_point("/sys/fs/cgroup", false);
556                 if (r <= 0)
557                         return r < 0 ? r : -ENOENT;
558
559                 /* Cache this to save a few stat()s */
560                 good = true;
561         }
562
563         p = controller ? normalize_controller(controller) : NULL;
564         return join_path(p, path, suffix, fs);
565 }
566
567 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
568         const char *p;
569         char *cc;
570
571         assert(controller);
572         assert(fs);
573
574         if (isempty(controller))
575                 return -EINVAL;
576
577         p = normalize_controller(controller);
578
579         /* Check if this controller actually really exists */
580         cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
581         strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
582         if (access(cc, F_OK) < 0)
583                 return -errno;
584
585         return join_path(p, path, suffix, fs);
586 }
587
588 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
589         char *p;
590         bool is_sticky;
591
592         if (typeflag != FTW_DP)
593                 return 0;
594
595         if (ftwbuf->level < 1)
596                 return 0;
597
598         p = strappend(path, "/tasks");
599         if (!p) {
600                 errno = ENOMEM;
601                 return 1;
602         }
603
604         is_sticky = file_is_priv_sticky(p) > 0;
605         free(p);
606
607         if (is_sticky)
608                 return 0;
609
610         rmdir(path);
611         return 0;
612 }
613
614 int cg_trim(const char *controller, const char *path, bool delete_root) {
615         char *fs;
616         int r = 0;
617
618         assert(controller);
619         assert(path);
620
621         r = cg_get_path(controller, path, NULL, &fs);
622         if (r < 0)
623                 return r;
624
625         errno = 0;
626         if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
627                 r = errno ? -errno : -EIO;
628
629         if (delete_root) {
630                 bool is_sticky;
631                 char *p;
632
633                 p = strappend(fs, "/tasks");
634                 if (!p) {
635                         free(fs);
636                         return -ENOMEM;
637                 }
638
639                 is_sticky = file_is_priv_sticky(p) > 0;
640                 free(p);
641
642                 if (!is_sticky)
643                         if (rmdir(fs) < 0 && errno != ENOENT) {
644                                 if (r == 0)
645                                         r = -errno;
646                         }
647         }
648
649         free(fs);
650
651         return r;
652 }
653
654 int cg_delete(const char *controller, const char *path) {
655         char *parent;
656         int r;
657
658         assert(controller);
659         assert(path);
660
661         if ((r = parent_of_path(path, &parent)) < 0)
662                 return r;
663
664         r = cg_migrate_recursive(controller, path, parent, false, true);
665         free(parent);
666
667         return r == -ENOENT ? 0 : r;
668 }
669
670 int cg_attach(const char *controller, const char *path, pid_t pid) {
671         char *fs;
672         int r;
673         char c[32];
674
675         assert(controller);
676         assert(path);
677         assert(pid >= 0);
678
679         r = cg_get_path_and_check(controller, path, "tasks", &fs);
680         if (r < 0)
681                 return r;
682
683         if (pid == 0)
684                 pid = getpid();
685
686         snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
687         char_array_0(c);
688
689         r = write_one_line_file(fs, c);
690         free(fs);
691
692         return r;
693 }
694
695 int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
696         char *fs;
697         int r;
698
699         assert(controller);
700         assert(path);
701
702         if (mode != (mode_t) -1)
703                 mode &= 0777;
704
705         r = cg_get_path(controller, path, NULL, &fs);
706         if (r < 0)
707                 return r;
708
709         r = chmod_and_chown(fs, mode, uid, gid);
710         free(fs);
711
712         return r;
713 }
714
715 int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
716         char *fs;
717         int r;
718
719         assert(controller);
720         assert(path);
721
722         if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
723                 return 0;
724
725         if (mode != (mode_t) -1)
726                 mode &= 0666;
727
728         r = cg_get_path(controller, path, "tasks", &fs);
729         if (r < 0)
730                 return r;
731
732         if (sticky >= 0 && mode != (mode_t) -1)
733                 /* Both mode and sticky param are passed */
734                 mode |= (sticky ? S_ISVTX : 0);
735         else if ((sticky >= 0 && mode == (mode_t) -1) ||
736                  (mode != (mode_t) -1 && sticky < 0)) {
737                 struct stat st;
738
739                 /* Only one param is passed, hence read the current
740                  * mode from the file itself */
741
742                 r = lstat(fs, &st);
743                 if (r < 0) {
744                         free(fs);
745                         return -errno;
746                 }
747
748                 if (mode == (mode_t) -1)
749                         /* No mode set, we just shall set the sticky bit */
750                         mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
751                 else
752                         /* Only mode set, leave sticky bit untouched */
753                         mode = (st.st_mode & ~0777) | mode;
754         }
755
756         r = chmod_and_chown(fs, mode, uid, gid);
757         free(fs);
758
759         return r;
760 }
761
762 int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
763         int r;
764         char *p = NULL;
765         FILE *f;
766         char *fs;
767         size_t cs;
768
769         assert(controller);
770         assert(path);
771         assert(pid >= 0);
772
773         if (pid == 0)
774                 pid = getpid();
775
776         if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
777                 return -ENOMEM;
778
779         f = fopen(fs, "re");
780         free(fs);
781
782         if (!f)
783                 return errno == ENOENT ? -ESRCH : -errno;
784
785         cs = strlen(controller);
786
787         while (!feof(f)) {
788                 char line[LINE_MAX];
789                 char *l;
790
791                 errno = 0;
792                 if (!(fgets(line, sizeof(line), f))) {
793                         if (feof(f))
794                                 break;
795
796                         r = errno ? -errno : -EIO;
797                         goto finish;
798                 }
799
800                 truncate_nl(line);
801
802                 if (!(l = strchr(line, ':')))
803                         continue;
804
805                 l++;
806                 if (strncmp(l, controller, cs) != 0)
807                         continue;
808
809                 if (l[cs] != ':')
810                         continue;
811
812                 if (!(p = strdup(l + cs + 1))) {
813                         r = -ENOMEM;
814                         goto finish;
815                 }
816
817                 *path = p;
818                 r = 0;
819                 goto finish;
820         }
821
822         r = -ENOENT;
823
824 finish:
825         fclose(f);
826
827         return r;
828 }
829
830 int cg_install_release_agent(const char *controller, const char *agent) {
831         char *fs = NULL, *contents = NULL, *line = NULL, *sc;
832         int r;
833
834         assert(controller);
835         assert(agent);
836
837         if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0)
838                 return r;
839
840         if ((r = read_one_line_file(fs, &contents)) < 0)
841                 goto finish;
842
843         sc = strstrip(contents);
844         if (sc[0] == 0) {
845
846                 if (asprintf(&line, "%s\n", agent) < 0) {
847                         r = -ENOMEM;
848                         goto finish;
849                 }
850
851                 if ((r = write_one_line_file(fs, line)) < 0)
852                         goto finish;
853
854         } else if (!streq(sc, agent)) {
855                 r = -EEXIST;
856                 goto finish;
857         }
858
859         free(fs);
860         fs = NULL;
861         if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0)
862                 goto finish;
863
864         free(contents);
865         contents = NULL;
866         if ((r = read_one_line_file(fs, &contents)) < 0)
867                 goto finish;
868
869         sc = strstrip(contents);
870
871         if (streq(sc, "0")) {
872                 if ((r = write_one_line_file(fs, "1\n")) < 0)
873                         goto finish;
874
875                 r = 1;
876         } else if (!streq(sc, "1")) {
877                 r = -EIO;
878                 goto finish;
879         } else
880                 r = 0;
881
882 finish:
883         free(fs);
884         free(contents);
885         free(line);
886
887         return r;
888 }
889
890 int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
891         pid_t pid = 0, self_pid;
892         int r;
893         FILE *f = NULL;
894         bool found = false;
895
896         assert(path);
897
898         r = cg_enumerate_tasks(controller, path, &f);
899         if (r < 0)
900                 return r == -ENOENT ? 1 : r;
901
902         self_pid = getpid();
903
904         while ((r = cg_read_pid(f, &pid)) > 0) {
905
906                 if (ignore_self && pid == self_pid)
907                         continue;
908
909                 found = true;
910                 break;
911         }
912
913         fclose(f);
914
915         if (r < 0)
916                 return r;
917
918         return !found;
919 }
920
921 int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
922         int r;
923         DIR *d = NULL;
924         char *fn;
925
926         assert(path);
927
928         r = cg_is_empty(controller, path, ignore_self);
929         if (r <= 0)
930                 return r;
931
932         r = cg_enumerate_subgroups(controller, path, &d);
933         if (r < 0)
934                 return r == -ENOENT ? 1 : r;
935
936         while ((r = cg_read_subgroup(d, &fn)) > 0) {
937                 char *p = NULL;
938
939                 r = asprintf(&p, "%s/%s", path, fn);
940                 free(fn);
941
942                 if (r < 0) {
943                         r = -ENOMEM;
944                         goto finish;
945                 }
946
947                 r = cg_is_empty_recursive(controller, p, ignore_self);
948                 free(p);
949
950                 if (r <= 0)
951                         goto finish;
952         }
953
954         if (r >= 0)
955                 r = 1;
956
957 finish:
958
959         if (d)
960                 closedir(d);
961
962         return r;
963 }
964
965 int cg_split_spec(const char *spec, char **controller, char **path) {
966         const char *e;
967         char *t = NULL, *u = NULL;
968
969         assert(spec);
970         assert(controller || path);
971
972         if (*spec == '/') {
973
974                 if (path) {
975                         if (!(t = strdup(spec)))
976                                 return -ENOMEM;
977
978                         *path = t;
979                 }
980
981                 if (controller)
982                         *controller = NULL;
983
984                 return 0;
985         }
986
987         if (!(e = strchr(spec, ':'))) {
988
989                 if (strchr(spec, '/') || spec[0] == 0)
990                         return -EINVAL;
991
992                 if (controller) {
993                         if (!(t = strdup(spec)))
994                                 return -ENOMEM;
995
996                         *controller = t;
997                 }
998
999                 if (path)
1000                         *path = NULL;
1001
1002                 return 0;
1003         }
1004
1005         if (e[1] != '/' ||
1006             e == spec ||
1007             memchr(spec, '/', e-spec))
1008                 return -EINVAL;
1009
1010         if (controller)
1011                 if (!(t = strndup(spec, e-spec)))
1012                         return -ENOMEM;
1013
1014         if (path)
1015                 if (!(u = strdup(e+1))) {
1016                         free(t);
1017                         return -ENOMEM;
1018                 }
1019
1020         if (controller)
1021                 *controller = t;
1022
1023         if (path)
1024                 *path = u;
1025
1026         return 0;
1027 }
1028
1029 int cg_join_spec(const char *controller, const char *path, char **spec) {
1030         assert(controller);
1031         assert(path);
1032
1033         if (!path_is_absolute(path) ||
1034             controller[0] == 0 ||
1035             strchr(controller, ':') ||
1036             strchr(controller, '/'))
1037                 return -EINVAL;
1038
1039         if (asprintf(spec, "%s:%s", controller, path) < 0)
1040                 return -ENOMEM;
1041
1042         return 0;
1043 }
1044
1045 int cg_fix_path(const char *path, char **result) {
1046         char *t, *c, *p;
1047         int r;
1048
1049         assert(path);
1050         assert(result);
1051
1052         /* First check if it already is a filesystem path */
1053         if (path_startswith(path, "/sys/fs/cgroup") &&
1054             access(path, F_OK) >= 0) {
1055
1056                 t = strdup(path);
1057                 if (!t)
1058                         return -ENOMEM;
1059
1060                 *result = t;
1061                 return 0;
1062         }
1063
1064         /* Otherwise treat it as cg spec */
1065         r = cg_split_spec(path, &c, &p);
1066         if (r < 0)
1067                 return r;
1068
1069         r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1070         free(c);
1071         free(p);
1072
1073         return r;
1074 }
1075
1076 int cg_get_user_path(char **path) {
1077         char *root, *p;
1078
1079         assert(path);
1080
1081         /* Figure out the place to put user cgroups below. We use the
1082          * same as PID 1 has but with the "/system" suffix replaced by
1083          * "/user" */
1084
1085         if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
1086                 p = strdup("/user");
1087         else {
1088                 if (endswith(root, "/system"))
1089                         root[strlen(root) - 7] = 0;
1090                 else if (streq(root, "/"))
1091                         root[0] = 0;
1092
1093                 p = strappend(root, "/user");
1094                 free(root);
1095         }
1096
1097         if (!p)
1098                 return -ENOMEM;
1099
1100         *path = p;
1101         return 0;
1102 }