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