]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
core: rework unit name validation and manipulation logic
[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
7410616c 1157 if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
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
7410616c 1184 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
8b0849e9
LP
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
329ac4bc 1320static const char *skip_user_prefix(const char *path) {
d4fffc4b 1321 const char *e, *t;
ef1673d1 1322
6c03089c 1323 assert(path);
ba1261bc 1324
9444b1f2
LP
1325 /* Skip slices, if there are any */
1326 e = skip_slices(path);
ba1261bc 1327
329ac4bc 1328 /* Skip the user manager, if it's in the path now... */
8b0849e9 1329 t = skip_user_manager(e);
329ac4bc
LP
1330 if (t)
1331 return t;
8b0849e9 1332
329ac4bc
LP
1333 /* Alternatively skip the user session if it is in the path... */
1334 return skip_session(e);
1335}
32081481 1336
329ac4bc
LP
1337int cg_path_get_user_unit(const char *path, char **ret) {
1338 const char *t;
6c03089c 1339
329ac4bc
LP
1340 assert(path);
1341 assert(ret);
8b0849e9 1342
329ac4bc
LP
1343 t = skip_user_prefix(path);
1344 if (!t)
8b0849e9 1345 return -ENXIO;
8b0849e9 1346
329ac4bc
LP
1347 /* And from here on it looks pretty much the same as for a
1348 * system unit, hence let's use the same parser from here
1349 * on. */
1350 return cg_path_get_unit(t, ret);
ef1673d1 1351}
ba1261bc 1352
ef1673d1 1353int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1354 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1355 int r;
1356
1357 assert(unit);
1358
7027ff61 1359 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1360 if (r < 0)
1361 return r;
1362
1363 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1364}
e884315e 1365
7027ff61 1366int cg_path_get_machine_name(const char *path, char **machine) {
89f7c846
LP
1367 _cleanup_free_ char *u = NULL, *sl = NULL;
1368 int r;
374ec6ab 1369
89f7c846
LP
1370 r = cg_path_get_unit(path, &u);
1371 if (r < 0)
1372 return r;
7027ff61 1373
89f7c846
LP
1374 sl = strjoin("/run/systemd/machines/unit:", u, NULL);
1375 if (!sl)
ae018d9b 1376 return -ENOMEM;
aff38e74 1377
89f7c846 1378 return readlink_malloc(sl, machine);
7027ff61
LP
1379}
1380
1381int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1382 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1383 int r;
1384
1385 assert(machine);
1386
1387 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1388 if (r < 0)
1389 return r;
1390
1391 return cg_path_get_machine_name(cgroup, machine);
1392}
1393
1394int cg_path_get_session(const char *path, char **session) {
8b0849e9
LP
1395 _cleanup_free_ char *unit = NULL;
1396 char *start, *end;
1397 int r;
7027ff61
LP
1398
1399 assert(path);
7027ff61 1400
8b0849e9
LP
1401 r = cg_path_get_unit(path, &unit);
1402 if (r < 0)
1403 return r;
7027ff61 1404
8b0849e9
LP
1405 start = startswith(unit, "session-");
1406 if (!start)
cfeaa44a 1407 return -ENXIO;
8b0849e9
LP
1408 end = endswith(start, ".scope");
1409 if (!end)
cfeaa44a 1410 return -ENXIO;
8b0849e9
LP
1411
1412 *end = 0;
1413 if (!session_id_valid(start))
cfeaa44a 1414 return -ENXIO;
374ec6ab 1415
af08d2f9 1416 if (session) {
8b0849e9 1417 char *rr;
af08d2f9 1418
8b0849e9
LP
1419 rr = strdup(start);
1420 if (!rr)
af08d2f9
LP
1421 return -ENOMEM;
1422
8b0849e9 1423 *session = rr;
af08d2f9 1424 }
7027ff61 1425
7027ff61
LP
1426 return 0;
1427}
1428
1429int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1430 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1431 int r;
1432
7027ff61
LP
1433 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1434 if (r < 0)
1435 return r;
1436
1437 return cg_path_get_session(cgroup, session);
1438}
1439
ae018d9b 1440int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab 1441 _cleanup_free_ char *slice = NULL;
8b0849e9 1442 char *start, *end;
374ec6ab 1443 int r;
ae018d9b
LP
1444
1445 assert(path);
ae018d9b 1446
374ec6ab
LP
1447 r = cg_path_get_slice(path, &slice);
1448 if (r < 0)
1449 return r;
ae018d9b 1450
674eb685
LP
1451 start = startswith(slice, "user-");
1452 if (!start)
cfeaa44a 1453 return -ENXIO;
8b0849e9 1454 end = endswith(start, ".slice");
674eb685 1455 if (!end)
cfeaa44a 1456 return -ENXIO;
ae018d9b 1457
8b0849e9
LP
1458 *end = 0;
1459 if (parse_uid(start, uid) < 0)
cfeaa44a 1460 return -ENXIO;
674eb685 1461
674eb685 1462 return 0;
ae018d9b
LP
1463}
1464
1465int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1466 _cleanup_free_ char *cgroup = NULL;
1467 int r;
1468
ae018d9b
LP
1469 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1470 if (r < 0)
1471 return r;
1472
1473 return cg_path_get_owner_uid(cgroup, uid);
1474}
1475
1021b21b
LP
1476int cg_path_get_slice(const char *p, char **slice) {
1477 const char *e = NULL;
1021b21b
LP
1478
1479 assert(p);
1480 assert(slice);
1481
329ac4bc
LP
1482 /* Finds the right-most slice unit from the beginning, but
1483 * stops before we come to the first non-slice unit. */
1484
1021b21b
LP
1485 for (;;) {
1486 size_t n;
1487
1488 p += strspn(p, "/");
1489
1490 n = strcspn(p, "/");
8b0849e9 1491 if (!valid_slice_name(p, n)) {
1021b21b 1492
8b0849e9
LP
1493 if (!e) {
1494 char *s;
1021b21b 1495
8b0849e9
LP
1496 s = strdup("-.slice");
1497 if (!s)
1498 return -ENOMEM;
1021b21b 1499
8b0849e9
LP
1500 *slice = s;
1501 return 0;
1502 }
1503
1504 return cg_path_decode_unit(e, slice);
1021b21b
LP
1505 }
1506
1507 e = p;
1021b21b
LP
1508 p += n;
1509 }
1510}
1511
1512int cg_pid_get_slice(pid_t pid, char **slice) {
1513 _cleanup_free_ char *cgroup = NULL;
1514 int r;
1515
1516 assert(slice);
1517
1518 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1519 if (r < 0)
1520 return r;
1521
1522 return cg_path_get_slice(cgroup, slice);
1523}
1524
329ac4bc
LP
1525int cg_path_get_user_slice(const char *p, char **slice) {
1526 const char *t;
1527 assert(p);
1528 assert(slice);
1529
1530 t = skip_user_prefix(p);
1531 if (!t)
1532 return -ENXIO;
1533
1534 /* And now it looks pretty much the same as for a system
1535 * slice, so let's just use the same parser from here on. */
1536 return cg_path_get_slice(t, slice);
1537}
1538
1539int cg_pid_get_user_slice(pid_t pid, char **slice) {
1540 _cleanup_free_ char *cgroup = NULL;
1541 int r;
1542
1543 assert(slice);
1544
1545 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1546 if (r < 0)
1547 return r;
1548
1549 return cg_path_get_user_slice(cgroup, slice);
1550}
1551
ae018d9b
LP
1552char *cg_escape(const char *p) {
1553 bool need_prefix = false;
1554
1555 /* This implements very minimal escaping for names to be used
1556 * as file names in the cgroup tree: any name which might
1557 * conflict with a kernel name or is prefixed with '_' is
1558 * prefixed with a '_'. That way, when reading cgroup names it
1559 * is sufficient to remove a single prefixing underscore if
1560 * there is one. */
1561
1562 /* The return value of this function (unlike cg_unescape())
1563 * needs free()! */
1564
a0ab5665
LP
1565 if (p[0] == 0 ||
1566 p[0] == '_' ||
1567 p[0] == '.' ||
1568 streq(p, "notify_on_release") ||
1569 streq(p, "release_agent") ||
1570 streq(p, "tasks"))
ae018d9b
LP
1571 need_prefix = true;
1572 else {
1573 const char *dot;
1574
1575 dot = strrchr(p, '.');
1576 if (dot) {
1577
1578 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1579 need_prefix = true;
1580 else {
1581 char *n;
1582
1583 n = strndupa(p, dot - p);
1584
1585 if (check_hierarchy(n) >= 0)
1586 need_prefix = true;
1587 }
1588 }
1589 }
1590
1591 if (need_prefix)
1592 return strappend("_", p);
1593 else
1594 return strdup(p);
1595}
1596
1597char *cg_unescape(const char *p) {
1598 assert(p);
1599
1600 /* The return value of this function (unlike cg_escape())
1601 * doesn't need free()! */
1602
1603 if (p[0] == '_')
1604 return (char*) p+1;
1605
1606 return (char*) p;
1607}
78edb35a
LP
1608
1609#define CONTROLLER_VALID \
4b549144 1610 DIGITS LETTERS \
78edb35a
LP
1611 "_"
1612
1613bool cg_controller_is_valid(const char *p, bool allow_named) {
1614 const char *t, *s;
1615
1616 if (!p)
1617 return false;
1618
1619 if (allow_named) {
1620 s = startswith(p, "name=");
1621 if (s)
1622 p = s;
1623 }
1624
1625 if (*p == 0 || *p == '_')
1626 return false;
1627
1628 for (t = p; *t; t++)
1629 if (!strchr(CONTROLLER_VALID, *t))
1630 return false;
1631
1632 if (t - p > FILENAME_MAX)
1633 return false;
1634
1635 return true;
1636}
a016b922
LP
1637
1638int cg_slice_to_path(const char *unit, char **ret) {
1639 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1640 const char *dash;
7410616c 1641 int r;
a016b922
LP
1642
1643 assert(unit);
1644 assert(ret);
1645
c96cc582
LP
1646 if (streq(unit, "-.slice")) {
1647 char *x;
1648
1649 x = strdup("");
1650 if (!x)
1651 return -ENOMEM;
1652 *ret = x;
1653 return 0;
1654 }
1655
7410616c 1656 if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
a016b922
LP
1657 return -EINVAL;
1658
1659 if (!endswith(unit, ".slice"))
1660 return -EINVAL;
1661
7410616c
LP
1662 r = unit_name_to_prefix(unit, &p);
1663 if (r < 0)
1664 return r;
a016b922
LP
1665
1666 dash = strchr(p, '-');
e66e5b61
LP
1667
1668 /* Don't allow initial dashes */
1669 if (dash == p)
1670 return -EINVAL;
1671
a016b922
LP
1672 while (dash) {
1673 _cleanup_free_ char *escaped = NULL;
1674 char n[dash - p + sizeof(".slice")];
1675
e66e5b61
LP
1676 /* Don't allow trailing or double dashes */
1677 if (dash[1] == 0 || dash[1] == '-')
c96cc582 1678 return -EINVAL;
a016b922 1679
c96cc582 1680 strcpy(stpncpy(n, p, dash - p), ".slice");
7410616c 1681 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
a016b922
LP
1682 return -EINVAL;
1683
1684 escaped = cg_escape(n);
1685 if (!escaped)
1686 return -ENOMEM;
1687
1688 if (!strextend(&s, escaped, "/", NULL))
1689 return -ENOMEM;
1690
1691 dash = strchr(dash+1, '-');
1692 }
1693
1694 e = cg_escape(unit);
1695 if (!e)
1696 return -ENOMEM;
1697
1698 if (!strextend(&s, e, NULL))
1699 return -ENOMEM;
1700
1701 *ret = s;
1702 s = NULL;
1703
1704 return 0;
1705}
4ad49000
LP
1706
1707int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1708 _cleanup_free_ char *p = NULL;
1709 int r;
1710
1711 r = cg_get_path(controller, path, attribute, &p);
1712 if (r < 0)
1713 return r;
1714
70c949a4 1715 return write_string_file_no_create(p, value);
4ad49000
LP
1716}
1717
934277fe
LP
1718int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
1719 _cleanup_free_ char *p = NULL;
1720 int r;
1721
1722 r = cg_get_path(controller, path, attribute, &p);
1723 if (r < 0)
1724 return r;
1725
1726 return read_one_line_file(p, ret);
1727}
1728
4ad49000
LP
1729static const char mask_names[] =
1730 "cpu\0"
1731 "cpuacct\0"
1732 "blkio\0"
1733 "memory\0"
1734 "devices\0";
1735
13b84ec7 1736int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) {
4ad49000
LP
1737 CGroupControllerMask bit = 1;
1738 const char *n;
1739 int r;
1740
1741 /* This one will create a cgroup in our private tree, but also
1742 * duplicate it in the trees specified in mask, and remove it
1743 * in all others */
1744
1745 /* First create the cgroup in our own hierarchy. */
1746 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1747 if (r < 0)
1748 return r;
1749
1750 /* Then, do the same in the other hierarchies */
1751 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1752 if (mask & bit)
4ad49000 1753 cg_create(n, path);
13b84ec7 1754 else if (supported & bit)
4ad49000
LP
1755 cg_trim(n, path, true);
1756
1757 bit <<= 1;
1758 }
1759
13b84ec7 1760 return 0;
4ad49000
LP
1761}
1762
7b3fd631 1763int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
4ad49000
LP
1764 CGroupControllerMask bit = 1;
1765 const char *n;
1766 int r;
1767
1768 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
13b84ec7
LP
1769 if (r < 0)
1770 return r;
4ad49000
LP
1771
1772 NULSTR_FOREACH(n, mask_names) {
7b3fd631
LP
1773
1774 if (supported & bit) {
1775 const char *p = NULL;
1776
1777 if (path_callback)
1778 p = path_callback(bit, userdata);
1779
1780 if (!p)
1781 p = path;
1782
13b84ec7 1783 cg_attach_fallback(n, path, pid);
7b3fd631 1784 }
4ad49000
LP
1785
1786 bit <<= 1;
1787 }
1788
13b84ec7 1789 return 0;
4ad49000
LP
1790}
1791
7b3fd631 1792int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
6c12b52e
LP
1793 Iterator i;
1794 void *pidp;
1795 int r = 0;
1796
1797 SET_FOREACH(pidp, pids, i) {
1798 pid_t pid = PTR_TO_LONG(pidp);
13b84ec7 1799 int q;
6c12b52e 1800
7b3fd631 1801 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
13b84ec7
LP
1802 if (q < 0)
1803 r = q;
6c12b52e
LP
1804 }
1805
1806 return r;
1807}
1808
03b90d4b 1809int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
4ad49000
LP
1810 CGroupControllerMask bit = 1;
1811 const char *n;
1812 int r;
1813
13b84ec7
LP
1814 if (!path_equal(from, to)) {
1815 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1816 if (r < 0)
1817 return r;
1818 }
4ad49000
LP
1819
1820 NULSTR_FOREACH(n, mask_names) {
03b90d4b
LP
1821 if (supported & bit) {
1822 const char *p = NULL;
1823
1824 if (to_callback)
1825 p = to_callback(bit, userdata);
1826
1827 if (!p)
1828 p = to;
1829
1830 cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false);
1831 }
4ad49000
LP
1832
1833 bit <<= 1;
1834 }
1835
13b84ec7 1836 return 0;
4ad49000
LP
1837}
1838
13b84ec7 1839int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) {
4ad49000
LP
1840 CGroupControllerMask bit = 1;
1841 const char *n;
1842 int r;
1843
1844 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1845 if (r < 0)
1846 return r;
1847
1848 NULSTR_FOREACH(n, mask_names) {
13b84ec7 1849 if (supported & bit)
4ad49000
LP
1850 cg_trim(n, path, delete_root);
1851
1852 bit <<= 1;
1853 }
1854
13b84ec7 1855 return 0;
4ad49000
LP
1856}
1857
1858CGroupControllerMask cg_mask_supported(void) {
1859 CGroupControllerMask bit = 1, mask = 0;
1860 const char *n;
1861
1862 NULSTR_FOREACH(n, mask_names) {
1863 if (check_hierarchy(n) >= 0)
1864 mask |= bit;
1865
1866 bit <<= 1;
1867 }
1868
1869 return mask;
1870}
b12afc8c
LP
1871
1872int cg_kernel_controllers(Set *controllers) {
1873 _cleanup_fclose_ FILE *f = NULL;
1874 char buf[LINE_MAX];
1875 int r;
1876
1877 assert(controllers);
1878
1879 f = fopen("/proc/cgroups", "re");
1880 if (!f) {
1881 if (errno == ENOENT)
1882 return 0;
1883 return -errno;
1884 }
1885
1886 /* Ignore the header line */
1887 (void) fgets(buf, sizeof(buf), f);
1888
1889 for (;;) {
1890 char *controller;
1891 int enabled = 0;
1892
1893 errno = 0;
1894 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
1895
1896 if (feof(f))
1897 break;
1898
1899 if (ferror(f) && errno)
1900 return -errno;
1901
1902 return -EBADMSG;
1903 }
1904
1905 if (!enabled) {
1906 free(controller);
1907 continue;
1908 }
1909
1910 if (!filename_is_valid(controller)) {
1911 free(controller);
1912 return -EBADMSG;
1913 }
1914
1915 r = set_consume(controllers, controller);
1916 if (r < 0)
1917 return r;
1918 }
1919
1920 return 0;
1921}