1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
5 #include "errno-util.h"
6 #include "initrd-util.h"
9 #include "nulstr-util.h"
10 #include "proc-cmdline.h"
11 #include "process-util.h"
13 #include "string-util.h"
19 static int parse_item(const char *key
, const char *value
, void *data
) {
21 assert_se(data
== &obj
);
23 log_info("kernel cmdline option <%s> = <%s>", key
, strna(value
));
27 TEST(proc_cmdline_parse
) {
28 assert_se(proc_cmdline_parse(parse_item
, &obj
, PROC_CMDLINE_STRIP_RD_PREFIX
) >= 0);
31 TEST(proc_cmdline_override
) {
32 _cleanup_free_
char *line
= NULL
, *value
= NULL
;
33 _cleanup_strv_free_
char **args
= NULL
;
35 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
36 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=different") == 0);
38 /* First test if the overrides for /proc/cmdline still work */
39 assert_se(proc_cmdline(&line
) >= 0);
40 assert_se(streq(line
, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
42 assert_se(proc_cmdline_strv(&args
) >= 0);
43 assert_se(strv_equal(args
, STRV_MAKE("foo_bar=quux", "wuff-piep=tuet", "zumm", "some_arg_with_space=foo bar", "and_one_more=zzz aaa")));
44 args
= strv_free(args
);
46 /* Test if parsing makes uses of the override */
47 assert_se(proc_cmdline_get_key("foo_bar", 0, &value
) > 0 && streq_ptr(value
, "quux"));
50 assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value
) > 0 && streq_ptr(value
, "foo bar"));
53 assert_se(proc_cmdline_get_key("and_one_more", 0, &value
) > 0 && streq_ptr(value
, "zzz aaa"));
56 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=hoge") == 0);
57 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
59 assert_se(proc_cmdline(&line
) >= 0);
60 assert_se(streq(line
, "hoge"));
62 assert_se(proc_cmdline_strv(&args
) >= 0);
63 assert_se(strv_equal(args
, STRV_MAKE("hoge")));
64 args
= strv_free(args
);
67 assert_se(proc_cmdline_get_key("foo_bar", 0, &value
) > 0 && streq_ptr(value
, "quux"));
70 assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value
) > 0 && streq_ptr(value
, "foo bar"));
73 assert_se(proc_cmdline_get_key("and_one_more", 0, &value
) > 0 && streq_ptr(value
, "zzz aaa"));
78 static int parse_item_given(const char *key
, const char *value
, void *data
) {
84 log_info("%s: option <%s> = <%s>", __func__
, key
, strna(value
));
85 if (proc_cmdline_key_streq(key
, "foo_bar"))
86 assert_se(streq(value
, "quux"));
87 else if (proc_cmdline_key_streq(key
, "wuff-piep"))
88 assert_se(streq(value
, "tuet "));
89 else if (proc_cmdline_key_streq(key
, "space"))
90 assert_se(streq(value
, "x y z"));
91 else if (proc_cmdline_key_streq(key
, "miepf"))
92 assert_se(streq(value
, "uuu"));
93 else if (in_initrd() && *strip
&& proc_cmdline_key_streq(key
, "zumm"))
95 else if (in_initrd() && !*strip
&& proc_cmdline_key_streq(key
, "rd.zumm"))
103 static void test_proc_cmdline_given_one(bool flip_initrd
) {
104 log_info("/* %s (flip: %s) */", __func__
, yes_no(flip_initrd
));
107 in_initrd_force(!in_initrd());
109 bool t
= true, f
= false;
110 assert_se(proc_cmdline_parse(parse_item_given
, &t
, PROC_CMDLINE_STRIP_RD_PREFIX
) >= 0);
111 assert_se(proc_cmdline_parse(parse_item_given
, &f
, 0) >= 0);
114 in_initrd_force(!in_initrd());
117 TEST(test_proc_cmdline_given
) {
118 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=\"tuet \" rd.zumm space='x y z' miepf=\"uuu\"") == 0);
119 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=miepf=\"uuu\"") == 0);
121 test_proc_cmdline_given_one(false);
122 /* Repeat the same thing, but now flip our ininitrdness */
123 test_proc_cmdline_given_one(true);
126 TEST(proc_cmdline_get_key
) {
127 _cleanup_free_
char *value
= NULL
;
129 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm-ghh spaaace='ö ü ß' ticks=\"''\"\n\nkkk=uuu\n\n\n") == 0);
131 assert_se(proc_cmdline_get_key("", 0, &value
) == -EINVAL
);
132 assert_se(proc_cmdline_get_key("abc", 0, NULL
) == 0);
133 assert_se(proc_cmdline_get_key("abc", 0, &value
) == 0 && value
== NULL
);
134 assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) == 0 && value
== NULL
);
136 assert_se(proc_cmdline_get_key("foo_bar", 0, &value
) > 0 && streq_ptr(value
, "quux"));
137 value
= mfree(value
);
138 assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && streq_ptr(value
, "quux"));
139 value
= mfree(value
);
140 assert_se(proc_cmdline_get_key("foo_bar", 0, NULL
) == 0);
141 assert_se(proc_cmdline_get_key("foo-bar", 0, &value
) > 0 && streq_ptr(value
, "quux"));
142 value
= mfree(value
);
143 assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && streq_ptr(value
, "quux"));
144 value
= mfree(value
);
145 assert_se(proc_cmdline_get_key("foo-bar", 0, NULL
) == 0);
146 assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL
, NULL
) == -EINVAL
);
148 assert_se(proc_cmdline_get_key("wuff-piep", 0, &value
) > 0 && streq_ptr(value
, "tuet"));
149 value
= mfree(value
);
150 assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && streq_ptr(value
, "tuet"));
151 value
= mfree(value
);
152 assert_se(proc_cmdline_get_key("wuff_piep", 0, &value
) > 0 && streq_ptr(value
, "tuet"));
153 value
= mfree(value
);
154 assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && streq_ptr(value
, "tuet"));
155 value
= mfree(value
);
156 assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL
) == 0);
157 assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL
, NULL
) == -EINVAL
);
159 assert_se(proc_cmdline_get_key("zumm-ghh", 0, &value
) == 0 && value
== NULL
);
160 assert_se(proc_cmdline_get_key("zumm-ghh", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && value
== NULL
);
161 assert_se(proc_cmdline_get_key("zumm-ghh", 0, NULL
) > 0);
162 assert_se(proc_cmdline_get_key("zumm_ghh", 0, &value
) == 0 && value
== NULL
);
163 assert_se(proc_cmdline_get_key("zumm_ghh", PROC_CMDLINE_VALUE_OPTIONAL
, &value
) > 0 && value
== NULL
);
164 assert_se(proc_cmdline_get_key("zumm_ghh", 0, NULL
) > 0);
166 assert_se(proc_cmdline_get_key("spaaace", 0, &value
) > 0 && streq_ptr(value
, "ö ü ß"));
167 value
= mfree(value
);
169 assert_se(proc_cmdline_get_key("ticks", 0, &value
) > 0 && streq_ptr(value
, "''"));
170 value
= mfree(value
);
172 assert_se(proc_cmdline_get_key("kkk", 0, &value
) > 0 && streq_ptr(value
, "uuu"));
175 TEST(proc_cmdline_get_bool
) {
178 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
179 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=") == 0);
181 assert_se(proc_cmdline_get_bool("", &value
) == -EINVAL
);
182 assert_se(proc_cmdline_get_bool("abc", &value
) == 0 && value
== false);
183 assert_se(proc_cmdline_get_bool("foo_bar", &value
) > 0 && value
== true);
184 assert_se(proc_cmdline_get_bool("foo-bar", &value
) > 0 && value
== true);
185 assert_se(proc_cmdline_get_bool("bar-waldo", &value
) > 0 && value
== true);
186 assert_se(proc_cmdline_get_bool("bar_waldo", &value
) > 0 && value
== true);
187 assert_se(proc_cmdline_get_bool("x_y-z", &value
) > 0 && value
== false);
188 assert_se(proc_cmdline_get_bool("x-y-z", &value
) > 0 && value
== false);
189 assert_se(proc_cmdline_get_bool("x-y_z", &value
) > 0 && value
== false);
190 assert_se(proc_cmdline_get_bool("x_y_z", &value
) > 0 && value
== false);
191 assert_se(proc_cmdline_get_bool("quux", &value
) == -EINVAL
&& value
== false);
192 assert_se(proc_cmdline_get_bool("da", &value
) > 0 && value
== true);
193 assert_se(proc_cmdline_get_bool("the", &value
) > 0 && value
== true);
197 TEST(proc_cmdline_get_bool_efi
) {
200 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=") == 0);
201 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
203 assert_se(proc_cmdline_get_bool("", &value
) == -EINVAL
);
204 assert_se(proc_cmdline_get_bool("abc", &value
) == 0 && value
== false);
205 assert_se(proc_cmdline_get_bool("foo_bar", &value
) > 0 && value
== true);
206 assert_se(proc_cmdline_get_bool("foo-bar", &value
) > 0 && value
== true);
207 assert_se(proc_cmdline_get_bool("bar-waldo", &value
) > 0 && value
== true);
208 assert_se(proc_cmdline_get_bool("bar_waldo", &value
) > 0 && value
== true);
209 assert_se(proc_cmdline_get_bool("x_y-z", &value
) > 0 && value
== false);
210 assert_se(proc_cmdline_get_bool("x-y-z", &value
) > 0 && value
== false);
211 assert_se(proc_cmdline_get_bool("x-y_z", &value
) > 0 && value
== false);
212 assert_se(proc_cmdline_get_bool("x_y_z", &value
) > 0 && value
== false);
213 assert_se(proc_cmdline_get_bool("quux", &value
) == -EINVAL
&& value
== false);
214 assert_se(proc_cmdline_get_bool("da", &value
) > 0 && value
== true);
215 assert_se(proc_cmdline_get_bool("the", &value
) > 0 && value
== true);
219 TEST(proc_cmdline_get_key_many
) {
220 _cleanup_free_
char *value1
= NULL
, *value2
= NULL
, *value3
= NULL
, *value4
= NULL
, *value5
= NULL
, *value6
= NULL
, *value7
= NULL
;
222 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm SPACE='one two' doubleticks=\" aaa aaa \"\n\nzummm='\n'\n") == 0);
224 assert_se(proc_cmdline_get_key_many(0,
225 "wuff-piep", &value3
,
227 "idontexist", &value2
,
230 "doubleticks", &value6
,
231 "zummm", &value7
) == 5);
233 assert_se(streq_ptr(value1
, "quux"));
235 assert_se(streq_ptr(value3
, "tuet"));
237 assert_se(streq_ptr(value5
, "one two"));
238 assert_se(streq_ptr(value6
, " aaa aaa "));
239 assert_se(streq_ptr(value7
, "\n"));
242 TEST(proc_cmdline_key_streq
) {
243 assert_se(proc_cmdline_key_streq("", ""));
244 assert_se(proc_cmdline_key_streq("a", "a"));
245 assert_se(!proc_cmdline_key_streq("", "a"));
246 assert_se(!proc_cmdline_key_streq("a", ""));
247 assert_se(proc_cmdline_key_streq("a", "a"));
248 assert_se(!proc_cmdline_key_streq("a", "b"));
249 assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z"));
250 assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z"));
251 assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z"));
252 assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z"));
253 assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z"));
254 assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z"));
257 TEST(proc_cmdline_key_startswith
) {
258 assert_se(proc_cmdline_key_startswith("", ""));
259 assert_se(proc_cmdline_key_startswith("x", ""));
260 assert_se(!proc_cmdline_key_startswith("", "x"));
261 assert_se(proc_cmdline_key_startswith("x", "x"));
262 assert_se(!proc_cmdline_key_startswith("x", "y"));
263 assert_se(!proc_cmdline_key_startswith("foo-bar", "quux"));
264 assert_se(proc_cmdline_key_startswith("foo-bar", "foo"));
265 assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar"));
266 assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar"));
267 assert_se(proc_cmdline_key_startswith("foo-bar", "foo_"));
268 assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
271 #define test_proc_cmdline_filter_pid1_args_one(nulstr, expected) \
273 _cleanup_strv_free_ char **a = NULL, **b = NULL; \
274 const char s[] = (nulstr); \
276 /* This emulates get_process_cmdline_strv(). */ \
277 assert_se(a = strv_parse_nulstr_full(s, ELEMENTSOF(s), \
278 /* drop_trailing_nuls = */ true)); \
279 assert_se(proc_cmdline_filter_pid1_args(a, &b) >= 0); \
280 assert_se(strv_equal(b, expected)); \
283 TEST(proc_cmdline_filter_pid1_args
) {
284 test_proc_cmdline_filter_pid1_args_one("systemd\0",
288 test_proc_cmdline_filter_pid1_args_one("systemd\0"
289 "-a\0" /* unknown option */
290 "-abc\0" /* unknown options */
291 "-h\0" /* known option */
292 "-hDbs\0" /* known options */
293 "-hsx\0" /* mixed (known and unknown) options */
294 "-z\0drop1\0" /* option with argument */
295 "-z\0-z\0accept1\0" /* the second -z is handled as argument */
296 "-az\0drop2\0" /* options with argument */
297 "-za\0accept2\0" /* options with argument */
298 "-z\0--\0-x\0", /* "--" is handled as argument */
299 STRV_MAKE("accept1", "accept2"));
302 test_proc_cmdline_filter_pid1_args_one("systemd\0"
303 "--unknown\0accept1\0" /* unknown option */
304 "--system\0accept2\0" /* no argument */
305 "--log-level\0drop1\0" /* required argument (separated with space) */
306 "--log-level=drop2\0accept3\0" /* required argument (concatenated with '=') */
307 "--log-level\0--log-level\0accept4\0" /* the second "--log-level" is handled as argument */
308 "--log-level\0--\0-x\0" /* "--" is handled as argument */
309 "--log-color\0--log-level\0drop3\0" /* optional argument ("--log-level" is handled as another option) */
310 "--log-color\0accept5\0" /* optional argument (separated with space) */
311 "--log-color=drop4\0accept6\0" /* optional argument (concatenated with '=') */
312 "--log-color\0--\0" /* "--" is _not_ handled as argument, and remaining strings are accepted */
313 "remaining\0-x\0--foo\0",
314 STRV_MAKE("accept1", "accept2", "accept3", "accept4", "accept5", "accept6", "remaining", "-x", "--foo"));
317 test_proc_cmdline_filter_pid1_args_one("systemd\0"
320 "--\0" /* remaining strings are accepted */
325 STRV_MAKE("-x", "-abc", "--hoge", "accepted"));
328 test_proc_cmdline_filter_pid1_args_one("/usr/lib/systemd/systemd\0"
331 "--deserialize\030\0" /* followed with space */
332 "--deserialize=31\0" /* followed with '=' */
335 "systemd.log_level=debug\0"
336 "--unit\0foo.target\0"
338 "systemd.log_target=console\0"
343 STRV_MAKE("", "", "", "systemd.log_level=debug", " ' quoted '", "systemd.log_target=console", "\t", " arg with space ", "3"));
346 static int intro(void) {
347 if (access("/proc/cmdline", R_OK
) < 0 && ERRNO_IS_PRIVILEGE(errno
))
348 return log_tests_skipped("can't read /proc/cmdline");
353 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO
, intro
);