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