battery-check: several follow-ups

Follow-ups for e3d4148d50.

- add reference to initrd-battery-check.service in man page, and move
  its section from 1 to 8,
- add link to man page in help message,
- introduce ERRNO_IS_NO_PLYMOUTH(),
- propagate error in battery_check_send_plymouth_message(),
- rename battery_check_send_plymouth_message() -> plymouth_send_message(),
- return earlier when the first battery level check passed to reduce
  indentation,
- fix potential use of invalid fd on battery restored,
- do not use emoji for /dev/console,
- add simple test (mostly for coverity),

etc, etc...
This commit is contained in:
Yu Watanabe 2023-06-29 02:43:17 +09:00
parent d625f717db
commit be994c2640
7 changed files with 166 additions and 129 deletions

View file

@ -0,0 +1,72 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="initrd-battery-check.service" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-battery-check</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>initrd-battery-check.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>initrd-battery-check.service</refname>
<refname>systemd-battery-check</refname>
<refpurpose>Check battery level whether there's enough charge, and power off if not.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>initrd-battery-check.service</filename></para>
<cmdsynopsis>
<command>/usr/lib/systemd/systemd-battery-check</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<filename>initrd-battery-check.service</filename> is used to check the battery level during the early
boot stage to determine whether there's sufficient battery power to carry on with the booting process.
</para>
<para>
<command>systemd-battery-check</command> returns success if the device is connected to an AC power
source or if the battery charge is greater than 5%. It returns failure otherwise.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood by <command>systemd-battery-check</command>:</para>
<variablelist>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>
On success (running on AC power or battery capacity greater than 5%), 0 is returned, a non-zero failure
code otherwise.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -23,6 +23,7 @@ manpages = [
['hostname', '5', [], ''],
['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
['hwdb', '7', [], 'ENABLE_HWDB'],
['initrd-battery-check.service', '8', ['systemd-battery-check'], ''],
['integritytab', '5', [], 'HAVE_LIBCRYPTSETUP'],
['iocost.conf', '5', [], ''],
['journal-remote.conf', '5', ['journal-remote.conf.d'], 'HAVE_MICROHTTPD'],
@ -887,7 +888,6 @@ manpages = [
''],
['systemd-ask-password', '1', [], ''],
['systemd-backlight@.service', '8', ['systemd-backlight'], 'ENABLE_BACKLIGHT'],
['systemd-battery-check', '1', [], ''],
['systemd-binfmt.service', '8', ['systemd-binfmt'], 'ENABLE_BINFMT'],
['systemd-bless-boot-generator', '8', [], 'ENABLE_BOOTLOADER'],
['systemd-bless-boot.service',

View file

@ -1,63 +0,0 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-battery-check" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-battery-check</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-battery-check</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-battery-check</refname>
<refpurpose>Checks battery level to see whether there's enough charge.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>systemd-battery-check</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>systemd-battery-check</command> is used to check the battery level during the early boot
stage to determine whether there's sufficient battery power to carry on with the booting process.
The tool returns success if the device is connected to an AC power source
or if the battery charge is greater than 5%. It returns failure otherwise.</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Exit status</title>
<para>On success (running on AC power or battery capacity greater than 5%), 0 is returned, a non-zero failure code otherwise.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View file

@ -50,18 +50,14 @@ typedef enum SpecialGlyph {
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
bool emoji_enabled(void);
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
static inline const char *special_glyph(SpecialGlyph code) {
return special_glyph_full(code, false);
}
static inline const char *special_glyph_force_utf(SpecialGlyph code) {
return special_glyph_full(code, true);
}
static inline const char *special_glyph_check_mark(bool b) {
return b ? special_glyph(SPECIAL_GLYPH_CHECK_MARK) : special_glyph(SPECIAL_GLYPH_CROSS_MARK);
}

View file

@ -14,47 +14,74 @@
#include "io-util.h"
#include "log.h"
#include "main-func.h"
#include "pretty-print.h"
#include "socket-util.h"
#include "terminal-util.h"
#include "time-util.h"
#define BATTERY_LOW_MESSAGE \
"Battery level critically low. Please connect your charger or the system will power off in 10 seconds."
#define BATTERY_RESTORED_MESSAGE \
"A.C. power restored, continuing."
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-battery-check", "8", &link);
if (r < 0)
return log_oom();
static void help(void) {
printf("%s\n\n"
"Checks battery level to see whether there's enough charge.\n\n"
"%sCheck battery level to see whether there's enough charge.%s\n\n"
" -h --help Show this help\n"
" --version Show package version\n",
program_invocation_short_name);
" --version Show package version\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
ansi_normal(),
link);
return 0;
}
static void battery_check_send_plymouth_message(char *message, const char *mode) {
assert(message);
assert(mode);
static bool ERRNO_IS_NO_PLYMOUTH(int r) {
return IN_SET(abs(r), EAGAIN, ENOENT) || ERRNO_IS_DISCONNECT(r);
}
int r;
static int plymouth_send_message(const char *mode, const char *message) {
static const union sockaddr_union sa = PLYMOUTH_SOCKET;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *plymouth_message = NULL;
_cleanup_close_ int fd = -EBADF;
int c, r;
int c = asprintf(&plymouth_message,
"C\x02%c%s%c"
"M\x02%c%s%c",
(int) strlen(mode) + 1, mode, '\x00',
(int) strlen(message) + 1, message, '\x00');
assert(mode);
assert(message);
c = asprintf(&plymouth_message,
"C\x02%c%s%c"
"M\x02%c%s%c",
(int) strlen(mode) + 1, mode, '\x00',
(int) strlen(message) + 1, message, '\x00');
if (c < 0)
return (void) log_oom();
return log_oom();
/* We set SOCK_NONBLOCK here so that we rather drop the
* message than wait for plymouth */
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (fd < 0)
return (void) log_warning_errno(errno, "socket() failed: %m");
return log_warning_errno(errno, "socket() failed: %m");
if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return (void) log_full_errno(IN_SET(errno, EAGAIN, ENOENT) || ERRNO_IS_DISCONNECT(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Connection to plymouth failed: %m");
return log_full_errno(ERRNO_IS_NO_PLYMOUTH(errno) ? LOG_DEBUG : LOG_WARNING, errno,
"Failed to connect to plymouth: %m");
r = loop_write(fd, plymouth_message, c, /* do_poll = */ false);
if (r < 0)
return (void) log_full_errno(IN_SET(r, -EAGAIN, -ENOENT) || ERRNO_IS_DISCONNECT(r) ?
LOG_DEBUG : LOG_WARNING, r, "Failed to write to plymouth, ignoring: %m");
return log_full_errno(ERRNO_IS_NO_PLYMOUTH(r) ? LOG_DEBUG : LOG_WARNING, r,
"Failed to write to plymouth: %m");
return 0;
}
static int parse_argv(int argc, char * argv[]) {
@ -79,8 +106,7 @@ static int parse_argv(int argc, char * argv[]) {
switch (c) {
case 'h':
help();
return 0;
return help();
case ARG_VERSION:
return version();
@ -100,10 +126,11 @@ static int parse_argv(int argc, char * argv[]) {
}
static int run(int argc, char *argv[]) {
_cleanup_free_ char *plymouth_message = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
log_parse_environment();
log_open();
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
@ -114,44 +141,40 @@ static int run(int argc, char *argv[]) {
log_warning_errno(r, "Failed to check battery status, ignoring: %m");
return 0;
}
if (r == 0)
return 0;
log_emergency("%s " BATTERY_LOW_MESSAGE, special_glyph(SPECIAL_GLYPH_LOW_BATTERY));
fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
log_warning_errno(fd, "Failed to open console, ignoring: %m");
else
dprintf(fd, ANSI_HIGHLIGHT_RED "%s " BATTERY_LOW_MESSAGE ANSI_NORMAL "\n",
special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY, /* force_utf = */ false));
if (asprintf(&plymouth_message, "%s " BATTERY_LOW_MESSAGE,
special_glyph_full(SPECIAL_GLYPH_LOW_BATTERY, /* force_utf = */ true)) < 0)
return log_oom();
(void) plymouth_send_message("shutdown", plymouth_message);
usleep_safe(10 * USEC_PER_SEC);
r = battery_is_discharging_and_low();
if (r < 0)
return log_warning_errno(r, "Failed to check battery status, assuming not charged yet, powering off: %m");
if (r > 0) {
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *message = NULL, *plymouth_message = NULL, *ac_message = NULL;
if (asprintf(&message, "%s Battery level critically low. Please connect your charger or the system will power off in 10 seconds.", special_glyph(SPECIAL_GLYPH_LOW_BATTERY)) < 0)
return log_oom();
if (asprintf(&plymouth_message, "%s Battery level critically low. Please connect your charger or the system will power off in 10 seconds.", special_glyph_force_utf(SPECIAL_GLYPH_LOW_BATTERY)) < 0)
return log_oom();
log_emergency("%s", message);
fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (fd < 0)
log_warning_errno(fd, "Failed to open console, ignoring: %m");
else
dprintf(fd, ANSI_HIGHLIGHT_RED "%s" ANSI_NORMAL "\n", message);
battery_check_send_plymouth_message(plymouth_message, "shutdown");
sleep(10);
r = battery_is_discharging_and_low();
if (r > 0) {
log_emergency("Battery level critically low, powering off.");
return r;
}
if (r < 0)
return log_warning_errno(r, "Failed to check battery status, ignoring: %m");
if (asprintf(&ac_message, "A.C. power restored, continuing") < 0)
return log_oom();
log_info("%s",ac_message);
dprintf(fd, "%s\n", ac_message);
battery_check_send_plymouth_message(ac_message, "boot-up");
log_emergency("Battery level critically low, powering off.");
return r;
}
return r;
log_info(BATTERY_RESTORED_MESSAGE);
if (fd >= 0)
dprintf(fd, BATTERY_RESTORED_MESSAGE "\n");
(void) plymouth_send_message("boot-up", BATTERY_RESTORED_MESSAGE);
return 0;
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
systemd-battery-check --help
systemd-battery-check --version
systemd-battery-check || :

View file

@ -9,7 +9,7 @@
[Unit]
Description=Check battery level during early boot
Documentation=man:systemd-battery-check(1)
Documentation=man:initrd-battery-check.service(8)
DefaultDependencies=no
AssertPathExists=/etc/initrd-release
Before=local-fs-pre.target