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