]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
udevadm hwdb: discard extra leading whitespaces in hwdb
[thirdparty/systemd.git] / src / shared / cgroup-util.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8c6db833
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8c6db833
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
8c6db833 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <unistd.h>
24#include <signal.h>
25#include <string.h>
26#include <stdlib.h>
35d2e7ec 27#include <dirent.h>
672c48cc
LP
28#include <sys/stat.h>
29#include <sys/types.h>
e27796a0 30#include <ftw.h>
8c6db833
LP
31
32#include "cgroup-util.h"
33#include "log.h"
34#include "set.h"
35#include "macro.h"
36#include "util.h"
9eb977db 37#include "path-util.h"
b59e2465 38#include "strv.h"
ef1673d1 39#include "unit-name.h"
a5c32cff 40#include "fileio.h"
9444b1f2 41#include "special.h"
1434ae6f 42#include "mkdir.h"
8c6db833 43
c6c18be3 44int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
7027ff61 45 _cleanup_free_ char *fs = NULL;
c6c18be3 46 FILE *f;
7027ff61 47 int r;
c6c18be3 48
c6c18be3
LP
49 assert(_f);
50
c3175a7f
LP
51 r = cg_get_path(controller, path, "cgroup.procs", &fs);
52 if (r < 0)
c6c18be3
LP
53 return r;
54
55 f = fopen(fs, "re");
c6c18be3
LP
56 if (!f)
57 return -errno;
58
59 *_f = f;
60 return 0;
61}
62
c6c18be3
LP
63int cg_read_pid(FILE *f, pid_t *_pid) {
64 unsigned long ul;
65
66 /* Note that the cgroup.procs might contain duplicates! See
67 * cgroups.txt for details. */
68
7027ff61
LP
69 assert(f);
70 assert(_pid);
71
c6c18be3
LP
72 errno = 0;
73 if (fscanf(f, "%lu", &ul) != 1) {
74
75 if (feof(f))
76 return 0;
77
78 return errno ? -errno : -EIO;
79 }
80
81 if (ul <= 0)
82 return -EIO;
83
84 *_pid = (pid_t) ul;
85 return 1;
86}
87
35d2e7ec 88int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
7027ff61 89 _cleanup_free_ char *fs = NULL;
35d2e7ec
LP
90 int r;
91 DIR *d;
92
35d2e7ec
LP
93 assert(_d);
94
95 /* This is not recursive! */
96
c3175a7f
LP
97 r = cg_get_path(controller, path, NULL, &fs);
98 if (r < 0)
35d2e7ec
LP
99 return r;
100
101 d = opendir(fs);
35d2e7ec
LP
102 if (!d)
103 return -errno;
104
105 *_d = d;
106 return 0;
107}
108
109int cg_read_subgroup(DIR *d, char **fn) {
110 struct dirent *de;
111
112 assert(d);
7027ff61 113 assert(fn);
35d2e7ec 114
7027ff61 115 FOREACH_DIRENT(de, d, return -errno) {
35d2e7ec
LP
116 char *b;
117
118 if (de->d_type != DT_DIR)
119 continue;
120
121 if (streq(de->d_name, ".") ||
122 streq(de->d_name, ".."))
123 continue;
124
7027ff61
LP
125 b = strdup(de->d_name);
126 if (!b)
35d2e7ec
LP
127 return -ENOMEM;
128
129 *fn = b;
130 return 1;
131 }
132
35d2e7ec
LP
133 return 0;
134}
135
4ad49000 136int cg_rmdir(const char *controller, const char *path) {
7027ff61 137 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
138 int r;
139
ad293f5a
LP
140 r = cg_get_path(controller, path, NULL, &p);
141 if (r < 0)
35d2e7ec
LP
142 return r;
143
144 r = rmdir(p);
7027ff61
LP
145 if (r < 0 && errno != ENOENT)
146 return -errno;
35d2e7ec 147
7027ff61 148 return 0;
35d2e7ec
LP
149}
150
430c18ed 151int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
7027ff61 152 _cleanup_set_free_ Set *allocated_set = NULL;
35d2e7ec 153 bool done = false;
8c6db833 154 int r, ret = 0;
35d2e7ec 155 pid_t my_pid;
8c6db833 156
8c6db833
LP
157 assert(sig >= 0);
158
159 /* This goes through the tasks list and kills them all. This
160 * is repeated until no further processes are added to the
161 * tasks list, to properly handle forking processes */
162
7027ff61 163 if (!s) {
d5099efc 164 s = allocated_set = set_new(NULL);
7027ff61 165 if (!s)
ca949c9d 166 return -ENOMEM;
7027ff61 167 }
8c6db833
LP
168
169 my_pid = getpid();
170
171 do {
7027ff61 172 _cleanup_fclose_ FILE *f = NULL;
0b172489 173 pid_t pid = 0;
8c6db833
LP
174 done = true;
175
7027ff61
LP
176 r = cg_enumerate_processes(controller, path, &f);
177 if (r < 0) {
4c633005 178 if (ret >= 0 && r != -ENOENT)
7027ff61 179 return r;
35d2e7ec 180
7027ff61 181 return ret;
35d2e7ec 182 }
c6c18be3
LP
183
184 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 185
7027ff61 186 if (ignore_self && pid == my_pid)
c6c18be3 187 continue;
8c6db833 188
c6c18be3
LP
189 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
190 continue;
8c6db833
LP
191
192 /* If we haven't killed this process yet, kill
193 * it */
4c633005
LP
194 if (kill(pid, sig) < 0) {
195 if (ret >= 0 && errno != ESRCH)
8c6db833 196 ret = -errno;
6e8314c4 197 } else {
430c18ed
LP
198 if (sigcont)
199 kill(pid, SIGCONT);
200
6e8314c4
LP
201 if (ret == 0)
202 ret = 1;
430c18ed 203 }
8c6db833 204
8c6db833
LP
205 done = false;
206
7027ff61
LP
207 r = set_put(s, LONG_TO_PTR(pid));
208 if (r < 0) {
35d2e7ec 209 if (ret >= 0)
7027ff61 210 return r;
35d2e7ec 211
7027ff61 212 return ret;
35d2e7ec
LP
213 }
214 }
215
216 if (r < 0) {
217 if (ret >= 0)
7027ff61 218 return r;
35d2e7ec 219
7027ff61 220 return ret;
8c6db833
LP
221 }
222
8c6db833
LP
223 /* To avoid racing against processes which fork
224 * quicker than we can kill them we repeat this until
225 * no new pids need to be killed. */
226
35d2e7ec 227 } while (!done);
8c6db833 228
35d2e7ec 229 return ret;
8c6db833
LP
230}
231
430c18ed 232int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
7027ff61
LP
233 _cleanup_set_free_ Set *allocated_set = NULL;
234 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 235 int r, ret = 0;
35d2e7ec 236 char *fn;
8c6db833
LP
237
238 assert(path);
8c6db833
LP
239 assert(sig >= 0);
240
7027ff61 241 if (!s) {
d5099efc 242 s = allocated_set = set_new(NULL);
7027ff61 243 if (!s)
ca949c9d 244 return -ENOMEM;
7027ff61 245 }
ca949c9d 246
430c18ed 247 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
8c6db833 248
7027ff61
LP
249 r = cg_enumerate_subgroups(controller, path, &d);
250 if (r < 0) {
4c633005 251 if (ret >= 0 && r != -ENOENT)
7027ff61 252 return r;
8c6db833 253
7027ff61 254 return ret;
35d2e7ec 255 }
8c6db833 256
35d2e7ec 257 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 258 _cleanup_free_ char *p = NULL;
8c6db833 259
7027ff61 260 p = strjoin(path, "/", fn, NULL);
35d2e7ec 261 free(fn);
7027ff61
LP
262 if (!p)
263 return -ENOMEM;
8c6db833 264
430c18ed 265 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
7027ff61 266 if (ret >= 0 && r != 0)
35d2e7ec 267 ret = r;
8c6db833
LP
268 }
269
7027ff61 270 if (ret >= 0 && r < 0)
35d2e7ec
LP
271 ret = r;
272
7027ff61 273 if (rem) {
4ad49000 274 r = cg_rmdir(controller, path);
7027ff61
LP
275 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
276 return r;
277 }
ca949c9d 278
8c6db833
LP
279 return ret;
280}
281
246aa6dd 282int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
35d2e7ec 283 bool done = false;
246aa6dd 284 _cleanup_set_free_ Set *s = NULL;
8c6db833
LP
285 int r, ret = 0;
286 pid_t my_pid;
287
246aa6dd
LP
288 assert(cfrom);
289 assert(pfrom);
290 assert(cto);
291 assert(pto);
8c6db833 292
d5099efc 293 s = set_new(NULL);
246aa6dd 294 if (!s)
35d2e7ec
LP
295 return -ENOMEM;
296
8c6db833
LP
297 my_pid = getpid();
298
299 do {
7027ff61 300 _cleanup_fclose_ FILE *f = NULL;
0b172489 301 pid_t pid = 0;
8c6db833
LP
302 done = true;
303
b043cd0b 304 r = cg_enumerate_processes(cfrom, pfrom, &f);
246aa6dd 305 if (r < 0) {
4c633005 306 if (ret >= 0 && r != -ENOENT)
7027ff61 307 return r;
35d2e7ec 308
246aa6dd 309 return ret;
35d2e7ec 310 }
c6c18be3
LP
311
312 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 313
35d2e7ec
LP
314 /* This might do weird stuff if we aren't a
315 * single-threaded program. However, we
316 * luckily know we are not */
7027ff61 317 if (ignore_self && pid == my_pid)
c6c18be3 318 continue;
8c6db833 319
35d2e7ec
LP
320 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
321 continue;
322
246aa6dd
LP
323 r = cg_attach(cto, pto, pid);
324 if (r < 0) {
4c633005 325 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
326 ret = r;
327 } else if (ret == 0)
328 ret = 1;
8c6db833 329
8c6db833 330 done = false;
35d2e7ec 331
246aa6dd
LP
332 r = set_put(s, LONG_TO_PTR(pid));
333 if (r < 0) {
35d2e7ec 334 if (ret >= 0)
7027ff61 335 return r;
35d2e7ec 336
246aa6dd 337 return ret;
35d2e7ec
LP
338 }
339 }
340
341 if (r < 0) {
342 if (ret >= 0)
7027ff61 343 return r;
35d2e7ec 344
246aa6dd 345 return ret;
8c6db833 346 }
35d2e7ec 347 } while (!done);
8c6db833 348
35d2e7ec 349 return ret;
8c6db833
LP
350}
351
4ad49000
LP
352int cg_migrate_recursive(
353 const char *cfrom,
354 const char *pfrom,
355 const char *cto,
356 const char *pto,
357 bool ignore_self,
358 bool rem) {
359
246aa6dd 360 _cleanup_closedir_ DIR *d = NULL;
7027ff61 361 int r, ret = 0;
35d2e7ec 362 char *fn;
8c6db833 363
246aa6dd
LP
364 assert(cfrom);
365 assert(pfrom);
366 assert(cto);
367 assert(pto);
8c6db833 368
246aa6dd 369 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
8c6db833 370
246aa6dd
LP
371 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
372 if (r < 0) {
4c633005 373 if (ret >= 0 && r != -ENOENT)
7027ff61
LP
374 return r;
375
246aa6dd 376 return ret;
35d2e7ec
LP
377 }
378
379 while ((r = cg_read_subgroup(d, &fn)) > 0) {
246aa6dd 380 _cleanup_free_ char *p = NULL;
8c6db833 381
246aa6dd 382 p = strjoin(pfrom, "/", fn, NULL);
35d2e7ec 383 free(fn);
246aa6dd 384 if (!p) {
35d2e7ec 385 if (ret >= 0)
7027ff61 386 return -ENOMEM;
35d2e7ec 387
246aa6dd 388 return ret;
8c6db833
LP
389 }
390
246aa6dd 391 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
35d2e7ec
LP
392 if (r != 0 && ret >= 0)
393 ret = r;
8c6db833
LP
394 }
395
35d2e7ec
LP
396 if (r < 0 && ret >= 0)
397 ret = r;
398
246aa6dd 399 if (rem) {
4ad49000 400 r = cg_rmdir(cfrom, pfrom);
246aa6dd
LP
401 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
402 return r;
403 }
8c6db833
LP
404
405 return ret;
406}
407
13b84ec7
LP
408int cg_migrate_recursive_fallback(
409 const char *cfrom,
410 const char *pfrom,
411 const char *cto,
412 const char *pto,
413 bool ignore_self,
414 bool rem) {
415
416 int r;
417
418 assert(cfrom);
419 assert(pfrom);
420 assert(cto);
421 assert(pto);
422
423 r = cg_migrate_recursive(cfrom, pfrom, cto, pto, ignore_self, rem);
424 if (r < 0) {
425 char prefix[strlen(pto) + 1];
426
427 /* This didn't work? Then let's try all prefixes of the destination */
428
fecffe5d 429 PATH_FOREACH_PREFIX(prefix, pto) {
13b84ec7
LP
430 r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem);
431 if (r >= 0)
432 break;
433 }
434 }
435
fecffe5d 436 return 0;
13b84ec7
LP
437}
438
3474ae3c
LP
439static const char *normalize_controller(const char *controller) {
440
7027ff61
LP
441 assert(controller);
442
3474ae3c
LP
443 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
444 return "systemd";
445 else if (startswith(controller, "name="))
446 return controller + 5;
447 else
448 return controller;
449}
450
451static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
018ef268 452 char *t = NULL;
3474ae3c 453
9444b1f2
LP
454 if (!isempty(controller)) {
455 if (!isempty(path) && !isempty(suffix))
b7def684 456 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
9444b1f2 457 else if (!isempty(path))
b7def684 458 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
9444b1f2 459 else if (!isempty(suffix))
b7def684 460 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
c3175a7f 461 else
7027ff61 462 t = strappend("/sys/fs/cgroup/", controller);
c3175a7f 463 } else {
9444b1f2 464 if (!isempty(path) && !isempty(suffix))
b7def684 465 t = strjoin(path, "/", suffix, NULL);
9444b1f2 466 else if (!isempty(path))
c3175a7f 467 t = strdup(path);
7027ff61
LP
468 else
469 return -EINVAL;
c3175a7f 470 }
3474ae3c
LP
471
472 if (!t)
473 return -ENOMEM;
474
dbb9401d 475 *fs = path_kill_slashes(t);
3474ae3c
LP
476 return 0;
477}
478
8c6db833 479int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
dbd821ac 480 const char *p;
ec202eae 481 static thread_local bool good = false;
8c6db833 482
dbd821ac
LP
483 assert(fs);
484
78edb35a
LP
485 if (controller && !cg_controller_is_valid(controller, true))
486 return -EINVAL;
487
3bfc7184 488 if (_unlikely_(!good)) {
70132bd0
LP
489 int r;
490
0c85a4f3 491 r = path_is_mount_point("/sys/fs/cgroup", false);
70132bd0
LP
492 if (r <= 0)
493 return r < 0 ? r : -ENOENT;
494
495 /* Cache this to save a few stat()s */
496 good = true;
497 }
498
c3175a7f 499 p = controller ? normalize_controller(controller) : NULL;
7027ff61 500
3474ae3c
LP
501 return join_path(p, path, suffix, fs);
502}
dbd821ac 503
7027ff61 504static int check_hierarchy(const char *p) {
37099707
LP
505 char *cc;
506
507 assert(p);
508
509 /* Check if this controller actually really exists */
f8294e41 510 cc = alloca(strlen("/sys/fs/cgroup/") + strlen(p) + 1);
37099707
LP
511 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
512 if (access(cc, F_OK) < 0)
513 return -errno;
514
515 return 0;
516}
517
3474ae3c
LP
518int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
519 const char *p;
37099707 520 int r;
dbd821ac 521
3474ae3c 522 assert(fs);
70132bd0 523
78edb35a 524 if (!cg_controller_is_valid(controller, true))
3474ae3c 525 return -EINVAL;
70132bd0 526
37099707 527 /* Normalize the controller syntax */
3474ae3c 528 p = normalize_controller(controller);
8c6db833 529
3474ae3c 530 /* Check if this controller actually really exists */
7027ff61 531 r = check_hierarchy(p);
37099707
LP
532 if (r < 0)
533 return r;
3474ae3c
LP
534
535 return join_path(p, path, suffix, fs);
8c6db833
LP
536}
537
e27796a0 538static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
539 assert(path);
540 assert(sb);
541 assert(ftwbuf);
e27796a0
LP
542
543 if (typeflag != FTW_DP)
544 return 0;
545
546 if (ftwbuf->level < 1)
547 return 0;
548
e27796a0
LP
549 rmdir(path);
550 return 0;
551}
552
8c6db833 553int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 554 _cleanup_free_ char *fs = NULL;
e27796a0 555 int r = 0;
8c6db833 556
8c6db833
LP
557 assert(path);
558
e27796a0
LP
559 r = cg_get_path(controller, path, NULL, &fs);
560 if (r < 0)
8c6db833
LP
561 return r;
562
e27796a0 563 errno = 0;
7027ff61 564 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
e27796a0
LP
565 r = errno ? -errno : -EIO;
566
567 if (delete_root) {
4ad49000
LP
568 if (rmdir(fs) < 0 && errno != ENOENT)
569 return -errno;
e27796a0
LP
570 }
571
e27796a0 572 return r;
8c6db833
LP
573}
574
575int cg_delete(const char *controller, const char *path) {
7027ff61 576 _cleanup_free_ char *parent = NULL;
8c6db833
LP
577 int r;
578
8c6db833
LP
579 assert(path);
580
7027ff61
LP
581 r = path_get_parent(path, &parent);
582 if (r < 0)
35d2e7ec 583 return r;
8c6db833 584
246aa6dd 585 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
4c633005 586 return r == -ENOENT ? 0 : r;
8c6db833
LP
587}
588
1434ae6f
LP
589int cg_create(const char *controller, const char *path) {
590 _cleanup_free_ char *fs = NULL;
591 int r;
592
593 r = cg_get_path_and_check(controller, path, NULL, &fs);
594 if (r < 0)
595 return r;
596
597 r = mkdir_parents(fs, 0755);
598 if (r < 0)
599 return r;
600
601 if (mkdir(fs, 0755) < 0) {
602
603 if (errno == EEXIST)
604 return 0;
605
606 return -errno;
607 }
608
609 return 1;
610}
611
612int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
613 int r, q;
614
615 assert(pid >= 0);
616
617 r = cg_create(controller, path);
618 if (r < 0)
619 return r;
620
621 q = cg_attach(controller, path, pid);
622 if (q < 0)
623 return q;
624
625 /* This does not remove the cgroup on failure */
626 return r;
627}
628
8c6db833 629int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
630 _cleanup_free_ char *fs = NULL;
631 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
632 int r;
633
8c6db833
LP
634 assert(path);
635 assert(pid >= 0);
636
b043cd0b 637 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 638 if (r < 0)
c6c18be3 639 return r;
8c6db833
LP
640
641 if (pid == 0)
642 pid = getpid();
643
de0671ee 644 snprintf(c, sizeof(c), PID_FMT"\n", pid);
8c6db833 645
70c949a4 646 return write_string_file_no_create(fs, c);
8c6db833
LP
647}
648
13b84ec7
LP
649int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
650 int r;
651
652 assert(controller);
653 assert(path);
654 assert(pid >= 0);
655
656 r = cg_attach(controller, path, pid);
657 if (r < 0) {
658 char prefix[strlen(path) + 1];
659
660 /* This didn't work? Then let's try all prefixes of
661 * the destination */
662
fecffe5d 663 PATH_FOREACH_PREFIX(prefix, path) {
13b84ec7
LP
664 r = cg_attach(controller, prefix, pid);
665 if (r >= 0)
666 break;
667 }
668 }
669
fecffe5d 670 return 0;
13b84ec7
LP
671}
672
2d76d14e
LP
673int cg_set_group_access(
674 const char *controller,
675 const char *path,
676 mode_t mode,
677 uid_t uid,
678 gid_t gid) {
679
574d5f2d 680 _cleanup_free_ char *fs = NULL;
8c6db833
LP
681 int r;
682
8c6db833
LP
683 assert(path);
684
8d53b453
LP
685 if (mode != (mode_t) -1)
686 mode &= 0777;
687
688 r = cg_get_path(controller, path, NULL, &fs);
689 if (r < 0)
8c6db833
LP
690 return r;
691
574d5f2d 692 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
693}
694
974efc46
LP
695int cg_set_task_access(
696 const char *controller,
697 const char *path,
698 mode_t mode,
699 uid_t uid,
4ad49000 700 gid_t gid) {
974efc46
LP
701
702 _cleanup_free_ char *fs = NULL, *procs = NULL;
8c6db833
LP
703 int r;
704
8c6db833
LP
705 assert(path);
706
4ad49000 707 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
8d53b453
LP
708 return 0;
709
710 if (mode != (mode_t) -1)
711 mode &= 0666;
712
b043cd0b 713 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 714 if (r < 0)
8c6db833
LP
715 return r;
716
717 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
718 if (r < 0)
719 return r;
8c6db833 720
b043cd0b
LP
721 /* Compatibility, Always keep values for "tasks" in sync with
722 * "cgroup.procs" */
723 r = cg_get_path(controller, path, "tasks", &procs);
974efc46
LP
724 if (r < 0)
725 return r;
726
727 return chmod_and_chown(procs, mode, uid, gid);
8c6db833
LP
728}
729
7027ff61 730int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
731 _cleanup_fclose_ FILE *f = NULL;
732 char line[LINE_MAX];
8af8afd6 733 const char *fs;
c6c18be3 734 size_t cs;
8c6db833 735
8c6db833 736 assert(path);
c6c18be3 737 assert(pid >= 0);
8c6db833 738
8af8afd6
LP
739 if (controller) {
740 if (!cg_controller_is_valid(controller, true))
741 return -EINVAL;
78edb35a 742
8af8afd6
LP
743 controller = normalize_controller(controller);
744 } else
7027ff61
LP
745 controller = SYSTEMD_CGROUP_CONTROLLER;
746
b68fa010 747 fs = procfs_file_alloca(pid, "cgroup");
8c6db833 748
c6c18be3 749 f = fopen(fs, "re");
4c633005
LP
750 if (!f)
751 return errno == ENOENT ? -ESRCH : -errno;
752
c6c18be3
LP
753 cs = strlen(controller);
754
7027ff61 755 FOREACH_LINE(line, f, return -errno) {
a2a5291b 756 char *l, *p, *e;
8af8afd6 757 size_t k;
a2a5291b 758 const char *word, *state;
8af8afd6 759 bool found = false;
c6c18be3
LP
760
761 truncate_nl(line);
762
7027ff61
LP
763 l = strchr(line, ':');
764 if (!l)
c6c18be3
LP
765 continue;
766
767 l++;
8af8afd6
LP
768 e = strchr(l, ':');
769 if (!e)
c6c18be3
LP
770 continue;
771
8af8afd6
LP
772 *e = 0;
773
a2a5291b 774 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
8af8afd6 775
a2a5291b 776 if (k == cs && memcmp(word, controller, cs) == 0) {
8af8afd6
LP
777 found = true;
778 break;
779 }
780
781 if (k == 5 + cs &&
a2a5291b
ZJS
782 memcmp(word, "name=", 5) == 0 &&
783 memcmp(word+5, controller, cs) == 0) {
8af8afd6
LP
784 found = true;
785 break;
786 }
787 }
788
789 if (!found)
c6c18be3
LP
790 continue;
791
8af8afd6 792 p = strdup(e + 1);
7027ff61
LP
793 if (!p)
794 return -ENOMEM;
c6c18be3
LP
795
796 *path = p;
7027ff61 797 return 0;
c6c18be3
LP
798 }
799
7027ff61 800 return -ENOENT;
8c6db833
LP
801}
802
803int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61
LP
804 _cleanup_free_ char *fs = NULL, *contents = NULL;
805 char *sc;
8c6db833
LP
806 int r;
807
8c6db833
LP
808 assert(agent);
809
7027ff61
LP
810 r = cg_get_path(controller, NULL, "release_agent", &fs);
811 if (r < 0)
c6c18be3 812 return r;
8c6db833 813
7027ff61
LP
814 r = read_one_line_file(fs, &contents);
815 if (r < 0)
816 return r;
8c6db833
LP
817
818 sc = strstrip(contents);
8c6db833 819 if (sc[0] == 0) {
70c949a4 820 r = write_string_file_no_create(fs, agent);
574d5f2d 821 if (r < 0)
7027ff61
LP
822 return r;
823 } else if (!streq(sc, agent))
824 return -EEXIST;
8c6db833 825
c6c18be3
LP
826 free(fs);
827 fs = NULL;
7027ff61
LP
828 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
829 if (r < 0)
830 return r;
8c6db833
LP
831
832 free(contents);
833 contents = NULL;
7027ff61
LP
834 r = read_one_line_file(fs, &contents);
835 if (r < 0)
836 return r;
8c6db833
LP
837
838 sc = strstrip(contents);
8c6db833 839 if (streq(sc, "0")) {
70c949a4 840 r = write_string_file_no_create(fs, "1");
7027ff61
LP
841 if (r < 0)
842 return r;
c6c18be3 843
7027ff61
LP
844 return 1;
845 }
8c6db833 846
7027ff61
LP
847 if (!streq(sc, "1"))
848 return -EIO;
8c6db833 849
7027ff61 850 return 0;
8c6db833
LP
851}
852
ad929bcc
KS
853int cg_uninstall_release_agent(const char *controller) {
854 _cleanup_free_ char *fs = NULL;
855 int r;
856
ac9ef333
LP
857 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
858 if (r < 0)
859 return r;
860
70c949a4 861 r = write_string_file_no_create(fs, "0");
ac9ef333
LP
862 if (r < 0)
863 return r;
864
865 free(fs);
866 fs = NULL;
867
ad929bcc
KS
868 r = cg_get_path(controller, NULL, "release_agent", &fs);
869 if (r < 0)
870 return r;
871
70c949a4 872 r = write_string_file_no_create(fs, "");
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) {
78edb35a 969 if (!cg_controller_is_valid(spec, true))
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;
78edb35a 992 if (!cg_controller_is_valid(t, true)) {
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){
d7bd3de0 1139 char *e, *c, *s;
ef1673d1
MT
1140
1141 assert(cgroup);
6c03089c 1142 assert(unit);
ef1673d1 1143
6c03089c
LP
1144 e = strchrnul(cgroup, '/');
1145 c = strndupa(cgroup, e - cgroup);
ae018d9b 1146 c = cg_unescape(c);
ef1673d1 1147
f78e6385 1148 if (!unit_name_is_valid(c, TEMPLATE_INVALID))
6c03089c 1149 return -EINVAL;
ef1673d1 1150
d7bd3de0 1151 s = strdup(c);
6c03089c
LP
1152 if (!s)
1153 return -ENOMEM;
1154
1155 *unit = s;
ef1673d1
MT
1156 return 0;
1157}
1158
9444b1f2 1159static const char *skip_slices(const char *p) {
9444b1f2
LP
1160 /* Skips over all slice assignments */
1161
1162 for (;;) {
1021b21b
LP
1163 size_t n;
1164
9444b1f2
LP
1165 p += strspn(p, "/");
1166
1167 n = strcspn(p, "/");
1168 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
1169 return p;
1170
1171 p += n;
1172 }
1173}
1174
6c03089c
LP
1175int cg_path_get_unit(const char *path, char **unit) {
1176 const char *e;
1177
1178 assert(path);
1179 assert(unit);
1180
9444b1f2 1181 e = skip_slices(path);
6c03089c 1182
7027ff61 1183 return cg_path_decode_unit(e, unit);
6c03089c
LP
1184}
1185
1186int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1187 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1188 int r;
ba1261bc 1189
ef1673d1
MT
1190 assert(unit);
1191
7027ff61 1192 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1193 if (r < 0)
1194 return r;
1195
6c03089c
LP
1196 return cg_path_get_unit(cgroup, unit);
1197}
ef1673d1 1198
d4fffc4b
ZJS
1199/**
1200 * Skip session-*.scope, but require it to be there.
1201 */
9444b1f2
LP
1202static const char *skip_session(const char *p) {
1203 size_t n;
1204
1205 assert(p);
1206
1207 p += strspn(p, "/");
1208
1209 n = strcspn(p, "/");
d4fffc4b
ZJS
1210 if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
1211 return NULL;
1212
1213 p += n;
1214 p += strspn(p, "/");
1215
1216 return p;
1217}
1218
1219/**
1220 * Skip user@*.service, but require it to be there.
1221 */
1222static const char *skip_user_manager(const char *p) {
1223 size_t n;
1224
1225 assert(p);
1226
1227 p += strspn(p, "/");
1228
1229 n = strcspn(p, "/");
1230 if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
6c03089c 1231 return NULL;
ef1673d1 1232
9444b1f2
LP
1233 p += n;
1234 p += strspn(p, "/");
1235
1236 return p;
1237}
1238
6c03089c 1239int cg_path_get_user_unit(const char *path, char **unit) {
d4fffc4b 1240 const char *e, *t;
ef1673d1 1241
6c03089c 1242 assert(path);
ba1261bc
LP
1243 assert(unit);
1244
6c03089c
LP
1245 /* We always have to parse the path from the beginning as unit
1246 * cgroups might have arbitrary child cgroups and we shouldn't get
1247 * confused by those */
ba1261bc 1248
9444b1f2
LP
1249 /* Skip slices, if there are any */
1250 e = skip_slices(path);
ba1261bc 1251
d4fffc4b
ZJS
1252 /* Skip the session scope... */
1253 t = skip_session(e);
1254 if (t)
1255 /* ... and skip more slices if there's one */
1256 e = skip_slices(t);
1257 else {
1258 /* ... or require a user manager unit to be there */
1259 e = skip_user_manager(e);
1260 if (!e)
1261 return -ENOENT;
1262 }
6c03089c 1263
7027ff61 1264 return cg_path_decode_unit(e, unit);
ef1673d1 1265}
ba1261bc 1266
ef1673d1 1267int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1268 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1269 int r;
1270
1271 assert(unit);
1272
7027ff61 1273 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1274 if (r < 0)
1275 return r;
1276
1277 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1278}
e884315e 1279
7027ff61 1280int cg_path_get_machine_name(const char *path, char **machine) {
89f7c846
LP
1281 _cleanup_free_ char *u = NULL, *sl = NULL;
1282 int r;
374ec6ab 1283
89f7c846
LP
1284 r = cg_path_get_unit(path, &u);
1285 if (r < 0)
1286 return r;
7027ff61 1287
89f7c846
LP
1288 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1289 if (!sl)
ae018d9b 1290 return -ENOMEM;
aff38e74 1291
89f7c846 1292 return readlink_malloc(sl, machine);
7027ff61
LP
1293}
1294
1295int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1296 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1297 int r;
1298
1299 assert(machine);
1300
1301 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1302 if (r < 0)
1303 return r;
1304
1305 return cg_path_get_machine_name(cgroup, machine);
1306}
1307
1308int cg_path_get_session(const char *path, char **session) {
a13ee4c7 1309 const char *e, *n, *x, *y;
af08d2f9 1310 char *s;
7027ff61
LP
1311
1312 assert(path);
7027ff61 1313
9444b1f2
LP
1314 /* Skip slices, if there are any */
1315 e = skip_slices(path);
7027ff61 1316
7027ff61 1317 n = strchrnul(e, '/');
374ec6ab 1318 if (e == n)
7027ff61 1319 return -ENOENT;
374ec6ab
LP
1320
1321 s = strndupa(e, n - e);
1322 s = cg_unescape(s);
1323
1324 x = startswith(s, "session-");
1325 if (!x)
1326 return -ENOENT;
a13ee4c7
ZJS
1327 y = endswith(x, ".scope");
1328 if (!y || x == y)
374ec6ab
LP
1329 return -ENOENT;
1330
af08d2f9
LP
1331 if (session) {
1332 char *r;
1333
a13ee4c7 1334 r = strndup(x, y - x);
af08d2f9
LP
1335 if (!r)
1336 return -ENOMEM;
1337
1338 *session = r;
1339 }
7027ff61 1340
7027ff61
LP
1341 return 0;
1342}
1343
1344int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1345 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1346 int r;
1347
7027ff61
LP
1348 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1349 if (r < 0)
1350 return r;
1351
1352 return cg_path_get_session(cgroup, session);
1353}
1354
ae018d9b 1355int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1356 _cleanup_free_ char *slice = NULL;
674eb685 1357 const char *start, *end;
ae018d9b 1358 char *s;
674eb685 1359 uid_t u;
374ec6ab 1360 int r;
ae018d9b
LP
1361
1362 assert(path);
ae018d9b 1363
374ec6ab
LP
1364 r = cg_path_get_slice(path, &slice);
1365 if (r < 0)
1366 return r;
ae018d9b 1367
674eb685
LP
1368 start = startswith(slice, "user-");
1369 if (!start)
ae018d9b 1370 return -ENOENT;
674eb685
LP
1371 end = endswith(slice, ".slice");
1372 if (!end)
ae018d9b
LP
1373 return -ENOENT;
1374
674eb685 1375 s = strndupa(start, end - start);
ae018d9b 1376 if (!s)
674eb685 1377 return -ENOENT;
ae018d9b 1378
674eb685
LP
1379 if (parse_uid(s, &u) < 0)
1380 return -EIO;
1381
1382 if (uid)
1383 *uid = u;
1384
1385 return 0;
ae018d9b
LP
1386}
1387
1388int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1389 _cleanup_free_ char *cgroup = NULL;
1390 int r;
1391
ae018d9b
LP
1392 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1393 if (r < 0)
1394 return r;
1395
1396 return cg_path_get_owner_uid(cgroup, uid);
1397}
1398
1021b21b
LP
1399int cg_path_get_slice(const char *p, char **slice) {
1400 const char *e = NULL;
1401 size_t m = 0;
1402
1403 assert(p);
1404 assert(slice);
1405
1406 for (;;) {
1407 size_t n;
1408
1409 p += strspn(p, "/");
1410
1411 n = strcspn(p, "/");
1412 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1413 char *s;
1414
1415 if (!e)
1416 return -ENOENT;
1417
1418 s = strndup(e, m);
1419 if (!s)
1420 return -ENOMEM;
1421
1422 *slice = s;
1423 return 0;
1424 }
1425
1426 e = p;
1427 m = n;
1428
1429 p += n;
1430 }
1431}
1432
1433int cg_pid_get_slice(pid_t pid, char **slice) {
1434 _cleanup_free_ char *cgroup = NULL;
1435 int r;
1436
1437 assert(slice);
1438
1439 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1440 if (r < 0)
1441 return r;
1442
1443 return cg_path_get_slice(cgroup, slice);
1444}
1445
ae018d9b
LP
1446char *cg_escape(const char *p) {
1447 bool need_prefix = false;
1448
1449 /* This implements very minimal escaping for names to be used
1450 * as file names in the cgroup tree: any name which might
1451 * conflict with a kernel name or is prefixed with '_' is
1452 * prefixed with a '_'. That way, when reading cgroup names it
1453 * is sufficient to remove a single prefixing underscore if
1454 * there is one. */
1455
1456 /* The return value of this function (unlike cg_unescape())
1457 * needs free()! */
1458
a0ab5665
LP
1459 if (p[0] == 0 ||
1460 p[0] == '_' ||
1461 p[0] == '.' ||
1462 streq(p, "notify_on_release") ||
1463 streq(p, "release_agent") ||
1464 streq(p, "tasks"))
ae018d9b
LP
1465 need_prefix = true;
1466 else {
1467 const char *dot;
1468
1469 dot = strrchr(p, '.');
1470 if (dot) {
1471
1472 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1473 need_prefix = true;
1474 else {
1475 char *n;
1476
1477 n = strndupa(p, dot - p);
1478
1479 if (check_hierarchy(n) >= 0)
1480 need_prefix = true;
1481 }
1482 }
1483 }
1484
1485 if (need_prefix)
1486 return strappend("_", p);
1487 else
1488 return strdup(p);
1489}
1490
1491char *cg_unescape(const char *p) {
1492 assert(p);
1493
1494 /* The return value of this function (unlike cg_escape())
1495 * doesn't need free()! */
1496
1497 if (p[0] == '_')
1498 return (char*) p+1;
1499
1500 return (char*) p;
1501}
78edb35a
LP
1502
1503#define CONTROLLER_VALID \
4b549144 1504 DIGITS LETTERS \
78edb35a
LP
1505 "_"
1506
1507bool cg_controller_is_valid(const char *p, bool allow_named) {
1508 const char *t, *s;
1509
1510 if (!p)
1511 return false;
1512
1513 if (allow_named) {
1514 s = startswith(p, "name=");
1515 if (s)
1516 p = s;
1517 }
1518
1519 if (*p == 0 || *p == '_')
1520 return false;
1521
1522 for (t = p; *t; t++)
1523 if (!strchr(CONTROLLER_VALID, *t))
1524 return false;
1525
1526 if (t - p > FILENAME_MAX)
1527 return false;
1528
1529 return true;
1530}
a016b922
LP
1531
1532int cg_slice_to_path(const char *unit, char **ret) {
1533 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1534 const char *dash;
1535
1536 assert(unit);
1537 assert(ret);
1538
f78e6385 1539 if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
a016b922
LP
1540 return -EINVAL;
1541
1542 if (!endswith(unit, ".slice"))
1543 return -EINVAL;
1544
1545 p = unit_name_to_prefix(unit);
1546 if (!p)
1547 return -ENOMEM;
1548
1549 dash = strchr(p, '-');
1550 while (dash) {
1551 _cleanup_free_ char *escaped = NULL;
1552 char n[dash - p + sizeof(".slice")];
1553
1554 strcpy(stpncpy(n, p, dash - p), ".slice");
1555
f78e6385 1556 if (!unit_name_is_valid(n, TEMPLATE_INVALID))
a016b922
LP
1557 return -EINVAL;
1558
1559 escaped = cg_escape(n);
1560 if (!escaped)
1561 return -ENOMEM;
1562
1563 if (!strextend(&s, escaped, "/", NULL))
1564 return -ENOMEM;
1565
1566 dash = strchr(dash+1, '-');
1567 }
1568
1569 e = cg_escape(unit);
1570 if (!e)
1571 return -ENOMEM;
1572
1573 if (!strextend(&s, e, NULL))
1574 return -ENOMEM;
1575
1576 *ret = s;
1577 s = NULL;
1578
1579 return 0;
1580}
4ad49000
LP
1581
1582int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1583 _cleanup_free_ char *p = NULL;
1584 int r;
1585
1586 r = cg_get_path(controller, path, attribute, &p);
1587 if (r < 0)
1588 return r;
1589
70c949a4 1590 return write_string_file_no_create(p, value);
4ad49000
LP
1591}
1592
1593static const char mask_names[] =
1594 "cpu\0"
1595 "cpuacct\0"
1596 "blkio\0"
1597 "memory\0"
1598 "devices\0";
1599
13b84ec7 1600int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1601 CGroupControllerMask bit = 1;
1602 const char *n;
1603 int r;
1604
1605 /* This one will create a cgroup in our private tree, but also
1606 * duplicate it in the trees specified in mask, and remove it
1607 * in all others */
1608
1609 /* First create the cgroup in our own hierarchy. */
1610 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1611 if (r < 0)
1612 return r;
1613
1614 /* Then, do the same in the other hierarchies */
1615 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1616 if (mask & bit)
4ad49000 1617 cg_create(n, path);
13b84ec7 1618 else if (supported & bit)
4ad49000
LP
1619 cg_trim(n, path, true);
1620
1621 bit <<= 1;
1622 }
1623
13b84ec7 1624 return 0;
4ad49000
LP
1625}
1626
13b84ec7 1627int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
4ad49000
LP
1628 CGroupControllerMask bit = 1;
1629 const char *n;
1630 int r;
1631
1632 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1633 if (r < 0)
1634 return r;
4ad49000
LP
1635
1636 NULSTR_FOREACH(n, mask_names) {
13b84ec7
LP
1637 if (supported & bit)
1638 cg_attach_fallback(n, path, pid);
4ad49000
LP
1639
1640 bit <<= 1;
1641 }
1642
13b84ec7 1643 return 0;
4ad49000
LP
1644}
1645
13b84ec7 1646int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
6c12b52e
LP
1647 Iterator i;
1648 void *pidp;
1649 int r = 0;
1650
1651 SET_FOREACH(pidp, pids, i) {
1652 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1653 int q;
6c12b52e 1654
13b84ec7
LP
1655 q = cg_attach_everywhere(supported, path, pid);
1656 if (q < 0)
1657 r = q;
6c12b52e
LP
1658 }
1659
1660 return r;
1661}
1662
03b90d4b 1663int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
4ad49000
LP
1664 CGroupControllerMask bit = 1;
1665 const char *n;
1666 int r;
1667
13b84ec7
LP
1668 if (!path_equal(from, to)) {
1669 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1670 if (r < 0)
1671 return r;
1672 }
4ad49000
LP
1673
1674 NULSTR_FOREACH(n, mask_names) {
03b90d4b
LP
1675 if (supported & bit) {
1676 const char *p = NULL;
1677
1678 if (to_callback)
1679 p = to_callback(bit, userdata);
1680
1681 if (!p)
1682 p = to;
1683
1684 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1685 }
4ad49000
LP
1686
1687 bit <<= 1;
1688 }
1689
13b84ec7 1690 return 0;
4ad49000
LP
1691}
1692
13b84ec7 1693int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1694 CGroupControllerMask bit = 1;
1695 const char *n;
1696 int r;
1697
1698 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1699 if (r < 0)
1700 return r;
1701
1702 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1703 if (supported & bit)
4ad49000
LP
1704 cg_trim(n, path, delete_root);
1705
1706 bit <<= 1;
1707 }
1708
13b84ec7 1709 return 0;
4ad49000
LP
1710}
1711
1712CGroupControllerMask cg_mask_supported(void) {
1713 CGroupControllerMask bit = 1, mask = 0;
1714 const char *n;
1715
1716 NULSTR_FOREACH(n, mask_names) {
1717 if (check_hierarchy(n) >= 0)
1718 mask |= bit;
1719
1720 bit <<= 1;
1721 }
1722
1723 return mask;
1724}