]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/proc-cmdline.c
7dca9e60b613f04777922072a09b09110856d69c
[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 #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 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, false, 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_QUOTES|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 r = proc_cmdline(&line);
121 if (r < 0)
122 return r;
123
124 return proc_cmdline_parse_given(line, parse_item, data, flags);
125 }
126
127 static bool relaxed_equal_char(char a, char b) {
128 return a == b ||
129 (a == '_' && b == '-') ||
130 (a == '-' && b == '_');
131 }
132
133 char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
134 assert(s);
135 assert(prefix);
136
137 /* Much like startswith(), but considers "-" and "_" the same */
138
139 for (; *prefix != 0; s++, prefix++)
140 if (!relaxed_equal_char(*s, *prefix))
141 return NULL;
142
143 return (char*) s;
144 }
145
146 bool proc_cmdline_key_streq(const char *x, const char *y) {
147 assert(x);
148 assert(y);
149
150 /* Much like streq(), but considers "-" and "_" the same */
151
152 for (; *x != 0 || *y != 0; x++, y++)
153 if (!relaxed_equal_char(*x, *y))
154 return false;
155
156 return true;
157 }
158
159 int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
160 _cleanup_free_ char *line = NULL, *ret = NULL;
161 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 if (isempty(key))
179 return -EINVAL;
180
181 if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
182 return -EINVAL;
183
184 r = proc_cmdline(&line);
185 if (r < 0)
186 return r;
187
188 p = line;
189 for (;;) {
190 _cleanup_free_ char *word = NULL;
191
192 r = proc_cmdline_extract_first(&p, &word, flags);
193 if (r < 0)
194 return r;
195 if (r == 0)
196 break;
197
198 if (ret_value) {
199 const char *e;
200
201 e = proc_cmdline_key_startswith(word, key);
202 if (!e)
203 continue;
204
205 if (*e == '=') {
206 r = free_and_strdup(&ret, e+1);
207 if (r < 0)
208 return r;
209
210 found = true;
211
212 } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
213 found = true;
214
215 } else {
216 if (streq(word, key)) {
217 found = true;
218 break; /* we found what we were looking for */
219 }
220 }
221 }
222
223 if (ret_value)
224 *ret_value = TAKE_PTR(ret);
225
226 return found;
227 }
228
229 int proc_cmdline_get_bool(const char *key, bool *ret) {
230 _cleanup_free_ char *v = NULL;
231 int r;
232
233 assert(ret);
234
235 r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
236 if (r < 0)
237 return r;
238 if (r == 0) {
239 *ret = false;
240 return 0;
241 }
242
243 if (v) { /* parameter passed */
244 r = parse_boolean(v);
245 if (r < 0)
246 return r;
247 *ret = r;
248 } else /* no parameter passed */
249 *ret = true;
250
251 return 1;
252 }
253
254 int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
255 _cleanup_free_ char *line = NULL;
256 const char *p;
257 va_list ap;
258 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 assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
263
264 /* This call may clobber arguments on failure! */
265
266 r = proc_cmdline(&line);
267 if (r < 0)
268 return r;
269
270 p = line;
271 for (;;) {
272 _cleanup_free_ char *word = NULL;
273
274 r = proc_cmdline_extract_first(&p, &word, flags);
275 if (r < 0)
276 return r;
277 if (r == 0)
278 break;
279
280 va_start(ap, flags);
281
282 for (;;) {
283 char **v;
284 const char *k, *e;
285
286 k = va_arg(ap, const char*);
287 if (!k)
288 break;
289
290 assert_se(v = va_arg(ap, char**));
291
292 e = proc_cmdline_key_startswith(word, k);
293 if (e && *e == '=') {
294 r = free_and_strdup(v, e + 1);
295 if (r < 0) {
296 va_end(ap);
297 return r;
298 }
299
300 ret++;
301 }
302 }
303
304 va_end(ap);
305 }
306
307 return ret;
308 }
309
310 int shall_restore_state(void) {
311 bool ret;
312 int r;
313
314 r = proc_cmdline_get_bool("systemd.restore_state", &ret);
315 if (r < 0)
316 return r;
317
318 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 const char* runlevel_to_target(const char *word) {
344 const char * const *rlmap_ptr;
345 size_t i;
346
347 if (!word)
348 return NULL;
349
350 if (in_initrd()) {
351 word = startswith(word, "rd.");
352 if (!word)
353 return NULL;
354 }
355
356 rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
357
358 for (i = 0; rlmap_ptr[i]; i += 2)
359 if (streq(word, rlmap_ptr[i]))
360 return rlmap_ptr[i+1];
361
362 return NULL;
363 }