chiark / gitweb /
build-sys: move *-setup out of shared to avoid selinux being pulled in
[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(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 }