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