]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
shared: add process-util.[ch]
[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"
8c6db833
LP
33#include "set.h"
34#include "macro.h"
35#include "util.h"
6482f626 36#include "formats-util.h"
0b452006 37#include "process-util.h"
9eb977db 38#include "path-util.h"
ef1673d1 39#include "unit-name.h"
a5c32cff 40#include "fileio.h"
9444b1f2 41#include "special.h"
1434ae6f 42#include "mkdir.h"
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 {
dc8962da 198 if (sigcont && sig != SIGKILL)
430c18ed
LP
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);
da00518b
LP
492 if (r < 0)
493 return r;
494 if (r == 0)
495 return -ENOENT;
70132bd0
LP
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) {
b12afc8c 507 const char *cc;
37099707
LP
508
509 assert(p);
510
b12afc8c
LP
511 if (!filename_is_valid(p))
512 return 0;
513
37099707 514 /* Check if this controller actually really exists */
63c372cb 515 cc = strjoina("/sys/fs/cgroup/", p);
b12afc8c 516 if (laccess(cc, F_OK) < 0)
37099707
LP
517 return -errno;
518
519 return 0;
520}
521
3474ae3c
LP
522int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
523 const char *p;
37099707 524 int r;
dbd821ac 525
3474ae3c 526 assert(fs);
70132bd0 527
78edb35a 528 if (!cg_controller_is_valid(controller, true))
3474ae3c 529 return -EINVAL;
70132bd0 530
37099707 531 /* Normalize the controller syntax */
3474ae3c 532 p = normalize_controller(controller);
8c6db833 533
3474ae3c 534 /* Check if this controller actually really exists */
7027ff61 535 r = check_hierarchy(p);
37099707
LP
536 if (r < 0)
537 return r;
3474ae3c
LP
538
539 return join_path(p, path, suffix, fs);
8c6db833
LP
540}
541
e27796a0 542static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
543 assert(path);
544 assert(sb);
545 assert(ftwbuf);
e27796a0
LP
546
547 if (typeflag != FTW_DP)
548 return 0;
549
550 if (ftwbuf->level < 1)
551 return 0;
552
e27796a0
LP
553 rmdir(path);
554 return 0;
555}
556
8c6db833 557int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 558 _cleanup_free_ char *fs = NULL;
e27796a0 559 int r = 0;
8c6db833 560
8c6db833
LP
561 assert(path);
562
e27796a0
LP
563 r = cg_get_path(controller, path, NULL, &fs);
564 if (r < 0)
8c6db833
LP
565 return r;
566
e27796a0 567 errno = 0;
7027ff61 568 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
e27796a0
LP
569 r = errno ? -errno : -EIO;
570
571 if (delete_root) {
4ad49000
LP
572 if (rmdir(fs) < 0 && errno != ENOENT)
573 return -errno;
e27796a0
LP
574 }
575
e27796a0 576 return r;
8c6db833
LP
577}
578
579int cg_delete(const char *controller, const char *path) {
7027ff61 580 _cleanup_free_ char *parent = NULL;
8c6db833
LP
581 int r;
582
8c6db833
LP
583 assert(path);
584
7027ff61
LP
585 r = path_get_parent(path, &parent);
586 if (r < 0)
35d2e7ec 587 return r;
8c6db833 588
246aa6dd 589 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
4c633005 590 return r == -ENOENT ? 0 : r;
8c6db833
LP
591}
592
1434ae6f
LP
593int cg_create(const char *controller, const char *path) {
594 _cleanup_free_ char *fs = NULL;
595 int r;
596
597 r = cg_get_path_and_check(controller, path, NULL, &fs);
598 if (r < 0)
599 return r;
600
601 r = mkdir_parents(fs, 0755);
602 if (r < 0)
603 return r;
604
605 if (mkdir(fs, 0755) < 0) {
606
607 if (errno == EEXIST)
608 return 0;
609
610 return -errno;
611 }
612
613 return 1;
614}
615
616int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
617 int r, q;
618
619 assert(pid >= 0);
620
621 r = cg_create(controller, path);
622 if (r < 0)
623 return r;
624
625 q = cg_attach(controller, path, pid);
626 if (q < 0)
627 return q;
628
629 /* This does not remove the cgroup on failure */
630 return r;
631}
632
8c6db833 633int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
634 _cleanup_free_ char *fs = NULL;
635 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
636 int r;
637
8c6db833
LP
638 assert(path);
639 assert(pid >= 0);
640
b043cd0b 641 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 642 if (r < 0)
c6c18be3 643 return r;
8c6db833
LP
644
645 if (pid == 0)
646 pid = getpid();
647
de0671ee 648 snprintf(c, sizeof(c), PID_FMT"\n", pid);
8c6db833 649
70c949a4 650 return write_string_file_no_create(fs, c);
8c6db833
LP
651}
652
13b84ec7
LP
653int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
654 int r;
655
656 assert(controller);
657 assert(path);
658 assert(pid >= 0);
659
660 r = cg_attach(controller, path, pid);
661 if (r < 0) {
662 char prefix[strlen(path) + 1];
663
664 /* This didn't work? Then let's try all prefixes of
665 * the destination */
666
fecffe5d 667 PATH_FOREACH_PREFIX(prefix, path) {
13b84ec7
LP
668 r = cg_attach(controller, prefix, pid);
669 if (r >= 0)
670 break;
671 }
672 }
673
fecffe5d 674 return 0;
13b84ec7
LP
675}
676
2d76d14e
LP
677int cg_set_group_access(
678 const char *controller,
679 const char *path,
680 mode_t mode,
681 uid_t uid,
682 gid_t gid) {
683
574d5f2d 684 _cleanup_free_ char *fs = NULL;
8c6db833
LP
685 int r;
686
8c6db833
LP
687 assert(path);
688
fed1e721 689 if (mode != MODE_INVALID)
8d53b453
LP
690 mode &= 0777;
691
692 r = cg_get_path(controller, path, NULL, &fs);
693 if (r < 0)
8c6db833
LP
694 return r;
695
574d5f2d 696 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
697}
698
974efc46
LP
699int cg_set_task_access(
700 const char *controller,
701 const char *path,
702 mode_t mode,
703 uid_t uid,
4ad49000 704 gid_t gid) {
974efc46
LP
705
706 _cleanup_free_ char *fs = NULL, *procs = NULL;
8c6db833
LP
707 int r;
708
8c6db833
LP
709 assert(path);
710
fed1e721 711 if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID)
8d53b453
LP
712 return 0;
713
fed1e721 714 if (mode != MODE_INVALID)
8d53b453
LP
715 mode &= 0666;
716
b043cd0b 717 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 718 if (r < 0)
8c6db833
LP
719 return r;
720
721 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
722 if (r < 0)
723 return r;
8c6db833 724
b043cd0b
LP
725 /* Compatibility, Always keep values for "tasks" in sync with
726 * "cgroup.procs" */
727 r = cg_get_path(controller, path, "tasks", &procs);
974efc46
LP
728 if (r < 0)
729 return r;
730
731 return chmod_and_chown(procs, mode, uid, gid);
8c6db833
LP
732}
733
7027ff61 734int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
735 _cleanup_fclose_ FILE *f = NULL;
736 char line[LINE_MAX];
8af8afd6 737 const char *fs;
c6c18be3 738 size_t cs;
8c6db833 739
8c6db833 740 assert(path);
c6c18be3 741 assert(pid >= 0);
8c6db833 742
8af8afd6
LP
743 if (controller) {
744 if (!cg_controller_is_valid(controller, true))
745 return -EINVAL;
78edb35a 746
8af8afd6
LP
747 controller = normalize_controller(controller);
748 } else
7027ff61
LP
749 controller = SYSTEMD_CGROUP_CONTROLLER;
750
b68fa010 751 fs = procfs_file_alloca(pid, "cgroup");
8c6db833 752
c6c18be3 753 f = fopen(fs, "re");
4c633005
LP
754 if (!f)
755 return errno == ENOENT ? -ESRCH : -errno;
756
c6c18be3
LP
757 cs = strlen(controller);
758
7027ff61 759 FOREACH_LINE(line, f, return -errno) {
a2a5291b 760 char *l, *p, *e;
8af8afd6 761 size_t k;
a2a5291b 762 const char *word, *state;
8af8afd6 763 bool found = false;
c6c18be3
LP
764
765 truncate_nl(line);
766
7027ff61
LP
767 l = strchr(line, ':');
768 if (!l)
c6c18be3
LP
769 continue;
770
771 l++;
8af8afd6
LP
772 e = strchr(l, ':');
773 if (!e)
c6c18be3
LP
774 continue;
775
8af8afd6
LP
776 *e = 0;
777
a2a5291b 778 FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
8af8afd6 779
a2a5291b 780 if (k == cs && memcmp(word, controller, cs) == 0) {
8af8afd6
LP
781 found = true;
782 break;
783 }
784
785 if (k == 5 + cs &&
a2a5291b
ZJS
786 memcmp(word, "name=", 5) == 0 &&
787 memcmp(word+5, controller, cs) == 0) {
8af8afd6
LP
788 found = true;
789 break;
790 }
791 }
792
793 if (!found)
c6c18be3
LP
794 continue;
795
8af8afd6 796 p = strdup(e + 1);
7027ff61
LP
797 if (!p)
798 return -ENOMEM;
c6c18be3
LP
799
800 *path = p;
7027ff61 801 return 0;
c6c18be3
LP
802 }
803
7027ff61 804 return -ENOENT;
8c6db833
LP
805}
806
807int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61
LP
808 _cleanup_free_ char *fs = NULL, *contents = NULL;
809 char *sc;
8c6db833
LP
810 int r;
811
8c6db833
LP
812 assert(agent);
813
7027ff61
LP
814 r = cg_get_path(controller, NULL, "release_agent", &fs);
815 if (r < 0)
c6c18be3 816 return r;
8c6db833 817
7027ff61
LP
818 r = read_one_line_file(fs, &contents);
819 if (r < 0)
820 return r;
8c6db833
LP
821
822 sc = strstrip(contents);
8c6db833 823 if (sc[0] == 0) {
70c949a4 824 r = write_string_file_no_create(fs, agent);
574d5f2d 825 if (r < 0)
7027ff61
LP
826 return r;
827 } else if (!streq(sc, agent))
828 return -EEXIST;
8c6db833 829
c6c18be3
LP
830 free(fs);
831 fs = NULL;
7027ff61
LP
832 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
833 if (r < 0)
834 return r;
8c6db833
LP
835
836 free(contents);
837 contents = NULL;
7027ff61
LP
838 r = read_one_line_file(fs, &contents);
839 if (r < 0)
840 return r;
8c6db833
LP
841
842 sc = strstrip(contents);
8c6db833 843 if (streq(sc, "0")) {
70c949a4 844 r = write_string_file_no_create(fs, "1");
7027ff61
LP
845 if (r < 0)
846 return r;
c6c18be3 847
7027ff61
LP
848 return 1;
849 }
8c6db833 850
7027ff61
LP
851 if (!streq(sc, "1"))
852 return -EIO;
8c6db833 853
7027ff61 854 return 0;
8c6db833
LP
855}
856
ad929bcc
KS
857int cg_uninstall_release_agent(const char *controller) {
858 _cleanup_free_ char *fs = NULL;
859 int r;
860
ac9ef333
LP
861 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
862 if (r < 0)
863 return r;
864
70c949a4 865 r = write_string_file_no_create(fs, "0");
ac9ef333
LP
866 if (r < 0)
867 return r;
868
869 free(fs);
870 fs = NULL;
871
ad929bcc
KS
872 r = cg_get_path(controller, NULL, "release_agent", &fs);
873 if (r < 0)
874 return r;
875
70c949a4 876 r = write_string_file_no_create(fs, "");
ad929bcc
KS
877 if (r < 0)
878 return r;
879
ac9ef333 880 return 0;
ad929bcc
KS
881}
882
8c6db833 883int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
7027ff61 884 _cleanup_fclose_ FILE *f = NULL;
c3175a7f 885 pid_t pid = 0, self_pid;
c6c18be3 886 bool found = false;
7027ff61 887 int r;
8c6db833 888
8c6db833
LP
889 assert(path);
890
b043cd0b 891 r = cg_enumerate_processes(controller, path, &f);
c3175a7f 892 if (r < 0)
4c633005 893 return r == -ENOENT ? 1 : r;
8c6db833 894
c3175a7f
LP
895 self_pid = getpid();
896
c6c18be3 897 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 898
c3175a7f 899 if (ignore_self && pid == self_pid)
c6c18be3 900 continue;
8c6db833 901
c6c18be3
LP
902 found = true;
903 break;
8c6db833
LP
904 }
905
c6c18be3
LP
906 if (r < 0)
907 return r;
8c6db833 908
c6c18be3 909 return !found;
8c6db833
LP
910}
911
912int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
7027ff61 913 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 914 char *fn;
7027ff61 915 int r;
8c6db833 916
8c6db833
LP
917 assert(path);
918
c3175a7f
LP
919 r = cg_is_empty(controller, path, ignore_self);
920 if (r <= 0)
35d2e7ec
LP
921 return r;
922
c3175a7f
LP
923 r = cg_enumerate_subgroups(controller, path, &d);
924 if (r < 0)
4c633005 925 return r == -ENOENT ? 1 : r;
8c6db833 926
35d2e7ec 927 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 928 _cleanup_free_ char *p = NULL;
8c6db833 929
7027ff61 930 p = strjoin(path, "/", fn, NULL);
35d2e7ec 931 free(fn);
7027ff61
LP
932 if (!p)
933 return -ENOMEM;
8c6db833 934
35d2e7ec 935 r = cg_is_empty_recursive(controller, p, ignore_self);
35d2e7ec 936 if (r <= 0)
7027ff61 937 return r;
35d2e7ec
LP
938 }
939
7027ff61
LP
940 if (r < 0)
941 return r;
35d2e7ec 942
7027ff61 943 return 1;
35d2e7ec
LP
944}
945
946int cg_split_spec(const char *spec, char **controller, char **path) {
947 const char *e;
948 char *t = NULL, *u = NULL;
5954c074 949 _cleanup_free_ char *v = NULL;
35d2e7ec
LP
950
951 assert(spec);
35d2e7ec
LP
952
953 if (*spec == '/') {
e884315e
LP
954 if (!path_is_safe(spec))
955 return -EINVAL;
35d2e7ec
LP
956
957 if (path) {
246aa6dd
LP
958 t = strdup(spec);
959 if (!t)
35d2e7ec
LP
960 return -ENOMEM;
961
dbb9401d 962 *path = path_kill_slashes(t);
8c6db833
LP
963 }
964
35d2e7ec
LP
965 if (controller)
966 *controller = NULL;
967
968 return 0;
8c6db833
LP
969 }
970
246aa6dd
LP
971 e = strchr(spec, ':');
972 if (!e) {
78edb35a 973 if (!cg_controller_is_valid(spec, true))
35d2e7ec
LP
974 return -EINVAL;
975
976 if (controller) {
5954c074 977 t = strdup(normalize_controller(spec));
246aa6dd 978 if (!t)
35d2e7ec
LP
979 return -ENOMEM;
980
981 *controller = t;
982 }
983
984 if (path)
985 *path = NULL;
986
987 return 0;
8c6db833
LP
988 }
989
5954c074
LP
990 v = strndup(spec, e-spec);
991 if (!v)
992 return -ENOMEM;
993 t = strdup(normalize_controller(v));
e884315e
LP
994 if (!t)
995 return -ENOMEM;
78edb35a 996 if (!cg_controller_is_valid(t, true)) {
e884315e 997 free(t);
35d2e7ec 998 return -EINVAL;
246aa6dd
LP
999 }
1000
baa89da4
LP
1001 if (streq(e+1, "")) {
1002 u = strdup("/");
1003 if (!u) {
1004 free(t);
1005 return -ENOMEM;
1006 }
1007 } else {
1008 u = strdup(e+1);
1009 if (!u) {
1010 free(t);
1011 return -ENOMEM;
1012 }
35d2e7ec 1013
baa89da4
LP
1014 if (!path_is_safe(u) ||
1015 !path_is_absolute(u)) {
1016 free(t);
1017 free(u);
1018 return -EINVAL;
1019 }
1020
1021 path_kill_slashes(u);
1022 }
5954c074 1023
35d2e7ec
LP
1024 if (controller)
1025 *controller = t;
e884315e
LP
1026 else
1027 free(t);
35d2e7ec
LP
1028
1029 if (path)
1030 *path = u;
e884315e
LP
1031 else
1032 free(u);
35d2e7ec
LP
1033
1034 return 0;
8c6db833 1035}
c6c18be3 1036
7027ff61 1037int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1038 _cleanup_free_ char *c = NULL, *p = NULL;
1039 char *t;
35d2e7ec
LP
1040 int r;
1041
1042 assert(path);
1043 assert(result);
1044
73e231ab 1045 /* First, check if it already is a filesystem path */
7027ff61 1046 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1047
b69d29ce
LP
1048 t = strdup(path);
1049 if (!t)
35d2e7ec
LP
1050 return -ENOMEM;
1051
dbb9401d 1052 *result = path_kill_slashes(t);
35d2e7ec
LP
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
32081481 1256 /* Skip the session scope or user manager... */
d4fffc4b 1257 t = skip_session(e);
32081481
LS
1258 if (!t)
1259 t = skip_user_manager(e);
1260 if (!t)
1261 return -ENOENT;
1262
1263 /* ... and skip more slices if there are any */
1264 e = skip_slices(t);
6c03089c 1265
7027ff61 1266 return cg_path_decode_unit(e, unit);
ef1673d1 1267}
ba1261bc 1268
ef1673d1 1269int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1270 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1271 int r;
1272
1273 assert(unit);
1274
7027ff61 1275 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1276 if (r < 0)
1277 return r;
1278
1279 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1280}
e884315e 1281
7027ff61 1282int cg_path_get_machine_name(const char *path, char **machine) {
89f7c846
LP
1283 _cleanup_free_ char *u = NULL, *sl = NULL;
1284 int r;
374ec6ab 1285
89f7c846
LP
1286 r = cg_path_get_unit(path, &u);
1287 if (r < 0)
1288 return r;
7027ff61 1289
89f7c846
LP
1290 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1291 if (!sl)
ae018d9b 1292 return -ENOMEM;
aff38e74 1293
89f7c846 1294 return readlink_malloc(sl, machine);
7027ff61
LP
1295}
1296
1297int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1298 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1299 int r;
1300
1301 assert(machine);
1302
1303 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1304 if (r < 0)
1305 return r;
1306
1307 return cg_path_get_machine_name(cgroup, machine);
1308}
1309
1310int cg_path_get_session(const char *path, char **session) {
a13ee4c7 1311 const char *e, *n, *x, *y;
af08d2f9 1312 char *s;
7027ff61
LP
1313
1314 assert(path);
7027ff61 1315
9444b1f2
LP
1316 /* Skip slices, if there are any */
1317 e = skip_slices(path);
7027ff61 1318
7027ff61 1319 n = strchrnul(e, '/');
374ec6ab 1320 if (e == n)
7027ff61 1321 return -ENOENT;
374ec6ab
LP
1322
1323 s = strndupa(e, n - e);
1324 s = cg_unescape(s);
1325
1326 x = startswith(s, "session-");
1327 if (!x)
1328 return -ENOENT;
a13ee4c7
ZJS
1329 y = endswith(x, ".scope");
1330 if (!y || x == y)
374ec6ab
LP
1331 return -ENOENT;
1332
af08d2f9
LP
1333 if (session) {
1334 char *r;
1335
a13ee4c7 1336 r = strndup(x, y - x);
af08d2f9
LP
1337 if (!r)
1338 return -ENOMEM;
1339
1340 *session = r;
1341 }
7027ff61 1342
7027ff61
LP
1343 return 0;
1344}
1345
1346int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1347 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1348 int r;
1349
7027ff61
LP
1350 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1351 if (r < 0)
1352 return r;
1353
1354 return cg_path_get_session(cgroup, session);
1355}
1356
ae018d9b 1357int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1358 _cleanup_free_ char *slice = NULL;
674eb685 1359 const char *start, *end;
ae018d9b 1360 char *s;
674eb685 1361 uid_t u;
374ec6ab 1362 int r;
ae018d9b
LP
1363
1364 assert(path);
ae018d9b 1365
374ec6ab
LP
1366 r = cg_path_get_slice(path, &slice);
1367 if (r < 0)
1368 return r;
ae018d9b 1369
674eb685
LP
1370 start = startswith(slice, "user-");
1371 if (!start)
ae018d9b 1372 return -ENOENT;
674eb685
LP
1373 end = endswith(slice, ".slice");
1374 if (!end)
ae018d9b
LP
1375 return -ENOENT;
1376
674eb685 1377 s = strndupa(start, end - start);
ae018d9b 1378 if (!s)
674eb685 1379 return -ENOENT;
ae018d9b 1380
674eb685
LP
1381 if (parse_uid(s, &u) < 0)
1382 return -EIO;
1383
1384 if (uid)
1385 *uid = u;
1386
1387 return 0;
ae018d9b
LP
1388}
1389
1390int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1391 _cleanup_free_ char *cgroup = NULL;
1392 int r;
1393
ae018d9b
LP
1394 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1395 if (r < 0)
1396 return r;
1397
1398 return cg_path_get_owner_uid(cgroup, uid);
1399}
1400
1021b21b
LP
1401int cg_path_get_slice(const char *p, char **slice) {
1402 const char *e = NULL;
1403 size_t m = 0;
1404
1405 assert(p);
1406 assert(slice);
1407
1408 for (;;) {
1409 size_t n;
1410
1411 p += strspn(p, "/");
1412
1413 n = strcspn(p, "/");
1414 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1415 char *s;
1416
1417 if (!e)
1418 return -ENOENT;
1419
1420 s = strndup(e, m);
1421 if (!s)
1422 return -ENOMEM;
1423
1424 *slice = s;
1425 return 0;
1426 }
1427
1428 e = p;
1429 m = n;
1430
1431 p += n;
1432 }
1433}
1434
1435int cg_pid_get_slice(pid_t pid, char **slice) {
1436 _cleanup_free_ char *cgroup = NULL;
1437 int r;
1438
1439 assert(slice);
1440
1441 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1442 if (r < 0)
1443 return r;
1444
1445 return cg_path_get_slice(cgroup, slice);
1446}
1447
ae018d9b
LP
1448char *cg_escape(const char *p) {
1449 bool need_prefix = false;
1450
1451 /* This implements very minimal escaping for names to be used
1452 * as file names in the cgroup tree: any name which might
1453 * conflict with a kernel name or is prefixed with '_' is
1454 * prefixed with a '_'. That way, when reading cgroup names it
1455 * is sufficient to remove a single prefixing underscore if
1456 * there is one. */
1457
1458 /* The return value of this function (unlike cg_unescape())
1459 * needs free()! */
1460
a0ab5665
LP
1461 if (p[0] == 0 ||
1462 p[0] == '_' ||
1463 p[0] == '.' ||
1464 streq(p, "notify_on_release") ||
1465 streq(p, "release_agent") ||
1466 streq(p, "tasks"))
ae018d9b
LP
1467 need_prefix = true;
1468 else {
1469 const char *dot;
1470
1471 dot = strrchr(p, '.');
1472 if (dot) {
1473
1474 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1475 need_prefix = true;
1476 else {
1477 char *n;
1478
1479 n = strndupa(p, dot - p);
1480
1481 if (check_hierarchy(n) >= 0)
1482 need_prefix = true;
1483 }
1484 }
1485 }
1486
1487 if (need_prefix)
1488 return strappend("_", p);
1489 else
1490 return strdup(p);
1491}
1492
1493char *cg_unescape(const char *p) {
1494 assert(p);
1495
1496 /* The return value of this function (unlike cg_escape())
1497 * doesn't need free()! */
1498
1499 if (p[0] == '_')
1500 return (char*) p+1;
1501
1502 return (char*) p;
1503}
78edb35a
LP
1504
1505#define CONTROLLER_VALID \
4b549144 1506 DIGITS LETTERS \
78edb35a
LP
1507 "_"
1508
1509bool cg_controller_is_valid(const char *p, bool allow_named) {
1510 const char *t, *s;
1511
1512 if (!p)
1513 return false;
1514
1515 if (allow_named) {
1516 s = startswith(p, "name=");
1517 if (s)
1518 p = s;
1519 }
1520
1521 if (*p == 0 || *p == '_')
1522 return false;
1523
1524 for (t = p; *t; t++)
1525 if (!strchr(CONTROLLER_VALID, *t))
1526 return false;
1527
1528 if (t - p > FILENAME_MAX)
1529 return false;
1530
1531 return true;
1532}
a016b922
LP
1533
1534int cg_slice_to_path(const char *unit, char **ret) {
1535 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1536 const char *dash;
1537
1538 assert(unit);
1539 assert(ret);
1540
f78e6385 1541 if (!unit_name_is_valid(unit, TEMPLATE_INVALID))
a016b922
LP
1542 return -EINVAL;
1543
1544 if (!endswith(unit, ".slice"))
1545 return -EINVAL;
1546
1547 p = unit_name_to_prefix(unit);
1548 if (!p)
1549 return -ENOMEM;
1550
1551 dash = strchr(p, '-');
1552 while (dash) {
1553 _cleanup_free_ char *escaped = NULL;
1554 char n[dash - p + sizeof(".slice")];
1555
1556 strcpy(stpncpy(n, p, dash - p), ".slice");
1557
f78e6385 1558 if (!unit_name_is_valid(n, TEMPLATE_INVALID))
a016b922
LP
1559 return -EINVAL;
1560
1561 escaped = cg_escape(n);
1562 if (!escaped)
1563 return -ENOMEM;
1564
1565 if (!strextend(&s, escaped, "/", NULL))
1566 return -ENOMEM;
1567
1568 dash = strchr(dash+1, '-');
1569 }
1570
1571 e = cg_escape(unit);
1572 if (!e)
1573 return -ENOMEM;
1574
1575 if (!strextend(&s, e, NULL))
1576 return -ENOMEM;
1577
1578 *ret = s;
1579 s = NULL;
1580
1581 return 0;
1582}
4ad49000
LP
1583
1584int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1585 _cleanup_free_ char *p = NULL;
1586 int r;
1587
1588 r = cg_get_path(controller, path, attribute, &p);
1589 if (r < 0)
1590 return r;
1591
70c949a4 1592 return write_string_file_no_create(p, value);
4ad49000
LP
1593}
1594
934277fe
LP
1595int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1596 _cleanup_free_ char *p = NULL;
1597 int r;
1598
1599 r = cg_get_path(controller, path, attribute, &p);
1600 if (r < 0)
1601 return r;
1602
1603 return read_one_line_file(p, ret);
1604}
1605
4ad49000
LP
1606static const char mask_names[] =
1607 "cpu\0"
1608 "cpuacct\0"
1609 "blkio\0"
1610 "memory\0"
1611 "devices\0";
1612
13b84ec7 1613int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1614 CGroupControllerMask bit = 1;
1615 const char *n;
1616 int r;
1617
1618 /* This one will create a cgroup in our private tree, but also
1619 * duplicate it in the trees specified in mask, and remove it
1620 * in all others */
1621
1622 /* First create the cgroup in our own hierarchy. */
1623 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1624 if (r < 0)
1625 return r;
1626
1627 /* Then, do the same in the other hierarchies */
1628 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1629 if (mask & bit)
4ad49000 1630 cg_create(n, path);
13b84ec7 1631 else if (supported & bit)
4ad49000
LP
1632 cg_trim(n, path, true);
1633
1634 bit <<= 1;
1635 }
1636
13b84ec7 1637 return 0;
4ad49000
LP
1638}
1639
7b3fd631 1640int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
4ad49000
LP
1641 CGroupControllerMask bit = 1;
1642 const char *n;
1643 int r;
1644
1645 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1646 if (r < 0)
1647 return r;
4ad49000
LP
1648
1649 NULSTR_FOREACH(n, mask_names) {
7b3fd631
LP
1650
1651 if (supported & bit) {
1652 const char *p = NULL;
1653
1654 if (path_callback)
1655 p = path_callback(bit, userdata);
1656
1657 if (!p)
1658 p = path;
1659
13b84ec7 1660 cg_attach_fallback(n, path, pid);
7b3fd631 1661 }
4ad49000
LP
1662
1663 bit <<= 1;
1664 }
1665
13b84ec7 1666 return 0;
4ad49000
LP
1667}
1668
7b3fd631 1669int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
1670 Iterator i;
1671 void *pidp;
1672 int r = 0;
1673
1674 SET_FOREACH(pidp, pids, i) {
1675 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1676 int q;
6c12b52e 1677
7b3fd631 1678 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
13b84ec7
LP
1679 if (q < 0)
1680 r = q;
6c12b52e
LP
1681 }
1682
1683 return r;
1684}
1685
03b90d4b 1686int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
4ad49000
LP
1687 CGroupControllerMask bit = 1;
1688 const char *n;
1689 int r;
1690
13b84ec7
LP
1691 if (!path_equal(from, to)) {
1692 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1693 if (r < 0)
1694 return r;
1695 }
4ad49000
LP
1696
1697 NULSTR_FOREACH(n, mask_names) {
03b90d4b
LP
1698 if (supported & bit) {
1699 const char *p = NULL;
1700
1701 if (to_callback)
1702 p = to_callback(bit, userdata);
1703
1704 if (!p)
1705 p = to;
1706
1707 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1708 }
4ad49000
LP
1709
1710 bit <<= 1;
1711 }
1712
13b84ec7 1713 return 0;
4ad49000
LP
1714}
1715
13b84ec7 1716int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1717 CGroupControllerMask bit = 1;
1718 const char *n;
1719 int r;
1720
1721 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1722 if (r < 0)
1723 return r;
1724
1725 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1726 if (supported & bit)
4ad49000
LP
1727 cg_trim(n, path, delete_root);
1728
1729 bit <<= 1;
1730 }
1731
13b84ec7 1732 return 0;
4ad49000
LP
1733}
1734
1735CGroupControllerMask cg_mask_supported(void) {
1736 CGroupControllerMask bit = 1, mask = 0;
1737 const char *n;
1738
1739 NULSTR_FOREACH(n, mask_names) {
1740 if (check_hierarchy(n) >= 0)
1741 mask |= bit;
1742
1743 bit <<= 1;
1744 }
1745
1746 return mask;
1747}
b12afc8c
LP
1748
1749int cg_kernel_controllers(Set *controllers) {
1750 _cleanup_fclose_ FILE *f = NULL;
1751 char buf[LINE_MAX];
1752 int r;
1753
1754 assert(controllers);
1755
1756 f = fopen("/proc/cgroups", "re");
1757 if (!f) {
1758 if (errno == ENOENT)
1759 return 0;
1760 return -errno;
1761 }
1762
1763 /* Ignore the header line */
1764 (void) fgets(buf, sizeof(buf), f);
1765
1766 for (;;) {
1767 char *controller;
1768 int enabled = 0;
1769
1770 errno = 0;
1771 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
1772
1773 if (feof(f))
1774 break;
1775
1776 if (ferror(f) && errno)
1777 return -errno;
1778
1779 return -EBADMSG;
1780 }
1781
1782 if (!enabled) {
1783 free(controller);
1784 continue;
1785 }
1786
1787 if (!filename_is_valid(controller)) {
1788 free(controller);
1789 return -EBADMSG;
1790 }
1791
1792 r = set_consume(controllers, controller);
1793 if (r < 0)
1794 return r;
1795 }
1796
1797 return 0;
1798}