]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-cgroup-util.c
Merge pull request #7540 from fbuihuu/systemd-delta-tweaks
[thirdparty/systemd.git] / src / test / test-cgroup-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "alloc-util.h"
22 #include "build.h"
23 #include "cgroup-util.h"
24 #include "dirent-util.h"
25 #include "fd-util.h"
26 #include "format-util.h"
27 #include "parse-util.h"
28 #include "proc-cmdline.h"
29 #include "process-util.h"
30 #include "special.h"
31 #include "stat-util.h"
32 #include "string-util.h"
33 #include "test-helper.h"
34 #include "user-util.h"
35 #include "util.h"
36
37 static void check_p_d_u(const char *path, int code, const char *result) {
38 _cleanup_free_ char *unit = NULL;
39 int r;
40
41 r = cg_path_decode_unit(path, &unit);
42 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
43 assert_se(r == code);
44 assert_se(streq_ptr(unit, result));
45 }
46
47 static void test_path_decode_unit(void) {
48 check_p_d_u("getty@tty2.service", 0, "getty@tty2.service");
49 check_p_d_u("getty@tty2.service/", 0, "getty@tty2.service");
50 check_p_d_u("getty@tty2.service/xxx", 0, "getty@tty2.service");
51 check_p_d_u("getty@.service/", -ENXIO, NULL);
52 check_p_d_u("getty@.service", -ENXIO, NULL);
53 check_p_d_u("getty.service", 0, "getty.service");
54 check_p_d_u("getty", -ENXIO, NULL);
55 check_p_d_u("getty/waldo", -ENXIO, NULL);
56 check_p_d_u("_cpu.service", 0, "cpu.service");
57 }
58
59 static void check_p_g_u(const char *path, int code, const char *result) {
60 _cleanup_free_ char *unit = NULL;
61 int r;
62
63 r = cg_path_get_unit(path, &unit);
64 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
65 assert_se(r == code);
66 assert_se(streq_ptr(unit, result));
67 }
68
69 static void test_path_get_unit(void) {
70 check_p_g_u("/system.slice/foobar.service/sdfdsaf", 0, "foobar.service");
71 check_p_g_u("/system.slice/getty@tty5.service", 0, "getty@tty5.service");
72 check_p_g_u("/system.slice/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
73 check_p_g_u("/system.slice/getty@tty5.service/", 0, "getty@tty5.service");
74 check_p_g_u("/system.slice/getty@tty6.service/tty5", 0, "getty@tty6.service");
75 check_p_g_u("sadfdsafsda", -ENXIO, NULL);
76 check_p_g_u("/system.slice/getty####@tty6.service/xxx", -ENXIO, NULL);
77 check_p_g_u("/system.slice/system-waldo.slice/foobar.service/sdfdsaf", 0, "foobar.service");
78 check_p_g_u("/system.slice/system-waldo.slice/_cpu.service/sdfdsaf", 0, "cpu.service");
79 check_p_g_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "user@1000.service");
80 check_p_g_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
81 }
82
83 static void check_p_g_u_u(const char *path, int code, const char *result) {
84 _cleanup_free_ char *unit = NULL;
85 int r;
86
87 r = cg_path_get_user_unit(path, &unit);
88 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
89 assert_se(r == code);
90 assert_se(streq_ptr(unit, result));
91 }
92
93 static void test_path_get_user_unit(void) {
94 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "foobar.service");
95 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo.slice/foobar.service", 0, "foobar.service");
96 check_p_g_u_u("/user.slice/user-1002.slice/session-2.scope/foobar.service/waldo", 0, "foobar.service");
97 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar.service/waldo/uuuux", 0, "foobar.service");
98 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo/waldo/uuuux", -ENXIO, NULL);
99 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
100 check_p_g_u_u("/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
101 check_p_g_u_u("/xyz.slice/xyz-waldo.slice/session-77.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
102 check_p_g_u_u("/meh.service", -ENXIO, NULL);
103 check_p_g_u_u("/session-3.scope/_cpu.service", 0, "cpu.service");
104 check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "server.service");
105 check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/foobar.slice/foobar@pie.service", 0, "foobar@pie.service");
106 check_p_g_u_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
107 }
108
109 static void check_p_g_s(const char *path, int code, const char *result) {
110 _cleanup_free_ char *s = NULL;
111
112 assert_se(cg_path_get_session(path, &s) == code);
113 assert_se(streq_ptr(s, result));
114 }
115
116 static void test_path_get_session(void) {
117 check_p_g_s("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "2");
118 check_p_g_s("/session-3.scope", 0, "3");
119 check_p_g_s("/session-.scope", -ENXIO, NULL);
120 check_p_g_s("", -ENXIO, NULL);
121 }
122
123 static void check_p_g_o_u(const char *path, int code, uid_t result) {
124 uid_t uid = 0;
125
126 assert_se(cg_path_get_owner_uid(path, &uid) == code);
127 assert_se(uid == result);
128 }
129
130 static void test_path_get_owner_uid(void) {
131 check_p_g_o_u("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, 1000);
132 check_p_g_o_u("/user.slice/user-1006.slice", 0, 1006);
133 check_p_g_o_u("", -ENXIO, 0);
134 }
135
136 static void check_p_g_slice(const char *path, int code, const char *result) {
137 _cleanup_free_ char *s = NULL;
138
139 assert_se(cg_path_get_slice(path, &s) == code);
140 assert_se(streq_ptr(s, result));
141 }
142
143 static void test_path_get_slice(void) {
144 check_p_g_slice("/user.slice", 0, "user.slice");
145 check_p_g_slice("/foobar", 0, SPECIAL_ROOT_SLICE);
146 check_p_g_slice("/user.slice/user-waldo.slice", 0, "user-waldo.slice");
147 check_p_g_slice("", 0, SPECIAL_ROOT_SLICE);
148 check_p_g_slice("foobar", 0, SPECIAL_ROOT_SLICE);
149 check_p_g_slice("foobar.slice", 0, "foobar.slice");
150 check_p_g_slice("foo.slice/foo-bar.slice/waldo.service", 0, "foo-bar.slice");
151 }
152
153 static void check_p_g_u_slice(const char *path, int code, const char *result) {
154 _cleanup_free_ char *s = NULL;
155
156 assert_se(cg_path_get_user_slice(path, &s) == code);
157 assert_se(streq_ptr(s, result));
158 }
159
160 static void test_path_get_user_slice(void) {
161 check_p_g_u_slice("/user.slice", -ENXIO, NULL);
162 check_p_g_u_slice("/foobar", -ENXIO, NULL);
163 check_p_g_u_slice("/user.slice/user-waldo.slice", -ENXIO, NULL);
164 check_p_g_u_slice("", -ENXIO, NULL);
165 check_p_g_u_slice("foobar", -ENXIO, NULL);
166 check_p_g_u_slice("foobar.slice", -ENXIO, NULL);
167 check_p_g_u_slice("foo.slice/foo-bar.slice/waldo.service", -ENXIO, NULL);
168
169 check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, SPECIAL_ROOT_SLICE);
170 check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, SPECIAL_ROOT_SLICE);
171 check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, SPECIAL_ROOT_SLICE);
172 check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, SPECIAL_ROOT_SLICE);
173 check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/piep.slice/foo.service", 0, "piep.slice");
174 check_p_g_u_slice("/foo.slice//foo-bar.slice/user@1000.service/piep.slice//piep-pap.slice//foo.service", 0, "piep-pap.slice");
175 }
176
177 static void test_get_paths(void) {
178 _cleanup_free_ char *a = NULL;
179
180 assert_se(cg_get_root_path(&a) >= 0);
181 log_info("Root = %s", a);
182 }
183
184 static void test_proc(void) {
185 _cleanup_closedir_ DIR *d = NULL;
186 struct dirent *de;
187 int r;
188
189 d = opendir("/proc");
190 assert_se(d);
191
192 FOREACH_DIRENT(de, d, break) {
193 _cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *slice = NULL;
194 pid_t pid;
195 uid_t uid = UID_INVALID;
196
197 if (!IN_SET(de->d_type, DT_DIR, DT_UNKNOWN))
198 continue;
199
200 r = parse_pid(de->d_name, &pid);
201 if (r < 0)
202 continue;
203
204 if (is_kernel_thread(pid))
205 continue;
206
207 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &path);
208 cg_pid_get_path_shifted(pid, NULL, &path_shifted);
209 cg_pid_get_owner_uid(pid, &uid);
210 cg_pid_get_session(pid, &session);
211 cg_pid_get_unit(pid, &unit);
212 cg_pid_get_user_unit(pid, &user_unit);
213 cg_pid_get_machine_name(pid, &machine);
214 cg_pid_get_slice(pid, &slice);
215
216 printf(PID_FMT"\t%s\t%s\t"UID_FMT"\t%s\t%s\t%s\t%s\t%s\n",
217 pid,
218 path,
219 path_shifted,
220 uid,
221 session,
222 unit,
223 user_unit,
224 machine,
225 slice);
226 }
227 }
228
229 static void test_escape_one(const char *s, const char *r) {
230 _cleanup_free_ char *b;
231
232 b = cg_escape(s);
233 assert_se(b);
234 assert_se(streq(b, r));
235
236 assert_se(streq(cg_unescape(b), s));
237 }
238
239 static void test_escape(void) {
240 test_escape_one("foobar", "foobar");
241 test_escape_one(".foobar", "_.foobar");
242 test_escape_one("foobar.service", "foobar.service");
243 test_escape_one("cgroup.service", "_cgroup.service");
244 test_escape_one("tasks", "_tasks");
245 if (access("/sys/fs/cgroup/cpu", F_OK) == 0)
246 test_escape_one("cpu.service", "_cpu.service");
247 test_escape_one("_foobar", "__foobar");
248 test_escape_one("", "_");
249 test_escape_one("_", "__");
250 test_escape_one(".", "_.");
251 }
252
253 static void test_controller_is_valid(void) {
254 assert_se(cg_controller_is_valid("foobar"));
255 assert_se(cg_controller_is_valid("foo_bar"));
256 assert_se(cg_controller_is_valid("name=foo"));
257 assert_se(!cg_controller_is_valid(""));
258 assert_se(!cg_controller_is_valid("name="));
259 assert_se(!cg_controller_is_valid("="));
260 assert_se(!cg_controller_is_valid("cpu,cpuacct"));
261 assert_se(!cg_controller_is_valid("_"));
262 assert_se(!cg_controller_is_valid("_foobar"));
263 assert_se(!cg_controller_is_valid("tatü"));
264 }
265
266 static void test_slice_to_path_one(const char *unit, const char *path, int error) {
267 _cleanup_free_ char *ret = NULL;
268
269 assert_se(cg_slice_to_path(unit, &ret) == error);
270 assert_se(streq_ptr(ret, path));
271 }
272
273 static void test_slice_to_path(void) {
274
275 test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
276 test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
277 test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
278 test_slice_to_path_one(SPECIAL_ROOT_SLICE, "", 0);
279 test_slice_to_path_one("--.slice", NULL, -EINVAL);
280 test_slice_to_path_one("-", NULL, -EINVAL);
281 test_slice_to_path_one("-foo-.slice", NULL, -EINVAL);
282 test_slice_to_path_one("-foo.slice", NULL, -EINVAL);
283 test_slice_to_path_one("foo-.slice", NULL, -EINVAL);
284 test_slice_to_path_one("foo--bar.slice", NULL, -EINVAL);
285 test_slice_to_path_one("foo.slice/foo--bar.slice", NULL, -EINVAL);
286 test_slice_to_path_one("a-b.slice", "a.slice/a-b.slice", 0);
287 test_slice_to_path_one("a-b-c-d-e.slice", "a.slice/a-b.slice/a-b-c.slice/a-b-c-d.slice/a-b-c-d-e.slice", 0);
288 }
289
290 static void test_shift_path_one(const char *raw, const char *root, const char *shifted) {
291 const char *s = NULL;
292
293 assert_se(cg_shift_path(raw, root, &s) >= 0);
294 assert_se(streq(s, shifted));
295 }
296
297 static void test_shift_path(void) {
298
299 test_shift_path_one("/foobar/waldo", "/", "/foobar/waldo");
300 test_shift_path_one("/foobar/waldo", "", "/foobar/waldo");
301 test_shift_path_one("/foobar/waldo", "/foobar", "/waldo");
302 test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo");
303 }
304
305 static void test_mask_supported(void) {
306
307 CGroupMask m;
308 CGroupController c;
309
310 assert_se(cg_mask_supported(&m) >= 0);
311
312 for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++)
313 printf("'%s' is supported: %s\n", cgroup_controller_to_string(c), yes_no(m & CGROUP_CONTROLLER_TO_MASK(c)));
314 }
315
316 static void test_is_cgroup_fs(void) {
317 struct statfs sfs;
318 assert_se(statfs("/sys/fs/cgroup", &sfs) == 0);
319 if (is_temporary_fs(&sfs))
320 assert_se(statfs("/sys/fs/cgroup/systemd", &sfs) == 0);
321 assert_se(is_cgroup_fs(&sfs));
322 }
323
324 static void test_fd_is_cgroup_fs(void) {
325 int fd;
326
327 fd = open("/sys/fs/cgroup", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
328 assert_se(fd >= 0);
329 if (fd_is_temporary_fs(fd)) {
330 fd = safe_close(fd);
331 fd = open("/sys/fs/cgroup/systemd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
332 assert_se(fd >= 0);
333 }
334 assert_se(fd_is_cgroup_fs(fd));
335 fd = safe_close(fd);
336 }
337
338 static void test_is_wanted_print(bool header) {
339 _cleanup_free_ char *cmdline = NULL;
340
341 log_info("-- %s --", __func__);
342 assert_se(proc_cmdline(&cmdline) >= 0);
343 log_info("cmdline: %s", cmdline);
344 if (header) {
345
346 log_info(_CGROUP_HIEARCHY_);
347 (void) system("findmnt -n /sys/fs/cgroup");
348 }
349
350 log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted()));
351 log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted()));
352 log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted()));
353 log_info(" ");
354 }
355
356 static void test_is_wanted(void) {
357 assert_se(setenv("SYSTEMD_PROC_CMDLINE",
358 "systemd.unified_cgroup_hierarchy", 1) >= 0);
359 test_is_wanted_print(false);
360
361 assert_se(setenv("SYSTEMD_PROC_CMDLINE",
362 "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
363 test_is_wanted_print(false);
364
365 assert_se(setenv("SYSTEMD_PROC_CMDLINE",
366 "systemd.unified_cgroup_hierarchy=0 "
367 "systemd.legacy_systemd_cgroup_controller", 1) >= 0);
368 test_is_wanted_print(false);
369
370 assert_se(setenv("SYSTEMD_PROC_CMDLINE",
371 "systemd.unified_cgroup_hierarchy=0 "
372 "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0);
373 test_is_wanted_print(false);
374 }
375
376 static void test_cg_tests(void) {
377 int all, hybrid, systemd, r;
378
379 r = cg_unified_flush();
380 if (r == -ENOMEDIUM) {
381 log_notice_errno(r, "Skipping cg hierarchy tests: %m");
382 return;
383 }
384 assert_se(r == 0);
385
386 all = cg_all_unified();
387 assert_se(IN_SET(all, 0, 1));
388
389 hybrid = cg_hybrid_unified();
390 assert_se(IN_SET(hybrid, 0, 1));
391
392 systemd = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
393 assert_se(IN_SET(systemd, 0, 1));
394
395 if (all) {
396 assert_se(systemd);
397 assert_se(!hybrid);
398
399 } else if (hybrid) {
400 assert_se(systemd);
401 assert_se(!all);
402
403 } else
404 assert_se(!systemd);
405 }
406
407 int main(void) {
408 log_set_max_level(LOG_DEBUG);
409 log_parse_environment();
410 log_open();
411
412 test_path_decode_unit();
413 test_path_get_unit();
414 test_path_get_user_unit();
415 test_path_get_session();
416 test_path_get_owner_uid();
417 test_path_get_slice();
418 test_path_get_user_slice();
419 TEST_REQ_RUNNING_SYSTEMD(test_get_paths());
420 test_proc();
421 TEST_REQ_RUNNING_SYSTEMD(test_escape());
422 test_controller_is_valid();
423 test_slice_to_path();
424 test_shift_path();
425 TEST_REQ_RUNNING_SYSTEMD(test_mask_supported());
426 TEST_REQ_RUNNING_SYSTEMD(test_is_cgroup_fs());
427 TEST_REQ_RUNNING_SYSTEMD(test_fd_is_cgroup_fs());
428 test_is_wanted_print(true);
429 test_is_wanted_print(false); /* run twice to test caching */
430 test_is_wanted();
431 test_cg_tests();
432
433 return 0;
434 }