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