]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
util: modernize readlink_malloc() a bit
[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 */
512 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
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
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
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) {
9444b1f2 1285 const char *e, *n, *x;
ae018d9b 1286 char *s, *r;
374ec6ab 1287 size_t l;
7027ff61
LP
1288
1289 assert(path);
1290 assert(machine);
1291
9444b1f2
LP
1292 /* Skip slices, if there are any */
1293 e = skip_slices(path);
7027ff61
LP
1294
1295 n = strchrnul(e, '/');
1296 if (e == n)
1297 return -ENOENT;
1298
ae018d9b 1299 s = strndupa(e, n - e);
9444b1f2
LP
1300 s = cg_unescape(s);
1301
374ec6ab 1302 x = startswith(s, "machine-");
9444b1f2
LP
1303 if (!x)
1304 return -ENOENT;
374ec6ab
LP
1305 if (!endswith(x, ".scope"))
1306 return -ENOENT;
1307
1308 l = strlen(x);
1309 if (l <= 6)
1310 return -ENOENT;
7027ff61 1311
374ec6ab 1312 r = strndup(x, l - 6);
ae018d9b
LP
1313 if (!r)
1314 return -ENOMEM;
aff38e74 1315
ae018d9b 1316 *machine = r;
7027ff61
LP
1317 return 0;
1318}
1319
1320int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1321 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1322 int r;
1323
1324 assert(machine);
1325
1326 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1327 if (r < 0)
1328 return r;
1329
1330 return cg_path_get_machine_name(cgroup, machine);
1331}
1332
1333int cg_path_get_session(const char *path, char **session) {
374ec6ab 1334 const char *e, *n, *x;
af08d2f9 1335 char *s;
374ec6ab 1336 size_t l;
7027ff61
LP
1337
1338 assert(path);
7027ff61 1339
9444b1f2
LP
1340 /* Skip slices, if there are any */
1341 e = skip_slices(path);
7027ff61 1342
7027ff61 1343 n = strchrnul(e, '/');
374ec6ab 1344 if (e == n)
7027ff61 1345 return -ENOENT;
374ec6ab
LP
1346
1347 s = strndupa(e, n - e);
1348 s = cg_unescape(s);
1349
1350 x = startswith(s, "session-");
1351 if (!x)
1352 return -ENOENT;
1353 if (!endswith(x, ".scope"))
7027ff61
LP
1354 return -ENOENT;
1355
374ec6ab
LP
1356 l = strlen(x);
1357 if (l <= 6)
1358 return -ENOENT;
1359
af08d2f9
LP
1360 if (session) {
1361 char *r;
1362
1363 r = strndup(x, l - 6);
1364 if (!r)
1365 return -ENOMEM;
1366
1367 *session = r;
1368 }
7027ff61 1369
7027ff61
LP
1370 return 0;
1371}
1372
1373int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1374 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1375 int r;
1376
7027ff61
LP
1377 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1378 if (r < 0)
1379 return r;
1380
1381 return cg_path_get_session(cgroup, session);
1382}
1383
ae018d9b 1384int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1385 _cleanup_free_ char *slice = NULL;
674eb685 1386 const char *start, *end;
ae018d9b 1387 char *s;
674eb685 1388 uid_t u;
374ec6ab 1389 int r;
ae018d9b
LP
1390
1391 assert(path);
ae018d9b 1392
374ec6ab
LP
1393 r = cg_path_get_slice(path, &slice);
1394 if (r < 0)
1395 return r;
ae018d9b 1396
674eb685
LP
1397 start = startswith(slice, "user-");
1398 if (!start)
ae018d9b 1399 return -ENOENT;
674eb685
LP
1400 end = endswith(slice, ".slice");
1401 if (!end)
ae018d9b
LP
1402 return -ENOENT;
1403
674eb685 1404 s = strndupa(start, end - start);
ae018d9b 1405 if (!s)
674eb685 1406 return -ENOENT;
ae018d9b 1407
674eb685
LP
1408 if (parse_uid(s, &u) < 0)
1409 return -EIO;
1410
1411 if (uid)
1412 *uid = u;
1413
1414 return 0;
ae018d9b
LP
1415}
1416
1417int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1418 _cleanup_free_ char *cgroup = NULL;
1419 int r;
1420
ae018d9b
LP
1421 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1422 if (r < 0)
1423 return r;
1424
1425 return cg_path_get_owner_uid(cgroup, uid);
1426}
1427
1021b21b
LP
1428int cg_path_get_slice(const char *p, char **slice) {
1429 const char *e = NULL;
1430 size_t m = 0;
1431
1432 assert(p);
1433 assert(slice);
1434
1435 for (;;) {
1436 size_t n;
1437
1438 p += strspn(p, "/");
1439
1440 n = strcspn(p, "/");
1441 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1442 char *s;
1443
1444 if (!e)
1445 return -ENOENT;
1446
1447 s = strndup(e, m);
1448 if (!s)
1449 return -ENOMEM;
1450
1451 *slice = s;
1452 return 0;
1453 }
1454
1455 e = p;
1456 m = n;
1457
1458 p += n;
1459 }
1460}
1461
1462int cg_pid_get_slice(pid_t pid, char **slice) {
1463 _cleanup_free_ char *cgroup = NULL;
1464 int r;
1465
1466 assert(slice);
1467
1468 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1469 if (r < 0)
1470 return r;
1471
1472 return cg_path_get_slice(cgroup, slice);
1473}
1474
ae018d9b
LP
1475char *cg_escape(const char *p) {
1476 bool need_prefix = false;
1477
1478 /* This implements very minimal escaping for names to be used
1479 * as file names in the cgroup tree: any name which might
1480 * conflict with a kernel name or is prefixed with '_' is
1481 * prefixed with a '_'. That way, when reading cgroup names it
1482 * is sufficient to remove a single prefixing underscore if
1483 * there is one. */
1484
1485 /* The return value of this function (unlike cg_unescape())
1486 * needs free()! */
1487
a0ab5665
LP
1488 if (p[0] == 0 ||
1489 p[0] == '_' ||
1490 p[0] == '.' ||
1491 streq(p, "notify_on_release") ||
1492 streq(p, "release_agent") ||
1493 streq(p, "tasks"))
ae018d9b
LP
1494 need_prefix = true;
1495 else {
1496 const char *dot;
1497
1498 dot = strrchr(p, '.');
1499 if (dot) {
1500
1501 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1502 need_prefix = true;
1503 else {
1504 char *n;
1505
1506 n = strndupa(p, dot - p);
1507
1508 if (check_hierarchy(n) >= 0)
1509 need_prefix = true;
1510 }
1511 }
1512 }
1513
1514 if (need_prefix)
1515 return strappend("_", p);
1516 else
1517 return strdup(p);
1518}
1519
1520char *cg_unescape(const char *p) {
1521 assert(p);
1522
1523 /* The return value of this function (unlike cg_escape())
1524 * doesn't need free()! */
1525
1526 if (p[0] == '_')
1527 return (char*) p+1;
1528
1529 return (char*) p;
1530}
78edb35a
LP
1531
1532#define CONTROLLER_VALID \
4b549144 1533 DIGITS LETTERS \
78edb35a
LP
1534 "_"
1535
1536bool cg_controller_is_valid(const char *p, bool allow_named) {
1537 const char *t, *s;
1538
1539 if (!p)
1540 return false;
1541
1542 if (allow_named) {
1543 s = startswith(p, "name=");
1544 if (s)
1545 p = s;
1546 }
1547
1548 if (*p == 0 || *p == '_')
1549 return false;
1550
1551 for (t = p; *t; t++)
1552 if (!strchr(CONTROLLER_VALID, *t))
1553 return false;
1554
1555 if (t - p > FILENAME_MAX)
1556 return false;
1557
1558 return true;
1559}
a016b922
LP
1560
1561int cg_slice_to_path(const char *unit, char **ret) {
1562 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1563 const char *dash;
1564
1565 assert(unit);
1566 assert(ret);
1567
f78e6385 1568 if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
a016b922
LP
1569 return -EINVAL;
1570
1571 if (!endswith(unit, ".slice"))
1572 return -EINVAL;
1573
1574 p = unit_name_to_prefix(unit);
1575 if (!p)
1576 return -ENOMEM;
1577
1578 dash = strchr(p, '-');
1579 while (dash) {
1580 _cleanup_free_ char *escaped = NULL;
1581 char n[dash - p + sizeof(".slice")];
1582
1583 strcpy(stpncpy(n, p, dash - p), ".slice");
1584
f78e6385 1585 if (!unit_name_is_valid(n, TEMPLATE_INVALID))
a016b922
LP
1586 return -EINVAL;
1587
1588 escaped = cg_escape(n);
1589 if (!escaped)
1590 return -ENOMEM;
1591
1592 if (!strextend(&s, escaped, "/", NULL))
1593 return -ENOMEM;
1594
1595 dash = strchr(dash+1, '-');
1596 }
1597
1598 e = cg_escape(unit);
1599 if (!e)
1600 return -ENOMEM;
1601
1602 if (!strextend(&s, e, NULL))
1603 return -ENOMEM;
1604
1605 *ret = s;
1606 s = NULL;
1607
1608 return 0;
1609}
4ad49000
LP
1610
1611int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1612 _cleanup_free_ char *p = NULL;
1613 int r;
1614
1615 r = cg_get_path(controller, path, attribute, &p);
1616 if (r < 0)
1617 return r;
1618
1619 return write_string_file(p, value);
1620}
1621
1622static const char mask_names[] =
1623 "cpu\0"
1624 "cpuacct\0"
1625 "blkio\0"
1626 "memory\0"
1627 "devices\0";
1628
13b84ec7 1629int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1630 CGroupControllerMask bit = 1;
1631 const char *n;
1632 int r;
1633
1634 /* This one will create a cgroup in our private tree, but also
1635 * duplicate it in the trees specified in mask, and remove it
1636 * in all others */
1637
1638 /* First create the cgroup in our own hierarchy. */
1639 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1640 if (r < 0)
1641 return r;
1642
1643 /* Then, do the same in the other hierarchies */
1644 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1645 if (mask & bit)
4ad49000 1646 cg_create(n, path);
13b84ec7 1647 else if (supported & bit)
4ad49000
LP
1648 cg_trim(n, path, true);
1649
1650 bit <<= 1;
1651 }
1652
13b84ec7 1653 return 0;
4ad49000
LP
1654}
1655
13b84ec7 1656int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) {
4ad49000
LP
1657 CGroupControllerMask bit = 1;
1658 const char *n;
1659 int r;
1660
1661 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1662 if (r < 0)
1663 return r;
4ad49000
LP
1664
1665 NULSTR_FOREACH(n, mask_names) {
13b84ec7
LP
1666 if (supported & bit)
1667 cg_attach_fallback(n, path, pid);
4ad49000
LP
1668
1669 bit <<= 1;
1670 }
1671
13b84ec7 1672 return 0;
4ad49000
LP
1673}
1674
13b84ec7 1675int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) {
6c12b52e
LP
1676 Iterator i;
1677 void *pidp;
1678 int r = 0;
1679
1680 SET_FOREACH(pidp, pids, i) {
1681 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1682 int q;
6c12b52e 1683
13b84ec7
LP
1684 q = cg_attach_everywhere(supported, path, pid);
1685 if (q < 0)
1686 r = q;
6c12b52e
LP
1687 }
1688
1689 return r;
1690}
1691
13b84ec7 1692int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to) {
4ad49000
LP
1693 CGroupControllerMask bit = 1;
1694 const char *n;
1695 int r;
1696
13b84ec7
LP
1697 if (!path_equal(from, to)) {
1698 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1699 if (r < 0)
1700 return r;
1701 }
4ad49000
LP
1702
1703 NULSTR_FOREACH(n, mask_names) {
13b84ec7
LP
1704 if (supported & bit)
1705 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
4ad49000
LP
1706
1707 bit <<= 1;
1708 }
1709
13b84ec7 1710 return 0;
4ad49000
LP
1711}
1712
13b84ec7 1713int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1714 CGroupControllerMask bit = 1;
1715 const char *n;
1716 int r;
1717
1718 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1719 if (r < 0)
1720 return r;
1721
1722 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1723 if (supported & bit)
4ad49000
LP
1724 cg_trim(n, path, delete_root);
1725
1726 bit <<= 1;
1727 }
1728
13b84ec7 1729 return 0;
4ad49000
LP
1730}
1731
1732CGroupControllerMask cg_mask_supported(void) {
1733 CGroupControllerMask bit = 1, mask = 0;
1734 const char *n;
1735
1736 NULSTR_FOREACH(n, mask_names) {
1737 if (check_hierarchy(n) >= 0)
1738 mask |= bit;
1739
1740 bit <<= 1;
1741 }
1742
1743 return mask;
1744}