]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/cgroup-util.c
transaction.c: do not point users to logs when unit not found
[thirdparty/systemd.git] / src / shared / cgroup-util.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
8c6db833
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8c6db833
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
8c6db833 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8c6db833
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <unistd.h>
24#include <signal.h>
25#include <string.h>
26#include <stdlib.h>
35d2e7ec 27#include <dirent.h>
672c48cc
LP
28#include <sys/stat.h>
29#include <sys/types.h>
e27796a0 30#include <ftw.h>
8c6db833
LP
31
32#include "cgroup-util.h"
33#include "log.h"
34#include "set.h"
35#include "macro.h"
36#include "util.h"
9eb977db 37#include "path-util.h"
b59e2465 38#include "strv.h"
ef1673d1 39#include "unit-name.h"
a5c32cff 40#include "fileio.h"
9444b1f2 41#include "special.h"
8c6db833 42
c6c18be3 43int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
7027ff61 44 _cleanup_free_ char *fs = NULL;
c6c18be3 45 FILE *f;
7027ff61 46 int r;
c6c18be3 47
c6c18be3
LP
48 assert(_f);
49
c3175a7f
LP
50 r = cg_get_path(controller, path, "cgroup.procs", &fs);
51 if (r < 0)
c6c18be3
LP
52 return r;
53
54 f = fopen(fs, "re");
c6c18be3
LP
55 if (!f)
56 return -errno;
57
58 *_f = f;
59 return 0;
60}
61
c6c18be3
LP
62int cg_read_pid(FILE *f, pid_t *_pid) {
63 unsigned long ul;
64
65 /* Note that the cgroup.procs might contain duplicates! See
66 * cgroups.txt for details. */
67
7027ff61
LP
68 assert(f);
69 assert(_pid);
70
c6c18be3
LP
71 errno = 0;
72 if (fscanf(f, "%lu", &ul) != 1) {
73
74 if (feof(f))
75 return 0;
76
77 return errno ? -errno : -EIO;
78 }
79
80 if (ul <= 0)
81 return -EIO;
82
83 *_pid = (pid_t) ul;
84 return 1;
85}
86
35d2e7ec 87int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
7027ff61 88 _cleanup_free_ char *fs = NULL;
35d2e7ec
LP
89 int r;
90 DIR *d;
91
35d2e7ec
LP
92 assert(_d);
93
94 /* This is not recursive! */
95
c3175a7f
LP
96 r = cg_get_path(controller, path, NULL, &fs);
97 if (r < 0)
35d2e7ec
LP
98 return r;
99
100 d = opendir(fs);
35d2e7ec
LP
101 if (!d)
102 return -errno;
103
104 *_d = d;
105 return 0;
106}
107
108int cg_read_subgroup(DIR *d, char **fn) {
109 struct dirent *de;
110
111 assert(d);
7027ff61 112 assert(fn);
35d2e7ec 113
7027ff61 114 FOREACH_DIRENT(de, d, return -errno) {
35d2e7ec
LP
115 char *b;
116
117 if (de->d_type != DT_DIR)
118 continue;
119
120 if (streq(de->d_name, ".") ||
121 streq(de->d_name, ".."))
122 continue;
123
7027ff61
LP
124 b = strdup(de->d_name);
125 if (!b)
35d2e7ec
LP
126 return -ENOMEM;
127
128 *fn = b;
129 return 1;
130 }
131
35d2e7ec
LP
132 return 0;
133}
134
4ad49000 135int cg_rmdir(const char *controller, const char *path) {
7027ff61 136 _cleanup_free_ char *p = NULL;
35d2e7ec
LP
137 int r;
138
ad293f5a
LP
139 r = cg_get_path(controller, path, NULL, &p);
140 if (r < 0)
35d2e7ec
LP
141 return r;
142
143 r = rmdir(p);
7027ff61
LP
144 if (r < 0 && errno != ENOENT)
145 return -errno;
35d2e7ec 146
7027ff61 147 return 0;
35d2e7ec
LP
148}
149
430c18ed 150int cg_kill(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, Set *s) {
7027ff61 151 _cleanup_set_free_ Set *allocated_set = NULL;
35d2e7ec 152 bool done = false;
8c6db833 153 int r, ret = 0;
35d2e7ec 154 pid_t my_pid;
8c6db833 155
8c6db833
LP
156 assert(sig >= 0);
157
158 /* This goes through the tasks list and kills them all. This
159 * is repeated until no further processes are added to the
160 * tasks list, to properly handle forking processes */
161
7027ff61
LP
162 if (!s) {
163 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
164 if (!s)
ca949c9d 165 return -ENOMEM;
7027ff61 166 }
8c6db833
LP
167
168 my_pid = getpid();
169
170 do {
7027ff61 171 _cleanup_fclose_ FILE *f = NULL;
0b172489 172 pid_t pid = 0;
8c6db833
LP
173 done = true;
174
7027ff61
LP
175 r = cg_enumerate_processes(controller, path, &f);
176 if (r < 0) {
4c633005 177 if (ret >= 0 && r != -ENOENT)
7027ff61 178 return r;
35d2e7ec 179
7027ff61 180 return ret;
35d2e7ec 181 }
c6c18be3
LP
182
183 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 184
7027ff61 185 if (ignore_self && pid == my_pid)
c6c18be3 186 continue;
8c6db833 187
c6c18be3
LP
188 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
189 continue;
8c6db833
LP
190
191 /* If we haven't killed this process yet, kill
192 * it */
4c633005
LP
193 if (kill(pid, sig) < 0) {
194 if (ret >= 0 && errno != ESRCH)
8c6db833 195 ret = -errno;
430c18ed
LP
196 } else if (ret == 0) {
197
198 if (sigcont)
199 kill(pid, SIGCONT);
200
35d2e7ec 201 ret = 1;
430c18ed 202 }
8c6db833 203
8c6db833
LP
204 done = false;
205
7027ff61
LP
206 r = set_put(s, LONG_TO_PTR(pid));
207 if (r < 0) {
35d2e7ec 208 if (ret >= 0)
7027ff61 209 return r;
35d2e7ec 210
7027ff61 211 return ret;
35d2e7ec
LP
212 }
213 }
214
215 if (r < 0) {
216 if (ret >= 0)
7027ff61 217 return r;
35d2e7ec 218
7027ff61 219 return ret;
8c6db833
LP
220 }
221
8c6db833
LP
222 /* To avoid racing against processes which fork
223 * quicker than we can kill them we repeat this until
224 * no new pids need to be killed. */
225
35d2e7ec 226 } while (!done);
8c6db833 227
35d2e7ec 228 return ret;
8c6db833
LP
229}
230
430c18ed 231int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) {
7027ff61
LP
232 _cleanup_set_free_ Set *allocated_set = NULL;
233 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 234 int r, ret = 0;
35d2e7ec 235 char *fn;
8c6db833
LP
236
237 assert(path);
8c6db833
LP
238 assert(sig >= 0);
239
7027ff61
LP
240 if (!s) {
241 s = allocated_set = set_new(trivial_hash_func, trivial_compare_func);
242 if (!s)
ca949c9d 243 return -ENOMEM;
7027ff61 244 }
ca949c9d 245
430c18ed 246 ret = cg_kill(controller, path, sig, sigcont, ignore_self, s);
8c6db833 247
7027ff61
LP
248 r = cg_enumerate_subgroups(controller, path, &d);
249 if (r < 0) {
4c633005 250 if (ret >= 0 && r != -ENOENT)
7027ff61 251 return r;
8c6db833 252
7027ff61 253 return ret;
35d2e7ec 254 }
8c6db833 255
35d2e7ec 256 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 257 _cleanup_free_ char *p = NULL;
8c6db833 258
7027ff61 259 p = strjoin(path, "/", fn, NULL);
35d2e7ec 260 free(fn);
7027ff61
LP
261 if (!p)
262 return -ENOMEM;
8c6db833 263
430c18ed 264 r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s);
7027ff61 265 if (ret >= 0 && r != 0)
35d2e7ec 266 ret = r;
8c6db833
LP
267 }
268
7027ff61 269 if (ret >= 0 && r < 0)
35d2e7ec
LP
270 ret = r;
271
7027ff61 272 if (rem) {
4ad49000 273 r = cg_rmdir(controller, path);
7027ff61
LP
274 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
275 return r;
276 }
ca949c9d 277
8c6db833
LP
278 return ret;
279}
280
35d2e7ec 281int cg_kill_recursive_and_wait(const char *controller, const char *path, bool rem) {
8c6db833
LP
282 unsigned i;
283
284 assert(path);
8c6db833
LP
285
286 /* This safely kills all processes; first it sends a SIGTERM,
9f452741
LP
287 * then checks 8 times after 200ms whether the group is now
288 * empty, then kills everything that is left with SIGKILL and
289 * finally checks 5 times after 200ms each whether the group
290 * is finally empty. */
8c6db833 291
9f452741 292 for (i = 0; i < 15; i++) {
1d0ae74a 293 int sig, r;
8c6db833
LP
294
295 if (i <= 0)
296 sig = SIGTERM;
9f452741 297 else if (i == 9)
8c6db833
LP
298 sig = SIGKILL;
299 else
300 sig = 0;
301
7027ff61
LP
302 r = cg_kill_recursive(controller, path, sig, true, true, rem, NULL);
303 if (r <= 0)
8c6db833
LP
304 return r;
305
9f452741 306 usleep(200 * USEC_PER_MSEC);
8c6db833
LP
307 }
308
309 return 0;
310}
311
246aa6dd 312int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, bool ignore_self) {
35d2e7ec 313 bool done = false;
246aa6dd 314 _cleanup_set_free_ Set *s = NULL;
8c6db833
LP
315 int r, ret = 0;
316 pid_t my_pid;
317
246aa6dd
LP
318 assert(cfrom);
319 assert(pfrom);
320 assert(cto);
321 assert(pto);
8c6db833 322
246aa6dd
LP
323 s = set_new(trivial_hash_func, trivial_compare_func);
324 if (!s)
35d2e7ec
LP
325 return -ENOMEM;
326
8c6db833
LP
327 my_pid = getpid();
328
329 do {
7027ff61 330 _cleanup_fclose_ FILE *f = NULL;
0b172489 331 pid_t pid = 0;
8c6db833
LP
332 done = true;
333
b043cd0b 334 r = cg_enumerate_processes(cfrom, pfrom, &f);
246aa6dd 335 if (r < 0) {
4c633005 336 if (ret >= 0 && r != -ENOENT)
7027ff61 337 return r;
35d2e7ec 338
246aa6dd 339 return ret;
35d2e7ec 340 }
c6c18be3
LP
341
342 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 343
35d2e7ec
LP
344 /* This might do weird stuff if we aren't a
345 * single-threaded program. However, we
346 * luckily know we are not */
7027ff61 347 if (ignore_self && pid == my_pid)
c6c18be3 348 continue;
8c6db833 349
35d2e7ec
LP
350 if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid))
351 continue;
352
246aa6dd
LP
353 r = cg_attach(cto, pto, pid);
354 if (r < 0) {
4c633005 355 if (ret >= 0 && r != -ESRCH)
35d2e7ec
LP
356 ret = r;
357 } else if (ret == 0)
358 ret = 1;
8c6db833 359
8c6db833 360 done = false;
35d2e7ec 361
246aa6dd
LP
362 r = set_put(s, LONG_TO_PTR(pid));
363 if (r < 0) {
35d2e7ec 364 if (ret >= 0)
7027ff61 365 return r;
35d2e7ec 366
246aa6dd 367 return ret;
35d2e7ec
LP
368 }
369 }
370
371 if (r < 0) {
372 if (ret >= 0)
7027ff61 373 return r;
35d2e7ec 374
246aa6dd 375 return ret;
8c6db833 376 }
35d2e7ec 377 } while (!done);
8c6db833 378
35d2e7ec 379 return ret;
8c6db833
LP
380}
381
4ad49000
LP
382int cg_migrate_recursive(
383 const char *cfrom,
384 const char *pfrom,
385 const char *cto,
386 const char *pto,
387 bool ignore_self,
388 bool rem) {
389
246aa6dd 390 _cleanup_closedir_ DIR *d = NULL;
7027ff61 391 int r, ret = 0;
35d2e7ec 392 char *fn;
8c6db833 393
246aa6dd
LP
394 assert(cfrom);
395 assert(pfrom);
396 assert(cto);
397 assert(pto);
8c6db833 398
246aa6dd 399 ret = cg_migrate(cfrom, pfrom, cto, pto, ignore_self);
8c6db833 400
246aa6dd
LP
401 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
402 if (r < 0) {
4c633005 403 if (ret >= 0 && r != -ENOENT)
7027ff61
LP
404 return r;
405
246aa6dd 406 return ret;
35d2e7ec
LP
407 }
408
409 while ((r = cg_read_subgroup(d, &fn)) > 0) {
246aa6dd 410 _cleanup_free_ char *p = NULL;
8c6db833 411
246aa6dd 412 p = strjoin(pfrom, "/", fn, NULL);
35d2e7ec 413 free(fn);
246aa6dd 414 if (!p) {
35d2e7ec 415 if (ret >= 0)
7027ff61 416 return -ENOMEM;
35d2e7ec 417
246aa6dd 418 return ret;
8c6db833
LP
419 }
420
246aa6dd 421 r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem);
35d2e7ec
LP
422 if (r != 0 && ret >= 0)
423 ret = r;
8c6db833
LP
424 }
425
35d2e7ec
LP
426 if (r < 0 && ret >= 0)
427 ret = r;
428
246aa6dd 429 if (rem) {
4ad49000 430 r = cg_rmdir(cfrom, pfrom);
246aa6dd
LP
431 if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY)
432 return r;
433 }
8c6db833
LP
434
435 return ret;
436}
437
3474ae3c
LP
438static const char *normalize_controller(const char *controller) {
439
7027ff61
LP
440 assert(controller);
441
3474ae3c
LP
442 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
443 return "systemd";
444 else if (startswith(controller, "name="))
445 return controller + 5;
446 else
447 return controller;
448}
449
450static int join_path(const char *controller, const char *path, const char *suffix, char **fs) {
018ef268 451 char *t = NULL;
3474ae3c 452
9444b1f2
LP
453 if (!isempty(controller)) {
454 if (!isempty(path) && !isempty(suffix))
b7def684 455 t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL);
9444b1f2 456 else if (!isempty(path))
b7def684 457 t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL);
9444b1f2 458 else if (!isempty(suffix))
b7def684 459 t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL);
c3175a7f 460 else
7027ff61 461 t = strappend("/sys/fs/cgroup/", controller);
c3175a7f 462 } else {
9444b1f2 463 if (!isempty(path) && !isempty(suffix))
b7def684 464 t = strjoin(path, "/", suffix, NULL);
9444b1f2 465 else if (!isempty(path))
c3175a7f 466 t = strdup(path);
7027ff61
LP
467 else
468 return -EINVAL;
c3175a7f 469 }
3474ae3c
LP
470
471 if (!t)
472 return -ENOMEM;
473
474 path_kill_slashes(t);
475
476 *fs = t;
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;
0ac10822 482 static __thread 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);
70132bd0
LP
493 if (r <= 0)
494 return r < 0 ? r : -ENOENT;
495
496 /* Cache this to save a few stat()s */
497 good = true;
498 }
499
c3175a7f 500 p = controller ? normalize_controller(controller) : NULL;
7027ff61 501
3474ae3c
LP
502 return join_path(p, path, suffix, fs);
503}
dbd821ac 504
7027ff61 505static int check_hierarchy(const char *p) {
37099707
LP
506 char *cc;
507
508 assert(p);
509
510 /* Check if this controller actually really exists */
511 cc = alloca(sizeof("/sys/fs/cgroup/") + strlen(p));
512 strcpy(stpcpy(cc, "/sys/fs/cgroup/"), p);
513 if (access(cc, F_OK) < 0)
514 return -errno;
515
516 return 0;
517}
518
3474ae3c
LP
519int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
520 const char *p;
37099707 521 int r;
dbd821ac 522
3474ae3c 523 assert(fs);
70132bd0 524
78edb35a 525 if (!cg_controller_is_valid(controller, true))
3474ae3c 526 return -EINVAL;
70132bd0 527
37099707 528 /* Normalize the controller syntax */
3474ae3c 529 p = normalize_controller(controller);
8c6db833 530
3474ae3c 531 /* Check if this controller actually really exists */
7027ff61 532 r = check_hierarchy(p);
37099707
LP
533 if (r < 0)
534 return r;
3474ae3c
LP
535
536 return join_path(p, path, suffix, fs);
8c6db833
LP
537}
538
e27796a0 539static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
4ad49000
LP
540 assert(path);
541 assert(sb);
542 assert(ftwbuf);
e27796a0
LP
543
544 if (typeflag != FTW_DP)
545 return 0;
546
547 if (ftwbuf->level < 1)
548 return 0;
549
e27796a0
LP
550 rmdir(path);
551 return 0;
552}
553
8c6db833 554int cg_trim(const char *controller, const char *path, bool delete_root) {
7027ff61 555 _cleanup_free_ char *fs = NULL;
e27796a0 556 int r = 0;
8c6db833 557
8c6db833
LP
558 assert(path);
559
e27796a0
LP
560 r = cg_get_path(controller, path, NULL, &fs);
561 if (r < 0)
8c6db833
LP
562 return r;
563
e27796a0 564 errno = 0;
7027ff61 565 if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0)
e27796a0
LP
566 r = errno ? -errno : -EIO;
567
568 if (delete_root) {
4ad49000
LP
569 if (rmdir(fs) < 0 && errno != ENOENT)
570 return -errno;
e27796a0
LP
571 }
572
e27796a0 573 return r;
8c6db833
LP
574}
575
576int cg_delete(const char *controller, const char *path) {
7027ff61 577 _cleanup_free_ char *parent = NULL;
8c6db833
LP
578 int r;
579
8c6db833
LP
580 assert(path);
581
7027ff61
LP
582 r = path_get_parent(path, &parent);
583 if (r < 0)
35d2e7ec 584 return r;
8c6db833 585
246aa6dd 586 r = cg_migrate_recursive(controller, path, controller, parent, false, true);
4c633005 587 return r == -ENOENT ? 0 : r;
8c6db833
LP
588}
589
8c6db833 590int cg_attach(const char *controller, const char *path, pid_t pid) {
574d5f2d
LP
591 _cleanup_free_ char *fs = NULL;
592 char c[DECIMAL_STR_MAX(pid_t) + 2];
8c6db833
LP
593 int r;
594
8c6db833
LP
595 assert(path);
596 assert(pid >= 0);
597
b043cd0b 598 r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
3474ae3c 599 if (r < 0)
c6c18be3 600 return r;
8c6db833
LP
601
602 if (pid == 0)
603 pid = getpid();
604
c6c18be3 605 snprintf(c, sizeof(c), "%lu\n", (unsigned long) pid);
8c6db833 606
574d5f2d 607 return write_string_file(fs, c);
8c6db833
LP
608}
609
2d76d14e
LP
610int cg_set_group_access(
611 const char *controller,
612 const char *path,
613 mode_t mode,
614 uid_t uid,
615 gid_t gid) {
616
574d5f2d 617 _cleanup_free_ char *fs = NULL;
8c6db833
LP
618 int r;
619
8c6db833
LP
620 assert(path);
621
8d53b453
LP
622 if (mode != (mode_t) -1)
623 mode &= 0777;
624
625 r = cg_get_path(controller, path, NULL, &fs);
626 if (r < 0)
8c6db833
LP
627 return r;
628
574d5f2d 629 return chmod_and_chown(fs, mode, uid, gid);
8c6db833
LP
630}
631
974efc46
LP
632int cg_set_task_access(
633 const char *controller,
634 const char *path,
635 mode_t mode,
636 uid_t uid,
4ad49000 637 gid_t gid) {
974efc46
LP
638
639 _cleanup_free_ char *fs = NULL, *procs = NULL;
8c6db833
LP
640 int r;
641
8c6db833
LP
642 assert(path);
643
4ad49000 644 if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1)
8d53b453
LP
645 return 0;
646
647 if (mode != (mode_t) -1)
648 mode &= 0666;
649
b043cd0b 650 r = cg_get_path(controller, path, "cgroup.procs", &fs);
8d53b453 651 if (r < 0)
8c6db833
LP
652 return r;
653
654 r = chmod_and_chown(fs, mode, uid, gid);
974efc46
LP
655 if (r < 0)
656 return r;
8c6db833 657
b043cd0b
LP
658 /* Compatibility, Always keep values for "tasks" in sync with
659 * "cgroup.procs" */
660 r = cg_get_path(controller, path, "tasks", &procs);
974efc46
LP
661 if (r < 0)
662 return r;
663
664 return chmod_and_chown(procs, mode, uid, gid);
8c6db833
LP
665}
666
7027ff61 667int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
7027ff61
LP
668 _cleanup_fclose_ FILE *f = NULL;
669 char line[LINE_MAX];
8af8afd6 670 const char *fs;
c6c18be3 671 size_t cs;
8c6db833 672
8c6db833 673 assert(path);
c6c18be3 674 assert(pid >= 0);
8c6db833 675
8af8afd6
LP
676 if (controller) {
677 if (!cg_controller_is_valid(controller, true))
678 return -EINVAL;
78edb35a 679
8af8afd6
LP
680 controller = normalize_controller(controller);
681 } else
7027ff61
LP
682 controller = SYSTEMD_CGROUP_CONTROLLER;
683
c6c18be3 684 if (pid == 0)
8af8afd6
LP
685 fs = "/proc/self/cgroup";
686 else
687 fs = procfs_file_alloca(pid, "cgroup");
8c6db833 688
c6c18be3 689 f = fopen(fs, "re");
4c633005
LP
690 if (!f)
691 return errno == ENOENT ? -ESRCH : -errno;
692
c6c18be3
LP
693 cs = strlen(controller);
694
7027ff61 695 FOREACH_LINE(line, f, return -errno) {
8af8afd6
LP
696 char *l, *p, *w, *e;
697 size_t k;
698 char *state;
699 bool found = false;
c6c18be3
LP
700
701 truncate_nl(line);
702
7027ff61
LP
703 l = strchr(line, ':');
704 if (!l)
c6c18be3
LP
705 continue;
706
707 l++;
8af8afd6
LP
708 e = strchr(l, ':');
709 if (!e)
c6c18be3
LP
710 continue;
711
8af8afd6
LP
712 *e = 0;
713
714 FOREACH_WORD_SEPARATOR(w, k, l, ",", state) {
715
716 if (k == cs && memcmp(w, controller, cs) == 0) {
717 found = true;
718 break;
719 }
720
721 if (k == 5 + cs &&
722 memcmp(w, "name=", 5) == 0 &&
723 memcmp(w+5, controller, cs) == 0) {
724 found = true;
725 break;
726 }
727 }
728
729 if (!found)
c6c18be3
LP
730 continue;
731
8af8afd6 732 p = strdup(e + 1);
7027ff61
LP
733 if (!p)
734 return -ENOMEM;
c6c18be3
LP
735
736 *path = p;
7027ff61 737 return 0;
c6c18be3
LP
738 }
739
7027ff61 740 return -ENOENT;
8c6db833
LP
741}
742
743int cg_install_release_agent(const char *controller, const char *agent) {
7027ff61
LP
744 _cleanup_free_ char *fs = NULL, *contents = NULL;
745 char *sc;
8c6db833
LP
746 int r;
747
8c6db833
LP
748 assert(agent);
749
7027ff61
LP
750 r = cg_get_path(controller, NULL, "release_agent", &fs);
751 if (r < 0)
c6c18be3 752 return r;
8c6db833 753
7027ff61
LP
754 r = read_one_line_file(fs, &contents);
755 if (r < 0)
756 return r;
8c6db833
LP
757
758 sc = strstrip(contents);
8c6db833 759 if (sc[0] == 0) {
7027ff61 760 r = write_string_file(fs, agent);
574d5f2d 761 if (r < 0)
7027ff61
LP
762 return r;
763 } else if (!streq(sc, agent))
764 return -EEXIST;
8c6db833 765
c6c18be3
LP
766 free(fs);
767 fs = NULL;
7027ff61
LP
768 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
769 if (r < 0)
770 return r;
8c6db833
LP
771
772 free(contents);
773 contents = NULL;
7027ff61
LP
774 r = read_one_line_file(fs, &contents);
775 if (r < 0)
776 return r;
8c6db833
LP
777
778 sc = strstrip(contents);
8c6db833 779 if (streq(sc, "0")) {
7027ff61
LP
780 r = write_string_file(fs, "1");
781 if (r < 0)
782 return r;
c6c18be3 783
7027ff61
LP
784 return 1;
785 }
8c6db833 786
7027ff61
LP
787 if (!streq(sc, "1"))
788 return -EIO;
8c6db833 789
7027ff61 790 return 0;
8c6db833
LP
791}
792
ad929bcc
KS
793int cg_uninstall_release_agent(const char *controller) {
794 _cleanup_free_ char *fs = NULL;
795 int r;
796
ac9ef333
LP
797 r = cg_get_path(controller, NULL, "notify_on_release", &fs);
798 if (r < 0)
799 return r;
800
801 r = write_string_file(fs, "0");
802 if (r < 0)
803 return r;
804
805 free(fs);
806 fs = NULL;
807
ad929bcc
KS
808 r = cg_get_path(controller, NULL, "release_agent", &fs);
809 if (r < 0)
810 return r;
811
812 r = write_string_file(fs, "");
813 if (r < 0)
814 return r;
815
ac9ef333 816 return 0;
ad929bcc
KS
817}
818
8c6db833 819int cg_is_empty(const char *controller, const char *path, bool ignore_self) {
7027ff61 820 _cleanup_fclose_ FILE *f = NULL;
c3175a7f 821 pid_t pid = 0, self_pid;
c6c18be3 822 bool found = false;
7027ff61 823 int r;
8c6db833 824
8c6db833
LP
825 assert(path);
826
b043cd0b 827 r = cg_enumerate_processes(controller, path, &f);
c3175a7f 828 if (r < 0)
4c633005 829 return r == -ENOENT ? 1 : r;
8c6db833 830
c3175a7f
LP
831 self_pid = getpid();
832
c6c18be3 833 while ((r = cg_read_pid(f, &pid)) > 0) {
8c6db833 834
c3175a7f 835 if (ignore_self && pid == self_pid)
c6c18be3 836 continue;
8c6db833 837
c6c18be3
LP
838 found = true;
839 break;
8c6db833
LP
840 }
841
c6c18be3
LP
842 if (r < 0)
843 return r;
8c6db833 844
c6c18be3 845 return !found;
8c6db833
LP
846}
847
b08121d0 848int cg_is_empty_by_spec(const char *spec, bool ignore_self) {
b08121d0 849 _cleanup_free_ char *controller = NULL, *path = NULL;
7027ff61 850 int r;
b08121d0
LP
851
852 assert(spec);
853
854 r = cg_split_spec(spec, &controller, &path);
855 if (r < 0)
856 return r;
857
858 return cg_is_empty(controller, path, ignore_self);
859}
860
8c6db833 861int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) {
7027ff61 862 _cleanup_closedir_ DIR *d = NULL;
35d2e7ec 863 char *fn;
7027ff61 864 int r;
8c6db833 865
8c6db833
LP
866 assert(path);
867
c3175a7f
LP
868 r = cg_is_empty(controller, path, ignore_self);
869 if (r <= 0)
35d2e7ec
LP
870 return r;
871
c3175a7f
LP
872 r = cg_enumerate_subgroups(controller, path, &d);
873 if (r < 0)
4c633005 874 return r == -ENOENT ? 1 : r;
8c6db833 875
35d2e7ec 876 while ((r = cg_read_subgroup(d, &fn)) > 0) {
7027ff61 877 _cleanup_free_ char *p = NULL;
8c6db833 878
7027ff61 879 p = strjoin(path, "/", fn, NULL);
35d2e7ec 880 free(fn);
7027ff61
LP
881 if (!p)
882 return -ENOMEM;
8c6db833 883
35d2e7ec 884 r = cg_is_empty_recursive(controller, p, ignore_self);
35d2e7ec 885 if (r <= 0)
7027ff61 886 return r;
35d2e7ec
LP
887 }
888
7027ff61
LP
889 if (r < 0)
890 return r;
35d2e7ec 891
7027ff61 892 return 1;
35d2e7ec
LP
893}
894
895int cg_split_spec(const char *spec, char **controller, char **path) {
896 const char *e;
897 char *t = NULL, *u = NULL;
5954c074 898 _cleanup_free_ char *v = NULL;
35d2e7ec
LP
899
900 assert(spec);
35d2e7ec
LP
901
902 if (*spec == '/') {
e884315e
LP
903 if (!path_is_safe(spec))
904 return -EINVAL;
35d2e7ec
LP
905
906 if (path) {
246aa6dd
LP
907 t = strdup(spec);
908 if (!t)
35d2e7ec
LP
909 return -ENOMEM;
910
5954c074 911 path_kill_slashes(t);
35d2e7ec 912 *path = t;
8c6db833
LP
913 }
914
35d2e7ec
LP
915 if (controller)
916 *controller = NULL;
917
918 return 0;
8c6db833
LP
919 }
920
246aa6dd
LP
921 e = strchr(spec, ':');
922 if (!e) {
78edb35a 923 if (!cg_controller_is_valid(spec, true))
35d2e7ec
LP
924 return -EINVAL;
925
926 if (controller) {
5954c074 927 t = strdup(normalize_controller(spec));
246aa6dd 928 if (!t)
35d2e7ec
LP
929 return -ENOMEM;
930
931 *controller = t;
932 }
933
934 if (path)
935 *path = NULL;
936
937 return 0;
8c6db833
LP
938 }
939
5954c074
LP
940 v = strndup(spec, e-spec);
941 if (!v)
942 return -ENOMEM;
943 t = strdup(normalize_controller(v));
e884315e
LP
944 if (!t)
945 return -ENOMEM;
78edb35a 946 if (!cg_controller_is_valid(t, true)) {
e884315e 947 free(t);
35d2e7ec 948 return -EINVAL;
246aa6dd
LP
949 }
950
e884315e
LP
951 u = strdup(e+1);
952 if (!u) {
953 free(t);
954 return -ENOMEM;
955 }
5954c074
LP
956 if (!path_is_safe(u) ||
957 !path_is_absolute(u)) {
e884315e
LP
958 free(t);
959 free(u);
960 return -EINVAL;
246aa6dd 961 }
35d2e7ec 962
5954c074
LP
963 path_kill_slashes(u);
964
35d2e7ec
LP
965 if (controller)
966 *controller = t;
e884315e
LP
967 else
968 free(t);
35d2e7ec
LP
969
970 if (path)
971 *path = u;
e884315e
LP
972 else
973 free(u);
35d2e7ec
LP
974
975 return 0;
8c6db833 976}
c6c18be3 977
35d2e7ec 978int cg_join_spec(const char *controller, const char *path, char **spec) {
7027ff61
LP
979 char *s;
980
35d2e7ec 981 assert(path);
c6c18be3 982
7027ff61
LP
983 if (!controller)
984 controller = "systemd";
78edb35a
LP
985 else {
986 if (!cg_controller_is_valid(controller, true))
987 return -EINVAL;
988
989 controller = normalize_controller(controller);
990 }
35d2e7ec 991
7027ff61
LP
992 if (!path_is_absolute(path))
993 return -EINVAL;
994
7027ff61
LP
995 s = strjoin(controller, ":", path, NULL);
996 if (!s)
35d2e7ec 997 return -ENOMEM;
c6c18be3 998
5954c074
LP
999 path_kill_slashes(s + strlen(controller) + 1);
1000
7027ff61 1001 *spec = s;
c6c18be3
LP
1002 return 0;
1003}
35d2e7ec 1004
7027ff61 1005int cg_mangle_path(const char *path, char **result) {
78edb35a
LP
1006 _cleanup_free_ char *c = NULL, *p = NULL;
1007 char *t;
35d2e7ec
LP
1008 int r;
1009
1010 assert(path);
1011 assert(result);
1012
1013 /* First check if it already is a filesystem path */
7027ff61 1014 if (path_startswith(path, "/sys/fs/cgroup")) {
35d2e7ec 1015
b69d29ce
LP
1016 t = strdup(path);
1017 if (!t)
35d2e7ec
LP
1018 return -ENOMEM;
1019
5954c074 1020 path_kill_slashes(t);
35d2e7ec
LP
1021 *result = t;
1022 return 0;
1023 }
1024
1025 /* Otherwise treat it as cg spec */
b69d29ce
LP
1026 r = cg_split_spec(path, &c, &p);
1027 if (r < 0)
35d2e7ec
LP
1028 return r;
1029
78edb35a 1030 return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result);
35d2e7ec 1031}
1f73f0f1 1032
7027ff61 1033int cg_get_root_path(char **path) {
9444b1f2 1034 char *p, *e;
7027ff61
LP
1035 int r;
1036
1037 assert(path);
1038
9444b1f2 1039 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
7027ff61
LP
1040 if (r < 0)
1041 return r;
1042
9444b1f2
LP
1043 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE);
1044 if (e)
7027ff61
LP
1045 *e = 0;
1046
1f73f0f1
LP
1047 *path = p;
1048 return 0;
1049}
b59e2465
LP
1050
1051char **cg_shorten_controllers(char **controllers) {
1052 char **f, **t;
1053
b59e2465
LP
1054 if (!controllers)
1055 return controllers;
1056
1057 for (f = controllers, t = controllers; *f; f++) {
37099707 1058 const char *p;
7027ff61
LP
1059 int r;
1060
1061 p = normalize_controller(*f);
b59e2465 1062
78edb35a
LP
1063 if (streq(p, "systemd")) {
1064 free(*f);
1065 continue;
1066 }
1067
1068 if (!cg_controller_is_valid(p, true)) {
1069 log_warning("Controller %s is not valid, removing from controllers list.", p);
b59e2465
LP
1070 free(*f);
1071 continue;
1072 }
1073
7027ff61 1074 r = check_hierarchy(p);
37099707 1075 if (r < 0) {
78edb35a 1076 log_debug("Controller %s is not available, removing from controllers list.", p);
b59e2465
LP
1077 free(*f);
1078 continue;
1079 }
1080
1081 *(t++) = *f;
1082 }
1083
1084 *t = NULL;
6c03089c 1085 return strv_uniq(controllers);
b59e2465 1086}
ba1261bc 1087
7027ff61
LP
1088int cg_pid_get_path_shifted(pid_t pid, char **root, char **cgroup) {
1089 _cleanup_free_ char *cg_root = NULL;
1090 char *cg_process, *p;
ba1261bc
LP
1091 int r;
1092
7027ff61 1093 r = cg_get_root_path(&cg_root);
ba1261bc
LP
1094 if (r < 0)
1095 return r;
1096
7027ff61
LP
1097 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cg_process);
1098 if (r < 0)
ba1261bc 1099 return r;
ba1261bc 1100
7027ff61
LP
1101 p = path_startswith(cg_process, cg_root);
1102 if (p)
1103 p--;
1104 else
1105 p = cg_process;
ba1261bc
LP
1106
1107 if (cgroup) {
1108 char* c;
1109
1110 c = strdup(p);
1111 if (!c) {
1112 free(cg_process);
1113 return -ENOMEM;
1114 }
1115
1116 *cgroup = c;
1117 }
1118
1119 if (root) {
1120 cg_process[p-cg_process] = 0;
1121 *root = cg_process;
1122 } else
1123 free(cg_process);
1124
1125 return 0;
1126}
1127
7027ff61 1128int cg_path_decode_unit(const char *cgroup, char **unit){
d7bd3de0 1129 char *e, *c, *s;
ef1673d1
MT
1130
1131 assert(cgroup);
6c03089c 1132 assert(unit);
ef1673d1 1133
6c03089c
LP
1134 e = strchrnul(cgroup, '/');
1135 c = strndupa(cgroup, e - cgroup);
ae018d9b 1136 c = cg_unescape(c);
ef1673d1 1137
d7bd3de0 1138 if (!unit_name_is_valid(c, false))
6c03089c 1139 return -EINVAL;
ef1673d1 1140
d7bd3de0 1141 s = strdup(c);
6c03089c
LP
1142 if (!s)
1143 return -ENOMEM;
1144
1145 *unit = s;
ef1673d1
MT
1146 return 0;
1147}
1148
9444b1f2 1149static const char *skip_slices(const char *p) {
9444b1f2
LP
1150 /* Skips over all slice assignments */
1151
1152 for (;;) {
1021b21b
LP
1153 size_t n;
1154
9444b1f2
LP
1155 p += strspn(p, "/");
1156
1157 n = strcspn(p, "/");
1158 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0)
1159 return p;
1160
1161 p += n;
1162 }
1163}
1164
6c03089c
LP
1165int cg_path_get_unit(const char *path, char **unit) {
1166 const char *e;
1167
1168 assert(path);
1169 assert(unit);
1170
9444b1f2 1171 e = skip_slices(path);
6c03089c 1172
7027ff61 1173 return cg_path_decode_unit(e, unit);
6c03089c
LP
1174}
1175
1176int cg_pid_get_unit(pid_t pid, char **unit) {
7fd1b19b 1177 _cleanup_free_ char *cgroup = NULL;
ba1261bc 1178 int r;
ba1261bc 1179
ef1673d1
MT
1180 assert(unit);
1181
7027ff61 1182 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
ef1673d1
MT
1183 if (r < 0)
1184 return r;
1185
6c03089c
LP
1186 return cg_path_get_unit(cgroup, unit);
1187}
ef1673d1 1188
9444b1f2
LP
1189static const char *skip_session(const char *p) {
1190 size_t n;
1191
1192 assert(p);
1193
1194 p += strspn(p, "/");
1195
1196 n = strcspn(p, "/");
374ec6ab 1197 if (n <= 12 || memcmp(p, "session-", 8) != 0 || memcmp(p + n - 6, ".scope", 6) != 0)
6c03089c 1198 return NULL;
ef1673d1 1199
9444b1f2
LP
1200 p += n;
1201 p += strspn(p, "/");
1202
1203 return p;
1204}
1205
6c03089c
LP
1206int cg_path_get_user_unit(const char *path, char **unit) {
1207 const char *e;
ef1673d1 1208
6c03089c 1209 assert(path);
ba1261bc
LP
1210 assert(unit);
1211
6c03089c
LP
1212 /* We always have to parse the path from the beginning as unit
1213 * cgroups might have arbitrary child cgroups and we shouldn't get
1214 * confused by those */
ba1261bc 1215
9444b1f2
LP
1216 /* Skip slices, if there are any */
1217 e = skip_slices(path);
ba1261bc 1218
374ec6ab 1219 /* Skip the session scope, require that there is one */
9444b1f2 1220 e = skip_session(e);
6c03089c
LP
1221 if (!e)
1222 return -ENOENT;
1223
374ec6ab
LP
1224 /* And skip more slices */
1225 e = skip_slices(e);
6c03089c 1226
7027ff61 1227 return cg_path_decode_unit(e, unit);
ef1673d1 1228}
ba1261bc 1229
ef1673d1 1230int cg_pid_get_user_unit(pid_t pid, char **unit) {
7fd1b19b 1231 _cleanup_free_ char *cgroup = NULL;
6c03089c
LP
1232 int r;
1233
1234 assert(unit);
1235
7027ff61 1236 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
6c03089c
LP
1237 if (r < 0)
1238 return r;
1239
1240 return cg_path_get_user_unit(cgroup, unit);
ba1261bc 1241}
e884315e 1242
7027ff61 1243int cg_path_get_machine_name(const char *path, char **machine) {
9444b1f2 1244 const char *e, *n, *x;
ae018d9b 1245 char *s, *r;
374ec6ab 1246 size_t l;
7027ff61
LP
1247
1248 assert(path);
1249 assert(machine);
1250
9444b1f2
LP
1251 /* Skip slices, if there are any */
1252 e = skip_slices(path);
7027ff61
LP
1253
1254 n = strchrnul(e, '/');
1255 if (e == n)
1256 return -ENOENT;
1257
ae018d9b 1258 s = strndupa(e, n - e);
9444b1f2
LP
1259 s = cg_unescape(s);
1260
374ec6ab 1261 x = startswith(s, "machine-");
9444b1f2
LP
1262 if (!x)
1263 return -ENOENT;
374ec6ab
LP
1264 if (!endswith(x, ".scope"))
1265 return -ENOENT;
1266
1267 l = strlen(x);
1268 if (l <= 6)
1269 return -ENOENT;
7027ff61 1270
374ec6ab 1271 r = strndup(x, l - 6);
ae018d9b
LP
1272 if (!r)
1273 return -ENOMEM;
aff38e74 1274
ae018d9b 1275 *machine = r;
7027ff61
LP
1276 return 0;
1277}
1278
1279int cg_pid_get_machine_name(pid_t pid, char **machine) {
7fd1b19b 1280 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1281 int r;
1282
1283 assert(machine);
1284
1285 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1286 if (r < 0)
1287 return r;
1288
1289 return cg_path_get_machine_name(cgroup, machine);
1290}
1291
1292int cg_path_get_session(const char *path, char **session) {
374ec6ab
LP
1293 const char *e, *n, *x;
1294 char *s, *r;
1295 size_t l;
7027ff61
LP
1296
1297 assert(path);
1298 assert(session);
1299
9444b1f2
LP
1300 /* Skip slices, if there are any */
1301 e = skip_slices(path);
7027ff61 1302
7027ff61 1303 n = strchrnul(e, '/');
374ec6ab 1304 if (e == n)
7027ff61 1305 return -ENOENT;
374ec6ab
LP
1306
1307 s = strndupa(e, n - e);
1308 s = cg_unescape(s);
1309
1310 x = startswith(s, "session-");
1311 if (!x)
1312 return -ENOENT;
1313 if (!endswith(x, ".scope"))
7027ff61
LP
1314 return -ENOENT;
1315
374ec6ab
LP
1316 l = strlen(x);
1317 if (l <= 6)
1318 return -ENOENT;
1319
1320 r = strndup(x, l - 6);
1321 if (!r)
7027ff61
LP
1322 return -ENOMEM;
1323
374ec6ab 1324 *session = r;
7027ff61
LP
1325 return 0;
1326}
1327
1328int cg_pid_get_session(pid_t pid, char **session) {
7fd1b19b 1329 _cleanup_free_ char *cgroup = NULL;
7027ff61
LP
1330 int r;
1331
1332 assert(session);
1333
1334 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1335 if (r < 0)
1336 return r;
1337
1338 return cg_path_get_session(cgroup, session);
1339}
1340
ae018d9b 1341int cg_path_get_owner_uid(const char *path, uid_t *uid) {
374ec6ab
LP
1342 _cleanup_free_ char *slice = NULL;
1343 const char *e;
ae018d9b 1344 char *s;
374ec6ab 1345 int r;
ae018d9b
LP
1346
1347 assert(path);
1348 assert(uid);
1349
374ec6ab
LP
1350 r = cg_path_get_slice(path, &slice);
1351 if (r < 0)
1352 return r;
ae018d9b 1353
374ec6ab
LP
1354 e = startswith(slice, "user-");
1355 if (!e)
ae018d9b 1356 return -ENOENT;
374ec6ab 1357 if (!endswith(slice, ".slice"))
ae018d9b
LP
1358 return -ENOENT;
1359
374ec6ab 1360 s = strndupa(e, strlen(e) - 6);
ae018d9b
LP
1361 if (!s)
1362 return -ENOMEM;
1363
1364 return parse_uid(s, uid);
1365}
1366
1367int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1368 _cleanup_free_ char *cgroup = NULL;
1369 int r;
1370
1371 assert(uid);
1372
1373 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1374 if (r < 0)
1375 return r;
1376
1377 return cg_path_get_owner_uid(cgroup, uid);
1378}
1379
1021b21b
LP
1380int cg_path_get_slice(const char *p, char **slice) {
1381 const char *e = NULL;
1382 size_t m = 0;
1383
1384 assert(p);
1385 assert(slice);
1386
1387 for (;;) {
1388 size_t n;
1389
1390 p += strspn(p, "/");
1391
1392 n = strcspn(p, "/");
1393 if (n <= 6 || memcmp(p + n - 6, ".slice", 6) != 0) {
1394 char *s;
1395
1396 if (!e)
1397 return -ENOENT;
1398
1399 s = strndup(e, m);
1400 if (!s)
1401 return -ENOMEM;
1402
1403 *slice = s;
1404 return 0;
1405 }
1406
1407 e = p;
1408 m = n;
1409
1410 p += n;
1411 }
1412}
1413
1414int cg_pid_get_slice(pid_t pid, char **slice) {
1415 _cleanup_free_ char *cgroup = NULL;
1416 int r;
1417
1418 assert(slice);
1419
1420 r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1421 if (r < 0)
1422 return r;
1423
1424 return cg_path_get_slice(cgroup, slice);
1425}
1426
e884315e
LP
1427int cg_controller_from_attr(const char *attr, char **controller) {
1428 const char *dot;
1429 char *c;
1430
1431 assert(attr);
1432 assert(controller);
1433
1434 if (!filename_is_safe(attr))
1435 return -EINVAL;
1436
1437 dot = strchr(attr, '.');
1438 if (!dot) {
1439 *controller = NULL;
1440 return 0;
1441 }
1442
1443 c = strndup(attr, dot - attr);
1444 if (!c)
1445 return -ENOMEM;
1446
78edb35a 1447 if (!cg_controller_is_valid(c, false)) {
e884315e
LP
1448 free(c);
1449 return -EINVAL;
1450 }
1451
1452 *controller = c;
1453 return 1;
1454}
ae018d9b
LP
1455
1456char *cg_escape(const char *p) {
1457 bool need_prefix = false;
1458
1459 /* This implements very minimal escaping for names to be used
1460 * as file names in the cgroup tree: any name which might
1461 * conflict with a kernel name or is prefixed with '_' is
1462 * prefixed with a '_'. That way, when reading cgroup names it
1463 * is sufficient to remove a single prefixing underscore if
1464 * there is one. */
1465
1466 /* The return value of this function (unlike cg_unescape())
1467 * needs free()! */
1468
a0ab5665
LP
1469 if (p[0] == 0 ||
1470 p[0] == '_' ||
1471 p[0] == '.' ||
1472 streq(p, "notify_on_release") ||
1473 streq(p, "release_agent") ||
1474 streq(p, "tasks"))
ae018d9b
LP
1475 need_prefix = true;
1476 else {
1477 const char *dot;
1478
1479 dot = strrchr(p, '.');
1480 if (dot) {
1481
1482 if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0)
1483 need_prefix = true;
1484 else {
1485 char *n;
1486
1487 n = strndupa(p, dot - p);
1488
1489 if (check_hierarchy(n) >= 0)
1490 need_prefix = true;
1491 }
1492 }
1493 }
1494
1495 if (need_prefix)
1496 return strappend("_", p);
1497 else
1498 return strdup(p);
1499}
1500
1501char *cg_unescape(const char *p) {
1502 assert(p);
1503
1504 /* The return value of this function (unlike cg_escape())
1505 * doesn't need free()! */
1506
1507 if (p[0] == '_')
1508 return (char*) p+1;
1509
1510 return (char*) p;
1511}
78edb35a
LP
1512
1513#define CONTROLLER_VALID \
1514 "0123456789" \
1515 "abcdefghijklmnopqrstuvwxyz" \
1516 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
1517 "_"
1518
1519bool cg_controller_is_valid(const char *p, bool allow_named) {
1520 const char *t, *s;
1521
1522 if (!p)
1523 return false;
1524
1525 if (allow_named) {
1526 s = startswith(p, "name=");
1527 if (s)
1528 p = s;
1529 }
1530
1531 if (*p == 0 || *p == '_')
1532 return false;
1533
1534 for (t = p; *t; t++)
1535 if (!strchr(CONTROLLER_VALID, *t))
1536 return false;
1537
1538 if (t - p > FILENAME_MAX)
1539 return false;
1540
1541 return true;
1542}
a016b922
LP
1543
1544int cg_slice_to_path(const char *unit, char **ret) {
1545 _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
1546 const char *dash;
1547
1548 assert(unit);
1549 assert(ret);
1550
1551 if (!unit_name_is_valid(unit, false))
1552 return -EINVAL;
1553
1554 if (!endswith(unit, ".slice"))
1555 return -EINVAL;
1556
1557 p = unit_name_to_prefix(unit);
1558 if (!p)
1559 return -ENOMEM;
1560
1561 dash = strchr(p, '-');
1562 while (dash) {
1563 _cleanup_free_ char *escaped = NULL;
1564 char n[dash - p + sizeof(".slice")];
1565
1566 strcpy(stpncpy(n, p, dash - p), ".slice");
1567
1568 if (!unit_name_is_valid(n, false))
1569 return -EINVAL;
1570
1571 escaped = cg_escape(n);
1572 if (!escaped)
1573 return -ENOMEM;
1574
1575 if (!strextend(&s, escaped, "/", NULL))
1576 return -ENOMEM;
1577
1578 dash = strchr(dash+1, '-');
1579 }
1580
1581 e = cg_escape(unit);
1582 if (!e)
1583 return -ENOMEM;
1584
1585 if (!strextend(&s, e, NULL))
1586 return -ENOMEM;
1587
1588 *ret = s;
1589 s = NULL;
1590
1591 return 0;
1592}
4ad49000
LP
1593
1594int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
1595 _cleanup_free_ char *p = NULL;
1596 int r;
1597
1598 r = cg_get_path(controller, path, attribute, &p);
1599 if (r < 0)
1600 return r;
1601
1602 return write_string_file(p, value);
1603}
1604
1605static const char mask_names[] =
1606 "cpu\0"
1607 "cpuacct\0"
1608 "blkio\0"
1609 "memory\0"
1610 "devices\0";
1611
1612int cg_create_with_mask(CGroupControllerMask mask, const char *path) {
1613 CGroupControllerMask bit = 1;
1614 const char *n;
1615 int r;
1616
1617 /* This one will create a cgroup in our private tree, but also
1618 * duplicate it in the trees specified in mask, and remove it
1619 * in all others */
1620
1621 /* First create the cgroup in our own hierarchy. */
1622 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
1623 if (r < 0)
1624 return r;
1625
1626 /* Then, do the same in the other hierarchies */
1627 NULSTR_FOREACH(n, mask_names) {
1628 if (bit & mask)
1629 cg_create(n, path);
1630 else
1631 cg_trim(n, path, true);
1632
1633 bit <<= 1;
1634 }
1635
1636 return r;
1637}
1638
1639int cg_attach_with_mask(CGroupControllerMask mask, const char *path, pid_t pid) {
1640 CGroupControllerMask bit = 1;
1641 const char *n;
1642 int r;
1643
1644 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
1645
1646 NULSTR_FOREACH(n, mask_names) {
1647 if (bit & mask)
1648 cg_attach(n, path, pid);
1649 else {
1650 char prefix[strlen(path) + 1], *slash;
1651
1652 /* OK, this one is a bit harder... Now we need
1653 * to add to the closest parent cgroup we
1654 * can find */
1655 strcpy(prefix, path);
1656 while ((slash = strrchr(prefix, '/'))) {
1657 int q;
1658 *slash = 0;
1659
1660 q = cg_attach(n, prefix, pid);
1661 if (q >= 0)
1662 break;
1663 }
1664 }
1665
1666 bit <<= 1;
1667 }
1668
1669 return r;
1670}
1671
6c12b52e
LP
1672int cg_attach_many_with_mask(CGroupControllerMask mask, const char *path, Set* pids) {
1673 Iterator i;
1674 void *pidp;
1675 int r = 0;
1676
1677 SET_FOREACH(pidp, pids, i) {
1678 pid_t pid = PTR_TO_LONG(pidp);
1679 int k;
1680
1681 k = cg_attach_with_mask(mask, path, pid);
1682 if (k < 0)
1683 r = k;
1684 }
1685
1686 return r;
1687}
1688
4ad49000
LP
1689int cg_migrate_with_mask(CGroupControllerMask mask, const char *from, const char *to) {
1690 CGroupControllerMask bit = 1;
1691 const char *n;
1692 int r;
1693
1694 if (path_equal(from, to))
1695 return 0;
1696
1697 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true);
1698
1699 NULSTR_FOREACH(n, mask_names) {
1700 if (bit & mask)
1701 cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, to, false, false);
1702 else {
1703 char prefix[strlen(to) + 1], *slash;
1704
1705 strcpy(prefix, to);
1706 while ((slash = strrchr(prefix, '/'))) {
1707 int q;
1708
1709 *slash = 0;
1710
1711 q = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, to, n, prefix, false, false);
1712 if (q >= 0)
1713 break;
1714 }
1715 }
1716
1717 bit <<= 1;
1718 }
1719
1720 return r;
1721}
1722
1723int cg_trim_with_mask(CGroupControllerMask mask, const char *path, bool delete_root) {
1724 CGroupControllerMask bit = 1;
1725 const char *n;
1726 int r;
1727
1728 r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
1729 if (r < 0)
1730 return r;
1731
1732 NULSTR_FOREACH(n, mask_names) {
1733 if (bit & mask)
1734 cg_trim(n, path, delete_root);
1735
1736 bit <<= 1;
1737 }
1738
1739 return r;
1740}
1741
1742CGroupControllerMask cg_mask_supported(void) {
1743 CGroupControllerMask bit = 1, mask = 0;
1744 const char *n;
1745
1746 NULSTR_FOREACH(n, mask_names) {
1747 if (check_hierarchy(n) >= 0)
1748 mask |= bit;
1749
1750 bit <<= 1;
1751 }
1752
1753 return mask;
1754}