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