]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
Use strlen even for constant strings
[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
LP
163 if (!s) {
164 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
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
LP
241 if (!s) {
242 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
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
246aa6dd
LP
293 s = set_new(trivial_hash_func, trivial_compare_func);
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
475 path_kill_slashes(t);
476
477 *fs = t;
478 return 0;
479}
480
8c6db833 481int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
dbd821ac 482 const char *p;
ec202eae 483 static thread_local bool good = false;
8c6db833 484
dbd821ac
LP
485 assert(fs);
486
78edb35a
LP
487 if (controller && !cg_controller_is_valid(controller, true))
488 return -EINVAL;
489
3bfc7184 490 if (_unlikely_(!good)) {
70132bd0
LP
491 int r;
492
0c85a4f3 493 r = path_is_mount_point("/sys/fs/cgroup", false);
70132bd0
LP
494 if (r <= 0)
495 return r < 0 ? r : -ENOENT;
496
497 /* Cache this to save a few stat()s */
498 good = true;
499 }
500
c3175a7f 501 p = controller ? normalize_controller(controller) : NULL;
7027ff61 502
3474ae3c
LP
503 return join_path(p, path, suffix, fs);
504}
dbd821ac 505
7027ff61 506static int check_hierarchy(const char *p) {
37099707
LP
507 char *cc;
508
509 assert(p);
510
511 /* Check if this controller actually really exists */
f8294e41 512 cc = alloca(strlen("/sys/fs/cgroup/") + strlen(p) + 1);
37099707
LP
513 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
514 if (access(cc, F_OK) < 0)
515 return -errno;
516
517 return 0;
518}
519
3474ae3c
LP
520int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
521 const char *p;
37099707 522 int r;
dbd821ac 523
3474ae3c 524 assert(fs);
70132bd0 525
78edb35a 526 if (!cg_controller_is_valid(controller, true))
3474ae3c 527 return -EINVAL;
70132bd0 528
37099707 529 /* Normalize the controller syntax */
3474ae3c 530 p = normalize_controller(controller);
8c6db833 531
3474ae3c 532 /* Check if this controller actually really exists */
7027ff61 533 r = check_hierarchy(p);
37099707
LP
534 if (r < 0)
535 return r;
3474ae3c
LP
536
537 return join_path(p, path, suffix, fs);
8c6db833
LP
538}
539
e27796a0 540static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
541 assert(path);
542 assert(sb);
543 assert(ftwbuf);
e27796a0
LP
544
545 if (typeflag != FTW_DP)
546 return 0;
547
548 if (ftwbuf->level < 1)
549 return 0;
550
e27796a0
LP
551 rmdir(path);
552 return 0;
553}
554
8c6db833 555int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 556 _cleanup_free_ char *fs = NULL;
e27796a0 557 int r = 0;
8c6db833 558
8c6db833
LP
559 assert(path);
560
e27796a0
LP
561 r = cg_get_path(controller, path, NULL, &fs);
562 if (r < 0)
8c6db833
LP
563 return r;
564
e27796a0 565 errno = 0;
7027ff61 566 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
e27796a0
LP
567 r = errno ? -errno : -EIO;
568
569 if (delete_root) {
4ad49000
LP
570 if (rmdir(fs) < 0 && errno != ENOENT)
571 return -errno;
e27796a0
LP
572 }
573
e27796a0 574 return r;
8c6db833
LP
575}
576
577int cg_delete(const char *controller, const char *path) {
7027ff61 578 _cleanup_free_ char *parent = NULL;
8c6db833
LP
579 int r;
580
8c6db833
LP
581 assert(path);
582
7027ff61
LP
583 r = path_get_parent(path, &parent);
584 if (r < 0)
35d2e7ec 585 return r;
8c6db833 586
246aa6dd 587 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
4c633005 588 return r == -ENOENT ? 0 : r;
8c6db833
LP
589}
590
1434ae6f
LP
591int cg_create(const char *controller, const char *path) {
592 _cleanup_free_ char *fs = NULL;
593 int r;
594
595 r = cg_get_path_and_check(controller, path, NULL, &fs);
596 if (r < 0)
597 return r;
598
599 r = mkdir_parents(fs, 0755);
600 if (r < 0)
601 return r;
602
603 if (mkdir(fs, 0755) < 0) {
604
605 if (errno == EEXIST)
606 return 0;
607
608 return -errno;
609 }
610
611 return 1;
612}
613
614int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
615 int r, q;
616
617 assert(pid >= 0);
618
619 r = cg_create(controller, path);
620 if (r < 0)
621 return r;
622
623 q = cg_attach(controller, path, pid);
624 if (q < 0)
625 return q;
626
627 /* This does not remove the cgroup on failure */
628 return r;
629}
630
8c6db833 631int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
632 _cleanup_free_ char *fs = NULL;
633 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
634 int r;
635
8c6db833
LP
636 assert(path);
637 assert(pid >= 0);
638
b043cd0b 639 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 640 if (r < 0)
c6c18be3 641 return r;
8c6db833
LP
642
643 if (pid == 0)
644 pid = getpid();
645
c6c18be3 646 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
8c6db833 647
574d5f2d 648 return write_string_file(fs, c);
8c6db833
LP
649}
650
13b84ec7
LP
651int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
652 int r;
653
654 assert(controller);
655 assert(path);
656 assert(pid >= 0);
657
658 r = cg_attach(controller, path, pid);
659 if (r < 0) {
660 char prefix[strlen(path) + 1];
661
662 /* This didn't work? Then let's try all prefixes of
663 * the destination */
664
fecffe5d 665 PATH_FOREACH_PREFIX(prefix, path) {
13b84ec7
LP
666 r = cg_attach(controller, prefix, pid);
667 if (r >= 0)
668 break;
669 }
670 }
671
fecffe5d 672 return 0;
13b84ec7
LP
673}
674
2d76d14e
LP
675int cg_set_group_access(
676 const char *controller,
677 const char *path,
678 mode_t mode,
679 uid_t uid,
680 gid_t gid) {
681
574d5f2d 682 _cleanup_free_ char *fs = NULL;
8c6db833
LP
683 int r;
684
8c6db833
LP
685 assert(path);
686
8d53b453
LP
687 if (mode != (mode_t) -1)
688 mode &= 0777;
689
690 r = cg_get_path(controller, path, NULL, &fs);
691 if (r < 0)
8c6db833
LP
692 return r;
693
574d5f2d 694 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
695}
696
974efc46
LP
697int cg_set_task_access(
698 const char *controller,
699 const char *path,
700 mode_t mode,
701 uid_t uid,
4ad49000 702 gid_t gid) {
974efc46
LP
703
704 _cleanup_free_ char *fs = NULL, *procs = NULL;
8c6db833
LP
705 int r;
706
8c6db833
LP
707 assert(path);
708
4ad49000 709 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
8d53b453
LP
710 return 0;
711
712 if (mode != (mode_t) -1)
713 mode &= 0666;
714
b043cd0b 715 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 716 if (r < 0)
8c6db833
LP
717 return r;
718
719 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
720 if (r < 0)
721 return r;
8c6db833 722
b043cd0b
LP
723 /* Compatibility, Always keep values for "tasks" in sync with
724 * "cgroup.procs" */
725 r = cg_get_path(controller, path, "tasks", &procs);
974efc46
LP
726 if (r < 0)
727 return r;
728
729 return chmod_and_chown(procs, mode, uid, gid);
8c6db833
LP
730}
731
7027ff61 732int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
733 _cleanup_fclose_ FILE *f = NULL;
734 char line[LINE_MAX];
8af8afd6 735 const char *fs;
c6c18be3 736 size_t cs;
8c6db833 737
8c6db833 738 assert(path);
c6c18be3 739 assert(pid >= 0);
8c6db833 740
8af8afd6
LP
741 if (controller) {
742 if (!cg_controller_is_valid(controller, true))
743 return -EINVAL;
78edb35a 744
8af8afd6
LP
745 controller = normalize_controller(controller);
746 } else
7027ff61
LP
747 controller = SYSTEMD_CGROUP_CONTROLLER;
748
b68fa010 749 fs = procfs_file_alloca(pid, "cgroup");
8c6db833 750
c6c18be3 751 f = fopen(fs, "re");
4c633005
LP
752 if (!f)
753 return errno == ENOENT ? -ESRCH : -errno;
754
c6c18be3
LP
755 cs = strlen(controller);
756
7027ff61 757 FOREACH_LINE(line, f, return -errno) {
8af8afd6
LP
758 char *l, *p, *w, *e;
759 size_t k;
760 char *state;
761 bool found = false;
c6c18be3
LP
762
763 truncate_nl(line);
764
7027ff61
LP
765 l = strchr(line, ':');
766 if (!l)
c6c18be3
LP
767 continue;
768
769 l++;
8af8afd6
LP
770 e = strchr(l, ':');
771 if (!e)
c6c18be3
LP
772 continue;
773
8af8afd6
LP
774 *e = 0;
775
776 FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
777
778 if (k == cs && memcmp(w, controller, cs) == 0) {
779 found = true;
780 break;
781 }
782
783 if (k == 5 + cs &&
784 memcmp(w, "name=", 5) == 0 &&
785 memcmp(w+5, controller, cs) == 0) {
786 found = true;
787 break;
788 }
789 }
790
791 if (!found)
c6c18be3
LP
792 continue;
793
8af8afd6 794 p = strdup(e + 1);
7027ff61
LP
795 if (!p)
796 return -ENOMEM;
c6c18be3
LP
797
798 *path = p;
7027ff61 799 return 0;
c6c18be3
LP
800 }
801
7027ff61 802 return -ENOENT;
8c6db833
LP
803}
804
805int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61
LP
806 _cleanup_free_ char *fs = NULL, *contents = NULL;
807 char *sc;
8c6db833
LP
808 int r;
809
8c6db833
LP
810 assert(agent);
811
7027ff61
LP
812 r = cg_get_path(controller, NULL, "release_agent", &fs);
813 if (r < 0)
c6c18be3 814 return r;
8c6db833 815
7027ff61
LP
816 r = read_one_line_file(fs, &contents);
817 if (r < 0)
818 return r;
8c6db833
LP
819
820 sc = strstrip(contents);
8c6db833 821 if (sc[0] == 0) {
7027ff61 822 r = write_string_file(fs, agent);
574d5f2d 823 if (r < 0)
7027ff61
LP
824 return r;
825 } else if (!streq(sc, agent))
826 return -EEXIST;
8c6db833 827
c6c18be3
LP
828 free(fs);
829 fs = NULL;
7027ff61
LP
830 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
831 if (r < 0)
832 return r;
8c6db833
LP
833
834 free(contents);
835 contents = NULL;
7027ff61
LP
836 r = read_one_line_file(fs, &contents);
837 if (r < 0)
838 return r;
8c6db833
LP
839
840 sc = strstrip(contents);
8c6db833 841 if (streq(sc, "0")) {
7027ff61
LP
842 r = write_string_file(fs, "1");
843 if (r < 0)
844 return r;
c6c18be3 845
7027ff61
LP
846 return 1;
847 }
8c6db833 848
7027ff61
LP
849 if (!streq(sc, "1"))
850 return -EIO;
8c6db833 851
7027ff61 852 return 0;
8c6db833
LP
853}
854
ad929bcc
KS
855int cg_uninstall_release_agent(const char *controller) {
856 _cleanup_free_ char *fs = NULL;
857 int r;
858
ac9ef333
LP
859 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
860 if (r < 0)
861 return r;
862
863 r = write_string_file(fs, "0");
864 if (r < 0)
865 return r;
866
867 free(fs);
868 fs = NULL;
869
ad929bcc
KS
870 r = cg_get_path(controller, NULL, "release_agent", &fs);
871 if (r < 0)
872 return r;
873
874 r = write_string_file(fs, "");
875 if (r < 0)
876 return r;
877
ac9ef333 878 return 0;
ad929bcc
KS
879}
880
8c6db833 881int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
7027ff61 882 _cleanup_fclose_ FILE *f = NULL;
c3175a7f 883 pid_t pid = 0, self_pid;
c6c18be3 884 bool found = false;
7027ff61 885 int r;
8c6db833 886
8c6db833
LP
887 assert(path);
888
b043cd0b 889 r = cg_enumerate_processes(controller, path, &f);
c3175a7f 890 if (r < 0)
4c633005 891 return r == -ENOENT ? 1 : r;
8c6db833 892
c3175a7f
LP
893 self_pid = getpid();
894
c6c18be3 895 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 896
c3175a7f 897 if (ignore_self && pid == self_pid)
c6c18be3 898 continue;
8c6db833 899
c6c18be3
LP
900 found = true;
901 break;
8c6db833
LP
902 }
903
c6c18be3
LP
904 if (r < 0)
905 return r;
8c6db833 906
c6c18be3 907 return !found;
8c6db833
LP
908}
909
910int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
7027ff61 911 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 912 char *fn;
7027ff61 913 int r;
8c6db833 914
8c6db833
LP
915 assert(path);
916
c3175a7f
LP
917 r = cg_is_empty(controller, path, ignore_self);
918 if (r <= 0)
35d2e7ec
LP
919 return r;
920
c3175a7f
LP
921 r = cg_enumerate_subgroups(controller, path, &d);
922 if (r < 0)
4c633005 923 return r == -ENOENT ? 1 : r;
8c6db833 924
35d2e7ec 925 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 926 _cleanup_free_ char *p = NULL;
8c6db833 927
7027ff61 928 p = strjoin(path, "/", fn, NULL);
35d2e7ec 929 free(fn);
7027ff61
LP
930 if (!p)
931 return -ENOMEM;
8c6db833 932
35d2e7ec 933 r = cg_is_empty_recursive(controller, p, ignore_self);
35d2e7ec 934 if (r <= 0)
7027ff61 935 return r;
35d2e7ec
LP
936 }
937
7027ff61
LP
938 if (r < 0)
939 return r;
35d2e7ec 940
7027ff61 941 return 1;
35d2e7ec
LP
942}
943
944int cg_split_spec(const char *spec, char **controller, char **path) {
945 const char *e;
946 char *t = NULL, *u = NULL;
5954c074 947 _cleanup_free_ char *v = NULL;
35d2e7ec
LP
948
949 assert(spec);
35d2e7ec
LP
950
951 if (*spec == '/') {
e884315e
LP
952 if (!path_is_safe(spec))
953 return -EINVAL;
35d2e7ec
LP
954
955 if (path) {
246aa6dd
LP
956 t = strdup(spec);
957 if (!t)
35d2e7ec
LP
958 return -ENOMEM;
959
5954c074 960 path_kill_slashes(t);
35d2e7ec 961 *path = t;
8c6db833
LP
962 }
963
35d2e7ec
LP
964 if (controller)
965 *controller = NULL;
966
967 return 0;
8c6db833
LP
968 }
969
246aa6dd
LP
970 e = strchr(spec, ':');
971 if (!e) {
78edb35a 972 if (!cg_controller_is_valid(spec, true))
35d2e7ec
LP
973 return -EINVAL;
974
975 if (controller) {
5954c074 976 t = strdup(normalize_controller(spec));
246aa6dd 977 if (!t)
35d2e7ec
LP
978 return -ENOMEM;
979
980 *controller = t;
981 }
982
983 if (path)
984 *path = NULL;
985
986 return 0;
8c6db833
LP
987 }
988
5954c074
LP
989 v = strndup(spec, e-spec);
990 if (!v)
991 return -ENOMEM;
992 t = strdup(normalize_controller(v));
e884315e
LP
993 if (!t)
994 return -ENOMEM;
78edb35a 995 if (!cg_controller_is_valid(t, true)) {
e884315e 996 free(t);
35d2e7ec 997 return -EINVAL;
246aa6dd
LP
998 }
999
baa89da4
LP
1000 if (streq(e+1, "")) {
1001 u = strdup("/");
1002 if (!u) {
1003 free(t);
1004 return -ENOMEM;
1005 }
1006 } else {
1007 u = strdup(e+1);
1008 if (!u) {
1009 free(t);
1010 return -ENOMEM;
1011 }
35d2e7ec 1012
baa89da4
LP
1013 if (!path_is_safe(u) ||
1014 !path_is_absolute(u)) {
1015 free(t);
1016 free(u);
1017 return -EINVAL;
1018 }
1019
1020 path_kill_slashes(u);
1021 }
5954c074 1022
35d2e7ec
LP
1023 if (controller)
1024 *controller = t;
e884315e
LP
1025 else
1026 free(t);
35d2e7ec
LP
1027
1028 if (path)
1029 *path = u;
e884315e
LP
1030 else
1031 free(u);
35d2e7ec
LP
1032
1033 return 0;
8c6db833 1034}
c6c18be3 1035
7027ff61 1036int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1037 _cleanup_free_ char *c = NULL, *p = NULL;
1038 char *t;
35d2e7ec
LP
1039 int r;
1040
1041 assert(path);
1042 assert(result);
1043
73e231ab 1044 /* First, check if it already is a filesystem path */
7027ff61 1045 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1046
b69d29ce
LP
1047 t = strdup(path);
1048 if (!t)
35d2e7ec
LP
1049 return -ENOMEM;
1050
5954c074 1051 path_kill_slashes(t);
35d2e7ec
LP
1052 *result = t;
1053 return 0;
1054 }
1055
73e231ab 1056 /* Otherwise, treat it as cg spec */
b69d29ce
LP
1057 r = cg_split_spec(path, &c, &p);
1058 if (r < 0)
35d2e7ec
LP
1059 return r;
1060
78edb35a 1061 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
35d2e7ec 1062}
1f73f0f1 1063
7027ff61 1064int cg_get_root_path(char **path) {
9444b1f2 1065 char *p, *e;
7027ff61
LP
1066 int r;
1067
1068 assert(path);
1069
9444b1f2 1070 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1071 if (r < 0)
1072 return r;
1073
9444b1f2
LP
1074 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1075 if (e)
7027ff61
LP
1076 *e = 0;
1077
1f73f0f1
LP
1078 *path = p;
1079 return 0;
1080}
b59e2465 1081
751bc6ac
LP
1082int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1083 _cleanup_free_ char *rt = NULL;
1084 char *p;
ba1261bc
LP
1085 int r;
1086
e9174f29 1087 assert(cgroup);
751bc6ac 1088 assert(shifted);
e9174f29
LP
1089
1090 if (!root) {
1091 /* If the root was specified let's use that, otherwise
1092 * let's determine it from PID 1 */
1093
751bc6ac 1094 r = cg_get_root_path(&rt);
e9174f29
LP
1095 if (r < 0)
1096 return r;
1097
751bc6ac 1098 root = rt;
e9174f29 1099 }
ba1261bc 1100
751bc6ac
LP
1101 p = path_startswith(cgroup, root);
1102 if (p)
1103 *shifted = p - 1;
1104 else
1105 *shifted = cgroup;
1106
1107 return 0;
1108}
1109
1110int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1111 _cleanup_free_ char *raw = NULL;
1112 const char *c;
1113 int r;
1114
1115 assert(pid >= 0);
1116 assert(cgroup);
1117
1118 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
7027ff61 1119 if (r < 0)
ba1261bc 1120 return r;
ba1261bc 1121
751bc6ac
LP
1122 r = cg_shift_path(raw, root, &c);
1123 if (r < 0)
1124 return r;
ba1261bc 1125
751bc6ac
LP
1126 if (c == raw) {
1127 *cgroup = raw;
1128 raw = NULL;
1129 } else {
1130 char *n;
ba1261bc 1131
751bc6ac
LP
1132 n = strdup(c);
1133 if (!n)
ba1261bc 1134 return -ENOMEM;
ba1261bc 1135
751bc6ac
LP
1136 *cgroup = n;
1137 }
ba1261bc
LP
1138
1139 return 0;
1140}
1141
7027ff61 1142int cg_path_decode_unit(const char *cgroup, char **unit){
d7bd3de0 1143 char *e, *c, *s;
ef1673d1
MT
1144
1145 assert(cgroup);
6c03089c 1146 assert(unit);
ef1673d1 1147
6c03089c
LP
1148 e = strchrnul(cgroup, '/');
1149 c = strndupa(cgroup, e - cgroup);
ae018d9b 1150 c = cg_unescape(c);
ef1673d1 1151
f78e6385 1152 if (!unit_name_is_valid(c, TEMPLATE_INVALID))
6c03089c 1153 return -EINVAL;
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
9444b1f2 1163static const char *skip_slices(const char *p) {
9444b1f2
LP
1164 /* Skips over all slice assignments */
1165
1166 for (;;) {
1021b21b
LP
1167 size_t n;
1168
9444b1f2
LP
1169 p += strspn(p, "/");
1170
1171 n = strcspn(p, "/");
1172 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
1173 return p;
1174
1175 p += n;
1176 }
1177}
1178
6c03089c
LP
1179int cg_path_get_unit(const char *path, char **unit) {
1180 const char *e;
1181
1182 assert(path);
1183 assert(unit);
1184
9444b1f2 1185 e = skip_slices(path);
6c03089c 1186
7027ff61 1187 return cg_path_decode_unit(e, unit);
6c03089c
LP
1188}
1189
1190int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1191 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1192 int r;
ba1261bc 1193
ef1673d1
MT
1194 assert(unit);
1195
7027ff61 1196 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1197 if (r < 0)
1198 return r;
1199
6c03089c
LP
1200 return cg_path_get_unit(cgroup, unit);
1201}
ef1673d1 1202
d4fffc4b
ZJS
1203/**
1204 * Skip session-*.scope, but require it to be there.
1205 */
9444b1f2
LP
1206static const char *skip_session(const char *p) {
1207 size_t n;
1208
1209 assert(p);
1210
1211 p += strspn(p, "/");
1212
1213 n = strcspn(p, "/");
d4fffc4b
ZJS
1214 if (n < strlen("session-x.scope") || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
1215 return NULL;
1216
1217 p += n;
1218 p += strspn(p, "/");
1219
1220 return p;
1221}
1222
1223/**
1224 * Skip user@*.service, but require it to be there.
1225 */
1226static const char *skip_user_manager(const char *p) {
1227 size_t n;
1228
1229 assert(p);
1230
1231 p += strspn(p, "/");
1232
1233 n = strcspn(p, "/");
1234 if (n < strlen("user@x.service") || memcmp(p, "user@", 5) != 0 || memcmp(p + n - 8, ".service", 8) != 0)
6c03089c 1235 return NULL;
ef1673d1 1236
9444b1f2
LP
1237 p += n;
1238 p += strspn(p, "/");
1239
1240 return p;
1241}
1242
6c03089c 1243int cg_path_get_user_unit(const char *path, char **unit) {
d4fffc4b 1244 const char *e, *t;
ef1673d1 1245
6c03089c 1246 assert(path);
ba1261bc
LP
1247 assert(unit);
1248
6c03089c
LP
1249 /* We always have to parse the path from the beginning as unit
1250 * cgroups might have arbitrary child cgroups and we shouldn't get
1251 * confused by those */
ba1261bc 1252
9444b1f2
LP
1253 /* Skip slices, if there are any */
1254 e = skip_slices(path);
ba1261bc 1255
d4fffc4b
ZJS
1256 /* Skip the session scope... */
1257 t = skip_session(e);
1258 if (t)
1259 /* ... and skip more slices if there's one */
1260 e = skip_slices(t);
1261 else {
1262 /* ... or require a user manager unit to be there */
1263 e = skip_user_manager(e);
1264 if (!e)
1265 return -ENOENT;
1266 }
6c03089c 1267
7027ff61 1268 return cg_path_decode_unit(e, unit);
ef1673d1 1269}
ba1261bc 1270
ef1673d1 1271int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1272 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1273 int r;
1274
1275 assert(unit);
1276
7027ff61 1277 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1278 if (r < 0)
1279 return r;
1280
1281 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1282}
e884315e 1283
7027ff61 1284int cg_path_get_machine_name(const char *path, char **machine) {
89f7c846
LP
1285 _cleanup_free_ char *u = NULL, *sl = NULL;
1286 int r;
374ec6ab 1287
89f7c846
LP
1288 r = cg_path_get_unit(path, &u);
1289 if (r < 0)
1290 return r;
7027ff61 1291
89f7c846
LP
1292 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1293 if (!sl)
ae018d9b 1294 return -ENOMEM;
aff38e74 1295
89f7c846 1296 return readlink_malloc(sl, machine);
7027ff61
LP
1297}
1298
1299int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1300 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1301 int r;
1302
1303 assert(machine);
1304
1305 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1306 if (r < 0)
1307 return r;
1308
1309 return cg_path_get_machine_name(cgroup, machine);
1310}
1311
1312int cg_path_get_session(const char *path, char **session) {
374ec6ab 1313 const char *e, *n, *x;
af08d2f9 1314 char *s;
374ec6ab 1315 size_t l;
7027ff61
LP
1316
1317 assert(path);
7027ff61 1318
9444b1f2
LP
1319 /* Skip slices, if there are any */
1320 e = skip_slices(path);
7027ff61 1321
7027ff61 1322 n = strchrnul(e, '/');
374ec6ab 1323 if (e == n)
7027ff61 1324 return -ENOENT;
374ec6ab
LP
1325
1326 s = strndupa(e, n - e);
1327 s = cg_unescape(s);
1328
1329 x = startswith(s, "session-");
1330 if (!x)
1331 return -ENOENT;
1332 if (!endswith(x, ".scope"))
7027ff61
LP
1333 return -ENOENT;
1334
374ec6ab
LP
1335 l = strlen(x);
1336 if (l <= 6)
1337 return -ENOENT;
1338
af08d2f9
LP
1339 if (session) {
1340 char *r;
1341
1342 r = strndup(x, l - 6);
1343 if (!r)
1344 return -ENOMEM;
1345
1346 *session = r;
1347 }
7027ff61 1348
7027ff61
LP
1349 return 0;
1350}
1351
1352int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1353 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1354 int r;
1355
7027ff61
LP
1356 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1357 if (r < 0)
1358 return r;
1359
1360 return cg_path_get_session(cgroup, session);
1361}
1362
ae018d9b 1363int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1364 _cleanup_free_ char *slice = NULL;
674eb685 1365 const char *start, *end;
ae018d9b 1366 char *s;
674eb685 1367 uid_t u;
374ec6ab 1368 int r;
ae018d9b
LP
1369
1370 assert(path);
ae018d9b 1371
374ec6ab
LP
1372 r = cg_path_get_slice(path, &slice);
1373 if (r < 0)
1374 return r;
ae018d9b 1375
674eb685
LP
1376 start = startswith(slice, "user-");
1377 if (!start)
ae018d9b 1378 return -ENOENT;
674eb685
LP
1379 end = endswith(slice, ".slice");
1380 if (!end)
ae018d9b
LP
1381 return -ENOENT;
1382
674eb685 1383 s = strndupa(start, end - start);
ae018d9b 1384 if (!s)
674eb685 1385 return -ENOENT;
ae018d9b 1386
674eb685
LP
1387 if (parse_uid(s, &u) < 0)
1388 return -EIO;
1389
1390 if (uid)
1391 *uid = u;
1392
1393 return 0;
ae018d9b
LP
1394}
1395
1396int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1397 _cleanup_free_ char *cgroup = NULL;
1398 int r;
1399
ae018d9b
LP
1400 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1401 if (r < 0)
1402 return r;
1403
1404 return cg_path_get_owner_uid(cgroup, uid);
1405}
1406
1021b21b
LP
1407int cg_path_get_slice(const char *p, char **slice) {
1408 const char *e = NULL;
1409 size_t m = 0;
1410
1411 assert(p);
1412 assert(slice);
1413
1414 for (;;) {
1415 size_t n;
1416
1417 p += strspn(p, "/");
1418
1419 n = strcspn(p, "/");
1420 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1421 char *s;
1422
1423 if (!e)
1424 return -ENOENT;
1425
1426 s = strndup(e, m);
1427 if (!s)
1428 return -ENOMEM;
1429
1430 *slice = s;
1431 return 0;
1432 }
1433
1434 e = p;
1435 m = n;
1436
1437 p += n;
1438 }
1439}
1440
1441int cg_pid_get_slice(pid_t pid, char **slice) {
1442 _cleanup_free_ char *cgroup = NULL;
1443 int r;
1444
1445 assert(slice);
1446
1447 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1448 if (r < 0)
1449 return r;
1450
1451 return cg_path_get_slice(cgroup, slice);
1452}
1453
ae018d9b
LP
1454char *cg_escape(const char *p) {
1455 bool need_prefix = false;
1456
1457 /* This implements very minimal escaping for names to be used
1458 * as file names in the cgroup tree: any name which might
1459 * conflict with a kernel name or is prefixed with '_' is
1460 * prefixed with a '_'. That way, when reading cgroup names it
1461 * is sufficient to remove a single prefixing underscore if
1462 * there is one. */
1463
1464 /* The return value of this function (unlike cg_unescape())
1465 * needs free()! */
1466
a0ab5665
LP
1467 if (p[0] == 0 ||
1468 p[0] == '_' ||
1469 p[0] == '.' ||
1470 streq(p, "notify_on_release") ||
1471 streq(p, "release_agent") ||
1472 streq(p, "tasks"))
ae018d9b
LP
1473 need_prefix = true;
1474 else {
1475 const char *dot;
1476
1477 dot = strrchr(p, '.');
1478 if (dot) {
1479
1480 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1481 need_prefix = true;
1482 else {
1483 char *n;
1484
1485 n = strndupa(p, dot - p);
1486
1487 if (check_hierarchy(n) >= 0)
1488 need_prefix = true;
1489 }
1490 }
1491 }
1492
1493 if (need_prefix)
1494 return strappend("_", p);
1495 else
1496 return strdup(p);
1497}
1498
1499char *cg_unescape(const char *p) {
1500 assert(p);
1501
1502 /* The return value of this function (unlike cg_escape())
1503 * doesn't need free()! */
1504
1505 if (p[0] == '_')
1506 return (char*) p+1;
1507
1508 return (char*) p;
1509}
78edb35a
LP
1510
1511#define CONTROLLER_VALID \
4b549144 1512 DIGITS LETTERS \
78edb35a
LP
1513 "_"
1514
1515bool cg_controller_is_valid(const char *p, bool allow_named) {
1516 const char *t, *s;
1517
1518 if (!p)
1519 return false;
1520
1521 if (allow_named) {
1522 s = startswith(p, "name=");
1523 if (s)
1524 p = s;
1525 }
1526
1527 if (*p == 0 || *p == '_')
1528 return false;
1529
1530 for (t = p; *t; t++)
1531 if (!strchr(CONTROLLER_VALID, *t))
1532 return false;
1533
1534 if (t - p > FILENAME_MAX)
1535 return false;
1536
1537 return true;
1538}
a016b922
LP
1539
1540int cg_slice_to_path(const char *unit, char **ret) {
1541 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1542 const char *dash;
1543
1544 assert(unit);
1545 assert(ret);
1546
f78e6385 1547 if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
a016b922
LP
1548 return -EINVAL;
1549
1550 if (!endswith(unit, ".slice"))
1551 return -EINVAL;
1552
1553 p = unit_name_to_prefix(unit);
1554 if (!p)
1555 return -ENOMEM;
1556
1557 dash = strchr(p, '-');
1558 while (dash) {
1559 _cleanup_free_ char *escaped = NULL;
1560 char n[dash - p + sizeof(".slice")];
1561
1562 strcpy(stpncpy(n, p, dash - p), ".slice");
1563
f78e6385 1564 if (!unit_name_is_valid(n, TEMPLATE_INVALID))
a016b922
LP
1565 return -EINVAL;
1566
1567 escaped = cg_escape(n);
1568 if (!escaped)
1569 return -ENOMEM;
1570
1571 if (!strextend(&s, escaped, "/", NULL))
1572 return -ENOMEM;
1573
1574 dash = strchr(dash+1, '-');
1575 }
1576
1577 e = cg_escape(unit);
1578 if (!e)
1579 return -ENOMEM;
1580
1581 if (!strextend(&s, e, NULL))
1582 return -ENOMEM;
1583
1584 *ret = s;
1585 s = NULL;
1586
1587 return 0;
1588}
4ad49000
LP
1589
1590int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1591 _cleanup_free_ char *p = NULL;
1592 int r;
1593
1594 r = cg_get_path(controller, path, attribute, &p);
1595 if (r < 0)
1596 return r;
1597
1598 return write_string_file(p, value);
1599}
1600
1601static const char mask_names[] =
1602 "cpu\0"
1603 "cpuacct\0"
1604 "blkio\0"
1605 "memory\0"
1606 "devices\0";
1607
13b84ec7 1608int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1609 CGroupControllerMask bit = 1;
1610 const char *n;
1611 int r;
1612
1613 /* This one will create a cgroup in our private tree, but also
1614 * duplicate it in the trees specified in mask, and remove it
1615 * in all others */
1616
1617 /* First create the cgroup in our own hierarchy. */
1618 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1619 if (r < 0)
1620 return r;
1621
1622 /* Then, do the same in the other hierarchies */
1623 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1624 if (mask & bit)
4ad49000 1625 cg_create(n, path);
13b84ec7 1626 else if (supported & bit)
4ad49000
LP
1627 cg_trim(n, path, true);
1628
1629 bit <<= 1;
1630 }
1631
13b84ec7 1632 return 0;
4ad49000
LP
1633}
1634
13b84ec7 1635int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
4ad49000
LP
1636 CGroupControllerMask bit = 1;
1637 const char *n;
1638 int r;
1639
1640 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1641 if (r < 0)
1642 return r;
4ad49000
LP
1643
1644 NULSTR_FOREACH(n, mask_names) {
13b84ec7
LP
1645 if (supported & bit)
1646 cg_attach_fallback(n, path, pid);
4ad49000
LP
1647
1648 bit <<= 1;
1649 }
1650
13b84ec7 1651 return 0;
4ad49000
LP
1652}
1653
13b84ec7 1654int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
6c12b52e
LP
1655 Iterator i;
1656 void *pidp;
1657 int r = 0;
1658
1659 SET_FOREACH(pidp, pids, i) {
1660 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1661 int q;
6c12b52e 1662
13b84ec7
LP
1663 q = cg_attach_everywhere(supported, path, pid);
1664 if (q < 0)
1665 r = q;
6c12b52e
LP
1666 }
1667
1668 return r;
1669}
1670
03b90d4b 1671int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
4ad49000
LP
1672 CGroupControllerMask bit = 1;
1673 const char *n;
1674 int r;
1675
13b84ec7
LP
1676 if (!path_equal(from, to)) {
1677 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1678 if (r < 0)
1679 return r;
1680 }
4ad49000
LP
1681
1682 NULSTR_FOREACH(n, mask_names) {
03b90d4b
LP
1683 if (supported & bit) {
1684 const char *p = NULL;
1685
1686 if (to_callback)
1687 p = to_callback(bit, userdata);
1688
1689 if (!p)
1690 p = to;
1691
1692 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1693 }
4ad49000
LP
1694
1695 bit <<= 1;
1696 }
1697
13b84ec7 1698 return 0;
4ad49000
LP
1699}
1700
13b84ec7 1701int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1702 CGroupControllerMask bit = 1;
1703 const char *n;
1704 int r;
1705
1706 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1707 if (r < 0)
1708 return r;
1709
1710 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1711 if (supported & bit)
4ad49000
LP
1712 cg_trim(n, path, delete_root);
1713
1714 bit <<= 1;
1715 }
1716
13b84ec7 1717 return 0;
4ad49000
LP
1718}
1719
1720CGroupControllerMask cg_mask_supported(void) {
1721 CGroupControllerMask bit = 1, mask = 0;
1722 const char *n;
1723
1724 NULSTR_FOREACH(n, mask_names) {
1725 if (check_hierarchy(n) >= 0)
1726 mask |= bit;
1727
1728 bit <<= 1;
1729 }
1730
1731 return mask;
1732}