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