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