Bug Summary

File:build-scan/../src/basic/cgroup-util.c
Warning:line 1730, column 9
Potential leak of memory pointed to by 'cgroup'

Annotated Source Code

Press '?' to see keyboard shortcuts

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