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