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