]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/proc-cmdline.c
proc-cmdline: split out rd. prefix handling in proc_cmdline_parse_given() and proc_cm...
[thirdparty/systemd.git] / src / basic / proc-cmdline.c
... / ...
CommitLineData
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
19int 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, 0, false, ret);
38 else
39 return read_one_line_file("/proc/cmdline", ret);
40}
41
42static 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
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, '=');
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
114int 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
127static bool relaxed_equal_char(char a, char b) {
128 return a == b ||
129 (a == '_' && b == '-') ||
130 (a == '-' && b == '_');
131}
132
133char *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
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
159int 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
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;
250
251 return 1;
252}
253
254int shall_restore_state(void) {
255 bool ret;
256 int r;
257
258 r = proc_cmdline_get_bool("systemd.restore_state", &ret);
259 if (r < 0)
260 return r;
261
262 return r > 0 ? ret : true;
263}
264
265static const char * const rlmap[] = {
266 "emergency", SPECIAL_EMERGENCY_TARGET,
267 "-b", SPECIAL_EMERGENCY_TARGET,
268 "rescue", SPECIAL_RESCUE_TARGET,
269 "single", SPECIAL_RESCUE_TARGET,
270 "-s", SPECIAL_RESCUE_TARGET,
271 "s", SPECIAL_RESCUE_TARGET,
272 "S", SPECIAL_RESCUE_TARGET,
273 "1", SPECIAL_RESCUE_TARGET,
274 "2", SPECIAL_MULTI_USER_TARGET,
275 "3", SPECIAL_MULTI_USER_TARGET,
276 "4", SPECIAL_MULTI_USER_TARGET,
277 "5", SPECIAL_GRAPHICAL_TARGET,
278 NULL
279};
280
281static const char * const rlmap_initrd[] = {
282 "emergency", SPECIAL_EMERGENCY_TARGET,
283 "rescue", SPECIAL_RESCUE_TARGET,
284 NULL
285};
286
287const char* runlevel_to_target(const char *word) {
288 const char * const *rlmap_ptr;
289 size_t i;
290
291 if (!word)
292 return NULL;
293
294 if (in_initrd()) {
295 word = startswith(word, "rd.");
296 if (!word)
297 return NULL;
298 }
299
300 rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
301
302 for (i = 0; rlmap_ptr[i]; i += 2)
303 if (streq(word, rlmap_ptr[i]))
304 return rlmap_ptr[i+1];
305
306 return NULL;
307}