]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/cgroup-util.c
cgroup: never migrate kernel threads out of the root cgroup
[thirdparty/systemd.git] / src / basic / 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"
8c6db833
LP
33#include "set.h"
34#include "macro.h"
35#include "util.h"
6482f626 36#include "formats-util.h"
0b452006 37#include "process-util.h"
9eb977db 38#include "path-util.h"
ef1673d1 39#include "unit-name.h"
a5c32cff 40#include "fileio.h"
9444b1f2 41#include "special.h"
1434ae6f 42#include "mkdir.h"
a095315b 43#include "login-util.h"
8c6db833 44
c6c18be3 45int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
7027ff61 46 _cleanup_free_ char *fs = NULL;
c6c18be3 47 FILE *f;
7027ff61 48 int r;
c6c18be3 49
c6c18be3
LP
50 assert(_f);
51
c3175a7f
LP
52 r = cg_get_path(controller, path, "cgroup.procs", &fs);
53 if (r < 0)
c6c18be3
LP
54 return r;
55
56 f = fopen(fs, "re");
c6c18be3
LP
57 if (!f)
58 return -errno;
59
60 *_f = f;
61 return 0;
62}
63
c6c18be3
LP
64int cg_read_pid(FILE *f, pid_t *_pid) {
65 unsigned long ul;
66
67 /* Note that the cgroup.procs might contain duplicates! See
68 * cgroups.txt for details. */
69
7027ff61
LP
70 assert(f);
71 assert(_pid);
72
c6c18be3
LP
73 errno = 0;
74 if (fscanf(f, "%lu", &ul) != 1) {
75
76 if (feof(f))
77 return 0;
78
79 return errno ? -errno : -EIO;
80 }
81
82 if (ul <= 0)
83 return -EIO;
84
85 *_pid = (pid_t) ul;
86 return 1;
87}
88
35d2e7ec 89int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
7027ff61 90 _cleanup_free_ char *fs = NULL;
35d2e7ec
LP
91 int r;
92 DIR *d;
93
35d2e7ec
LP
94 assert(_d);
95
96 /* This is not recursive! */
97
c3175a7f
LP
98 r = cg_get_path(controller, path, NULL, &fs);
99 if (r < 0)
35d2e7ec
LP
100 return r;
101
102 d = opendir(fs);
35d2e7ec
LP
103 if (!d)
104 return -errno;
105
106 *_d = d;
107 return 0;
108}
109
110int cg_read_subgroup(DIR *d, char **fn) {
111 struct dirent *de;
112
113 assert(d);
7027ff61 114 assert(fn);
35d2e7ec 115
7027ff61 116 FOREACH_DIRENT(de, d, return -errno) {
35d2e7ec
LP
117 char *b;
118
119 if (de->d_type != DT_DIR)
120 continue;
121
122 if (streq(de->d_name, ".") ||
123 streq(de->d_name, ".."))
124 continue;
125
7027ff61
LP
126 b = strdup(de->d_name);
127 if (!b)
35d2e7ec
LP
128 return -ENOMEM;
129
130 *fn = b;
131 return 1;
132 }
133
35d2e7ec
LP
134 return 0;
135}
136
4ad49000 137int cg_rmdir(const char *controller, const char *path) {
7027ff61 138 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
139 int r;
140
ad293f5a
LP
141 r = cg_get_path(controller, path, NULL, &p);
142 if (r < 0)
35d2e7ec
LP
143 return r;
144
145 r = rmdir(p);
7027ff61
LP
146 if (r < 0 && errno != ENOENT)
147 return -errno;
35d2e7ec 148
7027ff61 149 return 0;
35d2e7ec
LP
150}
151
430c18ed 152int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
7027ff61 153 _cleanup_set_free_ Set *allocated_set = NULL;
35d2e7ec 154 bool done = false;
8c6db833 155 int r, ret = 0;
35d2e7ec 156 pid_t my_pid;
8c6db833 157
8c6db833
LP
158 assert(sig >= 0);
159
160 /* This goes through the tasks list and kills them all. This
161 * is repeated until no further processes are added to the
162 * tasks list, to properly handle forking processes */
163
7027ff61 164 if (!s) {
d5099efc 165 s = allocated_set = set_new(NULL);
7027ff61 166 if (!s)
ca949c9d 167 return -ENOMEM;
7027ff61 168 }
8c6db833
LP
169
170 my_pid = getpid();
171
172 do {
7027ff61 173 _cleanup_fclose_ FILE *f = NULL;
0b172489 174 pid_t pid = 0;
8c6db833
LP
175 done = true;
176
7027ff61
LP
177 r = cg_enumerate_processes(controller, path, &f);
178 if (r < 0) {
4c633005 179 if (ret >= 0 && r != -ENOENT)
7027ff61 180 return r;
35d2e7ec 181
7027ff61 182 return ret;
35d2e7ec 183 }
c6c18be3
LP
184
185 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 186
7027ff61 187 if (ignore_self && pid == my_pid)
c6c18be3 188 continue;
8c6db833 189
c6c18be3
LP
190 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
191 continue;
8c6db833
LP
192
193 /* If we haven't killed this process yet, kill
194 * it */
4c633005
LP
195 if (kill(pid, sig) < 0) {
196 if (ret >= 0 && errno != ESRCH)
8c6db833 197 ret = -errno;
6e8314c4 198 } else {
dc8962da 199 if (sigcont && sig != SIGKILL)
430c18ed
LP
200 kill(pid, SIGCONT);
201
6e8314c4
LP
202 if (ret == 0)
203 ret = 1;
430c18ed 204 }
8c6db833 205
8c6db833
LP
206 done = false;
207
7027ff61
LP
208 r = set_put(s, LONG_TO_PTR(pid));
209 if (r < 0) {
35d2e7ec 210 if (ret >= 0)
7027ff61 211 return r;
35d2e7ec 212
7027ff61 213 return ret;
35d2e7ec
LP
214 }
215 }
216
217 if (r < 0) {
218 if (ret >= 0)
7027ff61 219 return r;
35d2e7ec 220
7027ff61 221 return ret;
8c6db833
LP
222 }
223
8c6db833
LP
224 /* To avoid racing against processes which fork
225 * quicker than we can kill them we repeat this until
226 * no new pids need to be killed. */
227
35d2e7ec 228 } while (!done);
8c6db833 229
35d2e7ec 230 return ret;
8c6db833
LP
231}
232
430c18ed 233int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
7027ff61
LP
234 _cleanup_set_free_ Set *allocated_set = NULL;
235 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 236 int r, ret = 0;
35d2e7ec 237 char *fn;
8c6db833
LP
238
239 assert(path);
8c6db833
LP
240 assert(sig >= 0);
241
7027ff61 242 if (!s) {
d5099efc 243 s = allocated_set = set_new(NULL);
7027ff61 244 if (!s)
ca949c9d 245 return -ENOMEM;
7027ff61 246 }
ca949c9d 247
430c18ed 248 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
8c6db833 249
7027ff61
LP
250 r = cg_enumerate_subgroups(controller, path, &d);
251 if (r < 0) {
4c633005 252 if (ret >= 0 && r != -ENOENT)
7027ff61 253 return r;
8c6db833 254
7027ff61 255 return ret;
35d2e7ec 256 }
8c6db833 257
35d2e7ec 258 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 259 _cleanup_free_ char *p = NULL;
8c6db833 260
7027ff61 261 p = strjoin(path, "/", fn, NULL);
35d2e7ec 262 free(fn);
7027ff61
LP
263 if (!p)
264 return -ENOMEM;
8c6db833 265
430c18ed 266 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
7027ff61 267 if (ret >= 0 && r != 0)
35d2e7ec 268 ret = r;
8c6db833
LP
269 }
270
7027ff61 271 if (ret >= 0 && r < 0)
35d2e7ec
LP
272 ret = r;
273
7027ff61 274 if (rem) {
4ad49000 275 r = cg_rmdir(controller, path);
7027ff61
LP
276 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
277 return r;
278 }
ca949c9d 279
8c6db833
LP
280 return ret;
281}
282
246aa6dd 283int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
35d2e7ec 284 bool done = false;
246aa6dd 285 _cleanup_set_free_ Set *s = NULL;
8c6db833
LP
286 int r, ret = 0;
287 pid_t my_pid;
288
246aa6dd
LP
289 assert(cfrom);
290 assert(pfrom);
291 assert(cto);
292 assert(pto);
8c6db833 293
d5099efc 294 s = set_new(NULL);
246aa6dd 295 if (!s)
35d2e7ec
LP
296 return -ENOMEM;
297
8c6db833
LP
298 my_pid = getpid();
299
300 do {
7027ff61 301 _cleanup_fclose_ FILE *f = NULL;
0b172489 302 pid_t pid = 0;
8c6db833
LP
303 done = true;
304
b043cd0b 305 r = cg_enumerate_processes(cfrom, pfrom, &f);
246aa6dd 306 if (r < 0) {
4c633005 307 if (ret >= 0 && r != -ENOENT)
7027ff61 308 return r;
35d2e7ec 309
246aa6dd 310 return ret;
35d2e7ec 311 }
c6c18be3
LP
312
313 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 314
35d2e7ec
LP
315 /* This might do weird stuff if we aren't a
316 * single-threaded program. However, we
317 * luckily know we are not */
7027ff61 318 if (ignore_self && pid == my_pid)
c6c18be3 319 continue;
8c6db833 320
35d2e7ec
LP
321 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
322 continue;
323
9b84c7f9
LP
324 /* Ignore kernel threads. Since they can only
325 * exist in the root cgroup, we only check for
326 * them there. */
327 if (cfrom &&
328 (isempty(pfrom) || path_equal(pfrom, "/")) &&
329 is_kernel_thread(pid) > 0)
330 continue;
331
246aa6dd
LP
332 r = cg_attach(cto, pto, pid);
333 if (r < 0) {
4c633005 334 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
335 ret = r;
336 } else if (ret == 0)
337 ret = 1;
8c6db833 338
8c6db833 339 done = false;
35d2e7ec 340
246aa6dd
LP
341 r = set_put(s, LONG_TO_PTR(pid));
342 if (r < 0) {
35d2e7ec 343 if (ret >= 0)
7027ff61 344 return r;
35d2e7ec 345
246aa6dd 346 return ret;
35d2e7ec
LP
347 }
348 }
349
350 if (r < 0) {
351 if (ret >= 0)
7027ff61 352 return r;
35d2e7ec 353
246aa6dd 354 return ret;
8c6db833 355 }
35d2e7ec 356 } while (!done);
8c6db833 357
35d2e7ec 358 return ret;
8c6db833
LP
359}
360
4ad49000
LP
361int cg_migrate_recursive(
362 const char *cfrom,
363 const char *pfrom,
364 const char *cto,
365 const char *pto,
366 bool ignore_self,
367 bool rem) {
368
246aa6dd 369 _cleanup_closedir_ DIR *d = NULL;
7027ff61 370 int r, ret = 0;
35d2e7ec 371 char *fn;
8c6db833 372
246aa6dd
LP
373 assert(cfrom);
374 assert(pfrom);
375 assert(cto);
376 assert(pto);
8c6db833 377
246aa6dd 378 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
8c6db833 379
246aa6dd
LP
380 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
381 if (r < 0) {
4c633005 382 if (ret >= 0 && r != -ENOENT)
7027ff61
LP
383 return r;
384
246aa6dd 385 return ret;
35d2e7ec
LP
386 }
387
388 while ((r = cg_read_subgroup(d, &fn)) > 0) {
246aa6dd 389 _cleanup_free_ char *p = NULL;
8c6db833 390
246aa6dd 391 p = strjoin(pfrom, "/", fn, NULL);
35d2e7ec 392 free(fn);
246aa6dd 393 if (!p) {
35d2e7ec 394 if (ret >= 0)
7027ff61 395 return -ENOMEM;
35d2e7ec 396
246aa6dd 397 return ret;
8c6db833
LP
398 }
399
246aa6dd 400 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
35d2e7ec
LP
401 if (r != 0 && ret >= 0)
402 ret = r;
8c6db833
LP
403 }
404
35d2e7ec
LP
405 if (r < 0 && ret >= 0)
406 ret = r;
407
246aa6dd 408 if (rem) {
4ad49000 409 r = cg_rmdir(cfrom, pfrom);
246aa6dd
LP
410 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
411 return r;
412 }
8c6db833
LP
413
414 return ret;
415}
416
13b84ec7
LP
417int cg_migrate_recursive_fallback(
418 const char *cfrom,
419 const char *pfrom,
420 const char *cto,
421 const char *pto,
422 bool ignore_self,
423 bool rem) {
424
425 int r;
426
427 assert(cfrom);
428 assert(pfrom);
429 assert(cto);
430 assert(pto);
431
432 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
433 if (r < 0) {
434 char prefix[strlen(pto) + 1];
435
436 /* This didn't work? Then let's try all prefixes of the destination */
437
fecffe5d 438 PATH_FOREACH_PREFIX(prefix, pto) {
13b84ec7
LP
439 r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
440 if (r >= 0)
441 break;
442 }
443 }
444
fecffe5d 445 return 0;
13b84ec7
LP
446}
447
3474ae3c
LP
448static const char *normalize_controller(const char *controller) {
449
7027ff61
LP
450 assert(controller);
451
185a0874 452 if (startswith(controller, "name="))
3474ae3c
LP
453 return controller + 5;
454 else
455 return controller;
456}
457
458static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
018ef268 459 char *t = NULL;
3474ae3c 460
9444b1f2
LP
461 if (!isempty(controller)) {
462 if (!isempty(path) && !isempty(suffix))
b7def684 463 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
9444b1f2 464 else if (!isempty(path))
b7def684 465 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
9444b1f2 466 else if (!isempty(suffix))
b7def684 467 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
c3175a7f 468 else
7027ff61 469 t = strappend("/sys/fs/cgroup/", controller);
c3175a7f 470 } else {
9444b1f2 471 if (!isempty(path) && !isempty(suffix))
b7def684 472 t = strjoin(path, "/", suffix, NULL);
9444b1f2 473 else if (!isempty(path))
c3175a7f 474 t = strdup(path);
7027ff61
LP
475 else
476 return -EINVAL;
c3175a7f 477 }
3474ae3c
LP
478
479 if (!t)
480 return -ENOMEM;
481
dbb9401d 482 *fs = path_kill_slashes(t);
3474ae3c
LP
483 return 0;
484}
485
8c6db833 486int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
dbd821ac 487 const char *p;
ec202eae 488 static thread_local bool good = false;
8c6db833 489
dbd821ac
LP
490 assert(fs);
491
185a0874 492 if (controller && !cg_controller_is_valid(controller))
78edb35a
LP
493 return -EINVAL;
494
3bfc7184 495 if (_unlikely_(!good)) {
70132bd0
LP
496 int r;
497
e26d6ce5 498 r = path_is_mount_point("/sys/fs/cgroup", 0);
da00518b
LP
499 if (r < 0)
500 return r;
501 if (r == 0)
502 return -ENOENT;
70132bd0
LP
503
504 /* Cache this to save a few stat()s */
505 good = true;
506 }
507
c3175a7f 508 p = controller ? normalize_controller(controller) : NULL;
7027ff61 509
3474ae3c
LP
510 return join_path(p, path, suffix, fs);
511}
dbd821ac 512
7027ff61 513static int check_hierarchy(const char *p) {
b12afc8c 514 const char *cc;
37099707
LP
515
516 assert(p);
517
b12afc8c
LP
518 if (!filename_is_valid(p))
519 return 0;
520
37099707 521 /* Check if this controller actually really exists */
63c372cb 522 cc = strjoina("/sys/fs/cgroup/", p);
b12afc8c 523 if (laccess(cc, F_OK) < 0)
37099707
LP
524 return -errno;
525
526 return 0;
527}
528
3474ae3c
LP
529int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
530 const char *p;
37099707 531 int r;
dbd821ac 532
3474ae3c 533 assert(fs);
70132bd0 534
185a0874 535 if (!cg_controller_is_valid(controller))
3474ae3c 536 return -EINVAL;
70132bd0 537
37099707 538 /* Normalize the controller syntax */
3474ae3c 539 p = normalize_controller(controller);
8c6db833 540
3474ae3c 541 /* Check if this controller actually really exists */
7027ff61 542 r = check_hierarchy(p);
37099707
LP
543 if (r < 0)
544 return r;
3474ae3c
LP
545
546 return join_path(p, path, suffix, fs);
8c6db833
LP
547}
548
e27796a0 549static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
550 assert(path);
551 assert(sb);
552 assert(ftwbuf);
e27796a0
LP
553
554 if (typeflag != FTW_DP)
555 return 0;
556
557 if (ftwbuf->level < 1)
558 return 0;
559
e27796a0
LP
560 rmdir(path);
561 return 0;
562}
563
8c6db833 564int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 565 _cleanup_free_ char *fs = NULL;
e27796a0 566 int r = 0;
8c6db833 567
8c6db833
LP
568 assert(path);
569
e27796a0
LP
570 r = cg_get_path(controller, path, NULL, &fs);
571 if (r < 0)
8c6db833
LP
572 return r;
573
e27796a0 574 errno = 0;
7027ff61 575 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
e27796a0
LP
576 r = errno ? -errno : -EIO;
577
578 if (delete_root) {
4ad49000
LP
579 if (rmdir(fs) < 0 && errno != ENOENT)
580 return -errno;
e27796a0
LP
581 }
582
e27796a0 583 return r;
8c6db833
LP
584}
585
586int cg_delete(const char *controller, const char *path) {
7027ff61 587 _cleanup_free_ char *parent = NULL;
8c6db833
LP
588 int r;
589
8c6db833
LP
590 assert(path);
591
7027ff61
LP
592 r = path_get_parent(path, &parent);
593 if (r < 0)
35d2e7ec 594 return r;
8c6db833 595
246aa6dd 596 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
4c633005 597 return r == -ENOENT ? 0 : r;
8c6db833
LP
598}
599
1434ae6f
LP
600int cg_create(const char *controller, const char *path) {
601 _cleanup_free_ char *fs = NULL;
602 int r;
603
604 r = cg_get_path_and_check(controller, path, NULL, &fs);
605 if (r < 0)
606 return r;
607
608 r = mkdir_parents(fs, 0755);
609 if (r < 0)
610 return r;
611
612 if (mkdir(fs, 0755) < 0) {
613
614 if (errno == EEXIST)
615 return 0;
616
617 return -errno;
618 }
619
620 return 1;
621}
622
623int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
624 int r, q;
625
626 assert(pid >= 0);
627
628 r = cg_create(controller, path);
629 if (r < 0)
630 return r;
631
632 q = cg_attach(controller, path, pid);
633 if (q < 0)
634 return q;
635
636 /* This does not remove the cgroup on failure */
637 return r;
638}
639
8c6db833 640int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
641 _cleanup_free_ char *fs = NULL;
642 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
643 int r;
644
8c6db833
LP
645 assert(path);
646 assert(pid >= 0);
647
b043cd0b 648 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 649 if (r < 0)
c6c18be3 650 return r;
8c6db833
LP
651
652 if (pid == 0)
653 pid = getpid();
654
de0671ee 655 snprintf(c, sizeof(c), PID_FMT"\n", pid);
8c6db833 656
4c1fc3e4 657 return write_string_file(fs, c, 0);
8c6db833
LP
658}
659
13b84ec7
LP
660int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
661 int r;
662
663 assert(controller);
664 assert(path);
665 assert(pid >= 0);
666
667 r = cg_attach(controller, path, pid);
668 if (r < 0) {
669 char prefix[strlen(path) + 1];
670
671 /* This didn't work? Then let's try all prefixes of
672 * the destination */
673
fecffe5d 674 PATH_FOREACH_PREFIX(prefix, path) {
13b84ec7
LP
675 r = cg_attach(controller, prefix, pid);
676 if (r >= 0)
677 break;
678 }
679 }
680
fecffe5d 681 return 0;
13b84ec7
LP
682}
683
2d76d14e
LP
684int cg_set_group_access(
685 const char *controller,
686 const char *path,
687 mode_t mode,
688 uid_t uid,
689 gid_t gid) {
690
574d5f2d 691 _cleanup_free_ char *fs = NULL;
8c6db833
LP
692 int r;
693
8c6db833
LP
694 assert(path);
695
fed1e721 696 if (mode != MODE_INVALID)
8d53b453
LP
697 mode &= 0777;
698
699 r = cg_get_path(controller, path, NULL, &fs);
700 if (r < 0)
8c6db833
LP
701 return r;
702
574d5f2d 703 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
704}
705
974efc46
LP
706int cg_set_task_access(
707 const char *controller,
708 const char *path,
709 mode_t mode,
710 uid_t uid,
4ad49000 711 gid_t gid) {
974efc46
LP
712
713 _cleanup_free_ char *fs = NULL, *procs = NULL;
8c6db833
LP
714 int r;
715
8c6db833
LP
716 assert(path);
717
fed1e721 718 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
8d53b453
LP
719 return 0;
720
fed1e721 721 if (mode != MODE_INVALID)
8d53b453
LP
722 mode &= 0666;
723
b043cd0b 724 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 725 if (r < 0)
8c6db833
LP
726 return r;
727
728 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
729 if (r < 0)
730 return r;
8c6db833 731
b043cd0b
LP
732 /* Compatibility, Always keep values for "tasks" in sync with
733 * "cgroup.procs" */
734 r = cg_get_path(controller, path, "tasks", &procs);
974efc46
LP
735 if (r < 0)
736 return r;
737
738 return chmod_and_chown(procs, mode, uid, gid);
8c6db833
LP
739}
740
7027ff61 741int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
742 _cleanup_fclose_ FILE *f = NULL;
743 char line[LINE_MAX];
8af8afd6 744 const char *fs;
c6c18be3 745 size_t cs;
8c6db833 746
8c6db833 747 assert(path);
c6c18be3 748 assert(pid >= 0);
8c6db833 749
8af8afd6 750 if (controller) {
185a0874 751 if (!cg_controller_is_valid(controller))
8af8afd6 752 return -EINVAL;
78edb35a 753
8af8afd6
LP
754 controller = normalize_controller(controller);
755 } else
7027ff61
LP
756 controller = SYSTEMD_CGROUP_CONTROLLER;
757
b68fa010 758 fs = procfs_file_alloca(pid, "cgroup");
8c6db833 759
c6c18be3 760 f = fopen(fs, "re");
4c633005
LP
761 if (!f)
762 return errno == ENOENT ? -ESRCH : -errno;
763
c6c18be3
LP
764 cs = strlen(controller);
765
7027ff61 766 FOREACH_LINE(line, f, return -errno) {
a2a5291b 767 char *l, *p, *e;
8af8afd6 768 size_t k;
a2a5291b 769 const char *word, *state;
8af8afd6 770 bool found = false;
c6c18be3
LP
771
772 truncate_nl(line);
773
7027ff61
LP
774 l = strchr(line, ':');
775 if (!l)
c6c18be3
LP
776 continue;
777
778 l++;
8af8afd6
LP
779 e = strchr(l, ':');
780 if (!e)
c6c18be3
LP
781 continue;
782
8af8afd6
LP
783 *e = 0;
784
a2a5291b 785 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
8af8afd6 786
a2a5291b 787 if (k == cs && memcmp(word, controller, cs) == 0) {
8af8afd6
LP
788 found = true;
789 break;
790 }
791
792 if (k == 5 + cs &&
a2a5291b
ZJS
793 memcmp(word, "name=", 5) == 0 &&
794 memcmp(word+5, controller, cs) == 0) {
8af8afd6
LP
795 found = true;
796 break;
797 }
798 }
799
800 if (!found)
c6c18be3
LP
801 continue;
802
8af8afd6 803 p = strdup(e + 1);
7027ff61
LP
804 if (!p)
805 return -ENOMEM;
c6c18be3
LP
806
807 *path = p;
7027ff61 808 return 0;
c6c18be3
LP
809 }
810
7027ff61 811 return -ENOENT;
8c6db833
LP
812}
813
814int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61
LP
815 _cleanup_free_ char *fs = NULL, *contents = NULL;
816 char *sc;
8c6db833
LP
817 int r;
818
8c6db833
LP
819 assert(agent);
820
7027ff61
LP
821 r = cg_get_path(controller, NULL, "release_agent", &fs);
822 if (r < 0)
c6c18be3 823 return r;
8c6db833 824
7027ff61
LP
825 r = read_one_line_file(fs, &contents);
826 if (r < 0)
827 return r;
8c6db833
LP
828
829 sc = strstrip(contents);
8c6db833 830 if (sc[0] == 0) {
4c1fc3e4 831 r = write_string_file(fs, agent, 0);
574d5f2d 832 if (r < 0)
7027ff61
LP
833 return r;
834 } else if (!streq(sc, agent))
835 return -EEXIST;
8c6db833 836
0da16248 837 fs = mfree(fs);
7027ff61
LP
838 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
839 if (r < 0)
840 return r;
8c6db833 841
0da16248 842 contents = mfree(contents);
7027ff61
LP
843 r = read_one_line_file(fs, &contents);
844 if (r < 0)
845 return r;
8c6db833
LP
846
847 sc = strstrip(contents);
8c6db833 848 if (streq(sc, "0")) {
4c1fc3e4 849 r = write_string_file(fs, "1", 0);
7027ff61
LP
850 if (r < 0)
851 return r;
c6c18be3 852
7027ff61
LP
853 return 1;
854 }
8c6db833 855
7027ff61
LP
856 if (!streq(sc, "1"))
857 return -EIO;
8c6db833 858
7027ff61 859 return 0;
8c6db833
LP
860}
861
ad929bcc
KS
862int cg_uninstall_release_agent(const char *controller) {
863 _cleanup_free_ char *fs = NULL;
864 int r;
865
ac9ef333
LP
866 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
867 if (r < 0)
868 return r;
869
4c1fc3e4 870 r = write_string_file(fs, "0", 0);
ac9ef333
LP
871 if (r < 0)
872 return r;
873
0da16248 874 fs = mfree(fs);
ac9ef333 875
ad929bcc
KS
876 r = cg_get_path(controller, NULL, "release_agent", &fs);
877 if (r < 0)
878 return r;
879
4c1fc3e4 880 r = write_string_file(fs, "", 0);
ad929bcc
KS
881 if (r < 0)
882 return r;
883
ac9ef333 884 return 0;
ad929bcc
KS
885}
886
8c6db833 887int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
7027ff61 888 _cleanup_fclose_ FILE *f = NULL;
c3175a7f 889 pid_t pid = 0, self_pid;
c6c18be3 890 bool found = false;
7027ff61 891 int r;
8c6db833 892
8c6db833
LP
893 assert(path);
894
b043cd0b 895 r = cg_enumerate_processes(controller, path, &f);
c3175a7f 896 if (r < 0)
4c633005 897 return r == -ENOENT ? 1 : r;
8c6db833 898
c3175a7f
LP
899 self_pid = getpid();
900
c6c18be3 901 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 902
c3175a7f 903 if (ignore_self && pid == self_pid)
c6c18be3 904 continue;
8c6db833 905
c6c18be3
LP
906 found = true;
907 break;
8c6db833
LP
908 }
909
c6c18be3
LP
910 if (r < 0)
911 return r;
8c6db833 912
c6c18be3 913 return !found;
8c6db833
LP
914}
915
916int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
7027ff61 917 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 918 char *fn;
7027ff61 919 int r;
8c6db833 920
8c6db833
LP
921 assert(path);
922
c3175a7f
LP
923 r = cg_is_empty(controller, path, ignore_self);
924 if (r <= 0)
35d2e7ec
LP
925 return r;
926
c3175a7f
LP
927 r = cg_enumerate_subgroups(controller, path, &d);
928 if (r < 0)
4c633005 929 return r == -ENOENT ? 1 : r;
8c6db833 930
35d2e7ec 931 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 932 _cleanup_free_ char *p = NULL;
8c6db833 933
7027ff61 934 p = strjoin(path, "/", fn, NULL);
35d2e7ec 935 free(fn);
7027ff61
LP
936 if (!p)
937 return -ENOMEM;
8c6db833 938
35d2e7ec 939 r = cg_is_empty_recursive(controller, p, ignore_self);
35d2e7ec 940 if (r <= 0)
7027ff61 941 return r;
35d2e7ec
LP
942 }
943
7027ff61
LP
944 if (r < 0)
945 return r;
35d2e7ec 946
7027ff61 947 return 1;
35d2e7ec
LP
948}
949
950int cg_split_spec(const char *spec, char **controller, char **path) {
951 const char *e;
952 char *t = NULL, *u = NULL;
5954c074 953 _cleanup_free_ char *v = NULL;
35d2e7ec
LP
954
955 assert(spec);
35d2e7ec
LP
956
957 if (*spec == '/') {
e884315e
LP
958 if (!path_is_safe(spec))
959 return -EINVAL;
35d2e7ec
LP
960
961 if (path) {
246aa6dd
LP
962 t = strdup(spec);
963 if (!t)
35d2e7ec
LP
964 return -ENOMEM;
965
dbb9401d 966 *path = path_kill_slashes(t);
8c6db833
LP
967 }
968
35d2e7ec
LP
969 if (controller)
970 *controller = NULL;
971
972 return 0;
8c6db833
LP
973 }
974
246aa6dd
LP
975 e = strchr(spec, ':');
976 if (!e) {
185a0874 977 if (!cg_controller_is_valid(spec))
35d2e7ec
LP
978 return -EINVAL;
979
980 if (controller) {
5954c074 981 t = strdup(normalize_controller(spec));
246aa6dd 982 if (!t)
35d2e7ec
LP
983 return -ENOMEM;
984
985 *controller = t;
986 }
987
988 if (path)
989 *path = NULL;
990
991 return 0;
8c6db833
LP
992 }
993
5954c074
LP
994 v = strndup(spec, e-spec);
995 if (!v)
996 return -ENOMEM;
997 t = strdup(normalize_controller(v));
e884315e
LP
998 if (!t)
999 return -ENOMEM;
185a0874 1000 if (!cg_controller_is_valid(t)) {
e884315e 1001 free(t);
35d2e7ec 1002 return -EINVAL;
246aa6dd
LP
1003 }
1004
baa89da4
LP
1005 if (streq(e+1, "")) {
1006 u = strdup("/");
1007 if (!u) {
1008 free(t);
1009 return -ENOMEM;
1010 }
1011 } else {
1012 u = strdup(e+1);
1013 if (!u) {
1014 free(t);
1015 return -ENOMEM;
1016 }
35d2e7ec 1017
baa89da4
LP
1018 if (!path_is_safe(u) ||
1019 !path_is_absolute(u)) {
1020 free(t);
1021 free(u);
1022 return -EINVAL;
1023 }
1024
1025 path_kill_slashes(u);
1026 }
5954c074 1027
35d2e7ec
LP
1028 if (controller)
1029 *controller = t;
e884315e
LP
1030 else
1031 free(t);
35d2e7ec
LP
1032
1033 if (path)
1034 *path = u;
e884315e
LP
1035 else
1036 free(u);
35d2e7ec
LP
1037
1038 return 0;
8c6db833 1039}
c6c18be3 1040
7027ff61 1041int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1042 _cleanup_free_ char *c = NULL, *p = NULL;
1043 char *t;
35d2e7ec
LP
1044 int r;
1045
1046 assert(path);
1047 assert(result);
1048
73e231ab 1049 /* First, check if it already is a filesystem path */
7027ff61 1050 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1051
b69d29ce
LP
1052 t = strdup(path);
1053 if (!t)
35d2e7ec
LP
1054 return -ENOMEM;
1055
dbb9401d 1056 *result = path_kill_slashes(t);
35d2e7ec
LP
1057 return 0;
1058 }
1059
73e231ab 1060 /* Otherwise, treat it as cg spec */
b69d29ce
LP
1061 r = cg_split_spec(path, &c, &p);
1062 if (r < 0)
35d2e7ec
LP
1063 return r;
1064
78edb35a 1065 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
35d2e7ec 1066}
1f73f0f1 1067
7027ff61 1068int cg_get_root_path(char **path) {
9444b1f2 1069 char *p, *e;
7027ff61
LP
1070 int r;
1071
1072 assert(path);
1073
9444b1f2 1074 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1075 if (r < 0)
1076 return r;
1077
9444b1f2
LP
1078 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1079 if (e)
7027ff61
LP
1080 *e = 0;
1081
1f73f0f1
LP
1082 *path = p;
1083 return 0;
1084}
b59e2465 1085
751bc6ac
LP
1086int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1087 _cleanup_free_ char *rt = NULL;
1088 char *p;
ba1261bc
LP
1089 int r;
1090
e9174f29 1091 assert(cgroup);
751bc6ac 1092 assert(shifted);
e9174f29
LP
1093
1094 if (!root) {
1095 /* If the root was specified let's use that, otherwise
1096 * let's determine it from PID 1 */
1097
751bc6ac 1098 r = cg_get_root_path(&rt);
e9174f29
LP
1099 if (r < 0)
1100 return r;
1101
751bc6ac 1102 root = rt;
e9174f29 1103 }
ba1261bc 1104
751bc6ac
LP
1105 p = path_startswith(cgroup, root);
1106 if (p)
1107 *shifted = p - 1;
1108 else
1109 *shifted = cgroup;
1110
1111 return 0;
1112}
1113
1114int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1115 _cleanup_free_ char *raw = NULL;
1116 const char *c;
1117 int r;
1118
1119 assert(pid >= 0);
1120 assert(cgroup);
1121
1122 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
7027ff61 1123 if (r < 0)
ba1261bc 1124 return r;
ba1261bc 1125
751bc6ac
LP
1126 r = cg_shift_path(raw, root, &c);
1127 if (r < 0)
1128 return r;
ba1261bc 1129
751bc6ac
LP
1130 if (c == raw) {
1131 *cgroup = raw;
1132 raw = NULL;
1133 } else {
1134 char *n;
ba1261bc 1135
751bc6ac
LP
1136 n = strdup(c);
1137 if (!n)
ba1261bc 1138 return -ENOMEM;
ba1261bc 1139
751bc6ac
LP
1140 *cgroup = n;
1141 }
ba1261bc
LP
1142
1143 return 0;
1144}
1145
7027ff61 1146int cg_path_decode_unit(const char *cgroup, char **unit){
8b0849e9
LP
1147 char *c, *s;
1148 size_t n;
ef1673d1
MT
1149
1150 assert(cgroup);
6c03089c 1151 assert(unit);
ef1673d1 1152
8b0849e9
LP
1153 n = strcspn(cgroup, "/");
1154 if (n < 3)
1155 return -ENXIO;
1156
1157 c = strndupa(cgroup, n);
ae018d9b 1158 c = cg_unescape(c);
ef1673d1 1159
7410616c 1160 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
cfeaa44a 1161 return -ENXIO;
ef1673d1 1162
d7bd3de0 1163 s = strdup(c);
6c03089c
LP
1164 if (!s)
1165 return -ENOMEM;
1166
1167 *unit = s;
ef1673d1
MT
1168 return 0;
1169}
1170
8b0849e9
LP
1171static bool valid_slice_name(const char *p, size_t n) {
1172
1173 if (!p)
1174 return false;
1175
1176 if (n < strlen("x.slice"))
1177 return false;
1178
1179 if (memcmp(p + n - 6, ".slice", 6) == 0) {
1180 char buf[n+1], *c;
1181
1182 memcpy(buf, p, n);
1183 buf[n] = 0;
1184
1185 c = cg_unescape(buf);
1186
7410616c 1187 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
8b0849e9
LP
1188 }
1189
1190 return false;
1191}
1192
9444b1f2 1193static const char *skip_slices(const char *p) {
8b0849e9
LP
1194 assert(p);
1195
9444b1f2
LP
1196 /* Skips over all slice assignments */
1197
1198 for (;;) {
1021b21b
LP
1199 size_t n;
1200
9444b1f2
LP
1201 p += strspn(p, "/");
1202
1203 n = strcspn(p, "/");
8b0849e9 1204 if (!valid_slice_name(p, n))
9444b1f2
LP
1205 return p;
1206
1207 p += n;
1208 }
1209}
1210
8b0849e9 1211int cg_path_get_unit(const char *path, char **ret) {
6c03089c 1212 const char *e;
8b0849e9
LP
1213 char *unit;
1214 int r;
6c03089c
LP
1215
1216 assert(path);
8b0849e9 1217 assert(ret);
6c03089c 1218
9444b1f2 1219 e = skip_slices(path);
6c03089c 1220
8b0849e9
LP
1221 r = cg_path_decode_unit(e, &unit);
1222 if (r < 0)
1223 return r;
1224
1225 /* We skipped over the slices, don't accept any now */
1226 if (endswith(unit, ".slice")) {
1227 free(unit);
1228 return -ENXIO;
1229 }
1230
1231 *ret = unit;
1232 return 0;
6c03089c
LP
1233}
1234
1235int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1236 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1237 int r;
ba1261bc 1238
ef1673d1
MT
1239 assert(unit);
1240
7027ff61 1241 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1242 if (r < 0)
1243 return r;
1244
6c03089c
LP
1245 return cg_path_get_unit(cgroup, unit);
1246}
ef1673d1 1247
d4fffc4b
ZJS
1248/**
1249 * Skip session-*.scope, but require it to be there.
1250 */
9444b1f2
LP
1251static const char *skip_session(const char *p) {
1252 size_t n;
1253
8b0849e9
LP
1254 if (isempty(p))
1255 return NULL;
9444b1f2
LP
1256
1257 p += strspn(p, "/");
1258
1259 n = strcspn(p, "/");
8b0849e9 1260 if (n < strlen("session-x.scope"))
d4fffc4b
ZJS
1261 return NULL;
1262
8b0849e9
LP
1263 if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1264 char buf[n - 8 - 6 + 1];
1265
1266 memcpy(buf, p + 8, n - 8 - 6);
1267 buf[n - 8 - 6] = 0;
d4fffc4b 1268
8b0849e9
LP
1269 /* Note that session scopes never need unescaping,
1270 * since they cannot conflict with the kernel's own
1271 * names, hence we don't need to call cg_unescape()
1272 * here. */
1273
1274 if (!session_id_valid(buf))
1275 return false;
1276
1277 p += n;
1278 p += strspn(p, "/");
1279 return p;
1280 }
1281
1282 return NULL;
d4fffc4b
ZJS
1283}
1284
1285/**
1286 * Skip user@*.service, but require it to be there.
1287 */
1288static const char *skip_user_manager(const char *p) {
1289 size_t n;
1290
8b0849e9
LP
1291 if (isempty(p))
1292 return NULL;
d4fffc4b
ZJS
1293
1294 p += strspn(p, "/");
1295
1296 n = strcspn(p, "/");
8b0849e9 1297 if (n < strlen("user@x.service"))
6c03089c 1298 return NULL;
ef1673d1 1299
8b0849e9
LP
1300 if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1301 char buf[n - 5 - 8 + 1];
9444b1f2 1302
8b0849e9
LP
1303 memcpy(buf, p + 5, n - 5 - 8);
1304 buf[n - 5 - 8] = 0;
1305
1306 /* Note that user manager services never need unescaping,
1307 * since they cannot conflict with the kernel's own
1308 * names, hence we don't need to call cg_unescape()
1309 * here. */
1310
1311 if (parse_uid(buf, NULL) < 0)
1312 return NULL;
1313
1314 p += n;
1315 p += strspn(p, "/");
1316
1317 return p;
1318 }
1319
1320 return NULL;
9444b1f2
LP
1321}
1322
329ac4bc 1323static const char *skip_user_prefix(const char *path) {
d4fffc4b 1324 const char *e, *t;
ef1673d1 1325
6c03089c 1326 assert(path);
ba1261bc 1327
9444b1f2
LP
1328 /* Skip slices, if there are any */
1329 e = skip_slices(path);
ba1261bc 1330
329ac4bc 1331 /* Skip the user manager, if it's in the path now... */
8b0849e9 1332 t = skip_user_manager(e);
329ac4bc
LP
1333 if (t)
1334 return t;
8b0849e9 1335
329ac4bc
LP
1336 /* Alternatively skip the user session if it is in the path... */
1337 return skip_session(e);
1338}
32081481 1339
329ac4bc
LP
1340int cg_path_get_user_unit(const char *path, char **ret) {
1341 const char *t;
6c03089c 1342
329ac4bc
LP
1343 assert(path);
1344 assert(ret);
8b0849e9 1345
329ac4bc
LP
1346 t = skip_user_prefix(path);
1347 if (!t)
8b0849e9 1348 return -ENXIO;
8b0849e9 1349
329ac4bc
LP
1350 /* And from here on it looks pretty much the same as for a
1351 * system unit, hence let's use the same parser from here
1352 * on. */
1353 return cg_path_get_unit(t, ret);
ef1673d1 1354}
ba1261bc 1355
ef1673d1 1356int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1357 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1358 int r;
1359
1360 assert(unit);
1361
7027ff61 1362 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1363 if (r < 0)
1364 return r;
1365
1366 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1367}
e884315e 1368
7027ff61 1369int cg_path_get_machine_name(const char *path, char **machine) {
89f7c846
LP
1370 _cleanup_free_ char *u = NULL, *sl = NULL;
1371 int r;
374ec6ab 1372
89f7c846
LP
1373 r = cg_path_get_unit(path, &u);
1374 if (r < 0)
1375 return r;
7027ff61 1376
89f7c846
LP
1377 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1378 if (!sl)
ae018d9b 1379 return -ENOMEM;
aff38e74 1380
89f7c846 1381 return readlink_malloc(sl, machine);
7027ff61
LP
1382}
1383
1384int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1385 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1386 int r;
1387
1388 assert(machine);
1389
1390 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1391 if (r < 0)
1392 return r;
1393
1394 return cg_path_get_machine_name(cgroup, machine);
1395}
1396
1397int cg_path_get_session(const char *path, char **session) {
8b0849e9
LP
1398 _cleanup_free_ char *unit = NULL;
1399 char *start, *end;
1400 int r;
7027ff61
LP
1401
1402 assert(path);
7027ff61 1403
8b0849e9
LP
1404 r = cg_path_get_unit(path, &unit);
1405 if (r < 0)
1406 return r;
7027ff61 1407
8b0849e9
LP
1408 start = startswith(unit, "session-");
1409 if (!start)
cfeaa44a 1410 return -ENXIO;
8b0849e9
LP
1411 end = endswith(start, ".scope");
1412 if (!end)
cfeaa44a 1413 return -ENXIO;
8b0849e9
LP
1414
1415 *end = 0;
1416 if (!session_id_valid(start))
cfeaa44a 1417 return -ENXIO;
374ec6ab 1418
af08d2f9 1419 if (session) {
8b0849e9 1420 char *rr;
af08d2f9 1421
8b0849e9
LP
1422 rr = strdup(start);
1423 if (!rr)
af08d2f9
LP
1424 return -ENOMEM;
1425
8b0849e9 1426 *session = rr;
af08d2f9 1427 }
7027ff61 1428
7027ff61
LP
1429 return 0;
1430}
1431
1432int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1433 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1434 int r;
1435
7027ff61
LP
1436 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1437 if (r < 0)
1438 return r;
1439
1440 return cg_path_get_session(cgroup, session);
1441}
1442
ae018d9b 1443int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1444 _cleanup_free_ char *slice = NULL;
8b0849e9 1445 char *start, *end;
374ec6ab 1446 int r;
ae018d9b
LP
1447
1448 assert(path);
ae018d9b 1449
374ec6ab
LP
1450 r = cg_path_get_slice(path, &slice);
1451 if (r < 0)
1452 return r;
ae018d9b 1453
674eb685
LP
1454 start = startswith(slice, "user-");
1455 if (!start)
cfeaa44a 1456 return -ENXIO;
8b0849e9 1457 end = endswith(start, ".slice");
674eb685 1458 if (!end)
cfeaa44a 1459 return -ENXIO;
ae018d9b 1460
8b0849e9
LP
1461 *end = 0;
1462 if (parse_uid(start, uid) < 0)
cfeaa44a 1463 return -ENXIO;
674eb685 1464
674eb685 1465 return 0;
ae018d9b
LP
1466}
1467
1468int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1469 _cleanup_free_ char *cgroup = NULL;
1470 int r;
1471
ae018d9b
LP
1472 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1473 if (r < 0)
1474 return r;
1475
1476 return cg_path_get_owner_uid(cgroup, uid);
1477}
1478
1021b21b
LP
1479int cg_path_get_slice(const char *p, char **slice) {
1480 const char *e = NULL;
1021b21b
LP
1481
1482 assert(p);
1483 assert(slice);
1484
329ac4bc
LP
1485 /* Finds the right-most slice unit from the beginning, but
1486 * stops before we come to the first non-slice unit. */
1487
1021b21b
LP
1488 for (;;) {
1489 size_t n;
1490
1491 p += strspn(p, "/");
1492
1493 n = strcspn(p, "/");
8b0849e9 1494 if (!valid_slice_name(p, n)) {
1021b21b 1495
8b0849e9
LP
1496 if (!e) {
1497 char *s;
1021b21b 1498
8b0849e9
LP
1499 s = strdup("-.slice");
1500 if (!s)
1501 return -ENOMEM;
1021b21b 1502
8b0849e9
LP
1503 *slice = s;
1504 return 0;
1505 }
1506
1507 return cg_path_decode_unit(e, slice);
1021b21b
LP
1508 }
1509
1510 e = p;
1021b21b
LP
1511 p += n;
1512 }
1513}
1514
1515int cg_pid_get_slice(pid_t pid, char **slice) {
1516 _cleanup_free_ char *cgroup = NULL;
1517 int r;
1518
1519 assert(slice);
1520
1521 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1522 if (r < 0)
1523 return r;
1524
1525 return cg_path_get_slice(cgroup, slice);
1526}
1527
329ac4bc
LP
1528int cg_path_get_user_slice(const char *p, char **slice) {
1529 const char *t;
1530 assert(p);
1531 assert(slice);
1532
1533 t = skip_user_prefix(p);
1534 if (!t)
1535 return -ENXIO;
1536
1537 /* And now it looks pretty much the same as for a system
1538 * slice, so let's just use the same parser from here on. */
1539 return cg_path_get_slice(t, slice);
1540}
1541
1542int cg_pid_get_user_slice(pid_t pid, char **slice) {
1543 _cleanup_free_ char *cgroup = NULL;
1544 int r;
1545
1546 assert(slice);
1547
1548 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1549 if (r < 0)
1550 return r;
1551
1552 return cg_path_get_user_slice(cgroup, slice);
1553}
1554
ae018d9b
LP
1555char *cg_escape(const char *p) {
1556 bool need_prefix = false;
1557
1558 /* This implements very minimal escaping for names to be used
1559 * as file names in the cgroup tree: any name which might
1560 * conflict with a kernel name or is prefixed with '_' is
1561 * prefixed with a '_'. That way, when reading cgroup names it
1562 * is sufficient to remove a single prefixing underscore if
1563 * there is one. */
1564
1565 /* The return value of this function (unlike cg_unescape())
1566 * needs free()! */
1567
a0ab5665
LP
1568 if (p[0] == 0 ||
1569 p[0] == '_' ||
1570 p[0] == '.' ||
1571 streq(p, "notify_on_release") ||
1572 streq(p, "release_agent") ||
1573 streq(p, "tasks"))
ae018d9b
LP
1574 need_prefix = true;
1575 else {
1576 const char *dot;
1577
1578 dot = strrchr(p, '.');
1579 if (dot) {
1580
1581 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1582 need_prefix = true;
1583 else {
1584 char *n;
1585
1586 n = strndupa(p, dot - p);
1587
1588 if (check_hierarchy(n) >= 0)
1589 need_prefix = true;
1590 }
1591 }
1592 }
1593
1594 if (need_prefix)
1595 return strappend("_", p);
1596 else
1597 return strdup(p);
1598}
1599
1600char *cg_unescape(const char *p) {
1601 assert(p);
1602
1603 /* The return value of this function (unlike cg_escape())
1604 * doesn't need free()! */
1605
1606 if (p[0] == '_')
1607 return (char*) p+1;
1608
1609 return (char*) p;
1610}
78edb35a
LP
1611
1612#define CONTROLLER_VALID \
4b549144 1613 DIGITS LETTERS \
78edb35a
LP
1614 "_"
1615
185a0874 1616bool cg_controller_is_valid(const char *p) {
78edb35a
LP
1617 const char *t, *s;
1618
1619 if (!p)
1620 return false;
1621
185a0874
DJL
1622 s = startswith(p, "name=");
1623 if (s)
1624 p = s;
78edb35a
LP
1625
1626 if (*p == 0 || *p == '_')
1627 return false;
1628
1629 for (t = p; *t; t++)
1630 if (!strchr(CONTROLLER_VALID, *t))
1631 return false;
1632
1633 if (t - p > FILENAME_MAX)
1634 return false;
1635
1636 return true;
1637}
a016b922
LP
1638
1639int cg_slice_to_path(const char *unit, char **ret) {
1640 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1641 const char *dash;
7410616c 1642 int r;
a016b922
LP
1643
1644 assert(unit);
1645 assert(ret);
1646
c96cc582
LP
1647 if (streq(unit, "-.slice")) {
1648 char *x;
1649
1650 x = strdup("");
1651 if (!x)
1652 return -ENOMEM;
1653 *ret = x;
1654 return 0;
1655 }
1656
7410616c 1657 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
a016b922
LP
1658 return -EINVAL;
1659
1660 if (!endswith(unit, ".slice"))
1661 return -EINVAL;
1662
7410616c
LP
1663 r = unit_name_to_prefix(unit, &p);
1664 if (r < 0)
1665 return r;
a016b922
LP
1666
1667 dash = strchr(p, '-');
e66e5b61
LP
1668
1669 /* Don't allow initial dashes */
1670 if (dash == p)
1671 return -EINVAL;
1672
a016b922
LP
1673 while (dash) {
1674 _cleanup_free_ char *escaped = NULL;
1675 char n[dash - p + sizeof(".slice")];
1676
e66e5b61
LP
1677 /* Don't allow trailing or double dashes */
1678 if (dash[1] == 0 || dash[1] == '-')
c96cc582 1679 return -EINVAL;
a016b922 1680
c96cc582 1681 strcpy(stpncpy(n, p, dash - p), ".slice");
7410616c 1682 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
a016b922
LP
1683 return -EINVAL;
1684
1685 escaped = cg_escape(n);
1686 if (!escaped)
1687 return -ENOMEM;
1688
1689 if (!strextend(&s, escaped, "/", NULL))
1690 return -ENOMEM;
1691
1692 dash = strchr(dash+1, '-');
1693 }
1694
1695 e = cg_escape(unit);
1696 if (!e)
1697 return -ENOMEM;
1698
1699 if (!strextend(&s, e, NULL))
1700 return -ENOMEM;
1701
1702 *ret = s;
1703 s = NULL;
1704
1705 return 0;
1706}
4ad49000
LP
1707
1708int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1709 _cleanup_free_ char *p = NULL;
1710 int r;
1711
1712 r = cg_get_path(controller, path, attribute, &p);
1713 if (r < 0)
1714 return r;
1715
4c1fc3e4 1716 return write_string_file(p, value, 0);
4ad49000
LP
1717}
1718
934277fe
LP
1719int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1720 _cleanup_free_ char *p = NULL;
1721 int r;
1722
1723 r = cg_get_path(controller, path, attribute, &p);
1724 if (r < 0)
1725 return r;
1726
1727 return read_one_line_file(p, ret);
1728}
1729
4ad49000
LP
1730static const char mask_names[] =
1731 "cpu\0"
1732 "cpuacct\0"
1733 "blkio\0"
1734 "memory\0"
1735 "devices\0";
1736
13b84ec7 1737int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1738 CGroupControllerMask bit = 1;
1739 const char *n;
1740 int r;
1741
1742 /* This one will create a cgroup in our private tree, but also
1743 * duplicate it in the trees specified in mask, and remove it
1744 * in all others */
1745
1746 /* First create the cgroup in our own hierarchy. */
1747 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1748 if (r < 0)
1749 return r;
1750
1751 /* Then, do the same in the other hierarchies */
1752 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1753 if (mask & bit)
4ad49000 1754 cg_create(n, path);
13b84ec7 1755 else if (supported & bit)
4ad49000
LP
1756 cg_trim(n, path, true);
1757
1758 bit <<= 1;
1759 }
1760
13b84ec7 1761 return 0;
4ad49000
LP
1762}
1763
7b3fd631 1764int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
4ad49000
LP
1765 CGroupControllerMask bit = 1;
1766 const char *n;
1767 int r;
1768
1769 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1770 if (r < 0)
1771 return r;
4ad49000
LP
1772
1773 NULSTR_FOREACH(n, mask_names) {
7b3fd631
LP
1774
1775 if (supported & bit) {
1776 const char *p = NULL;
1777
1778 if (path_callback)
1779 p = path_callback(bit, userdata);
1780
1781 if (!p)
1782 p = path;
1783
edf5b3b8 1784 cg_attach_fallback(n, p, pid);
7b3fd631 1785 }
4ad49000
LP
1786
1787 bit <<= 1;
1788 }
1789
13b84ec7 1790 return 0;
4ad49000
LP
1791}
1792
7b3fd631 1793int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
1794 Iterator i;
1795 void *pidp;
1796 int r = 0;
1797
1798 SET_FOREACH(pidp, pids, i) {
1799 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1800 int q;
6c12b52e 1801
7b3fd631 1802 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
13b84ec7
LP
1803 if (q < 0)
1804 r = q;
6c12b52e
LP
1805 }
1806
1807 return r;
1808}
1809
03b90d4b 1810int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
4ad49000
LP
1811 CGroupControllerMask bit = 1;
1812 const char *n;
1813 int r;
1814
13b84ec7
LP
1815 if (!path_equal(from, to)) {
1816 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1817 if (r < 0)
1818 return r;
1819 }
4ad49000
LP
1820
1821 NULSTR_FOREACH(n, mask_names) {
03b90d4b
LP
1822 if (supported & bit) {
1823 const char *p = NULL;
1824
1825 if (to_callback)
1826 p = to_callback(bit, userdata);
1827
1828 if (!p)
1829 p = to;
1830
1831 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1832 }
4ad49000
LP
1833
1834 bit <<= 1;
1835 }
1836
13b84ec7 1837 return 0;
4ad49000
LP
1838}
1839
13b84ec7 1840int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1841 CGroupControllerMask bit = 1;
1842 const char *n;
1843 int r;
1844
1845 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1846 if (r < 0)
1847 return r;
1848
1849 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1850 if (supported & bit)
4ad49000
LP
1851 cg_trim(n, path, delete_root);
1852
1853 bit <<= 1;
1854 }
1855
13b84ec7 1856 return 0;
4ad49000
LP
1857}
1858
1859CGroupControllerMask cg_mask_supported(void) {
1860 CGroupControllerMask bit = 1, mask = 0;
1861 const char *n;
1862
1863 NULSTR_FOREACH(n, mask_names) {
1864 if (check_hierarchy(n) >= 0)
1865 mask |= bit;
1866
1867 bit <<= 1;
1868 }
1869
1870 return mask;
1871}
b12afc8c
LP
1872
1873int cg_kernel_controllers(Set *controllers) {
1874 _cleanup_fclose_ FILE *f = NULL;
1875 char buf[LINE_MAX];
1876 int r;
1877
1878 assert(controllers);
1879
1880 f = fopen("/proc/cgroups", "re");
1881 if (!f) {
1882 if (errno == ENOENT)
1883 return 0;
1884 return -errno;
1885 }
1886
1887 /* Ignore the header line */
1888 (void) fgets(buf, sizeof(buf), f);
1889
1890 for (;;) {
1891 char *controller;
1892 int enabled = 0;
1893
1894 errno = 0;
1895 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
1896
1897 if (feof(f))
1898 break;
1899
1900 if (ferror(f) && errno)
1901 return -errno;
1902
1903 return -EBADMSG;
1904 }
1905
1906 if (!enabled) {
1907 free(controller);
1908 continue;
1909 }
1910
1911 if (!filename_is_valid(controller)) {
1912 free(controller);
1913 return -EBADMSG;
1914 }
1915
1916 r = set_consume(controllers, controller);
1917 if (r < 0)
1918 return r;
1919 }
1920
1921 return 0;
1922}