]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/proc-cmdline.c
network,udev: refuse .link and .network settings with no matches
[thirdparty/systemd.git] / src / basic / proc-cmdline.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <stdbool.h>
4 #include <stddef.h>
5
6 #include "alloc-util.h"
7 #include "efivars.h"
8 #include "extract-word.h"
9 #include "fileio.h"
10 #include "macro.h"
11 #include "parse-util.h"
12 #include "proc-cmdline.h"
13 #include "process-util.h"
14 #include "special.h"
15 #include "string-util.h"
16 #include "util.h"
17 #include "virt.h"
18
19 int proc_cmdline(char **ret) {
20 const char *e;
21 assert(ret);
22
23 /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
24 e = secure_getenv("SYSTEMD_PROC_CMDLINE");
25 if (e) {
26 char *m;
27
28 m = strdup(e);
29 if (!m)
30 return -ENOMEM;
31
32 *ret = m;
33 return 0;
34 }
35
36 if (detect_container() > 0)
37 return get_process_cmdline(1, SIZE_MAX, 0, ret);
38 else
39 return read_one_line_file("/proc/cmdline", ret);
40 }
41
42 static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
43 const char *q = *p;
44 int r;
45
46 for (;;) {
47 _cleanup_free_ char *word = NULL;
48 const char *c;
49
50 r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
51 if (r < 0)
52 return r;
53 if (r == 0)
54 break;
55
56 /* Filter out arguments that are intended only for the initrd */
57 c = startswith(word, "rd.");
58 if (c) {
59 if (!in_initrd())
60 continue;
61
62 if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX)) {
63 r = free_and_strdup(&word, c);
64 if (r < 0)
65 return r;
66 }
67
68 } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
69 continue; /* And optionally filter out arguments that are intended only for the host */
70
71 *p = q;
72 *ret_word = TAKE_PTR(word);
73 return 1;
74 }
75
76 *p = q;
77 *ret_word = NULL;
78 return 0;
79 }
80
81 int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
82 const char *p;
83 int r;
84
85 assert(parse_item);
86
87 /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_parse(), let's make this
88 * clear. */
89 assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
90
91 p = line;
92 for (;;) {
93 _cleanup_free_ char *word = NULL;
94 char *value;
95
96 r = proc_cmdline_extract_first(&p, &word, flags);
97 if (r < 0)
98 return r;
99 if (r == 0)
100 break;
101
102 value = strchr(word, '=');
103 if (value)
104 *(value++) = 0;
105
106 r = parse_item(word, value, data);
107 if (r < 0)
108 return r;
109 }
110
111 return 0;
112 }
113
114 int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
115 _cleanup_free_ char *line = NULL;
116 int r;
117
118 assert(parse_item);
119
120 /* We parse the EFI variable first, because later settings have higher priority. */
121
122 r = systemd_efi_options_variable(&line);
123 if (r < 0 && r != -ENODATA)
124 log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
125
126 r = proc_cmdline_parse_given(line, parse_item, data, flags);
127 if (r < 0)
128 return r;
129
130 line = mfree(line);
131 r = proc_cmdline(&line);
132 if (r < 0)
133 return r;
134
135 return proc_cmdline_parse_given(line, parse_item, data, flags);
136 }
137
138 static bool relaxed_equal_char(char a, char b) {
139 return a == b ||
140 (a == '_' && b == '-') ||
141 (a == '-' && b == '_');
142 }
143
144 char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
145 assert(s);
146 assert(prefix);
147
148 /* Much like startswith(), but considers "-" and "_" the same */
149
150 for (; *prefix != 0; s++, prefix++)
151 if (!relaxed_equal_char(*s, *prefix))
152 return NULL;
153
154 return (char*) s;
155 }
156
157 bool proc_cmdline_key_streq(const char *x, const char *y) {
158 assert(x);
159 assert(y);
160
161 /* Much like streq(), but considers "-" and "_" the same */
162
163 for (; *x != 0 || *y != 0; x++, y++)
164 if (!relaxed_equal_char(*x, *y))
165 return false;
166
167 return true;
168 }
169
170 static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags flags, char **ret_value) {
171 _cleanup_free_ char *ret = NULL;
172 bool found = false;
173 const char *p;
174 int r;
175
176 assert(line);
177 assert(key);
178
179 p = line;
180 for (;;) {
181 _cleanup_free_ char *word = NULL;
182
183 r = proc_cmdline_extract_first(&p, &word, flags);
184 if (r < 0)
185 return r;
186 if (r == 0)
187 break;
188
189 if (ret_value) {
190 const char *e;
191
192 e = proc_cmdline_key_startswith(word, key);
193 if (!e)
194 continue;
195
196 if (*e == '=') {
197 r = free_and_strdup(&ret, e+1);
198 if (r < 0)
199 return r;
200
201 found = true;
202
203 } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
204 found = true;
205
206 } else {
207 if (streq(word, key)) {
208 found = true;
209 break; /* we found what we were looking for */
210 }
211 }
212 }
213
214 if (ret_value)
215 *ret_value = TAKE_PTR(ret);
216
217 return found;
218 }
219
220 int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
221 _cleanup_free_ char *line = NULL;
222 int r;
223
224 /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable.
225 * Supports three modes:
226 *
227 * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
228 * "=" is searched for, and the value following it is returned in "ret_value".
229 *
230 * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
231 * word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
232 * also accepted, and "value" is returned as NULL.
233 *
234 * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
235 *
236 * In all three cases, > 0 is returned if the key is found, 0 if not. */
237
238 if (isempty(key))
239 return -EINVAL;
240
241 if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
242 return -EINVAL;
243
244 r = proc_cmdline(&line);
245 if (r < 0)
246 return r;
247
248 r = cmdline_get_key(line, key, flags, ret_value);
249 if (r != 0) /* Either error or true if found. */
250 return r;
251
252 line = mfree(line);
253 r = systemd_efi_options_variable(&line);
254 if (r == -ENODATA)
255 return false; /* Not found */
256 if (r < 0)
257 return r;
258
259 return cmdline_get_key(line, key, flags, ret_value);
260 }
261
262 int proc_cmdline_get_bool(const char *key, bool *ret) {
263 _cleanup_free_ char *v = NULL;
264 int r;
265
266 assert(ret);
267
268 r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
269 if (r < 0)
270 return r;
271 if (r == 0) {
272 *ret = false;
273 return 0;
274 }
275
276 if (v) { /* parameter passed */
277 r = parse_boolean(v);
278 if (r < 0)
279 return r;
280 *ret = r;
281 } else /* no parameter passed */
282 *ret = true;
283
284 return 1;
285 }
286
287 int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
288 _cleanup_free_ char *line = NULL;
289 const char *p;
290 va_list ap;
291 int r, ret = 0;
292
293 /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_get_key_many(), let's make
294 * this clear. */
295 assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
296
297 /* This call may clobber arguments on failure! */
298
299 r = proc_cmdline(&line);
300 if (r < 0)
301 return r;
302
303 p = line;
304 for (;;) {
305 _cleanup_free_ char *word = NULL;
306
307 r = proc_cmdline_extract_first(&p, &word, flags);
308 if (r < 0)
309 return r;
310 if (r == 0)
311 break;
312
313 va_start(ap, flags);
314
315 for (;;) {
316 char **v;
317 const char *k, *e;
318
319 k = va_arg(ap, const char*);
320 if (!k)
321 break;
322
323 assert_se(v = va_arg(ap, char**));
324
325 e = proc_cmdline_key_startswith(word, k);
326 if (e && *e == '=') {
327 r = free_and_strdup(v, e + 1);
328 if (r < 0) {
329 va_end(ap);
330 return r;
331 }
332
333 ret++;
334 }
335 }
336
337 va_end(ap);
338 }
339
340 return ret;
341 }