chiark / gitweb /
Prep v238: Uncomment now needed headers and unmask now needed functions in src/basic...
[elogind.git] / src / basic / cgroup-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <ftw.h>
24 //#include <limits.h>
25 #include <signal.h>
26 //#include <stddef.h>
27 #include <stdio_ext.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 //#include <sys/statfs.h>
32 #include <sys/types.h>
33 #include <sys/xattr.h>
34 #include <unistd.h>
35
36 #include "alloc-util.h"
37 #include "cgroup-util.h"
38 //#include "def.h"
39 #include "dirent-util.h"
40 #include "extract-word.h"
41 #include "fd-util.h"
42 #include "fileio.h"
43 #include "format-util.h"
44 #include "fs-util.h"
45 //#include "log.h"
46 #include "login-util.h"
47 #include "macro.h"
48 //#include "missing.h"
49 #include "mkdir.h"
50 #include "parse-util.h"
51 #include "path-util.h"
52 #include "proc-cmdline.h"
53 #include "process-util.h"
54 #include "set.h"
55 //#include "special.h"
56 #include "stat-util.h"
57 #include "stdio-util.h"
58 #include "string-table.h"
59 #include "string-util.h"
60 #include "strv.h"
61 #include "unit-name.h"
62 #include "user-util.h"
63
64 int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
65         _cleanup_free_ char *fs = NULL;
66         FILE *f;
67         int r;
68
69         assert(_f);
70
71         r = cg_get_path(controller, path, "cgroup.procs", &fs);
72         if (r < 0)
73                 return r;
74
75         f = fopen(fs, "re");
76         if (!f)
77                 return -errno;
78
79         *_f = f;
80         return 0;
81 }
82
83 int cg_read_pid(FILE *f, pid_t *_pid) {
84         unsigned long ul;
85
86         /* Note that the cgroup.procs might contain duplicates! See
87          * cgroups.txt for details. */
88
89         assert(f);
90         assert(_pid);
91
92         errno = 0;
93         if (fscanf(f, "%lu", &ul) != 1) {
94
95                 if (feof(f))
96                         return 0;
97
98                 return errno > 0 ? -errno : -EIO;
99         }
100
101         if (ul <= 0)
102                 return -EIO;
103
104         *_pid = (pid_t) ul;
105         return 1;
106 }
107
108 int cg_read_event(
109                 const char *controller,
110                 const char *path,
111                 const char *event,
112                 char **val) {
113
114         _cleanup_free_ char *events = NULL, *content = NULL;
115         char *p, *line;
116         int r;
117
118         r = cg_get_path(controller, path, "cgroup.events", &events);
119         if (r < 0)
120                 return r;
121
122         r = read_full_file(events, &content, NULL);
123         if (r < 0)
124                 return r;
125
126         p = content;
127         while ((line = strsep(&p, "\n"))) {
128                 char *key;
129
130                 key = strsep(&line, " ");
131                 if (!key || !line)
132                         return -EINVAL;
133
134                 if (strcmp(key, event))
135                         continue;
136
137                 *val = strdup(line);
138                 return 0;
139         }
140
141         return -ENOENT;
142 }
143
144 #if 0 /// UNNEEDED by elogind
145 bool cg_ns_supported(void) {
146         static thread_local int enabled = -1;
147
148         if (enabled >= 0)
149                 return enabled;
150
151         if (access("/proc/self/ns/cgroup", F_OK) == 0)
152                 enabled = 1;
153         else
154                 enabled = 0;
155
156         return enabled;
157 }
158 #endif // 0
159
160 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
161         _cleanup_free_ char *fs = NULL;
162         int r;
163         DIR *d;
164
165         assert(_d);
166
167         /* This is not recursive! */
168
169         r = cg_get_path(controller, path, NULL, &fs);
170         if (r < 0)
171                 return r;
172
173         d = opendir(fs);
174         if (!d)
175                 return -errno;
176
177         *_d = d;
178         return 0;
179 }
180
181 int cg_read_subgroup(DIR *d, char **fn) {
182         struct dirent *de;
183
184         assert(d);
185         assert(fn);
186
187         FOREACH_DIRENT_ALL(de, d, return -errno) {
188                 char *b;
189
190                 if (de->d_type != DT_DIR)
191                         continue;
192
193                 if (dot_or_dot_dot(de->d_name))
194                         continue;
195
196                 b = strdup(de->d_name);
197                 if (!b)
198                         return -ENOMEM;
199
200                 *fn = b;
201                 return 1;
202         }
203
204         return 0;
205 }
206
207 int cg_rmdir(const char *controller, const char *path) {
208         _cleanup_free_ char *p = NULL;
209         int r;
210
211         r = cg_get_path(controller, path, NULL, &p);
212         if (r < 0)
213                 return r;
214
215         r = rmdir(p);
216         if (r < 0 && errno != ENOENT)
217                 return -errno;
218
219         r = cg_hybrid_unified();
220         if (r < 0)
221                 return r;
222         if (r == 0)
223                 return 0;
224
225         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
226                 r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
227                 if (r < 0)
228                         log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
229         }
230
231         return 0;
232 }
233
234 int cg_kill(
235                 const char *controller,
236                 const char *path,
237                 int sig,
238                 CGroupFlags flags,
239                 Set *s,
240                 cg_kill_log_func_t log_kill,
241                 void *userdata) {
242
243         _cleanup_set_free_ Set *allocated_set = NULL;
244         bool done = false;
245         int r, ret = 0;
246         pid_t my_pid;
247
248         assert(sig >= 0);
249
250          /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send
251           * SIGCONT on SIGKILL. */
252         if (IN_SET(sig, SIGCONT, SIGKILL))
253                 flags &= ~CGROUP_SIGCONT;
254
255         /* This goes through the tasks list and kills them all. This
256          * is repeated until no further processes are added to the
257          * tasks list, to properly handle forking processes */
258
259         if (!s) {
260                 s = allocated_set = set_new(NULL);
261                 if (!s)
262                         return -ENOMEM;
263         }
264
265         my_pid = getpid_cached();
266
267         do {
268                 _cleanup_fclose_ FILE *f = NULL;
269                 pid_t pid = 0;
270                 done = true;
271
272                 r = cg_enumerate_processes(controller, path, &f);
273                 if (r < 0) {
274                         if (ret >= 0 && r != -ENOENT)
275                                 return r;
276
277                         return ret;
278                 }
279
280                 while ((r = cg_read_pid(f, &pid)) > 0) {
281
282                         if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
283                                 continue;
284
285                         if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
286                                 continue;
287
288                         if (log_kill)
289                                 log_kill(pid, sig, userdata);
290
291                         /* If we haven't killed this process yet, kill
292                          * it */
293                         if (kill(pid, sig) < 0) {
294                                 if (ret >= 0 && errno != ESRCH)
295                                         ret = -errno;
296                         } else {
297                                 if (flags & CGROUP_SIGCONT)
298                                         (void) kill(pid, SIGCONT);
299
300                                 if (ret == 0)
301                                         ret = 1;
302                         }
303
304                         done = false;
305
306                         r = set_put(s, PID_TO_PTR(pid));
307                         if (r < 0) {
308                                 if (ret >= 0)
309                                         return r;
310
311                                 return ret;
312                         }
313                 }
314
315                 if (r < 0) {
316                         if (ret >= 0)
317                                 return r;
318
319                         return ret;
320                 }
321
322                 /* To avoid racing against processes which fork
323                  * quicker than we can kill them we repeat this until
324                  * no new pids need to be killed. */
325
326         } while (!done);
327
328         return ret;
329 }
330
331 int cg_kill_recursive(
332                 const char *controller,
333                 const char *path,
334                 int sig,
335                 CGroupFlags flags,
336                 Set *s,
337                 cg_kill_log_func_t log_kill,
338                 void *userdata) {
339
340         _cleanup_set_free_ Set *allocated_set = NULL;
341         _cleanup_closedir_ DIR *d = NULL;
342         int r, ret;
343         char *fn;
344
345         assert(path);
346         assert(sig >= 0);
347
348         if (!s) {
349                 s = allocated_set = set_new(NULL);
350                 if (!s)
351                         return -ENOMEM;
352         }
353
354         ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata);
355
356         r = cg_enumerate_subgroups(controller, path, &d);
357         if (r < 0) {
358                 if (ret >= 0 && r != -ENOENT)
359                         return r;
360
361                 return ret;
362         }
363
364         while ((r = cg_read_subgroup(d, &fn)) > 0) {
365                 _cleanup_free_ char *p = NULL;
366
367                 p = strjoin(path, "/", fn);
368                 free(fn);
369                 if (!p)
370                         return -ENOMEM;
371
372                 r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata);
373                 if (r != 0 && ret >= 0)
374                         ret = r;
375         }
376         if (ret >= 0 && r < 0)
377                 ret = r;
378
379         if (flags & CGROUP_REMOVE) {
380                 r = cg_rmdir(controller, path);
381                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
382                         return r;
383         }
384
385         return ret;
386 }
387
388 int cg_migrate(
389                 const char *cfrom,
390                 const char *pfrom,
391                 const char *cto,
392                 const char *pto,
393                 CGroupFlags flags) {
394
395         bool done = false;
396         _cleanup_set_free_ Set *s = NULL;
397         int r, ret = 0;
398         pid_t my_pid;
399
400         assert(cfrom);
401         assert(pfrom);
402         assert(cto);
403         assert(pto);
404
405         s = set_new(NULL);
406         if (!s)
407                 return -ENOMEM;
408
409         my_pid = getpid_cached();
410
411         log_debug_elogind("Migrating \"%s\"/\"%s\" to \"%s\"/\"%s\" (%s)",
412                           cfrom, pfrom, cto, pto,
413                           (flags & CGROUP_IGNORE_SELF)
414                           ? "ignoring self" : "watching self");
415         do {
416                 _cleanup_fclose_ FILE *f = NULL;
417                 pid_t pid = 0;
418                 done = true;
419
420                 r = cg_enumerate_processes(cfrom, pfrom, &f);
421                 if (r < 0) {
422                         if (ret >= 0 && r != -ENOENT)
423                                 return r;
424
425                         return ret;
426                 }
427
428                 while ((r = cg_read_pid(f, &pid)) > 0) {
429
430                         /* This might do weird stuff if we aren't a
431                          * single-threaded program. However, we
432                          * luckily know we are not */
433                         if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid)
434                                 continue;
435
436                         if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid))
437                                 continue;
438
439                         /* Ignore kernel threads. Since they can only
440                          * exist in the root cgroup, we only check for
441                          * them there. */
442                         if (cfrom &&
443                             (isempty(pfrom) || path_equal(pfrom, "/")) &&
444                             is_kernel_thread(pid) > 0)
445                                 continue;
446
447                         r = cg_attach(cto, pto, pid);
448                         if (r < 0) {
449                                 if (ret >= 0 && r != -ESRCH)
450                                         ret = r;
451                         } else if (ret == 0)
452                                 ret = 1;
453
454                         done = false;
455
456                         r = set_put(s, PID_TO_PTR(pid));
457                         if (r < 0) {
458                                 if (ret >= 0)
459                                         return r;
460
461                                 return ret;
462                         }
463                 }
464
465                 if (r < 0) {
466                         if (ret >= 0)
467                                 return r;
468
469                         return ret;
470                 }
471         } while (!done);
472
473         return ret;
474 }
475
476 int cg_migrate_recursive(
477                 const char *cfrom,
478                 const char *pfrom,
479                 const char *cto,
480                 const char *pto,
481                 CGroupFlags flags) {
482
483         _cleanup_closedir_ DIR *d = NULL;
484         int r, ret = 0;
485         char *fn;
486
487         assert(cfrom);
488         assert(pfrom);
489         assert(cto);
490         assert(pto);
491
492         ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
493
494         r = cg_enumerate_subgroups(cfrom, pfrom, &d);
495         if (r < 0) {
496                 if (ret >= 0 && r != -ENOENT)
497                         return r;
498
499                 return ret;
500         }
501
502         while ((r = cg_read_subgroup(d, &fn)) > 0) {
503                 _cleanup_free_ char *p = NULL;
504
505                 p = strjoin(pfrom, "/", fn);
506                 free(fn);
507                 if (!p)
508                         return -ENOMEM;
509
510                 r = cg_migrate_recursive(cfrom, p, cto, pto, flags);
511                 if (r != 0 && ret >= 0)
512                         ret = r;
513         }
514
515         if (r < 0 && ret >= 0)
516                 ret = r;
517
518         if (flags & CGROUP_REMOVE) {
519                 r = cg_rmdir(cfrom, pfrom);
520                 if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY))
521                         return r;
522         }
523
524         return ret;
525 }
526
527 int cg_migrate_recursive_fallback(
528                 const char *cfrom,
529                 const char *pfrom,
530                 const char *cto,
531                 const char *pto,
532                 CGroupFlags flags) {
533
534         int r;
535
536         assert(cfrom);
537         assert(pfrom);
538         assert(cto);
539         assert(pto);
540
541         r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags);
542         if (r < 0) {
543                 char prefix[strlen(pto) + 1];
544
545                 /* This didn't work? Then let's try all prefixes of the destination */
546
547                 PATH_FOREACH_PREFIX(prefix, pto) {
548                         int q;
549
550                         q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags);
551                         if (q >= 0)
552                                 return q;
553                 }
554         }
555
556         return r;
557 }
558
559 static const char *controller_to_dirname(const char *controller) {
560         const char *e;
561
562         assert(controller);
563
564         /* Converts a controller name to the directory name below
565          * /sys/fs/cgroup/ we want to mount it to. Effectively, this
566          * just cuts off the name= prefixed used for named
567          * hierarchies, if it is specified. */
568
569         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
570                 if (cg_hybrid_unified() > 0)
571                         controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
572                 else
573                         controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
574         }
575
576         e = startswith(controller, "name=");
577         if (e)
578                 return e;
579
580         return controller;
581 }
582
583 static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) {
584         const char *dn;
585         char *t = NULL;
586
587         assert(fs);
588         assert(controller);
589
590         dn = controller_to_dirname(controller);
591
592         if (isempty(path) && isempty(suffix))
593                 t = strappend("/sys/fs/cgroup/", dn);
594         else if (isempty(path))
595                 t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
596         else if (isempty(suffix))
597                 t = strjoin("/sys/fs/cgroup/", dn, "/", path);
598         else
599                 t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
600         if (!t)
601                 return -ENOMEM;
602
603         *fs = t;
604         return 0;
605 }
606
607 static int join_path_unified(const char *path, const char *suffix, char **fs) {
608         char *t;
609
610         assert(fs);
611
612         if (isempty(path) && isempty(suffix))
613                 t = strdup("/sys/fs/cgroup");
614         else if (isempty(path))
615                 t = strappend("/sys/fs/cgroup/", suffix);
616         else if (isempty(suffix))
617                 t = strappend("/sys/fs/cgroup/", path);
618         else
619                 t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
620         if (!t)
621                 return -ENOMEM;
622
623         *fs = t;
624         return 0;
625 }
626
627 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
628         int r;
629
630         assert(fs);
631
632         if (!controller) {
633                 char *t;
634
635                 /* If no controller is specified, we return the path
636                  * *below* the controllers, without any prefix. */
637
638                 if (!path && !suffix)
639                         return -EINVAL;
640
641                 if (!suffix)
642                         t = strdup(path);
643                 else if (!path)
644                         t = strdup(suffix);
645                 else
646                         t = strjoin(path, "/", suffix);
647                 if (!t)
648                         return -ENOMEM;
649
650                 *fs = path_kill_slashes(t);
651                 return 0;
652         }
653
654         if (!cg_controller_is_valid(controller))
655                 return -EINVAL;
656
657         r = cg_all_unified();
658         if (r < 0)
659                 return r;
660         if (r > 0)
661                 r = join_path_unified(path, suffix, fs);
662         else
663                 r = join_path_legacy(controller, path, suffix, fs);
664         if (r < 0)
665                 return r;
666
667         path_kill_slashes(*fs);
668         return 0;
669 }
670
671 static int controller_is_accessible(const char *controller) {
672         int r;
673
674         assert(controller);
675
676         /* Checks whether a specific controller is accessible,
677          * i.e. its hierarchy mounted. In the unified hierarchy all
678          * controllers are considered accessible, except for the named
679          * hierarchies */
680
681         if (!cg_controller_is_valid(controller))
682                 return -EINVAL;
683
684         r = cg_all_unified();
685         if (r < 0)
686                 return r;
687         if (r > 0) {
688                 /* We don't support named hierarchies if we are using
689                  * the unified hierarchy. */
690
691                 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
692                         return 0;
693
694                 if (startswith(controller, "name="))
695                         return -EOPNOTSUPP;
696
697         } else {
698                 const char *cc, *dn;
699
700                 dn = controller_to_dirname(controller);
701                 cc = strjoina("/sys/fs/cgroup/", dn);
702
703                 if (laccess(cc, F_OK) < 0)
704                         return -errno;
705         }
706
707         return 0;
708 }
709
710 int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) {
711         int r;
712
713         assert(controller);
714         assert(fs);
715
716         /* Check if the specified controller is actually accessible */
717         r = controller_is_accessible(controller);
718         if (r < 0)
719                 return r;
720
721         return cg_get_path(controller, path, suffix, fs);
722 }
723
724 static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
725         assert(path);
726         assert(sb);
727         assert(ftwbuf);
728
729         if (typeflag != FTW_DP)
730                 return 0;
731
732         if (ftwbuf->level < 1)
733                 return 0;
734
735         (void) rmdir(path);
736         return 0;
737 }
738
739 int cg_trim(const char *controller, const char *path, bool delete_root) {
740         _cleanup_free_ char *fs = NULL;
741         int r = 0, q;
742
743         assert(path);
744
745         r = cg_get_path(controller, path, NULL, &fs);
746         if (r < 0)
747                 return r;
748
749         errno = 0;
750         if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) {
751                 if (errno == ENOENT)
752                         r = 0;
753                 else if (errno > 0)
754                         r = -errno;
755                 else
756                         r = -EIO;
757         }
758
759         if (delete_root) {
760                 if (rmdir(fs) < 0 && errno != ENOENT)
761                         return -errno;
762         }
763
764         q = cg_hybrid_unified();
765         if (q < 0)
766                 return q;
767         if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
768                 q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
769                 if (q < 0)
770                         log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
771         }
772
773         return r;
774 }
775
776 int cg_create(const char *controller, const char *path) {
777         _cleanup_free_ char *fs = NULL;
778         int r;
779
780         r = cg_get_path_and_check(controller, path, NULL, &fs);
781         if (r < 0)
782                 return r;
783
784         r = mkdir_parents(fs, 0755);
785         if (r < 0)
786                 return r;
787
788         r = mkdir_errno_wrapper(fs, 0755);
789         if (r == -EEXIST)
790                 return 0;
791         if (r < 0)
792                 return r;
793
794         r = cg_hybrid_unified();
795         if (r < 0)
796                 return r;
797
798         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
799                 r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
800                 if (r < 0)
801                         log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
802         }
803
804         return 1;
805 }
806
807 int cg_create_and_attach(const char *controller, const char *path, pid_t pid) {
808         int r, q;
809
810         assert(pid >= 0);
811
812         r = cg_create(controller, path);
813         if (r < 0)
814                 return r;
815
816         q = cg_attach(controller, path, pid);
817         if (q < 0)
818                 return q;
819
820         /* This does not remove the cgroup on failure */
821         return r;
822 }
823
824 int cg_attach(const char *controller, const char *path, pid_t pid) {
825         _cleanup_free_ char *fs = NULL;
826         char c[DECIMAL_STR_MAX(pid_t) + 2];
827         int r;
828
829         assert(path);
830         assert(pid >= 0);
831
832         r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs);
833         if (r < 0)
834                 return r;
835
836         if (pid == 0)
837                 pid = getpid_cached();
838
839         xsprintf(c, PID_FMT "\n", pid);
840
841         r = write_string_file(fs, c, 0);
842         if (r < 0)
843                 return r;
844
845         r = cg_hybrid_unified();
846         if (r < 0)
847                 return r;
848
849         if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
850                 r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
851                 if (r < 0)
852                         log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path);
853         }
854
855         return 0;
856 }
857
858 int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
859         int r;
860
861         assert(controller);
862         assert(path);
863         assert(pid >= 0);
864
865         r = cg_attach(controller, path, pid);
866         if (r < 0) {
867                 char prefix[strlen(path) + 1];
868
869                 /* This didn't work? Then let's try all prefixes of
870                  * the destination */
871
872                 PATH_FOREACH_PREFIX(prefix, path) {
873                         int q;
874
875                         q = cg_attach(controller, prefix, pid);
876                         if (q >= 0)
877                                 return q;
878                 }
879         }
880
881         return r;
882 }
883
884 #if 0 /// UNNEEDED by elogind
885 int cg_set_access(
886                 const char *controller,
887                 const char *path,
888                 uid_t uid,
889                 gid_t gid) {
890
891         struct Attribute {
892                 const char *name;
893                 bool fatal;
894         };
895
896         /* cgroupsv1, aka legacy/non-unified */
897         static const struct Attribute legacy_attributes[] = {
898                 { "cgroup.procs",           true  },
899                 { "tasks",                  false },
900                 { "cgroup.clone_children",  false },
901                 {},
902         };
903
904         /* cgroupsv2, aka unified */
905         static const struct Attribute unified_attributes[] = {
906                 { "cgroup.procs",           true  },
907                 { "cgroup.subtree_control", true  },
908                 { "cgroup.threads",         false },
909                 {},
910         };
911
912         static const struct Attribute* const attributes[] = {
913                 [false] = legacy_attributes,
914                 [true]  = unified_attributes,
915         };
916
917         _cleanup_free_ char *fs = NULL;
918         const struct Attribute *i;
919         int r, unified;
920
921         assert(path);
922
923         if (uid == UID_INVALID && gid == GID_INVALID)
924                 return 0;
925
926         unified = cg_unified_controller(controller);
927         if (unified < 0)
928                 return unified;
929
930         /* Configure access to the cgroup itself */
931         r = cg_get_path(controller, path, NULL, &fs);
932         if (r < 0)
933                 return r;
934
935         r = chmod_and_chown(fs, 0755, uid, gid);
936         if (r < 0)
937                 return r;
938
939         /* Configure access to the cgroup's attributes */
940         for (i = attributes[unified]; i->name; i++) {
941                 fs = mfree(fs);
942
943                 r = cg_get_path(controller, path, i->name, &fs);
944                 if (r < 0)
945                         return r;
946
947                 r = chmod_and_chown(fs, 0644, uid, gid);
948                 if (r < 0) {
949                         if (i->fatal)
950                                 return r;
951
952                         log_debug_errno(r, "Failed to set access on cgroup %s, ignoring: %m", fs);
953                 }
954         }
955
956         if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
957                 r = cg_hybrid_unified();
958                 if (r < 0)
959                         return r;
960                 if (r > 0) {
961                         /* Always propagate access mode from unified to legacy controller */
962                         r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, uid, gid);
963                         if (r < 0)
964                                 log_debug_errno(r, "Failed to set access on compatibility elogind cgroup %s, ignoring: %m", path);
965                 }
966         }
967
968         return 0;
969 }
970
971 int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
972         _cleanup_free_ char *fs = NULL;
973         int r;
974
975         assert(path);
976         assert(name);
977         assert(value || size <= 0);
978
979         r = cg_get_path(controller, path, NULL, &fs);
980         if (r < 0)
981                 return r;
982
983         if (setxattr(fs, name, value, size, flags) < 0)
984                 return -errno;
985
986         return 0;
987 }
988
989 int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
990         _cleanup_free_ char *fs = NULL;
991         ssize_t n;
992         int r;
993
994         assert(path);
995         assert(name);
996
997         r = cg_get_path(controller, path, NULL, &fs);
998         if (r < 0)
999                 return r;
1000
1001         n = getxattr(fs, name, value, size);
1002         if (n < 0)
1003                 return -errno;
1004
1005         return (int) n;
1006 }
1007 #endif // 0
1008
1009 int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
1010         _cleanup_fclose_ FILE *f = NULL;
1011         char line[LINE_MAX];
1012 #if 0 /// At elogind we do not want that (false alarm) "maybe uninitialized" warning
1013         const char *fs, *controller_str;
1014 #else
1015         const char *fs, *controller_str = NULL;
1016 #endif // 0
1017         size_t cs = 0;
1018         int unified;
1019
1020         assert(path);
1021         assert(pid >= 0);
1022
1023         if (controller) {
1024                 if (!cg_controller_is_valid(controller))
1025                         return -EINVAL;
1026         } else
1027                 controller = SYSTEMD_CGROUP_CONTROLLER;
1028
1029         unified = cg_unified_controller(controller);
1030         if (unified < 0)
1031                 return unified;
1032         if (unified == 0) {
1033                 if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
1034                         controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
1035                 else
1036                         controller_str = controller;
1037
1038                 cs = strlen(controller_str);
1039         }
1040
1041         fs = procfs_file_alloca(pid, "cgroup");
1042         log_debug_elogind("Searching for PID %u in \"%s\" (controller \"%s\")",
1043                           pid, fs, controller);
1044         f = fopen(fs, "re");
1045         if (!f)
1046                 return errno == ENOENT ? -ESRCH : -errno;
1047
1048         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
1049
1050         FOREACH_LINE(line, f, return -errno) {
1051                 char *e, *p;
1052
1053                 truncate_nl(line);
1054
1055                 if (unified) {
1056                         e = startswith(line, "0:");
1057                         if (!e)
1058                                 continue;
1059
1060                         e = strchr(e, ':');
1061                         if (!e)
1062                                 continue;
1063                 } else {
1064                         char *l;
1065                         size_t k;
1066                         const char *word, *state;
1067                         bool found = false;
1068
1069                         l = strchr(line, ':');
1070                         if (!l)
1071                                 continue;
1072
1073                         l++;
1074                         e = strchr(l, ':');
1075                         if (!e)
1076                                 continue;
1077
1078                         *e = 0;
1079                         FOREACH_WORD_SEPARATOR(word, k, l, ",", state)
1080                                 if (k == cs && memcmp(word, controller_str, cs) == 0) {
1081                                         found = true;
1082                                         break;
1083                                 }
1084                         if (!found)
1085                                 continue;
1086                 }
1087
1088                 log_debug_elogind("Found %s:%s", line, e+1);
1089                 p = strdup(e + 1);
1090                 if (!p)
1091                         return -ENOMEM;
1092
1093                 /* Truncate suffix indicating the process is a zombie */
1094                 e = endswith(p, " (deleted)");
1095                 if (e)
1096                         *e = 0;
1097
1098                 *path = p;
1099                 return 0;
1100         }
1101
1102         return -ENODATA;
1103 }
1104
1105 #if 0 /// UNNEEDED by elogind
1106 int cg_install_release_agent(const char *controller, const char *agent) {
1107         _cleanup_free_ char *fs = NULL, *contents = NULL;
1108         const char *sc;
1109         int r;
1110
1111         assert(agent);
1112
1113         r = cg_unified_controller(controller);
1114         if (r < 0)
1115                 return r;
1116         if (r > 0) /* doesn't apply to unified hierarchy */
1117                 return -EOPNOTSUPP;
1118
1119         r = cg_get_path(controller, NULL, "release_agent", &fs);
1120         if (r < 0)
1121                 return r;
1122
1123         r = read_one_line_file(fs, &contents);
1124         if (r < 0)
1125                 return r;
1126
1127         sc = strstrip(contents);
1128         if (isempty(sc)) {
1129                 r = write_string_file(fs, agent, 0);
1130                 if (r < 0)
1131                         return r;
1132         } else if (!path_equal(sc, agent))
1133                 return -EEXIST;
1134
1135         fs = mfree(fs);
1136         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1137         if (r < 0)
1138                 return r;
1139
1140         contents = mfree(contents);
1141         r = read_one_line_file(fs, &contents);
1142         if (r < 0)
1143                 return r;
1144
1145         sc = strstrip(contents);
1146         if (streq(sc, "0")) {
1147                 r = write_string_file(fs, "1", 0);
1148                 if (r < 0)
1149                         return r;
1150
1151                 return 1;
1152         }
1153
1154         if (!streq(sc, "1"))
1155                 return -EIO;
1156
1157         return 0;
1158 }
1159
1160 int cg_uninstall_release_agent(const char *controller) {
1161         _cleanup_free_ char *fs = NULL;
1162         int r;
1163
1164         r = cg_unified_controller(controller);
1165         if (r < 0)
1166                 return r;
1167         if (r > 0) /* Doesn't apply to unified hierarchy */
1168                 return -EOPNOTSUPP;
1169
1170         r = cg_get_path(controller, NULL, "notify_on_release", &fs);
1171         if (r < 0)
1172                 return r;
1173
1174         r = write_string_file(fs, "0", 0);
1175         if (r < 0)
1176                 return r;
1177
1178         fs = mfree(fs);
1179
1180         r = cg_get_path(controller, NULL, "release_agent", &fs);
1181         if (r < 0)
1182                 return r;
1183
1184         r = write_string_file(fs, "", 0);
1185         if (r < 0)
1186                 return r;
1187
1188         return 0;
1189 }
1190 #endif // 0
1191
1192 int cg_is_empty(const char *controller, const char *path) {
1193         _cleanup_fclose_ FILE *f = NULL;
1194         pid_t pid;
1195         int r;
1196
1197         assert(path);
1198
1199         r = cg_enumerate_processes(controller, path, &f);
1200         if (r == -ENOENT)
1201                 return 1;
1202         if (r < 0)
1203                 return r;
1204
1205         r = cg_read_pid(f, &pid);
1206         if (r < 0)
1207                 return r;
1208
1209         return r == 0;
1210 }
1211
1212 int cg_is_empty_recursive(const char *controller, const char *path) {
1213         int r;
1214
1215         assert(path);
1216
1217         /* The root cgroup is always populated */
1218         if (controller && (isempty(path) || path_equal(path, "/")))
1219                 return false;
1220
1221         r = cg_unified_controller(controller);
1222         if (r < 0)
1223                 return r;
1224         if (r > 0) {
1225                 _cleanup_free_ char *t = NULL;
1226
1227                 /* On the unified hierarchy we can check empty state
1228                  * via the "populated" attribute of "cgroup.events". */
1229
1230                 r = cg_read_event(controller, path, "populated", &t);
1231                 if (r < 0)
1232                         return r;
1233
1234                 return streq(t, "0");
1235         } else {
1236                 _cleanup_closedir_ DIR *d = NULL;
1237                 char *fn;
1238
1239                 r = cg_is_empty(controller, path);
1240                 if (r <= 0)
1241                         return r;
1242
1243                 r = cg_enumerate_subgroups(controller, path, &d);
1244                 if (r == -ENOENT)
1245                         return 1;
1246                 if (r < 0)
1247                         return r;
1248
1249                 while ((r = cg_read_subgroup(d, &fn)) > 0) {
1250                         _cleanup_free_ char *p = NULL;
1251
1252                         p = strjoin(path, "/", fn);
1253                         free(fn);
1254                         if (!p)
1255                                 return -ENOMEM;
1256
1257                         r = cg_is_empty_recursive(controller, p);
1258                         if (r <= 0)
1259                                 return r;
1260                 }
1261                 if (r < 0)
1262                         return r;
1263
1264                 return true;
1265         }
1266 }
1267
1268 int cg_split_spec(const char *spec, char **controller, char **path) {
1269         char *t = NULL, *u = NULL;
1270         const char *e;
1271
1272         assert(spec);
1273
1274         if (*spec == '/') {
1275                 if (!path_is_normalized(spec))
1276                         return -EINVAL;
1277
1278                 if (path) {
1279                         t = strdup(spec);
1280                         if (!t)
1281                                 return -ENOMEM;
1282
1283                         *path = path_kill_slashes(t);
1284                 }
1285
1286                 if (controller)
1287                         *controller = NULL;
1288
1289                 return 0;
1290         }
1291
1292         e = strchr(spec, ':');
1293         if (!e) {
1294                 if (!cg_controller_is_valid(spec))
1295                         return -EINVAL;
1296
1297                 if (controller) {
1298                         t = strdup(spec);
1299                         if (!t)
1300                                 return -ENOMEM;
1301
1302                         *controller = t;
1303                 }
1304
1305                 if (path)
1306                         *path = NULL;
1307
1308                 return 0;
1309         }
1310
1311         t = strndup(spec, e-spec);
1312         if (!t)
1313                 return -ENOMEM;
1314         if (!cg_controller_is_valid(t)) {
1315                 free(t);
1316                 return -EINVAL;
1317         }
1318
1319         if (isempty(e+1))
1320                 u = NULL;
1321         else {
1322                 u = strdup(e+1);
1323                 if (!u) {
1324                         free(t);
1325                         return -ENOMEM;
1326                 }
1327
1328                 if (!path_is_normalized(u) ||
1329                     !path_is_absolute(u)) {
1330                         free(t);
1331                         free(u);
1332                         return -EINVAL;
1333                 }
1334
1335                 path_kill_slashes(u);
1336         }
1337
1338         if (controller)
1339                 *controller = t;
1340         else
1341                 free(t);
1342
1343         if (path)
1344                 *path = u;
1345         else
1346                 free(u);
1347
1348         return 0;
1349 }
1350
1351 int cg_mangle_path(const char *path, char **result) {
1352         _cleanup_free_ char *c = NULL, *p = NULL;
1353         char *t;
1354         int r;
1355
1356         assert(path);
1357         assert(result);
1358
1359         /* First, check if it already is a filesystem path */
1360         if (path_startswith(path, "/sys/fs/cgroup")) {
1361
1362                 t = strdup(path);
1363                 if (!t)
1364                         return -ENOMEM;
1365
1366                 *result = path_kill_slashes(t);
1367                 return 0;
1368         }
1369
1370         /* Otherwise, treat it as cg spec */
1371         r = cg_split_spec(path, &c, &p);
1372         if (r < 0)
1373                 return r;
1374
1375         return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result);
1376 }
1377
1378 int cg_get_root_path(char **path) {
1379         char *p, *e;
1380         int r;
1381
1382         assert(path);
1383
1384         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p);
1385         if (r < 0)
1386                 return r;
1387
1388 #if 0 /// elogind does not support systemd scopes and slices
1389         e = endswith(p, "/" SPECIAL_INIT_SCOPE);
1390         if (!e)
1391                 e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */
1392         if (!e)
1393                 e = endswith(p, "/system"); /* even more legacy */
1394 #else
1395         e = endswith(p, "/elogind");
1396 #endif // 0
1397         if (e)
1398                 *e = 0;
1399
1400         *path = p;
1401         return 0;
1402 }
1403
1404 int cg_shift_path(const char *cgroup, const char *root, const char **shifted) {
1405         _cleanup_free_ char *rt = NULL;
1406         char *p;
1407         int r;
1408
1409         assert(cgroup);
1410         assert(shifted);
1411
1412         if (!root) {
1413                 /* If the root was specified let's use that, otherwise
1414                  * let's determine it from PID 1 */
1415
1416                 r = cg_get_root_path(&rt);
1417                 if (r < 0)
1418                         return r;
1419
1420                 root = rt;
1421                 log_debug_elogind("Determined root path: \"%s\"", root);
1422         }
1423
1424         p = path_startswith(cgroup, root);
1425 #if 0 /// With other controllers, elogind might end up in /elogind, and *p is 0
1426         if (p && p > cgroup)
1427 #else
1428         if (p && p[0] && (p > cgroup))
1429 #endif // 0
1430                 *shifted = p - 1;
1431         else
1432                 *shifted = cgroup;
1433
1434         return 0;
1435 }
1436
1437 int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) {
1438         _cleanup_free_ char *raw = NULL;
1439         const char *c;
1440         int r;
1441
1442         assert(pid >= 0);
1443         assert(cgroup);
1444
1445         r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw);
1446         if (r < 0)
1447                 return r;
1448
1449         log_debug_elogind("Shifting path: \"%s\" (PID %u, root: \"%s\")",
1450                           raw, pid, root ? root : "NULL");
1451         r = cg_shift_path(raw, root, &c);
1452         if (r < 0)
1453                 return r;
1454
1455         if (c == raw) {
1456                 *cgroup = raw;
1457                 raw = NULL;
1458         } else {
1459                 char *n;
1460
1461                 n = strdup(c);
1462                 if (!n)
1463                         return -ENOMEM;
1464
1465                 *cgroup = n;
1466         }
1467         log_debug_elogind("Resulting cgroup:\"%s\"", *cgroup);
1468
1469         return 0;
1470 }
1471
1472 int cg_path_decode_unit(const char *cgroup, char **unit) {
1473         char *c, *s;
1474         size_t n;
1475
1476         assert(cgroup);
1477         assert(unit);
1478
1479         n = strcspn(cgroup, "/");
1480         if (n < 3)
1481                 return -ENXIO;
1482
1483         c = strndupa(cgroup, n);
1484         c = cg_unescape(c);
1485
1486         if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1487                 return -ENXIO;
1488
1489         s = strdup(c);
1490         if (!s)
1491                 return -ENOMEM;
1492
1493         *unit = s;
1494         return 0;
1495 }
1496
1497 static bool valid_slice_name(const char *p, size_t n) {
1498
1499         if (!p)
1500                 return false;
1501
1502         if (n < STRLEN("x.slice"))
1503                 return false;
1504
1505         if (memcmp(p + n - 6, ".slice", 6) == 0) {
1506                 char buf[n+1], *c;
1507
1508                 memcpy(buf, p, n);
1509                 buf[n] = 0;
1510
1511                 c = cg_unescape(buf);
1512
1513                 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
1514         }
1515
1516         return false;
1517 }
1518
1519 static const char *skip_slices(const char *p) {
1520         assert(p);
1521
1522         /* Skips over all slice assignments */
1523
1524         for (;;) {
1525                 size_t n;
1526
1527                 p += strspn(p, "/");
1528
1529                 n = strcspn(p, "/");
1530                 if (!valid_slice_name(p, n))
1531                         return p;
1532
1533                 p += n;
1534         }
1535 }
1536
1537 int cg_path_get_unit(const char *path, char **ret) {
1538         const char *e;
1539         char *unit;
1540         int r;
1541
1542         assert(path);
1543         assert(ret);
1544
1545         e = skip_slices(path);
1546
1547         r = cg_path_decode_unit(e, &unit);
1548         if (r < 0)
1549                 return r;
1550
1551         /* We skipped over the slices, don't accept any now */
1552         if (endswith(unit, ".slice")) {
1553                 free(unit);
1554                 return -ENXIO;
1555         }
1556
1557         *ret = unit;
1558         return 0;
1559 }
1560
1561 int cg_pid_get_unit(pid_t pid, char **unit) {
1562         _cleanup_free_ char *cgroup = NULL;
1563         int r;
1564
1565         assert(unit);
1566
1567         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1568         if (r < 0)
1569                 return r;
1570
1571         return cg_path_get_unit(cgroup, unit);
1572 }
1573
1574 #if 0 /// UNNEEDED by elogind
1575 /**
1576  * Skip session-*.scope, but require it to be there.
1577  */
1578 static const char *skip_session(const char *p) {
1579         size_t n;
1580
1581         if (isempty(p))
1582                 return NULL;
1583
1584         p += strspn(p, "/");
1585
1586         n = strcspn(p, "/");
1587         if (n < STRLEN("session-x.scope"))
1588                 return NULL;
1589
1590         if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1591                 char buf[n - 8 - 6 + 1];
1592
1593                 memcpy(buf, p + 8, n - 8 - 6);
1594                 buf[n - 8 - 6] = 0;
1595
1596                 /* Note that session scopes never need unescaping,
1597                  * since they cannot conflict with the kernel's own
1598                  * names, hence we don't need to call cg_unescape()
1599                  * here. */
1600
1601                 if (!session_id_valid(buf))
1602                         return false;
1603
1604                 p += n;
1605                 p += strspn(p, "/");
1606                 return p;
1607         }
1608
1609         return NULL;
1610 }
1611
1612 /**
1613  * Skip user@*.service, but require it to be there.
1614  */
1615 static const char *skip_user_manager(const char *p) {
1616         size_t n;
1617
1618         if (isempty(p))
1619                 return NULL;
1620
1621         p += strspn(p, "/");
1622
1623         n = strcspn(p, "/");
1624         if (n < STRLEN("user@x.service"))
1625                 return NULL;
1626
1627         if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1628                 char buf[n - 5 - 8 + 1];
1629
1630                 memcpy(buf, p + 5, n - 5 - 8);
1631                 buf[n - 5 - 8] = 0;
1632
1633                 /* Note that user manager services never need unescaping,
1634                  * since they cannot conflict with the kernel's own
1635                  * names, hence we don't need to call cg_unescape()
1636                  * here. */
1637
1638                 if (parse_uid(buf, NULL) < 0)
1639                         return NULL;
1640
1641                 p += n;
1642                 p += strspn(p, "/");
1643
1644                 return p;
1645         }
1646
1647         return NULL;
1648 }
1649
1650 static const char *skip_user_prefix(const char *path) {
1651         const char *e, *t;
1652
1653         assert(path);
1654
1655         /* Skip slices, if there are any */
1656         e = skip_slices(path);
1657
1658         /* Skip the user manager, if it's in the path now... */
1659         t = skip_user_manager(e);
1660         if (t)
1661                 return t;
1662
1663         /* Alternatively skip the user session if it is in the path... */
1664         return skip_session(e);
1665 }
1666
1667 int cg_path_get_user_unit(const char *path, char **ret) {
1668         const char *t;
1669
1670         assert(path);
1671         assert(ret);
1672
1673         t = skip_user_prefix(path);
1674         if (!t)
1675                 return -ENXIO;
1676
1677         /* And from here on it looks pretty much the same as for a
1678          * system unit, hence let's use the same parser from here
1679          * on. */
1680         return cg_path_get_unit(t, ret);
1681 }
1682
1683 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1684         _cleanup_free_ char *cgroup = NULL;
1685         int r;
1686
1687         assert(unit);
1688
1689         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1690         if (r < 0)
1691                 return r;
1692
1693         return cg_path_get_user_unit(cgroup, unit);
1694 }
1695
1696 int cg_path_get_machine_name(const char *path, char **machine) {
1697         _cleanup_free_ char *u = NULL;
1698         const char *sl;
1699         int r;
1700
1701         r = cg_path_get_unit(path, &u);
1702         if (r < 0)
1703                 return r;
1704
1705         sl = strjoina("/run/systemd/machines/unit:", u);
1706         return readlink_malloc(sl, machine);
1707 }
1708
1709 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1710         _cleanup_free_ char *cgroup = NULL;
1711         int r;
1712
1713         assert(machine);
1714
1715         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1716         if (r < 0)
1717                 return r;
1718
1719         return cg_path_get_machine_name(cgroup, machine);
1720 }
1721 #endif // 0
1722
1723 int cg_path_get_session(const char *path, char **session) {
1724 #if 0 /// UNNEEDED by elogind
1725         _cleanup_free_ char *unit = NULL;
1726         char *start, *end;
1727         int r;
1728
1729         assert(path);
1730
1731         r = cg_path_get_unit(path, &unit);
1732         if (r < 0)
1733                 return r;
1734
1735         start = startswith(unit, "session-");
1736         if (!start)
1737                 return -ENXIO;
1738         end = endswith(start, ".scope");
1739         if (!end)
1740                 return -ENXIO;
1741
1742         *end = 0;
1743         if (!session_id_valid(start))
1744                 return -ENXIO;
1745 #else
1746         /* Elogind uses a flat hierarchy, just "/SESSION".  The only
1747            wrinkle is that SESSION might be escaped.  */
1748         const char *e, *n, *start;
1749
1750         assert(path);
1751         log_debug_elogind("path is \"%s\"", path);
1752         assert(path[0] == '/');
1753
1754         e = path + 1;
1755         n = strchrnul(e, '/');
1756         if (e == n)
1757                 return -ENOENT;
1758
1759         start = strndupa(e, n - e);
1760         start = cg_unescape(start);
1761
1762         if (!start[0])
1763                 return -ENOENT;
1764 #endif // 0
1765
1766         if (session) {
1767                 char *rr;
1768
1769                 log_debug_elogind("found session: \"%s\"", start);
1770                 rr = strdup(start);
1771                 if (!rr)
1772                         return -ENOMEM;
1773
1774                 *session = rr;
1775         }
1776
1777         return 0;
1778 }
1779
1780 int cg_pid_get_session(pid_t pid, char **session) {
1781         _cleanup_free_ char *cgroup = NULL;
1782         int r;
1783
1784         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1785         if (r < 0)
1786                 return r;
1787
1788         return cg_path_get_session(cgroup, session);
1789 }
1790
1791 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1792 #if 0 /// elogind needs one more value
1793         _cleanup_free_ char *slice = NULL;
1794         char *start, *end;
1795 #else
1796         _cleanup_free_ char *slice = NULL, *p = NULL, *s = NULL;
1797 #endif // 0
1798         int r;
1799
1800         assert(path);
1801
1802         r = cg_path_get_slice(path, &slice);
1803         if (r < 0)
1804                 return r;
1805
1806 #if 0 /// elogind does not support systemd slices
1807         start = startswith(slice, "user-");
1808         if (!start)
1809                 return -ENXIO;
1810         end = endswith(start, ".slice");
1811         if (!end)
1812                 return -ENXIO;
1813
1814         *end = 0;
1815         if (parse_uid(start, uid) < 0)
1816                 return -ENXIO;
1817 #else
1818         p = strappend("/run/systemd/sessions/", slice);
1819
1820         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
1821         if (r == -ENOENT)
1822                 return -ENXIO;
1823         if (r < 0)
1824                 return r;
1825         if (isempty(s))
1826                 return -EIO;
1827
1828         if (parse_uid(s, uid) < 0)
1829                 return -ENXIO;
1830 #endif // 0
1831
1832         return 0;
1833 }
1834
1835 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1836         _cleanup_free_ char *cgroup = NULL;
1837         int r;
1838
1839         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1840         if (r < 0)
1841                 return r;
1842
1843         return cg_path_get_owner_uid(cgroup, uid);
1844 }
1845
1846 int cg_path_get_slice(const char *p, char **slice) {
1847         const char *e = NULL;
1848
1849         assert(p);
1850         assert(slice);
1851
1852 #if 0 /// elogind does not support systemd slices
1853         /* Finds the right-most slice unit from the beginning, but
1854          * stops before we come to the first non-slice unit. */
1855
1856         for (;;) {
1857                 size_t n;
1858
1859                 p += strspn(p, "/");
1860
1861                 n = strcspn(p, "/");
1862                 if (!valid_slice_name(p, n)) {
1863
1864                         if (!e) {
1865                                 char *s;
1866
1867                                 s = strdup(SPECIAL_ROOT_SLICE);
1868                                 if (!s)
1869                                         return -ENOMEM;
1870
1871                                 *slice = s;
1872                                 return 0;
1873                         }
1874
1875                         return cg_path_decode_unit(e, slice);
1876                 }
1877
1878                 e = p;
1879                 p += n;
1880         }
1881 #else
1882         /* In elogind, what is reported here, is the location of
1883          * the session. This is derived from /proc/<self|PID>/cgroup.
1884          * In there we look at the controller, which will look something
1885          * like "1:name=openrc:/3".
1886          * The last part gets extracted (and is now p), which is "/3" in
1887          * this case. The three is the session id, and that can be mapped.
1888          */
1889         e = startswith(p, "/");
1890
1891         if (e)
1892                 *slice = strdup(e);
1893         else
1894                 *slice = strdup(p);
1895
1896         return 0;
1897 #endif // 0
1898 }
1899
1900 int cg_pid_get_slice(pid_t pid, char **slice) {
1901         _cleanup_free_ char *cgroup = NULL;
1902         int r;
1903
1904         assert(slice);
1905
1906         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1907         log_debug_elogind("Found cgroup %s for pid %u (result %d)",
1908                           cgroup, pid, r);
1909         if (r < 0)
1910                 return r;
1911
1912         return cg_path_get_slice(cgroup, slice);
1913 }
1914
1915 int cg_path_get_user_slice(const char *p, char **slice) {
1916 #if 0 /// UNNEEDED by elogind
1917         const char *t;
1918 #endif // 0
1919         assert(p);
1920         assert(slice);
1921
1922 #if 0 /// nothing to skip in elogind
1923         t = skip_user_prefix(p);
1924         if (!t)
1925                 return -ENXIO;
1926 #endif // 0
1927
1928 #if 0 /// UNNEEDED by elogind
1929         /* And now it looks pretty much the same as for a system
1930          * slice, so let's just use the same parser from here on. */
1931         return cg_path_get_slice(t, slice);
1932 #else
1933         /* In elogind there is nothing to skip, we can use the path
1934          * directly. Generally speaking this is always a session id
1935          * to user mapping. */
1936         return cg_path_get_slice(p, slice);
1937 #endif // 0
1938 }
1939
1940 int cg_pid_get_user_slice(pid_t pid, char **slice) {
1941         _cleanup_free_ char *cgroup = NULL;
1942         int r;
1943
1944         assert(slice);
1945
1946         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1947         if (r < 0)
1948                 return r;
1949
1950         return cg_path_get_user_slice(cgroup, slice);
1951 }
1952
1953 char *cg_escape(const char *p) {
1954         bool need_prefix = false;
1955
1956         /* This implements very minimal escaping for names to be used
1957          * as file names in the cgroup tree: any name which might
1958          * conflict with a kernel name or is prefixed with '_' is
1959          * prefixed with a '_'. That way, when reading cgroup names it
1960          * is sufficient to remove a single prefixing underscore if
1961          * there is one. */
1962
1963         /* The return value of this function (unlike cg_unescape())
1964          * needs free()! */
1965
1966         if (IN_SET(p[0], 0, '_', '.') ||
1967             streq(p, "notify_on_release") ||
1968             streq(p, "release_agent") ||
1969             streq(p, "tasks") ||
1970             startswith(p, "cgroup."))
1971                 need_prefix = true;
1972         else {
1973                 const char *dot;
1974
1975                 dot = strrchr(p, '.');
1976                 if (dot) {
1977                         CGroupController c;
1978                         size_t l = dot - p;
1979
1980                         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1981                                 const char *n;
1982
1983                                 n = cgroup_controller_to_string(c);
1984
1985                                 if (l != strlen(n))
1986                                         continue;
1987
1988                                 if (memcmp(p, n, l) != 0)
1989                                         continue;
1990
1991                                 need_prefix = true;
1992                                 break;
1993                         }
1994                 }
1995         }
1996
1997         if (need_prefix)
1998                 return strappend("_", p);
1999
2000         return strdup(p);
2001 }
2002
2003 char *cg_unescape(const char *p) {
2004         assert(p);
2005
2006         /* The return value of this function (unlike cg_escape())
2007          * doesn't need free()! */
2008
2009         if (p[0] == '_')
2010                 return (char*) p+1;
2011
2012         return (char*) p;
2013 }
2014
2015 #define CONTROLLER_VALID                        \
2016         DIGITS LETTERS                          \
2017         "_"
2018
2019 bool cg_controller_is_valid(const char *p) {
2020         const char *t, *s;
2021
2022         if (!p)
2023                 return false;
2024
2025         if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
2026                 return true;
2027
2028         s = startswith(p, "name=");
2029         if (s)
2030                 p = s;
2031
2032         if (IN_SET(*p, 0, '_'))
2033                 return false;
2034
2035         for (t = p; *t; t++)
2036                 if (!strchr(CONTROLLER_VALID, *t))
2037                         return false;
2038
2039         if (t - p > FILENAME_MAX)
2040                 return false;
2041
2042         return true;
2043 }
2044
2045 #if 0 /// UNNEEDED by elogind
2046 int cg_slice_to_path(const char *unit, char **ret) {
2047         _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
2048         const char *dash;
2049         int r;
2050
2051         assert(unit);
2052         assert(ret);
2053
2054         if (streq(unit, SPECIAL_ROOT_SLICE)) {
2055                 char *x;
2056
2057                 x = strdup("");
2058                 if (!x)
2059                         return -ENOMEM;
2060                 *ret = x;
2061                 return 0;
2062         }
2063
2064         if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
2065                 return -EINVAL;
2066
2067         if (!endswith(unit, ".slice"))
2068                 return -EINVAL;
2069
2070         r = unit_name_to_prefix(unit, &p);
2071         if (r < 0)
2072                 return r;
2073
2074         dash = strchr(p, '-');
2075
2076         /* Don't allow initial dashes */
2077         if (dash == p)
2078                 return -EINVAL;
2079
2080         while (dash) {
2081                 _cleanup_free_ char *escaped = NULL;
2082                 char n[dash - p + sizeof(".slice")];
2083
2084                 /* Don't allow trailing or double dashes */
2085                 if (IN_SET(dash[1], 0, '-'))
2086                         return -EINVAL;
2087
2088                 strcpy(stpncpy(n, p, dash - p), ".slice");
2089                 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
2090                         return -EINVAL;
2091
2092                 escaped = cg_escape(n);
2093                 if (!escaped)
2094                         return -ENOMEM;
2095
2096                 if (!strextend(&s, escaped, "/", NULL))
2097                         return -ENOMEM;
2098
2099                 dash = strchr(dash+1, '-');
2100         }
2101
2102         e = cg_escape(unit);
2103         if (!e)
2104                 return -ENOMEM;
2105
2106         if (!strextend(&s, e, NULL))
2107                 return -ENOMEM;
2108
2109         *ret = s;
2110         s = NULL;
2111
2112         return 0;
2113 }
2114 #endif // 0
2115
2116 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
2117         _cleanup_free_ char *p = NULL;
2118         int r;
2119
2120         r = cg_get_path(controller, path, attribute, &p);
2121         if (r < 0)
2122                 return r;
2123
2124         return write_string_file(p, value, 0);
2125 }
2126
2127 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
2128         _cleanup_free_ char *p = NULL;
2129         int r;
2130
2131         r = cg_get_path(controller, path, attribute, &p);
2132         if (r < 0)
2133                 return r;
2134
2135         return read_one_line_file(p, ret);
2136 }
2137
2138 #if 0 /// UNNEEDED by elogind
2139 int cg_get_keyed_attribute(
2140                 const char *controller,
2141                 const char *path,
2142                 const char *attribute,
2143                 char **keys,
2144                 char **ret_values) {
2145
2146         _cleanup_free_ char *filename = NULL, *contents = NULL;
2147         _cleanup_fclose_ FILE *f = NULL;
2148         const char *p;
2149         size_t n, i, n_done = 0;
2150         char **v;
2151         int r;
2152
2153         /* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
2154          * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
2155          * entries as 'keys'. On success each entry will be set to the value of the matching key.
2156          *
2157          * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
2158
2159         r = cg_get_path(controller, path, attribute, &filename);
2160         if (r < 0)
2161                 return r;
2162
2163         r = read_full_file(filename, &contents, NULL);
2164         if (r < 0)
2165                 return r;
2166
2167         n = strv_length(keys);
2168         if (n == 0) /* No keys to retrieve? That's easy, we are done then */
2169                 return 0;
2170
2171         /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
2172         v = newa0(char*, n);
2173
2174         for (p = contents; *p;) {
2175                 const char *w = NULL;
2176
2177                 for (i = 0; i < n; i++)
2178                         if (!v[i]) {
2179                                 w = first_word(p, keys[i]);
2180                                 if (w)
2181                                         break;
2182                         }
2183
2184                 if (w) {
2185                         size_t l;
2186
2187                         l = strcspn(w, NEWLINE);
2188                         v[i] = strndup(w, l);
2189                         if (!v[i]) {
2190                                 r = -ENOMEM;
2191                                 goto fail;
2192                         }
2193
2194                         n_done++;
2195                         if (n_done >= n)
2196                                 goto done;
2197
2198                         p = w + l;
2199                 } else
2200                         p += strcspn(p, NEWLINE);
2201
2202                 p += strspn(p, NEWLINE);
2203         }
2204
2205         r = -ENXIO;
2206
2207 fail:
2208         for (i = 0; i < n; i++)
2209                 free(v[i]);
2210
2211         return r;
2212
2213 done:
2214         memcpy(ret_values, v, sizeof(char*) * n);
2215         return 0;
2216
2217 }
2218
2219 int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2220         CGroupController c;
2221         int r;
2222
2223         /* This one will create a cgroup in our private tree, but also
2224          * duplicate it in the trees specified in mask, and remove it
2225          * in all others */
2226
2227         /* First create the cgroup in our own hierarchy. */
2228         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2229         if (r < 0)
2230                 return r;
2231
2232         /* If we are in the unified hierarchy, we are done now */
2233         r = cg_all_unified();
2234         if (r < 0)
2235                 return r;
2236         if (r > 0)
2237                 return 0;
2238
2239         /* Otherwise, do the same in the other hierarchies */
2240         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2241                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2242                 const char *n;
2243
2244                 n = cgroup_controller_to_string(c);
2245
2246                 if (mask & bit)
2247                         (void) cg_create(n, path);
2248                 else if (supported & bit)
2249                         (void) cg_trim(n, path, true);
2250         }
2251
2252         return 0;
2253 }
2254
2255 int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2256         CGroupController c;
2257         int r;
2258
2259         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
2260         if (r < 0)
2261                 return r;
2262
2263         r = cg_all_unified();
2264         if (r < 0)
2265                 return r;
2266         if (r > 0)
2267                 return 0;
2268
2269         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2270                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2271                 const char *p = NULL;
2272
2273                 if (!(supported & bit))
2274                         continue;
2275
2276                 if (path_callback)
2277                         p = path_callback(bit, userdata);
2278
2279                 if (!p)
2280                         p = path;
2281
2282                 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
2283         }
2284
2285         return 0;
2286 }
2287
2288 int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
2289         Iterator i;
2290         void *pidp;
2291         int r = 0;
2292
2293         SET_FOREACH(pidp, pids, i) {
2294                 pid_t pid = PTR_TO_PID(pidp);
2295                 int q;
2296
2297                 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
2298                 if (q < 0 && r >= 0)
2299                         r = q;
2300         }
2301
2302         return r;
2303 }
2304
2305 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
2306         CGroupController c;
2307         int r = 0, q;
2308
2309         if (!path_equal(from, to))  {
2310                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
2311                 if (r < 0)
2312                         return r;
2313         }
2314
2315         q = cg_all_unified();
2316         if (q < 0)
2317                 return q;
2318         if (q > 0)
2319                 return r;
2320
2321         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2322                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2323                 const char *p = NULL;
2324
2325                 if (!(supported & bit))
2326                         continue;
2327
2328                 if (to_callback)
2329                         p = to_callback(bit, userdata);
2330
2331                 if (!p)
2332                         p = to;
2333
2334                 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
2335         }
2336
2337         return 0;
2338 }
2339
2340 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2341         CGroupController c;
2342         int r, q;
2343
2344         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2345         if (r < 0)
2346                 return r;
2347
2348         q = cg_all_unified();
2349         if (q < 0)
2350                 return q;
2351         if (q > 0)
2352                 return r;
2353
2354         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2355                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2356
2357                 if (!(supported & bit))
2358                         continue;
2359
2360                 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
2361         }
2362
2363         return 0;
2364 }
2365 #endif // 0
2366
2367 int cg_mask_to_string(CGroupMask mask, char **ret) {
2368         _cleanup_free_ char *s = NULL;
2369         size_t n = 0, allocated = 0;
2370         bool space = false;
2371         CGroupController c;
2372
2373         assert(ret);
2374
2375         if (mask == 0) {
2376                 *ret = NULL;
2377                 return 0;
2378         }
2379
2380         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2381                 const char *k;
2382                 size_t l;
2383
2384                 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2385                         continue;
2386
2387                 k = cgroup_controller_to_string(c);
2388                 l = strlen(k);
2389
2390                 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2391                         return -ENOMEM;
2392
2393                 if (space)
2394                         s[n] = ' ';
2395                 memcpy(s + n + space, k, l);
2396                 n += space + l;
2397
2398                 space = true;
2399         }
2400
2401         assert(s);
2402
2403         s[n] = 0;
2404         *ret = s;
2405         s = NULL;
2406
2407         return 0;
2408 }
2409
2410 int cg_mask_from_string(const char *value, CGroupMask *mask) {
2411         assert(mask);
2412         assert(value);
2413
2414         for (;;) {
2415                 _cleanup_free_ char *n = NULL;
2416                 CGroupController v;
2417                 int r;
2418
2419                 r = extract_first_word(&value, &n, NULL, 0);
2420                 if (r < 0)
2421                         return r;
2422                 if (r == 0)
2423                         break;
2424
2425                 v = cgroup_controller_from_string(n);
2426                 if (v < 0)
2427                         continue;
2428
2429                 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2430         }
2431         return 0;
2432 }
2433
2434 int cg_mask_supported(CGroupMask *ret) {
2435         CGroupMask mask = 0;
2436         int r;
2437
2438         /* Determines the mask of supported cgroup controllers. Only
2439          * includes controllers we can make sense of and that are
2440          * actually accessible. */
2441
2442         r = cg_all_unified();
2443         if (r < 0)
2444                 return r;
2445         if (r > 0) {
2446                 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
2447
2448                 /* In the unified hierarchy we can read the supported
2449                  * and accessible controllers from a the top-level
2450                  * cgroup attribute */
2451
2452                 r = cg_get_root_path(&root);
2453                 if (r < 0)
2454                         return r;
2455
2456                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2457                 if (r < 0)
2458                         return r;
2459
2460                 r = read_one_line_file(path, &controllers);
2461                 if (r < 0)
2462                         return r;
2463
2464                 r = cg_mask_from_string(controllers, &mask);
2465                 if (r < 0)
2466                         return r;
2467
2468                 /* Currently, we support the cpu, memory, io and pids
2469                  * controller in the unified hierarchy, mask
2470                  * everything else off. */
2471                 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
2472
2473         } else {
2474                 CGroupController c;
2475
2476                 /* In the legacy hierarchy, we check whether which
2477                  * hierarchies are mounted. */
2478
2479                 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2480                         const char *n;
2481
2482                         n = cgroup_controller_to_string(c);
2483                         if (controller_is_accessible(n) >= 0)
2484                                 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2485                 }
2486         }
2487
2488         *ret = mask;
2489         return 0;
2490 }
2491
2492 #if 0 /// UNNEEDED by elogind
2493 int cg_kernel_controllers(Set **ret) {
2494         _cleanup_set_free_free_ Set *controllers = NULL;
2495         _cleanup_fclose_ FILE *f = NULL;
2496         int r;
2497
2498         assert(ret);
2499
2500         /* Determines the full list of kernel-known controllers. Might
2501          * include controllers we don't actually support, arbitrary
2502          * named hierarchies and controllers that aren't currently
2503          * accessible (because not mounted). */
2504
2505         controllers = set_new(&string_hash_ops);
2506         if (!controllers)
2507                 return -ENOMEM;
2508
2509         f = fopen("/proc/cgroups", "re");
2510         if (!f) {
2511                 if (errno == ENOENT) {
2512                         *ret = NULL;
2513                         return 0;
2514                 }
2515
2516                 return -errno;
2517         }
2518
2519         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2520
2521         /* Ignore the header line */
2522         (void) read_line(f, (size_t) -1, NULL);
2523
2524         for (;;) {
2525                 char *controller;
2526                 int enabled = 0;
2527
2528                 errno = 0;
2529                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2530
2531                         if (feof(f))
2532                                 break;
2533
2534                         if (ferror(f) && errno > 0)
2535                                 return -errno;
2536
2537                         return -EBADMSG;
2538                 }
2539
2540                 if (!enabled) {
2541                         free(controller);
2542                         continue;
2543                 }
2544
2545                 if (!cg_controller_is_valid(controller)) {
2546                         free(controller);
2547                         return -EBADMSG;
2548                 }
2549
2550                 r = set_consume(controllers, controller);
2551                 if (r < 0)
2552                         return r;
2553         }
2554
2555         *ret = controllers;
2556         controllers = NULL;
2557
2558         return 0;
2559 }
2560 #endif // 0
2561
2562 static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2563
2564 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd.  This
2565  * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2566  * /sys/fs/cgroup/systemd.  From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2567  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2568  *
2569  * To keep live upgrade working, we detect and support v232 layout.  When v232 layout is detected, to keep cgroup v2
2570  * process management but disable the compat dual layout, we return %true on
2571  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2572  */
2573 static thread_local bool unified_systemd_v232;
2574
2575 static int cg_unified_update(void) {
2576
2577         struct statfs fs;
2578
2579         /* Checks if we support the unified hierarchy. Returns an
2580          * error when the cgroup hierarchies aren't mounted yet or we
2581          * have any other trouble determining if the unified hierarchy
2582          * is supported. */
2583
2584         if (unified_cache >= CGROUP_UNIFIED_NONE)
2585                 return 0;
2586
2587         if (statfs("/sys/fs/cgroup/", &fs) < 0)
2588                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m");
2589
2590         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2591                 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2592                 unified_cache = CGROUP_UNIFIED_ALL;
2593 #if 0 /// The handling of cgroups is a bit different with elogind
2594         } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2595                         log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2596 #else
2597         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)
2598               || F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2599 #endif // 0
2600                 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
2601                     F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2602                         unified_cache = CGROUP_UNIFIED_SYSTEMD;
2603                         unified_systemd_v232 = false;
2604                 } else {
2605 #if 0 /// There is no sub-grouping within elogind
2606                         if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
2607                                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2608
2609                         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2610                                 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2611                                 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2612                                 unified_systemd_v232 = true;
2613                         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2614                                 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2615                                 unified_cache = CGROUP_UNIFIED_NONE;
2616                         } else {
2617                                 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2618                                           (unsigned long long) fs.f_type);
2619                                 unified_cache = CGROUP_UNIFIED_NONE;
2620                         }
2621 #else
2622                         unified_cache = CGROUP_UNIFIED_NONE;
2623 #endif // 0
2624                 }
2625         } else {
2626                 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2627                           (unsigned long long) fs.f_type);
2628                 return -ENOMEDIUM;
2629         }
2630
2631         return 0;
2632 }
2633
2634 int cg_unified_controller(const char *controller) {
2635         int r;
2636
2637         r = cg_unified_update();
2638         if (r < 0)
2639                 return r;
2640
2641         if (unified_cache == CGROUP_UNIFIED_NONE)
2642                 return false;
2643
2644         if (unified_cache >= CGROUP_UNIFIED_ALL)
2645                 return true;
2646
2647 #if 0 /// only if elogind is the controller we can use cgroups2 in hybrid mode
2648         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
2649 #else
2650         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID);
2651 #endif // 0
2652 }
2653
2654 int cg_all_unified(void) {
2655         int r;
2656
2657         r = cg_unified_update();
2658         if (r < 0)
2659                 return r;
2660
2661         return unified_cache >= CGROUP_UNIFIED_ALL;
2662 }
2663
2664 int cg_hybrid_unified(void) {
2665         int r;
2666
2667         r = cg_unified_update();
2668         if (r < 0)
2669                 return r;
2670
2671         return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2672 }
2673
2674 int cg_unified_flush(void) {
2675         unified_cache = CGROUP_UNIFIED_UNKNOWN;
2676
2677         return cg_unified_update();
2678 }
2679
2680 #if 0 /// UNNEEDED by elogind
2681 int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
2682         _cleanup_fclose_ FILE *f = NULL;
2683         _cleanup_free_ char *fs = NULL;
2684         CGroupController c;
2685         int r;
2686
2687         assert(p);
2688
2689         if (supported == 0)
2690                 return 0;
2691
2692         r = cg_all_unified();
2693         if (r < 0)
2694                 return r;
2695         if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
2696                 return 0;
2697
2698         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2699         if (r < 0)
2700                 return r;
2701
2702         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2703                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2704                 const char *n;
2705
2706                 if (!(supported & bit))
2707                         continue;
2708
2709                 n = cgroup_controller_to_string(c);
2710                 {
2711                         char s[1 + strlen(n) + 1];
2712
2713                         s[0] = mask & bit ? '+' : '-';
2714                         strcpy(s + 1, n);
2715
2716                         if (!f) {
2717                                 f = fopen(fs, "we");
2718                                 if (!f) {
2719                                         log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2720                                         break;
2721                                 }
2722                         }
2723
2724                         r = write_string_stream(f, s, 0);
2725                         if (r < 0)
2726                                 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
2727                 }
2728         }
2729
2730         return 0;
2731 }
2732 #endif // 0
2733
2734 bool cg_is_unified_wanted(void) {
2735         static thread_local int wanted = -1;
2736         int r;
2737         bool b;
2738         const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
2739
2740         /* If we have a cached value, return that. */
2741         if (wanted >= 0)
2742                 return wanted;
2743
2744         /* If the hierarchy is already mounted, then follow whatever
2745          * was chosen for it. */
2746         if (cg_unified_flush() >= 0)
2747                 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
2748
2749 #if 0 /// elogind is not init and has no business with kernel command line
2750         /* Otherwise, let's see what the kernel command line has to say.
2751          * Since checking is expensive, cache a non-error result. */
2752         r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
2753 #endif // 0
2754
2755         return (wanted = r > 0 ? b : is_default);
2756 }
2757
2758 bool cg_is_legacy_wanted(void) {
2759         static thread_local int wanted = -1;
2760
2761         /* If we have a cached value, return that. */
2762         if (wanted >= 0)
2763                 return wanted;
2764
2765         /* Check if we have cgroups2 already mounted. */
2766         if (cg_unified_flush() >= 0 &&
2767             unified_cache == CGROUP_UNIFIED_ALL)
2768                 return (wanted = false);
2769
2770         /* Otherwise, assume that at least partial legacy is wanted,
2771          * since cgroups2 should already be mounted at this point. */
2772         return (wanted = true);
2773 }
2774
2775 bool cg_is_hybrid_wanted(void) {
2776         static thread_local int wanted = -1;
2777         int r;
2778         bool b;
2779         const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2780         /* We default to true if the default is "hybrid", obviously,
2781          * but also when the default is "unified", because if we get
2782          * called, it means that unified hierarchy was not mounted. */
2783
2784         /* If we have a cached value, return that. */
2785         if (wanted >= 0)
2786                 return wanted;
2787
2788         /* If the hierarchy is already mounted, then follow whatever
2789          * was chosen for it. */
2790         if (cg_unified_flush() >= 0 &&
2791             unified_cache == CGROUP_UNIFIED_ALL)
2792                 return (wanted = false);
2793
2794 #if 0 /// elogind is not init and has no business with kernel command line
2795         /* Otherwise, let's see what the kernel command line has to say.
2796          * Since checking is expensive, cache a non-error result. */
2797         r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
2798 #endif // 0
2799
2800         /* The meaning of the kernel option is reversed wrt. to the return value
2801          * of this function, hence the negation. */
2802         return (wanted = r > 0 ? !b : is_default);
2803 }
2804
2805 #if 0 /// UNNEEDED by elogind
2806 int cg_weight_parse(const char *s, uint64_t *ret) {
2807         uint64_t u;
2808         int r;
2809
2810         if (isempty(s)) {
2811                 *ret = CGROUP_WEIGHT_INVALID;
2812                 return 0;
2813         }
2814
2815         r = safe_atou64(s, &u);
2816         if (r < 0)
2817                 return r;
2818
2819         if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2820                 return -ERANGE;
2821
2822         *ret = u;
2823         return 0;
2824 }
2825
2826 const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2827         [CGROUP_IO_RBPS_MAX]    = CGROUP_LIMIT_MAX,
2828         [CGROUP_IO_WBPS_MAX]    = CGROUP_LIMIT_MAX,
2829         [CGROUP_IO_RIOPS_MAX]   = CGROUP_LIMIT_MAX,
2830         [CGROUP_IO_WIOPS_MAX]   = CGROUP_LIMIT_MAX,
2831 };
2832
2833 static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2834         [CGROUP_IO_RBPS_MAX]    = "IOReadBandwidthMax",
2835         [CGROUP_IO_WBPS_MAX]    = "IOWriteBandwidthMax",
2836         [CGROUP_IO_RIOPS_MAX]   = "IOReadIOPSMax",
2837         [CGROUP_IO_WIOPS_MAX]   = "IOWriteIOPSMax",
2838 };
2839
2840 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2841
2842 int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2843         uint64_t u;
2844         int r;
2845
2846         if (isempty(s)) {
2847                 *ret = CGROUP_CPU_SHARES_INVALID;
2848                 return 0;
2849         }
2850
2851         r = safe_atou64(s, &u);
2852         if (r < 0)
2853                 return r;
2854
2855         if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2856                 return -ERANGE;
2857
2858         *ret = u;
2859         return 0;
2860 }
2861
2862 int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2863         uint64_t u;
2864         int r;
2865
2866         if (isempty(s)) {
2867                 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2868                 return 0;
2869         }
2870
2871         r = safe_atou64(s, &u);
2872         if (r < 0)
2873                 return r;
2874
2875         if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2876                 return -ERANGE;
2877
2878         *ret = u;
2879         return 0;
2880 }
2881 #endif // 0
2882
2883 bool is_cgroup_fs(const struct statfs *s) {
2884         return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2885                is_fs_type(s, CGROUP2_SUPER_MAGIC);
2886 }
2887
2888 bool fd_is_cgroup_fs(int fd) {
2889         struct statfs s;
2890
2891         if (fstatfs(fd, &s) < 0)
2892                 return -errno;
2893
2894         return is_cgroup_fs(&s);
2895 }
2896
2897 static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2898         [CGROUP_CONTROLLER_CPU] = "cpu",
2899         [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2900         [CGROUP_CONTROLLER_IO] = "io",
2901         [CGROUP_CONTROLLER_BLKIO] = "blkio",
2902         [CGROUP_CONTROLLER_MEMORY] = "memory",
2903         [CGROUP_CONTROLLER_DEVICES] = "devices",
2904         [CGROUP_CONTROLLER_PIDS] = "pids",
2905 };
2906
2907 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);