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