chiark / gitweb /
Prep v238: Fix cg_path_decode_unit() to understand elogind session naming.
[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 #if 0 /// elogind has a different naming: <controller>:/<session id>. So prefix is always len < 3
1480         n = strcspn(cgroup, "/");
1481         if (n < 3)
1482                 return -ENXIO;
1483 #else
1484         n = strspn(cgroup, "/") + 1;
1485 #endif // 0
1486
1487         c = strndupa(cgroup, n);
1488         c = cg_unescape(c);
1489
1490 #if 0 /// elogind session ids are never valid unit names.
1491         if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
1492                 return -ENXIO;
1493 #endif // 0
1494
1495         s = strdup(c);
1496         if (!s)
1497                 return -ENOMEM;
1498
1499         *unit = s;
1500         return 0;
1501 }
1502
1503 static bool valid_slice_name(const char *p, size_t n) {
1504
1505         if (!p)
1506                 return false;
1507
1508         if (n < STRLEN("x.slice"))
1509                 return false;
1510
1511         if (memcmp(p + n - 6, ".slice", 6) == 0) {
1512                 char buf[n+1], *c;
1513
1514                 memcpy(buf, p, n);
1515                 buf[n] = 0;
1516
1517                 c = cg_unescape(buf);
1518
1519                 return unit_name_is_valid(c, UNIT_NAME_PLAIN);
1520         }
1521
1522         return false;
1523 }
1524
1525 static const char *skip_slices(const char *p) {
1526         assert(p);
1527
1528         /* Skips over all slice assignments */
1529
1530         for (;;) {
1531                 size_t n;
1532
1533                 p += strspn(p, "/");
1534
1535                 n = strcspn(p, "/");
1536                 if (!valid_slice_name(p, n))
1537                         return p;
1538
1539                 p += n;
1540         }
1541 }
1542
1543 int cg_path_get_unit(const char *path, char **ret) {
1544         const char *e;
1545         char *unit;
1546         int r;
1547
1548         assert(path);
1549         assert(ret);
1550
1551         e = skip_slices(path);
1552
1553         r = cg_path_decode_unit(e, &unit);
1554         if (r < 0)
1555                 return r;
1556
1557         /* We skipped over the slices, don't accept any now */
1558         if (endswith(unit, ".slice")) {
1559                 free(unit);
1560                 return -ENXIO;
1561         }
1562
1563         *ret = unit;
1564         return 0;
1565 }
1566
1567 int cg_pid_get_unit(pid_t pid, char **unit) {
1568         _cleanup_free_ char *cgroup = NULL;
1569         int r;
1570
1571         assert(unit);
1572
1573         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1574         if (r < 0)
1575                 return r;
1576
1577         return cg_path_get_unit(cgroup, unit);
1578 }
1579
1580 #if 0 /// UNNEEDED by elogind
1581 /**
1582  * Skip session-*.scope, but require it to be there.
1583  */
1584 static const char *skip_session(const char *p) {
1585         size_t n;
1586
1587         if (isempty(p))
1588                 return NULL;
1589
1590         p += strspn(p, "/");
1591
1592         n = strcspn(p, "/");
1593         if (n < STRLEN("session-x.scope"))
1594                 return NULL;
1595
1596         if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) {
1597                 char buf[n - 8 - 6 + 1];
1598
1599                 memcpy(buf, p + 8, n - 8 - 6);
1600                 buf[n - 8 - 6] = 0;
1601
1602                 /* Note that session scopes never need unescaping,
1603                  * since they cannot conflict with the kernel's own
1604                  * names, hence we don't need to call cg_unescape()
1605                  * here. */
1606
1607                 if (!session_id_valid(buf))
1608                         return false;
1609
1610                 p += n;
1611                 p += strspn(p, "/");
1612                 return p;
1613         }
1614
1615         return NULL;
1616 }
1617
1618 /**
1619  * Skip user@*.service, but require it to be there.
1620  */
1621 static const char *skip_user_manager(const char *p) {
1622         size_t n;
1623
1624         if (isempty(p))
1625                 return NULL;
1626
1627         p += strspn(p, "/");
1628
1629         n = strcspn(p, "/");
1630         if (n < STRLEN("user@x.service"))
1631                 return NULL;
1632
1633         if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) {
1634                 char buf[n - 5 - 8 + 1];
1635
1636                 memcpy(buf, p + 5, n - 5 - 8);
1637                 buf[n - 5 - 8] = 0;
1638
1639                 /* Note that user manager services never need unescaping,
1640                  * since they cannot conflict with the kernel's own
1641                  * names, hence we don't need to call cg_unescape()
1642                  * here. */
1643
1644                 if (parse_uid(buf, NULL) < 0)
1645                         return NULL;
1646
1647                 p += n;
1648                 p += strspn(p, "/");
1649
1650                 return p;
1651         }
1652
1653         return NULL;
1654 }
1655
1656 static const char *skip_user_prefix(const char *path) {
1657         const char *e, *t;
1658
1659         assert(path);
1660
1661         /* Skip slices, if there are any */
1662         e = skip_slices(path);
1663
1664         /* Skip the user manager, if it's in the path now... */
1665         t = skip_user_manager(e);
1666         if (t)
1667                 return t;
1668
1669         /* Alternatively skip the user session if it is in the path... */
1670         return skip_session(e);
1671 }
1672
1673 int cg_path_get_user_unit(const char *path, char **ret) {
1674         const char *t;
1675
1676         assert(path);
1677         assert(ret);
1678
1679         t = skip_user_prefix(path);
1680         if (!t)
1681                 return -ENXIO;
1682
1683         /* And from here on it looks pretty much the same as for a
1684          * system unit, hence let's use the same parser from here
1685          * on. */
1686         return cg_path_get_unit(t, ret);
1687 }
1688
1689 int cg_pid_get_user_unit(pid_t pid, char **unit) {
1690         _cleanup_free_ char *cgroup = NULL;
1691         int r;
1692
1693         assert(unit);
1694
1695         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1696         if (r < 0)
1697                 return r;
1698
1699         return cg_path_get_user_unit(cgroup, unit);
1700 }
1701
1702 int cg_path_get_machine_name(const char *path, char **machine) {
1703         _cleanup_free_ char *u = NULL;
1704         const char *sl;
1705         int r;
1706
1707         r = cg_path_get_unit(path, &u);
1708         if (r < 0)
1709                 return r;
1710
1711         sl = strjoina("/run/systemd/machines/unit:", u);
1712         return readlink_malloc(sl, machine);
1713 }
1714
1715 int cg_pid_get_machine_name(pid_t pid, char **machine) {
1716         _cleanup_free_ char *cgroup = NULL;
1717         int r;
1718
1719         assert(machine);
1720
1721         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1722         if (r < 0)
1723                 return r;
1724
1725         return cg_path_get_machine_name(cgroup, machine);
1726 }
1727 #endif // 0
1728
1729 int cg_path_get_session(const char *path, char **session) {
1730 #if 0 /// UNNEEDED by elogind
1731         _cleanup_free_ char *unit = NULL;
1732         char *start, *end;
1733         int r;
1734
1735         assert(path);
1736
1737         r = cg_path_get_unit(path, &unit);
1738         if (r < 0)
1739                 return r;
1740
1741         start = startswith(unit, "session-");
1742         if (!start)
1743                 return -ENXIO;
1744         end = endswith(start, ".scope");
1745         if (!end)
1746                 return -ENXIO;
1747
1748         *end = 0;
1749         if (!session_id_valid(start))
1750                 return -ENXIO;
1751 #else
1752         /* Elogind uses a flat hierarchy, just "/SESSION".  The only
1753            wrinkle is that SESSION might be escaped.  */
1754         const char *e, *n, *start;
1755
1756         assert(path);
1757         log_debug_elogind("path is \"%s\"", path);
1758         assert(path[0] == '/');
1759
1760         e = path + 1;
1761         n = strchrnul(e, '/');
1762         if (e == n)
1763                 return -ENOENT;
1764
1765         start = strndupa(e, n - e);
1766         start = cg_unescape(start);
1767
1768         if (!start[0])
1769                 return -ENOENT;
1770 #endif // 0
1771
1772         if (session) {
1773                 char *rr;
1774
1775                 log_debug_elogind("found session: \"%s\"", start);
1776                 rr = strdup(start);
1777                 if (!rr)
1778                         return -ENOMEM;
1779
1780                 *session = rr;
1781         }
1782
1783         return 0;
1784 }
1785
1786 int cg_pid_get_session(pid_t pid, char **session) {
1787         _cleanup_free_ char *cgroup = NULL;
1788         int r;
1789
1790         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1791         if (r < 0)
1792                 return r;
1793
1794         return cg_path_get_session(cgroup, session);
1795 }
1796
1797 int cg_path_get_owner_uid(const char *path, uid_t *uid) {
1798 #if 0 /// elogind needs one more value
1799         _cleanup_free_ char *slice = NULL;
1800         char *start, *end;
1801 #else
1802         _cleanup_free_ char *slice = NULL, *p = NULL, *s = NULL;
1803 #endif // 0
1804         int r;
1805
1806         assert(path);
1807
1808         r = cg_path_get_slice(path, &slice);
1809         if (r < 0)
1810                 return r;
1811
1812 #if 0 /// elogind does not support systemd slices
1813         start = startswith(slice, "user-");
1814         if (!start)
1815                 return -ENXIO;
1816         end = endswith(start, ".slice");
1817         if (!end)
1818                 return -ENXIO;
1819
1820         *end = 0;
1821         if (parse_uid(start, uid) < 0)
1822                 return -ENXIO;
1823 #else
1824         p = strappend("/run/systemd/sessions/", slice);
1825
1826         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
1827         if (r == -ENOENT)
1828                 return -ENXIO;
1829         if (r < 0)
1830                 return r;
1831         if (isempty(s))
1832                 return -EIO;
1833
1834         if (parse_uid(s, uid) < 0)
1835                 return -ENXIO;
1836 #endif // 0
1837
1838         return 0;
1839 }
1840
1841 int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) {
1842         _cleanup_free_ char *cgroup = NULL;
1843         int r;
1844
1845         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1846         if (r < 0)
1847                 return r;
1848
1849         return cg_path_get_owner_uid(cgroup, uid);
1850 }
1851
1852 int cg_path_get_slice(const char *p, char **slice) {
1853         const char *e = NULL;
1854
1855         assert(p);
1856         assert(slice);
1857
1858 #if 0 /// elogind does not support systemd slices
1859         /* Finds the right-most slice unit from the beginning, but
1860          * stops before we come to the first non-slice unit. */
1861
1862         for (;;) {
1863                 size_t n;
1864
1865                 p += strspn(p, "/");
1866
1867                 n = strcspn(p, "/");
1868                 if (!valid_slice_name(p, n)) {
1869
1870                         if (!e) {
1871                                 char *s;
1872
1873                                 s = strdup(SPECIAL_ROOT_SLICE);
1874                                 if (!s)
1875                                         return -ENOMEM;
1876
1877                                 *slice = s;
1878                                 return 0;
1879                         }
1880
1881                         return cg_path_decode_unit(e, slice);
1882                 }
1883
1884                 e = p;
1885                 p += n;
1886         }
1887 #else
1888         /* In elogind, what is reported here, is the location of
1889          * the session. This is derived from /proc/<self|PID>/cgroup.
1890          * In there we look at the controller, which will look something
1891          * like "1:name=openrc:/3".
1892          * The last part gets extracted (and is now p), which is "/3" in
1893          * this case. The three is the session id, and that can be mapped.
1894          */
1895         e = startswith(p, "/");
1896
1897         if (e)
1898                 *slice = strdup(e);
1899         else
1900                 *slice = strdup(p);
1901
1902         return 0;
1903 #endif // 0
1904 }
1905
1906 int cg_pid_get_slice(pid_t pid, char **slice) {
1907         _cleanup_free_ char *cgroup = NULL;
1908         int r;
1909
1910         assert(slice);
1911
1912         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1913         log_debug_elogind("Found cgroup %s for pid %u (result %d)",
1914                           cgroup, pid, r);
1915         if (r < 0)
1916                 return r;
1917
1918         return cg_path_get_slice(cgroup, slice);
1919 }
1920
1921 int cg_path_get_user_slice(const char *p, char **slice) {
1922 #if 0 /// UNNEEDED by elogind
1923         const char *t;
1924 #endif // 0
1925         assert(p);
1926         assert(slice);
1927
1928 #if 0 /// nothing to skip in elogind
1929         t = skip_user_prefix(p);
1930         if (!t)
1931                 return -ENXIO;
1932 #endif // 0
1933
1934 #if 0 /// UNNEEDED by elogind
1935         /* And now it looks pretty much the same as for a system
1936          * slice, so let's just use the same parser from here on. */
1937         return cg_path_get_slice(t, slice);
1938 #else
1939         /* In elogind there is nothing to skip, we can use the path
1940          * directly. Generally speaking this is always a session id
1941          * to user mapping. */
1942         return cg_path_get_slice(p, slice);
1943 #endif // 0
1944 }
1945
1946 int cg_pid_get_user_slice(pid_t pid, char **slice) {
1947         _cleanup_free_ char *cgroup = NULL;
1948         int r;
1949
1950         assert(slice);
1951
1952         r = cg_pid_get_path_shifted(pid, NULL, &cgroup);
1953         if (r < 0)
1954                 return r;
1955
1956         return cg_path_get_user_slice(cgroup, slice);
1957 }
1958
1959 char *cg_escape(const char *p) {
1960         bool need_prefix = false;
1961
1962         /* This implements very minimal escaping for names to be used
1963          * as file names in the cgroup tree: any name which might
1964          * conflict with a kernel name or is prefixed with '_' is
1965          * prefixed with a '_'. That way, when reading cgroup names it
1966          * is sufficient to remove a single prefixing underscore if
1967          * there is one. */
1968
1969         /* The return value of this function (unlike cg_unescape())
1970          * needs free()! */
1971
1972         if (IN_SET(p[0], 0, '_', '.') ||
1973             streq(p, "notify_on_release") ||
1974             streq(p, "release_agent") ||
1975             streq(p, "tasks") ||
1976             startswith(p, "cgroup."))
1977                 need_prefix = true;
1978         else {
1979                 const char *dot;
1980
1981                 dot = strrchr(p, '.');
1982                 if (dot) {
1983                         CGroupController c;
1984                         size_t l = dot - p;
1985
1986                         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
1987                                 const char *n;
1988
1989                                 n = cgroup_controller_to_string(c);
1990
1991                                 if (l != strlen(n))
1992                                         continue;
1993
1994                                 if (memcmp(p, n, l) != 0)
1995                                         continue;
1996
1997                                 need_prefix = true;
1998                                 break;
1999                         }
2000                 }
2001         }
2002
2003         if (need_prefix)
2004                 return strappend("_", p);
2005
2006         return strdup(p);
2007 }
2008
2009 char *cg_unescape(const char *p) {
2010         assert(p);
2011
2012         /* The return value of this function (unlike cg_escape())
2013          * doesn't need free()! */
2014
2015         if (p[0] == '_')
2016                 return (char*) p+1;
2017
2018         return (char*) p;
2019 }
2020
2021 #define CONTROLLER_VALID                        \
2022         DIGITS LETTERS                          \
2023         "_"
2024
2025 bool cg_controller_is_valid(const char *p) {
2026         const char *t, *s;
2027
2028         if (!p)
2029                 return false;
2030
2031         if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
2032                 return true;
2033
2034         s = startswith(p, "name=");
2035         if (s)
2036                 p = s;
2037
2038         if (IN_SET(*p, 0, '_'))
2039                 return false;
2040
2041         for (t = p; *t; t++)
2042                 if (!strchr(CONTROLLER_VALID, *t))
2043                         return false;
2044
2045         if (t - p > FILENAME_MAX)
2046                 return false;
2047
2048         return true;
2049 }
2050
2051 #if 0 /// UNNEEDED by elogind
2052 int cg_slice_to_path(const char *unit, char **ret) {
2053         _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL;
2054         const char *dash;
2055         int r;
2056
2057         assert(unit);
2058         assert(ret);
2059
2060         if (streq(unit, SPECIAL_ROOT_SLICE)) {
2061                 char *x;
2062
2063                 x = strdup("");
2064                 if (!x)
2065                         return -ENOMEM;
2066                 *ret = x;
2067                 return 0;
2068         }
2069
2070         if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN))
2071                 return -EINVAL;
2072
2073         if (!endswith(unit, ".slice"))
2074                 return -EINVAL;
2075
2076         r = unit_name_to_prefix(unit, &p);
2077         if (r < 0)
2078                 return r;
2079
2080         dash = strchr(p, '-');
2081
2082         /* Don't allow initial dashes */
2083         if (dash == p)
2084                 return -EINVAL;
2085
2086         while (dash) {
2087                 _cleanup_free_ char *escaped = NULL;
2088                 char n[dash - p + sizeof(".slice")];
2089
2090                 /* Don't allow trailing or double dashes */
2091                 if (IN_SET(dash[1], 0, '-'))
2092                         return -EINVAL;
2093
2094                 strcpy(stpncpy(n, p, dash - p), ".slice");
2095                 if (!unit_name_is_valid(n, UNIT_NAME_PLAIN))
2096                         return -EINVAL;
2097
2098                 escaped = cg_escape(n);
2099                 if (!escaped)
2100                         return -ENOMEM;
2101
2102                 if (!strextend(&s, escaped, "/", NULL))
2103                         return -ENOMEM;
2104
2105                 dash = strchr(dash+1, '-');
2106         }
2107
2108         e = cg_escape(unit);
2109         if (!e)
2110                 return -ENOMEM;
2111
2112         if (!strextend(&s, e, NULL))
2113                 return -ENOMEM;
2114
2115         *ret = s;
2116         s = NULL;
2117
2118         return 0;
2119 }
2120 #endif // 0
2121
2122 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) {
2123         _cleanup_free_ char *p = NULL;
2124         int r;
2125
2126         r = cg_get_path(controller, path, attribute, &p);
2127         if (r < 0)
2128                 return r;
2129
2130         return write_string_file(p, value, 0);
2131 }
2132
2133 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) {
2134         _cleanup_free_ char *p = NULL;
2135         int r;
2136
2137         r = cg_get_path(controller, path, attribute, &p);
2138         if (r < 0)
2139                 return r;
2140
2141         return read_one_line_file(p, ret);
2142 }
2143
2144 #if 0 /// UNNEEDED by elogind
2145 int cg_get_keyed_attribute(
2146                 const char *controller,
2147                 const char *path,
2148                 const char *attribute,
2149                 char **keys,
2150                 char **ret_values) {
2151
2152         _cleanup_free_ char *filename = NULL, *contents = NULL;
2153         _cleanup_fclose_ FILE *f = NULL;
2154         const char *p;
2155         size_t n, i, n_done = 0;
2156         char **v;
2157         int r;
2158
2159         /* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
2160          * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
2161          * entries as 'keys'. On success each entry will be set to the value of the matching key.
2162          *
2163          * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
2164
2165         r = cg_get_path(controller, path, attribute, &filename);
2166         if (r < 0)
2167                 return r;
2168
2169         r = read_full_file(filename, &contents, NULL);
2170         if (r < 0)
2171                 return r;
2172
2173         n = strv_length(keys);
2174         if (n == 0) /* No keys to retrieve? That's easy, we are done then */
2175                 return 0;
2176
2177         /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
2178         v = newa0(char*, n);
2179
2180         for (p = contents; *p;) {
2181                 const char *w = NULL;
2182
2183                 for (i = 0; i < n; i++)
2184                         if (!v[i]) {
2185                                 w = first_word(p, keys[i]);
2186                                 if (w)
2187                                         break;
2188                         }
2189
2190                 if (w) {
2191                         size_t l;
2192
2193                         l = strcspn(w, NEWLINE);
2194                         v[i] = strndup(w, l);
2195                         if (!v[i]) {
2196                                 r = -ENOMEM;
2197                                 goto fail;
2198                         }
2199
2200                         n_done++;
2201                         if (n_done >= n)
2202                                 goto done;
2203
2204                         p = w + l;
2205                 } else
2206                         p += strcspn(p, NEWLINE);
2207
2208                 p += strspn(p, NEWLINE);
2209         }
2210
2211         r = -ENXIO;
2212
2213 fail:
2214         for (i = 0; i < n; i++)
2215                 free(v[i]);
2216
2217         return r;
2218
2219 done:
2220         memcpy(ret_values, v, sizeof(char*) * n);
2221         return 0;
2222
2223 }
2224
2225 int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2226         CGroupController c;
2227         int r;
2228
2229         /* This one will create a cgroup in our private tree, but also
2230          * duplicate it in the trees specified in mask, and remove it
2231          * in all others */
2232
2233         /* First create the cgroup in our own hierarchy. */
2234         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2235         if (r < 0)
2236                 return r;
2237
2238         /* If we are in the unified hierarchy, we are done now */
2239         r = cg_all_unified();
2240         if (r < 0)
2241                 return r;
2242         if (r > 0)
2243                 return 0;
2244
2245         /* Otherwise, do the same in the other hierarchies */
2246         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2247                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2248                 const char *n;
2249
2250                 n = cgroup_controller_to_string(c);
2251
2252                 if (mask & bit)
2253                         (void) cg_create(n, path);
2254                 else if (supported & bit)
2255                         (void) cg_trim(n, path, true);
2256         }
2257
2258         return 0;
2259 }
2260
2261 int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2262         CGroupController c;
2263         int r;
2264
2265         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
2266         if (r < 0)
2267                 return r;
2268
2269         r = cg_all_unified();
2270         if (r < 0)
2271                 return r;
2272         if (r > 0)
2273                 return 0;
2274
2275         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2276                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2277                 const char *p = NULL;
2278
2279                 if (!(supported & bit))
2280                         continue;
2281
2282                 if (path_callback)
2283                         p = path_callback(bit, userdata);
2284
2285                 if (!p)
2286                         p = path;
2287
2288                 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
2289         }
2290
2291         return 0;
2292 }
2293
2294 int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
2295         Iterator i;
2296         void *pidp;
2297         int r = 0;
2298
2299         SET_FOREACH(pidp, pids, i) {
2300                 pid_t pid = PTR_TO_PID(pidp);
2301                 int q;
2302
2303                 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
2304                 if (q < 0 && r >= 0)
2305                         r = q;
2306         }
2307
2308         return r;
2309 }
2310
2311 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
2312         CGroupController c;
2313         int r = 0, q;
2314
2315         if (!path_equal(from, to))  {
2316                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
2317                 if (r < 0)
2318                         return r;
2319         }
2320
2321         q = cg_all_unified();
2322         if (q < 0)
2323                 return q;
2324         if (q > 0)
2325                 return r;
2326
2327         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2328                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2329                 const char *p = NULL;
2330
2331                 if (!(supported & bit))
2332                         continue;
2333
2334                 if (to_callback)
2335                         p = to_callback(bit, userdata);
2336
2337                 if (!p)
2338                         p = to;
2339
2340                 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
2341         }
2342
2343         return 0;
2344 }
2345
2346 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2347         CGroupController c;
2348         int r, q;
2349
2350         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2351         if (r < 0)
2352                 return r;
2353
2354         q = cg_all_unified();
2355         if (q < 0)
2356                 return q;
2357         if (q > 0)
2358                 return r;
2359
2360         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2361                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2362
2363                 if (!(supported & bit))
2364                         continue;
2365
2366                 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
2367         }
2368
2369         return 0;
2370 }
2371 #endif // 0
2372
2373 int cg_mask_to_string(CGroupMask mask, char **ret) {
2374         _cleanup_free_ char *s = NULL;
2375         size_t n = 0, allocated = 0;
2376         bool space = false;
2377         CGroupController c;
2378
2379         assert(ret);
2380
2381         if (mask == 0) {
2382                 *ret = NULL;
2383                 return 0;
2384         }
2385
2386         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2387                 const char *k;
2388                 size_t l;
2389
2390                 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2391                         continue;
2392
2393                 k = cgroup_controller_to_string(c);
2394                 l = strlen(k);
2395
2396                 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2397                         return -ENOMEM;
2398
2399                 if (space)
2400                         s[n] = ' ';
2401                 memcpy(s + n + space, k, l);
2402                 n += space + l;
2403
2404                 space = true;
2405         }
2406
2407         assert(s);
2408
2409         s[n] = 0;
2410         *ret = s;
2411         s = NULL;
2412
2413         return 0;
2414 }
2415
2416 int cg_mask_from_string(const char *value, CGroupMask *mask) {
2417         assert(mask);
2418         assert(value);
2419
2420         for (;;) {
2421                 _cleanup_free_ char *n = NULL;
2422                 CGroupController v;
2423                 int r;
2424
2425                 r = extract_first_word(&value, &n, NULL, 0);
2426                 if (r < 0)
2427                         return r;
2428                 if (r == 0)
2429                         break;
2430
2431                 v = cgroup_controller_from_string(n);
2432                 if (v < 0)
2433                         continue;
2434
2435                 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2436         }
2437         return 0;
2438 }
2439
2440 int cg_mask_supported(CGroupMask *ret) {
2441         CGroupMask mask = 0;
2442         int r;
2443
2444         /* Determines the mask of supported cgroup controllers. Only
2445          * includes controllers we can make sense of and that are
2446          * actually accessible. */
2447
2448         r = cg_all_unified();
2449         if (r < 0)
2450                 return r;
2451         if (r > 0) {
2452                 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
2453
2454                 /* In the unified hierarchy we can read the supported
2455                  * and accessible controllers from a the top-level
2456                  * cgroup attribute */
2457
2458                 r = cg_get_root_path(&root);
2459                 if (r < 0)
2460                         return r;
2461
2462                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2463                 if (r < 0)
2464                         return r;
2465
2466                 r = read_one_line_file(path, &controllers);
2467                 if (r < 0)
2468                         return r;
2469
2470                 r = cg_mask_from_string(controllers, &mask);
2471                 if (r < 0)
2472                         return r;
2473
2474                 /* Currently, we support the cpu, memory, io and pids
2475                  * controller in the unified hierarchy, mask
2476                  * everything else off. */
2477                 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
2478
2479         } else {
2480                 CGroupController c;
2481
2482                 /* In the legacy hierarchy, we check whether which
2483                  * hierarchies are mounted. */
2484
2485                 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2486                         const char *n;
2487
2488                         n = cgroup_controller_to_string(c);
2489                         if (controller_is_accessible(n) >= 0)
2490                                 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2491                 }
2492         }
2493
2494         *ret = mask;
2495         return 0;
2496 }
2497
2498 #if 0 /// UNNEEDED by elogind
2499 int cg_kernel_controllers(Set **ret) {
2500         _cleanup_set_free_free_ Set *controllers = NULL;
2501         _cleanup_fclose_ FILE *f = NULL;
2502         int r;
2503
2504         assert(ret);
2505
2506         /* Determines the full list of kernel-known controllers. Might
2507          * include controllers we don't actually support, arbitrary
2508          * named hierarchies and controllers that aren't currently
2509          * accessible (because not mounted). */
2510
2511         controllers = set_new(&string_hash_ops);
2512         if (!controllers)
2513                 return -ENOMEM;
2514
2515         f = fopen("/proc/cgroups", "re");
2516         if (!f) {
2517                 if (errno == ENOENT) {
2518                         *ret = NULL;
2519                         return 0;
2520                 }
2521
2522                 return -errno;
2523         }
2524
2525         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2526
2527         /* Ignore the header line */
2528         (void) read_line(f, (size_t) -1, NULL);
2529
2530         for (;;) {
2531                 char *controller;
2532                 int enabled = 0;
2533
2534                 errno = 0;
2535                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2536
2537                         if (feof(f))
2538                                 break;
2539
2540                         if (ferror(f) && errno > 0)
2541                                 return -errno;
2542
2543                         return -EBADMSG;
2544                 }
2545
2546                 if (!enabled) {
2547                         free(controller);
2548                         continue;
2549                 }
2550
2551                 if (!cg_controller_is_valid(controller)) {
2552                         free(controller);
2553                         return -EBADMSG;
2554                 }
2555
2556                 r = set_consume(controllers, controller);
2557                 if (r < 0)
2558                         return r;
2559         }
2560
2561         *ret = controllers;
2562         controllers = NULL;
2563
2564         return 0;
2565 }
2566 #endif // 0
2567
2568 static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2569
2570 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd.  This
2571  * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2572  * /sys/fs/cgroup/systemd.  From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2573  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2574  *
2575  * To keep live upgrade working, we detect and support v232 layout.  When v232 layout is detected, to keep cgroup v2
2576  * process management but disable the compat dual layout, we return %true on
2577  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2578  */
2579 static thread_local bool unified_systemd_v232;
2580
2581 static int cg_unified_update(void) {
2582
2583         struct statfs fs;
2584
2585         /* Checks if we support the unified hierarchy. Returns an
2586          * error when the cgroup hierarchies aren't mounted yet or we
2587          * have any other trouble determining if the unified hierarchy
2588          * is supported. */
2589
2590         if (unified_cache >= CGROUP_UNIFIED_NONE)
2591                 return 0;
2592
2593         if (statfs("/sys/fs/cgroup/", &fs) < 0)
2594                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m");
2595
2596         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2597                 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2598                 unified_cache = CGROUP_UNIFIED_ALL;
2599 #if 0 /// The handling of cgroups is a bit different with elogind
2600         } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2601                         log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2602 #else
2603         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)
2604               || F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2605 #endif // 0
2606                 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
2607                     F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2608                         unified_cache = CGROUP_UNIFIED_SYSTEMD;
2609                         unified_systemd_v232 = false;
2610                 } else {
2611 #if 0 /// There is no sub-grouping within elogind
2612                         if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
2613                                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2614
2615                         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2616                                 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2617                                 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2618                                 unified_systemd_v232 = true;
2619                         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2620                                 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2621                                 unified_cache = CGROUP_UNIFIED_NONE;
2622                         } else {
2623                                 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2624                                           (unsigned long long) fs.f_type);
2625                                 unified_cache = CGROUP_UNIFIED_NONE;
2626                         }
2627 #else
2628                         unified_cache = CGROUP_UNIFIED_NONE;
2629 #endif // 0
2630                 }
2631         } else {
2632                 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2633                           (unsigned long long) fs.f_type);
2634                 return -ENOMEDIUM;
2635         }
2636
2637         return 0;
2638 }
2639
2640 int cg_unified_controller(const char *controller) {
2641         int r;
2642
2643         r = cg_unified_update();
2644         if (r < 0)
2645                 return r;
2646
2647         if (unified_cache == CGROUP_UNIFIED_NONE)
2648                 return false;
2649
2650         if (unified_cache >= CGROUP_UNIFIED_ALL)
2651                 return true;
2652
2653 #if 0 /// only if elogind is the controller we can use cgroups2 in hybrid mode
2654         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
2655 #else
2656         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID);
2657 #endif // 0
2658 }
2659
2660 int cg_all_unified(void) {
2661         int r;
2662
2663         r = cg_unified_update();
2664         if (r < 0)
2665                 return r;
2666
2667         return unified_cache >= CGROUP_UNIFIED_ALL;
2668 }
2669
2670 int cg_hybrid_unified(void) {
2671         int r;
2672
2673         r = cg_unified_update();
2674         if (r < 0)
2675                 return r;
2676
2677         return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2678 }
2679
2680 int cg_unified_flush(void) {
2681         unified_cache = CGROUP_UNIFIED_UNKNOWN;
2682
2683         return cg_unified_update();
2684 }
2685
2686 #if 0 /// UNNEEDED by elogind
2687 int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
2688         _cleanup_fclose_ FILE *f = NULL;
2689         _cleanup_free_ char *fs = NULL;
2690         CGroupController c;
2691         int r;
2692
2693         assert(p);
2694
2695         if (supported == 0)
2696                 return 0;
2697
2698         r = cg_all_unified();
2699         if (r < 0)
2700                 return r;
2701         if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
2702                 return 0;
2703
2704         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2705         if (r < 0)
2706                 return r;
2707
2708         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2709                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2710                 const char *n;
2711
2712                 if (!(supported & bit))
2713                         continue;
2714
2715                 n = cgroup_controller_to_string(c);
2716                 {
2717                         char s[1 + strlen(n) + 1];
2718
2719                         s[0] = mask & bit ? '+' : '-';
2720                         strcpy(s + 1, n);
2721
2722                         if (!f) {
2723                                 f = fopen(fs, "we");
2724                                 if (!f) {
2725                                         log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2726                                         break;
2727                                 }
2728                         }
2729
2730                         r = write_string_stream(f, s, 0);
2731                         if (r < 0)
2732                                 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
2733                 }
2734         }
2735
2736         return 0;
2737 }
2738 #endif // 0
2739
2740 bool cg_is_unified_wanted(void) {
2741         static thread_local int wanted = -1;
2742         int r;
2743         bool b;
2744         const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
2745
2746         /* If we have a cached value, return that. */
2747         if (wanted >= 0)
2748                 return wanted;
2749
2750         /* If the hierarchy is already mounted, then follow whatever
2751          * was chosen for it. */
2752         if (cg_unified_flush() >= 0)
2753                 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
2754
2755 #if 0 /// elogind is not init and has no business with kernel command line
2756         /* Otherwise, let's see what the kernel command line has to say.
2757          * Since checking is expensive, cache a non-error result. */
2758         r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
2759 #endif // 0
2760
2761         return (wanted = r > 0 ? b : is_default);
2762 }
2763
2764 bool cg_is_legacy_wanted(void) {
2765         static thread_local int wanted = -1;
2766
2767         /* If we have a cached value, return that. */
2768         if (wanted >= 0)
2769                 return wanted;
2770
2771         /* Check if we have cgroups2 already mounted. */
2772         if (cg_unified_flush() >= 0 &&
2773             unified_cache == CGROUP_UNIFIED_ALL)
2774                 return (wanted = false);
2775
2776         /* Otherwise, assume that at least partial legacy is wanted,
2777          * since cgroups2 should already be mounted at this point. */
2778         return (wanted = true);
2779 }
2780
2781 bool cg_is_hybrid_wanted(void) {
2782         static thread_local int wanted = -1;
2783         int r;
2784         bool b;
2785         const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2786         /* We default to true if the default is "hybrid", obviously,
2787          * but also when the default is "unified", because if we get
2788          * called, it means that unified hierarchy was not mounted. */
2789
2790         /* If we have a cached value, return that. */
2791         if (wanted >= 0)
2792                 return wanted;
2793
2794         /* If the hierarchy is already mounted, then follow whatever
2795          * was chosen for it. */
2796         if (cg_unified_flush() >= 0 &&
2797             unified_cache == CGROUP_UNIFIED_ALL)
2798                 return (wanted = false);
2799
2800 #if 0 /// elogind is not init and has no business with kernel command line
2801         /* Otherwise, let's see what the kernel command line has to say.
2802          * Since checking is expensive, cache a non-error result. */
2803         r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
2804 #endif // 0
2805
2806         /* The meaning of the kernel option is reversed wrt. to the return value
2807          * of this function, hence the negation. */
2808         return (wanted = r > 0 ? !b : is_default);
2809 }
2810
2811 #if 0 /// UNNEEDED by elogind
2812 int cg_weight_parse(const char *s, uint64_t *ret) {
2813         uint64_t u;
2814         int r;
2815
2816         if (isempty(s)) {
2817                 *ret = CGROUP_WEIGHT_INVALID;
2818                 return 0;
2819         }
2820
2821         r = safe_atou64(s, &u);
2822         if (r < 0)
2823                 return r;
2824
2825         if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2826                 return -ERANGE;
2827
2828         *ret = u;
2829         return 0;
2830 }
2831
2832 const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2833         [CGROUP_IO_RBPS_MAX]    = CGROUP_LIMIT_MAX,
2834         [CGROUP_IO_WBPS_MAX]    = CGROUP_LIMIT_MAX,
2835         [CGROUP_IO_RIOPS_MAX]   = CGROUP_LIMIT_MAX,
2836         [CGROUP_IO_WIOPS_MAX]   = CGROUP_LIMIT_MAX,
2837 };
2838
2839 static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2840         [CGROUP_IO_RBPS_MAX]    = "IOReadBandwidthMax",
2841         [CGROUP_IO_WBPS_MAX]    = "IOWriteBandwidthMax",
2842         [CGROUP_IO_RIOPS_MAX]   = "IOReadIOPSMax",
2843         [CGROUP_IO_WIOPS_MAX]   = "IOWriteIOPSMax",
2844 };
2845
2846 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2847
2848 int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2849         uint64_t u;
2850         int r;
2851
2852         if (isempty(s)) {
2853                 *ret = CGROUP_CPU_SHARES_INVALID;
2854                 return 0;
2855         }
2856
2857         r = safe_atou64(s, &u);
2858         if (r < 0)
2859                 return r;
2860
2861         if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2862                 return -ERANGE;
2863
2864         *ret = u;
2865         return 0;
2866 }
2867
2868 int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2869         uint64_t u;
2870         int r;
2871
2872         if (isempty(s)) {
2873                 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2874                 return 0;
2875         }
2876
2877         r = safe_atou64(s, &u);
2878         if (r < 0)
2879                 return r;
2880
2881         if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2882                 return -ERANGE;
2883
2884         *ret = u;
2885         return 0;
2886 }
2887 #endif // 0
2888
2889 bool is_cgroup_fs(const struct statfs *s) {
2890         return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2891                is_fs_type(s, CGROUP2_SUPER_MAGIC);
2892 }
2893
2894 bool fd_is_cgroup_fs(int fd) {
2895         struct statfs s;
2896
2897         if (fstatfs(fd, &s) < 0)
2898                 return -errno;
2899
2900         return is_cgroup_fs(&s);
2901 }
2902
2903 static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2904         [CGROUP_CONTROLLER_CPU] = "cpu",
2905         [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2906         [CGROUP_CONTROLLER_IO] = "io",
2907         [CGROUP_CONTROLLER_BLKIO] = "blkio",
2908         [CGROUP_CONTROLLER_MEMORY] = "memory",
2909         [CGROUP_CONTROLLER_DEVICES] = "devices",
2910         [CGROUP_CONTROLLER_PIDS] = "pids",
2911 };
2912
2913 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);