1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "alloc-util.h"
4 #include "env-util.h"
5 #include "errno-util.h"
6 #include "log.h"
7 #include "macro.h"
8 #include "proc-cmdline.h"
9 #include "special.h"
10 #include "string-util.h"
11 #include "tests.h"
12 #include "util.h"
13
14 static int obj;
15
parse_item(const char * key,const char * value,void * data)16 static int parse_item(const char *key, const char *value, void *data) {
17 assert_se(key);
18 assert_se(data == &obj);
19
20 log_info("kernel cmdline option <%s> = <%s>", key, strna(value));
21 return 0;
22 }
23
TEST(proc_cmdline_parse)24 TEST(proc_cmdline_parse) {
25 assert_se(proc_cmdline_parse(parse_item, &obj, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
26 }
27
TEST(proc_cmdline_override)28 TEST(proc_cmdline_override) {
29 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);
30 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=different") == 0);
31
32 /* First test if the overrides for /proc/cmdline still work */
33 _cleanup_free_ char *line = NULL, *value = NULL;
34 assert_se(proc_cmdline(&line) >= 0);
35
36 /* Test if parsing makes uses of the override */
37 assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
38 assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
39 value = mfree(value);
40
41 assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value) > 0 && streq_ptr(value, "foo bar"));
42 value = mfree(value);
43
44 assert_se(proc_cmdline_get_key("and_one_more", 0, &value) > 0 && streq_ptr(value, "zzz aaa"));
45 value = mfree(value);
46
47 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=") == 0);
48 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);
49
50 assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
51 assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
52 value = mfree(value);
53
54 assert_se(proc_cmdline_get_key("some_arg_with_space", 0, &value) > 0 && streq_ptr(value, "foo bar"));
55 value = mfree(value);
56
57 assert_se(proc_cmdline_get_key("and_one_more", 0, &value) > 0 && streq_ptr(value, "zzz aaa"));
58 value = mfree(value);
59 }
60
parse_item_given(const char * key,const char * value,void * data)61 static int parse_item_given(const char *key, const char *value, void *data) {
62 assert_se(key);
63 assert_se(data);
64
65 bool *strip = data;
66
67 log_info("%s: option <%s> = <%s>", __func__, key, strna(value));
68 if (proc_cmdline_key_streq(key, "foo_bar"))
69 assert_se(streq(value, "quux"));
70 else if (proc_cmdline_key_streq(key, "wuff-piep"))
71 assert_se(streq(value, "tuet "));
72 else if (proc_cmdline_key_streq(key, "space"))
73 assert_se(streq(value, "x y z"));
74 else if (proc_cmdline_key_streq(key, "miepf"))
75 assert_se(streq(value, "uuu"));
76 else if (in_initrd() && *strip && proc_cmdline_key_streq(key, "zumm"))
77 assert_se(!value);
78 else if (in_initrd() && !*strip && proc_cmdline_key_streq(key, "rd.zumm"))
79 assert_se(!value);
80 else
81 assert_not_reached();
82
83 return 0;
84 }
85
test_proc_cmdline_given_one(bool flip_initrd)86 static void test_proc_cmdline_given_one(bool flip_initrd) {
87 log_info("/* %s (flip: %s) */", __func__, yes_no(flip_initrd));
88
89 if (flip_initrd)
90 in_initrd_force(!in_initrd());
91
92 bool t = true, f = false;
93 assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm space='x y z' miepf=\"uuu\"",
94 parse_item_given, &t, PROC_CMDLINE_STRIP_RD_PREFIX) >= 0);
95
96 assert_se(proc_cmdline_parse_given("foo_bar=quux wuff-piep=\"tuet \" rd.zumm space='x y z' miepf=\"uuu\"",
97 parse_item_given, &f, 0) >= 0);
98
99 if (flip_initrd)
100 in_initrd_force(!in_initrd());
101 }
102
TEST(test_proc_cmdline_given)103 TEST(test_proc_cmdline_given) {
104 test_proc_cmdline_given_one(false);
105 /* Repeat the same thing, but now flip our ininitrdness */
106 test_proc_cmdline_given_one(true);
107 }
108
TEST(proc_cmdline_get_key)109 TEST(proc_cmdline_get_key) {
110 _cleanup_free_ char *value = NULL;
111
112 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm spaaace='ö ü ß' ticks=\"''\"\n\nkkk=uuu\n\n\n") == 0);
113
114 assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL);
115 assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0);
116 assert_se(proc_cmdline_get_key("abc", 0, &value) == 0 && value == NULL);
117 assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL, &value) == 0 && value == NULL);
118
119 assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
120 value = mfree(value);
121 assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
122 value = mfree(value);
123 assert_se(proc_cmdline_get_key("foo-bar", 0, &value) > 0 && streq_ptr(value, "quux"));
124 value = mfree(value);
125 assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux"));
126 value = mfree(value);
127 assert_se(proc_cmdline_get_key("foo-bar", 0, NULL) == 0);
128 assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
129
130 assert_se(proc_cmdline_get_key("wuff-piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
131 value = mfree(value);
132 assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
133 value = mfree(value);
134 assert_se(proc_cmdline_get_key("wuff_piep", 0, &value) > 0 && streq_ptr(value, "tuet"));
135 value = mfree(value);
136 assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet"));
137 value = mfree(value);
138 assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL) == 0);
139 assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL);
140
141 assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL);
142 assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL);
143 assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0);
144
145 assert_se(proc_cmdline_get_key("spaaace", 0, &value) > 0 && streq_ptr(value, "ö ü ß"));
146 value = mfree(value);
147
148 assert_se(proc_cmdline_get_key("ticks", 0, &value) > 0 && streq_ptr(value, "''"));
149 value = mfree(value);
150
151 assert_se(proc_cmdline_get_key("kkk", 0, &value) > 0 && streq_ptr(value, "uuu"));
152 }
153
TEST(proc_cmdline_get_bool)154 TEST(proc_cmdline_get_bool) {
155 bool value = false;
156
157 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
158 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=") == 0);
159
160 assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
161 assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
162 assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
163 assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
164 assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
165 assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
166 assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false);
167 assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false);
168 assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false);
169 assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false);
170 assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
171 assert_se(proc_cmdline_get_bool("da", &value) > 0 && value == true);
172 assert_se(proc_cmdline_get_bool("the", &value) > 0 && value == true);
173 }
174
175 #if ENABLE_EFI
TEST(proc_cmdline_get_bool_efi)176 TEST(proc_cmdline_get_bool_efi) {
177 bool value = false;
178
179 assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=") == 0);
180 assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0);
181
182 assert_se(proc_cmdline_get_bool("", &value) == -EINVAL);
183 assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false);
184 assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true);
185 assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true);
186 assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true);
187 assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true);
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("x_y_z", &value) > 0 && value == false);
192 assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false);
193 assert_se(proc_cmdline_get_bool("da", &value) > 0 && value == true);
194 assert_se(proc_cmdline_get_bool("the", &value) > 0 && value == true);
195 }
196 #endif
197
TEST(proc_cmdline_get_key_many)198 TEST(proc_cmdline_get_key_many) {
199 _cleanup_free_ char *value1 = NULL, *value2 = NULL, *value3 = NULL, *value4 = NULL, *value5 = NULL, *value6 = NULL, *value7 = NULL;
200
201 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);
202
203 assert_se(proc_cmdline_get_key_many(0,
204 "wuff-piep", &value3,
205 "foo_bar", &value1,
206 "idontexist", &value2,
207 "zumm", &value4,
208 "SPACE", &value5,
209 "doubleticks", &value6,
210 "zummm", &value7) == 5);
211
212 assert_se(streq_ptr(value1, "quux"));
213 assert_se(!value2);
214 assert_se(streq_ptr(value3, "tuet"));
215 assert_se(!value4);
216 assert_se(streq_ptr(value5, "one two"));
217 assert_se(streq_ptr(value6, " aaa aaa "));
218 assert_se(streq_ptr(value7, "\n"));
219 }
220
TEST(proc_cmdline_key_streq)221 TEST(proc_cmdline_key_streq) {
222 assert_se(proc_cmdline_key_streq("", ""));
223 assert_se(proc_cmdline_key_streq("a", "a"));
224 assert_se(!proc_cmdline_key_streq("", "a"));
225 assert_se(!proc_cmdline_key_streq("a", ""));
226 assert_se(proc_cmdline_key_streq("a", "a"));
227 assert_se(!proc_cmdline_key_streq("a", "b"));
228 assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z"));
229 assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z"));
230 assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z"));
231 assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z"));
232 assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z"));
233 assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z"));
234 }
235
TEST(proc_cmdline_key_startswith)236 TEST(proc_cmdline_key_startswith) {
237 assert_se(proc_cmdline_key_startswith("", ""));
238 assert_se(proc_cmdline_key_startswith("x", ""));
239 assert_se(!proc_cmdline_key_startswith("", "x"));
240 assert_se(proc_cmdline_key_startswith("x", "x"));
241 assert_se(!proc_cmdline_key_startswith("x", "y"));
242 assert_se(!proc_cmdline_key_startswith("foo-bar", "quux"));
243 assert_se(proc_cmdline_key_startswith("foo-bar", "foo"));
244 assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar"));
245 assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar"));
246 assert_se(proc_cmdline_key_startswith("foo-bar", "foo_"));
247 assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
248 }
249
intro(void)250 static int intro(void) {
251 if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
252 return log_tests_skipped("can't read /proc/cmdline");
253
254 return EXIT_SUCCESS;
255 }
256
257 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
258