1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "alloc-util.h"
8 #include "extract-word.h"
10 #include "getopt-defs.h"
11 #include "initrd-util.h"
13 #include "parse-util.h"
14 #include "proc-cmdline.h"
15 #include "process-util.h"
16 #include "string-util.h"
20 int proc_cmdline_filter_pid1_args(
21 char **argv
, /* input, may be reordered by this function. */
30 static const struct option options
[] = {
31 COMMON_GETOPT_OPTIONS
,
32 SYSTEMD_GETOPT_OPTIONS
,
33 SHUTDOWN_GETOPT_OPTIONS
,
37 int saved_optind
, saved_opterr
, saved_optopt
, argc
;
45 /* Backup global variables. */
46 saved_optind
= optind
;
47 saved_opterr
= opterr
;
48 saved_optopt
= optopt
;
49 saved_optarg
= optarg
;
51 /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
52 * that checks for GNU extensions in optstring ('-' or '+' at the beginning). Here, we do not use
53 * the GNU extensions, but might be used previously. Hence, we need to always reset it. */
56 /* Do not print an error message. */
59 /* Filter out all known options. */
60 argc
= strv_length(argv
);
61 while (getopt_long(argc
, argv
, SYSTEMD_GETOPT_SHORT_OPTIONS
, options
, NULL
) >= 0)
66 /* Restore global variables. */
67 optind
= saved_optind
;
68 opterr
= saved_opterr
;
69 optopt
= saved_optopt
;
70 optarg
= saved_optarg
;
72 filtered
= strv_copy(strv_skip(argv
, idx
));
80 int proc_cmdline(char **ret
) {
85 /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
86 e
= secure_getenv("SYSTEMD_PROC_CMDLINE");
98 if (detect_container() > 0)
99 return get_process_cmdline(1, SIZE_MAX
, 0, ret
);
101 return read_one_line_file("/proc/cmdline", ret
);
104 static int proc_cmdline_strv_internal(char ***ret
, bool filter_pid1_args
) {
110 /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
111 e
= secure_getenv("SYSTEMD_PROC_CMDLINE");
113 return strv_split_full(ret
, e
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RELAX
|EXTRACT_RETAIN_ESCAPE
);
115 if (detect_container() > 0) {
116 _cleanup_strv_free_
char **args
= NULL
;
118 r
= get_process_cmdline_strv(1, /* flags = */ 0, &args
);
122 if (filter_pid1_args
)
123 return proc_cmdline_filter_pid1_args(args
, ret
);
125 *ret
= TAKE_PTR(args
);
129 _cleanup_free_
char *s
= NULL
;
131 r
= read_one_line_file("/proc/cmdline", &s
);
135 return strv_split_full(ret
, s
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RELAX
|EXTRACT_RETAIN_ESCAPE
);
139 int proc_cmdline_strv(char ***ret
) {
140 return proc_cmdline_strv_internal(ret
, /* filter_pid1_args = */ false);
143 static char *mangle_word(const char *word
, ProcCmdlineFlags flags
) {
146 c
= startswith(word
, "rd.");
148 /* Filter out arguments that are intended only for the initrd */
153 if (FLAGS_SET(flags
, PROC_CMDLINE_STRIP_RD_PREFIX
))
156 } else if (FLAGS_SET(flags
, PROC_CMDLINE_RD_STRICT
) && in_initrd())
157 /* And optionally filter out arguments that are intended only for the host */
163 static int proc_cmdline_parse_strv(char **args
, proc_cmdline_parse_t parse_item
, void *data
, ProcCmdlineFlags flags
) {
168 /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_parse(), let's
169 * make this clear. */
170 assert(!FLAGS_SET(flags
, PROC_CMDLINE_VALUE_OPTIONAL
));
172 STRV_FOREACH(word
, args
) {
175 key
= mangle_word(*word
, flags
);
179 value
= strchr(key
, '=');
183 r
= parse_item(key
, value
, data
);
191 int proc_cmdline_parse(proc_cmdline_parse_t parse_item
, void *data
, ProcCmdlineFlags flags
) {
192 _cleanup_strv_free_
char **args
= NULL
;
197 /* We parse the EFI variable first, because later settings have higher priority. */
199 if (!FLAGS_SET(flags
, PROC_CMDLINE_IGNORE_EFI_OPTIONS
)) {
200 _cleanup_free_
char *line
= NULL
;
202 r
= systemd_efi_options_variable(&line
);
205 log_debug_errno(r
, "Failed to get SystemdOptions EFI variable, ignoring: %m");
207 r
= strv_split_full(&args
, line
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RELAX
|EXTRACT_RETAIN_ESCAPE
);
211 r
= proc_cmdline_parse_strv(args
, parse_item
, data
, flags
);
215 args
= strv_free(args
);
219 r
= proc_cmdline_strv_internal(&args
, /* filter_pid1_args = */ true);
223 return proc_cmdline_parse_strv(args
, parse_item
, data
, flags
);
226 static bool relaxed_equal_char(char a
, char b
) {
228 (a
== '_' && b
== '-') ||
229 (a
== '-' && b
== '_');
232 char *proc_cmdline_key_startswith(const char *s
, const char *prefix
) {
236 /* Much like startswith(), but considers "-" and "_" the same */
238 for (; *prefix
!= 0; s
++, prefix
++)
239 if (!relaxed_equal_char(*s
, *prefix
))
245 bool proc_cmdline_key_streq(const char *x
, const char *y
) {
249 /* Much like streq(), but considers "-" and "_" the same */
251 for (; *x
!= 0 || *y
!= 0; x
++, y
++)
252 if (!relaxed_equal_char(*x
, *y
))
258 static int cmdline_get_key(char **args
, const char *key
, ProcCmdlineFlags flags
, char **ret_value
) {
259 _cleanup_free_
char *v
= NULL
;
265 STRV_FOREACH(p
, args
) {
268 word
= mangle_word(*p
, flags
);
275 e
= proc_cmdline_key_startswith(word
, key
);
280 r
= free_and_strdup(&v
, e
+1);
286 } else if (*e
== 0 && FLAGS_SET(flags
, PROC_CMDLINE_VALUE_OPTIONAL
))
290 if (proc_cmdline_key_streq(word
, key
)) {
292 break; /* we found what we were looking for */
298 *ret_value
= TAKE_PTR(v
);
303 int proc_cmdline_get_key(const char *key
, ProcCmdlineFlags flags
, char **ret_value
) {
304 _cleanup_strv_free_
char **args
= NULL
;
305 _cleanup_free_
char *line
= NULL
, *v
= NULL
;
308 /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable.
309 * Supports three modes:
311 * a) The "ret_value" parameter is used. In this case a parameter beginning with the "key" string followed by
312 * "=" is searched for, and the value following it is returned in "ret_value".
314 * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a separate
315 * word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then this is
316 * also accepted, and "value" is returned as NULL.
318 * c) The "ret_value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
320 * In all three cases, > 0 is returned if the key is found, 0 if not. */
325 if (FLAGS_SET(flags
, PROC_CMDLINE_VALUE_OPTIONAL
) && !ret_value
)
328 r
= proc_cmdline_strv_internal(&args
, /* filter_pid1_args = */ true);
332 if (FLAGS_SET(flags
, PROC_CMDLINE_IGNORE_EFI_OPTIONS
)) /* Shortcut */
333 return cmdline_get_key(args
, key
, flags
, ret_value
);
335 r
= cmdline_get_key(args
, key
, flags
, ret_value
? &v
: NULL
);
340 *ret_value
= TAKE_PTR(v
);
345 r
= systemd_efi_options_variable(&line
);
350 return false; /* Not found */
355 args
= strv_free(args
);
356 r
= strv_split_full(&args
, line
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RELAX
|EXTRACT_RETAIN_ESCAPE
);
360 return cmdline_get_key(args
, key
, flags
, ret_value
);
363 int proc_cmdline_get_bool(const char *key
, bool *ret
) {
364 _cleanup_free_
char *v
= NULL
;
369 r
= proc_cmdline_get_key(key
, PROC_CMDLINE_VALUE_OPTIONAL
, &v
);
372 if (r
== 0) { /* key not specified at all */
377 if (v
) { /* key with parameter passed */
378 r
= parse_boolean(v
);
382 } else /* key without parameter passed */
388 static int cmdline_get_key_ap(ProcCmdlineFlags flags
, char* const* args
, va_list ap
) {
395 k
= va_arg(ap
, const char*);
399 assert_se(v
= va_arg(ap
, char**));
401 STRV_FOREACH(p
, args
) {
404 word
= mangle_word(*p
, flags
);
408 e
= proc_cmdline_key_startswith(word
, k
);
409 if (e
&& *e
== '=') {
410 r
= free_and_strdup(v
, e
+ 1);
422 int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags
, ...) {
423 _cleanup_strv_free_
char **args
= NULL
;
427 /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_get_key_many(), let's make
429 assert(!FLAGS_SET(flags
, PROC_CMDLINE_VALUE_OPTIONAL
));
431 /* This call may clobber arguments on failure! */
433 if (!FLAGS_SET(flags
, PROC_CMDLINE_IGNORE_EFI_OPTIONS
)) {
434 _cleanup_free_
char *line
= NULL
;
436 r
= systemd_efi_options_variable(&line
);
437 if (r
< 0 && r
!= -ENODATA
)
438 log_debug_errno(r
, "Failed to get SystemdOptions EFI variable, ignoring: %m");
440 r
= strv_split_full(&args
, line
, NULL
, EXTRACT_UNQUOTE
|EXTRACT_RELAX
|EXTRACT_RETAIN_ESCAPE
);
445 r
= cmdline_get_key_ap(flags
, args
, ap
);
451 args
= strv_free(args
);
455 r
= proc_cmdline_strv(&args
);
460 r
= cmdline_get_key_ap(flags
, args
, ap
);