chiark / gitweb /
basic/cgroup-util: remove unused variable
[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         const char *p;
2154         size_t n, i, n_done = 0;
2155         char **v;
2156         int r;
2157
2158         /* Reads one or more fields of a cgroupsv2 keyed attribute file. The 'keys' parameter should be an strv with
2159          * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
2160          * entries as 'keys'. On success each entry will be set to the value of the matching key.
2161          *
2162          * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
2163
2164         r = cg_get_path(controller, path, attribute, &filename);
2165         if (r < 0)
2166                 return r;
2167
2168         r = read_full_file(filename, &contents, NULL);
2169         if (r < 0)
2170                 return r;
2171
2172         n = strv_length(keys);
2173         if (n == 0) /* No keys to retrieve? That's easy, we are done then */
2174                 return 0;
2175
2176         /* Let's build this up in a temporary array for now in order not to clobber the return parameter on failure */
2177         v = newa0(char*, n);
2178
2179         for (p = contents; *p;) {
2180                 const char *w = NULL;
2181
2182                 for (i = 0; i < n; i++)
2183                         if (!v[i]) {
2184                                 w = first_word(p, keys[i]);
2185                                 if (w)
2186                                         break;
2187                         }
2188
2189                 if (w) {
2190                         size_t l;
2191
2192                         l = strcspn(w, NEWLINE);
2193                         v[i] = strndup(w, l);
2194                         if (!v[i]) {
2195                                 r = -ENOMEM;
2196                                 goto fail;
2197                         }
2198
2199                         n_done++;
2200                         if (n_done >= n)
2201                                 goto done;
2202
2203                         p = w + l;
2204                 } else
2205                         p += strcspn(p, NEWLINE);
2206
2207                 p += strspn(p, NEWLINE);
2208         }
2209
2210         r = -ENXIO;
2211
2212 fail:
2213         for (i = 0; i < n; i++)
2214                 free(v[i]);
2215
2216         return r;
2217
2218 done:
2219         memcpy(ret_values, v, sizeof(char*) * n);
2220         return 0;
2221
2222 }
2223
2224 int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
2225         CGroupController c;
2226         int r;
2227
2228         /* This one will create a cgroup in our private tree, but also
2229          * duplicate it in the trees specified in mask, and remove it
2230          * in all others */
2231
2232         /* First create the cgroup in our own hierarchy. */
2233         r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
2234         if (r < 0)
2235                 return r;
2236
2237         /* If we are in the unified hierarchy, we are done now */
2238         r = cg_all_unified();
2239         if (r < 0)
2240                 return r;
2241         if (r > 0)
2242                 return 0;
2243
2244         /* Otherwise, do the same in the other hierarchies */
2245         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2246                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2247                 const char *n;
2248
2249                 n = cgroup_controller_to_string(c);
2250
2251                 if (mask & bit)
2252                         (void) cg_create(n, path);
2253                 else if (supported & bit)
2254                         (void) cg_trim(n, path, true);
2255         }
2256
2257         return 0;
2258 }
2259
2260 int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
2261         CGroupController c;
2262         int r;
2263
2264         r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
2265         if (r < 0)
2266                 return r;
2267
2268         r = cg_all_unified();
2269         if (r < 0)
2270                 return r;
2271         if (r > 0)
2272                 return 0;
2273
2274         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2275                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2276                 const char *p = NULL;
2277
2278                 if (!(supported & bit))
2279                         continue;
2280
2281                 if (path_callback)
2282                         p = path_callback(bit, userdata);
2283
2284                 if (!p)
2285                         p = path;
2286
2287                 (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid);
2288         }
2289
2290         return 0;
2291 }
2292
2293 int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) {
2294         Iterator i;
2295         void *pidp;
2296         int r = 0;
2297
2298         SET_FOREACH(pidp, pids, i) {
2299                 pid_t pid = PTR_TO_PID(pidp);
2300                 int q;
2301
2302                 q = cg_attach_everywhere(supported, path, pid, path_callback, userdata);
2303                 if (q < 0 && r >= 0)
2304                         r = q;
2305         }
2306
2307         return r;
2308 }
2309
2310 int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
2311         CGroupController c;
2312         int r = 0, q;
2313
2314         if (!path_equal(from, to))  {
2315                 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
2316                 if (r < 0)
2317                         return r;
2318         }
2319
2320         q = cg_all_unified();
2321         if (q < 0)
2322                 return q;
2323         if (q > 0)
2324                 return r;
2325
2326         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2327                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2328                 const char *p = NULL;
2329
2330                 if (!(supported & bit))
2331                         continue;
2332
2333                 if (to_callback)
2334                         p = to_callback(bit, userdata);
2335
2336                 if (!p)
2337                         p = to;
2338
2339                 (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0);
2340         }
2341
2342         return 0;
2343 }
2344
2345 int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
2346         CGroupController c;
2347         int r, q;
2348
2349         r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
2350         if (r < 0)
2351                 return r;
2352
2353         q = cg_all_unified();
2354         if (q < 0)
2355                 return q;
2356         if (q > 0)
2357                 return r;
2358
2359         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2360                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2361
2362                 if (!(supported & bit))
2363                         continue;
2364
2365                 (void) cg_trim(cgroup_controller_to_string(c), path, delete_root);
2366         }
2367
2368         return 0;
2369 }
2370 #endif // 0
2371
2372 int cg_mask_to_string(CGroupMask mask, char **ret) {
2373         _cleanup_free_ char *s = NULL;
2374         size_t n = 0, allocated = 0;
2375         bool space = false;
2376         CGroupController c;
2377
2378         assert(ret);
2379
2380         if (mask == 0) {
2381                 *ret = NULL;
2382                 return 0;
2383         }
2384
2385         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2386                 const char *k;
2387                 size_t l;
2388
2389                 if (!(mask & CGROUP_CONTROLLER_TO_MASK(c)))
2390                         continue;
2391
2392                 k = cgroup_controller_to_string(c);
2393                 l = strlen(k);
2394
2395                 if (!GREEDY_REALLOC(s, allocated, n + space + l + 1))
2396                         return -ENOMEM;
2397
2398                 if (space)
2399                         s[n] = ' ';
2400                 memcpy(s + n + space, k, l);
2401                 n += space + l;
2402
2403                 space = true;
2404         }
2405
2406         assert(s);
2407
2408         s[n] = 0;
2409         *ret = s;
2410         s = NULL;
2411
2412         return 0;
2413 }
2414
2415 int cg_mask_from_string(const char *value, CGroupMask *mask) {
2416         assert(mask);
2417         assert(value);
2418
2419         for (;;) {
2420                 _cleanup_free_ char *n = NULL;
2421                 CGroupController v;
2422                 int r;
2423
2424                 r = extract_first_word(&value, &n, NULL, 0);
2425                 if (r < 0)
2426                         return r;
2427                 if (r == 0)
2428                         break;
2429
2430                 v = cgroup_controller_from_string(n);
2431                 if (v < 0)
2432                         continue;
2433
2434                 *mask |= CGROUP_CONTROLLER_TO_MASK(v);
2435         }
2436         return 0;
2437 }
2438
2439 int cg_mask_supported(CGroupMask *ret) {
2440         CGroupMask mask = 0;
2441         int r;
2442
2443         /* Determines the mask of supported cgroup controllers. Only
2444          * includes controllers we can make sense of and that are
2445          * actually accessible. */
2446
2447         r = cg_all_unified();
2448         if (r < 0)
2449                 return r;
2450         if (r > 0) {
2451                 _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
2452
2453                 /* In the unified hierarchy we can read the supported
2454                  * and accessible controllers from a the top-level
2455                  * cgroup attribute */
2456
2457                 r = cg_get_root_path(&root);
2458                 if (r < 0)
2459                         return r;
2460
2461                 r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path);
2462                 if (r < 0)
2463                         return r;
2464
2465                 r = read_one_line_file(path, &controllers);
2466                 if (r < 0)
2467                         return r;
2468
2469                 r = cg_mask_from_string(controllers, &mask);
2470                 if (r < 0)
2471                         return r;
2472
2473                 /* Currently, we support the cpu, memory, io and pids
2474                  * controller in the unified hierarchy, mask
2475                  * everything else off. */
2476                 mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
2477
2478         } else {
2479                 CGroupController c;
2480
2481                 /* In the legacy hierarchy, we check whether which
2482                  * hierarchies are mounted. */
2483
2484                 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2485                         const char *n;
2486
2487                         n = cgroup_controller_to_string(c);
2488                         if (controller_is_accessible(n) >= 0)
2489                                 mask |= CGROUP_CONTROLLER_TO_MASK(c);
2490                 }
2491         }
2492
2493         *ret = mask;
2494         return 0;
2495 }
2496
2497 #if 0 /// UNNEEDED by elogind
2498 int cg_kernel_controllers(Set **ret) {
2499         _cleanup_set_free_free_ Set *controllers = NULL;
2500         _cleanup_fclose_ FILE *f = NULL;
2501         int r;
2502
2503         assert(ret);
2504
2505         /* Determines the full list of kernel-known controllers. Might
2506          * include controllers we don't actually support, arbitrary
2507          * named hierarchies and controllers that aren't currently
2508          * accessible (because not mounted). */
2509
2510         controllers = set_new(&string_hash_ops);
2511         if (!controllers)
2512                 return -ENOMEM;
2513
2514         f = fopen("/proc/cgroups", "re");
2515         if (!f) {
2516                 if (errno == ENOENT) {
2517                         *ret = NULL;
2518                         return 0;
2519                 }
2520
2521                 return -errno;
2522         }
2523
2524         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
2525
2526         /* Ignore the header line */
2527         (void) read_line(f, (size_t) -1, NULL);
2528
2529         for (;;) {
2530                 char *controller;
2531                 int enabled = 0;
2532
2533                 errno = 0;
2534                 if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) {
2535
2536                         if (feof(f))
2537                                 break;
2538
2539                         if (ferror(f) && errno > 0)
2540                                 return -errno;
2541
2542                         return -EBADMSG;
2543                 }
2544
2545                 if (!enabled) {
2546                         free(controller);
2547                         continue;
2548                 }
2549
2550                 if (!cg_controller_is_valid(controller)) {
2551                         free(controller);
2552                         return -EBADMSG;
2553                 }
2554
2555                 r = set_consume(controllers, controller);
2556                 if (r < 0)
2557                         return r;
2558         }
2559
2560         *ret = controllers;
2561         controllers = NULL;
2562
2563         return 0;
2564 }
2565 #endif // 0
2566
2567 static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
2568
2569 /* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd.  This
2570  * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
2571  * /sys/fs/cgroup/systemd.  From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
2572  * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
2573  *
2574  * To keep live upgrade working, we detect and support v232 layout.  When v232 layout is detected, to keep cgroup v2
2575  * process management but disable the compat dual layout, we return %true on
2576  * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
2577  */
2578 static thread_local bool unified_systemd_v232;
2579
2580 static int cg_unified_update(void) {
2581
2582         struct statfs fs;
2583
2584         /* Checks if we support the unified hierarchy. Returns an
2585          * error when the cgroup hierarchies aren't mounted yet or we
2586          * have any other trouble determining if the unified hierarchy
2587          * is supported. */
2588
2589         if (unified_cache >= CGROUP_UNIFIED_NONE)
2590                 return 0;
2591
2592         if (statfs("/sys/fs/cgroup/", &fs) < 0)
2593                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/\" failed: %m");
2594
2595         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2596                 log_debug("Found cgroup2 on /sys/fs/cgroup/, full unified hierarchy");
2597                 unified_cache = CGROUP_UNIFIED_ALL;
2598 #if 0 /// The handling of cgroups is a bit different with elogind
2599         } else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2600                         log_debug("Found cgroup2 on /sys/fs/cgroup/unified, unified hierarchy for systemd controller");
2601 #else
2602         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)
2603               || F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
2604 #endif // 0
2605                 if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
2606                     F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2607                         unified_cache = CGROUP_UNIFIED_SYSTEMD;
2608                         unified_systemd_v232 = false;
2609                 } else {
2610 #if 0 /// There is no sub-grouping within elogind
2611                         if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
2612                                 return log_debug_errno(errno, "statfs(\"/sys/fs/cgroup/systemd\" failed: %m");
2613
2614                         if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
2615                                 log_debug("Found cgroup2 on /sys/fs/cgroup/systemd, unified hierarchy for systemd controller (v232 variant)");
2616                                 unified_cache = CGROUP_UNIFIED_SYSTEMD;
2617                                 unified_systemd_v232 = true;
2618                         } else if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) {
2619                                 log_debug("Found cgroup on /sys/fs/cgroup/systemd, legacy hierarchy");
2620                                 unified_cache = CGROUP_UNIFIED_NONE;
2621                         } else {
2622                                 log_debug("Unexpected filesystem type %llx mounted on /sys/fs/cgroup/systemd, assuming legacy hierarchy",
2623                                           (unsigned long long) fs.f_type);
2624                                 unified_cache = CGROUP_UNIFIED_NONE;
2625                         }
2626 #else
2627                         unified_cache = CGROUP_UNIFIED_NONE;
2628 #endif // 0
2629                 }
2630         } else {
2631                 log_debug("Unknown filesystem type %llx mounted on /sys/fs/cgroup.",
2632                           (unsigned long long) fs.f_type);
2633                 return -ENOMEDIUM;
2634         }
2635
2636         return 0;
2637 }
2638
2639 int cg_unified_controller(const char *controller) {
2640         int r;
2641
2642         r = cg_unified_update();
2643         if (r < 0)
2644                 return r;
2645
2646         if (unified_cache == CGROUP_UNIFIED_NONE)
2647                 return false;
2648
2649         if (unified_cache >= CGROUP_UNIFIED_ALL)
2650                 return true;
2651
2652 #if 0 /// only if elogind is the controller we can use cgroups2 in hybrid mode
2653         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
2654 #else
2655         return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID);
2656 #endif // 0
2657 }
2658
2659 int cg_all_unified(void) {
2660         int r;
2661
2662         r = cg_unified_update();
2663         if (r < 0)
2664                 return r;
2665
2666         return unified_cache >= CGROUP_UNIFIED_ALL;
2667 }
2668
2669 int cg_hybrid_unified(void) {
2670         int r;
2671
2672         r = cg_unified_update();
2673         if (r < 0)
2674                 return r;
2675
2676         return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
2677 }
2678
2679 int cg_unified_flush(void) {
2680         unified_cache = CGROUP_UNIFIED_UNKNOWN;
2681
2682         return cg_unified_update();
2683 }
2684
2685 #if 0 /// UNNEEDED by elogind
2686 int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
2687         _cleanup_fclose_ FILE *f = NULL;
2688         _cleanup_free_ char *fs = NULL;
2689         CGroupController c;
2690         int r;
2691
2692         assert(p);
2693
2694         if (supported == 0)
2695                 return 0;
2696
2697         r = cg_all_unified();
2698         if (r < 0)
2699                 return r;
2700         if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
2701                 return 0;
2702
2703         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
2704         if (r < 0)
2705                 return r;
2706
2707         for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
2708                 CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c);
2709                 const char *n;
2710
2711                 if (!(supported & bit))
2712                         continue;
2713
2714                 n = cgroup_controller_to_string(c);
2715                 {
2716                         char s[1 + strlen(n) + 1];
2717
2718                         s[0] = mask & bit ? '+' : '-';
2719                         strcpy(s + 1, n);
2720
2721                         if (!f) {
2722                                 f = fopen(fs, "we");
2723                                 if (!f) {
2724                                         log_debug_errno(errno, "Failed to open cgroup.subtree_control file of %s: %m", p);
2725                                         break;
2726                                 }
2727                         }
2728
2729                         r = write_string_stream(f, s, 0);
2730                         if (r < 0)
2731                                 log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs);
2732                 }
2733         }
2734
2735         return 0;
2736 }
2737 #endif // 0
2738
2739 bool cg_is_unified_wanted(void) {
2740         static thread_local int wanted = -1;
2741         int r;
2742         bool b;
2743         const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
2744
2745         /* If we have a cached value, return that. */
2746         if (wanted >= 0)
2747                 return wanted;
2748
2749         /* If the hierarchy is already mounted, then follow whatever
2750          * was chosen for it. */
2751         if (cg_unified_flush() >= 0)
2752                 return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
2753
2754 #if 0 /// elogind is not init and has no business with kernel command line
2755         /* Otherwise, let's see what the kernel command line has to say.
2756          * Since checking is expensive, cache a non-error result. */
2757         r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
2758 #endif // 0
2759
2760         return (wanted = r > 0 ? b : is_default);
2761 }
2762
2763 bool cg_is_legacy_wanted(void) {
2764         static thread_local int wanted = -1;
2765
2766         /* If we have a cached value, return that. */
2767         if (wanted >= 0)
2768                 return wanted;
2769
2770         /* Check if we have cgroups2 already mounted. */
2771         if (cg_unified_flush() >= 0 &&
2772             unified_cache == CGROUP_UNIFIED_ALL)
2773                 return (wanted = false);
2774
2775         /* Otherwise, assume that at least partial legacy is wanted,
2776          * since cgroups2 should already be mounted at this point. */
2777         return (wanted = true);
2778 }
2779
2780 bool cg_is_hybrid_wanted(void) {
2781         static thread_local int wanted = -1;
2782         int r;
2783         bool b;
2784         const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
2785         /* We default to true if the default is "hybrid", obviously,
2786          * but also when the default is "unified", because if we get
2787          * called, it means that unified hierarchy was not mounted. */
2788
2789         /* If we have a cached value, return that. */
2790         if (wanted >= 0)
2791                 return wanted;
2792
2793         /* If the hierarchy is already mounted, then follow whatever
2794          * was chosen for it. */
2795         if (cg_unified_flush() >= 0 &&
2796             unified_cache == CGROUP_UNIFIED_ALL)
2797                 return (wanted = false);
2798
2799 #if 0 /// elogind is not init and has no business with kernel command line
2800         /* Otherwise, let's see what the kernel command line has to say.
2801          * Since checking is expensive, cache a non-error result. */
2802         r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
2803 #endif // 0
2804
2805         /* The meaning of the kernel option is reversed wrt. to the return value
2806          * of this function, hence the negation. */
2807         return (wanted = r > 0 ? !b : is_default);
2808 }
2809
2810 #if 0 /// UNNEEDED by elogind
2811 int cg_weight_parse(const char *s, uint64_t *ret) {
2812         uint64_t u;
2813         int r;
2814
2815         if (isempty(s)) {
2816                 *ret = CGROUP_WEIGHT_INVALID;
2817                 return 0;
2818         }
2819
2820         r = safe_atou64(s, &u);
2821         if (r < 0)
2822                 return r;
2823
2824         if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX)
2825                 return -ERANGE;
2826
2827         *ret = u;
2828         return 0;
2829 }
2830
2831 const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2832         [CGROUP_IO_RBPS_MAX]    = CGROUP_LIMIT_MAX,
2833         [CGROUP_IO_WBPS_MAX]    = CGROUP_LIMIT_MAX,
2834         [CGROUP_IO_RIOPS_MAX]   = CGROUP_LIMIT_MAX,
2835         [CGROUP_IO_WIOPS_MAX]   = CGROUP_LIMIT_MAX,
2836 };
2837
2838 static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = {
2839         [CGROUP_IO_RBPS_MAX]    = "IOReadBandwidthMax",
2840         [CGROUP_IO_WBPS_MAX]    = "IOWriteBandwidthMax",
2841         [CGROUP_IO_RIOPS_MAX]   = "IOReadIOPSMax",
2842         [CGROUP_IO_WIOPS_MAX]   = "IOWriteIOPSMax",
2843 };
2844
2845 DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType);
2846
2847 int cg_cpu_shares_parse(const char *s, uint64_t *ret) {
2848         uint64_t u;
2849         int r;
2850
2851         if (isempty(s)) {
2852                 *ret = CGROUP_CPU_SHARES_INVALID;
2853                 return 0;
2854         }
2855
2856         r = safe_atou64(s, &u);
2857         if (r < 0)
2858                 return r;
2859
2860         if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX)
2861                 return -ERANGE;
2862
2863         *ret = u;
2864         return 0;
2865 }
2866
2867 int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
2868         uint64_t u;
2869         int r;
2870
2871         if (isempty(s)) {
2872                 *ret = CGROUP_BLKIO_WEIGHT_INVALID;
2873                 return 0;
2874         }
2875
2876         r = safe_atou64(s, &u);
2877         if (r < 0)
2878                 return r;
2879
2880         if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX)
2881                 return -ERANGE;
2882
2883         *ret = u;
2884         return 0;
2885 }
2886 #endif // 0
2887
2888 bool is_cgroup_fs(const struct statfs *s) {
2889         return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
2890                is_fs_type(s, CGROUP2_SUPER_MAGIC);
2891 }
2892
2893 bool fd_is_cgroup_fs(int fd) {
2894         struct statfs s;
2895
2896         if (fstatfs(fd, &s) < 0)
2897                 return -errno;
2898
2899         return is_cgroup_fs(&s);
2900 }
2901
2902 static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
2903         [CGROUP_CONTROLLER_CPU] = "cpu",
2904         [CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
2905         [CGROUP_CONTROLLER_IO] = "io",
2906         [CGROUP_CONTROLLER_BLKIO] = "blkio",
2907         [CGROUP_CONTROLLER_MEMORY] = "memory",
2908         [CGROUP_CONTROLLER_DEVICES] = "devices",
2909         [CGROUP_CONTROLLER_PIDS] = "pids",
2910 };
2911
2912 DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);