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