diff --git a/NEWS b/NEWS
index 185126f644..c1770565e9 100644
--- a/NEWS
+++ b/NEWS
@@ -698,7 +698,8 @@ CHANGES WITH 254 in spe:
charge level of the system. In case the charge level is very low the
user is notified (graphically via Plymouth – if available – as well
as in text form on the console), and the system is turned off after a
- 10s delay.
+ 10s delay. The feature can be disabled by passing
+ systemd.battery-check=0 through the kernel command line.
* The 'passwdqc' library is now supported as an alternative to the
'pwquality' library and it can be selected at build time.
diff --git a/man/systemd-battery-check.service.xml b/man/systemd-battery-check.service.xml
index 2d9005696e..6f1b0ea687 100644
--- a/man/systemd-battery-check.service.xml
+++ b/man/systemd-battery-check.service.xml
@@ -62,6 +62,26 @@
+
+ Kernel Command Line
+
+ The following variables are understood:
+
+
+
+ systemd.battery-check=BOOL
+
+
+ Takes a boolean. If specified with false, systemd-battery-check command
+ will return immediately with exit status 0 without checking battery capacity and AC power
+ existence, and the service systemd-battery-check.service will succeed. This
+ may be useful when the command wrongly detects and reports battery capacity percentage or AC power
+ existence, or when you want to boot the system forcibly.
+
+
+
+
+
See Also
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 6203b79959..32d4f414cc 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -17,63 +17,95 @@
#include "strv.h"
#include "virt.h"
-int proc_cmdline_filter_pid1_args(
- char **argv, /* input, may be reordered by this function. */
- char ***ret) {
-
+int proc_cmdline_filter_pid1_args(char **argv, 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,
- {}
};
+ static const char *short_options = SYSTEMD_GETOPT_SHORT_OPTIONS;
- int saved_optind, saved_opterr, saved_optopt, argc;
- char *saved_optarg;
- char **filtered;
- size_t idx;
+ _cleanup_strv_free_ char **filtered = NULL;
+ int state, r;
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;
+ /* Currently, we do not support '-', '+', and ':' at the beginning. */
+ assert(!IN_SET(short_options[0], '-', '+', ':'));
/* Filter out all known options. */
- argc = strv_length(argv);
- while (getopt_long(argc, argv, SYSTEMD_GETOPT_SHORT_OPTIONS, options, NULL) >= 0)
- ;
+ state = no_argument;
+ STRV_FOREACH(p, strv_skip(argv, 1)) {
+ int prev_state = state;
+ const char *a = *p;
- idx = optind;
+ /* Reset the state for the next step. */
+ state = no_argument;
- /* Restore global variables. */
- optind = saved_optind;
- opterr = saved_opterr;
- optopt = saved_optopt;
- optarg = saved_optarg;
+ if (prev_state == required_argument ||
+ (prev_state == optional_argument && a[0] != '-'))
+ /* Handled as an argument of the previous option, filtering out the string. */
+ continue;
- filtered = strv_copy(strv_skip(argv, idx));
- if (!filtered)
- return -ENOMEM;
+ if (a[0] != '-') {
+ /* Not an option, accepting the string. */
+ r = strv_extend(&filtered, a);
+ if (r < 0)
+ return r;
+ continue;
+ }
- *ret = filtered;
+ if (a[1] == '-') {
+ if (a[2] == '\0') {
+ /* "--" is specified, accepting remaining strings. */
+ r = strv_extend_strv(&filtered, strv_skip(p, 1), /* filter_duplicates = */ false);
+ if (r < 0)
+ return r;
+ break;
+ }
+
+ /* long option, e.g. --foo */
+ for (size_t i = 0; i < ELEMENTSOF(options); i++) {
+ const char *q = startswith(a + 2, options[i].name);
+ if (!q || !IN_SET(q[0], '=', '\0'))
+ continue;
+
+ /* Found matching option, updating the state if necessary. */
+ if (q[0] == '\0' && options[i].has_arg == required_argument)
+ state = required_argument;
+
+ break;
+ }
+ continue;
+ }
+
+ /* short option(s), e.g. -x or -xyz */
+ while (a && *++a != '\0')
+ for (const char *q = short_options; *q != '\0'; q++) {
+ if (*q != *a)
+ continue;
+
+ /* Found matching short option. */
+
+ if (q[1] == ':') {
+ /* An argument is required or optional, and remaining part
+ * is handled as argument if exists. */
+ state = a[1] != '\0' ? no_argument :
+ q[2] == ':' ? optional_argument : required_argument;
+
+ a = NULL; /* Not necessary to parse remaining part. */
+ }
+ break;
+ }
+ }
+
+ *ret = TAKE_PTR(filtered);
return 0;
}
diff --git a/src/battery-check/battery-check.c b/src/battery-check/battery-check.c
index ca13251ae0..c3091d3474 100644
--- a/src/battery-check/battery-check.c
+++ b/src/battery-check/battery-check.c
@@ -14,7 +14,9 @@
#include "io-util.h"
#include "log.h"
#include "main-func.h"
+#include "parse-util.h"
#include "pretty-print.h"
+#include "proc-cmdline.h"
#include "socket-util.h"
#include "terminal-util.h"
#include "time-util.h"
@@ -24,6 +26,8 @@
#define BATTERY_RESTORED_MESSAGE \
"A.C. power restored, continuing."
+static bool arg_doit = true;
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
@@ -84,6 +88,23 @@ static int plymouth_send_message(const char *mode, const char *message) {
return 0;
}
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+ int r;
+
+ assert(key);
+
+ if (streq(key, "systemd.battery-check")) {
+
+ r = value ? parse_boolean(value) : 1;
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s switch, ignoring: %s", key, value);
+ else
+ arg_doit = r;
+ }
+
+ return 0;
+}
+
static int parse_argv(int argc, char * argv[]) {
enum {
@@ -132,10 +153,19 @@ static int run(int argc, char *argv[]) {
log_setup();
+ r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
+
r = parse_argv(argc, argv);
if (r <= 0)
return r;
+ if (!arg_doit) {
+ log_info("Checking battery status and AC power existence is disabled by the kernel command line, skipping execution.");
+ return 0;
+ }
+
r = battery_is_discharging_and_low();
if (r < 0) {
log_warning_errno(r, "Failed to check battery status, ignoring: %m");
diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c
index 2d51dbe4b9..4fd9125378 100644
--- a/src/test/test-proc-cmdline.c
+++ b/src/test/test-proc-cmdline.c
@@ -284,14 +284,47 @@ TEST(proc_cmdline_filter_pid1_args) {
test_proc_cmdline_filter_pid1_args_one("systemd\0",
STRV_MAKE_EMPTY);
+ /* short option */
test_proc_cmdline_filter_pid1_args_one("systemd\0"
- "hoge\0"
- "-x\0"
- "foo\0"
- "--aaa\0"
- "var\0",
- STRV_MAKE("hoge", "foo", "var"));
+ "-a\0" /* unknown option */
+ "-abc\0" /* unknown options */
+ "-h\0" /* known option */
+ "-hDbs\0" /* known options */
+ "-hsx\0" /* mixed (known and unknown) options */
+ "-z\0drop1\0" /* option with argument */
+ "-z\0-z\0accept1\0" /* the second -z is handled as argument */
+ "-az\0drop2\0" /* options with argument */
+ "-za\0accept2\0" /* options with argument */
+ "-z\0--\0-x\0", /* "--" is handled as argument */
+ STRV_MAKE("accept1", "accept2"));
+ /* long option */
+ test_proc_cmdline_filter_pid1_args_one("systemd\0"
+ "--unknown\0accept1\0" /* unknown option */
+ "--system\0accept2\0" /* no argument */
+ "--log-level\0drop1\0" /* required argument (separated with space) */
+ "--log-level=drop2\0accept3\0" /* required argument (concatenated with '=') */
+ "--log-level\0--log-level\0accept4\0" /* the second "--log-level" is handled as argument */
+ "--log-level\0--\0-x\0" /* "--" is handled as argument */
+ "--log-color\0--log-level\0drop3\0" /* optional argument ("--log-level" is handled as another option) */
+ "--log-color\0accept5\0" /* optional argument (separated with space) */
+ "--log-color=drop4\0accept6\0" /* optional argument (concatenated with '=') */
+ "--log-color\0--\0" /* "--" is _not_ handled as argument, and remaining strings are accepted */
+ "remaining\0-x\0--foo\0",
+ STRV_MAKE("accept1", "accept2", "accept3", "accept4", "accept5", "accept6", "remaining", "-x", "--foo"));
+
+ /* test for "--" */
+ test_proc_cmdline_filter_pid1_args_one("systemd\0"
+ "-a\0"
+ "--dropped\0"
+ "--\0" /* remaining strings are accepted */
+ "-x\0"
+ "-abc\0"
+ "--hoge\0"
+ "accepted\0",
+ STRV_MAKE("-x", "-abc", "--hoge", "accepted"));
+
+ /* test for space */
test_proc_cmdline_filter_pid1_args_one("/usr/lib/systemd/systemd\0"
"--switched-root\0"
"--system\0"
diff --git a/units/systemd-battery-check.service.in b/units/systemd-battery-check.service.in
index 61f4ae16e0..a6a0d26fde 100644
--- a/units/systemd-battery-check.service.in
+++ b/units/systemd-battery-check.service.in
@@ -12,6 +12,7 @@ Description=Check battery level during early boot
Documentation=man:systemd-battery-check.service(8)
ConditionVirtualization=no
ConditionDirectoryNotEmpty=/sys/class/power_supply/
+ConditionKernelCommandLine=!systemd.battery-check=0
AssertPathExists=/etc/initrd-release
DefaultDependencies=no
After=plymouth-start.service