Branch data 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 : 1798 : int proc_cmdline(char **ret) {
20 : : const char *e;
21 [ - + ]: 1798 : assert(ret);
22 : :
23 : : /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
24 : 1798 : e = secure_getenv("SYSTEMD_PROC_CMDLINE");
25 [ + + ]: 1798 : if (e) {
26 : : char *m;
27 : :
28 : 168 : m = strdup(e);
29 [ - + ]: 168 : if (!m)
30 : 0 : return -ENOMEM;
31 : :
32 : 168 : *ret = m;
33 : 168 : return 0;
34 : : }
35 : :
36 [ - + ]: 1630 : if (detect_container() > 0)
37 : 0 : return get_process_cmdline(1, SIZE_MAX, 0, ret);
38 : : else
39 : 1630 : return read_one_line_file("/proc/cmdline", ret);
40 : : }
41 : :
42 : 12338 : static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
43 : 12338 : const char *q = *p;
44 : : int r;
45 : :
46 : 4850 : for (;;) {
47 [ + + + ]: 17188 : _cleanup_free_ char *word = NULL;
48 : : const char *c;
49 : :
50 : 17188 : r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
51 [ - + ]: 17188 : if (r < 0)
52 : 0 : return r;
53 [ + + ]: 17188 : if (r == 0)
54 : 1766 : break;
55 : :
56 : : /* Filter out arguments that are intended only for the initrd */
57 : 15422 : c = startswith(word, "rd.");
58 [ + + ]: 15422 : if (c) {
59 [ + + ]: 4858 : if (!in_initrd())
60 : 4850 : continue;
61 : :
62 [ + + ]: 8 : if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX)) {
63 : 4 : r = free_and_strdup(&word, c);
64 [ - + ]: 4 : if (r < 0)
65 : 0 : return r;
66 : : }
67 : :
68 [ - + # # ]: 10564 : } 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 : 10572 : *p = q;
72 : 10572 : *ret_word = TAKE_PTR(word);
73 : 10572 : return 1;
74 : : }
75 : :
76 : 1766 : *p = q;
77 : 1766 : *ret_word = NULL;
78 : 1766 : return 0;
79 : : }
80 : :
81 : 1626 : 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 [ - + ]: 1626 : 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 [ - + ]: 1626 : assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
90 : :
91 : 1626 : p = line;
92 : 9732 : for (;;) {
93 [ + - + ]: 11358 : _cleanup_free_ char *word = NULL;
94 : : char *value;
95 : :
96 : 11358 : r = proc_cmdline_extract_first(&p, &word, flags);
97 [ - + ]: 11358 : if (r < 0)
98 : 0 : return r;
99 [ + + ]: 11358 : if (r == 0)
100 : 1626 : break;
101 : :
102 : 9732 : value = strchr(word, '=');
103 [ + + ]: 9732 : if (value)
104 : 4894 : *(value++) = 0;
105 : :
106 : 9732 : r = parse_item(word, value, data);
107 [ - + ]: 9732 : if (r < 0)
108 : 0 : return r;
109 : : }
110 : :
111 : 1626 : return 0;
112 : : }
113 : :
114 : 1610 : int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
115 : 1610 : _cleanup_free_ char *line = NULL;
116 : : int r;
117 : :
118 [ - + ]: 1610 : assert(parse_item);
119 : :
120 : 1610 : r = proc_cmdline(&line);
121 [ - + ]: 1610 : if (r < 0)
122 : 0 : return r;
123 : :
124 : 1610 : return proc_cmdline_parse_given(line, parse_item, data, flags);
125 : : }
126 : :
127 : 41088 : static bool relaxed_equal_char(char a, char b) {
128 [ + + ]: 39548 : return a == b ||
129 [ + + - + : 80688 : (a == '_' && b == '-') ||
+ + ]
130 [ + - ]: 52 : (a == '-' && b == '_');
131 : : }
132 : :
133 : 944 : char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
134 [ - + ]: 944 : assert(s);
135 [ - + ]: 944 : assert(prefix);
136 : :
137 : : /* Much like startswith(), but considers "-" and "_" the same */
138 : :
139 [ + + ]: 1988 : for (; *prefix != 0; s++, prefix++)
140 [ + + ]: 1824 : if (!relaxed_equal_char(*s, *prefix))
141 : 780 : return NULL;
142 : :
143 : 164 : return (char*) s;
144 : : }
145 : :
146 : 38792 : bool proc_cmdline_key_streq(const char *x, const char *y) {
147 [ - + ]: 38792 : assert(x);
148 [ - + ]: 38792 : assert(y);
149 : :
150 : : /* Much like streq(), but considers "-" and "_" the same */
151 : :
152 [ + + + + ]: 39368 : for (; *x != 0 || *y != 0; x++, y++)
153 [ + + ]: 39264 : if (!relaxed_equal_char(*x, *y))
154 : 38688 : return false;
155 : :
156 : 104 : return true;
157 : : }
158 : :
159 : 156 : int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
160 : 156 : _cleanup_free_ char *line = NULL, *ret = NULL;
161 : 156 : 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 [ + + ]: 156 : if (isempty(key))
179 : 8 : return -EINVAL;
180 : :
181 [ + + + + ]: 148 : if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
182 : 8 : return -EINVAL;
183 : :
184 : 140 : r = proc_cmdline(&line);
185 [ - + ]: 140 : if (r < 0)
186 : 0 : return r;
187 : :
188 : 140 : p = line;
189 : 812 : for (;;) {
190 [ + - + + ]: 952 : _cleanup_free_ char *word = NULL;
191 : :
192 : 952 : r = proc_cmdline_extract_first(&p, &word, flags);
193 [ - + ]: 952 : if (r < 0)
194 : 0 : return r;
195 [ + + ]: 952 : if (r == 0)
196 : 136 : break;
197 : :
198 [ + + ]: 816 : if (ret_value) {
199 : : const char *e;
200 : :
201 : 732 : e = proc_cmdline_key_startswith(word, key);
202 [ + + ]: 732 : if (!e)
203 : 624 : continue;
204 : :
205 [ + + ]: 108 : if (*e == '=') {
206 : 92 : r = free_and_strdup(&ret, e+1);
207 [ - + ]: 92 : if (r < 0)
208 : 0 : return r;
209 : :
210 : 92 : found = true;
211 : :
212 [ + - + + ]: 16 : } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
213 : 12 : found = true;
214 : :
215 : : } else {
216 [ + + ]: 84 : if (streq(word, key)) {
217 : 4 : found = true;
218 : 4 : break; /* we found what we were looking for */
219 : : }
220 : : }
221 : : }
222 : :
223 [ + + ]: 140 : if (ret_value)
224 : 124 : *ret_value = TAKE_PTR(ret);
225 : :
226 : 140 : return found;
227 : : }
228 : :
229 : 56 : int proc_cmdline_get_bool(const char *key, bool *ret) {
230 : 56 : _cleanup_free_ char *v = NULL;
231 : : int r;
232 : :
233 [ - + ]: 56 : assert(ret);
234 : :
235 : 56 : r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
236 [ + + ]: 56 : if (r < 0)
237 : 4 : return r;
238 [ + + ]: 52 : if (r == 0) {
239 : 8 : *ret = false;
240 : 8 : return 0;
241 : : }
242 : :
243 [ + + ]: 44 : if (v) { /* parameter passed */
244 : 36 : r = parse_boolean(v);
245 [ + + ]: 36 : if (r < 0)
246 : 4 : return r;
247 : 32 : *ret = r;
248 : : } else /* no parameter passed */
249 : 8 : *ret = true;
250 : :
251 : 40 : return 1;
252 : : }
253 : :
254 : 4 : int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
255 : 4 : _cleanup_free_ char *line = NULL;
256 : : const char *p;
257 : : va_list ap;
258 : 4 : 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 [ - + ]: 4 : assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
263 : :
264 : : /* This call may clobber arguments on failure! */
265 : :
266 : 4 : r = proc_cmdline(&line);
267 [ - + ]: 4 : if (r < 0)
268 : 0 : return r;
269 : :
270 : 4 : p = line;
271 : 24 : for (;;) {
272 [ + - + ]: 28 : _cleanup_free_ char *word = NULL;
273 : :
274 : 28 : r = proc_cmdline_extract_first(&p, &word, flags);
275 [ - + ]: 28 : if (r < 0)
276 : 0 : return r;
277 [ + + ]: 28 : if (r == 0)
278 : 4 : break;
279 : :
280 : 24 : va_start(ap, flags);
281 : :
282 : 168 : for (;;) {
283 : : char **v;
284 : : const char *k, *e;
285 : :
286 : 192 : k = va_arg(ap, const char*);
287 [ + + ]: 192 : if (!k)
288 : 24 : break;
289 : :
290 [ - + ]: 168 : assert_se(v = va_arg(ap, char**));
291 : :
292 : 168 : e = proc_cmdline_key_startswith(word, k);
293 [ + + + + ]: 168 : if (e && *e == '=') {
294 : 20 : r = free_and_strdup(v, e + 1);
295 [ - + ]: 20 : if (r < 0) {
296 : 0 : va_end(ap);
297 : 0 : return r;
298 : : }
299 : :
300 : 20 : ret++;
301 : : }
302 : : }
303 : :
304 : 24 : va_end(ap);
305 : : }
306 : :
307 : 4 : 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 : 40 : const char* runlevel_to_target(const char *word) {
344 : : const char * const *rlmap_ptr;
345 : : size_t i;
346 : :
347 [ + + ]: 40 : if (!word)
348 : 8 : return NULL;
349 : :
350 [ + + ]: 32 : if (in_initrd()) {
351 : 16 : word = startswith(word, "rd.");
352 [ + + ]: 16 : if (!word)
353 : 8 : return NULL;
354 : : }
355 : :
356 [ + + ]: 24 : rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
357 : :
358 [ + + ]: 216 : for (i = 0; rlmap_ptr[i]; i += 2)
359 [ + + ]: 200 : if (streq(word, rlmap_ptr[i]))
360 : 8 : return rlmap_ptr[i+1];
361 : :
362 : 16 : return NULL;
363 : : }
|