]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
util: introduce memdup()
[thirdparty/systemd.git] / src / shared / 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
5430f7f2
LP
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
8c6db833
LP
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
5430f7f2 16 Lesser General Public License for more details.
8c6db833 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
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>
e27796a0 30#include <ftw.h>
8c6db833
LP
31
32#include "cgroup-util.h"
33#include "log.h"
34#include "set.h"
35#include "macro.h"
36#include "util.h"
37
c6c18be3
LP
38int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
39 char *fs;
40 int r;
41 FILE *f;
42
c6c18be3
LP
43 assert(path);
44 assert(_f);
45
c3175a7f
LP
46 r = cg_get_path(controller, path, "cgroup.procs", &fs);
47 if (r < 0)
c6c18be3
LP
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
60int cg_enumerate_tasks(const char *controller, const char *path, FILE **_f) {
61 char *fs;
62 int r;
63 FILE *f;
64
c6c18be3
LP
65 assert(path);
66 assert(_f);
67
c3175a7f
LP
68 r = cg_get_path(controller, path, "tasks", &fs);
69 if (r < 0)
c6c18be3
LP
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
82int 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
35d2e7ec
LP
104int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
105 char *fs;
106 int r;
107 DIR *d;
108
35d2e7ec
LP
109 assert(path);
110 assert(_d);
111
112 /* This is not recursive! */
113
c3175a7f
LP
114 r = cg_get_path(controller, path, NULL, &fs);
115 if (r < 0)
35d2e7ec
LP
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
128int 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
ad293f5a 157int cg_rmdir(const char *controller, const char *path, bool honour_sticky) {
35d2e7ec
LP
158 char *p;
159 int r;
160
ad293f5a
LP
161 r = cg_get_path(controller, path, NULL, &p);
162 if (r < 0)
35d2e7ec
LP
163 return r;
164
ad293f5a
LP
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
8d53b453 176 r = file_is_priv_sticky(tasks);
ad293f5a
LP
177 free(tasks);
178
179 if (r > 0) {
180 free(p);
181 return 0;
182 }
183 }
184
35d2e7ec
LP
185 r = rmdir(p);
186 free(p);
187
ad293f5a 188 return (r < 0 && errno != ENOENT) ? -errno : 0;
35d2e7ec
LP
189}
190
430c18ed 191int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
35d2e7ec 192 bool done = false;
8c6db833 193 int r, ret = 0;
35d2e7ec 194 pid_t my_pid;
c6c18be3 195 FILE *f = NULL;
ca949c9d 196 Set *allocated_set = NULL;
8c6db833
LP
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
ca949c9d
LP
206 if (!s)
207 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
208 return -ENOMEM;
8c6db833
LP
209
210 my_pid = getpid();
211
212 do {
0b172489 213 pid_t pid = 0;
8c6db833
LP
214 done = true;
215
35d2e7ec 216 if ((r = cg_enumerate_processes(controller, path, &f)) < 0) {
4c633005 217 if (ret >= 0 && r != -ENOENT)
35d2e7ec
LP
218 ret = r;
219
c6c18be3 220 goto finish;
35d2e7ec 221 }
c6c18be3
LP
222
223 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833
LP
224
225 if (pid == my_pid && ignore_self)
c6c18be3 226 continue;
8c6db833 227
c6c18be3
LP
228 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
229 continue;
8c6db833
LP
230
231 /* If we haven't killed this process yet, kill
232 * it */
4c633005
LP
233 if (kill(pid, sig) < 0) {
234 if (ret >= 0 && errno != ESRCH)
8c6db833 235 ret = -errno;
430c18ed
LP
236 } else if (ret == 0) {
237
238 if (sigcont)
239 kill(pid, SIGCONT);
240
35d2e7ec 241 ret = 1;
430c18ed 242 }
8c6db833 243
8c6db833
LP
244 done = false;
245
35d2e7ec
LP
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;
8c6db833
LP
259 }
260
c6c18be3
LP
261 fclose(f);
262 f = NULL;
8c6db833
LP
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
35d2e7ec 268 } while (!done);
8c6db833 269
c6c18be3 270finish:
ca949c9d
LP
271 if (allocated_set)
272 set_free(allocated_set);
8c6db833 273
c6c18be3
LP
274 if (f)
275 fclose(f);
8c6db833 276
35d2e7ec 277 return ret;
8c6db833
LP
278}
279
430c18ed 280int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
35d2e7ec
LP
281 int r, ret = 0;
282 DIR *d = NULL;
283 char *fn;
ca949c9d 284 Set *allocated_set = NULL;
8c6db833
LP
285
286 assert(path);
287 assert(controller);
288 assert(sig >= 0);
289
ca949c9d
LP
290 if (!s)
291 if (!(s = allocated_set = set_new(trivial_hash_func, trivial_compare_func)))
292 return -ENOMEM;
293
430c18ed 294 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
8c6db833 295
35d2e7ec 296 if ((r = cg_enumerate_subgroups(controller, path, &d)) < 0) {
4c633005 297 if (ret >= 0 && r != -ENOENT)
35d2e7ec 298 ret = r;
8c6db833 299
35d2e7ec
LP
300 goto finish;
301 }
8c6db833 302
35d2e7ec
LP
303 while ((r = cg_read_subgroup(d, &fn)) > 0) {
304 char *p = NULL;
8c6db833 305
35d2e7ec
LP
306 r = asprintf(&p, "%s/%s", path, fn);
307 free(fn);
308
309 if (r < 0) {
310 if (ret >= 0)
311 ret = -ENOMEM;
8c6db833 312
35d2e7ec
LP
313 goto finish;
314 }
8c6db833 315
430c18ed 316 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
35d2e7ec 317 free(p);
8c6db833 318
35d2e7ec
LP
319 if (r != 0 && ret >= 0)
320 ret = r;
8c6db833
LP
321 }
322
35d2e7ec
LP
323 if (r < 0 && ret >= 0)
324 ret = r;
325
326 if (rem)
ad293f5a 327 if ((r = cg_rmdir(controller, path, true)) < 0) {
2db9ecac
LP
328 if (ret >= 0 &&
329 r != -ENOENT &&
330 r != -EBUSY)
35d2e7ec
LP
331 ret = r;
332 }
8c6db833 333
35d2e7ec
LP
334finish:
335 if (d)
336 closedir(d);
8c6db833 337
ca949c9d
LP
338 if (allocated_set)
339 set_free(allocated_set);
340
8c6db833
LP
341 return ret;
342}
343
35d2e7ec 344int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
8c6db833
LP
345 unsigned i;
346
347 assert(path);
348 assert(controller);
349
350 /* This safely kills all processes; first it sends a SIGTERM,
9f452741
LP
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. */
8c6db833 355
9f452741 356 for (i = 0; i < 15; i++) {
1d0ae74a 357 int sig, r;
8c6db833
LP
358
359 if (i <= 0)
360 sig = SIGTERM;
9f452741 361 else if (i == 9)
8c6db833
LP
362 sig = SIGKILL;
363 else
364 sig = 0;
365
430c18ed 366 if ((r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL)) <= 0)
8c6db833
LP
367 return r;
368
9f452741 369 usleep(200 * USEC_PER_MSEC);
8c6db833
LP
370 }
371
372 return 0;
373}
374
375int cg_migrate(const char *controller, const char *from, const char *to, bool ignore_self) {
35d2e7ec
LP
376 bool done = false;
377 Set *s;
8c6db833
LP
378 int r, ret = 0;
379 pid_t my_pid;
c6c18be3 380 FILE *f = NULL;
8c6db833
LP
381
382 assert(controller);
383 assert(from);
384 assert(to);
385
35d2e7ec
LP
386 if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
387 return -ENOMEM;
388
8c6db833
LP
389 my_pid = getpid();
390
391 do {
0b172489 392 pid_t pid = 0;
8c6db833
LP
393 done = true;
394
35d2e7ec 395 if ((r = cg_enumerate_tasks(controller, from, &f)) < 0) {
4c633005 396 if (ret >= 0 && r != -ENOENT)
35d2e7ec
LP
397 ret = r;
398
c6c18be3 399 goto finish;
35d2e7ec 400 }
c6c18be3
LP
401
402 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 403
35d2e7ec
LP
404 /* This might do weird stuff if we aren't a
405 * single-threaded program. However, we
406 * luckily know we are not */
8c6db833 407 if (pid == my_pid && ignore_self)
c6c18be3 408 continue;
8c6db833 409
35d2e7ec
LP
410 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
411 continue;
412
c6c18be3 413 if ((r = cg_attach(controller, to, pid)) < 0) {
4c633005 414 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
415 ret = r;
416 } else if (ret == 0)
417 ret = 1;
8c6db833 418
8c6db833 419 done = false;
35d2e7ec
LP
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;
8c6db833
LP
434 }
435
c6c18be3
LP
436 fclose(f);
437 f = NULL;
8c6db833 438
35d2e7ec 439 } while (!done);
8c6db833 440
c6c18be3 441finish:
35d2e7ec 442 set_free(s);
8c6db833 443
c6c18be3
LP
444 if (f)
445 fclose(f);
8c6db833 446
35d2e7ec 447 return ret;
8c6db833
LP
448}
449
35d2e7ec
LP
450int 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;
8c6db833
LP
454
455 assert(controller);
456 assert(from);
457 assert(to);
458
35d2e7ec 459 ret = cg_migrate(controller, from, to, ignore_self);
8c6db833 460
35d2e7ec 461 if ((r = cg_enumerate_subgroups(controller, from, &d)) < 0) {
4c633005 462 if (ret >= 0 && r != -ENOENT)
35d2e7ec
LP
463 ret = r;
464 goto finish;
465 }
466
467 while ((r = cg_read_subgroup(d, &fn)) > 0) {
468 char *p = NULL;
8c6db833 469
35d2e7ec
LP
470 r = asprintf(&p, "%s/%s", from, fn);
471 free(fn);
8c6db833 472
35d2e7ec
LP
473 if (r < 0) {
474 if (ret >= 0)
475 ret = -ENOMEM;
476
477 goto finish;
8c6db833
LP
478 }
479
35d2e7ec 480 r = cg_migrate_recursive(controller, p, to, ignore_self, rem);
8c6db833
LP
481 free(p);
482
35d2e7ec
LP
483 if (r != 0 && ret >= 0)
484 ret = r;
8c6db833
LP
485 }
486
35d2e7ec
LP
487 if (r < 0 && ret >= 0)
488 ret = r;
489
490 if (rem)
ad293f5a 491 if ((r = cg_rmdir(controller, from, true)) < 0) {
2db9ecac
LP
492 if (ret >= 0 &&
493 r != -ENOENT &&
494 r != -EBUSY)
35d2e7ec
LP
495 ret = r;
496 }
8c6db833 497
35d2e7ec
LP
498finish:
499 if (d)
500 closedir(d);
8c6db833
LP
501
502 return ret;
503}
504
3474ae3c
LP
505static const char *normalize_controller(const char *controller) {
506
507 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
508 return "systemd";
509 else if (startswith(controller, "name="))
510 return controller + 5;
511 else
512 return controller;
513}
514
515static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
516 char *t;
517
c3175a7f
LP
518 if (!(controller || path))
519 return -EINVAL;
520
521 if (controller) {
522 if (path && suffix)
523 t = join("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
524 else if (path)
525 t = join("/sys/fs/cgroup/", controller, "/", path, NULL);
526 else if (suffix)
527 t = join("/sys/fs/cgroup/", controller, "/", suffix, NULL);
528 else
529 t = join("/sys/fs/cgroup/", controller, NULL);
530 } else {
531 if (path && suffix)
532 t = join(path, "/", suffix, NULL);
533 else if (path)
534 t = strdup(path);
535 }
3474ae3c
LP
536
537 if (!t)
538 return -ENOMEM;
539
540 path_kill_slashes(t);
541
542 *fs = t;
543 return 0;
544}
545
8c6db833 546int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
dbd821ac 547 const char *p;
0ac10822 548 static __thread bool good = false;
8c6db833 549
dbd821ac
LP
550 assert(fs);
551
3bfc7184 552 if (_unlikely_(!good)) {
70132bd0
LP
553 int r;
554
0c85a4f3 555 r = path_is_mount_point("/sys/fs/cgroup", false);
70132bd0
LP
556 if (r <= 0)
557 return r < 0 ? r : -ENOENT;
558
559 /* Cache this to save a few stat()s */
560 good = true;
561 }
562
c3175a7f 563 p = controller ? normalize_controller(controller) : NULL;
3474ae3c
LP
564 return join_path(p, path, suffix, fs);
565}
dbd821ac 566
3474ae3c
LP
567int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
568 const char *p;
569 char *cc;
dbd821ac 570
3474ae3c
LP
571 assert(controller);
572 assert(fs);
70132bd0 573
3474ae3c
LP
574 if (isempty(controller))
575 return -EINVAL;
70132bd0 576
3474ae3c 577 p = normalize_controller(controller);
8c6db833 578
3474ae3c
LP
579 /* Check if this controller actually really exists */
580 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
581 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
582 if (access(cc, F_OK) < 0)
583 return -errno;
584
585 return join_path(p, path, suffix, fs);
8c6db833
LP
586}
587
e27796a0
LP
588static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
589 char *p;
590 bool is_sticky;
591
592 if (typeflag != FTW_DP)
593 return 0;
594
595 if (ftwbuf->level < 1)
596 return 0;
597
598 p = strappend(path, "/tasks");
599 if (!p) {
600 errno = ENOMEM;
601 return 1;
602 }
603
8d53b453 604 is_sticky = file_is_priv_sticky(p) > 0;
e27796a0
LP
605 free(p);
606
607 if (is_sticky)
608 return 0;
609
610 rmdir(path);
611 return 0;
612}
613
8c6db833
LP
614int cg_trim(const char *controller, const char *path, bool delete_root) {
615 char *fs;
e27796a0 616 int r = 0;
8c6db833
LP
617
618 assert(controller);
619 assert(path);
620
e27796a0
LP
621 r = cg_get_path(controller, path, NULL, &fs);
622 if (r < 0)
8c6db833
LP
623 return r;
624
e27796a0
LP
625 errno = 0;
626 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) < 0)
627 r = errno ? -errno : -EIO;
628
629 if (delete_root) {
630 bool is_sticky;
631 char *p;
632
633 p = strappend(fs, "/tasks");
634 if (!p) {
635 free(fs);
636 return -ENOMEM;
637 }
638
8d53b453 639 is_sticky = file_is_priv_sticky(p) > 0;
e27796a0
LP
640 free(p);
641
642 if (!is_sticky)
643 if (rmdir(fs) < 0 && errno != ENOENT) {
644 if (r == 0)
645 r = -errno;
646 }
647 }
648
8c6db833
LP
649 free(fs);
650
e27796a0 651 return r;
8c6db833
LP
652}
653
654int cg_delete(const char *controller, const char *path) {
35d2e7ec 655 char *parent;
8c6db833
LP
656 int r;
657
658 assert(controller);
659 assert(path);
660
35d2e7ec
LP
661 if ((r = parent_of_path(path, &parent)) < 0)
662 return r;
8c6db833 663
35d2e7ec
LP
664 r = cg_migrate_recursive(controller, path, parent, false, true);
665 free(parent);
8c6db833 666
4c633005 667 return r == -ENOENT ? 0 : r;
8c6db833
LP
668}
669
8c6db833 670int cg_attach(const char *controller, const char *path, pid_t pid) {
c6c18be3 671 char *fs;
8c6db833 672 int r;
c6c18be3 673 char c[32];
8c6db833
LP
674
675 assert(controller);
676 assert(path);
677 assert(pid >= 0);
678
3474ae3c
LP
679 r = cg_get_path_and_check(controller, path, "tasks", &fs);
680 if (r < 0)
c6c18be3 681 return r;
8c6db833
LP
682
683 if (pid == 0)
684 pid = getpid();
685
c6c18be3
LP
686 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
687 char_array_0(c);
8c6db833 688
c6c18be3
LP
689 r = write_one_line_file(fs, c);
690 free(fs);
8c6db833
LP
691
692 return r;
693}
694
8c6db833
LP
695int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid) {
696 char *fs;
697 int r;
698
699 assert(controller);
700 assert(path);
701
8d53b453
LP
702 if (mode != (mode_t) -1)
703 mode &= 0777;
704
705 r = cg_get_path(controller, path, NULL, &fs);
706 if (r < 0)
8c6db833
LP
707 return r;
708
709 r = chmod_and_chown(fs, mode, uid, gid);
710 free(fs);
711
712 return r;
713}
714
8d53b453 715int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid, int sticky) {
8c6db833
LP
716 char *fs;
717 int r;
718
719 assert(controller);
720 assert(path);
721
8d53b453
LP
722 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1 && sticky < 0)
723 return 0;
724
725 if (mode != (mode_t) -1)
726 mode &= 0666;
727
728 r = cg_get_path(controller, path, "tasks", &fs);
729 if (r < 0)
8c6db833
LP
730 return r;
731
8d53b453
LP
732 if (sticky >= 0 && mode != (mode_t) -1)
733 /* Both mode and sticky param are passed */
734 mode |= (sticky ? S_ISVTX : 0);
735 else if ((sticky >= 0 && mode == (mode_t) -1) ||
736 (mode != (mode_t) -1 && sticky < 0)) {
737 struct stat st;
738
739 /* Only one param is passed, hence read the current
740 * mode from the file itself */
741
742 r = lstat(fs, &st);
743 if (r < 0) {
744 free(fs);
745 return -errno;
746 }
747
748 if (mode == (mode_t) -1)
749 /* No mode set, we just shall set the sticky bit */
750 mode = (st.st_mode & ~S_ISVTX) | (sticky ? S_ISVTX : 0);
751 else
752 /* Only mode set, leave sticky bit untouched */
753 mode = (st.st_mode & ~0777) | mode;
754 }
755
8c6db833
LP
756 r = chmod_and_chown(fs, mode, uid, gid);
757 free(fs);
758
759 return r;
760}
761
762int cg_get_by_pid(const char *controller, pid_t pid, char **path) {
763 int r;
764 char *p = NULL;
c6c18be3
LP
765 FILE *f;
766 char *fs;
767 size_t cs;
8c6db833
LP
768
769 assert(controller);
8c6db833 770 assert(path);
c6c18be3 771 assert(pid >= 0);
8c6db833 772
c6c18be3
LP
773 if (pid == 0)
774 pid = getpid();
8c6db833 775
c6c18be3
LP
776 if (asprintf(&fs, "/proc/%lu/cgroup", (unsigned long) pid) < 0)
777 return -ENOMEM;
8c6db833 778
c6c18be3
LP
779 f = fopen(fs, "re");
780 free(fs);
781
4c633005
LP
782 if (!f)
783 return errno == ENOENT ? -ESRCH : -errno;
784
c6c18be3
LP
785 cs = strlen(controller);
786
787 while (!feof(f)) {
788 char line[LINE_MAX];
789 char *l;
790
791 errno = 0;
792 if (!(fgets(line, sizeof(line), f))) {
793 if (feof(f))
794 break;
795
796 r = errno ? -errno : -EIO;
797 goto finish;
798 }
799
800 truncate_nl(line);
801
802 if (!(l = strchr(line, ':')))
803 continue;
804
805 l++;
806 if (strncmp(l, controller, cs) != 0)
807 continue;
808
809 if (l[cs] != ':')
810 continue;
811
812 if (!(p = strdup(l + cs + 1))) {
813 r = -ENOMEM;
814 goto finish;
815 }
816
817 *path = p;
818 r = 0;
819 goto finish;
820 }
821
822 r = -ENOENT;
823
824finish:
825 fclose(f);
826
827 return r;
8c6db833
LP
828}
829
830int cg_install_release_agent(const char *controller, const char *agent) {
c6c18be3 831 char *fs = NULL, *contents = NULL, *line = NULL, *sc;
8c6db833
LP
832 int r;
833
834 assert(controller);
835 assert(agent);
836
c6c18be3
LP
837 if ((r = cg_get_path(controller, NULL, "release_agent", &fs)) < 0)
838 return r;
8c6db833 839
c6c18be3 840 if ((r = read_one_line_file(fs, &contents)) < 0)
8c6db833
LP
841 goto finish;
842
843 sc = strstrip(contents);
8c6db833
LP
844 if (sc[0] == 0) {
845
846 if (asprintf(&line, "%s\n", agent) < 0) {
847 r = -ENOMEM;
848 goto finish;
849 }
850
c6c18be3 851 if ((r = write_one_line_file(fs, line)) < 0)
8c6db833
LP
852 goto finish;
853
854 } else if (!streq(sc, agent)) {
855 r = -EEXIST;
856 goto finish;
857 }
858
c6c18be3
LP
859 free(fs);
860 fs = NULL;
e364ad06 861 if ((r = cg_get_path(controller, NULL, "notify_on_release", &fs)) < 0)
8c6db833 862 goto finish;
8c6db833
LP
863
864 free(contents);
865 contents = NULL;
c6c18be3 866 if ((r = read_one_line_file(fs, &contents)) < 0)
8c6db833
LP
867 goto finish;
868
869 sc = strstrip(contents);
870
871 if (streq(sc, "0")) {
c6c18be3 872 if ((r = write_one_line_file(fs, "1\n")) < 0)
8c6db833 873 goto finish;
c6c18be3
LP
874
875 r = 1;
8c6db833
LP
876 } else if (!streq(sc, "1")) {
877 r = -EIO;
878 goto finish;
c6c18be3
LP
879 } else
880 r = 0;
8c6db833
LP
881
882finish:
c6c18be3 883 free(fs);
8c6db833
LP
884 free(contents);
885 free(line);
886
887 return r;
888}
889
890int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
c3175a7f 891 pid_t pid = 0, self_pid;
8c6db833 892 int r;
0b172489 893 FILE *f = NULL;
c6c18be3 894 bool found = false;
8c6db833 895
8c6db833
LP
896 assert(path);
897
c3175a7f
LP
898 r = cg_enumerate_tasks(controller, path, &f);
899 if (r < 0)
4c633005 900 return r == -ENOENT ? 1 : r;
8c6db833 901
c3175a7f
LP
902 self_pid = getpid();
903
c6c18be3 904 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 905
c3175a7f 906 if (ignore_self && pid == self_pid)
c6c18be3 907 continue;
8c6db833 908
c6c18be3
LP
909 found = true;
910 break;
8c6db833
LP
911 }
912
c6c18be3 913 fclose(f);
8c6db833 914
c6c18be3
LP
915 if (r < 0)
916 return r;
8c6db833 917
c6c18be3 918 return !found;
8c6db833
LP
919}
920
921int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
35d2e7ec
LP
922 int r;
923 DIR *d = NULL;
924 char *fn;
8c6db833 925
8c6db833
LP
926 assert(path);
927
c3175a7f
LP
928 r = cg_is_empty(controller, path, ignore_self);
929 if (r <= 0)
35d2e7ec
LP
930 return r;
931
c3175a7f
LP
932 r = cg_enumerate_subgroups(controller, path, &d);
933 if (r < 0)
4c633005 934 return r == -ENOENT ? 1 : r;
8c6db833 935
35d2e7ec
LP
936 while ((r = cg_read_subgroup(d, &fn)) > 0) {
937 char *p = NULL;
8c6db833 938
35d2e7ec
LP
939 r = asprintf(&p, "%s/%s", path, fn);
940 free(fn);
8c6db833 941
35d2e7ec
LP
942 if (r < 0) {
943 r = -ENOMEM;
944 goto finish;
8c6db833
LP
945 }
946
35d2e7ec 947 r = cg_is_empty_recursive(controller, p, ignore_self);
8c6db833
LP
948 free(p);
949
35d2e7ec
LP
950 if (r <= 0)
951 goto finish;
952 }
953
954 if (r >= 0)
955 r = 1;
956
957finish:
958
959 if (d)
960 closedir(d);
961
962 return r;
963}
964
965int cg_split_spec(const char *spec, char **controller, char **path) {
966 const char *e;
967 char *t = NULL, *u = NULL;
968
969 assert(spec);
970 assert(controller || path);
971
972 if (*spec == '/') {
973
974 if (path) {
975 if (!(t = strdup(spec)))
976 return -ENOMEM;
977
978 *path = t;
8c6db833
LP
979 }
980
35d2e7ec
LP
981 if (controller)
982 *controller = NULL;
983
984 return 0;
8c6db833
LP
985 }
986
35d2e7ec
LP
987 if (!(e = strchr(spec, ':'))) {
988
989 if (strchr(spec, '/') || spec[0] == 0)
990 return -EINVAL;
991
992 if (controller) {
993 if (!(t = strdup(spec)))
994 return -ENOMEM;
995
996 *controller = t;
997 }
998
999 if (path)
1000 *path = NULL;
1001
1002 return 0;
8c6db833
LP
1003 }
1004
35d2e7ec
LP
1005 if (e[1] != '/' ||
1006 e == spec ||
1007 memchr(spec, '/', e-spec))
1008 return -EINVAL;
8c6db833 1009
35d2e7ec
LP
1010 if (controller)
1011 if (!(t = strndup(spec, e-spec)))
1012 return -ENOMEM;
1013
1014 if (path)
1015 if (!(u = strdup(e+1))) {
1016 free(t);
1017 return -ENOMEM;
1018 }
1019
1020 if (controller)
1021 *controller = t;
1022
1023 if (path)
1024 *path = u;
1025
1026 return 0;
8c6db833 1027}
c6c18be3 1028
35d2e7ec
LP
1029int cg_join_spec(const char *controller, const char *path, char **spec) {
1030 assert(controller);
1031 assert(path);
c6c18be3 1032
35d2e7ec
LP
1033 if (!path_is_absolute(path) ||
1034 controller[0] == 0 ||
1035 strchr(controller, ':') ||
1036 strchr(controller, '/'))
1037 return -EINVAL;
1038
1039 if (asprintf(spec, "%s:%s", controller, path) < 0)
1040 return -ENOMEM;
c6c18be3
LP
1041
1042 return 0;
1043}
35d2e7ec
LP
1044
1045int cg_fix_path(const char *path, char **result) {
1046 char *t, *c, *p;
1047 int r;
1048
1049 assert(path);
1050 assert(result);
1051
1052 /* First check if it already is a filesystem path */
1053 if (path_is_absolute(path) &&
77d5f105 1054 path_startswith(path, "/sys/fs/cgroup") &&
35d2e7ec
LP
1055 access(path, F_OK) >= 0) {
1056
1057 if (!(t = strdup(path)))
1058 return -ENOMEM;
1059
1060 *result = t;
1061 return 0;
1062 }
1063
1064 /* Otherwise treat it as cg spec */
1065 if ((r = cg_split_spec(path, &c, &p)) < 0)
1066 return r;
1067
1068 r = cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
1069 free(c);
1070 free(p);
1071
1072 return r;
1073}
1f73f0f1
LP
1074
1075int cg_get_user_path(char **path) {
1076 char *root, *p;
1077
1078 assert(path);
1079
1080 /* Figure out the place to put user cgroups below. We use the
1081 * same as PID 1 has but with the "/system" suffix replaced by
1082 * "/user" */
1083
1084 if (cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root) < 0)
1085 p = strdup("/user");
1086 else {
1087 if (endswith(root, "/system"))
1088 root[strlen(root) - 7] = 0;
1089 else if (streq(root, "/"))
1090 root[0] = 0;
1091
1092 p = strappend(root, "/user");
1093 free(root);
1094 }
1095
1096 if (!p)
1097 return -ENOMEM;
1098
1099 *path = p;
1100 return 0;
1101}