1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
21 #include "alloc-util.h"
23 #include "cgroup-util.h"
24 #include "dirent-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"
36 static void check_p_d_u(const char *path
, int code
, const char *result
) {
37 _cleanup_free_
char *unit
= NULL
;
40 r
= cg_path_decode_unit(path
, &unit
);
41 printf("%s: %s → %s %d expected %s %d\n", __func__
, path
, unit
, r
, result
, code
);
43 assert_se(streq_ptr(unit
, result
));
46 static 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");
58 static void check_p_g_u(const char *path
, int code
, const char *result
) {
59 _cleanup_free_
char *unit
= NULL
;
62 r
= cg_path_get_unit(path
, &unit
);
63 printf("%s: %s → %s %d expected %s %d\n", __func__
, path
, unit
, r
, result
, code
);
65 assert_se(streq_ptr(unit
, result
));
68 static 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
);
82 static void check_p_g_u_u(const char *path
, int code
, const char *result
) {
83 _cleanup_free_
char *unit
= NULL
;
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
);
89 assert_se(streq_ptr(unit
, result
));
92 static 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
);
108 static void check_p_g_s(const char *path
, int code
, const char *result
) {
109 _cleanup_free_
char *s
= NULL
;
111 assert_se(cg_path_get_session(path
, &s
) == code
);
112 assert_se(streq_ptr(s
, result
));
115 static 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
);
122 static void check_p_g_o_u(const char *path
, int code
, uid_t result
) {
125 assert_se(cg_path_get_owner_uid(path
, &uid
) == code
);
126 assert_se(uid
== result
);
129 static 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);
135 static void check_p_g_slice(const char *path
, int code
, const char *result
) {
136 _cleanup_free_
char *s
= NULL
;
138 assert_se(cg_path_get_slice(path
, &s
) == code
);
139 assert_se(streq_ptr(s
, result
));
142 static 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");
152 static void check_p_g_u_slice(const char *path
, int code
, const char *result
) {
153 _cleanup_free_
char *s
= NULL
;
155 assert_se(cg_path_get_user_slice(path
, &s
) == code
);
156 assert_se(streq_ptr(s
, result
));
159 static 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
);
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");
176 static void test_get_paths(void) {
177 _cleanup_free_
char *a
= NULL
;
179 assert_se(cg_get_root_path(&a
) >= 0);
180 log_info("Root = %s", a
);
183 static void test_proc(void) {
184 _cleanup_closedir_
DIR *d
= NULL
;
188 d
= opendir("/proc");
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
;
194 uid_t uid
= UID_INVALID
;
196 if (!IN_SET(de
->d_type
, DT_DIR
, DT_UNKNOWN
))
199 r
= parse_pid(de
->d_name
, &pid
);
203 if (is_kernel_thread(pid
))
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
);
215 printf(PID_FMT
"\t%s\t%s\t"UID_FMT
"\t%s\t%s\t%s\t%s\t%s\n",
228 static void test_escape_one(const char *s
, const char *r
) {
229 _cleanup_free_
char *b
;
233 assert_se(streq(b
, r
));
235 assert_se(streq(cg_unescape(b
), s
));
238 static 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(".", "_.");
252 static 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ü"));
265 static void test_slice_to_path_one(const char *unit
, const char *path
, int error
) {
266 _cleanup_free_
char *ret
= NULL
;
268 assert_se(cg_slice_to_path(unit
, &ret
) == error
);
269 assert_se(streq_ptr(ret
, path
));
272 static void test_slice_to_path(void) {
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);
289 static void test_shift_path_one(const char *raw
, const char *root
, const char *shifted
) {
290 const char *s
= NULL
;
292 assert_se(cg_shift_path(raw
, root
, &s
) >= 0);
293 assert_se(streq(s
, shifted
));
296 static void test_shift_path(void) {
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");
304 static void test_mask_supported(void) {
309 assert_se(cg_mask_supported(&m
) >= 0);
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
)));
315 static void test_is_cgroup_fs(void) {
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
));
323 static void test_fd_is_cgroup_fs(void) {
326 fd
= open("/sys/fs/cgroup", O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
);
328 if (fd_is_temporary_fs(fd
)) {
330 fd
= open("/sys/fs/cgroup/systemd", O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
);
333 assert_se(fd_is_cgroup_fs(fd
));
337 static void test_is_wanted_print(bool header
) {
338 _cleanup_free_
char *cmdline
= NULL
;
340 log_info("-- %s --", __func__
);
341 assert_se(proc_cmdline(&cmdline
) >= 0);
342 log_info("cmdline: %s", cmdline
);
345 log_info(_CGROUP_HIEARCHY_
);
346 (void) system("findmnt -n /sys/fs/cgroup");
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()));
355 static 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);
360 assert_se(setenv("SYSTEMD_PROC_CMDLINE",
361 "systemd.unified_cgroup_hierarchy=0", 1) >= 0);
362 test_is_wanted_print(false);
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);
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);
375 static void test_cg_tests(void) {
376 int all
, hybrid
, systemd
, r
;
378 r
= cg_unified_flush();
379 if (r
== -ENOMEDIUM
) {
380 log_notice_errno(r
, "Skipping cg hierarchy tests: %m");
385 all
= cg_all_unified();
386 assert_se(IN_SET(all
, 0, 1));
388 hybrid
= cg_hybrid_unified();
389 assert_se(IN_SET(hybrid
, 0, 1));
391 systemd
= cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER
);
392 assert_se(IN_SET(systemd
, 0, 1));
407 log_set_max_level(LOG_DEBUG
);
408 log_parse_environment();
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());
420 TEST_REQ_RUNNING_SYSTEMD(test_escape());
421 test_controller_is_valid();
422 test_slice_to_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 */