Merge pull request #27830 from 1awesomeJ/initrd

PID1: Detect battery level in initrd and if low refuse continuing to …
This commit is contained in:
Lennart Poettering 2023-06-28 19:18:03 +02:00 committed by GitHub
commit 5e4c66450c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 271 additions and 5 deletions

2
TODO
View file

@ -1323,8 +1323,6 @@ Features:
* when configuring loopback netif, and it fails due to EPERM, eat up error if
it happens to be set up alright already.
* at boot: check if battery above some threshold, if not power off again after explanation
* userdb: add field for ambient caps, so that a user can have CAP_WAKE_ALARM
for example. And add code that resets ambient caps for all services by
default.

View file

@ -887,6 +887,7 @@ 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

@ -0,0 +1,63 @@
<?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

@ -3846,6 +3846,16 @@ public_programs += executable(
install : true,
install_dir : rootbindir)
public_programs += executable(
'systemd-battery-check',
'src/battery-check/battery-check.c',
include_directories : includes,
link_with : [libshared],
dependencies : [userspace, versiondep],
install_rpath : rootpkglibdir,
install_dir : rootlibexecdir,
install : true)
# Protecting files from the distro in /usr doesn't make sense since they can be trivially accessed otherwise,
# so don't restrict the access mode in /usr. That doesn't apply to /etc, so we do restrict the access mode
# there.

View file

@ -23,7 +23,7 @@ bool emoji_enabled(void) {
return cached_emoji_enabled;
}
const char *special_glyph(SpecialGlyph code) {
const char *special_glyph_full(SpecialGlyph code, bool force_utf) {
/* A list of a number of interesting unicode glyphs we can use to decorate our output. It's probably wise to be
* conservative here, and primarily stick to the glyphs defined in the eurlatgr font, so that display still
@ -71,6 +71,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = "~",
[SPECIAL_GLYPH_DOWNLOAD] = "\\",
[SPECIAL_GLYPH_SPARKLES] = "*",
[SPECIAL_GLYPH_LOW_BATTERY] = "!",
[SPECIAL_GLYPH_WARNING_SIGN] = "!",
},
@ -129,6 +130,7 @@ const char *special_glyph(SpecialGlyph code) {
[SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */
[SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */
[SPECIAL_GLYPH_SPARKLES] = u8"",
[SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫",
[SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️",
},
};
@ -137,5 +139,5 @@ const char *special_glyph(SpecialGlyph code) {
return NULL;
assert(code < _SPECIAL_GLYPH_MAX);
return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
return draw_table[force_utf || (code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8())][code];
}

View file

@ -44,15 +44,24 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_RECYCLING,
SPECIAL_GLYPH_DOWNLOAD,
SPECIAL_GLYPH_SPARKLES,
SPECIAL_GLYPH_LOW_BATTERY,
SPECIAL_GLYPH_WARNING_SIGN,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;
const char *special_glyph(SpecialGlyph code) _const_;
const char *special_glyph_full(SpecialGlyph code, bool force_utf) _const_;
bool emoji_enabled(void);
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

@ -0,0 +1,157 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <getopt.h>
#include <sys/socket.h>
#include <unistd.h>
#include "battery-util.h"
#include "build.h"
#include "constants.h"
#include "errno-util.h"
#include "glyph-util.h"
#include "fd-util.h"
#include "io-util.h"
#include "log.h"
#include "main-func.h"
#include "socket-util.h"
#include "terminal-util.h"
static void help(void) {
printf("%s\n\n"
"Checks battery level to see whether there's enough charge.\n\n"
" -h --help Show this help\n"
" --version Show package version\n",
program_invocation_short_name);
}
static void battery_check_send_plymouth_message(char *message, const char *mode) {
assert(message);
assert(mode);
int r;
static const union sockaddr_union sa = PLYMOUTH_SOCKET;
_cleanup_close_ int fd = -EBADF;
_cleanup_free_ char *plymouth_message = NULL;
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');
if (c < 0)
return (void) 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");
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");
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");
}
static int parse_argv(int argc, char * argv[]) {
enum {
ARG_VERSION = 0x100,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return version();
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"%s takes no argument.",
program_invocation_short_name);
return 1;
}
static int run(int argc, char *argv[]) {
int r;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
r = battery_is_discharging_and_low();
if (r < 0) {
log_warning_errno(r, "Failed to check battery status, ignoring: %m");
return 0;
}
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");
}
return r;
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View file

@ -119,6 +119,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_RECYCLING);
dump_glyph(SPECIAL_GLYPH_DOWNLOAD);
dump_glyph(SPECIAL_GLYPH_SPARKLES);
dump_glyph(SPECIAL_GLYPH_LOW_BATTERY);
dump_glyph(SPECIAL_GLYPH_WARNING_SIGN);
}

View file

@ -0,0 +1,21 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Check battery level during early boot
Documentation=man:systemd-battery-check(1)
DefaultDependencies=no
AssertPathExists=/etc/initrd-release
Before=local-fs-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-battery-check
FailureAction=poweroff-force

View file

@ -55,6 +55,10 @@ units = [
'file' : 'hybrid-sleep.target',
'conditions' : ['ENABLE_HIBERNATE'],
},
{
'file' : 'initrd-battery-check.service.in',
'conditions' : ['ENABLE_INITRD'],
},
{
'file' : 'initrd-cleanup.service',
'conditions' : ['ENABLE_INITRD'],