summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2023-04-07 10:55:30 +0200
committerGitHub <noreply@github.com>2023-04-07 10:55:30 +0200
commitddd43f31e3be45a276ec4191eace333f844b98b8 (patch)
treeb92573d9b4beb6a6e3f049993f27349273110e40
parent9c1d3d6fa7f00673c0f650139cf5e9a3d4f37827 (diff)
parent6339d3e6021f31a8a8907c2613f1aaac279fe745 (diff)
downloadsystemd-ddd43f31e3be45a276ec4191eace333f844b98b8.tar.gz
Merge pull request #26887 from yuwata/proc-cmdline-filter-arguments
proc-cmdline: filter PID1 arguments on container
-rw-r--r--src/activate/activate.c3
-rw-r--r--src/ask-password/ask-password.c4
-rw-r--r--src/basic/getopt-defs.h75
-rw-r--r--src/basic/proc-cmdline.c317
-rw-r--r--src/basic/proc-cmdline.h3
-rw-r--r--src/cgls/cgls.c3
-rw-r--r--src/core/main.c60
-rw-r--r--src/journal/cat.c3
-rw-r--r--src/login/inhibit.c3
-rw-r--r--src/machine/machinectl.c4
-rw-r--r--src/nspawn/nspawn.c3
-rw-r--r--src/run/run.c3
-rw-r--r--src/shared/condition.c17
-rw-r--r--src/shutdown/shutdown.c23
-rw-r--r--src/test/test-proc-cmdline.c52
-rw-r--r--src/udev/udevadm-lock.c3
-rw-r--r--src/udev/udevadm.c3
-rw-r--r--src/userdb/userdbctl.c4
18 files changed, 389 insertions, 194 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 4a63970326..1caa30d7d4 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -347,6 +347,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
switch (c) {
case 'h':
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index c02e3b42fe..de41d7b641 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -108,6 +108,10 @@ static int parse_argv(int argc, char *argv[]) {
/* Note the asymmetry: the long option --echo= allows an optional argument, the short option does
* not. */
+
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+hen", options, NULL)) >= 0)
switch (c) {
diff --git a/src/basic/getopt-defs.h b/src/basic/getopt-defs.h
new file mode 100644
index 0000000000..3efeb6df80
--- /dev/null
+++ b/src/basic/getopt-defs.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <getopt.h>
+
+#define SYSTEMD_GETOPT_SHORT_OPTIONS "hDbsz:"
+
+#define COMMON_GETOPT_ARGS \
+ ARG_LOG_LEVEL = 0x100, \
+ ARG_LOG_TARGET, \
+ ARG_LOG_COLOR, \
+ ARG_LOG_LOCATION, \
+ ARG_LOG_TIME
+
+#define SYSTEMD_GETOPT_ARGS \
+ ARG_UNIT, \
+ ARG_SYSTEM, \
+ ARG_USER, \
+ ARG_TEST, \
+ ARG_NO_PAGER, \
+ ARG_VERSION, \
+ ARG_DUMP_CONFIGURATION_ITEMS, \
+ ARG_DUMP_BUS_PROPERTIES, \
+ ARG_BUS_INTROSPECT, \
+ ARG_DUMP_CORE, \
+ ARG_CRASH_CHVT, \
+ ARG_CRASH_SHELL, \
+ ARG_CRASH_REBOOT, \
+ ARG_CONFIRM_SPAWN, \
+ ARG_SHOW_STATUS, \
+ ARG_DESERIALIZE, \
+ ARG_SWITCHED_ROOT, \
+ ARG_DEFAULT_STD_OUTPUT, \
+ ARG_DEFAULT_STD_ERROR, \
+ ARG_MACHINE_ID, \
+ ARG_SERVICE_WATCHDOGS
+
+#define SHUTDOWN_GETOPT_ARGS \
+ ARG_EXIT_CODE, \
+ ARG_TIMEOUT
+
+#define COMMON_GETOPT_OPTIONS \
+ { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, \
+ { "log-target", required_argument, NULL, ARG_LOG_TARGET }, \
+ { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, \
+ { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, \
+ { "log-time", optional_argument, NULL, ARG_LOG_TIME }
+
+#define SYSTEMD_GETOPT_OPTIONS \
+ { "unit", required_argument, NULL, ARG_UNIT }, \
+ { "system", no_argument, NULL, ARG_SYSTEM }, \
+ { "user", no_argument, NULL, ARG_USER }, \
+ { "test", no_argument, NULL, ARG_TEST }, \
+ { "no-pager", no_argument, NULL, ARG_NO_PAGER }, \
+ { "help", no_argument, NULL, 'h' }, \
+ { "version", no_argument, NULL, ARG_VERSION }, \
+ { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS }, \
+ { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES }, \
+ { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT }, \
+ { "dump-core", optional_argument, NULL, ARG_DUMP_CORE }, \
+ { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT }, \
+ { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL }, \
+ { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT }, \
+ { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN }, \
+ { "show-status", optional_argument, NULL, ARG_SHOW_STATUS }, \
+ { "deserialize", required_argument, NULL, ARG_DESERIALIZE }, \
+ { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT }, \
+ { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, \
+ { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, \
+ { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, \
+ { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS }
+
+#define SHUTDOWN_GETOPT_OPTIONS \
+ { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, \
+ { "timeout", required_argument, NULL, ARG_TIMEOUT }
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index de1f66635a..39e9f2c668 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -7,15 +7,76 @@
#include "efivars.h"
#include "extract-word.h"
#include "fileio.h"
+#include "getopt-defs.h"
#include "initrd-util.h"
#include "macro.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
-#include "special.h"
#include "string-util.h"
+#include "strv.h"
#include "virt.h"
+int proc_cmdline_filter_pid1_args(
+ char **argv, /* input, may be reordered by this function. */
+ char ***ret) {
+
+ enum {
+ COMMON_GETOPT_ARGS,
+ SYSTEMD_GETOPT_ARGS,
+ SHUTDOWN_GETOPT_ARGS,
+ };
+
+ static const struct option options[] = {
+ COMMON_GETOPT_OPTIONS,
+ SYSTEMD_GETOPT_OPTIONS,
+ SHUTDOWN_GETOPT_OPTIONS,
+ {}
+ };
+
+ int saved_optind, saved_opterr, saved_optopt, argc;
+ char *saved_optarg;
+ char **filtered;
+ size_t idx;
+
+ assert(argv);
+ assert(ret);
+
+ /* Backup global variables. */
+ saved_optind = optind;
+ saved_opterr = opterr;
+ saved_optopt = optopt;
+ saved_optarg = optarg;
+
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). Here, we do not use
+ * the GNU extensions, but might be used previously. Hence, we need to always reset it. */
+ optind = 0;
+
+ /* Do not print an error message. */
+ opterr = 0;
+
+ /* Filter out all known options. */
+ argc = strv_length(argv);
+ while (getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL) >= 0)
+ ;
+
+ idx = optind;
+
+ /* Restore global variables. */
+ optind = saved_optind;
+ opterr = saved_opterr;
+ optopt = saved_optopt;
+ optarg = saved_optarg;
+
+ filtered = strv_copy(strv_skip(argv, idx));
+ if (!filtered)
+ return -ENOMEM;
+
+ *ret = filtered;
+ return 0;
+}
+
int proc_cmdline(char **ret) {
const char *e;
@@ -40,71 +101,86 @@ int proc_cmdline(char **ret) {
return read_one_line_file("/proc/cmdline", ret);
}
-static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdlineFlags flags) {
- const char *q = *p;
+static int proc_cmdline_strv_internal(char ***ret, bool filter_pid1_args) {
+ const char *e;
int r;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- const char *c;
+ assert(ret);
- r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
+ /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
+ e = secure_getenv("SYSTEMD_PROC_CMDLINE");
+ if (e)
+ return strv_split_full(ret, e, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
+
+ if (detect_container() > 0) {
+ _cleanup_strv_free_ char **args = NULL;
+
+ r = get_process_cmdline_strv(1, /* flags = */ 0, &args);
if (r < 0)
return r;
- if (r == 0)
- break;
- /* Filter out arguments that are intended only for the initrd */
- c = startswith(word, "rd.");
- if (c) {
- if (!in_initrd())
- continue;
+ if (filter_pid1_args)
+ return proc_cmdline_filter_pid1_args(args, ret);
- if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX)) {
- r = free_and_strdup(&word, c);
- if (r < 0)
- return r;
- }
+ *ret = TAKE_PTR(args);
+ return 0;
- } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
- continue; /* And optionally filter out arguments that are intended only for the host */
+ } else {
+ _cleanup_free_ char *s = NULL;
+
+ r = read_one_line_file("/proc/cmdline", &s);
+ if (r < 0)
+ return r;
- *p = q;
- *ret_word = TAKE_PTR(word);
- return 1;
+ return strv_split_full(ret, s, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
}
+}
- *p = q;
- *ret_word = NULL;
- return 0;
+int proc_cmdline_strv(char ***ret) {
+ return proc_cmdline_strv_internal(ret, /* filter_pid1_args = */ false);
+}
+
+static char *mangle_word(const char *word, ProcCmdlineFlags flags) {
+ char *c;
+
+ c = startswith(word, "rd.");
+ if (c) {
+ /* Filter out arguments that are intended only for the initrd */
+
+ if (!in_initrd())
+ return NULL;
+
+ if (FLAGS_SET(flags, PROC_CMDLINE_STRIP_RD_PREFIX))
+ return c;
+
+ } else if (FLAGS_SET(flags, PROC_CMDLINE_RD_STRICT) && in_initrd())
+ /* And optionally filter out arguments that are intended only for the host */
+ return NULL;
+
+ return (char*) word;
}
-static int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
- const char *p;
+static int proc_cmdline_parse_strv(char **args, proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
int r;
assert(parse_item);
- /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_parse(), let's make this
- * clear. */
+ /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_parse(), let's
+ * make this clear. */
assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
- char *value;
+ STRV_FOREACH(word, args) {
+ char *key, *value;
- r = proc_cmdline_extract_first(&p, &word, flags);
- if (r < 0)
- return r;
- if (r == 0)
- break;
+ key = mangle_word(*word, flags);
+ if (!key)
+ continue;
- value = strchr(word, '=');
+ value = strchr(key, '=');
if (value)
- *(value++) = 0;
+ *(value++) = '\0';
- r = parse_item(word, value, data);
+ r = parse_item(key, value, data);
if (r < 0)
return r;
}
@@ -113,7 +189,7 @@ static int proc_cmdline_parse_given(const char *line, proc_cmdline_parse_t parse
}
int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineFlags flags) {
- _cleanup_free_ char *line = NULL;
+ _cleanup_strv_free_ char **args = NULL;
int r;
assert(parse_item);
@@ -121,24 +197,30 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF
/* We parse the EFI variable first, because later settings have higher priority. */
if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+ _cleanup_free_ char *line = NULL;
+
r = systemd_efi_options_variable(&line);
if (r < 0) {
if (r != -ENODATA)
log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
} else {
- r = proc_cmdline_parse_given(line, parse_item, data, flags);
+ r = strv_split_full(&args, line, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
if (r < 0)
return r;
- line = mfree(line);
+ r = proc_cmdline_parse_strv(args, parse_item, data, flags);
+ if (r < 0)
+ return r;
+
+ args = strv_free(args);
}
}
- r = proc_cmdline(&line);
+ r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
if (r < 0)
return r;
- return proc_cmdline_parse_given(line, parse_item, data, flags);
+ return proc_cmdline_parse_strv(args, parse_item, data, flags);
}
static bool relaxed_equal_char(char a, char b) {
@@ -173,24 +255,19 @@ bool proc_cmdline_key_streq(const char *x, const char *y) {
return true;
}
-static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags flags, char **ret_value) {
+static int cmdline_get_key(char **args, const char *key, ProcCmdlineFlags flags, char **ret_value) {
_cleanup_free_ char *v = NULL;
bool found = false;
- const char *p;
int r;
- assert(line);
assert(key);
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
+ STRV_FOREACH(p, args) {
+ const char *word;
- r = proc_cmdline_extract_first(&p, &word, flags);
- if (r < 0)
- return r;
- if (r == 0)
- break;
+ word = mangle_word(*p, flags);
+ if (!word)
+ continue;
if (ret_value) {
const char *e;
@@ -224,6 +301,7 @@ static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags f
}
int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
+ _cleanup_strv_free_ char **args = NULL;
_cleanup_free_ char *line = NULL, *v = NULL;
int r;
@@ -247,14 +325,14 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
if (FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL) && !ret_value)
return -EINVAL;
- r = proc_cmdline(&line);
+ r = proc_cmdline_strv_internal(&args, /* filter_pid1_args = */ true);
if (r < 0)
return r;
if (FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) /* Shortcut */
- return cmdline_get_key(line, key, flags, ret_value);
+ return cmdline_get_key(args, key, flags, ret_value);
- r = cmdline_get_key(line, key, flags, ret_value ? &v : NULL);
+ r = cmdline_get_key(args, key, flags, ret_value ? &v : NULL);
if (r < 0)
return r;
if (r > 0) {
@@ -264,7 +342,6 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
return r;
}
- line = mfree(line);
r = systemd_efi_options_variable(&line);
if (r == -ENODATA) {
if (ret_value)
@@ -275,7 +352,12 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
if (r < 0)
return r;
- return cmdline_get_key(line, key, flags, ret_value);
+ args = strv_free(args);
+ r = strv_split_full(&args, line, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
+
+ return cmdline_get_key(args, key, flags, ret_value);
}
int proc_cmdline_get_bool(const char *key, bool *ret) {
@@ -303,75 +385,82 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
return 1;
}
-int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
- _cleanup_free_ char *line = NULL;
- bool processing_efi = true;
- const char *p;
- va_list ap;
+static int cmdline_get_key_ap(ProcCmdlineFlags flags, char* const* args, va_list ap) {
int r, ret = 0;
- /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_get_key_many(), let's make
- * this clear. */
- assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
+ for (;;) {
+ char **v;
+ const char *k, *e;
- /* This call may clobber arguments on failure! */
+ k = va_arg(ap, const char*);
+ if (!k)
+ break;
- if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
- r = systemd_efi_options_variable(&line);
- if (r < 0 && r != -ENODATA)
- log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
- }
+ assert_se(v = va_arg(ap, char**));
- p = line;
- for (;;) {
- _cleanup_free_ char *word = NULL;
+ STRV_FOREACH(p, args) {
+ const char *word;
- r = proc_cmdline_extract_first(&p, &word, flags);
- if (r < 0)
- return r;
- if (r == 0) {
- /* We finished with this command line. If this was the EFI one, then let's proceed with the regular one */
- if (processing_efi) {
- processing_efi = false;
+ word = mangle_word(*p, flags);
+ if (!word)
+ continue;
- line = mfree(line);
- r = proc_cmdline(&line);
+ e = proc_cmdline_key_startswith(word, k);
+ if (e && *e == '=') {
+ r = free_and_strdup(v, e + 1);
if (r < 0)
return r;
- p = line;
- continue;
+ ret++;
}
-
- break;
}
+ }
+
+ return ret;
+}
+
+int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
+ _cleanup_strv_free_ char **args = NULL;
+ int r, ret = 0;
+ va_list ap;
- va_start(ap, flags);
+ /* The PROC_CMDLINE_VALUE_OPTIONAL flag doesn't really make sense for proc_cmdline_get_key_many(), let's make
+ * this clear. */
+ assert(!FLAGS_SET(flags, PROC_CMDLINE_VALUE_OPTIONAL));
- for (;;) {
- char **v;
- const char *k, *e;
+ /* This call may clobber arguments on failure! */
- k = va_arg(ap, const char*);
- if (!k)
- break;
+ if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+ _cleanup_free_ char *line = NULL;
- assert_se(v = va_arg(ap, char**));
+ r = systemd_efi_options_variable(&line);
+ if (r < 0 && r != -ENODATA)
+ log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+ if (r >= 0) {
+ r = strv_split_full(&args, line, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX|EXTRACT_RETAIN_ESCAPE);
+ if (r < 0)
+ return r;
- e = proc_cmdline_key_startswith(word, k);
- if (e && *e == '=') {
- r = free_and_strdup(v, e + 1);
- if (r < 0) {
- va_end(ap);
- return r;
- }
+ va_start(ap, flags);
+ r = cmdline_get_key_ap(flags, args, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
- ret++;
- }
+ ret = r;
+ args = strv_free(args);
}
-
- va_end(ap);
}
- return ret;
+ r = proc_cmdline_strv(&args);
+ if (r < 0)
+ return r;
+
+ va_start(ap, flags);
+ r = cmdline_get_key_ap(flags, args, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+
+ return ret + r;
}
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 8650e293ce..a64d7757bc 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -14,7 +14,10 @@ typedef enum ProcCmdlineFlags {
typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
+int proc_cmdline_filter_pid1_args(char **argv, char ***ret);
+
int proc_cmdline(char **ret);
+int proc_cmdline_strv(char ***ret);
int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, ProcCmdlineFlags flags);
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
index 32780c606a..a34b0aa604 100644
--- a/src/cgls/cgls.c
+++ b/src/cgls/cgls.c
@@ -93,6 +93,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "-hkalM:u::xc", options, NULL)) >= 0)
switch (c) {
diff --git a/src/core/main.c b/src/core/main.c
index 2cd2da7284..9588c625b2 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -49,6 +49,7 @@
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "getopt-defs.h"
#include "hexdecoct.h"
#include "hostname-setup.h"
#include "ima-setup.h"
@@ -818,62 +819,13 @@ static void set_manager_settings(Manager *m) {
static int parse_argv(int argc, char *argv[]) {
enum {
- ARG_LOG_LEVEL = 0x100,
- ARG_LOG_TARGET,
- ARG_LOG_COLOR,
- ARG_LOG_LOCATION,
- ARG_LOG_TIME,
- ARG_UNIT,
- ARG_SYSTEM,
- ARG_USER,
- ARG_TEST,
- ARG_NO_PAGER,
- ARG_VERSION,
- ARG_DUMP_CONFIGURATION_ITEMS,
- ARG_DUMP_BUS_PROPERTIES,
- ARG_BUS_INTROSPECT,
- ARG_DUMP_CORE,
- ARG_CRASH_CHVT,
- ARG_CRASH_SHELL,
- ARG_CRASH_REBOOT,
- ARG_CONFIRM_SPAWN,
- ARG_SHOW_STATUS,
- ARG_DESERIALIZE,
- ARG_SWITCHED_ROOT,
- ARG_DEFAULT_STD_OUTPUT,
- ARG_DEFAULT_STD_ERROR,
- ARG_MACHINE_ID,
- ARG_SERVICE_WATCHDOGS,
+ COMMON_GETOPT_ARGS,
+ SYSTEMD_GETOPT_ARGS,
};
static const struct option options[] = {
- { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
- { "log-target", required_argument, NULL, ARG_LOG_TARGET },
- { "log-color", optional_argument, NULL, ARG_LOG_COLOR },
- { "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
- { "log-time", optional_argument, NULL, ARG_LOG_TIME },
- { "unit", required_argument, NULL, ARG_UNIT },
- { "system", no_argument, NULL, ARG_SYSTEM },
- { "user", no_argument, NULL, ARG_USER },
- { "test", no_argument, NULL, ARG_TEST },
- { "no-pager", no_argument, NULL, ARG_NO_PAGER },
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
- { "dump-bus-properties", no_argument, NULL, ARG_DUMP_BUS_PROPERTIES },
- { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT },
- { "dump-core", optional_argument, NULL, ARG_DUMP_CORE },
- { "crash-chvt", required_argument, NULL, ARG_CRASH_CHVT },
- { "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL },
- { "crash-reboot", optional_argument, NULL, ARG_CRASH_REBOOT },
- { "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN },
- { "show-status", optional_argument, NULL, ARG_SHOW_STATUS },
- { "deserialize", required_argument, NULL, ARG_DESERIALIZE },
- { "switched-root", no_argument, NULL, ARG_SWITCHED_ROOT },
- { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
- { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
- { "machine-id", required_argument, NULL, ARG_MACHINE_ID },
- { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS },
+ COMMON_GETOPT_OPTIONS,
+ SYSTEMD_GETOPT_OPTIONS,
{}
};
@@ -886,7 +838,7 @@ static int parse_argv(int argc, char *argv[]) {
if (getpid_cached() == 1)
opterr = 0;
- while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL)) >= 0)
switch (c) {
diff --git a/src/journal/cat.c b/src/journal/cat.c
index 5908758a8f..d3f7785ad3 100644
--- a/src/journal/cat.c
+++ b/src/journal/cat.c
@@ -75,6 +75,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+ht:p:", options, NULL)) >= 0)
switch (c) {
diff --git a/src/login/inhibit.c b/src/login/inhibit.c
index 7cd2fd3e66..25ba848492 100644
--- a/src/login/inhibit.c
+++ b/src/login/inhibit.c
@@ -210,6 +210,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+h", options, NULL)) >= 0)
switch (c) {
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 6a29a32bcd..447f1a70b0 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -2720,6 +2720,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
+
for (;;) {
static const char option_string[] = "-hp:als:H:M:qn:o:E:";
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 8abb016791..ce95b10b1c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -815,6 +815,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:P", options, NULL)) >= 0)
switch (c) {
diff --git a/src/run/run.c b/src/run/run.c
index 8377c2e8cd..ee4a151a4a 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -242,6 +242,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+hrH:M:E:p:tPqGdSu:", options, NULL)) >= 0)
switch (c) {
diff --git a/src/shared/condition.c b/src/shared/condition.c
index d5fdbbf9e0..e5a80757e0 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -106,35 +106,28 @@ Condition* condition_free_list_type(Condition *head, ConditionType type) {
}
static int condition_test_kernel_command_line(Condition *c, char **env) {
- _cleanup_free_ char *line = NULL;
+ _cleanup_strv_free_ char **args = NULL;
int r;
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_KERNEL_COMMAND_LINE);
- r = proc_cmdline(&line);
+ r = proc_cmdline_strv(&args);
if (r < 0)
return r;
bool equal = strchr(c->parameter, '=');
- for (const char *p = line;;) {
- _cleanup_free_ char *word = NULL;
+ STRV_FOREACH(word, args) {
bool found;
- r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
- if (r < 0)
- return r;
- if (r == 0)
- break;
-
if (equal)
- found = streq(word, c->parameter);
+ found = streq(*word, c->parameter);
else {
const char *f;
- f = startswith(word, c->parameter);
+ f = startswith(*word, c->parameter);
found = f && IN_SET(*f, 0, '=');
}
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index 42111d2772..a8248901ce 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -25,6 +25,7 @@
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "getopt-defs.h"
#include "initrd-util.h"
#include "killall.h"
#include "log.h"
@@ -50,23 +51,13 @@ static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
static int parse_argv(int argc, char *argv[]) {
enum {
- ARG_LOG_LEVEL = 0x100,
- ARG_LOG_TARGET,
- ARG_LOG_COLOR,
- ARG_LOG_LOCATION,
- ARG_LOG_TIME,
- ARG_EXIT_CODE,
- ARG_TIMEOUT,
+ COMMON_GETOPT_ARGS,
+ SHUTDOWN_GETOPT_ARGS,
};
static const struct option options[] = {
- { "log-level", required_argument, NULL, ARG_LOG_LEVEL },
- { "log-target", required_argument, NULL, ARG_LOG_TARGET },
- { "log-color", optional_argument, NULL, ARG_LOG_COLOR },
- { "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
- { "log-time", optional_argument, NULL, ARG_LOG_TIME },
- { "exit-code", required_argument, NULL, ARG_EXIT_CODE },
- { "timeout", required_argument, NULL, ARG_TIMEOUT },
+ COMMON_GETOPT_OPTIONS,
+ SHUTDOWN_GETOPT_OPTIONS,
{}
};
@@ -75,6 +66,10 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 1);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
+
/* "-" prevents getopt from permuting argv[] and moving the verb away
* from argv[1]. Our interface to initrd promises it'll be there. */
while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0)
diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c
index 7f8330cc24..6df2bca787 100644
--- a/src/test/test-proc-cmdline.c
+++ b/src/test/test-proc-cmdline.c
@@ -6,9 +6,12 @@
#include "initrd-util.h"
#include "log.h"
#include "macro.h"
+#include "nulstr-util.h"
#include "proc-cmdline.h"
+#include "process-util.h"
#include "special.h"
#include "string-util.h"
+#include "strv.h"
#include "tests.h"
static int obj;
@@ -27,6 +30,7 @@ TEST(proc_cmdline_parse) {
TEST(proc_cmdline_override) {
_cleanup_free_ char *line = NULL, *value = NULL;
+ _cleanup_strv_free_ char **args = NULL;
assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=different") == 0);
@@ -35,6 +39,9 @@ TEST(proc_cmdline_override) {
assert_se(proc_cmdline(&line) >= 0);
assert_se(streq(line, "foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\""));
line = mfree(line);
+ assert_se(proc_cmdline_strv(&args) >= 0);
+ assert_se(strv_equal(args, STRV_MAKE("foo_bar=quux", "wuff-piep=tuet", "zumm", "some_arg_with_space=foo bar", "and_one_more=zzz aaa")));
+ args = strv_free(args);
/* Test if parsing makes uses of the override */
assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
@@ -52,6 +59,9 @@ TEST(proc_cmdline_override) {
assert_se(proc_cmdline(&line) >= 0);
assert_se(streq(line, "hoge"));
line = mfree(line);
+ assert_se(proc_cmdline_strv(&args) >= 0);
+ assert_se(strv_equal(args, STRV_MAKE("hoge")));
+ args = strv_free(args);
assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux"));
value = mfree(value);
@@ -256,6 +266,48 @@ TEST(proc_cmdline_key_startswith) {
assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx"));
}
+#define test_proc_cmdline_filter_pid1_args_one(nulstr, expected) \
+ ({ \
+ _cleanup_strv_free_ char **a = NULL, **b = NULL; \
+ const char s[] = (nulstr); \
+ \
+ /* This emulates get_process_cmdline_strv(). */ \
+ assert_se(a = strv_parse_nulstr_full(s, ELEMENTSOF(s), \
+ /* drop_trailing_nuls = */ true)); \
+ assert_se(proc_cmdline_filter_pid1_args(a, &b) >= 0); \
+ assert_se(strv_equal(b, expected)); \
+ })
+
+TEST(proc_cmdline_filter_pid1_args) {
+ test_proc_cmdline_filter_pid1_args_one("systemd\0",
+ STRV_MAKE_EMPTY);
+
+ test_proc_cmdline_filter_pid1_args_one("systemd\0"
+ "hoge\0"
+ "-x\0"
+ "foo\0"
+ "--aaa\0"
+ "var\0",
+ STRV_MAKE("hoge", "foo", "var"));
+
+ test_proc_cmdline_filter_pid1_args_one("/usr/lib/systemd/systemd\0"
+ "--switched-root\0"
+ "--system\0"
+ "--deserialize\030\0" /* followed with space */
+ "--deserialize=31\0" /* followed with '=' */
+ "--exit-code=42\0"
+ "\0\0\0"
+ "systemd.log_level=debug\0"
+ "--unit\0foo.target\0"
+ " ' quoted '\0"
+ "systemd.log_target=console\0"
+ "\t\0"
+ " arg with space \0"
+ "3\0"
+ "\0\0\0",
+ STRV_MAKE("", "", "", "systemd.log_level=debug", " ' quoted '", "systemd.log_target=console", "\t", " arg with space ", "3"));
+}
+
static int intro(void) {
if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
return log_tests_skipped("can't read /proc/cmdline");
diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c
index 9a4a8adbc3..b9f18d1552 100644
--- a/src/udev/udevadm-lock.c
+++ b/src/udev/udevadm-lock.c
@@ -75,6 +75,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, arg_print ? "hVd:b:t:p" : "+hVd:b:t:p", options, NULL)) >= 0)
switch (c) {
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index 30a72f2a42..b803f7bb0f 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -62,6 +62,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0)
switch (c) {
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index eab0c3af15..67675b4b7f 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -1150,6 +1150,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_services = l;
}
+ /* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
+ * that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
+ optind = 0;
+
for (;;) {
int c;