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