Bug Summary

File:build-scan/../src/basic/cgroup-util.c
Warning:line 492, column 21
3rd function call argument is an uninitialized value

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)
;
17
Taking false branch
18
Loop condition is false. Exiting loop
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)
;
19
Taking false branch
20
Loop condition is false. Exiting loop
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
{
21
Loop condition is true. Entering loop body
22
Assuming 'de' is null
23
Taking true branch
24
Assuming the condition is true
25
Taking true branch
26
Returning without writing to '*fn'
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;
4
'fn' declared without an initial value
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)
;
5
Taking false branch
6
Loop condition is false. Exiting loop
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)
;
7
Assuming 'pfrom' is non-null
8
Taking false branch
9
Loop condition is false. Exiting loop
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)
;
10
Taking false branch
11
Loop condition is false. Exiting loop
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)
;
12
Assuming 'pto' is non-null
13
Taking false branch
14
Loop condition is false. Exiting loop
478
479 ret = cg_migrate(cfrom, pfrom, cto, pto, flags);
480
481 r = cg_enumerate_subgroups(cfrom, pfrom, &d);
482 if (r
14.1
'r' is >= 0
< 0) {
15
Taking false branch
483 if (ret >= 0 && r != -ENOENT2)
484 return r;
485
486 return ret;
487 }
488
489 while ((r = cg_read_subgroup(d, &fn)) > 0) {
16
Calling 'cg_read_subgroup'
27
Returning from 'cg_read_subgroup'
28
Assuming the condition is true
29
Loop condition is true. Entering loop body
490 _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0);
491
492 p = strjoin(pfrom, "/", fn)strjoin_real((pfrom), "/", fn, ((void*)0));
30
3rd function call argument is an uninitialized value
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)
;
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)
;
1414
1415 r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER"_systemd", pid, &raw);
1416 if (r < 0)
1417 return r;
1418
1419 r = cg_shift_path(raw, root, &c);
1420 if (r < 0)
1421 return r;
1422
1423 if (c == raw)
1424 *cgroup = TAKE_PTR(raw)({ typeof(raw) _ptr_ = (raw); (raw) = ((void*)0); _ptr_; });
1425 else {
1426 char *n;
1427
1428 n = strdup(c);
1429 if (!n)
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);
1727 if (r < 0)
1728 return r;
1729
1730 return cg_path_get_session(cgroup, session);
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)) {
1
Assuming the condition is true
2
Taking true branch
2219 r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER"_systemd", from, SYSTEMD_CGROUP_CONTROLLER"_systemd", to, CGROUP_REMOVE);
3
Calling 'cg_migrate_recursive'
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); }
;