File: | build-scan/../src/basic/cgroup-util.c |
Warning: | line 1730, column 9 Potential leak of memory pointed to by 'cgroup' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | ||||
46 | int 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 | ||||
65 | int 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 | ||||
90 | int 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 | ||||
126 | bool_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 | ||||
140 | bool_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 | ||||
151 | int 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 | ||||
172 | int cg_read_subgroup(DIR *d, char **fn) { | |||
173 | struct dirent *de; | |||
174 | ||||
175 | assert(d)do { if ((__builtin_expect(!!(!(d)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("d"), "../src/basic/cgroup-util.c", 175, __PRETTY_FUNCTION__); } while (0); | |||
176 | assert(fn)do { if ((__builtin_expect(!!(!(fn)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("fn"), "../src/basic/cgroup-util.c", 176 , __PRETTY_FUNCTION__); } while (0); | |||
177 | ||||
178 | FOREACH_DIRENT_ALL(de, d, return -errno)for ((*__errno_location ()) = 0, de = readdir(d);; (*__errno_location ()) = 0, de = readdir(d)) if (!de) { if ((*__errno_location ( )) > 0) { return -(*__errno_location ()); } break; } else { | |||
179 | char *b; | |||
180 | ||||
181 | if (de->d_type != DT_DIRDT_DIR) | |||
182 | continue; | |||
183 | ||||
184 | if (dot_or_dot_dot(de->d_name)) | |||
185 | continue; | |||
186 | ||||
187 | b = strdup(de->d_name); | |||
188 | if (!b) | |||
189 | return -ENOMEM12; | |||
190 | ||||
191 | *fn = b; | |||
192 | return 1; | |||
193 | } | |||
194 | ||||
195 | return 0; | |||
196 | } | |||
197 | ||||
198 | int 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 | ||||
225 | int 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 | ||||
322 | int 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 | ||||
379 | int 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 | ||||
463 | int cg_migrate_recursive( | |||
464 | const char *cfrom, | |||
465 | const char *pfrom, | |||
466 | const char *cto, | |||
467 | const char *pto, | |||
468 | CGroupFlags flags) { | |||
469 | ||||
470 | _cleanup_closedir___attribute__((cleanup(closedirp))) DIR *d = NULL((void*)0); | |||
471 | int r, ret = 0; | |||
472 | char *fn; | |||
473 | ||||
474 | assert(cfrom)do { if ((__builtin_expect(!!(!(cfrom)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("cfrom"), "../src/basic/cgroup-util.c", 474 , __PRETTY_FUNCTION__); } while (0); | |||
475 | assert(pfrom)do { if ((__builtin_expect(!!(!(pfrom)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pfrom"), "../src/basic/cgroup-util.c", 475 , __PRETTY_FUNCTION__); } while (0); | |||
476 | assert(cto)do { if ((__builtin_expect(!!(!(cto)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("cto"), "../src/basic/cgroup-util.c", 476 , __PRETTY_FUNCTION__); } while (0); | |||
477 | assert(pto)do { if ((__builtin_expect(!!(!(pto)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("pto"), "../src/basic/cgroup-util.c", 477 , __PRETTY_FUNCTION__); } while (0); | |||
478 | ||||
479 | ret = cg_migrate(cfrom, pfrom, cto, pto, flags); | |||
480 | ||||
481 | r = cg_enumerate_subgroups(cfrom, pfrom, &d); | |||
482 | if (r < 0) { | |||
483 | if (ret >= 0 && r != -ENOENT2) | |||
484 | return r; | |||
485 | ||||
486 | return ret; | |||
487 | } | |||
488 | ||||
489 | while ((r = cg_read_subgroup(d, &fn)) > 0) { | |||
490 | _cleanup_free___attribute__((cleanup(freep))) char *p = NULL((void*)0); | |||
491 | ||||
492 | p = strjoin(pfrom, "/", fn)strjoin_real((pfrom), "/", fn, ((void*)0)); | |||
493 | free(fn); | |||
494 | if (!p) | |||
495 | return -ENOMEM12; | |||
496 | ||||
497 | r = cg_migrate_recursive(cfrom, p, cto, pto, flags); | |||
498 | if (r != 0 && ret >= 0) | |||
499 | ret = r; | |||
500 | } | |||
501 | ||||
502 | if (r < 0 && ret >= 0) | |||
503 | ret = r; | |||
504 | ||||
505 | if (flags & CGROUP_REMOVE) { | |||
506 | r = cg_rmdir(cfrom, pfrom); | |||
507 | if (r < 0 && ret >= 0 && !IN_SET(r, -ENOENT, -EBUSY)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){-2, -16})/sizeof(int)]; switch(r) { case -2: case -16: _found = 1; break; default: break; } _found; } )) | |||
508 | return r; | |||
509 | } | |||
510 | ||||
511 | return ret; | |||
512 | } | |||
513 | ||||
514 | int 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 | ||||
546 | static 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 | ||||
570 | static 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 | ||||
594 | static 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 | ||||
614 | int 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 | ||||
658 | static 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 | ||||
697 | int 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 | ||||
711 | static 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 | ||||
726 | int 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 | */ | |||
766 | int 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 | ||||
797 | int 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 | ||||
814 | int 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 | ||||
848 | int 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 | ||||
874 | int 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 | ||||
960 | int 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 | ||||
978 | int 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 | ||||
997 | int 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 | ||||
1086 | int 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 | ||||
1140 | int 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 | ||||
1171 | int 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 | ||||
1191 | int 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 | ||||
1247 | int 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 | ||||
1330 | int 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 | ||||
1357 | int 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 | ||||
1379 | int 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 | ||||
1407 | int 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
| |||
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 | ||||
1438 | int 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 | ||||
1463 | static 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 | ||||
1485 | static 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 | ||||
1503 | int 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 | ||||
1527 | int 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 | */ | |||
1543 | static 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 | */ | |||
1580 | static 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 | ||||
1615 | static 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 | ||||
1632 | int 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 | ||||
1648 | int 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 | ||||
1661 | int 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 | ||||
1674 | int 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 | ||||
1687 | int 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 | ||||
1722 | int 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
| |||
1728 | return r; | |||
1729 | ||||
1730 | return cg_path_get_session(cgroup, session); | |||
| ||||
1731 | } | |||
1732 | ||||
1733 | int 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 | ||||
1758 | int 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 | ||||
1769 | int 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 | ||||
1805 | int 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 | ||||
1818 | int 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 | ||||
1832 | int 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 | ||||
1845 | char *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 | ||||
1895 | char *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 | ||||
1911 | bool_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 | ||||
1937 | int 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 | ||||
2013 | int 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 | ||||
2024 | int 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 | ||||
2035 | int 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 | ||||
2107 | fail: | |||
2108 | for (i = 0; i < n; i++) | |||
2109 | free(v[i]); | |||
2110 | ||||
2111 | return r; | |||
2112 | ||||
2113 | done: | |||
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 | ||||
2122 | int 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 | ||||
2164 | int 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 | ||||
2197 | int 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 | ||||
2214 | int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { | |||
2215 | CGroupController c; | |||
2216 | int r = 0, q; | |||
2217 | ||||
2218 | if (!path_equal(from, to)) { | |||
2219 | r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER"_systemd", from, SYSTEMD_CGROUP_CONTROLLER"_systemd", to, CGROUP_REMOVE); | |||
2220 | if (r < 0) | |||
2221 | return r; | |||
2222 | } | |||
2223 | ||||
2224 | q = cg_all_unified(); | |||
2225 | if (q < 0) | |||
2226 | return q; | |||
2227 | if (q > 0) | |||
2228 | return r; | |||
2229 | ||||
2230 | for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { | |||
2231 | CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c)(1 << (c)); | |||
2232 | const char *p = NULL((void*)0); | |||
2233 | ||||
2234 | if (!(supported & bit)) | |||
2235 | continue; | |||
2236 | ||||
2237 | if (to_callback) | |||
2238 | p = to_callback(bit, userdata); | |||
2239 | ||||
2240 | if (!p) | |||
2241 | p = to; | |||
2242 | ||||
2243 | (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER"_systemd", to, cgroup_controller_to_string(c), p, 0); | |||
2244 | } | |||
2245 | ||||
2246 | return 0; | |||
2247 | } | |||
2248 | ||||
2249 | int 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 | ||||
2275 | int 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 | ||||
2317 | int 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 | ||||
2341 | int 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 | ||||
2402 | int 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 | ||||
2469 | static 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 | */ | |||
2480 | static thread_local__thread bool_Bool unified_systemd_v232; | |||
2481 | ||||
2482 | static 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 | ||||
2532 | int 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 | ||||
2548 | int 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 | ||||
2558 | int 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 | ||||
2568 | int cg_unified_flush(void) { | |||
2569 | unified_cache = CGROUP_UNIFIED_UNKNOWN; | |||
2570 | ||||
2571 | return cg_unified_update(); | |||
2572 | } | |||
2573 | ||||
2574 | int 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 | ||||
2628 | bool_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 | ||||
2650 | bool_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 | ||||
2667 | bool_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 | ||||
2695 | int 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 | ||||
2715 | const 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 | ||||
2722 | static 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 | ||||
2729 | DEFINE_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 | ||||
2731 | int 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 | ||||
2751 | int 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 | ||||
2771 | bool_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 | ||||
2776 | bool_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 | ||||
2785 | static 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 | ||||
2796 | DEFINE_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); }; |