mirror of
https://github.com/systemd/systemd
synced 2024-07-21 10:17:21 +00:00
Merge pull request #27830 from 1awesomeJ/initrd
PID1: Detect battery level in initrd and if low refuse continuing to …
This commit is contained in:
commit
5e4c66450c
2
TODO
2
TODO
|
@ -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.
|
||||
|
|
|
@ -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',
|
||||
|
|
63
man/systemd-battery-check.xml
Normal file
63
man/systemd-battery-check.xml
Normal 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>
|
10
meson.build
10
meson.build
|
@ -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.
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
157
src/battery-check/battery-check.c
Normal file
157
src/battery-check/battery-check.c
Normal 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);
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
21
units/initrd-battery-check.service.in
Normal file
21
units/initrd-battery-check.service.in
Normal 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
|
|
@ -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'],
|
||||
|
|
Loading…
Reference in a new issue