]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-cgroup-util.c
core: rework cgroup path parse logic
[thirdparty/systemd.git] / src / test / test-cgroup-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Zbigniew Jędrzejewski-Szmek
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22
23 #include "util.h"
24 #include "cgroup-util.h"
25 #include "test-helper.h"
26 #include "formats-util.h"
27 #include "process-util.h"
28
29 static void check_p_d_u(const char *path, int code, const char *result) {
30 _cleanup_free_ char *unit = NULL;
31 int r;
32
33 r = cg_path_decode_unit(path, &unit);
34 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
35 assert_se(r == code);
36 assert_se(streq_ptr(unit, result));
37 }
38
39 static void test_path_decode_unit(void) {
40 check_p_d_u("getty@tty2.service", 0, "getty@tty2.service");
41 check_p_d_u("getty@tty2.service/", 0, "getty@tty2.service");
42 check_p_d_u("getty@tty2.service/xxx", 0, "getty@tty2.service");
43 check_p_d_u("getty@.service/", -ENXIO, NULL);
44 check_p_d_u("getty@.service", -ENXIO, NULL);
45 check_p_d_u("getty.service", 0, "getty.service");
46 check_p_d_u("getty", -ENXIO, NULL);
47 check_p_d_u("getty/waldo", -ENXIO, NULL);
48 check_p_d_u("_cpu.service", 0, "cpu.service");
49 }
50
51 static void check_p_g_u(const char *path, int code, const char *result) {
52 _cleanup_free_ char *unit = NULL;
53 int r;
54
55 r = cg_path_get_unit(path, &unit);
56 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
57 assert_se(r == code);
58 assert_se(streq_ptr(unit, result));
59 }
60
61 static void test_path_get_unit(void) {
62 check_p_g_u("/system.slice/foobar.service/sdfdsaf", 0, "foobar.service");
63 check_p_g_u("/system.slice/getty@tty5.service", 0, "getty@tty5.service");
64 check_p_g_u("/system.slice/getty@tty5.service/aaa/bbb", 0, "getty@tty5.service");
65 check_p_g_u("/system.slice/getty@tty5.service/", 0, "getty@tty5.service");
66 check_p_g_u("/system.slice/getty@tty6.service/tty5", 0, "getty@tty6.service");
67 check_p_g_u("sadfdsafsda", -ENXIO, NULL);
68 check_p_g_u("/system.slice/getty####@tty6.service/xxx", -ENXIO, NULL);
69 check_p_g_u("/system.slice/system-waldo.slice/foobar.service/sdfdsaf", 0, "foobar.service");
70 check_p_g_u("/system.slice/system-waldo.slice/_cpu.service/sdfdsaf", 0, "cpu.service");
71 check_p_g_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "user@1000.service");
72 check_p_g_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
73 }
74
75 static void check_p_g_u_u(const char *path, int code, const char *result) {
76 _cleanup_free_ char *unit = NULL;
77 int r;
78
79 r = cg_path_get_user_unit(path, &unit);
80 printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
81 assert_se(r == code);
82 assert_se(streq_ptr(unit, result));
83 }
84
85 static void test_path_get_user_unit(void) {
86 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "foobar.service");
87 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo.slice/foobar.service", 0, "foobar.service");
88 check_p_g_u_u("/user.slice/user-1002.slice/session-2.scope/foobar.service/waldo", 0, "foobar.service");
89 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar.service/waldo/uuuux", 0, "foobar.service");
90 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/waldo/waldo/uuuux", -ENXIO, NULL);
91 check_p_g_u_u("/user.slice/user-1000.slice/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
92 check_p_g_u_u("/session-2.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
93 check_p_g_u_u("/xyz.slice/xyz-waldo.slice/session-77.scope/foobar@pie.service/pa/po", 0, "foobar@pie.service");
94 check_p_g_u_u("/meh.service", -ENXIO, NULL);
95 check_p_g_u_u("/session-3.scope/_cpu.service", 0, "cpu.service");
96 check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/server.service", 0, "server.service");
97 check_p_g_u_u("/user.slice/user-1000.slice/user@1000.service/foobar.slice/foobar@pie.service", 0, "foobar@pie.service");
98 check_p_g_u_u("/user.slice/user-1000.slice/user@.service/server.service", -ENXIO, NULL);
99 }
100
101 static void check_p_g_s(const char *path, int code, const char *result) {
102 _cleanup_free_ char *s = NULL;
103
104 assert_se(cg_path_get_session(path, &s) == code);
105 assert_se(streq_ptr(s, result));
106 }
107
108 static void test_path_get_session(void) {
109 check_p_g_s("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, "2");
110 check_p_g_s("/session-3.scope", 0, "3");
111 check_p_g_s("/session-.scope", -ENXIO, NULL);
112 check_p_g_s("", -ENXIO, NULL);
113 }
114
115 static void check_p_g_o_u(const char *path, int code, uid_t result) {
116 uid_t uid = 0;
117
118 assert_se(cg_path_get_owner_uid(path, &uid) == code);
119 assert_se(uid == result);
120 }
121
122 static void test_path_get_owner_uid(void) {
123 check_p_g_o_u("/user.slice/user-1000.slice/session-2.scope/foobar.service", 0, 1000);
124 check_p_g_o_u("/user.slice/user-1006.slice", 0, 1006);
125 check_p_g_o_u("", -ENXIO, 0);
126 }
127
128 static void check_p_g_slice(const char *path, int code, const char *result) {
129 _cleanup_free_ char *s = NULL;
130
131 assert_se(cg_path_get_slice(path, &s) == code);
132 assert_se(streq_ptr(s, result));
133 }
134
135 static void test_path_get_slice(void) {
136 check_p_g_slice("/user.slice", 0, "user.slice");
137 check_p_g_slice("/foobar", 0, "-.slice");
138 check_p_g_slice("/user.slice/user-waldo.slice", 0, "user-waldo.slice");
139 check_p_g_slice("", 0, "-.slice");
140 check_p_g_slice("foobar", 0, "-.slice");
141 check_p_g_slice("foobar.slice", 0, "foobar.slice");
142 check_p_g_slice("foo.slice/foo-bar.slice/waldo.service", 0, "foo-bar.slice");
143 }
144
145 static void test_get_paths(void) {
146 _cleanup_free_ char *a = NULL;
147
148 assert_se(cg_get_root_path(&a) >= 0);
149 log_info("Root = %s", a);
150 }
151
152 static void test_proc(void) {
153 _cleanup_closedir_ DIR *d = NULL;
154 struct dirent *de;
155 int r;
156
157 d = opendir("/proc");
158 assert_se(d);
159
160 FOREACH_DIRENT(de, d, break) {
161 _cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *slice = NULL;
162 pid_t pid;
163 uid_t uid = UID_INVALID;
164
165 if (de->d_type != DT_DIR &&
166 de->d_type != DT_UNKNOWN)
167 continue;
168
169 r = parse_pid(de->d_name, &pid);
170 if (r < 0)
171 continue;
172
173 if (is_kernel_thread(pid))
174 continue;
175
176 cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &path);
177 cg_pid_get_path_shifted(pid, NULL, &path_shifted);
178 cg_pid_get_owner_uid(pid, &uid);
179 cg_pid_get_session(pid, &session);
180 cg_pid_get_unit(pid, &unit);
181 cg_pid_get_user_unit(pid, &user_unit);
182 cg_pid_get_machine_name(pid, &machine);
183 cg_pid_get_slice(pid, &slice);
184
185 printf(PID_FMT"\t%s\t%s\t"UID_FMT"\t%s\t%s\t%s\t%s\t%s\n",
186 pid,
187 path,
188 path_shifted,
189 uid,
190 session,
191 unit,
192 user_unit,
193 machine,
194 slice);
195 }
196 }
197
198 static void test_escape_one(const char *s, const char *r) {
199 _cleanup_free_ char *b;
200
201 b = cg_escape(s);
202 assert_se(b);
203 assert_se(streq(b, r));
204
205 assert_se(streq(cg_unescape(b), s));
206 }
207
208 static void test_escape(void) {
209 test_escape_one("foobar", "foobar");
210 test_escape_one(".foobar", "_.foobar");
211 test_escape_one("foobar.service", "foobar.service");
212 test_escape_one("cgroup.service", "_cgroup.service");
213 test_escape_one("tasks", "_tasks");
214 if (access("/sys/fs/cgroup/cpu", F_OK) == 0)
215 test_escape_one("cpu.service", "_cpu.service");
216 test_escape_one("_foobar", "__foobar");
217 test_escape_one("", "_");
218 test_escape_one("_", "__");
219 test_escape_one(".", "_.");
220 }
221
222 static void test_controller_is_valid(void) {
223 assert_se(cg_controller_is_valid("foobar", false));
224 assert_se(cg_controller_is_valid("foo_bar", false));
225 assert_se(cg_controller_is_valid("name=foo", true));
226 assert_se(!cg_controller_is_valid("", false));
227 assert_se(!cg_controller_is_valid("name=", true));
228 assert_se(!cg_controller_is_valid("=", false));
229 assert_se(!cg_controller_is_valid("cpu,cpuacct", false));
230 assert_se(!cg_controller_is_valid("_", false));
231 assert_se(!cg_controller_is_valid("_foobar", false));
232 assert_se(!cg_controller_is_valid("tatü", false));
233 }
234
235 static void test_slice_to_path_one(const char *unit, const char *path, int error) {
236 _cleanup_free_ char *ret = NULL;
237
238 assert_se(cg_slice_to_path(unit, &ret) == error);
239 assert_se(streq_ptr(ret, path));
240 }
241
242 static void test_slice_to_path(void) {
243
244 test_slice_to_path_one("foobar.slice", "foobar.slice", 0);
245 test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0);
246 test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL);
247 test_slice_to_path_one("-.slice", NULL, -EINVAL);
248 test_slice_to_path_one("-foo-.slice", NULL, -EINVAL);
249 test_slice_to_path_one("-foo.slice", NULL, -EINVAL);
250 test_slice_to_path_one("a-b.slice", "a.slice/a-b.slice", 0);
251 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);
252 }
253
254 static void test_shift_path_one(const char *raw, const char *root, const char *shifted) {
255 const char *s = NULL;
256
257 assert_se(cg_shift_path(raw, root, &s) >= 0);
258 assert_se(streq(s, shifted));
259 }
260
261 static void test_shift_path(void) {
262
263 test_shift_path_one("/foobar/waldo", "/", "/foobar/waldo");
264 test_shift_path_one("/foobar/waldo", "", "/foobar/waldo");
265 test_shift_path_one("/foobar/waldo", "/foobar", "/waldo");
266 test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo");
267 }
268
269 int main(void) {
270 test_path_decode_unit();
271 test_path_get_unit();
272 test_path_get_user_unit();
273 test_path_get_session();
274 test_path_get_owner_uid();
275 test_path_get_slice();
276 TEST_REQ_RUNNING_SYSTEMD(test_get_paths());
277 test_proc();
278 TEST_REQ_RUNNING_SYSTEMD(test_escape());
279 test_controller_is_valid();
280 test_slice_to_path();
281 test_shift_path();
282
283 return 0;
284 }