Merge pull request #9437 from poettering/sd-boot-count
many sd-boot/bootctl fixes, and a new "boot counting" concept, for automatic fallback to older kernels on persistent failures
This commit is contained in:
commit
a2689fa5fc
20
TODO
20
TODO
|
@ -52,6 +52,23 @@ Features:
|
|||
show state of these flags, and optionally trigger such a factory reset on
|
||||
next boot by setting the flag.
|
||||
|
||||
* sd-boot: search drop-ins in $BOOT, too
|
||||
|
||||
* sd-boot: add "oneshot boot timeout" variable support
|
||||
|
||||
* sd-boot: automatically load EFI modules from some drop-in dir, so that people
|
||||
can add in file system drivers and such
|
||||
|
||||
* esp generator: also mount $BOOT if found
|
||||
|
||||
* sd-boot: optionally, show boot menu when previous default boot item has
|
||||
non-zero "tries done" count
|
||||
|
||||
* logind: add "boot into bootmenu" API, and possibly even "boot into windows"
|
||||
and "boot into macos".
|
||||
|
||||
* bootspec.c: also enumerate EFI unified kernel images.
|
||||
|
||||
* maybe extend .path units to expose fanotify() per-mount change events
|
||||
|
||||
* Add a "systemctl list-units --by-slice" mode or so, which rearranges the
|
||||
|
@ -175,9 +192,6 @@ Features:
|
|||
* calenderspec: add support for week numbers and day numbers within a
|
||||
year. This would allow us to define "bi-weekly" triggers safely.
|
||||
|
||||
* add bpf-based implementation of devices cgroup controller logic for compat
|
||||
with cgroupsv2 as supported by newest kernel
|
||||
|
||||
* sd-bus: add vtable flag, that may be used to request client creds implicitly
|
||||
and asynchronously before dispatching the operation
|
||||
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
# Automatic Boot Assessment
|
||||
|
||||
systemd provides support for automatically reverting back to the previous
|
||||
version of the OS or kernel in case the system consistently fails to boot. This
|
||||
support is built into various of its components. When used together these
|
||||
components provide a complete solution on UEFI systems, built as add-on to the
|
||||
[Boot Loader
|
||||
Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION). However, the
|
||||
different components may also be used independently, and in combination with
|
||||
other software, to implement similar schemes, for example with other boot
|
||||
loaders or for non-UEFI systems. Here's a brief overview of the complete set of
|
||||
components:
|
||||
|
||||
* The
|
||||
[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
|
||||
boot loader optionally maintains a per-boot-loader-entry counter that is
|
||||
decreased by one on each attempt to boot the entry, prioritizing entries that
|
||||
have non-zero counters over those which already reached a counter of zero
|
||||
when choosing the entry to boot.
|
||||
|
||||
* The
|
||||
[`systemd-bless-boot.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-bless-boot.service.html)
|
||||
service automatically marks a boot loader entry, for which boot counting as
|
||||
mentioned above is enabled, as "good" when a boot has been determined to be
|
||||
successful, thus turning off boot counting for it.
|
||||
|
||||
* The
|
||||
[`systemd-bless-boot-generator(8)`](https://www.freedesktop.org/software/systemd/man/systemd-bless-boot-generator.html)
|
||||
generator automatically pulls in `systemd-bless-boot.service` when use of
|
||||
`systemd-boot` with boot counting enabled is detected.
|
||||
|
||||
* The
|
||||
[`systemd-boot-check-no-failures.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-boot-check-no-failures.service.html)
|
||||
service is a simple health check tool that determines whether the boot
|
||||
completed successfuly. When enabled it becomes an indirect dependency of
|
||||
`systemd-bless-boot.service` (by means of `boot-complete.target`, see
|
||||
below), ensuring that the boot will not be considered successful if there are
|
||||
any failed services.
|
||||
|
||||
* The `boot-complete.target` target unit (see
|
||||
[`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html))
|
||||
serves as a generic extension point both for units that shall be considered
|
||||
necessary to consider a boot successful on one side (example:
|
||||
`systemd-boot-check-no-failures.service` as described above), and units that
|
||||
want to act only if the boot is successful on the other (example:
|
||||
`systemd-bless-boot.service` as described above).
|
||||
|
||||
* The
|
||||
[`kernel-install(8)`](https://www.freedesktop.org/software/systemd/man/kernel-install.html)
|
||||
script can optionally create boot loader entries that carry an initial boot
|
||||
counter (the initial counter is configurable in `/etc/kernel/tries`).
|
||||
|
||||
# Details
|
||||
|
||||
The boot counting data `systemd-boot` and `systemd-bless-boot.service`
|
||||
manage is stored in the name of the boot loader entries. If a boot loader entry
|
||||
file name contains `+` followed by one or two numbers (if two numbers, then
|
||||
those need to be separated by `-`) right before the `.conf` suffix, then boot
|
||||
counting is enabled for it. The first number is the "tries left" counter
|
||||
encoding how many attempts to boot this entry shall still be made. The second
|
||||
number is the "tries done" counter, encoding how many failed attempts to boot
|
||||
it have already been made. Each time a boot loader entry marked this way is
|
||||
booted the first counter is decreased by one, and the second one increased by
|
||||
one. (If the second counter is missing, then it is assumed to be equivalent to
|
||||
zero.) If the "tries left" counter is above zero the entry is still considered
|
||||
for booting (the entry's state is considered to be "indeterminate"), as soon as
|
||||
it reached zero the entry is not tried anymore (entry state "bad"). If the boot
|
||||
attempt completed successfully the entry's counters are removed from the name
|
||||
(entry state "good"), thus turning off boot counting for the future.
|
||||
|
||||
## Walkthrough
|
||||
|
||||
Here's an example walkthrough of how this all fits together.
|
||||
|
||||
1. The user runs `echo 3 > /etc/kernel/tries` to enable boot counting.
|
||||
|
||||
2. A new kernel is installed. `kernel-install` is used to generate a new boot
|
||||
loader entry file for it. Let's say the version string for the new kernel is
|
||||
`4.14.11-300.fc27.x86_64`, a new boot loader entry
|
||||
`/boot/loader/entries/4.14.11-300.fc27.x86_64+3.conf` is hence created.
|
||||
|
||||
3. The system is booted for the first time after the new kernel is
|
||||
installed. The boot loader now sees the `+3` counter in the entry file
|
||||
name. It hence renames the file to `4.14.11-300.fc27.x86_64+2-1.conf`
|
||||
indicating that at this point one attempt has started and thus only one less
|
||||
is left. After the rename completed the entry is booted as usual.
|
||||
|
||||
4. Let's say this attempt to boot fails. On the following boot the boot loader
|
||||
will hence see the `+2-1` tag in the name, and hence rename the entry file to
|
||||
`4.14.11-300.fc27.x86_64+1-2.conf`, and boot it.
|
||||
|
||||
5. Let's say the boot fails again. On the subsequent boot the loader hence will
|
||||
see the `+1-2` tag, and rename the file to
|
||||
`4.14.11-300.fc27.x86_64+0-3.conf` and boot it.
|
||||
|
||||
6. If this boot also fails, on the next boot the boot loader will see the the
|
||||
tag `+0-3`, i.e. the counter reached zero. At this point the entry will be
|
||||
considered "bad", and ordered to the end of the list of entries. The next
|
||||
newest boot entry is now tried, i.e. the system automatically reverted back
|
||||
to an earlier version.
|
||||
|
||||
The above describes the walkthrough when the selected boot entry continously
|
||||
fails. Let's have a look at an alternative ending to this walkthrough. In this
|
||||
scenario the first 4 steps are the same as above:
|
||||
|
||||
1. *as above*
|
||||
|
||||
2. *as above*
|
||||
|
||||
3. *as above*
|
||||
|
||||
4. *as above*
|
||||
|
||||
5. Let's say the second boot succeeds. The kernel initializes properly, systemd
|
||||
is started and invokes all generators.
|
||||
|
||||
6. One of the generators started is `systemd-bless-boot-generator` which
|
||||
detects that boot counting is used. It hence pulls
|
||||
`systemd-bless-boot.service` into the initial transaction.
|
||||
|
||||
7. `systemd-bless-boot.service` is ordered after and `Requires=` the generic
|
||||
`boot-complete.target` unit. This unit is hence also pulled into the initial
|
||||
transaction.
|
||||
|
||||
8. The `boot-complete.target` unit is ordered after and pulls in various units
|
||||
that are required to succeed for the boot process to be considered
|
||||
successful. One such unit is `systemd-boot-check-no-failures.service`.
|
||||
|
||||
9. `systemd-boot-check-no-failures.service` is run after all its own
|
||||
dependencies completed, and assesses that the boot completed
|
||||
successfully. It hence exits cleanly.
|
||||
|
||||
10. This allows `boot-complete.target` to be reached. This signifies to the
|
||||
system that this boot attempt shall be considered successful.
|
||||
|
||||
11. Which in turn permits `systemd-bless-boot.service` to run. It now
|
||||
determines which boot loader entry file was used to boot the system, and
|
||||
renames it dropping the counter tag. Thus
|
||||
`4.14.11-300.fc27.x86_64+1-2.conf` is renamed to
|
||||
`4.14.11-300.fc27.x86_64.conf`. From this moment boot counting is turned
|
||||
off.
|
||||
|
||||
12. On the following boot (and all subsequent boots after that) the entry is
|
||||
now seen with boot counting turned off, no further renaming takes place.
|
||||
|
||||
# How to adapt this scheme to other setups
|
||||
|
||||
Of the stack described above many components may be replaced or augmented. Here
|
||||
are a couple of recommendations.
|
||||
|
||||
1. To support alternative boot loaders in place of `systemd-boot` two scenarios
|
||||
are recommended:
|
||||
|
||||
a. Boot loaders already implementing the Boot Loader Specification can simply
|
||||
implement an equivalent file rename based logic, and thus integrate fully
|
||||
with the rest of the stack.
|
||||
|
||||
b. Boot loaders that want to implement boot counting and store the counters
|
||||
elsewhere can provide their own replacements for
|
||||
`systemd-bless-boot.service` and `systemd-bless-boot-generator`, but should
|
||||
continue to use `boot-complete.target` and thus support any services
|
||||
ordered before that.
|
||||
|
||||
2. To support additional components that shall succeed before the boot is
|
||||
considered successful, simply place them in units (if they aren't already)
|
||||
and order them before the generic `boot-complete.target` target unit,
|
||||
combined with `Requires=` dependencies from the target, so that the target
|
||||
cannot be reached when any of the units fail. You may add any number of
|
||||
units like this, and only if they all succeed the boot entry is marked as
|
||||
good. Note that the target unit shall pull in these boot checking units, not
|
||||
the other way around.
|
||||
|
||||
3. To support additional components that shall only run on boot success, simply
|
||||
wrap them in a unit and order them after `boot-complete.target`, pulling it
|
||||
in.
|
||||
|
||||
# FAQ
|
||||
|
||||
1. *Why do you use file renames to store the counter? Why not a regular file?*
|
||||
— Mainly two reasons: it's relatively likely that renames can be implemented
|
||||
atomically even in simpler file systems, while writing to file contents has
|
||||
a much bigger chance to be result in incomplete or corrupt data, as renaming
|
||||
generally avoids allocating or releasing data blocks. Moreover it has the
|
||||
benefit that the boot count metadata is directly attached to the boot loader
|
||||
entry file, and thus the lifecycle of the metadata and the entry itself are
|
||||
bound together. This means no additional clean-up needs to take place to
|
||||
drop the boot loader counting information for an entry when it is removed.
|
||||
|
||||
2. *Why not use EFI variables for storing the boot counter?* — The memory chips
|
||||
used to back the persistent EFI variables are generally not of the highest
|
||||
quality, hence shouldn't be written to more than necessary. This means we
|
||||
can't really use it for changes made regularly during boot, but can use it
|
||||
only for seldom made configuration changes.
|
||||
|
||||
3. *I have a service which — when it fails — should immediately cause a
|
||||
reboot. How does that fit in with the above?* — Well, that's orthogonal to
|
||||
the above, please use `FailureAction=` in the unit file for this.
|
||||
|
||||
4. *Under some condition I want to mark the current boot loader entry as bad
|
||||
right-away, so that it never is tried again, how do I do that?* — You may
|
||||
invoke `/usr/lib/systemd/systemd-bless-boot bad` at any time to mark the
|
||||
current boot loader entry as "bad" right-away so that it isn't tried again
|
||||
on later boots.
|
|
@ -63,49 +63,61 @@
|
|||
<varlistentry>
|
||||
<term><command>add <replaceable>KERNEL-VERSION</replaceable> <replaceable>KERNEL-IMAGE</replaceable></command></term>
|
||||
<listitem>
|
||||
<para><command>kernel-install</command> creates the directory
|
||||
<para>This command expects a kernel version string and a path to a kernel image file as
|
||||
arguments. <command>kernel-install</command> creates the directory
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename>
|
||||
and calls executables from
|
||||
<filename>/usr/lib/kernel/install.d/*.install</filename> and
|
||||
<filename>/etc/kernel/install.d/*.install</filename> with
|
||||
the arguments
|
||||
<programlisting>add <replaceable>KERNEL-VERSION</replaceable> \
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> <replaceable>KERNEL-IMAGE</replaceable></programlisting>
|
||||
and calls the executables from <filename>/usr/lib/kernel/install.d/*.install</filename> and
|
||||
<filename>/etc/kernel/install.d/*.install</filename> with the following arguments:
|
||||
|
||||
<programlisting>add <replaceable>KERNEL-VERSION</replaceable> <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> <replaceable>KERNEL-IMAGE</replaceable></programlisting>
|
||||
</para>
|
||||
|
||||
<para>The kernel-install plugin <filename>50-depmod.install</filename> runs depmod for the <replaceable>KERNEL-VERSION</replaceable>.</para>
|
||||
<para>Two default plugins execute the following operations in this case:</para>
|
||||
|
||||
<para>The kernel-install plugin
|
||||
<filename>90-loaderentry.install</filename> copies
|
||||
<replaceable>KERNEL-IMAGE</replaceable> to
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/linux</filename>.
|
||||
It also creates a boot loader entry according to the boot
|
||||
loader specification in
|
||||
<filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.
|
||||
The title of the entry is the
|
||||
<replaceable>PRETTY_NAME</replaceable> parameter specified
|
||||
in <filename>/etc/os-release</filename> or
|
||||
<filename>/usr/lib/os-release</filename> (if the former is
|
||||
missing), or "Linux
|
||||
<replaceable>KERNEL-VERSION</replaceable>", if unset. If
|
||||
the file <filename>initrd</filename> is found next to the
|
||||
<filename>linux</filename> file, the initrd will be added to
|
||||
the configuration.</para>
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><filename>50-depmod.install</filename> runs
|
||||
<citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry> for the
|
||||
<replaceable>KERNEL-VERSION</replaceable>.</para></listitem>
|
||||
|
||||
<listitem><para><filename>90-loaderentry.install</filename> copies <replaceable>KERNEL-IMAGE</replaceable>
|
||||
to
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/linux</filename>.
|
||||
It also creates a boot loader entry according to the <ulink
|
||||
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> in
|
||||
<filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.
|
||||
The title of the entry is the <replaceable>PRETTY_NAME</replaceable> parameter specified in
|
||||
<filename>/etc/os-release</filename> or <filename>/usr/lib/os-release</filename> (if the former is
|
||||
missing), or "Linux <replaceable>KERNEL-VERSION</replaceable>", if unset. If the file
|
||||
<filename>initrd</filename> is found next to the kernel image file, the initrd will be added to the
|
||||
configuration.</para></listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><command>remove <replaceable>KERNEL-VERSION</replaceable></command></term>
|
||||
<listitem>
|
||||
<para>Calls executables from <filename>/usr/lib/kernel/install.d/*.install</filename>
|
||||
and <filename>/etc/kernel/install.d/*.install</filename> with the arguments
|
||||
<para>This command expects a kernel version string as single argument. This calls executables from
|
||||
<filename>/usr/lib/kernel/install.d/*.install</filename> and
|
||||
<filename>/etc/kernel/install.d/*.install</filename> with the following arguments:
|
||||
|
||||
<programlisting>remove <replaceable>KERNEL-VERSION</replaceable> <filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename></programlisting>
|
||||
</para>
|
||||
|
||||
<para><command>kernel-install</command> removes the entire directory
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename> afterwards.</para>
|
||||
<para>Afterwards, <command>kernel-install</command> removes the directory
|
||||
<filename>/boot/<replaceable>MACHINE-ID</replaceable>/<replaceable>KERNEL-VERSION</replaceable>/</filename>
|
||||
and its contents.</para>
|
||||
|
||||
<para>Two default plugins execute the following operations in this case:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para><filename>50-depmod.install</filename> removes the files generated by <command>depmod</command> for this kernel again.</para></listitem>
|
||||
|
||||
<listitem><para><filename>90-loaderentry.install</filename> removes the file
|
||||
<filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The kernel-install plugin <filename>90-loaderentry.install</filename> removes the file
|
||||
<filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>.conf</filename>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
|
@ -136,8 +148,22 @@
|
|||
<filename>/proc/cmdline</filename>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>The content of the file <filename>/etc/kernel/cmdline</filename> specifies the kernel command line to use.
|
||||
If that file does not exist, <filename>/proc/cmdline</filename> is used.</para>
|
||||
<para>Read by <filename>90-loaderentry.install</filename>. The content of the file
|
||||
<filename>/etc/kernel/cmdline</filename> specifies the kernel command line to use. If that file does not
|
||||
exist, <filename>/proc/cmdline</filename> is used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>
|
||||
<filename>/etc/kernel/tries</filename>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>Read by <filename>90-loaderentry.install</filename>. If this file exists a numeric value is read from
|
||||
it and the naming of the generated entry file is slightly altered to include it as
|
||||
<filename>/boot/loader/entries/<replaceable>MACHINE-ID</replaceable>-<replaceable>KERNEL-VERSION</replaceable>+<replaceable>TRIES</replaceable>.conf</filename>. This
|
||||
is useful for boot loaders such as
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> which
|
||||
implement boot attempt counting with a counter embedded in the entry file name.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
@ -165,6 +191,8 @@
|
|||
<para>
|
||||
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
|
|
@ -617,6 +617,9 @@ manpages = [
|
|||
['systemd-ask-password', '1', [], ''],
|
||||
['systemd-backlight@.service', '8', ['systemd-backlight'], 'ENABLE_BACKLIGHT'],
|
||||
['systemd-binfmt.service', '8', ['systemd-binfmt'], 'ENABLE_BINFMT'],
|
||||
['systemd-bless-boot-generator', '8', [], 'ENABLE_EFI'],
|
||||
['systemd-bless-boot.service', '8', [], 'ENABLE_EFI'],
|
||||
['systemd-boot-check-no-failures.service', '8', [], ''],
|
||||
['systemd-boot', '7', ['sd-boot'], 'ENABLE_EFI'],
|
||||
['systemd-cat', '1', [], ''],
|
||||
['systemd-cgls', '1', [], ''],
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
-->
|
||||
<refentry id="systemd-bless-boot-generator" conditional='ENABLE_EFI'>
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-bless-boot-generator</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-bless-boot-generator</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-bless-boot-generator</refname>
|
||||
<refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect.</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>/usr/lib/systemd/system-generators/systemd-bless-boot-generator</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-bless-boot-generator</filename> is a generator that pulls
|
||||
<filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting, as
|
||||
implemented by <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, is
|
||||
enabled.</para>
|
||||
|
||||
<para><filename>systemd-bless-boot-generator</filename> implements
|
||||
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
|
@ -0,0 +1,115 @@
|
|||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
-->
|
||||
|
||||
<refentry id="systemd-bless-boot.service" conditional='ENABLE_EFI'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-bless-boot.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-bless-boot.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-bless-boot.service</refname>
|
||||
<refpurpose>Mark current boot process as successful</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-bless-boot.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/system-bless-boot</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-bless-boot.service</filename> is a system service that marks the current boot process as
|
||||
successful. It's automatically pulled into the initial transaction when
|
||||
<citerefentry><refentrytitle>systemd-bless-boot-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
detects that <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> style
|
||||
boot counting is used.</para>
|
||||
|
||||
<para>Internally, the service operates based on the <varname>LoaderBootCountPath</varname> EFI variable (of the
|
||||
vendor UUID <constant>4a67b082-0a4c-41cf-b6c7-440b29bb8c4</constant>), which is passed from the boot loader to the
|
||||
OS. It contains a file system path (relative to the EFI system partition) of the <ulink
|
||||
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink> compliant boot loader entry
|
||||
file or unified kernel image file that was used to boot up the
|
||||
system. <command>systemd-bless-boot.service</command> removes the two 'tries done' and 'tries left' numeric boot
|
||||
counters from the filename, which indicates to future invocations of the boot loader that the entry has completed
|
||||
booting successfully at least once. (This service will hence rename the boot loader entry file or unified kernel
|
||||
image file on the first successful boot.)</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The <filename>/usr/lib/systemd/system-bless-boot</filename> executable may also be invoked from the
|
||||
command line, taking one of the following command arguments:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>status</option></term>
|
||||
|
||||
<listitem><para>The current status of the boot loader entry file or unified kernel image file is shown. This
|
||||
outputs one of <literal>good</literal>, <literal>bad</literal>, <literal>indeterminate</literal>,
|
||||
<literal>clean</literal>, depending on the state and previous invocations of the command. The string
|
||||
<literal>indeterminate</literal> is shown initially after boot, before it has been marked as "good" or
|
||||
"bad". The string <literal>good</literal> is shown after the boot was marked as "good" with the
|
||||
<option>good</option> command below, and "bad" conversely after the <option>bad</option> command was
|
||||
invoked. The string <literal>clean</literal> is returned when boot counting is currently not in effect.</para>
|
||||
|
||||
<para>This command is implied if no command argument is specified.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>good</option></term>
|
||||
|
||||
<listitem><para>When invoked, the current boot loader entry file or unified kernel image file will be marked as
|
||||
"good", executing the file rename operation described above. This command is intended to be invoked at the end
|
||||
of a successful boot. The <filename>systemd-bless-boot.service</filename> unit invokes this
|
||||
command.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>bad</option></term>
|
||||
|
||||
<listitem><para>When called the 'tries left' counter in the boot loader entry file name or unified kernel image
|
||||
file name is set to zero, marking the boot loader entry or kernel image as "bad", so that the boot loader won't
|
||||
consider it anymore on future boots (at least as long as there are other entries available that are not marked
|
||||
"bad" yet). This command is normally not executed, but can be used to instantly put an end to the boot counting
|
||||
logic if a problem is detected and persistently mark the boot entry as bad.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>indeterminate</option></term>
|
||||
|
||||
<listitem><para>This command undoes any marking of the current boot loader entry file or unified kernel image
|
||||
file as good or bad. This is implemented by renaming the boot loader entry file or unified kernel image file
|
||||
back to the path encoded in the <varname>LoaderBootCountPath</varname> EFI variable.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1+
|
||||
-->
|
||||
|
||||
<refentry id="systemd-boot-check-no-failures.service"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-boot-check-no-failures.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-boot-check-no-failures.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-boot-check-no-failures.service</refname>
|
||||
<refpurpose>verify that the system booted up cleanly</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-boot-check-no-failures.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/system-boot-check-no-failures</filename></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-boot-check-no-failures.service</filename> is a system service that checks whether the
|
||||
system booted up successfully. This service implements a very minimal test only: whether there are any failed units on
|
||||
the system. This service is disabled by default. When enabled, it is ordered before
|
||||
<filename>boot-complete.target</filename>, thus ensuring the target cannot be reached when the system booted up
|
||||
with failed services.</para>
|
||||
|
||||
<para>Note that due the simple nature of this check this service is probably not suitable for deployment in most
|
||||
scenarios. It is primarily useful only as example for developing more fine-grained checks to order before
|
||||
<filename>boot-complete.target</filename>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
|
@ -75,67 +75,67 @@
|
|||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>↑ (Up)</term>
|
||||
<term>↓ (Down)</term>
|
||||
<term>j</term>
|
||||
<term>k</term>
|
||||
<term>PageUp</term>
|
||||
<term>PageDown</term>
|
||||
<term>Home</term>
|
||||
<term>End</term>
|
||||
<term><keycap>↑</keycap> (Up)</term>
|
||||
<term><keycap>↓</keycap> (Down)</term>
|
||||
<term><keycap>j</keycap></term>
|
||||
<term><keycap>k</keycap></term>
|
||||
<term><keycap>PageUp</keycap></term>
|
||||
<term><keycap>PageDown</keycap></term>
|
||||
<term><keycap>Home</keycap></term>
|
||||
<term><keycap>End</keycap></term>
|
||||
<listitem><para>Navigate up/down in the entry list</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>↵ (Enter)</term>
|
||||
<term><keycap>↵</keycap> (Enter)</term>
|
||||
<listitem><para>Boot selected entry</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>d</term>
|
||||
<term><keycap>d</keycap></term>
|
||||
<listitem><para>Make selected entry the default</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>e</term>
|
||||
<term><keycap>e</keycap></term>
|
||||
<listitem><para>Edit the kernel command line for selected entry</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>+</term>
|
||||
<term>t</term>
|
||||
<term><keycap>+</keycap></term>
|
||||
<term><keycap>t</keycap></term>
|
||||
<listitem><para>Increase the timeout before default entry is booted</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-</term>
|
||||
<term>T</term>
|
||||
<term><keycap>-</keycap></term>
|
||||
<term><keycap>T</keycap></term>
|
||||
<listitem><para>Decrease the timeout</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>v</term>
|
||||
<term><keycap>v</keycap></term>
|
||||
<listitem><para>Show systemd-boot, UEFI, and firmware versions</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>P</term>
|
||||
<term><keycap>P</keycap></term>
|
||||
<listitem><para>Print status</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Q</term>
|
||||
<term><keycap>Q</keycap></term>
|
||||
<listitem><para>Quit</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>h</term>
|
||||
<term>?</term>
|
||||
<term><keycap>h</keycap></term>
|
||||
<term><keycap>?</keycap></term>
|
||||
<listitem><para>Show a help screen</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Ctrl + l</term>
|
||||
<term><keycombo><keycap>Ctrl</keycap><keycap>l</keycap></keycombo></term>
|
||||
<listitem><para>Reprint the screen</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
@ -145,35 +145,35 @@
|
|||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>l</term>
|
||||
<term><keycap>l</keycap></term>
|
||||
<listitem><para>Linux</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>w</term>
|
||||
<term><keycap>w</keycap></term>
|
||||
<listitem><para>Windows</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>a</term>
|
||||
<term><keycap>a</keycap></term>
|
||||
<listitem><para>OS X</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>s</term>
|
||||
<term><keycap>s</keycap></term>
|
||||
<listitem><para>EFI shell</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>1</term>
|
||||
<term>2</term>
|
||||
<term>3</term>
|
||||
<term>4</term>
|
||||
<term>5</term>
|
||||
<term>6</term>
|
||||
<term>7</term>
|
||||
<term>8</term>
|
||||
<term>9</term>
|
||||
<term><keycap>1</keycap></term>
|
||||
<term><keycap>2</keycap></term>
|
||||
<term><keycap>3</keycap></term>
|
||||
<term><keycap>4</keycap></term>
|
||||
<term><keycap>5</keycap></term>
|
||||
<term><keycap>6</keycap></term>
|
||||
<term><keycap>7</keycap></term>
|
||||
<term><keycap>8</keycap></term>
|
||||
<term><keycap>9</keycap></term>
|
||||
<listitem><para>Boot entry number 1 … 9</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
@ -183,36 +183,36 @@
|
|||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>← (Left)</term>
|
||||
<term>→ (Right)</term>
|
||||
<term>Home</term>
|
||||
<term>End</term>
|
||||
<term><keycap>←</keycap> (Left)</term>
|
||||
<term><keycap>→</keycap> (Right)</term>
|
||||
<term><keycap>Home</keycap></term>
|
||||
<term><keycap>End</keycap></term>
|
||||
<listitem><para>Navigate left/right</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Esc</term>
|
||||
<term><keycap>Esc</keycap></term>
|
||||
<listitem><para>Abort the edit and quit the editor</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Ctrl + k</term>
|
||||
<term><keycombo><keycap>Ctrl</keycap><keycap>k</keycap></keycombo></term>
|
||||
<listitem><para>Clear the command line</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Ctrl + w</term>
|
||||
<term>Alt + Backspace</term>
|
||||
<term><keycombo><keycap>Ctrl</keycap><keycap>w</keycap></keycombo></term>
|
||||
<term><keycombo><keycap>Alt</keycap><keycap>Backspace</keycap></keycombo></term>
|
||||
<listitem><para>Delete word backwards</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>Alt + d </term>
|
||||
<term><keycombo><keycap>Alt</keycap><keycap>d</keycap></keycombo></term>
|
||||
<listitem><para>Delete word forwards</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>↵ (Enter)</term>
|
||||
<term><keycap>↵</keycap> (Enter)</term>
|
||||
<listitem><para>Boot entry with the edited command line</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
@ -237,11 +237,168 @@
|
|||
Loader Specification</ulink> are read from <filename>/EFI/Linux/</filename> on the ESP.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>EFI Variables</title>
|
||||
|
||||
<para>The following EFI variables are defined, set and read by <command>systemd-boot</command>, under the vendor
|
||||
UUID <literal>4a67b082-0a4c-41cf-b6c7-440b29bb8c4</literal>, for communication between the OS and the boot
|
||||
loader:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>LoaderBootCountPath</varname></term>
|
||||
<listitem><para>If boot counting is enabled, contains the path to the file in whose name the boot counters are
|
||||
encoded. Set by the boot
|
||||
loader. <citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
uses this information to mark a boot as successful as determined by the successful activation of the
|
||||
<filename>boot-complete.target</filename> target unit.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderConfigTimeout</varname></term>
|
||||
<listitem><para>The menu time-out. Read by the boot loader. (Also, modified by it when the
|
||||
<keycap>t</keycap>/<keycap>T</keycap> keys are used, see above.)</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderDevicePartUUID</varname></term>
|
||||
|
||||
<listitem><para>Contains the partition UUID of the EFI System Partition the boot loader was run from. Set by
|
||||
the boot
|
||||
loader. <citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
uses this information to automatically find the disk booted from, in order to discover various other partitions
|
||||
on the same disk automatically.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderEntries</varname></term>
|
||||
|
||||
<listitem><para>A list of the identifiers of all discovered boot loader entries. Set by the boot
|
||||
loader.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderEntryDefault</varname></term>
|
||||
<term><varname>LoaderEntryOneShot</varname></term>
|
||||
|
||||
<listitem><para>The identifier of the default boot loader entry. Set primarily by the OS and read by the boot
|
||||
loader. <varname>LoaderEntryOneShot</varname> sets the default entry for the next boot only, while
|
||||
<varname>LoaderEntryDefault</varname> sets it persistently for all future
|
||||
boots. <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
|
||||
<option>set-default</option> and <option>set-oneshot</option> commands make use of these variables. The boot
|
||||
loader modifies <varname>LoaderEntryDefault</varname> on request, when the <keycap>d</keycap> key is used, see
|
||||
above.)</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderEntrySelected</varname></term>
|
||||
|
||||
<listitem><para>The identifier of the boot loader entry currently being booted. Set by the boot
|
||||
loader.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderFirmwareInfo</varname></term>
|
||||
<term><varname>LoaderFirmwareType</varname></term>
|
||||
|
||||
<listitem><para>Brief firmware information. Set by the boot loader. Use
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
|
||||
data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderImageIdentifier</varname></term>
|
||||
|
||||
<listitem><para>The path of executable of the boot loader used for the current boot, relative to the EFI System
|
||||
Partition's root directory. Set by the boot loader. Use
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
|
||||
data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderInfo</varname></term>
|
||||
|
||||
<listitem><para>Brief information about the boot loader. Set by the boot loader. Use
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to view this
|
||||
data.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>LoaderTimeExecUSec</varname></term>
|
||||
<term><varname>LoaderTimeInitUSec</varname></term>
|
||||
<term><varname>LoaderTimeMenuUsec</varname></term>
|
||||
|
||||
<listitem><para>Information about the time spent in various parts of the boot loader. Set by the boot
|
||||
loader. Use <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
to view this data. These variables are defined by the <ulink
|
||||
url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader
|
||||
Interface</ulink>.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Boot Counting</title>
|
||||
|
||||
<para><command>systemd-boot</command> implements a simple boot counting mechanism on top of the <ulink
|
||||
url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>, for automatic and unattended
|
||||
fallback to older kernel versions/boot loader entries when a specific entry continously fails. Any boot loader
|
||||
entry file and unified kernel image file that contains a <literal>+</literal> followed by one or two numbers (if
|
||||
two they need to be separated by a <literal>-</literal>), before the <filename>.conf</filename> or
|
||||
<filename>.efi</filename> suffix is subject to boot counting: the first of the two numbers ('tries left') is
|
||||
decreased by one on every boot attempt, the second of the two numbers ('tries done') is increased by one (if 'tries
|
||||
done' is absent it is considered equivalent to 0). Depending on the current value of these two counters the boot
|
||||
entry is considered to be in one of three states:</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para>If the 'tries left' counter of an entry is greater than zero the entry is considered to be in
|
||||
'indeterminate' state. This means the entry has not completed booting successfully yet, but also hasn't been
|
||||
determined not to work.</para></listitem>
|
||||
|
||||
<listitem><para>If the 'tries left' counter of an entry is zero it is considered to be in 'bad' state. This means
|
||||
no further attempts to boot this item will be made (that is, unless all other boot entries are also in 'bad'
|
||||
state), as all attempts to boot this entry have not completed successfully.</para></listitem>
|
||||
|
||||
<listitem><para>If the 'tries left' and 'tries done' counters of an entry are absent it is considered to be in
|
||||
'good' state. This means further boot counting for the entry is turned off, as it successfully booted at least
|
||||
once. The
|
||||
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
service moves the currently booted entry from 'indeterminate' into 'good' state when a boot attempt completed
|
||||
successfully.</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>Generally, when new entries are added to the boot loader, they first start out in 'indeterminate' state,
|
||||
i.e. with a 'tries left' counter greater than zero. The boot entry remains in this state until either it managed to
|
||||
complete a full boot successfully at least once (in which case it will be in 'good' state) — or the 'tries left'
|
||||
counter reaches zero (in which case it will be in 'bad' state).</para>
|
||||
|
||||
<para>Example: let's say a boot loader entry file <filename>foo.conf</filename> is set up for 3 boot tries. The
|
||||
installer will hence create it under the name <filename>foo+3.conf</filename>. On first boot, the boot loader will
|
||||
rename it to <filename>foo+2-1.conf</filename>. If that boot does not complete successfully, the boot loader will
|
||||
rename it to <filename>foo+1-2.conf</filename> on the following boot. If that fails too, it will finally be renamed
|
||||
<filename>foo+0-3.conf</filename> by the boot loader on next boot, after which it will be considered 'bad'. If the
|
||||
boot succeeds however the entry file will be renamed to <filename>foo.conf</filename> by the OS, so that it is
|
||||
considered 'good' from then on.</para>
|
||||
|
||||
<para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
|
||||
state are ordered at the end of the list, and entries in 'good' or 'indeterminate' at the beginning. The user can
|
||||
freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
|
||||
automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the top item of
|
||||
the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
|
||||
'indeterminate' entries left.</para>
|
||||
|
||||
<para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
|
||||
install framework optionally sets the initial 'tries left' counter to the value specified in
|
||||
<filename>/etc/kernel/tries</filename> when a boot loader entry is first created.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para>
|
||||
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
|
||||
<ulink url="https://www.freedesktop.org/wiki/Software/systemd/BootLoaderInterface">Boot Loader Interface</ulink>
|
||||
</para>
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
<filename>cryptsetup-pre.target</filename>,
|
||||
<filename>cryptsetup.target</filename>,
|
||||
<filename>ctrl-alt-del.target</filename>,
|
||||
<filename>boot-complete.target</filename>,
|
||||
<filename>default.target</filename>,
|
||||
<filename>emergency.target</filename>,
|
||||
<filename>exit.target</filename>,
|
||||
|
@ -144,7 +145,28 @@
|
|||
<citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
for details on the targets involved.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><filename>boot-complete.target</filename></term>
|
||||
<listitem>
|
||||
<para>This target is intended as generic synchronization point for services that shall determine or act on
|
||||
whether the boot process completed successfully. Order units that are required to succeed for a boot process
|
||||
to be considered successful before this unit, and add a <varname>Requires=</varname> dependency from the
|
||||
target unit to them. Order units that shall only run when the boot process is considered successful after the
|
||||
target unit and pull in the target from it, also with <varname>Requires=</varname>. Note that by default this
|
||||
target unit is not part of the initial boot transaction, but is supposed to be pulled in only if required by
|
||||
units that want to run only on successful boots.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd-boot-check-no-failures.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
for a service that implements a generic system health check and orders itself before
|
||||
<filename>boot-complete.target</filename>.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
for a service that propagates boot success information to the boot loader, and orders itself after
|
||||
<filename>boot-complete.target</filename>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
|
26
meson.build
26
meson.build
|
@ -1796,8 +1796,34 @@ if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_BLKID') == 1
|
|||
install_rpath : rootlibexecdir,
|
||||
install : true)
|
||||
public_programs += exe
|
||||
|
||||
executable('systemd-bless-boot',
|
||||
'src/boot/bless-boot.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libblkid],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
executable('systemd-bless-boot-generator',
|
||||
'src/boot/bless-boot-generator.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : systemgeneratordir)
|
||||
endif
|
||||
|
||||
executable('systemd-boot-check-no-failures',
|
||||
'src/boot/boot-check-no-failures.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libblkid],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
|
||||
exe = executable('systemd-socket-activate', 'src/activate/activate.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
|
|
|
@ -1235,6 +1235,34 @@ int fsync_directory_of_file(int fd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fsync_path_at(int at_fd, const char *path) {
|
||||
_cleanup_close_ int opened_fd = -1;
|
||||
int fd;
|
||||
|
||||
if (isempty(path)) {
|
||||
if (at_fd == AT_FDCWD) {
|
||||
opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
} else
|
||||
fd = at_fd;
|
||||
} else {
|
||||
|
||||
opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC);
|
||||
if (opened_fd < 0)
|
||||
return -errno;
|
||||
|
||||
fd = opened_fd;
|
||||
}
|
||||
|
||||
if (fsync(fd) < 0)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode) {
|
||||
_cleanup_free_ char *parent = NULL;
|
||||
int fd;
|
||||
|
|
|
@ -105,5 +105,6 @@ void unlink_tempfilep(char (*p)[]);
|
|||
int unlinkat_deallocate(int fd, const char *name, int flags);
|
||||
|
||||
int fsync_directory_of_file(int fd);
|
||||
int fsync_path_at(int at_fd, const char *path);
|
||||
|
||||
int open_parent(const char *path, int flags, mode_t mode);
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "efivars.h"
|
||||
#include "log.h"
|
||||
#include "mkdir.h"
|
||||
#include "special.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
#include "virt.h"
|
||||
|
||||
/* This generator pulls systemd-bless-boot.service into the initial transaction if the "LoaderBootCountPath" EFI
|
||||
* variable is set, i.e. the system boots up with boot counting in effect, which means we should mark the boot as
|
||||
* "good" if we manage to boot up far enough. */
|
||||
|
||||
static const char *arg_dest = "/tmp";
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
const char *p;
|
||||
|
||||
log_set_prohibit_ipc(true);
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
umask(0022);
|
||||
|
||||
if (argc > 1 && argc != 4) {
|
||||
log_error("This program takes three or no arguments.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
arg_dest = argv[2];
|
||||
|
||||
if (in_initrd() > 0) {
|
||||
log_debug("Skipping generator, running in the initrd.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (detect_container() > 0) {
|
||||
log_debug("Skipping generator, running in a container.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_debug("Skipping generator, not an EFI boot.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (access("/sys/firmware/efi/efivars/LoaderBootCountPath-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f", F_OK) < 0) {
|
||||
|
||||
if (errno == ENOENT) {
|
||||
log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
log_error_errno(errno, "Failed to check if LoaderBootCountPath EFI variable exists: %m");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in rescue.target or
|
||||
* even emergency.target. */
|
||||
p = strjoina(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/systemd-bless-boot.service");
|
||||
(void) mkdir_parents(p, 0755);
|
||||
if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-bless-boot.service", p) < 0) {
|
||||
log_error_errno(errno, "Failed to create symlink '%s': %m", p);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,476 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bootspec.h"
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "util.h"
|
||||
#include "verbs.h"
|
||||
#include "virt.h"
|
||||
|
||||
static char *arg_path = NULL;
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
|
||||
printf("%s [COMMAND] [OPTIONS...]\n"
|
||||
"\n"
|
||||
"Mark the boot process as good or bad.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n"
|
||||
" --path=PATH Path to the EFI System Partition (ESP)\n"
|
||||
"\n"
|
||||
"Commands:\n"
|
||||
" good Mark this boot as good\n"
|
||||
" bad Mark this boot as bad\n"
|
||||
" indeterminate Undo any marking as good or bad\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_PATH = 0x100,
|
||||
ARG_VERSION,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "path", required_argument, NULL, ARG_PATH },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help(0, NULL, NULL);
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_PATH:
|
||||
r = free_and_strdup(&arg_path, optarg);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached("Unknown option");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int acquire_esp(void) {
|
||||
_cleanup_free_ char *np = NULL;
|
||||
int r;
|
||||
|
||||
r = find_esp_and_warn(arg_path, false, &np, NULL, NULL, NULL, NULL);
|
||||
if (r == -ENOKEY) /* find_esp_and_warn() doesn't warn in this one error case, but in all others */
|
||||
return log_error_errno(r,
|
||||
"Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
|
||||
"Alternatively, use --path= to specify path to mount point.");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
free_and_replace(arg_path, np);
|
||||
log_debug("Using EFI System Partition at %s.", arg_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_counter(
|
||||
const char *path,
|
||||
const char **p,
|
||||
uint64_t *ret_left,
|
||||
uint64_t *ret_done) {
|
||||
|
||||
uint64_t left, done;
|
||||
const char *z, *e;
|
||||
size_t k;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(p);
|
||||
|
||||
e = *p;
|
||||
assert(e);
|
||||
assert(*e == '+');
|
||||
|
||||
e++;
|
||||
|
||||
k = strspn(e, DIGITS);
|
||||
if (k == 0) {
|
||||
log_error("Can't parse empty 'tries left' counter from LoaderBootCountPath: %s", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
z = strndupa(e, k);
|
||||
r = safe_atou64(z, &left);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse 'tries left' counter from LoaderBootCountPath: %s", path);
|
||||
|
||||
e += k;
|
||||
|
||||
if (*e == '-') {
|
||||
e++;
|
||||
|
||||
k = strspn(e, DIGITS);
|
||||
if (k == 0) { /* If there's a "-" there also needs to be at least one digit */
|
||||
log_error("Can't parse empty 'tries done' counter from LoaderBootCountPath: %s", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
z = strndupa(e, k);
|
||||
r = safe_atou64(z, &done);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse 'tries done' counter from LoaderBootCountPath: %s", path);
|
||||
|
||||
e += k;
|
||||
} else
|
||||
done = 0;
|
||||
|
||||
if (done == 0)
|
||||
log_warning("The 'tries done' counter is currently at zero. This can't really be, after all we are running, and this boot must hence count as one. Proceeding anyway.");
|
||||
|
||||
*p = e;
|
||||
|
||||
if (ret_left)
|
||||
*ret_left = left;
|
||||
|
||||
if (ret_done)
|
||||
*ret_done = done;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acquire_boot_count_path(
|
||||
char **ret_path,
|
||||
char **ret_prefix,
|
||||
uint64_t *ret_left,
|
||||
uint64_t *ret_done,
|
||||
char **ret_suffix) {
|
||||
|
||||
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL;
|
||||
const char *last, *e;
|
||||
uint64_t left, done;
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderBootCountPath", &path);
|
||||
if (r == -ENOENT)
|
||||
return -EUNATCH; /* in this case, let the caller print a message */
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read LoaderBootCountPath EFI variable: %m");
|
||||
|
||||
efi_tilt_backslashes(path);
|
||||
|
||||
if (!path_is_normalized(path)) {
|
||||
log_error("Path read from LoaderBootCountPath is not normalized, refusing: %s", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!path_is_absolute(path)) {
|
||||
log_error("Path read from LoaderBootCountPath is not absolute, refusing: %s", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
last = last_path_component(path);
|
||||
e = strrchr(last, '+');
|
||||
if (!e) {
|
||||
log_error("Path read from LoaderBootCountPath does not contain a counter, refusing: %s", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret_prefix) {
|
||||
prefix = strndup(path, e - path);
|
||||
if (!prefix)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = parse_counter(path, &e, &left, &done);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_suffix) {
|
||||
suffix = strdup(e);
|
||||
if (!suffix)
|
||||
return log_oom();
|
||||
|
||||
*ret_suffix = TAKE_PTR(suffix);
|
||||
}
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(path);
|
||||
if (ret_prefix)
|
||||
*ret_prefix = TAKE_PTR(prefix);
|
||||
if (ret_left)
|
||||
*ret_left = left;
|
||||
if (ret_done)
|
||||
*ret_done = done;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_good(const char *prefix, const char *suffix, char **ret) {
|
||||
_cleanup_free_ char *good = NULL;
|
||||
|
||||
assert(prefix);
|
||||
assert(suffix);
|
||||
assert(ret);
|
||||
|
||||
/* Generate the path we'd use on good boots. This one is easy. If we are successful, we simple drop the counter
|
||||
* pair entirely from the name. After all, we know all is good, and the logs will contain information about the
|
||||
* tries we needed to come here, hence it's safe to drop the counters from the name. */
|
||||
|
||||
good = strjoin(prefix, suffix);
|
||||
if (!good)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = TAKE_PTR(good);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_bad(const char *prefix, uint64_t done, const char *suffix, char **ret) {
|
||||
_cleanup_free_ char *bad = NULL;
|
||||
|
||||
assert(prefix);
|
||||
assert(suffix);
|
||||
assert(ret);
|
||||
|
||||
/* Generate the path we'd use on bad boots. Let's simply set the 'left' counter to zero, and keep the 'done'
|
||||
* counter. The information might be interesting to boot loaders, after all. */
|
||||
|
||||
if (done == 0) {
|
||||
bad = strjoin(prefix, "+0", suffix);
|
||||
if (!bad)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
if (asprintf(&bad, "%s+0-%" PRIu64 "%s", prefix, done, suffix) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(bad);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *skip_slash(const char *path) {
|
||||
assert(path);
|
||||
assert(path[0] == '/');
|
||||
|
||||
return path + 1;
|
||||
}
|
||||
|
||||
static int verb_status(int argc, char *argv[], void *userdata) {
|
||||
|
||||
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
uint64_t left, done;
|
||||
int r;
|
||||
|
||||
r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix);
|
||||
if (r == -EUNATCH) { /* No boot count in place, then let's consider this a "clean" boot, as "good", "bad" or "indeterminate" don't apply. */
|
||||
puts("clean");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acquire_esp();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = make_good(prefix, suffix, &good);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = make_bad(prefix, done, suffix, &bad);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
log_debug("Booted file: %s%s\n"
|
||||
"The same modified for 'good': %s%s\n"
|
||||
"The same modified for 'bad': %s%s\n",
|
||||
arg_path, path,
|
||||
arg_path, good,
|
||||
arg_path, bad);
|
||||
|
||||
log_debug("Tries left: %" PRIu64"\n"
|
||||
"Tries done: %" PRIu64"\n",
|
||||
left, done);
|
||||
|
||||
fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
|
||||
|
||||
if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) {
|
||||
puts("indeterminate");
|
||||
return 0;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to check if '%s' exists: %m", path);
|
||||
|
||||
if (faccessat(fd, skip_slash(good), F_OK, 0) >= 0) {
|
||||
puts("good");
|
||||
return 0;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to check if '%s' exists: %m", good);
|
||||
|
||||
if (faccessat(fd, skip_slash(bad), F_OK, 0) >= 0) {
|
||||
puts("bad");
|
||||
return 0;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return log_error_errno(errno, "Failed to check if '%s' exists: %m", bad);
|
||||
|
||||
return log_error_errno(errno, "Couldn't determine boot state: %m");
|
||||
}
|
||||
|
||||
static int verb_set(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL;
|
||||
const char *target, *source1, *source2;
|
||||
_cleanup_close_ int fd = -1;
|
||||
uint64_t done;
|
||||
int r;
|
||||
|
||||
r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix);
|
||||
if (r == -EUNATCH) /* acquire_boot_count_path() won't log on its own for this specific error */
|
||||
return log_error_errno(r, "Not booted with boot counting in effect.");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = acquire_esp();
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = make_good(prefix, suffix, &good);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = make_bad(prefix, done, suffix, &bad);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
fd = open(arg_path, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
|
||||
if (fd < 0)
|
||||
return log_error_errno(errno, "Failed to open ESP '%s': %m", arg_path);
|
||||
|
||||
/* Figure out what rename to what */
|
||||
if (streq(argv[0], "good")) {
|
||||
target = good;
|
||||
source1 = path;
|
||||
source2 = bad; /* Maybe this boot was previously marked as 'bad'? */
|
||||
} else if (streq(argv[0], "bad")) {
|
||||
target = bad;
|
||||
source1 = path;
|
||||
source2 = good; /* Maybe this boot was previously marked as 'good'? */
|
||||
} else {
|
||||
assert(streq(argv[0], "indeterminate"));
|
||||
target = path;
|
||||
source1 = good;
|
||||
source2 = bad;
|
||||
}
|
||||
|
||||
r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target));
|
||||
if (r == -EEXIST)
|
||||
goto exists;
|
||||
else if (r == -ENOENT) {
|
||||
|
||||
r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target));
|
||||
if (r == -EEXIST)
|
||||
goto exists;
|
||||
else if (r == -ENOENT) {
|
||||
|
||||
if (access(target, F_OK) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */
|
||||
goto exists;
|
||||
|
||||
return log_error_errno(r, "Can't find boot counter source file for '%s': %m", target);
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source2, target);
|
||||
else
|
||||
log_debug("Successfully renamed '%s' to '%s'.", source2, target);
|
||||
|
||||
} else if (r < 0)
|
||||
return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target);
|
||||
else
|
||||
log_debug("Successfully renamed '%s' to '%s'.", source1, target);
|
||||
|
||||
/* First, fsync() the directory these files are located in */
|
||||
parent = dirname_malloc(path);
|
||||
if (!parent)
|
||||
return log_oom();
|
||||
|
||||
r = fsync_path_at(fd, skip_slash(parent));
|
||||
if (r < 0)
|
||||
log_debug_errno(errno, "Failed to synchronize image directory, ignoring: %m");
|
||||
|
||||
/* Secondly, syncfs() the whole file system these files are located in */
|
||||
if (syncfs(fd) < 0)
|
||||
log_debug_errno(errno, "Failed to synchronize ESP, ignoring: %m");
|
||||
|
||||
log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done);
|
||||
|
||||
return 1;
|
||||
|
||||
exists:
|
||||
log_debug("Operation already executed before, not doing anything.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
|
||||
{ "good", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
|
||||
{ "bad", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
|
||||
{ "indeterminate", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
|
||||
{}
|
||||
};
|
||||
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
goto finish;
|
||||
|
||||
if (detect_container() > 0) {
|
||||
log_error("Marking a boot is not supported in containers.");
|
||||
r = -EOPNOTSUPP;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_error("Marking a boot is only supported on EFI systems.");
|
||||
r = -EOPNOTSUPP;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = dispatch_verb(argc, argv, verbs, NULL);
|
||||
|
||||
finish:
|
||||
free(arg_path);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-error.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
static int help(void) {
|
||||
|
||||
printf("%s [COMMAND] [OPTIONS...]\n"
|
||||
"\n"
|
||||
"Verify system operational state.\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version\n",
|
||||
program_invocation_short_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_PATH = 0x100,
|
||||
ARG_VERSION,
|
||||
};
|
||||
|
||||
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("Unknown option");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
uint32_t n;
|
||||
int r;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
goto finish;
|
||||
|
||||
r = sd_bus_open_system(&bus);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to connect to system bus: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_get_property_trivial(
|
||||
bus,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"NFailedUnits",
|
||||
&error,
|
||||
'u',
|
||||
&n);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to get failed units counter: %s", bus_error_message(&error, r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
log_notice("Health check: %" PRIu32 " units have failed.", n);
|
||||
else
|
||||
log_info("Health check: no failed units.");
|
||||
|
||||
r = n > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
||||
finish:
|
||||
return r < 0 ? EXIT_FAILURE : r;
|
||||
}
|
|
@ -19,10 +19,11 @@ MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID
|
|||
|
||||
BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION"
|
||||
BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR}
|
||||
LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
|
||||
|
||||
if [[ $COMMAND == remove ]]; then
|
||||
exec rm -f "$LOADER_ENTRY"
|
||||
rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
|
||||
rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [[ $COMMAND == add ]]; then
|
||||
|
@ -63,6 +64,17 @@ if ! [[ ${BOOT_OPTIONS[*]} ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f /etc/kernel/tries ]]; then
|
||||
read -r TRIES </etc/kernel/tries
|
||||
if ! [[ "$TRIES" =~ ^[0-9]+$ ]] ; then
|
||||
echo "/etc/kernel/tries does not contain an integer." >&2
|
||||
exit 1
|
||||
fi
|
||||
LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+$TRIES.conf"
|
||||
else
|
||||
LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf"
|
||||
fi
|
||||
|
||||
cp "$KERNEL_IMAGE" "$BOOT_DIR_ABS/linux" &&
|
||||
chown root:root "$BOOT_DIR_ABS/linux" &&
|
||||
chmod 0644 "$BOOT_DIR_ABS/linux" || {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# 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=Boot Completion Check
|
||||
Documentation=man:systemd.special(7)
|
||||
Requires=sysinit.target
|
||||
After=sysinit.target
|
|
@ -3,6 +3,7 @@
|
|||
units = [
|
||||
['basic.target', ''],
|
||||
['bluetooth.target', ''],
|
||||
['boot-complete.target', ''],
|
||||
['cryptsetup-pre.target', 'HAVE_LIBCRYPTSETUP'],
|
||||
['cryptsetup.target', 'HAVE_LIBCRYPTSETUP',
|
||||
'sysinit.target.wants/'],
|
||||
|
@ -135,6 +136,8 @@ in_units = [
|
|||
['systemd-backlight@.service', 'ENABLE_BACKLIGHT'],
|
||||
['systemd-binfmt.service', 'ENABLE_BINFMT',
|
||||
'sysinit.target.wants/'],
|
||||
['systemd-bless-boot.service', 'ENABLE_EFI HAVE_BLKID'],
|
||||
['systemd-boot-check-no-failures.service', ''],
|
||||
['systemd-coredump@.service', 'ENABLE_COREDUMP'],
|
||||
['systemd-firstboot.service', 'ENABLE_FIRSTBOOT',
|
||||
'sysinit.target.wants/'],
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# 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=Mark the Current Boot Loader Entry as Good
|
||||
Documentation=man:systemd-bless-boot.service(8)
|
||||
DefaultDependencies=no
|
||||
Requires=boot-complete.target
|
||||
After=local-fs.target boot-complete.target
|
||||
Conflicts=shutdown.target
|
||||
Before=shutdown.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootlibexecdir@/systemd-bless-boot good
|
|
@ -0,0 +1,24 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
#
|
||||
# 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 if Any System Units Failed
|
||||
Documentation=man:systemd-boot-check-no-failures.service(8)
|
||||
After=default.target graphical.target multi-user.target
|
||||
Before=boot-complete.target
|
||||
Conflicts=shutdown.target
|
||||
Before=shutdown.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=@rootlibexecdir@/systemd-boot-check-no-failures
|
||||
|
||||
[Install]
|
||||
RequiredBy=boot-complete.target
|
Loading…
Reference in New Issue