]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/proc-cmdline.c
basic/process-util: convert bool arg to flags
[thirdparty/systemd.git] / src / basic / proc-cmdline.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
4e731273 2
11c3a366
TA
3#include <stdbool.h>
4#include <stddef.h>
5#include <string.h>
6
b5efdb8a 7#include "alloc-util.h"
4e731273
LP
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"
c573dcfe 14#include "special.h"
4e731273
LP
15#include "string-util.h"
16#include "util.h"
17#include "virt.h"
18
19int proc_cmdline(char **ret) {
2467cc55 20 const char *e;
4e731273
LP
21 assert(ret);
22
2467cc55
LP
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
4e731273 36 if (detect_container() > 0)
09c1dcee 37 return get_process_cmdline(1, SIZE_MAX, 0, ret);
4e731273
LP
38 else
39 return read_one_line_file("/proc/cmdline", ret);
40}
41
1e7a5996
LP
42static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
43 const char *q = *p;
4e731273
LP
44 int r;
45
4e731273
LP
46 for (;;) {
47 _cleanup_free_ char *word = NULL;
1e7a5996 48 const char *c;
4e731273 49
1e7a5996 50 r = extract_first_word(&q, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
4e731273
LP
51 if (r < 0)
52 return r;
53 if (r == 0)
54 break;
55
1d84ad94 56 /* Filter out arguments that are intended only for the initrd */
1e7a5996
LP
57 c = startswith(word, "rd.");
58 if (c) {
1d84ad94
LP
59 if (!in_initrd())
60 continue;
61
1e7a5996
LP
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 }
cb447ff5
LP
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 */
4e731273 70
1e7a5996
LP
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
81int 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, '=');
4e731273
LP
103 if (value)
104 *(value++) = 0;
105
1e7a5996 106 r = parse_item(word, value, data);
4e731273
LP
107 if (r < 0)
108 return r;
109 }
110
111 return 0;
112}
113
f6dd5e7c 114int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
9a135c08
ZJS
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
1d84ad94 127static bool relaxed_equal_char(char a, char b) {
1d84ad94
LP
128 return a == b ||
129 (a == '_' && b == '-') ||
130 (a == '-' && b == '_');
131}
132
133char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
1d84ad94
LP
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
146bool 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
1e7a5996 159int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
4e731273
LP
160 _cleanup_free_ char *line = NULL, *ret = NULL;
161 bool found = false;
162 const char *p;
163 int r;
164
1e7a5996 165 /* Looks for a specific key on the kernel command line. Supports three modes:
1d84ad94 166 *
1e7a5996
LP
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".
1d84ad94 169 *
1e7a5996
LP
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.
1d84ad94 173 *
1e7a5996 174 * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
1d84ad94 175 *
13e785f7 176 * In all three cases, > 0 is returned if the key is found, 0 if not. */
1d84ad94
LP
177
178 if (isempty(key))
179 return -EINVAL;
180
1e7a5996 181 if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
1d84ad94 182 return -EINVAL;
4e731273
LP
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;
4e731273 191
1e7a5996 192 r = proc_cmdline_extract_first(&p, &word, flags);
4e731273
LP
193 if (r < 0)
194 return r;
195 if (r == 0)
196 break;
197
1e7a5996
LP
198 if (ret_value) {
199 const char *e;
4e731273 200
1e7a5996 201 e = proc_cmdline_key_startswith(word, key);
4e731273
LP
202 if (!e)
203 continue;
204
1d84ad94
LP
205 if (*e == '=') {
206 r = free_and_strdup(&ret, e+1);
207 if (r < 0)
208 return r;
209
210 found = true;
211
cb447ff5 212 } else if (*e == 0 && FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL))
1d84ad94 213 found = true;
4e731273 214
4e731273 215 } else {
1e7a5996 216 if (streq(word, key)) {
4e731273 217 found = true;
1e7a5996
LP
218 break; /* we found what we were looking for */
219 }
4e731273
LP
220 }
221 }
222
1e7a5996
LP
223 if (ret_value)
224 *ret_value = TAKE_PTR(ret);
4e731273
LP
225
226 return found;
1d84ad94
LP
227}
228
229int 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;
4e731273 250
1d84ad94 251 return 1;
4e731273
LP
252}
253
78b30ee0
LP
254int 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
4e731273 310int shall_restore_state(void) {
1d84ad94 311 bool ret;
4e731273
LP
312 int r;
313
1d84ad94 314 r = proc_cmdline_get_bool("systemd.restore_state", &ret);
4e731273
LP
315 if (r < 0)
316 return r;
4e731273 317
1d84ad94 318 return r > 0 ? ret : true;
4e731273 319}
c573dcfe
EV
320
321static 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,
dcd61450
IS
334 NULL
335};
336
337static const char * const rlmap_initrd[] = {
338 "emergency", SPECIAL_EMERGENCY_TARGET,
339 "rescue", SPECIAL_RESCUE_TARGET,
340 NULL
c573dcfe
EV
341};
342
343const char* runlevel_to_target(const char *word) {
11f5d825 344 const char * const *rlmap_ptr;
c573dcfe
EV
345 size_t i;
346
347 if (!word)
348 return NULL;
349
11f5d825
LP
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;
dcd61450 357
11f5d825 358 for (i = 0; rlmap_ptr[i]; i += 2)
dcd61450
IS
359 if (streq(word, rlmap_ptr[i]))
360 return rlmap_ptr[i+1];
c573dcfe
EV
361
362 return NULL;
363}